From 854ab5457f9154d8d3517386d0af6f69d6fe2bf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 19 Oct 2024 00:39:52 +0000 Subject: [PATCH] Added chart versions: codefresh/cf-runtime: - 6.4.7 intel/intel-device-plugins-operator: - 0.31.1 intel/intel-device-plugins-qat: - 0.31.1 intel/intel-device-plugins-sgx: - 0.31.1 linkerd/linkerd-control-plane: - 2024.10.3 linkerd/linkerd-crds: - 2024.10.3 speedscale/speedscale-operator: - 2.2.556 --- assets/codefresh/cf-runtime-6.4.7.tgz | Bin 0 -> 43706 bytes .../intel-device-plugins-operator-0.31.1.tgz | Bin 0 -> 9193 bytes .../intel/intel-device-plugins-qat-0.31.1.tgz | Bin 0 -> 2557 bytes .../intel/intel-device-plugins-sgx-0.31.1.tgz | Bin 0 -> 2174 bytes .../linkerd-control-plane-2024.10.2.tgz | Bin 31591 -> 31579 bytes .../linkerd-control-plane-2024.10.3.tgz | Bin 0 -> 31590 bytes assets/linkerd/linkerd-crds-2024.10.3.tgz | Bin 0 -> 113070 bytes .../speedscale-operator-2.2.556.tgz | Bin 0 -> 17062 bytes charts/codefresh/cf-runtime/6.4.7/.helmignore | 3 + charts/codefresh/cf-runtime/6.4.7/Chart.yaml | 28 + charts/codefresh/cf-runtime/6.4.7/README.md | 1230 ++++ .../cf-runtime/6.4.7/README.md.gotmpl | 1007 ++++ .../cf-runtime/6.4.7/files/cleanup-runtime.sh | 37 + .../6.4.7/files/configure-dind-certs.sh | 132 + .../cf-runtime/6.4.7/files/init-runtime.sh | 80 + .../6.4.7/files/reconcile-runtime.sh | 38 + .../_components/app-proxy/_deployment.yaml | 70 + .../_components/app-proxy/_env-vars.yaml | 19 + .../_components/app-proxy/_helpers.tpl | 43 + .../_components/app-proxy/_ingress.yaml | 32 + .../_components/app-proxy/_rbac.yaml | 47 + .../_components/app-proxy/_service.yaml | 17 + .../event-exporter/_deployment.yaml | 62 + .../_components/event-exporter/_env-vars.yaml | 14 + .../_components/event-exporter/_helpers.tpl | 43 + .../_components/event-exporter/_rbac.yaml | 47 + .../_components/event-exporter/_service.yaml | 17 + .../event-exporter/_serviceMontor.yaml | 14 + .../_components/monitor/_deployment.yaml | 70 + .../_components/monitor/_env-vars.yaml | 26 + .../_components/monitor/_helpers.tpl | 42 + .../templates/_components/monitor/_rbac.yaml | 56 + .../_components/monitor/_service.yaml | 17 + .../_components/runner/_deployment.yaml | 103 + .../templates/_components/runner/_helpers.tpl | 42 + .../templates/_components/runner/_rbac.yaml | 53 + .../_init-container.yaml | 30 + .../_main-container.yaml | 28 + .../_sidecar-container.yaml | 22 + .../volume-provisioner/_cronjob.yaml | 58 + .../volume-provisioner/_daemonset.yaml | 98 + .../volume-provisioner/_deployment.yaml | 67 + .../volume-provisioner/_env-vars.yaml | 88 + .../volume-provisioner/_helpers.tpl | 93 + .../_components/volume-provisioner/_rbac.yaml | 71 + .../volume-provisioner/_secret.yaml | 22 + .../volume-provisioner/_storageclass.yaml | 47 + .../cf-runtime/6.4.7/templates/_helpers.tpl | 51 + .../6.4.7/templates/app-proxy/deployment.yaml | 9 + .../6.4.7/templates/app-proxy/ingress.yaml | 9 + .../6.4.7/templates/app-proxy/rbac.yaml | 9 + .../6.4.7/templates/app-proxy/service.yaml | 9 + .../templates/event-exporter/deployment.yaml | 9 + .../6.4.7/templates/event-exporter/rbac.yaml | 9 + .../templates/event-exporter/service.yaml | 11 + .../templates/extra/extra-resources.yaml | 6 + .../templates/extra/runtime-images-cm.yaml | 19 + .../hooks/post-install/cm-update-runtime.yaml | 18 + .../hooks/post-install/job-gencerts-dind.yaml | 68 + .../post-install/job-update-runtime.yaml | 77 + .../post-install/rbac-gencerts-dind.yaml | 37 + .../pre-delete/job-cleanup-resources.yaml | 73 + .../pre-delete/rbac-cleanup-resources.yaml | 46 + .../6.4.7/templates/monitor/deployment.yaml | 9 + .../6.4.7/templates/monitor/rbac.yaml | 9 + .../6.4.7/templates/monitor/service.yaml | 9 + .../templates/other/external-secrets.yaml | 2 + .../6.4.7/templates/other/podMonitor.yaml | 2 + .../6.4.7/templates/other/serviceMonitor.yaml | 2 + .../6.4.7/templates/runner/deployment.yaml | 9 + .../6.4.7/templates/runner/rbac.yaml | 9 + .../6.4.7/templates/runtime/_helpers.tpl | 123 + .../templates/runtime/cm-dind-daemon.yaml | 10 + .../6.4.7/templates/runtime/rbac.yaml | 48 + .../runtime/runtime-env-spec-tmpl.yaml | 206 + .../6.4.7/templates/runtime/secret.yaml | 11 + .../6.4.7/templates/runtime/svc-dind.yaml | 16 + .../templates/volume-provisioner/cronjob.yaml | 11 + .../volume-provisioner/daemonset.yaml | 11 + .../volume-provisioner/deployment.yaml | 10 + .../templates/volume-provisioner/rbac.yaml | 9 + .../templates/volume-provisioner/secret.yaml | 10 + .../volume-provisioner/storageclass.yaml | 10 + charts/codefresh/cf-runtime/6.4.7/values.yaml | 951 +++ .../0.31.1/.helmignore | 23 + .../0.31.1/Chart.yaml | 13 + .../0.31.1/LICENSE | 14 + .../0.31.1/README.md | 54 + ...viceplugin.intel.com_dlbdeviceplugins.yaml | 190 + ...viceplugin.intel.com_dsadeviceplugins.yaml | 200 + ...iceplugin.intel.com_fpgadeviceplugins.yaml | 197 + ...viceplugin.intel.com_gpudeviceplugins.yaml | 214 + ...viceplugin.intel.com_iaadeviceplugins.yaml | 199 + ...viceplugin.intel.com_qatdeviceplugins.yaml | 230 + ...viceplugin.intel.com_sgxdeviceplugins.yaml | 201 + .../fpga.intel.com_acceleratorfunctions.yaml | 68 + .../crds/fpga.intel.com_fpgaregions.yaml | 59 + .../0.31.1/templates/NOTES.txt | 3 + .../0.31.1/templates/operator.yaml | 726 +++ .../0.31.1/values.yaml | 28 + .../0.31.1/.helmignore | 23 + .../0.31.1/Chart.yaml | 13 + .../intel-device-plugins-qat/0.31.1/LICENSE | 14 + .../intel-device-plugins-qat/0.31.1/README.md | 50 + .../0.31.1/questions.yaml | 6 + .../0.31.1/templates/NOTES.txt | 1 + .../0.31.1/templates/qat.yaml | 53 + .../0.31.1/values.yaml | 23 + .../0.31.1/.helmignore | 23 + .../0.31.1/Chart.yaml | 13 + .../intel-device-plugins-sgx/0.31.1/LICENSE | 14 + .../intel-device-plugins-sgx/0.31.1/README.md | 40 + .../0.31.1/questions.yaml | 6 + .../0.31.1/templates/sgx.yaml | 43 + .../0.31.1/values.yaml | 16 + .../2024.10.2/Chart.yaml | 1 - .../2024.10.3/.helmignore | 22 + .../2024.10.3/Chart.lock | 6 + .../2024.10.3/Chart.yaml | 29 + .../linkerd-control-plane/2024.10.3/README.md | 321 + .../2024.10.3/README.md.gotmpl | 133 + .../2024.10.3/app-readme.md | 14 + .../2024.10.3/charts/partials/.helmignore | 21 + .../2024.10.3/charts/partials/Chart.yaml | 5 + .../2024.10.3/charts/partials/README.md | 9 + .../charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 271 + .../partials/templates/_pull-secrets.tpl | 6 + .../charts/partials/templates/_resources.tpl | 28 + .../partials/templates/_tolerations.tpl | 4 + .../charts/partials/templates/_trace.tpl | 5 + .../charts/partials/templates/_validate.tpl | 19 + .../charts/partials/templates/_volumes.tpl | 20 + .../2024.10.3/charts/partials/values.yaml | 0 .../2024.10.3/questions.yaml | 19 + .../2024.10.3/templates/NOTES.txt | 19 + .../2024.10.3/templates/config-rbac.yaml | 16 + .../2024.10.3/templates/config.yaml | 39 + .../2024.10.3/templates/destination-rbac.yaml | 327 + .../2024.10.3/templates/destination.yaml | 435 ++ .../2024.10.3/templates/heartbeat-rbac.yaml | 78 + .../2024.10.3/templates/heartbeat.yaml | 94 + .../2024.10.3/templates/identity-rbac.yaml | 49 + .../2024.10.3/templates/identity.yaml | 272 + .../2024.10.3/templates/namespace.yaml | 18 + .../2024.10.3/templates/podmonitor.yaml | 128 + .../templates/proxy-injector-rbac.yaml | 120 + .../2024.10.3/templates/proxy-injector.yaml | 222 + .../2024.10.3/templates/psp.yaml | 119 + .../2024.10.3/values-ha.yaml | 63 + .../2024.10.3/values.yaml | 664 ++ .../linkerd-crds/2024.10.3/.helmignore | 22 + .../linkerd/linkerd-crds/2024.10.3/Chart.lock | 6 + .../linkerd/linkerd-crds/2024.10.3/Chart.yaml | 26 + .../linkerd/linkerd-crds/2024.10.3/README.md | 71 + .../linkerd-crds/2024.10.3/README.md.gotmpl | 59 + .../linkerd-crds/2024.10.3/app-readme.md | 9 + .../2024.10.3/charts/partials/.helmignore | 21 + .../2024.10.3/charts/partials/Chart.yaml | 5 + .../2024.10.3/charts/partials/README.md | 9 + .../charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 271 + .../partials/templates/_pull-secrets.tpl | 6 + .../charts/partials/templates/_resources.tpl | 28 + .../partials/templates/_tolerations.tpl | 4 + .../charts/partials/templates/_trace.tpl | 5 + .../charts/partials/templates/_validate.tpl | 19 + .../charts/partials/templates/_volumes.tpl | 20 + .../2024.10.3/charts/partials/values.yaml | 0 .../2024.10.3/templates/NOTES.txt | 6 + .../gateway.networking.k8s.io_grpcroutes.yaml | 1507 +++++ .../gateway.networking.k8s.io_httproutes.yaml | 3881 ++++++++++++ .../policy/authorization-policy.yaml | 99 + .../2024.10.3/templates/policy/httproute.yaml | 5328 +++++++++++++++++ .../policy/meshtls-authentication.yaml | 87 + .../policy/network-authentication.yaml | 53 + .../policy/server-authorization.yaml | 266 + .../2024.10.3/templates/policy/server.yaml | 319 + .../2024.10.3/templates/serviceprofile.yaml | 274 + .../templates/workload/external-workload.yaml | 303 + .../linkerd-crds/2024.10.3/values.yaml | 1 + .../speedscale-operator/2.2.556/.helmignore | 23 + .../speedscale-operator/2.2.556/Chart.yaml | 27 + .../speedscale-operator/2.2.556/LICENSE | 201 + .../speedscale-operator/2.2.556/README.md | 111 + .../speedscale-operator/2.2.556/app-readme.md | 111 + .../2.2.556/questions.yaml | 9 + .../2.2.556/templates/NOTES.txt | 12 + .../2.2.556/templates/admission.yaml | 209 + .../2.2.556/templates/configmap.yaml | 43 + .../templates/crds/trafficreplays.yaml | 525 ++ .../2.2.556/templates/deployments.yaml | 132 + .../2.2.556/templates/hooks.yaml | 73 + .../2.2.556/templates/rbac.yaml | 244 + .../2.2.556/templates/secrets.yaml | 18 + .../2.2.556/templates/services.yaml | 22 + .../2.2.556/templates/tls.yaml | 183 + .../speedscale-operator/2.2.556/values.yaml | 138 + index.yaml | 180 +- 219 files changed, 27824 insertions(+), 3 deletions(-) create mode 100644 assets/codefresh/cf-runtime-6.4.7.tgz create mode 100644 assets/intel/intel-device-plugins-operator-0.31.1.tgz create mode 100644 assets/intel/intel-device-plugins-qat-0.31.1.tgz create mode 100644 assets/intel/intel-device-plugins-sgx-0.31.1.tgz create mode 100644 assets/linkerd/linkerd-control-plane-2024.10.3.tgz create mode 100644 assets/linkerd/linkerd-crds-2024.10.3.tgz create mode 100644 assets/speedscale/speedscale-operator-2.2.556.tgz create mode 100644 charts/codefresh/cf-runtime/6.4.7/.helmignore create mode 100644 charts/codefresh/cf-runtime/6.4.7/Chart.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/README.md create mode 100644 charts/codefresh/cf-runtime/6.4.7/README.md.gotmpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/files/cleanup-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.7/files/configure-dind-certs.sh create mode 100644 charts/codefresh/cf-runtime/6.4.7/files/init-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.7/files/reconcile-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_ingress.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_serviceMontor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_init-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_main-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_sidecar-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/ingress.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/extra/extra-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/extra/runtime-images-cm.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/cm-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/rbac-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/job-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/rbac-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/monitor/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/monitor/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/monitor/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/other/external-secrets.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/other/podMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/other/serviceMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/cm-dind-daemon.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/runtime-env-spec-tmpl.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/runtime/svc-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.7/values.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/.helmignore create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/Chart.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/LICENSE create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/README.md create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dlbdeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dsadeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_fpgadeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_gpudeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_iaadeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_qatdeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_sgxdeviceplugins.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_acceleratorfunctions.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_fpgaregions.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/templates/NOTES.txt create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/templates/operator.yaml create mode 100644 charts/intel/intel-device-plugins-operator/0.31.1/values.yaml create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/.helmignore create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/Chart.yaml create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/LICENSE create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/README.md create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/questions.yaml create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/templates/NOTES.txt create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/templates/qat.yaml create mode 100644 charts/intel/intel-device-plugins-qat/0.31.1/values.yaml create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/.helmignore create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/Chart.yaml create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/LICENSE create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/README.md create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/questions.yaml create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/templates/sgx.yaml create mode 100644 charts/intel/intel-device-plugins-sgx/0.31.1/values.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/.helmignore create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/Chart.lock create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/Chart.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/README.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/app-readme.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/.helmignore create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/Chart.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_affinity.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_capabilities.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_debug.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_helpers.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_metadata.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_network-validator.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_resources.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_tolerations.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_trace.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_validate.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_volumes.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/values.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/questions.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/config-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/config.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/namespace.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/podmonitor.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/templates/psp.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/values-ha.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.10.3/values.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/.helmignore create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/Chart.lock create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/Chart.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/README.md create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/app-readme.md create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/.helmignore create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/Chart.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_affinity.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_capabilities.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_debug.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_helpers.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_metadata.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_network-validator.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_resources.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_tolerations.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_trace.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_validate.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_volumes.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/charts/partials/values.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_grpcroutes.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_httproutes.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/authorization-policy.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/httproute.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/meshtls-authentication.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/network-authentication.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/server-authorization.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/policy/server.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/serviceprofile.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/templates/workload/external-workload.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.10.3/values.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/.helmignore create mode 100644 charts/speedscale/speedscale-operator/2.2.556/Chart.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/LICENSE create mode 100644 charts/speedscale/speedscale-operator/2.2.556/README.md create mode 100644 charts/speedscale/speedscale-operator/2.2.556/app-readme.md create mode 100644 charts/speedscale/speedscale-operator/2.2.556/questions.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/NOTES.txt create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/admission.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/configmap.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/crds/trafficreplays.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/deployments.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/hooks.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/rbac.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/secrets.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/services.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/templates/tls.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.556/values.yaml diff --git a/assets/codefresh/cf-runtime-6.4.7.tgz b/assets/codefresh/cf-runtime-6.4.7.tgz new file mode 100644 index 0000000000000000000000000000000000000000..877079900530b62c062d8980b3c6822088afad38 GIT binary patch literal 43706 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/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 Ts_EYW00960?S`^P05SjoGOh75 literal 0 HcmV?d00001 diff --git a/assets/intel/intel-device-plugins-sgx-0.31.1.tgz b/assets/intel/intel-device-plugins-sgx-0.31.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e13ba3922c4e52939256f679b8d2b4588ab976e6 GIT binary patch literal 2174 zcmV-^2!Zz>iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PI=)ZW}ie&)L=-g$68<9S3*<>X{lvWn2Z4A)KV_XIH%TfjG0Vw)Rs*c78xk2tw8-rv4R=J z=^_%e!VHGas$lfpdpP!%1wVMp`ee_`dW_KwwP8|Z@bn*l@5lYWM(NW}3Txy@4K~AU zGG0Ozd)I254G2|g`8SPs`*EKXXmZVz^=*gnJ@N{2XYw$SdgJ_A?!+gIW0oY;7_=sF zZ6?_?l~{?AxFx>(#0u|OgiTG^WW(IGsdF(C?>2 zX*pxgL_2L(KM8=9qz#`qCTvmC;83w^%7oEgE+M4}*bw{C}`}aIoh82m9$@>;I2I z*VhpwFUXi0EIA$~l|VNq_^BbTQ(?tp8yRHoTRP7s>4{S&6_aGQr_< zStYYRxn!cqU{uaSm~EnLg_ai7Qil%#pRTVVeis6gYk=EZ0?(~2(zJ8ZZC z!9;<=!k+7E6y_d$JiI}#quw{g(|QcIw@pwt<-GBX>?1)-OSpjoC$#1k;*c~SF4GOT zz3mZzRh;Z7=`zBi)&aC5yvhpVci7sMY+HlO3gm)2@a0~1IB?T$;M!&ZbWwMue}}QL zJBgx*Twep50RFx*atFFSay#j+*@#IIUw&gE!%80sW_`-ll(trLx~k$6R?}dc@+TOK zaC!8)=IAqCfT$7g1=BR;yyx2iI2|Kzq#X4=CtCF~=(**zN^ovb6j+?0k+sgTqt7Ah zoj;(;GwA&@S9NSKuQjs^H<=h!-s-BT&8N+xJ9;8$rj&u}ovWG^OJ>vSWUe$C($$a587cn%i~mo1+c|cHY~nw|^hrGaLnr>TZJ3sc`)L1~nMm#wjcI;aD}Spugp<{ewMM+Wx|7$X-z+@>_@@T7|{58%`_G;UbGdtF-PF^3Zw>F zYQ(}LsDLZYtVcW}C-#bJBn2~8vvF-#HmIEyGtd=~0;quA;RuGK9(;Q^8jfC)pN8k( zzy0wXemXomJ3Kufo{Zq_863SmJszG9-=2=(?Q1wZ{Tco>JUxC1$m|p~V91Kx1ex19 zEMhW3Tp4^K!(?1u%qA=cPDNSM5}}kc)WQb<)D;Wal}B;z?jKFs|Iu>duL1S8)Y3-$ zKRY=*{`W~-6%QZdKKwt}{|Ntg54P_=k3u_}-a5jQh%aamvaIDqN zM3Re(3*znypixPnMG@}Z9%N0Hgd&Q|&ZhO)U6Gw^Mf?D+2JecQH0M0S5 zEy9HtGe~tl4booeg$oH46&`pEBB+{kEW$gEfnTJ5-l6>0*sowHjHR4+7q*-`Yj}5+ zg2oxbNE@Gb;Fk%frTNus+S!30RjFx#>+0*K`9bQve$mc!eFU*_3Cqq${_b42aUBtU zIjW7775l@%yaREs6~4uZy9P8H#zgt+v-ULIV6mAdh0Oh5J?X z!#PJ|uPJ_GU7e`=sWg`z-_Gc-PA(xPC&xY`NFgxn~qX@wT` zP0$24{RD$Gn2b)a?zIan#!tI+j)oY{WL|fsq3@T*T>I&vh1HI zuy_ffc<5u7n{d$Tpv*%OoW4F@4(RK>ac`TPZfQ$f+EPRR4*&rF|FAhYQ~)Rd03D7! Ag8%>k literal 0 HcmV?d00001 diff --git a/assets/linkerd/linkerd-control-plane-2024.10.2.tgz b/assets/linkerd/linkerd-control-plane-2024.10.2.tgz index 9431464c5940bbeb19d8aa8c23927a48efb4ad17..133a67cca0575a6164ab5a17039b24eaadcce3cf 100644 GIT binary patch delta 27033 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 IeE|Fd0N|~>;P`{Y7m{!hI- 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

CSs(1WavV-@z^v` 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 diff --git a/assets/linkerd/linkerd-control-plane-2024.10.3.tgz b/assets/linkerd/linkerd-control-plane-2024.10.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a9b64c6c0ae3d2baf17ab62da71c445b507db465 GIT binary patch literal 31590 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 Nv`LO;1AT%3{U2wx(+&Ut literal 0 HcmV?d00001 diff --git a/assets/linkerd/linkerd-crds-2024.10.3.tgz b/assets/linkerd/linkerd-crds-2024.10.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..26e55c85cff4e2e98fa8e3ef66957d783887f85c GIT binary patch literal 113070 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ⅅM+djY&>?e4Whi;10Mp48Vr?YH58m?@FE@Vg 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.18-0' + catalog.cattle.io/release-name: cf-runtime +apiVersion: v2 +dependencies: +- name: cf-common + repository: oci://quay.io/codefresh/charts + version: 0.16.0 +description: A Helm chart for Codefresh Runner +home: https://codefresh.io/ +icon: file://assets/icons/cf-runtime.png +keywords: +- codefresh +- runner +kubeVersion: '>=1.18-0' +maintainers: +- name: codefresh + url: https://codefresh-io.github.io/ +name: cf-runtime +sources: +- https://github.com/codefresh-io/venona +version: 6.4.7 diff --git a/charts/codefresh/cf-runtime/6.4.7/README.md b/charts/codefresh/cf-runtime/6.4.7/README.md new file mode 100644 index 0000000000..ac4b585dae --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/README.md @@ -0,0 +1,1230 @@ +## Codefresh Runner + +![Version: 6.4.7](https://img.shields.io/badge/Version-6.4.7-informational?style=flat-square) + +Helm chart for deploying [Codefresh Runner](https://codefresh.io/docs/docs/installation/codefresh-runner/) to Kubernetes. + +## Table of Content + +- [Prerequisites](#prerequisites) +- [Get Chart Info](#get-chart-info) +- [Install Chart](#install-chart) +- [Chart Configuration](#chart-configuration) +- [Upgrade Chart](#upgrade-chart) + - [To 2.x](#to-2-x) + - [To 3.x](#to-3-x) + - [To 4.x](#to-4-x) + - [To 5.x](#to-5-x) + - [To 6.x](#to-6-x) +- [Architecture](#architecture) +- [Configuration](#configuration) + - [EBS backend volume configuration in AWS](#ebs-backend-volume-configuration) + - [Azure Disks backend volume configuration in AKS](#azure-disks-backend-volume-configuration) + - [GCE Disks backend volume configuration in GKE](#gce-disks-backend-volume-configuration-in-gke) + - [Custom volume mounts](#custom-volume-mounts) + - [Custom global environment variables](#custom-global-environment-variables) + - [Volume reuse policy](#volume-reuse-policy) + - [Volume cleaners](#volume-cleaners) + - [Rootless DinD](#rootless-dind) + - [ARM](#arm) + - [Openshift](#openshift) + - [On-premise](#on-premise) + +## Prerequisites + +- Kubernetes **1.19+** +- Helm **3.8.0+** + +⚠️⚠️⚠️ +> Since version 6.2.x chart is pushed **only** to OCI registry at `oci://quay.io/codefresh/cf-runtime` + +> Versions prior to 6.2.x are still available in ChartMuseum at `http://chartmuseum.codefresh.io/cf-runtime` + +## Get Chart Info + +```console +helm show all oci://quay.io/codefresh/cf-runtime +``` +See [Use OCI-based registries](https://helm.sh/docs/topics/registries/) + +## Install Chart + +**Important:** only helm3 is supported + +- Specify the following mandatory values + +`values.yaml` +```yaml +# -- Global parameters +# @default -- See below +global: + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used '{{ .Values.global.context }}_{{ .Release.Namespace }}' + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used '{{ .Values.global.context }}/{{ .Release.Namespace }}' + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace +``` + +- Install chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace codefresh +``` + +## Chart Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +## Upgrade Chart + +### To 2.x + +This major release renames and deprecated several values in the chart. Most of the workload templates have been refactored. + +Affected values: +- `dockerRegistry` is deprecated. Replaced with `global.imageRegistry` +- `re` is renamed to `runtime` +- `storage.localVolumeMonitor` is replaced with `volumeProvisioner.dind-lv-monitor` +- `volumeProvisioner.volume-cleanup` is replaced with `volumeProvisioner.dind-volume-cleanup` +- `image` values structure has been updated. Split to `image.registry` `image.repository` `image.tag` +- pod's `annotations` is renamed to `podAnnotations` + +### To 3.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release adds [runtime-environment](https://codefresh.io/docs/docs/installation/codefresh-runner/#runtime-environment-specification) spec into chart templates. +That means it is possible to set parametes for `dind` and `engine` pods via [values.yaml](./values.yaml). + +**If you had any overrides (i.e. tolerations/nodeSelector/environment variables/etc) added in runtime spec via [codefresh CLI](https://codefresh-io.github.io/cli/) (for example, you did use [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands to modify the runtime-environment), you MUST add these into chart's [values.yaml](./values.yaml) for `.Values.runtime.dind` or(and) .`Values.runtime.engine`** + +**For backward compatibility, you can disable updating runtime-environment spec via** `.Values.runtime.patch.enabled=false` + +Affected values: +- added **mandatory** `global.codefreshToken`/`global.codefreshTokenSecretKeyRef` **You must specify it before the upgrade!** +- `runtime.engine` is added +- `runtime.dind` is added +- `global.existingAgentToken` is replaced with `global.agentTokenSecretKeyRef` +- `global.existingDindCertsSecret` is replaced with `global.dindCertsSecretRef` + +### To 4.x + +This major release adds **agentless inCluster** runtime mode (relevant only for [Codefresh On-Premises](#on-premise) users) + +Affected values: +- `runtime.agent` / `runtime.inCluster` / `runtime.accounts` / `runtime.description` are added + +### To 5.x + +This major release converts `.runtime.dind.pvcs` from **list** to **dict** + +> 4.x chart's values example: +```yaml +runtime: + dind: + pvcs: + - name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +> 5.x chart's values example: +```yaml +runtime: + dind: + pvcs: + dind: + name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +Affected values: +- `.runtime.dind.pvcs` converted from **list** to **dict** + +### To 6.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release deprecates previously required `codefresh runner init --generate-helm-values-file`. + +Affected values: +- **Replaced** `.monitor.clusterId` with `.global.context` as **mandatory** value! +- **Deprecated** `.global.agentToken` / `.global.agentTokenSecretKeyRef` +- **Removed** `.global.agentId` +- **Removed** `.global.keys` / `.global.dindCertsSecretRef` +- **Removed** `.global.existingAgentToken` / `existingDindCertsSecret` +- **Removed** `.monitor.clusterId` / `.monitor.token` / `.monitor.existingMonitorToken` + +#### Migrate the Helm chart from version 5.x to 6.x + +Given this is the legacy `generated_values.yaml` values: + +> legacy `generated_values.yaml` +```yaml +{ + "appProxy": { + "enabled": false, + }, + "monitor": { + "enabled": false, + "clusterId": "my-cluster-name", + "token": "1234567890" + }, + "global": { + "namespace": "namespace", + "codefreshHost": "https://g.codefresh.io", + "agentToken": "0987654321", + "agentId": "agent-id-here", + "agentName": "my-cluster-name_my-namespace", + "accountId": "my-account-id", + "runtimeName": "my-cluster-name/my-namespace", + "codefreshToken": "1234567890", + "keys": { + "key": "-----BEGIN RSA PRIVATE KEY-----...", + "csr": "-----BEGIN CERTIFICATE REQUEST-----...", + "ca": "-----BEGIN CERTIFICATE-----...", + "serverCert": "-----BEGIN CERTIFICATE-----..." + } + } +} +``` + +Update `values.yaml` for new chart version: + +> For existing installation for backward compatibility `.Values.global.agentToken/agentTokenSecretKeyRef` **must be provided!** For installation from scratch this value is no longer required. + +> updated `values.yaml` +```yaml +global: + codefreshToken: "1234567890" + accountId: "my-account-id" + context: "my-cluster-name" + agentToken: "0987654321" # MANDATORY when migrating from < 6.x chart version ! + agentName: "my-cluster-name_my-namespace" # optional + runtimeName: "my-cluster-name/my-namespace" # optional +``` + +> **Note!** Though it's still possible to update runtime-environment via [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands, it's recommended to enable sidecar container to pull runtime spec from Codefresh API to detect any drift in configuration. + +```yaml +runner: + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: true +``` + +## Architecture + +[Codefresh Runner architecture](https://codefresh.io/docs/docs/installation/codefresh-runner/#codefresh-runner-architecture) + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +### EBS backend volume configuration + +`dind-volume-provisioner` should have permissions to create/attach/detach/delete/get EBS volumes + +Minimal IAM policy for `dind-volume-provisioner` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AttachVolume", + "ec2:CreateSnapshot", + "ec2:CreateTags", + "ec2:CreateVolume", + "ec2:DeleteSnapshot", + "ec2:DeleteTags", + "ec2:DeleteVolume", + "ec2:DescribeInstances", + "ec2:DescribeSnapshots", + "ec2:DescribeTags", + "ec2:DescribeVolumes", + "ec2:DetachVolume" + ], + "Resource": "*" + } + ] +} +``` + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM role + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] +``` + +2. Pass static credentials in `.Values.storage.ebs.accessKeyId/accessKeyIdSecretKeyRef` and `.Values.storage.ebs.secretAccessKey/secretAccessKeySecretKeyRef` + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" +``` + +### Custom volume mounts + +You can add your own volumes and volume mounts in the runtime environment, so that all pipeline steps will have access to the same set of external files. + +```yaml +runtime: + dind: + userVolumes: + regctl-docker-registry: + name: regctl-docker-registry + secret: + items: + - key: .dockerconfigjson + path: config.json + secretName: regctl-docker-registry + optional: true + userVolumeMounts: + regctl-docker-registry: + name: regctl-docker-registry + mountPath: /home/appuser/.docker/ + readOnly: true + +``` + +### Azure Disks backend volume configuration + +`dind-volume-provisioner` should have permissions to create/delete/get Azure Disks + +Role definition for `dind-volume-provisioner` + +`dind-volume-provisioner-role.json` +```json +{ + "Name": "CodefreshDindVolumeProvisioner", + "Description": "Perform create/delete/get disks", + "IsCustom": true, + "Actions": [ + "Microsoft.Compute/disks/read", + "Microsoft.Compute/disks/write", + "Microsoft.Compute/disks/delete" + + ], + "AssignableScopes": ["/subscriptions/"] +} +``` + +When creating an AKS cluster in Azure there is the option to use a [managed identity](https://learn.microsoft.com/en-us/azure/aks/use-managed-identity) that is assigned to the kubelet. This identity is assigned to the underlying node pool in the AKS cluster and can then be used by the dind-volume-provisioner. + +```console +export ROLE_DEFINITIN_FILE=dind-volume-provisioner-role.json +export SUBSCRIPTION_ID=$(az account show --query "id" | xargs echo ) +export RESOURCE_GROUP= +export AKS_NAME= +export LOCATION=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query location | xargs echo) +export NODES_RESOURCE_GROUP=MC_${RESOURCE_GROUP}_${AKS_NAME}_${LOCATION} +export NODE_SERVICE_PRINCIPAL=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query identityProfile.kubeletidentity.objectId | xargs echo) + +az role definition create --role-definition @${ROLE_DEFINITIN_FILE} +az role assignment create --assignee $NODE_SERVICE_PRINCIPAL --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$NODES_RESOURCE_GROUP --role CodefreshDindVolumeProvisioner +``` + +Deploy Helm chart with the following values: + +`values.yaml` +```yaml +volumeProvisioner: + podSecurityContext: + enabled: true + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 + +storage: + backend: azuredisk + azuredisk: + availabilityZone: northeurope-1 # replace with your zone + resourceGroup: my-resource-group-name + + mountAzureJson: true + +runtime: + dind: + nodeSelector: + topology.kubernetes.io/zone: northeurope-1 +``` + +### GCE Disks backend volume configuration in GKE + +`dind-volume-provisioner` should have `ComputeEngine.StorageAdmin` permissions + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM Service Account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +2. Pass static credentials in `.Values.storage.gcedisk.serviceAccountJson` (inline) or `.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef` (from your own secret) + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: | + { + "type": "service_account", + "project_id": "...", + "private_key_id": "...", + "private_key": "...", + "client_email": "...", + "client_id": "...", + "auth_uri": "...", + "token_uri": "...", + "auth_provider_x509_cert_url": "...", + "client_x509_cert_url": "..." + } + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g.: + # serviceAccountJsonSecretKeyRef: + # name: gce-service-account + # key: service-account.json + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + iam.gke.io/gcp-service-account: @.iam.gserviceaccount.com + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +### Custom global environment variables + +You can add your own environment variables to the runtime environment. All pipeline steps have access to the global variables. + +```yaml +runtime: + engine: + userEnvVars: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token +``` + +### Volume reuse policy + +Volume reuse behavior depends on the configuration for `reuseVolumeSelector` in the runtime environment spec. + +```yaml +runtime: + dind: + pvcs: + - name: dind + ... + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +The following options are available: +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName'` - PV can be used by ANY pipeline in the specified account (default). +Benefit: Fewer PVs, resulting in lower costs. Since any PV can be used by any pipeline, the cluster needs to maintain/reserve fewer PVs in its PV pool for Codefresh. +Downside: Since the PV can be used by any pipeline, the PVs could have assets and info from different pipelines, reducing the probability of cache. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,project_id'` - PV can be used by ALL pipelines in your account, assigned to the same project. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id'` - PV can be used only by a single pipeline. +Benefit: More probability of cache without “spam” from other pipelines. +Downside: More PVs to maintain and therefore higher costs. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,io.codefresh.branch_name'` - PV can be used only by single pipeline AND single branch. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,trigger'` - PV can be used only by single pipeline AND single trigger. + +### Volume cleaners + +Codefresh pipelines require disk space for: + * [Pipeline Shared Volume](https://codefresh.io/docs/docs/pipelines/introduction-to-codefresh-pipelines/#sharing-the-workspace-between-build-steps) (`/codefresh/volume`, implemented as [docker volume](https://docs.docker.com/storage/volumes/)) + * Docker containers, both running and stopped + * Docker images and cached layers + +Codefresh offers two options to manage disk space and prevent out-of-space errors: +* Use runtime cleaners on Docker images and volumes +* [Set the minimum disk space per pipeline build volume](https://codefresh.io/docs/docs/pipelines/pipelines/#set-minimum-disk-space-for-a-pipeline-build) + +To improve performance by using Docker cache, Codefresh `volume-provisioner` can provision previously used disks with Docker images and pipeline volumes from previously run builds. + +### Types of runtime volume cleaners + +Docker images and volumes must be cleaned on a regular basis. + +* [IN-DIND cleaner](https://github.com/codefresh-io/dind/tree/master/cleaner): Deletes extra Docker containers, volumes, and images in **DIND pod**. +* [External volume cleaner](https://github.com/codefresh-io/dind-volume-cleanup): Deletes unused **external** PVs (EBS, GCE/Azure disks). +* [Local volume cleaner](https://github.com/codefresh-io/dind-volume-utils/blob/master/local-volumes/lv-cleaner.sh): Deletes **local** volumes if node disk space is close to the threshold. + +### IN-DIND cleaner + +**Purpose:** Removes unneeded *docker containers, images, volumes* inside Kubernetes volume mounted on the DIND pod + +**How it runs:** Inside each DIND pod as script + +**Triggered by:** SIGTERM and also during the run when disk usage > 90% (configurable) + +**Configured by:** Environment Variables which can be set in Runtime Environment spec + +**Configuration/Logic:** [README.md](https://github.com/codefresh-io/dind/tree/master/cleaner#readme) + +Override `.Values.runtime.dind.env` if necessary (the following are **defaults**): + +```yaml +runtime: + dind: + env: + CLEAN_PERIOD_SECONDS: '21600' # launch clean if last clean was more than CLEAN_PERIOD_SECONDS seconds ago + CLEAN_PERIOD_BUILDS: '5' # launch clean if last clean was more CLEAN_PERIOD_BUILDS builds since last build + IMAGE_RETAIN_PERIOD: '14400' # do not delete docker images if they have events since current_timestamp - IMAGE_RETAIN_PERIOD + VOLUMES_RETAIN_PERIOD: '14400' # do not delete docker volumes if they have events since current_timestamp - VOLUMES_RETAIN_PERIOD + DISK_USAGE_THRESHOLD: '0.8' # launch clean based on current disk usage DISK_USAGE_THRESHOLD + INODES_USAGE_THRESHOLD: '0.8' # launch clean based on current inodes usage INODES_USAGE_THRESHOLD +``` + +### External volumes cleaner + +**Purpose:** Removes unused *kubernetes volumes and related backend volumes* + +**How it runs:** Runs as `dind-volume-cleanup` CronJob. Installed in case the Runner uses non-local volumes `.Values.storage.backend != local` + +**Triggered by:** CronJob every 10min (configurable) + +**Configuration:** + +Set `codefresh.io/volume-retention` for dinds' PVCs: + +```yaml +runtime: + dind: + pvcs: + dind: + ... + annotations: + codefresh.io/volume-retention: 7d +``` + +Or override environment variables for `dind-volume-cleanup` cronjob: + +```yaml +volumeProvisioner: + dind-volume-cleanup: + env: + RETENTION_DAYS: 7 # clean volumes that were last used more than `RETENTION_DAYS` (default is 4) ago +``` + +### Local volumes cleaner + +**Purpose:** Deletes local volumes when node disk space is close to the threshold + +**How it runs:** Runs as `dind-lv-monitor` DaemonSet. Installed in case the Runner uses local volumes `.Values.storage.backend == local` + +**Triggered by:** Disk space usage or inode usage that exceeds thresholds (configurable) + +**Configuration:** + +Override environment variables for `dind-lv-monitor` daemonset: + +```yaml +volumeProvisioner: + dind-lv-monitor: + env: + KB_USAGE_THRESHOLD: 60 # default 80 (percentage) + INODE_USAGE_THRESHOLD: 60 # default 80 +``` + +### Rootless DinD + +DinD pod runs a `priviliged` container with **rootfull** docker. +To run the docker daemon as non-root user (**rootless** mode), change dind image tag: + +`values.yaml` +```yaml +runtime: + dind: + image: + tag: rootless +``` + +### ARM + +With the Codefresh Runner, you can run native ARM64v8 builds. + +> **Note!** +> You cannot run both amd64 and arm64 images within the same pipeline. As one pipeline can map only to one runtime, you can run either amd64 or arm64 within the same pipeline. + +Provide `nodeSelector` and(or) `tolerations` for dind pods: + +`values.yaml` +```yaml +runtime: + dind: + nodeSelector: + arch: arm64 + tolerations: + - key: arch + operator: Equal + value: arm64 + effect: NoSchedule +``` + +### Openshift + +To install Codefresh Runner on OpenShift use the following `values.yaml` example + +```yaml +runner: + podSecurityContext: + enabled: false + +volumeProvisioner: + podSecurityContext: + enabled: false + env: + PRIVILEGED_CONTAINER: true + dind-lv-monitor: + containerSecurityContext: + enabled: true + privileged: true + volumePermissions: + enabled: true + securityContext: + privileged: true + runAsUser: auto +``` + +Grant `privileged` SCC to `cf-runtime-runner` and `cf-runtime-volume-provisioner` service accounts. + +```console +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-runner + +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-volume-provisioner +``` + +### On-premise + +If you have [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) deployed, you can install Codefresh Runner in **agentless** mode. + +**What is agentless mode?** + +Agent (aka venona) is Runner component which responsible for calling Codefresh API to run builds and create dind/engine pods and pvc objects. Agent can only be assigned to a single account, thus you can't share one runtime across multiple accounts. However, with **agentless** mode it's possible to register the runtime as **system**-type runtime so it's registered on the platform level and can be assigned/shared across multiple accounts. + +**What are the prerequisites?** +- You have a running [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) control-plane environment +- You have a Codefresh API token with platform **Admin** permissions scope + +### How to deploy agentless runtime when it's on the SAME k8s cluster as On-Premises control-plane environment? + +- Enable cluster-level permissions for cf-api (On-Premises control-plane component) + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) Helm chart +```yaml +cfapi: + ... + # -- Enable ClusterRole/ClusterRoleBinding + rbac: + namespaced: false +``` + +- Set the following values for Runner Helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=true` + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + runtimeName: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: true + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to check the runtime. Assign it to the required account(s). Run test pipeline on it. + +### How to deploy agentless runtime when it's on the DIFFERENT k8s cluster than On-Premises control-plane environment? + +In this case, it's required to mount runtime cluster's `KUBECONFIG` into On-Premises `cf-api` deployment + +- Create the neccessary RBAC resources + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +extraResources: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: codefresh-role + namespace: '{{ .Release.Namespace }}' + rules: + - apiGroups: [""] + resources: ["pods", "persistentvolumeclaims", "persistentvolumes"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] +- apiVersion: v1 + kind: ServiceAccount + metadata: + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: codefresh-role + subjects: + - kind: ServiceAccount + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' +- apiVersion: v1 + kind: Secret + metadata: + name: codefresh-runtime-user-token + namespace: '{{ .Release.Namespace }}' + annotations: + kubernetes.io/service-account.name: codefresh-runtime-user + type: kubernetes.io/service-account-token +``` + +- Set up the following environment variables to create a `KUBECONFIG` file + +```shell +NAMESPACE=cf-runtime +CLUSTER_NAME=prod-ue1-some-cluster-name +CURRENT_CONTEXT=$(kubectl config current-context) + +USER_TOKEN_VALUE=$(kubectl -n cf-runtime get secret/codefresh-runtime-user-token -o=go-template='{{.data.token}}' | base64 --decode) +CURRENT_CLUSTER=$(kubectl config view --raw -o=go-template='{{range .contexts}}{{if eq .name "'''${CURRENT_CONTEXT}'''"}}{{ index .context "cluster" }}{{end}}{{end}}') +CLUSTER_CA=$(kubectl config view --raw -o=go-template='{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}"{{with index .cluster "certificate-authority-data" }}{{.}}{{end}}"{{ end }}{{ end }}') +CLUSTER_SERVER=$(kubectl config view --raw -o=go-template='{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}{{ .cluster.server }}{{end}}{{ end }}') + +export -p USER_TOKEN_VALUE CURRENT_CONTEXT CURRENT_CLUSTER CLUSTER_CA CLUSTER_SERVER CLUSTER_NAME +``` + +- Create a kubeconfig file + +```console +cat << EOF > $CLUSTER_NAME-kubeconfig +apiVersion: v1 +kind: Config +current-context: ${CLUSTER_NAME} +contexts: +- name: ${CLUSTER_NAME} + context: + cluster: ${CLUSTER_NAME} + user: codefresh-runtime-user + namespace: ${NAMESPACE} +clusters: +- name: ${CLUSTER_NAME} + cluster: + certificate-authority-data: ${CLUSTER_CA} + server: ${CLUSTER_SERVER} +users: +- name: ${CLUSTER_NAME} + user: + token: ${USER_TOKEN_VALUE} +EOF +``` + +- **Switch context to On-Premises control-plane cluster**. Create k8s secret (via any tool like [ESO](https://external-secrets.io/v0.4.4/), `kubectl`, etc ) containing runtime cluster's `KUBECONFG` created in previous step. + +```shell +NAMESPACE=codefresh +kubectl create secret generic dind-runtime-clusters --from-file=$CLUSTER_NAME=$CLUSTER_NAME-kubeconfig -n $NAMESPACE +``` + +- Mount secret containing runtime cluster's `KUBECONFG` into cf-api in On-Premises control-plane cluster + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) helm chart +```yaml +cf-api: + ... + volumes: + dind-clusters: + enabled: true + type: secret + nameOverride: dind-runtime-clusters + optional: true +``` +> volumeMount `/etc/kubeconfig` is already configured in cf-api Helm chart template. No need to specify it. + +- Set the following values for Runner helm chart + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=false` + +**Important!** +`.Values.global.name` ("system/" prefix is ignored!) should match the cluster name (key in `dind-runtime-clusters` secret created previously) +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + name: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: false + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- (optional) Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to see the runtime. Assign it to the required account(s). + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| oci://quay.io/codefresh/charts | cf-common | 0.16.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| appProxy.affinity | object | `{}` | Set affinity | +| appProxy.enabled | bool | `false` | Enable app-proxy | +| appProxy.env | object | `{}` | Add additional env vars | +| appProxy.image | object | `{"registry":"quay.io","repository":"codefresh/cf-app-proxy","tag":"0.0.47"}` | Set image | +| appProxy.ingress.annotations | object | `{}` | Set extra annotations for ingress object | +| appProxy.ingress.class | string | `""` | Set ingress class | +| appProxy.ingress.host | string | `""` | Set DNS hostname the ingress will use | +| appProxy.ingress.pathPrefix | string | `""` | Set path prefix for ingress (keep empty for default `/` path) | +| appProxy.ingress.tlsSecret | string | `""` | Set k8s tls secret for the ingress object | +| appProxy.nodeSelector | object | `{}` | Set node selector | +| appProxy.podAnnotations | object | `{}` | Set pod annotations | +| appProxy.podSecurityContext | object | `{}` | Set security context for the pod | +| appProxy.rbac | object | `{"create":true,"namespaced":true,"rules":[]}` | RBAC parameters | +| appProxy.rbac.create | bool | `true` | Create RBAC resources | +| appProxy.rbac.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| appProxy.rbac.rules | list | `[]` | Add custom rule to the role | +| appProxy.readinessProbe | object | See below | Readiness probe configuration | +| appProxy.replicasCount | int | `1` | Set number of pods | +| appProxy.resources | object | `{}` | Set requests and limits | +| appProxy.serviceAccount | object | `{"annotations":{},"create":true,"name":"","namespaced":true}` | Service Account parameters | +| appProxy.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| appProxy.serviceAccount.create | bool | `true` | Create service account | +| appProxy.serviceAccount.name | string | `""` | Override service account name | +| appProxy.serviceAccount.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| appProxy.tolerations | list | `[]` | Set tolerations | +| appProxy.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| dockerRegistry | string | `""` | | +| event-exporter | object | See below | Event exporter parameters | +| event-exporter.affinity | object | `{}` | Set affinity | +| event-exporter.enabled | bool | `false` | Enable event-exporter | +| event-exporter.env | object | `{}` | Add additional env vars | +| event-exporter.image | object | `{"registry":"docker.io","repository":"codefresh/k8s-event-exporter","tag":"latest"}` | Set image | +| event-exporter.nodeSelector | object | `{}` | Set node selector | +| event-exporter.podAnnotations | object | `{}` | Set pod annotations | +| event-exporter.podSecurityContext | object | See below | Set security context for the pod | +| event-exporter.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| event-exporter.rbac.create | bool | `true` | Create RBAC resources | +| event-exporter.rbac.rules | list | `[]` | Add custom rule to the role | +| event-exporter.replicasCount | int | `1` | Set number of pods | +| event-exporter.resources | object | `{}` | Set resources | +| event-exporter.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| event-exporter.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| event-exporter.serviceAccount.create | bool | `true` | Create service account | +| event-exporter.serviceAccount.name | string | `""` | Override service account name | +| event-exporter.tolerations | list | `[]` | Set tolerations | +| event-exporter.updateStrategy | object | `{"type":"Recreate"}` | Upgrade strategy | +| extraResources | list | `[]` | Array of extra objects to deploy with the release | +| fullnameOverride | string | `""` | String to fully override cf-runtime.fullname template | +| global | object | See below | Global parameters | +| global.accountId | string | `""` | Account ID (required!) Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information | +| global.agentName | string | `""` | Agent Name (optional!) If omitted, the following format will be used `{{ .Values.global.context }}_{{ .Release.Namespace }}` | +| global.agentToken | string | `""` | DEPRECATED Agent token in plain text. !!! MUST BE provided if migrating from < 6.x chart version | +| global.agentTokenSecretKeyRef | object | `{}` | DEPRECATED Agent token that references an existing secret containing API key. !!! MUST BE provided if migrating from < 6.x chart version | +| global.codefreshHost | string | `"https://g.codefresh.io"` | URL of Codefresh Platform (required!) | +| global.codefreshToken | string | `""` | User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) Ref: https://g.codefresh.io/user/settings (see API Keys) Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) | +| global.codefreshTokenSecretKeyRef | object | `{}` | User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) | +| global.context | string | `""` | K8s context name (required!) | +| global.imagePullSecrets | list | `[]` | Global Docker registry secret names as array | +| global.imageRegistry | string | `""` | Global Docker image registry | +| global.runtimeName | string | `""` | Runtime name (optional!) If omitted, the following format will be used `{{ .Values.global.context }}/{{ .Release.Namespace }}` | +| monitor.affinity | object | `{}` | Set affinity | +| monitor.enabled | bool | `false` | Enable monitor Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#install-monitoring-component | +| monitor.env | object | `{}` | Add additional env vars | +| monitor.image | object | `{"registry":"quay.io","repository":"codefresh/cf-k8s-agent","tag":"1.3.18"}` | Set image | +| monitor.nodeSelector | object | `{}` | Set node selector | +| monitor.podAnnotations | object | `{}` | Set pod annotations | +| monitor.podSecurityContext | object | `{}` | | +| monitor.rbac | object | `{"create":true,"namespaced":true,"rules":[]}` | RBAC parameters | +| monitor.rbac.create | bool | `true` | Create RBAC resources | +| monitor.rbac.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| monitor.rbac.rules | list | `[]` | Add custom rule to the role | +| monitor.readinessProbe | object | See below | Readiness probe configuration | +| monitor.replicasCount | int | `1` | Set number of pods | +| monitor.resources | object | `{}` | Set resources | +| monitor.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| monitor.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| monitor.serviceAccount.create | bool | `true` | Create service account | +| monitor.serviceAccount.name | string | `""` | Override service account name | +| monitor.tolerations | list | `[]` | Set tolerations | +| monitor.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| nameOverride | string | `""` | String to partially override cf-runtime.fullname template (will maintain the release name) | +| podMonitor | object | See below | Add podMonitor (for engine pods) | +| podMonitor.main.enabled | bool | `false` | Enable pod monitor for engine pods | +| podMonitor.runner.enabled | bool | `false` | Enable pod monitor for runner pod | +| podMonitor.volume-provisioner.enabled | bool | `false` | Enable pod monitor for volumeProvisioner pod | +| re | object | `{}` | | +| runner | object | See below | Runner parameters | +| runner.affinity | object | `{}` | Set affinity | +| runner.enabled | bool | `true` | Enable the runner | +| runner.env | object | `{}` | Add additional env vars | +| runner.image | object | `{"registry":"quay.io","repository":"codefresh/venona","tag":"1.10.2"}` | Set image | +| runner.init | object | `{"image":{"registry":"quay.io","repository":"codefresh/cli","tag":"0.85.0-rootless"},"resources":{"limits":{"cpu":"1","memory":"512Mi"},"requests":{"cpu":"0.2","memory":"256Mi"}}}` | Init container | +| runner.nodeSelector | object | `{}` | Set node selector | +| runner.podAnnotations | object | `{}` | Set pod annotations | +| runner.podSecurityContext | object | See below | Set security context for the pod | +| runner.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| runner.rbac.create | bool | `true` | Create RBAC resources | +| runner.rbac.rules | list | `[]` | Add custom rule to the role | +| runner.readinessProbe | object | See below | Readiness probe configuration | +| runner.replicasCount | int | `1` | Set number of pods | +| runner.resources | object | `{}` | Set requests and limits | +| runner.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| runner.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| runner.serviceAccount.create | bool | `true` | Create service account | +| runner.serviceAccount.name | string | `""` | Override service account name | +| runner.sidecar | object | `{"enabled":false,"env":{"RECONCILE_INTERVAL":300},"image":{"registry":"quay.io","repository":"codefresh/codefresh-shell","tag":"0.0.2"},"resources":{}}` | Sidecar container Reconciles runtime spec from Codefresh API for drift detection | +| runner.tolerations | list | `[]` | Set tolerations | +| runner.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| runtime | object | See below | Set runtime parameters | +| runtime.accounts | list | `[]` | (for On-Premise only) Assign accounts to runtime (list of account ids) | +| runtime.agent | bool | `true` | (for On-Premise only) Enable agent | +| runtime.description | string | `""` | Runtime description | +| runtime.dind | object | `{"affinity":{},"env":{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true},"image":{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.7"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"pvcs":{"dind":{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}},"resources":{"limits":{"cpu":"400m","memory":"800Mi"},"requests":null},"schedulerName":"","serviceAccount":"codefresh-engine","terminationGracePeriodSeconds":30,"tolerations":[],"userAccess":true,"userVolumeMounts":{},"userVolumes":{}}` | Parameters for DinD (docker-in-docker) pod (aka "runtime" pod). | +| runtime.dind.affinity | object | `{}` | Set affinity | +| runtime.dind.env | object | `{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true}` | Set additional env vars. | +| runtime.dind.image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.7"}` | Set dind image. | +| runtime.dind.nodeSelector | object | `{}` | Set node selector. | +| runtime.dind.podAnnotations | object | `{}` | Set pod annotations. | +| runtime.dind.podLabels | object | `{}` | Set pod labels. | +| runtime.dind.pvcs | object | `{"dind":{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}}` | PV claim spec parametes. | +| runtime.dind.pvcs.dind | object | `{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}` | Default dind PVC parameters | +| runtime.dind.pvcs.dind.annotations | object | `{}` | PV annotations. | +| runtime.dind.pvcs.dind.name | string | `"dind"` | PVC name prefix. Keep `dind` as default! Don't change! | +| runtime.dind.pvcs.dind.reuseVolumeSelector | string | `"codefresh-app,io.codefresh.accountName"` | PV reuse selector. Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#volume-reuse-policy | +| runtime.dind.pvcs.dind.storageClassName | string | `"{{ include \"dind-volume-provisioner.storageClassName\" . }}"` | PVC storage class name. Change ONLY if you need to use storage class NOT from Codefresh volume-provisioner | +| runtime.dind.pvcs.dind.volumeSize | string | `"16Gi"` | PVC size. | +| runtime.dind.resources | object | `{"limits":{"cpu":"400m","memory":"800Mi"},"requests":null}` | Set dind resources. | +| runtime.dind.schedulerName | string | `""` | Set scheduler name. | +| runtime.dind.serviceAccount | string | `"codefresh-engine"` | Set service account for pod. | +| runtime.dind.terminationGracePeriodSeconds | int | `30` | Set termination grace period. | +| runtime.dind.tolerations | list | `[]` | Set tolerations. | +| runtime.dind.userAccess | bool | `true` | Keep `true` as default! | +| runtime.dind.userVolumeMounts | object | `{}` | Add extra volume mounts | +| runtime.dind.userVolumes | object | `{}` | Add extra volumes | +| runtime.dindDaemon | object | See below | DinD pod daemon config | +| runtime.engine | object | `{"affinity":{},"command":["npm","run","start"],"env":{"CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS":1000,"DOCKER_REQUEST_TIMEOUT_MS":30000,"FORCE_COMPOSE_SERIAL_PULL":false,"LOGGER_LEVEL":"debug","LOG_OUTGOING_HTTP_REQUESTS":false,"METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS":false,"METRICS_PROMETHEUS_ENABLED":true,"METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS":false,"METRICS_PROMETHEUS_HOST":"0.0.0.0","METRICS_PROMETHEUS_PORT":9100},"image":{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.174.13"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"resources":{"limits":{"cpu":"1000m","memory":"2048Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"runtimeImages":{"COMPOSE_IMAGE":"quay.io/codefresh/compose:v2.28.1-1.5.0","CONTAINER_LOGGER_IMAGE":"quay.io/codefresh/cf-container-logger:1.11.7","COSIGN_IMAGE_SIGNER_IMAGE":"quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2","CR_6177_FIXER":"quay.io/codefresh/alpine:edge","DOCKER_BUILDER_IMAGE":"quay.io/codefresh/cf-docker-builder:1.3.14","DOCKER_PULLER_IMAGE":"quay.io/codefresh/cf-docker-puller:8.0.18","DOCKER_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-pusher:6.0.16","DOCKER_TAG_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-tag-pusher:1.3.14","FS_OPS_IMAGE":"quay.io/codefresh/fs-ops:1.2.3","GC_BUILDER_IMAGE":"quay.io/codefresh/cf-gc-builder:0.5.3","GIT_CLONE_IMAGE":"quay.io/codefresh/cf-git-cloner:10.1.28","KUBE_DEPLOY":"quay.io/codefresh/cf-deploy-kubernetes:16.1.11","PIPELINE_DEBUGGER_IMAGE":"quay.io/codefresh/cf-debugger:1.3.6","TEMPLATE_ENGINE":"quay.io/codefresh/pikolo:0.14.1"},"schedulerName":"","serviceAccount":"codefresh-engine","terminationGracePeriodSeconds":180,"tolerations":[],"userEnvVars":[],"workflowLimits":{"MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS":600,"MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION":86400,"MAXIMUM_ELECTED_STATE_AGE_ALLOWED":900,"MAXIMUM_RETRY_ATTEMPTS_ALLOWED":20,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED":900,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE":300,"TIME_ENGINE_INACTIVE_UNTIL_TERMINATION":300,"TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY":60,"TIME_INACTIVE_UNTIL_TERMINATION":2700}}` | Parameters for Engine pod (aka "pipeline" orchestrator). | +| runtime.engine.affinity | object | `{}` | Set affinity | +| runtime.engine.command | list | `["npm","run","start"]` | Set container command. | +| runtime.engine.env | object | `{"CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS":1000,"DOCKER_REQUEST_TIMEOUT_MS":30000,"FORCE_COMPOSE_SERIAL_PULL":false,"LOGGER_LEVEL":"debug","LOG_OUTGOING_HTTP_REQUESTS":false,"METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS":false,"METRICS_PROMETHEUS_ENABLED":true,"METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS":false,"METRICS_PROMETHEUS_HOST":"0.0.0.0","METRICS_PROMETHEUS_PORT":9100}` | Set additional env vars. | +| runtime.engine.env.CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS | int | `1000` | Interval to check the exec status in the container-logger | +| runtime.engine.env.DOCKER_REQUEST_TIMEOUT_MS | int | `30000` | Timeout while doing requests to the Docker daemon | +| runtime.engine.env.FORCE_COMPOSE_SERIAL_PULL | bool | `false` | If "true", composition images will be pulled sequentially | +| runtime.engine.env.LOGGER_LEVEL | string | `"debug"` | Level of logging for engine | +| runtime.engine.env.LOG_OUTGOING_HTTP_REQUESTS | bool | `false` | Enable debug-level logging of outgoing HTTP/HTTPS requests | +| runtime.engine.env.METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS | bool | `false` | Enable collecting process metrics | +| runtime.engine.env.METRICS_PROMETHEUS_ENABLED | bool | `true` | Enable emitting metrics from engine | +| runtime.engine.env.METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS | bool | `false` | Enable legacy metrics | +| runtime.engine.env.METRICS_PROMETHEUS_HOST | string | `"0.0.0.0"` | Host for Prometheus metrics server | +| runtime.engine.env.METRICS_PROMETHEUS_PORT | int | `9100` | Port for Prometheus metrics server | +| runtime.engine.image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.174.13"}` | Set image. | +| runtime.engine.nodeSelector | object | `{}` | Set node selector. | +| runtime.engine.podAnnotations | object | `{}` | Set pod annotations. | +| runtime.engine.podLabels | object | `{}` | Set pod labels. | +| runtime.engine.resources | object | `{"limits":{"cpu":"1000m","memory":"2048Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}` | Set resources. | +| runtime.engine.runtimeImages | object | See below. | Set system(base) runtime images. | +| runtime.engine.schedulerName | string | `""` | Set scheduler name. | +| runtime.engine.serviceAccount | string | `"codefresh-engine"` | Set service account for pod. | +| runtime.engine.terminationGracePeriodSeconds | int | `180` | Set termination grace period. | +| runtime.engine.tolerations | list | `[]` | Set tolerations. | +| runtime.engine.userEnvVars | list | `[]` | Set extra env vars | +| runtime.engine.workflowLimits | object | `{"MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS":600,"MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION":86400,"MAXIMUM_ELECTED_STATE_AGE_ALLOWED":900,"MAXIMUM_RETRY_ATTEMPTS_ALLOWED":20,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED":900,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE":300,"TIME_ENGINE_INACTIVE_UNTIL_TERMINATION":300,"TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY":60,"TIME_INACTIVE_UNTIL_TERMINATION":2700}` | Set workflow limits. | +| runtime.engine.workflowLimits.MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS | int | `600` | Maximum time allowed to the engine to wait for the pre-steps (aka "Initializing Process") to succeed; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION | int | `86400` | Maximum time for workflow execution; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_ELECTED_STATE_AGE_ALLOWED | int | `900` | Maximum time allowed to workflow to spend in "elected" state; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_RETRY_ATTEMPTS_ALLOWED | int | `20` | Maximum retry attempts allowed for workflow. | +| runtime.engine.workflowLimits.MAXIMUM_TERMINATING_STATE_AGE_ALLOWED | int | `900` | Maximum time allowed to workflow to spend in "terminating" state until force terminated; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE | int | `300` | Maximum time allowed to workflow to spend in "terminating" state without logs activity until force terminated; seconds. | +| runtime.engine.workflowLimits.TIME_ENGINE_INACTIVE_UNTIL_TERMINATION | int | `300` | Time since the last health check report after which workflow is terminated; seconds. | +| runtime.engine.workflowLimits.TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY | int | `60` | Time since the last health check report after which the engine is considered unhealthy; seconds. | +| runtime.engine.workflowLimits.TIME_INACTIVE_UNTIL_TERMINATION | int | `2700` | Time since the last workflow logs activity after which workflow is terminated; seconds. | +| runtime.gencerts | object | See below | Parameters for `gencerts-dind` post-upgrade/install hook | +| runtime.inCluster | bool | `true` | (for On-Premise only) Set inCluster runtime | +| runtime.patch | object | See below | Parameters for `runtime-patch` post-upgrade/install hook | +| runtime.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| runtime.rbac.create | bool | `true` | Create RBAC resources | +| runtime.rbac.rules | list | `[]` | Add custom rule to the engine role | +| runtime.runtimeExtends | list | `["system/default/hybrid/k8s_low_limits"]` | Set parent runtime to inherit. Should not be changes. Parent runtime is controlled from Codefresh side. | +| runtime.serviceAccount | object | `{"annotations":{},"create":true}` | Set annotation on engine Service Account Ref: https://codefresh.io/docs/docs/administration/codefresh-runner/#injecting-aws-arn-roles-into-the-cluster | +| serviceMonitor | object | See below | Add serviceMonitor | +| serviceMonitor.main.enabled | bool | `false` | Enable service monitor for dind pods | +| storage.azuredisk.cachingMode | string | `"None"` | | +| storage.azuredisk.skuName | string | `"Premium_LRS"` | Set storage type (`Premium_LRS`) | +| storage.backend | string | `"local"` | Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) | +| storage.ebs.accessKeyId | string | `""` | Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions | +| storage.ebs.accessKeyIdSecretKeyRef | object | `{}` | Existing secret containing AWS_ACCESS_KEY_ID. | +| storage.ebs.availabilityZone | string | `"us-east-1a"` | Set EBS volumes availability zone (required) | +| storage.ebs.encrypted | string | `"false"` | Enable encryption (optional) | +| storage.ebs.kmsKeyId | string | `""` | Set KMS encryption key ID (optional) | +| storage.ebs.secretAccessKey | string | `""` | Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions | +| storage.ebs.secretAccessKeySecretKeyRef | object | `{}` | Existing secret containing AWS_SECRET_ACCESS_KEY | +| storage.ebs.volumeType | string | `"gp2"` | Set EBS volume type (`gp2`/`gp3`/`io1`) (required) | +| storage.fsType | string | `"ext4"` | Set filesystem type (`ext4`/`xfs`) | +| storage.gcedisk.availabilityZone | string | `"us-west1-a"` | Set GCP volume availability zone | +| storage.gcedisk.serviceAccountJson | string | `""` | Set Google SA JSON key for volume-provisioner (optional) | +| storage.gcedisk.serviceAccountJsonSecretKeyRef | object | `{}` | Existing secret containing containing Google SA JSON key for volume-provisioner (optional) | +| storage.gcedisk.volumeType | string | `"pd-ssd"` | Set GCP volume backend type (`pd-ssd`/`pd-standard`) | +| storage.local.volumeParentDir | string | `"/var/lib/codefresh/dind-volumes"` | Set volume path on the host filesystem | +| storage.mountAzureJson | bool | `false` | | +| volumeProvisioner | object | See below | Volume Provisioner parameters | +| volumeProvisioner.affinity | object | `{}` | Set affinity | +| volumeProvisioner.dind-lv-monitor | object | See below | `dind-lv-monitor` DaemonSet parameters (local volumes cleaner) | +| volumeProvisioner.enabled | bool | `true` | Enable volume-provisioner | +| volumeProvisioner.env | object | `{}` | Add additional env vars | +| volumeProvisioner.image | object | `{"registry":"quay.io","repository":"codefresh/dind-volume-provisioner","tag":"1.35.0"}` | Set image | +| volumeProvisioner.nodeSelector | object | `{}` | Set node selector | +| volumeProvisioner.podAnnotations | object | `{}` | Set pod annotations | +| volumeProvisioner.podSecurityContext | object | See below | Set security context for the pod | +| volumeProvisioner.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| volumeProvisioner.rbac.create | bool | `true` | Create RBAC resources | +| volumeProvisioner.rbac.rules | list | `[]` | Add custom rule to the role | +| volumeProvisioner.replicasCount | int | `1` | Set number of pods | +| volumeProvisioner.resources | object | `{}` | Set resources | +| volumeProvisioner.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| volumeProvisioner.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| volumeProvisioner.serviceAccount.create | bool | `true` | Create service account | +| volumeProvisioner.serviceAccount.name | string | `""` | Override service account name | +| volumeProvisioner.tolerations | list | `[]` | Set tolerations | +| volumeProvisioner.updateStrategy | object | `{"type":"Recreate"}` | Upgrade strategy | + diff --git a/charts/codefresh/cf-runtime/6.4.7/README.md.gotmpl b/charts/codefresh/cf-runtime/6.4.7/README.md.gotmpl new file mode 100644 index 0000000000..96e5ca5748 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/README.md.gotmpl @@ -0,0 +1,1007 @@ +## Codefresh Runner + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +Helm chart for deploying [Codefresh Runner](https://codefresh.io/docs/docs/installation/codefresh-runner/) to Kubernetes. + +## Table of Content + +- [Prerequisites](#prerequisites) +- [Get Chart Info](#get-chart-info) +- [Install Chart](#install-chart) +- [Chart Configuration](#chart-configuration) +- [Upgrade Chart](#upgrade-chart) + - [To 2.x](#to-2-x) + - [To 3.x](#to-3-x) + - [To 4.x](#to-4-x) + - [To 5.x](#to-5-x) + - [To 6.x](#to-6-x) +- [Architecture](#architecture) +- [Configuration](#configuration) + - [EBS backend volume configuration in AWS](#ebs-backend-volume-configuration) + - [Azure Disks backend volume configuration in AKS](#azure-disks-backend-volume-configuration) + - [GCE Disks backend volume configuration in GKE](#gce-disks-backend-volume-configuration-in-gke) + - [Custom volume mounts](#custom-volume-mounts) + - [Custom global environment variables](#custom-global-environment-variables) + - [Volume reuse policy](#volume-reuse-policy) + - [Volume cleaners](#volume-cleaners) + - [Rootless DinD](#rootless-dind) + - [ARM](#arm) + - [Openshift](#openshift) + - [On-premise](#on-premise) + +## Prerequisites + +- Kubernetes **1.19+** +- Helm **3.8.0+** + +⚠️⚠️⚠️ +> Since version 6.2.x chart is pushed **only** to OCI registry at `oci://quay.io/codefresh/cf-runtime` + +> Versions prior to 6.2.x are still available in ChartMuseum at `http://chartmuseum.codefresh.io/cf-runtime` + +## Get Chart Info + +```console +helm show all oci://quay.io/codefresh/cf-runtime +``` +See [Use OCI-based registries](https://helm.sh/docs/topics/registries/) + +## Install Chart + +**Important:** only helm3 is supported + +- Specify the following mandatory values + +`values.yaml` +```yaml +# -- Global parameters +# @default -- See below +global: + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used '{{ `{{ .Values.global.context }}_{{ .Release.Namespace }}` }}' + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used '{{ `{{ .Values.global.context }}/{{ .Release.Namespace }}` }}' + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace +``` + +- Install chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace codefresh +``` + +## Chart Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +## Upgrade Chart + +### To 2.x + +This major release renames and deprecated several values in the chart. Most of the workload templates have been refactored. + +Affected values: +- `dockerRegistry` is deprecated. Replaced with `global.imageRegistry` +- `re` is renamed to `runtime` +- `storage.localVolumeMonitor` is replaced with `volumeProvisioner.dind-lv-monitor` +- `volumeProvisioner.volume-cleanup` is replaced with `volumeProvisioner.dind-volume-cleanup` +- `image` values structure has been updated. Split to `image.registry` `image.repository` `image.tag` +- pod's `annotations` is renamed to `podAnnotations` + +### To 3.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release adds [runtime-environment](https://codefresh.io/docs/docs/installation/codefresh-runner/#runtime-environment-specification) spec into chart templates. +That means it is possible to set parametes for `dind` and `engine` pods via [values.yaml](./values.yaml). + +**If you had any overrides (i.e. tolerations/nodeSelector/environment variables/etc) added in runtime spec via [codefresh CLI](https://codefresh-io.github.io/cli/) (for example, you did use [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands to modify the runtime-environment), you MUST add these into chart's [values.yaml](./values.yaml) for `.Values.runtime.dind` or(and) .`Values.runtime.engine`** + +**For backward compatibility, you can disable updating runtime-environment spec via** `.Values.runtime.patch.enabled=false` + +Affected values: +- added **mandatory** `global.codefreshToken`/`global.codefreshTokenSecretKeyRef` **You must specify it before the upgrade!** +- `runtime.engine` is added +- `runtime.dind` is added +- `global.existingAgentToken` is replaced with `global.agentTokenSecretKeyRef` +- `global.existingDindCertsSecret` is replaced with `global.dindCertsSecretRef` + +### To 4.x + +This major release adds **agentless inCluster** runtime mode (relevant only for [Codefresh On-Premises](#on-premise) users) + +Affected values: +- `runtime.agent` / `runtime.inCluster` / `runtime.accounts` / `runtime.description` are added + +### To 5.x + +This major release converts `.runtime.dind.pvcs` from **list** to **dict** + +> 4.x chart's values example: +```yaml +runtime: + dind: + pvcs: + - name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +> 5.x chart's values example: +```yaml +runtime: + dind: + pvcs: + dind: + name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +Affected values: +- `.runtime.dind.pvcs` converted from **list** to **dict** + +### To 6.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release deprecates previously required `codefresh runner init --generate-helm-values-file`. + +Affected values: +- **Replaced** `.monitor.clusterId` with `.global.context` as **mandatory** value! +- **Deprecated** `.global.agentToken` / `.global.agentTokenSecretKeyRef` +- **Removed** `.global.agentId` +- **Removed** `.global.keys` / `.global.dindCertsSecretRef` +- **Removed** `.global.existingAgentToken` / `existingDindCertsSecret` +- **Removed** `.monitor.clusterId` / `.monitor.token` / `.monitor.existingMonitorToken` + +#### Migrate the Helm chart from version 5.x to 6.x + +Given this is the legacy `generated_values.yaml` values: + +> legacy `generated_values.yaml` +```yaml +{ + "appProxy": { + "enabled": false, + }, + "monitor": { + "enabled": false, + "clusterId": "my-cluster-name", + "token": "1234567890" + }, + "global": { + "namespace": "namespace", + "codefreshHost": "https://g.codefresh.io", + "agentToken": "0987654321", + "agentId": "agent-id-here", + "agentName": "my-cluster-name_my-namespace", + "accountId": "my-account-id", + "runtimeName": "my-cluster-name/my-namespace", + "codefreshToken": "1234567890", + "keys": { + "key": "-----BEGIN RSA PRIVATE KEY-----...", + "csr": "-----BEGIN CERTIFICATE REQUEST-----...", + "ca": "-----BEGIN CERTIFICATE-----...", + "serverCert": "-----BEGIN CERTIFICATE-----..." + } + } +} +``` + +Update `values.yaml` for new chart version: + +> For existing installation for backward compatibility `.Values.global.agentToken/agentTokenSecretKeyRef` **must be provided!** For installation from scratch this value is no longer required. + +> updated `values.yaml` +```yaml +global: + codefreshToken: "1234567890" + accountId: "my-account-id" + context: "my-cluster-name" + agentToken: "0987654321" # MANDATORY when migrating from < 6.x chart version ! + agentName: "my-cluster-name_my-namespace" # optional + runtimeName: "my-cluster-name/my-namespace" # optional +``` + +> **Note!** Though it's still possible to update runtime-environment via [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands, it's recommended to enable sidecar container to pull runtime spec from Codefresh API to detect any drift in configuration. + +```yaml +runner: + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: true +``` + +## Architecture + +[Codefresh Runner architecture](https://codefresh.io/docs/docs/installation/codefresh-runner/#codefresh-runner-architecture) + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +### EBS backend volume configuration + +`dind-volume-provisioner` should have permissions to create/attach/detach/delete/get EBS volumes + +Minimal IAM policy for `dind-volume-provisioner` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AttachVolume", + "ec2:CreateSnapshot", + "ec2:CreateTags", + "ec2:CreateVolume", + "ec2:DeleteSnapshot", + "ec2:DeleteTags", + "ec2:DeleteVolume", + "ec2:DescribeInstances", + "ec2:DescribeSnapshots", + "ec2:DescribeTags", + "ec2:DescribeVolumes", + "ec2:DetachVolume" + ], + "Resource": "*" + } + ] +} +``` + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM role + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] +``` + +2. Pass static credentials in `.Values.storage.ebs.accessKeyId/accessKeyIdSecretKeyRef` and `.Values.storage.ebs.secretAccessKey/secretAccessKeySecretKeyRef` + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" +``` + +### Custom volume mounts + +You can add your own volumes and volume mounts in the runtime environment, so that all pipeline steps will have access to the same set of external files. + +```yaml +runtime: + dind: + userVolumes: + regctl-docker-registry: + name: regctl-docker-registry + secret: + items: + - key: .dockerconfigjson + path: config.json + secretName: regctl-docker-registry + optional: true + userVolumeMounts: + regctl-docker-registry: + name: regctl-docker-registry + mountPath: /home/appuser/.docker/ + readOnly: true + +``` + +### Azure Disks backend volume configuration + +`dind-volume-provisioner` should have permissions to create/delete/get Azure Disks + +Role definition for `dind-volume-provisioner` + +`dind-volume-provisioner-role.json` +```json +{ + "Name": "CodefreshDindVolumeProvisioner", + "Description": "Perform create/delete/get disks", + "IsCustom": true, + "Actions": [ + "Microsoft.Compute/disks/read", + "Microsoft.Compute/disks/write", + "Microsoft.Compute/disks/delete" + + ], + "AssignableScopes": ["/subscriptions/"] +} +``` + +When creating an AKS cluster in Azure there is the option to use a [managed identity](https://learn.microsoft.com/en-us/azure/aks/use-managed-identity) that is assigned to the kubelet. This identity is assigned to the underlying node pool in the AKS cluster and can then be used by the dind-volume-provisioner. + +```console +export ROLE_DEFINITIN_FILE=dind-volume-provisioner-role.json +export SUBSCRIPTION_ID=$(az account show --query "id" | xargs echo ) +export RESOURCE_GROUP= +export AKS_NAME= +export LOCATION=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query location | xargs echo) +export NODES_RESOURCE_GROUP=MC_${RESOURCE_GROUP}_${AKS_NAME}_${LOCATION} +export NODE_SERVICE_PRINCIPAL=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query identityProfile.kubeletidentity.objectId | xargs echo) + +az role definition create --role-definition @${ROLE_DEFINITIN_FILE} +az role assignment create --assignee $NODE_SERVICE_PRINCIPAL --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$NODES_RESOURCE_GROUP --role CodefreshDindVolumeProvisioner +``` + +Deploy Helm chart with the following values: + +`values.yaml` +```yaml +volumeProvisioner: + podSecurityContext: + enabled: true + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 + +storage: + backend: azuredisk + azuredisk: + availabilityZone: northeurope-1 # replace with your zone + resourceGroup: my-resource-group-name + + mountAzureJson: true + +runtime: + dind: + nodeSelector: + topology.kubernetes.io/zone: northeurope-1 +``` + +### GCE Disks backend volume configuration in GKE + +`dind-volume-provisioner` should have `ComputeEngine.StorageAdmin` permissions + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM Service Account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +2. Pass static credentials in `.Values.storage.gcedisk.serviceAccountJson` (inline) or `.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef` (from your own secret) + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: | + { + "type": "service_account", + "project_id": "...", + "private_key_id": "...", + "private_key": "...", + "client_email": "...", + "client_id": "...", + "auth_uri": "...", + "token_uri": "...", + "auth_provider_x509_cert_url": "...", + "client_x509_cert_url": "..." + } + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g.: + # serviceAccountJsonSecretKeyRef: + # name: gce-service-account + # key: service-account.json + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + iam.gke.io/gcp-service-account: @.iam.gserviceaccount.com + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +### Custom global environment variables + +You can add your own environment variables to the runtime environment. All pipeline steps have access to the global variables. + +```yaml +runtime: + engine: + userEnvVars: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token +``` + +### Volume reuse policy + +Volume reuse behavior depends on the configuration for `reuseVolumeSelector` in the runtime environment spec. + +```yaml +runtime: + dind: + pvcs: + - name: dind + ... + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +The following options are available: +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName'` - PV can be used by ANY pipeline in the specified account (default). +Benefit: Fewer PVs, resulting in lower costs. Since any PV can be used by any pipeline, the cluster needs to maintain/reserve fewer PVs in its PV pool for Codefresh. +Downside: Since the PV can be used by any pipeline, the PVs could have assets and info from different pipelines, reducing the probability of cache. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,project_id'` - PV can be used by ALL pipelines in your account, assigned to the same project. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id'` - PV can be used only by a single pipeline. +Benefit: More probability of cache without “spam” from other pipelines. +Downside: More PVs to maintain and therefore higher costs. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,io.codefresh.branch_name'` - PV can be used only by single pipeline AND single branch. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,trigger'` - PV can be used only by single pipeline AND single trigger. + +### Volume cleaners + +Codefresh pipelines require disk space for: + * [Pipeline Shared Volume](https://codefresh.io/docs/docs/pipelines/introduction-to-codefresh-pipelines/#sharing-the-workspace-between-build-steps) (`/codefresh/volume`, implemented as [docker volume](https://docs.docker.com/storage/volumes/)) + * Docker containers, both running and stopped + * Docker images and cached layers + +Codefresh offers two options to manage disk space and prevent out-of-space errors: +* Use runtime cleaners on Docker images and volumes +* [Set the minimum disk space per pipeline build volume](https://codefresh.io/docs/docs/pipelines/pipelines/#set-minimum-disk-space-for-a-pipeline-build) + +To improve performance by using Docker cache, Codefresh `volume-provisioner` can provision previously used disks with Docker images and pipeline volumes from previously run builds. + +### Types of runtime volume cleaners + +Docker images and volumes must be cleaned on a regular basis. + +* [IN-DIND cleaner](https://github.com/codefresh-io/dind/tree/master/cleaner): Deletes extra Docker containers, volumes, and images in **DIND pod**. +* [External volume cleaner](https://github.com/codefresh-io/dind-volume-cleanup): Deletes unused **external** PVs (EBS, GCE/Azure disks). +* [Local volume cleaner](https://github.com/codefresh-io/dind-volume-utils/blob/master/local-volumes/lv-cleaner.sh): Deletes **local** volumes if node disk space is close to the threshold. + +### IN-DIND cleaner + +**Purpose:** Removes unneeded *docker containers, images, volumes* inside Kubernetes volume mounted on the DIND pod + +**How it runs:** Inside each DIND pod as script + +**Triggered by:** SIGTERM and also during the run when disk usage > 90% (configurable) + +**Configured by:** Environment Variables which can be set in Runtime Environment spec + +**Configuration/Logic:** [README.md](https://github.com/codefresh-io/dind/tree/master/cleaner#readme) + +Override `.Values.runtime.dind.env` if necessary (the following are **defaults**): + +```yaml +runtime: + dind: + env: + CLEAN_PERIOD_SECONDS: '21600' # launch clean if last clean was more than CLEAN_PERIOD_SECONDS seconds ago + CLEAN_PERIOD_BUILDS: '5' # launch clean if last clean was more CLEAN_PERIOD_BUILDS builds since last build + IMAGE_RETAIN_PERIOD: '14400' # do not delete docker images if they have events since current_timestamp - IMAGE_RETAIN_PERIOD + VOLUMES_RETAIN_PERIOD: '14400' # do not delete docker volumes if they have events since current_timestamp - VOLUMES_RETAIN_PERIOD + DISK_USAGE_THRESHOLD: '0.8' # launch clean based on current disk usage DISK_USAGE_THRESHOLD + INODES_USAGE_THRESHOLD: '0.8' # launch clean based on current inodes usage INODES_USAGE_THRESHOLD +``` + +### External volumes cleaner + +**Purpose:** Removes unused *kubernetes volumes and related backend volumes* + +**How it runs:** Runs as `dind-volume-cleanup` CronJob. Installed in case the Runner uses non-local volumes `.Values.storage.backend != local` + +**Triggered by:** CronJob every 10min (configurable) + +**Configuration:** + +Set `codefresh.io/volume-retention` for dinds' PVCs: + +```yaml +runtime: + dind: + pvcs: + dind: + ... + annotations: + codefresh.io/volume-retention: 7d +``` + +Or override environment variables for `dind-volume-cleanup` cronjob: + +```yaml +volumeProvisioner: + dind-volume-cleanup: + env: + RETENTION_DAYS: 7 # clean volumes that were last used more than `RETENTION_DAYS` (default is 4) ago +``` + +### Local volumes cleaner + +**Purpose:** Deletes local volumes when node disk space is close to the threshold + +**How it runs:** Runs as `dind-lv-monitor` DaemonSet. Installed in case the Runner uses local volumes `.Values.storage.backend == local` + +**Triggered by:** Disk space usage or inode usage that exceeds thresholds (configurable) + +**Configuration:** + +Override environment variables for `dind-lv-monitor` daemonset: + +```yaml +volumeProvisioner: + dind-lv-monitor: + env: + KB_USAGE_THRESHOLD: 60 # default 80 (percentage) + INODE_USAGE_THRESHOLD: 60 # default 80 +``` + +### Rootless DinD + +DinD pod runs a `priviliged` container with **rootfull** docker. +To run the docker daemon as non-root user (**rootless** mode), change dind image tag: + +`values.yaml` +```yaml +runtime: + dind: + image: + tag: rootless +``` + +### ARM + +With the Codefresh Runner, you can run native ARM64v8 builds. + +> **Note!** +> You cannot run both amd64 and arm64 images within the same pipeline. As one pipeline can map only to one runtime, you can run either amd64 or arm64 within the same pipeline. + +Provide `nodeSelector` and(or) `tolerations` for dind pods: + +`values.yaml` +```yaml +runtime: + dind: + nodeSelector: + arch: arm64 + tolerations: + - key: arch + operator: Equal + value: arm64 + effect: NoSchedule +``` + +### Openshift + +To install Codefresh Runner on OpenShift use the following `values.yaml` example + +```yaml +runner: + podSecurityContext: + enabled: false + +volumeProvisioner: + podSecurityContext: + enabled: false + env: + PRIVILEGED_CONTAINER: true + dind-lv-monitor: + containerSecurityContext: + enabled: true + privileged: true + volumePermissions: + enabled: true + securityContext: + privileged: true + runAsUser: auto +``` + +Grant `privileged` SCC to `cf-runtime-runner` and `cf-runtime-volume-provisioner` service accounts. + +```console +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-runner + +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-volume-provisioner +``` + +### On-premise + +If you have [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) deployed, you can install Codefresh Runner in **agentless** mode. + +**What is agentless mode?** + +Agent (aka venona) is Runner component which responsible for calling Codefresh API to run builds and create dind/engine pods and pvc objects. Agent can only be assigned to a single account, thus you can't share one runtime across multiple accounts. However, with **agentless** mode it's possible to register the runtime as **system**-type runtime so it's registered on the platform level and can be assigned/shared across multiple accounts. + +**What are the prerequisites?** +- You have a running [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) control-plane environment +- You have a Codefresh API token with platform **Admin** permissions scope + + +### How to deploy agentless runtime when it's on the SAME k8s cluster as On-Premises control-plane environment? + +- Enable cluster-level permissions for cf-api (On-Premises control-plane component) + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) Helm chart +```yaml +cfapi: + ... + # -- Enable ClusterRole/ClusterRoleBinding + rbac: + namespaced: false +``` + +- Set the following values for Runner Helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=true` + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + runtimeName: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: true + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to check the runtime. Assign it to the required account(s). Run test pipeline on it. + + +### How to deploy agentless runtime when it's on the DIFFERENT k8s cluster than On-Premises control-plane environment? + +In this case, it's required to mount runtime cluster's `KUBECONFIG` into On-Premises `cf-api` deployment + +- Create the neccessary RBAC resources + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +extraResources: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: codefresh-role + namespace: '{{ "{{ .Release.Namespace }}" }}' + rules: + - apiGroups: [""] + resources: ["pods", "persistentvolumeclaims", "persistentvolumes"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] +- apiVersion: v1 + kind: ServiceAccount + metadata: + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: codefresh-role + subjects: + - kind: ServiceAccount + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' +- apiVersion: v1 + kind: Secret + metadata: + name: codefresh-runtime-user-token + namespace: '{{ "{{ .Release.Namespace }}" }}' + annotations: + kubernetes.io/service-account.name: codefresh-runtime-user + type: kubernetes.io/service-account-token +``` + +- Set up the following environment variables to create a `KUBECONFIG` file + +```shell +NAMESPACE=cf-runtime +CLUSTER_NAME=prod-ue1-some-cluster-name +CURRENT_CONTEXT=$(kubectl config current-context) + +USER_TOKEN_VALUE=$(kubectl -n cf-runtime get secret/codefresh-runtime-user-token -o=go-template='{{ `{{.data.token}}` }}' | base64 --decode) +CURRENT_CLUSTER=$(kubectl config view --raw -o=go-template='{{ `{{range .contexts}}{{if eq .name "'''${CURRENT_CONTEXT}'''"}}{{ index .context "cluster" }}{{end}}{{end}}` }}') +CLUSTER_CA=$(kubectl config view --raw -o=go-template='{{ `{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}"{{with index .cluster "certificate-authority-data" }}{{.}}{{end}}"{{ end }}{{ end }}` }}') +CLUSTER_SERVER=$(kubectl config view --raw -o=go-template='{{ `{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}{{ .cluster.server }}{{end}}{{ end }}` }}') + +export -p USER_TOKEN_VALUE CURRENT_CONTEXT CURRENT_CLUSTER CLUSTER_CA CLUSTER_SERVER CLUSTER_NAME +``` + +- Create a kubeconfig file + +```console +cat << EOF > $CLUSTER_NAME-kubeconfig +apiVersion: v1 +kind: Config +current-context: ${CLUSTER_NAME} +contexts: +- name: ${CLUSTER_NAME} + context: + cluster: ${CLUSTER_NAME} + user: codefresh-runtime-user + namespace: ${NAMESPACE} +clusters: +- name: ${CLUSTER_NAME} + cluster: + certificate-authority-data: ${CLUSTER_CA} + server: ${CLUSTER_SERVER} +users: +- name: ${CLUSTER_NAME} + user: + token: ${USER_TOKEN_VALUE} +EOF +``` + +- **Switch context to On-Premises control-plane cluster**. Create k8s secret (via any tool like [ESO](https://external-secrets.io/v0.4.4/), `kubectl`, etc ) containing runtime cluster's `KUBECONFG` created in previous step. + +```shell +NAMESPACE=codefresh +kubectl create secret generic dind-runtime-clusters --from-file=$CLUSTER_NAME=$CLUSTER_NAME-kubeconfig -n $NAMESPACE +``` + +- Mount secret containing runtime cluster's `KUBECONFG` into cf-api in On-Premises control-plane cluster + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) helm chart +```yaml +cf-api: + ... + volumes: + dind-clusters: + enabled: true + type: secret + nameOverride: dind-runtime-clusters + optional: true +``` +> volumeMount `/etc/kubeconfig` is already configured in cf-api Helm chart template. No need to specify it. + +- Set the following values for Runner helm chart + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=false` + +**Important!** +`.Values.global.name` ("system/" prefix is ignored!) should match the cluster name (key in `dind-runtime-clusters` secret created previously) +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + name: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: false + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- (optional) Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to see the runtime. Assign it to the required account(s). + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + diff --git a/charts/codefresh/cf-runtime/6.4.7/files/cleanup-runtime.sh b/charts/codefresh/cf-runtime/6.4.7/files/cleanup-runtime.sh new file mode 100644 index 0000000000..c1fc5f3682 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/files/cleanup-runtime.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "-----" +echo "API_HOST: ${API_HOST}" +echo "AGENT_NAME: ${AGENT_NAME}" +echo "RUNTIME_NAME: ${RUNTIME_NAME}" +echo "AGENT: ${AGENT}" +echo "AGENT_SECRET_NAME: ${AGENT_SECRET_NAME}" +echo "DIND_SECRET_NAME: ${DIND_SECRET_NAME}" +echo "-----" + +auth() { + codefresh auth create-context --api-key ${API_TOKEN} --url ${API_HOST} +} + +remove_runtime() { + if [ "$AGENT" == "true" ]; then + codefresh delete re ${RUNTIME_NAME} || true + else + codefresh delete sys-re ${RUNTIME_NAME} || true + fi +} + +remove_agent() { + codefresh delete agent ${AGENT_NAME} || true +} + +remove_secrets() { + kubectl patch secret $(kubectl get secret -l codefresh.io/internal=true | awk 'NR>1{print $1}' | xargs) -p '{"metadata":{"finalizers":null}}' --type=merge || true + kubectl delete secret $AGENT_SECRET_NAME || true + kubectl delete secret $DIND_SECRET_NAME || true +} + +auth +remove_runtime +remove_agent +remove_secrets \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/files/configure-dind-certs.sh b/charts/codefresh/cf-runtime/6.4.7/files/configure-dind-certs.sh new file mode 100644 index 0000000000..a1092eb1e6 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/files/configure-dind-certs.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# + +#--- +fatal() { + echo "ERROR: $1" + exit 1 +} + +msg() { echo -e "\e[32mINFO ---> $1\e[0m"; } +err() { echo -e "\e[31mERR ---> $1\e[0m" ; return 1; } + +exit_trap () { + local lc="$BASH_COMMAND" rc=$? + if [ $rc != 0 ]; then + if [[ -n "$SLEEP_ON_ERROR" ]]; then + echo -e "\nSLEEP_ON_ERROR is set - Sleeping to fix error" + sleep $SLEEP_ON_ERROR + fi + fi +} +trap exit_trap EXIT + +usage() { + echo "Usage: + $0 [-n | --namespace] [--server-cert-cn] [--server-cert-extra-sans] codefresh-api-host codefresh-api-token + +Example: + $0 -n workflow https://g.codefresh.io 21341234.423141234.412431234 + +" +} + +# Args +while [[ $1 =~ ^(-(n|h)|--(namespace|server-cert-cn|server-cert-extra-sans|help)) ]] +do + key=$1 + value=$2 + + case $key in + -h|--help) + usage + exit + ;; + -n|--namespace) + NAMESPACE="$value" + shift + ;; + --server-cert-cn) + SERVER_CERT_CN="$value" + shift + ;; + --server-cert-extra-sans) + SERVER_CERT_EXTRA_SANS="$value" + shift + ;; + esac + shift # past argument or value +done + +API_HOST=${1:-"$CF_API_HOST"} +API_TOKEN=${2:-"$CF_API_TOKEN"} + +[[ -z "$API_HOST" ]] && usage && fatal "Missing API_HOST" +[[ -z "$API_TOKEN" ]] && usage && fatal "Missing token" + + +API_SIGN_PATH=${API_SIGN_PATH:-"api/custom_clusters/signServerCerts"} + +NAMESPACE=${NAMESPACE:-default} +RELEASE=${RELEASE:-cf-runtime} + +DIR=$(dirname $0) +TMPDIR=/tmp/codefresh/ + +TMP_CERTS_FILE_ZIP=$TMPDIR/cf-certs.zip +TMP_CERTS_HEADERS_FILE=$TMPDIR/cf-certs-response-headers.txt +CERTS_DIR=$TMPDIR/ssl +SRV_TLS_CA_CERT=${CERTS_DIR}/ca.pem +SRV_TLS_KEY=${CERTS_DIR}/server-key.pem +SRV_TLS_CSR=${CERTS_DIR}/server-cert.csr +SRV_TLS_CERT=${CERTS_DIR}/server-cert.pem +CF_SRV_TLS_CERT=${CERTS_DIR}/cf-server-cert.pem +CF_SRV_TLS_CA_CERT=${CERTS_DIR}/cf-ca.pem +mkdir -p $TMPDIR $CERTS_DIR + +K8S_CERT_SECRET_NAME=codefresh-certs-server +echo -e "\n------------------\nGenerating server tls certificates ... " + +SERVER_CERT_CN=${SERVER_CERT_CN:-"docker.codefresh.io"} +SERVER_CERT_EXTRA_SANS="${SERVER_CERT_EXTRA_SANS}" +### + + openssl genrsa -out $SRV_TLS_KEY 4096 || fatal "Failed to generate openssl key " + openssl req -subj "/CN=${SERVER_CERT_CN}" -new -key $SRV_TLS_KEY -out $SRV_TLS_CSR || fatal "Failed to generate openssl csr " + GENERATE_CERTS=true + CSR=$(sed ':a;N;$!ba;s/\n/\\n/g' ${SRV_TLS_CSR}) + + SERVER_CERT_SANS="IP:127.0.0.1,DNS:dind,DNS:*.dind.${NAMESPACE},DNS:*.dind.${NAMESPACE}.svc${KUBE_DOMAIN},DNS:*.cf-cd.com,DNS:*.codefresh.io" + if [[ -n "${SERVER_CERT_EXTRA_SANS}" ]]; then + SERVER_CERT_SANS=${SERVER_CERT_SANS},${SERVER_CERT_EXTRA_SANS} + fi + echo "{\"reqSubjectAltName\": \"${SERVER_CERT_SANS}\", \"csr\": \"${CSR}\" }" > ${TMPDIR}/sign_req.json + + rm -fv ${TMP_CERTS_HEADERS_FILE} ${TMP_CERTS_FILE_ZIP} + + SIGN_STATUS=$(curl -k -sSL -d @${TMPDIR}/sign_req.json -H "Content-Type: application/json" -H "Authorization: ${API_TOKEN}" -H "Expect: " \ + -o ${TMP_CERTS_FILE_ZIP} -D ${TMP_CERTS_HEADERS_FILE} -w '%{http_code}' ${API_HOST}/${API_SIGN_PATH} ) + + echo "Sign request completed with HTTP_STATUS_CODE=$SIGN_STATUS" + if [[ $SIGN_STATUS != 200 ]]; then + echo "ERROR: Cannot sign certificates" + if [[ -f ${TMP_CERTS_FILE_ZIP} ]]; then + mv ${TMP_CERTS_FILE_ZIP} ${TMP_CERTS_FILE_ZIP}.error + cat ${TMP_CERTS_FILE_ZIP}.error + fi + exit 1 + fi + unzip -o -d ${CERTS_DIR}/ ${TMP_CERTS_FILE_ZIP} || fatal "Failed to unzip certificates to ${CERTS_DIR} " + cp -v ${CF_SRV_TLS_CA_CERT} $SRV_TLS_CA_CERT || fatal "received ${TMP_CERTS_FILE_ZIP} does not contains ca.pem" + cp -v ${CF_SRV_TLS_CERT} $SRV_TLS_CERT || fatal "received ${TMP_CERTS_FILE_ZIP} does not contains cf-server-cert.pem" + + +echo -e "\n------------------\nCreating certificate secret " + +kubectl -n $NAMESPACE create secret generic $K8S_CERT_SECRET_NAME \ + --from-file=$SRV_TLS_CA_CERT \ + --from-file=$SRV_TLS_KEY \ + --from-file=$SRV_TLS_CERT \ + --dry-run=client -o yaml | kubectl apply --overwrite -f - +kubectl -n $NAMESPACE label --overwrite secret ${K8S_CERT_SECRET_NAME} codefresh.io/internal=true +kubectl -n $NAMESPACE patch secret $K8S_CERT_SECRET_NAME -p '{"metadata": {"finalizers": ["kubernetes"]}}' diff --git a/charts/codefresh/cf-runtime/6.4.7/files/init-runtime.sh b/charts/codefresh/cf-runtime/6.4.7/files/init-runtime.sh new file mode 100644 index 0000000000..eb3488af11 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/files/init-runtime.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +echo "-----" +echo "API_HOST: ${API_HOST}" +echo "AGENT_NAME: ${AGENT_NAME}" +echo "KUBE_CONTEXT: ${KUBE_CONTEXT}" +echo "KUBE_NAMESPACE: ${KUBE_NAMESPACE}" +echo "OWNER_NAME: ${OWNER_NAME}" +echo "RUNTIME_NAME: ${RUNTIME_NAME}" +echo "SECRET_NAME: ${SECRET_NAME}" +echo "-----" + +create_agent_secret() { + + kubectl apply -f - < $1\e[0m"; } +err() { echo -e "\e[31mERR ---> $1\e[0m" ; return 1; } + + +if [ -z "${USER_CODEFRESH_TOKEN}" ]; then + err "missing codefresh user token. must supply \".global.codefreshToken\" if agent-codefresh-token does not exist" + exit 1 +fi + +codefresh auth create-context --api-key ${USER_CODEFRESH_TOKEN} --url ${API_HOST} + +while true; do + msg "Reconciling ${RUNTIME_NAME} runtime" + + sleep $RECONCILE_INTERVAL + + codefresh get re \ + --name ${RUNTIME_NAME} \ + -o yaml \ + | yq 'del(.version, .metadata.changedBy, .metadata.creationTime)' > /tmp/runtime.yaml + + kubectl get cm ${CONFIGMAP_NAME} -n ${KUBE_NAMESPACE} -o yaml \ + | yq 'del(.metadata.resourceVersion, .metadata.uid)' \ + | yq eval '.data["runtime.yaml"] = load_str("/tmp/runtime.yaml")' \ + | kubectl apply -f - +done diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_deployment.yaml new file mode 100644 index 0000000000..26f3576b77 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_deployment.yaml @@ -0,0 +1,70 @@ +{{- define "app-proxy.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "app-proxy.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "app-proxy.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "app-proxy.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: app-proxy + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include "app-proxy.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 3000 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /health + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_env-vars.yaml new file mode 100644 index 0000000000..c9b9a0e36a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_env-vars.yaml @@ -0,0 +1,19 @@ +{{- define "app-proxy.environment-variables.defaults" }} +PORT: 3000 +{{- end }} + +{{- define "app-proxy.environment-variables.calculated" }} +CODEFRESH_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +{{- with .Values.ingress.pathPrefix }} +API_PATH_PREFIX: {{ . | quote }} +{{- end }} +{{- end }} + +{{- define "app-proxy.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "app-proxy.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "app-proxy.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_helpers.tpl new file mode 100644 index 0000000000..2d4272ca92 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "app-proxy.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "app-proxy" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "app-proxy.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "app-proxy" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "app-proxy.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: app-proxy +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "app-proxy.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: app-proxy +{{- end }} + + +{{/* +Create the name of the service account to use +*/}} +{{- define "app-proxy.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "app-proxy.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_ingress.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_ingress.yaml new file mode 100644 index 0000000000..d7860b3638 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_ingress.yaml @@ -0,0 +1,32 @@ +{{- define "app-proxy.resources.ingress" -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: {{- include "app-proxy.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.class }} + {{- end }} + {{- if .Values.ingress.tlsSecret }} + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ .Values.tlsSecret }} + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: {{ .Values.ingress.pathPrefix | default "/" }} + pathType: ImplementationSpecific + backend: + service: + name: {{ include "app-proxy.fullname" . }} + port: + number: 80 +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_rbac.yaml new file mode 100644 index 0000000000..87bd869ba0 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_rbac.yaml @@ -0,0 +1,47 @@ +{{- define "app-proxy.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "app-proxy.serviceAccountName" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "app-proxy.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ include "app-proxy.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_service.yaml new file mode 100644 index 0000000000..4c3a93bf27 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/app-proxy/_service.yaml @@ -0,0 +1,17 @@ +{{- define "app-proxy.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 3000 + selector: + {{- include "app-proxy.selectorLabels" . | nindent 4 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_deployment.yaml new file mode 100644 index 0000000000..62588b4d3d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_deployment.yaml @@ -0,0 +1,62 @@ +{{- define "event-exporter.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "event-exporter.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "event-exporter.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "event-exporter.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: event-exporter + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + args: [--running-in-cluster=true] + env: + {{- include "event-exporter.environment-variables" . | nindent 8 }} + ports: + - name: metrics + containerPort: 9102 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_env-vars.yaml new file mode 100644 index 0000000000..d28d0776f3 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_env-vars.yaml @@ -0,0 +1,14 @@ +{{- define "event-exporter.environment-variables.defaults" }} +{{- end }} + +{{- define "event-exporter.environment-variables.calculated" }} +{{- end }} + +{{- define "event-exporter.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "event-exporter.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "event-exporter.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_helpers.tpl new file mode 100644 index 0000000000..5b8b5eff7f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "event-exporter.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "event-exporter" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "event-exporter.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "event-exporter" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "event-exporter.labels" -}} +{{ include "cf-runtime.labels" . }} +app: event-exporter +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "event-exporter.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +app: event-exporter +{{- end }} + + +{{/* +Create the name of the service account to use +*/}} +{{- define "event-exporter.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "event-exporter.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_rbac.yaml new file mode 100644 index 0000000000..69d7b6b2fb --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_rbac.yaml @@ -0,0 +1,47 @@ +{{- define "event-exporter.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "event-exporter.serviceAccountName" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: [events] + verbs: [get, list, watch] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "event-exporter.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "event-exporter.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_service.yaml new file mode 100644 index 0000000000..6fa29ec1a0 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_service.yaml @@ -0,0 +1,17 @@ +{{- define "event-exporter.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: metrics + port: 9102 + targetPort: metrics + protocol: TCP + selector: + {{- include "event-exporter.selectorLabels" . | nindent 4 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_serviceMontor.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_serviceMontor.yaml new file mode 100644 index 0000000000..6092443f0a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/event-exporter/_serviceMontor.yaml @@ -0,0 +1,14 @@ +{{- define "event-exporter.resources.serviceMonitor" -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + endpoints: + - port: metrics + selector: + matchLabels: + {{- include "event-exporter.selectorLabels" . | nindent 6 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_deployment.yaml new file mode 100644 index 0000000000..7efa6557b1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_deployment.yaml @@ -0,0 +1,70 @@ +{{- define "monitor.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "monitor.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "monitor.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "monitor.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: monitor + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include "monitor.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 9020 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /api/ping + port: 9020 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_env-vars.yaml new file mode 100644 index 0000000000..f58c7fa250 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_env-vars.yaml @@ -0,0 +1,26 @@ +{{- define "monitor.environment-variables.defaults" }} +SERVICE_NAME: {{ include "monitor.fullname" . }} +PORT: 9020 +HELM3: true +NODE_OPTIONS: "--max_old_space_size=4096" +{{- end }} + +{{- define "monitor.environment-variables.calculated" }} +API_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +CLUSTER_ID: {{ include "runtime.runtime-environment-spec.context-name" . }} +API_URL: {{ include "runtime.runtime-environment-spec.codefresh-host" . }}/api/k8s-monitor/events +ACCOUNT_ID: {{ .Values.global.accountId }} +NAMESPACE: {{ .Release.Namespace }} +{{- if .Values.rbac.namespaced }} +ROLE_BINDING: true +{{- end }} +{{- end }} + +{{- define "monitor.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "monitor.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "monitor.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_helpers.tpl new file mode 100644 index 0000000000..71cc1c027d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "monitor.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "monitor.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "monitor.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: monitor +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "monitor.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: monitor +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "monitor.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "monitor.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_rbac.yaml new file mode 100644 index 0000000000..88204796ae --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_rbac.yaml @@ -0,0 +1,56 @@ +{{- define "monitor.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "monitor.serviceAccountName" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch", "create", "delete" ] + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "create", "deletecollection" ] + - apiGroups: [ "extensions" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "apps" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "monitor.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} + name: {{ include "monitor.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_service.yaml new file mode 100644 index 0000000000..f6ae9bb0f7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/monitor/_service.yaml @@ -0,0 +1,17 @@ +{{- define "monitor.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 9020 + selector: + {{- include "monitor.selectorLabels" . | nindent 4 }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_deployment.yaml new file mode 100644 index 0000000000..e1fb9439ab --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_deployment.yaml @@ -0,0 +1,103 @@ +{{- define "runner.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "runner.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "runner.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "runner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + initContainers: + - name: init + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.init.image "context" .) }} + imagePullPolicy: {{ .Values.init.image.pullPolicy | default "IfNotPresent" }} + command: + - /bin/bash + args: + - -ec + - | {{ .Files.Get "files/init-runtime.sh" | nindent 10 }} + env: + {{- include "runner-init.environment-variables" . | nindent 8 }} + {{- with .Values.init.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + containers: + - name: runner + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }} + env: + {{- include "runner.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 8080 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /health + port: http + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.sidecar.enabled }} + - name: reconcile-runtime + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.sidecar.image "context" .) }} + imagePullPolicy: {{ .Values.sidecar.image.pullPolicy | default "IfNotPresent" }} + command: + - /bin/bash + args: + - -ec + - | {{ .Files.Get "files/reconcile-runtime.sh" | nindent 10 }} + env: + {{- include "runner-sidecar.environment-variables" . | nindent 8 }} + {{- with .Values.sidecar.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.extraVolumes }} + volumes: + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_helpers.tpl new file mode 100644 index 0000000000..2608cb67ee --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "runner.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "runner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "runner.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "runner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "runner.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: runner +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "runner.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: runner +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "runner.serviceAccountName" -}} + {{- if .Values.serviceAccount.create }} + {{- default (include "runner.fullname" .) .Values.serviceAccount.name }} + {{- else }} + {{- default "default" .Values.serviceAccount.name }} + {{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_rbac.yaml new file mode 100644 index 0000000000..d95b958d54 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/_rbac.yaml @@ -0,0 +1,53 @@ +{{- define "runner.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runner.serviceAccountName" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "pods", "persistentvolumeclaims" ] + verbs: [ "get", "create", "delete", patch ] + - apiGroups: [ "" ] + resources: [ "configmaps", "secrets" ] + verbs: [ "get", "create", "update", patch ] + - apiGroups: [ "apps" ] + resources: [ "deployments" ] + verbs: [ "get" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "runner.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ include "runner.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_init-container.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_init-container.yaml new file mode 100644 index 0000000000..6dda110f78 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_init-container.yaml @@ -0,0 +1,30 @@ +{{- define "runner-init.environment-variables.defaults" }} +HOME: /tmp +{{- end }} + +{{- define "runner-init.environment-variables.calculated" }} +AGENT_NAME: {{ include "runtime.runtime-environment-spec.agent-name" . }} +API_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +AGENT_CODEFRESH_TOKEN: + valueFrom: + secretKeyRef: + name: {{ include "runner.fullname" . }} + key: agent-codefresh-token + optional: true +EXISTING_AGENT_CODEFRESH_TOKEN: {{ include "runtime.agent-token-env-var-value" . | nindent 2 }} +KUBE_CONTEXT: {{ include "runtime.runtime-environment-spec.context-name" . }} +KUBE_NAMESPACE: {{ .Release.Namespace }} +OWNER_NAME: {{ include "runner.fullname" . }} +RUNTIME_NAME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +SECRET_NAME: {{ include "runner.fullname" . }} +USER_CODEFRESH_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +{{- end }} + +{{- define "runner-init.environment-variables" }} + {{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + {{- $defaults := (include "runner-init.environment-variables.defaults" . | fromYaml) }} + {{- $calculated := (include "runner-init.environment-variables.calculated" . | fromYaml) }} + {{- $overrides := .Values.env }} + {{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_main-container.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_main-container.yaml new file mode 100644 index 0000000000..4d3f0304e2 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_main-container.yaml @@ -0,0 +1,28 @@ +{{- define "runner.environment-variables.defaults" }} +AGENT_MODE: InCluster +SELF_DEPLOYMENT_NAME: + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- end }} + +{{- define "runner.environment-variables.calculated" }} +AGENT_ID: {{ include "runtime.runtime-environment-spec.agent-name" . }} +CODEFRESH_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +CODEFRESH_IN_CLUSTER_RUNTIME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +CODEFRESH_TOKEN: + valueFrom: + secretKeyRef: + name: {{ include "runner.fullname" . }} + key: agent-codefresh-token +DOCKER_REGISTRY: {{ .Values.global.imageRegistry }} +{{- end }} + +{{- define "runner.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "runner.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "runner.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_sidecar-container.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_sidecar-container.yaml new file mode 100644 index 0000000000..3adcbe5d49 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/runner/environment-variables/_sidecar-container.yaml @@ -0,0 +1,22 @@ +{{- define "runner-sidecar.environment-variables.defaults" }} +HOME: /tmp +{{- end }} + +{{- define "runner-sidecar.environment-variables.calculated" }} +API_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +USER_CODEFRESH_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +KUBE_CONTEXT: {{ include "runtime.runtime-environment-spec.context-name" . }} +KUBE_NAMESPACE: {{ .Release.Namespace }} +OWNER_NAME: {{ include "runner.fullname" . }} +RUNTIME_NAME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +CONFIGMAP_NAME: {{ printf "%s-%s" (include "runtime.fullname" .) "spec" }} +{{- end }} + +{{- define "runner-sidecar.environment-variables" }} + {{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + {{- $defaults := (include "runner-sidecar.environment-variables.defaults" . | fromYaml) }} + {{- $calculated := (include "runner-sidecar.environment-variables.calculated" . | fromYaml) }} + {{- $overrides := .Values.sidecar.env }} + {{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_cronjob.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_cronjob.yaml new file mode 100644 index 0000000000..20bd2d56e1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_cronjob.yaml @@ -0,0 +1,58 @@ +{{- define "dind-volume-provisioner.resources.cronjob" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- if not (eq .Values.storage.backend "local") }} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "dind-volume-cleanup.fullname" . }} + labels: + {{- include "dind-volume-cleanup.labels" . | nindent 4 }} +spec: + concurrencyPolicy: {{ .Values.concurrencyPolicy }} + schedule: {{ .Values.schedule | quote }} + successfulJobsHistoryLimit: {{ .Values.successfulJobsHistory }} + failedJobsHistoryLimit: {{ .Values.failedJobsHistory }} + {{- with .Values.suspend }} + suspend: {{ . }} + {{- end }} + jobTemplate: + spec: + template: + metadata: + labels: + {{- include "dind-volume-cleanup.selectorLabels" . | nindent 12 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 12 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 10 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + restartPolicy: {{ .Values.restartPolicy | default "Never" }} + containers: + - name: dind-volume-cleanup + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" .Values.env "context" .) | nindent 12 }} + - name: PROVISIONED_BY + value: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} + resources: + {{- toYaml .Values.resources | nindent 14 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_daemonset.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_daemonset.yaml new file mode 100644 index 0000000000..cb463231d2 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_daemonset.yaml @@ -0,0 +1,98 @@ +{{- define "dind-volume-provisioner.resources.daemonset" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $localVolumeParentDir := .Values.storage.local.volumeParentDir }} +{{- if eq .Values.storage.backend "local" }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "dind-lv-monitor.fullname" . }} + labels: + {{- include "dind-lv-monitor.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "dind-lv-monitor.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "dind-lv-monitor.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.volumePermissions.enabled }} + initContainers: + - name: volume-permissions + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.volumePermissions.image "context" .) }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | default "Always" }} + command: + - /bin/sh + args: + - -ec + - | + chown -R {{ .Values.podSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} {{ $localVolumeParentDir }} + volumeMounts: + - mountPath: {{ $localVolumeParentDir }} + name: dind-volume-dir + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 10 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 10 }} + {{- end }} + resources: + {{- toYaml .Values.volumePermissions.resources | nindent 10 }} + {{- end }} + containers: + - name: dind-lv-monitor + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - /home/dind-volume-utils/bin/local-volumes-agent + env: + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" .Values.env "context" .) | nindent 10 }} + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: VOLUME_PARENT_DIR + value: {{ $localVolumeParentDir }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + volumeMounts: + - mountPath: {{ $localVolumeParentDir }} + readOnly: false + name: dind-volume-dir + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + - name: dind-volume-dir + hostPath: + path: {{ $localVolumeParentDir }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_deployment.yaml new file mode 100644 index 0000000000..9252b45200 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_deployment.yaml @@ -0,0 +1,67 @@ +{{- define "dind-volume-provisioner.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "dind-volume-provisioner.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "dind-volume-provisioner.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: dind-volume-provisioner + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + command: + - /usr/local/bin/dind-volume-provisioner + - -v=4 + - --resync-period=50s + env: + {{- include "dind-volume-provisioner.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 8080 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- include "dind-volume-provisioner.volumeMounts.calculated" . | nindent 8 }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- include "dind-volume-provisioner.volumes.calculated" . | nindent 6 }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_env-vars.yaml new file mode 100644 index 0000000000..e1f5dfe603 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_env-vars.yaml @@ -0,0 +1,88 @@ +{{- define "dind-volume-provisioner.environment-variables.defaults" }} +{{- end }} + +{{- define "dind-volume-provisioner.environment-variables.calculated" }} +DOCKER_REGISTRY: {{ .Values.global.imageRegistry }} +PROVISIONER_NAME: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} + +{{- if or .Values.storage.ebs.accessKeyId .Values.storage.ebs.accessKeyIdSecretKeyRef }} +AWS_ACCESS_KEY_ID: + {{- if .Values.storage.ebs.accessKeyId }} + valueFrom: + secretKeyRef: + name: {{ include "dind-volume-provisioner.fullname" . }} + key: aws_access_key_id + {{- else if .Values.storage.ebs.accessKeyIdSecretKeyRef }} + valueFrom: + secretKeyRef: + {{- .Values.storage.ebs.accessKeyIdSecretKeyRef | toYaml | nindent 6 }} + {{- end }} +{{- end }} + +{{- if or .Values.storage.ebs.secretAccessKey .Values.storage.ebs.secretAccessKeySecretKeyRef }} +AWS_SECRET_ACCESS_KEY: + {{- if .Values.storage.ebs.secretAccessKey }} + valueFrom: + secretKeyRef: + name: {{ include "dind-volume-provisioner.fullname" . }} + key: aws_secret_access_key + {{- else if .Values.storage.ebs.secretAccessKeySecretKeyRef }} + valueFrom: + secretKeyRef: + {{- .Values.storage.ebs.secretAccessKeySecretKeyRef | toYaml | nindent 6 }} + {{- end }} +{{- end }} + +{{- if or .Values.storage.gcedisk.serviceAccountJson .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +GOOGLE_APPLICATION_CREDENTIALS: {{ printf "/etc/dind-volume-provisioner/credentials/%s" (.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef.key | default "google-service-account.json") }} +{{- end }} + +{{- if and .Values.storage.mountAzureJson }} +AZURE_CREDENTIAL_FILE: /etc/kubernetes/azure.json +CLOUDCONFIG_AZURE: /etc/kubernetes/azure.json +{{- end }} + +{{- end }} + +{{- define "dind-volume-provisioner.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "dind-volume-provisioner.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "dind-volume-provisioner.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} + + +{{- define "dind-volume-provisioner.volumes.calculated" }} + {{- if .Values.storage.gcedisk.serviceAccountJson }} +- name: credentials + secret: + secretName: {{ include "dind-volume-provisioner.fullname" . }} + optional: true + {{- else if .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +- name: credentials + secret: + secretName: {{ .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef.name }} + optional: true + {{- end }} + {{- if .Values.storage.mountAzureJson }} +- name: azure-json + hostPath: + path: /etc/kubernetes/azure.json + type: File + {{- end }} +{{- end }} + +{{- define "dind-volume-provisioner.volumeMounts.calculated" }} + {{- if or .Values.storage.gcedisk.serviceAccountJson .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +- name: credentials + readOnly: true + mountPath: "/etc/dind-volume-provisioner/credentials" + {{- end }} + {{- if .Values.storage.mountAzureJson }} +- name: azure-json + readOnly: true + mountPath: "/etc/kubernetes/azure.json" + {{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_helpers.tpl new file mode 100644 index 0000000000..e3d3a0d3f7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_helpers.tpl @@ -0,0 +1,93 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "dind-volume-provisioner.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "volume-provisioner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "dind-volume-provisioner.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "volume-provisioner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "dind-volume-cleanup.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "volume-cleanup" | trunc 52 | trimSuffix "-" }} +{{- end }} + +{{- define "dind-lv-monitor.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "lv-monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Provisioner name for storage class +*/}} +{{- define "dind-volume-provisioner.volumeProvisionerName" }} + {{- printf "codefresh.io/dind-volume-provisioner-runner-%s" .Release.Namespace }} +{{- end }} + +{{/* +Common labels for dind-lv-monitor +*/}} +{{- define "dind-lv-monitor.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: lv-monitor +{{- end }} + +{{/* +Selector labels for dind-lv-monitor +*/}} +{{- define "dind-lv-monitor.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: lv-monitor +{{- end }} + +{{/* +Common labels for dind-volume-provisioner +*/}} +{{- define "dind-volume-provisioner.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: volume-provisioner +{{- end }} + +{{/* +Selector labels for dind-volume-provisioner +*/}} +{{- define "dind-volume-provisioner.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: volume-provisioner +{{- end }} + +{{/* +Common labels for dind-volume-cleanup +*/}} +{{- define "dind-volume-cleanup.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: pv-cleanup +{{- end }} + +{{/* +Common labels for dind-volume-cleanup +*/}} +{{- define "dind-volume-cleanup.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: pv-cleanup +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "dind-volume-provisioner.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "dind-volume-provisioner.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "dind-volume-provisioner.storageClassName" }} +{{- printf "dind-local-volumes-runner-%s" .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_rbac.yaml new file mode 100644 index 0000000000..fbcbc684fc --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_rbac.yaml @@ -0,0 +1,71 @@ +{{- define "dind-volume-provisioner.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "dind-volume-provisioner.serviceAccountName" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "persistentvolumes" ] + verbs: [ "get", "list", "watch", "create", "delete", "patch" ] + - apiGroups: [ "" ] + resources: [ "persistentvolumeclaims" ] + verbs: [ "get", "list", "watch", "update", "delete" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "list", "watch", "create", "update", "patch" ] + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get", "list" ] + - apiGroups: [ "" ] + resources: [ "nodes" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "create", "delete", "patch" ] + - apiGroups: [ "" ] + resources: [ "endpoints" ] + verbs: [ "get", "list", "watch", "create", "update", "delete" ] + - apiGroups: [ "coordination.k8s.io" ] + resources: [ "leases" ] + verbs: [ "get", "create", "update" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "dind-volume-provisioner.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "dind-volume-provisioner.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_secret.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_secret.yaml new file mode 100644 index 0000000000..f361a79910 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_secret.yaml @@ -0,0 +1,22 @@ +{{- define "dind-volume-provisioner.resources.secret" -}} +{{- if or .Values.storage.ebs.accessKeyId .Values.storage.ebs.secretAccessKey .Values.storage.gcedisk.serviceAccountJson }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +stringData: + {{- with .Values.storage.gcedisk.serviceAccountJson }} + google-service-account.json: | +{{- . | nindent 4 }} + {{- end }} + {{- with .Values.storage.ebs.accessKeyId }} + aws_access_key_id: {{ . }} + {{- end }} + {{- with .Values.storage.ebs.secretAccessKey }} + aws_secret_access_key: {{ . }} + {{- end }} +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_storageclass.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_storageclass.yaml new file mode 100644 index 0000000000..62e910c87e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_components/volume-provisioner/_storageclass.yaml @@ -0,0 +1,47 @@ +{{- define "dind-volume-provisioner.resources.storageclass" -}} +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + {{/* has to be exactly that */}} + name: {{ include "dind-volume-provisioner.storageClassName" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +provisioner: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} +parameters: +{{- if eq .Values.storage.backend "local" }} + volumeBackend: local + volumeParentDir: {{ .Values.storage.local.volumeParentDir }} +{{- else if eq .Values.storage.backend "gcedisk" }} + volumeBackend: {{ .Values.storage.backend }} + type: {{ .Values.storage.gcedisk.volumeType | default "pd-ssd" }} + zone: {{ required ".Values.storage.gcedisk.availabilityZone is required" .Values.storage.gcedisk.availabilityZone }} + fsType: {{ .Values.storage.fsType | default "ext4" }} +{{- else if or (eq .Values.storage.backend "ebs") (eq .Values.storage.backend "ebs-csi")}} + volumeBackend: {{ .Values.storage.backend }} + VolumeType: {{ .Values.storage.ebs.volumeType | default "gp3" }} + AvailabilityZone: {{ required ".Values.storage.ebs.availabilityZone is required" .Values.storage.ebs.availabilityZone }} + fsType: {{ .Values.storage.fsType | default "ext4" }} + encrypted: {{ .Values.storage.ebs.encrypted | default "false" | quote }} + {{- with .Values.storage.ebs.kmsKeyId }} + kmsKeyId: {{ . | quote }} + {{- end }} + {{- with .Values.storage.ebs.iops }} + iops: {{ . | quote }} + {{- end }} + {{- with .Values.storage.ebs.throughput }} + throughput: {{ . | quote }} + {{- end }} +{{- else if or (eq .Values.storage.backend "azuredisk") (eq .Values.storage.backend "azuredisk-csi")}} + volumeBackend: {{ .Values.storage.backend }} + kind: managed + skuName: {{ .Values.storage.azuredisk.skuName | default "Premium_LRS" }} + fsType: {{ .Values.storage.fsType | default "ext4" }} + cachingMode: {{ .Values.storage.azuredisk.cachingMode | default "None" }} + {{- with .Values.storage.azuredisk.availabilityZone }} + availabilityZone: {{ . | quote }} + {{- end }} + {{- with .Values.storage.azuredisk.resourceGroup }} + resourceGroup: {{ . | quote }} + {{- end }} +{{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/_helpers.tpl new file mode 100644 index 0000000000..72f44e36af --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cf-runtime.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cf-runtime.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cf-runtime.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cf-runtime.labels" -}} +helm.sh/chart: {{ include "cf-runtime.chart" . }} +{{ include "cf-runtime.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "cf-runtime.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cf-runtime.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/deployment.yaml new file mode 100644 index 0000000000..90341b3059 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/deployment.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.deployment" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/ingress.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/ingress.yaml new file mode 100644 index 0000000000..56ab5e95ea --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/ingress.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.ingress" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/rbac.yaml new file mode 100644 index 0000000000..4db87dcb45 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/rbac.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.rbac" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/service.yaml new file mode 100644 index 0000000000..0b9d85ec0d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/app-proxy/service.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.service" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/deployment.yaml new file mode 100644 index 0000000000..4942882407 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/deployment.yaml @@ -0,0 +1,9 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.deployment" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/rbac.yaml new file mode 100644 index 0000000000..6a9bf5c65a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/rbac.yaml @@ -0,0 +1,9 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.rbac" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/service.yaml new file mode 100644 index 0000000000..c5d856dfe3 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/event-exporter/service.yaml @@ -0,0 +1,11 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.service" $eventExporterContext }} +--- +{{- include "event-exporter.resources.serviceMonitor" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/extra/extra-resources.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/extra/extra-resources.yaml new file mode 100644 index 0000000000..1a9777c649 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/extra/extra-resources.yaml @@ -0,0 +1,6 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + +{{- range .Values.extraResources }} +--- +{{ include (printf "%s.tplrender" $cfCommonTplSemver) (dict "Values" . "context" $) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/extra/runtime-images-cm.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/extra/runtime-images-cm.yaml new file mode 100644 index 0000000000..f269c84b2b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/extra/runtime-images-cm.yaml @@ -0,0 +1,19 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.engine.runtimeImages }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + {{- /* dummy template just to list runtime images */}} + name: {{ include "runtime.fullname" . }}-images + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + images: | + {{- range $key, $val := $values }} + image: {{ $val }} + {{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/cm-update-runtime.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/cm-update-runtime.yaml new file mode 100644 index 0000000000..46a306c560 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/cm-update-runtime.yaml @@ -0,0 +1,18 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if $values.enabled }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "runtime.fullname" . }}-spec + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + runtime.yaml: | + {{ include "runtime.runtime-environment-spec.template" . | nindent 4 | trim }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-gencerts-dind.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-gencerts-dind.yaml new file mode 100644 index 0000000000..4a08a229c8 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-gencerts-dind.yaml @@ -0,0 +1,68 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.gencerts }} +{{- if and $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "3" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + {{- if $values.rbac.enabled }} + serviceAccountName: {{ template "runtime.fullname" . }}-gencerts-dind + {{- end }} + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: gencerts-dind + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | {{ .Files.Get "files/configure-dind-certs.sh" | nindent 10 }} + env: + - name: NAMESPACE + value: {{ .Release.Namespace }} + - name: RELEASE + value: {{ .Release.Name }} + - name: CF_API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + - name: CF_API_TOKEN + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-update-runtime.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-update-runtime.yaml new file mode 100644 index 0000000000..955e882d77 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/job-update-runtime.yaml @@ -0,0 +1,77 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-patch + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "5" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-patch + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: patch-runtime + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | + codefresh auth create-context --api-key $API_KEY --url $API_HOST + cat /usr/share/extras/runtime.yaml + codefresh get re +{{- if .Values.runtime.agent }} + codefresh patch re -f /usr/share/extras/runtime.yaml +{{- else }} + codefresh patch sys-re -f /usr/share/extras/runtime.yaml +{{- end }} + env: + - name: API_KEY + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + - name: API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + volumeMounts: + - name: config + mountPath: /usr/share/extras/runtime.yaml + subPath: runtime.yaml + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure + volumes: + - name: config + configMap: + name: {{ include "runtime.fullname" . }}-spec +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/rbac-gencerts-dind.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/rbac-gencerts-dind.yaml new file mode 100644 index 0000000000..4907dac380 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/post-install/rbac-gencerts-dind.yaml @@ -0,0 +1,37 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.gencerts }} +{{- if and $values.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - "" + resources: + - secrets + - configmaps + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "runtime.fullname" . }}-gencerts-dind +subjects: + - kind: ServiceAccount + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/job-cleanup-resources.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/job-cleanup-resources.yaml new file mode 100644 index 0000000000..0e3c7659f1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/job-cleanup-resources.yaml @@ -0,0 +1,73 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if and $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-delete + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-cleanup + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + {{- if $values.rbac.enabled }} + serviceAccountName: {{ template "runtime.fullname" . }}-cleanup + {{- end }} + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: cleanup + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | {{ .Files.Get "files/cleanup-runtime.sh" | nindent 10 }} + env: + - name: AGENT_NAME + value: {{ include "runtime.runtime-environment-spec.agent-name" . }} + - name: RUNTIME_NAME + value: {{ include "runtime.runtime-environment-spec.runtime-name" . }} + - name: API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + - name: API_TOKEN + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + - name: AGENT + value: {{ .Values.runtime.agent | quote }} + - name: AGENT_SECRET_NAME + value: {{ include "runner.fullname" . }} + - name: DIND_SECRET_NAME + value: codefresh-certs-server + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/rbac-cleanup-resources.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/rbac-cleanup-resources.yaml new file mode 100644 index 0000000000..468ec2212d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/hooks/pre-delete/rbac-cleanup-resources.yaml @@ -0,0 +1,46 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if and $values.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +rules: + - apiGroups: + - "*" + resources: + - "*" + verbs: + - "*" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "runtime.fullname" . }}-cleanup +subjects: + - kind: ServiceAccount + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/monitor/deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/deployment.yaml new file mode 100644 index 0000000000..00c9fb2f91 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/deployment.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.deployment" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/monitor/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/rbac.yaml new file mode 100644 index 0000000000..f9812d565d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/rbac.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.rbac" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/monitor/service.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/service.yaml new file mode 100644 index 0000000000..f99706614a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/monitor/service.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.service" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/other/external-secrets.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/other/external-secrets.yaml new file mode 100644 index 0000000000..dc24e24e51 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/other/external-secrets.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.external-secrets" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/other/podMonitor.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/other/podMonitor.yaml new file mode 100644 index 0000000000..4319b722b9 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/other/podMonitor.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.podMonitor" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/other/serviceMonitor.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/other/serviceMonitor.yaml new file mode 100644 index 0000000000..29f890fe2b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/other/serviceMonitor.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.serviceMonitor" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runner/deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runner/deployment.yaml new file mode 100644 index 0000000000..85777c487f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runner/deployment.yaml @@ -0,0 +1,9 @@ +{{- $runnerContext := deepCopy . }} +{{- $_ := set $runnerContext "Values" (get .Values "runner") }} +{{- $_ := set $runnerContext.Values "global" (get .Values "global") }} +{{- $_ := set $runnerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $runnerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $runnerContext.Values.enabled .Values.runtime.agent }} +{{- include "runner.resources.deployment" $runnerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runner/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runner/rbac.yaml new file mode 100644 index 0000000000..d5f8c13233 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runner/rbac.yaml @@ -0,0 +1,9 @@ +{{- $runnerContext := deepCopy . }} +{{- $_ := set $runnerContext "Values" (get .Values "runner") }} +{{- $_ := set $runnerContext.Values "global" (get .Values "global") }} +{{- $_ := set $runnerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $runnerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $runnerContext.Values.enabled .Values.runtime.agent }} +{{- include "runner.resources.rbac" $runnerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/_helpers.tpl new file mode 100644 index 0000000000..6ba04fcc3e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/_helpers.tpl @@ -0,0 +1,123 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "runtime.name" -}} + {{- printf "%s" (include "cf-runtime.name" .) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "runtime.fullname" -}} + {{- printf "%s" (include "cf-runtime.fullname" .) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "runtime.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: runtime +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "runtime.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: runtime +{{- end }} + +{{/* +Return runtime image (classic runtime) with private registry prefix +*/}} +{{- define "runtime.runtimeImageName" -}} + {{- if .registry -}} + {{- $imageName := (trimPrefix "quay.io/" .imageFullName) -}} + {{- printf "%s/%s" .registry $imageName -}} + {{- else -}} + {{- printf "%s" .imageFullName -}} + {{- end -}} +{{- end -}} + +{{/* +Environment variable value of Codefresh installation token +*/}} +{{- define "runtime.installation-token-env-var-value" -}} + {{- if .Values.global.codefreshToken }} +valueFrom: + secretKeyRef: + name: {{ include "runtime.installation-token-secret-name" . }} + key: codefresh-api-token + {{- else if .Values.global.codefreshTokenSecretKeyRef }} +valueFrom: + secretKeyRef: + {{- .Values.global.codefreshTokenSecretKeyRef | toYaml | nindent 4 }} + {{- end }} +{{- end }} + +{{/* +Environment variable value of Codefresh agent token +*/}} +{{- define "runtime.agent-token-env-var-value" -}} + {{- if .Values.global.agentToken }} +{{- printf "%s" .Values.global.agentToken | toYaml }} + {{- else if .Values.global.agentTokenSecretKeyRef }} +valueFrom: + secretKeyRef: + {{- .Values.global.agentTokenSecretKeyRef | toYaml | nindent 4 }} + {{- end }} +{{- end }} + +{{/* +Print Codefresh API token secret name +*/}} +{{- define "runtime.installation-token-secret-name" }} +{{- print "codefresh-user-token" }} +{{- end }} + +{{/* +Print Codefresh host +*/}} +{{- define "runtime.runtime-environment-spec.codefresh-host" }} +{{- if and (not .Values.global.codefreshHost) }} + {{- fail "ERROR: .global.codefreshHost is required" }} +{{- else }} + {{- printf "%s" (trimSuffix "/" .Values.global.codefreshHost) }} +{{- end }} +{{- end }} + +{{/* +Print runtime-environment name +*/}} +{{- define "runtime.runtime-environment-spec.runtime-name" }} +{{- if and (not .Values.global.runtimeName) }} + {{- printf "%s/%s" .Values.global.context .Release.Namespace }} +{{- else }} + {{- printf "%s" .Values.global.runtimeName }} +{{- end }} +{{- end }} + +{{/* +Print agent name +*/}} +{{- define "runtime.runtime-environment-spec.agent-name" }} +{{- if and (not .Values.global.agentName) }} + {{- printf "%s_%s" .Values.global.context .Release.Namespace }} +{{- else }} + {{- printf "%s" .Values.global.agentName }} +{{- end }} +{{- end }} + +{{/* +Print context +*/}} +{{- define "runtime.runtime-environment-spec.context-name" }} +{{- if and (not .Values.global.context) }} + {{- fail "ERROR: .global.context is required" }} +{{- else }} + {{- printf "%s" .Values.global.context }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/cm-dind-daemon.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/cm-dind-daemon.yaml new file mode 100644 index 0000000000..fc7f92905b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/cm-dind-daemon.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + {{- /* has to be a constant */}} + name: codefresh-dind-config + labels: + {{- include "runtime.labels" . | nindent 4 }} +data: + daemon.json: | +{{ coalesce .Values.re.dindDaemon .Values.runtime.dindDaemon | toPrettyJson | indent 4 }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/rbac.yaml new file mode 100644 index 0000000000..a51b125262 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/rbac.yaml @@ -0,0 +1,48 @@ +{{ $values := .Values.runtime }} +--- +{{- if or $values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- /* has to be a constant */}} + name: codefresh-engine + labels: + {{- include "runtime.labels" . | nindent 4 }} + {{- with $values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if $values.rbac.create }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: codefresh-engine + labels: + {{- include "runner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get" ] +{{- with $values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and $values.serviceAccount.create $values.rbac.create }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: codefresh-engine + labels: + {{- include "runner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: codefresh-engine +roleRef: + kind: Role + name: codefresh-engine + apiGroup: rbac.authorization.k8s.io +{{- end }} + diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/runtime-env-spec-tmpl.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/runtime-env-spec-tmpl.yaml new file mode 100644 index 0000000000..e2094e0037 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/runtime-env-spec-tmpl.yaml @@ -0,0 +1,206 @@ +{{- define "runtime.runtime-environment-spec.template" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version -}} +{{- $kubeconfigFilePath := (include "runtime.runtime-environment-spec.runtime-name" .) -}} +{{- $name := (include "runtime.runtime-environment-spec.runtime-name" .) -}} +{{- $engineContext := .Values.runtime.engine -}} +{{- $dindContext := .Values.runtime.dind -}} +{{- $imageRegistry := .Values.global.imageRegistry -}} +metadata: + name: {{ include "runtime.runtime-environment-spec.runtime-name" . }} + agent: {{ .Values.runtime.agent }} +runtimeScheduler: + type: KubernetesPod + {{- if $engineContext.image }} + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $engineContext.image "context" .) | squote }} + {{- end }} + imagePullPolicy: {{ $engineContext.image.pullPolicy }} + {{- with $engineContext.command }} + command: {{- toYaml . | nindent 4 }} + {{- end }} + envVars: + {{- with $engineContext.env }} + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + COMPOSE_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.COMPOSE_IMAGE) | squote }} + CONTAINER_LOGGER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.CONTAINER_LOGGER_IMAGE) | squote }} + DOCKER_BUILDER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_BUILDER_IMAGE) | squote }} + DOCKER_PULLER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_PULLER_IMAGE) | squote }} + DOCKER_PUSHER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_PUSHER_IMAGE) | squote }} + DOCKER_TAG_PUSHER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_TAG_PUSHER_IMAGE) | squote }} + FS_OPS_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.FS_OPS_IMAGE) | squote }} + GIT_CLONE_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.GIT_CLONE_IMAGE) | squote }} + KUBE_DEPLOY: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.KUBE_DEPLOY) | squote }} + PIPELINE_DEBUGGER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.PIPELINE_DEBUGGER_IMAGE) | squote }} + TEMPLATE_ENGINE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.TEMPLATE_ENGINE) | squote }} + CR_6177_FIXER: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.CR_6177_FIXER) | squote }} + GC_BUILDER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.GC_BUILDER_IMAGE) | squote }} + COSIGN_IMAGE_SIGNER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.COSIGN_IMAGE_SIGNER_IMAGE) | squote }} + {{- with $engineContext.userEnvVars }} + userEnvVars: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.workflowLimits }} + workflowLimits: {{- toYaml . | nindent 4 }} + {{- end }} + cluster: + namespace: {{ .Release.Namespace }} + serviceAccount: {{ $engineContext.serviceAccount }} + {{- if .Values.runtime.agent }} + clusterProvider: + accountId: {{ .Values.global.accountId }} + selector: {{ include "runtime.runtime-environment-spec.context-name" . }} + {{- else }} + {{- if .Values.runtime.inCluster }} + inCluster: true + kubeconfigFilePath: null + {{- else }} + name: {{ $name }} + kubeconfigFilePath: {{ printf "/etc/kubeconfig/%s" $kubeconfigFilePath }} + {{- end }} + {{- end }} + {{- with $engineContext.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with $engineContext.affinity }} + affinity: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.tolerations }} + tolerations: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.podAnnotations }} + annotations: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + {{- with $engineContext.podLabels }} + labels: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $engineContext.schedulerName }} + schedulerName: {{ $engineContext.schedulerName }} + {{- end }} + resources: + {{- if $engineContext.resources}} + {{- toYaml $engineContext.resources | nindent 4 }} + {{- end }} + {{- with $engineContext.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} +dockerDaemonScheduler: + type: DindKubernetesPod + {{- if $dindContext.image }} + dindImage: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $dindContext.image "context" .) | squote }} + {{- end }} + imagePullPolicy: {{ $dindContext.image.pullPolicy }} + {{- with $dindContext.userAccess }} + userAccess: {{ . }} + {{- end }} + {{- with $dindContext.env }} + envVars: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + cluster: + namespace: {{ .Release.Namespace }} + serviceAccount: {{ $dindContext.serviceAccount }} + {{- if .Values.runtime.agent }} + clusterProvider: + accountId: {{ .Values.global.accountId }} + selector: {{ include "runtime.runtime-environment-spec.context-name" . }} + {{- else }} + {{- if .Values.runtime.inCluster }} + inCluster: true + kubeconfigFilePath: null + {{- else }} + name: {{ $name }} + kubeconfigFilePath: {{ printf "/etc/kubeconfig/%s" $kubeconfigFilePath }} + {{- end }} + {{- end }} + {{- with $dindContext.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with $dindContext.affinity }} + affinity: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.tolerations }} + tolerations: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.podAnnotations }} + annotations: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + {{- with $dindContext.podLabels }} + labels: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $dindContext.schedulerName }} + schedulerName: {{ $dindContext.schedulerName }} + {{- end }} + {{- if $dindContext.pvcs }} + pvcs: + {{- range $index, $pvc := $dindContext.pvcs }} + - name: {{ $pvc.name }} + reuseVolumeSelector: {{ $pvc.reuseVolumeSelector | squote }} + reuseVolumeSortOrder: {{ $pvc.reuseVolumeSortOrder }} + storageClassName: {{ include (printf "%v.tplrender" $cfCommonTplSemver) (dict "Values" $pvc.storageClassName "context" $) }} + volumeSize: {{ $pvc.volumeSize }} + {{- with $pvc.annotations }} + annotations: {{ . | toYaml | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + defaultDindResources: + {{- with $dindContext.resources }} + {{- if not .requests }} + limits: {{- toYaml .limits | nindent 6 }} + requests: null + {{- else }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} + {{- with $dindContext.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + {{- with $dindContext.userVolumeMounts }} + userVolumeMounts: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.userVolumes }} + userVolumes: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if and (not .Values.runtime.agent) }} + clientCertPath: /etc/ssl/cf/ + volumeMounts: + codefresh-certs-server: + name: codefresh-certs-server + mountPath: /etc/ssl/cf + readOnly: false + volumes: + codefresh-certs-server: + name: codefresh-certs-server + secret: + secretName: codefresh-certs-server + {{- end }} +extends: {{- toYaml .Values.runtime.runtimeExtends | nindent 2 }} + {{- if .Values.runtime.description }} +description: {{ .Values.runtime.description }} + {{- else }} +description: null + {{- end }} +{{- if .Values.global.accountId }} +accountId: {{ .Values.global.accountId }} +{{- end }} +{{- if not .Values.runtime.agent }} +accounts: {{- toYaml .Values.runtime.accounts | nindent 2 }} +{{- end }} +{{- if .Values.appProxy.enabled }} +appProxy: + externalIP: >- + {{ printf "https://%s%s" .Values.appProxy.ingress.host (.Values.appProxy.ingress.pathPrefix | default "/") }} +{{- end }} +{{- if not .Values.runtime.agent }} +systemHybrid: true +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/secret.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/secret.yaml new file mode 100644 index 0000000000..2366d3ccf6 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/secret.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.global.codefreshToken }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "runtime.installation-token-secret-name" . }} + labels: + {{- include "runtime.labels" . | nindent 4 }} +stringData: + codefresh-api-token: {{ .Values.global.codefreshToken }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/runtime/svc-dind.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/svc-dind.yaml new file mode 100644 index 0000000000..098edb4e87 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/runtime/svc-dind.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "runtime.labels" . | nindent 4 }} + app: dind + {{/* has to be a constant */}} + name: dind +spec: + ports: + - name: "dind-port" + port: 1300 + protocol: TCP + clusterIP: None + selector: + app: dind diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/cronjob.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/cronjob.yaml new file mode 100644 index 0000000000..db955bc771 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/cronjob.yaml @@ -0,0 +1,11 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values.volumeProvisioner "dind-volume-cleanup") }} +{{- $_ := set $volumeProvisionerContext.Values "serviceAccount" (get .Values.volumeProvisioner "serviceAccount") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $volumeProvisionerContext.Values.enabled .Values.volumeProvisioner.enabled }} +{{- include "dind-volume-provisioner.resources.cronjob" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/daemonset.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/daemonset.yaml new file mode 100644 index 0000000000..39927149e8 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/daemonset.yaml @@ -0,0 +1,11 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values.volumeProvisioner "dind-lv-monitor") }} +{{- $_ := set $volumeProvisionerContext.Values "serviceAccount" (get .Values.volumeProvisioner "serviceAccount") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $volumeProvisionerContext.Values.enabled .Values.volumeProvisioner.enabled }} +{{- include "dind-volume-provisioner.resources.daemonset" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/deployment.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/deployment.yaml new file mode 100644 index 0000000000..522fa8791f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/deployment.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.deployment" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/rbac.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/rbac.yaml new file mode 100644 index 0000000000..f3ae9609f9 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/rbac.yaml @@ -0,0 +1,9 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.rbac" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/secret.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/secret.yaml new file mode 100644 index 0000000000..accf601d13 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/secret.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.secret" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/storageclass.yaml b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/storageclass.yaml new file mode 100644 index 0000000000..77a7602da1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/templates/volume-provisioner/storageclass.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.storageclass" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/6.4.7/values.yaml b/charts/codefresh/cf-runtime/6.4.7/values.yaml new file mode 100644 index 0000000000..e843ca1a6c --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.7/values.yaml @@ -0,0 +1,951 @@ +# -- String to partially override cf-runtime.fullname template (will maintain the release name) +nameOverride: "" +# -- String to fully override cf-runtime.fullname template +fullnameOverride: "" + +# -- Global parameters +# @default -- See below +global: + # -- Global Docker image registry + imageRegistry: "" + # -- Global Docker registry secret names as array + imagePullSecrets: [] + + # -- URL of Codefresh Platform (required!) + codefreshHost: "https://g.codefresh.io" + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used `{{ .Values.global.context }}_{{ .Release.Namespace }}` + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used `{{ .Values.global.context }}/{{ .Release.Namespace }}` + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace + + # -- DEPRECATED Agent token in plain text. + # !!! MUST BE provided if migrating from < 6.x chart version + agentToken: "" + # -- DEPRECATED Agent token that references an existing secret containing API key. + # !!! MUST BE provided if migrating from < 6.x chart version + agentTokenSecretKeyRef: {} + # E.g. + # agentTokenSecretKeyRef: + # name: my-codefresh-agent-secret + # key: codefresh-agent-token + +# DEPRECATED -- Use `.Values.global.imageRegistry` instead +dockerRegistry: "" + +# DEPRECATED -- Use `.Values.runtime` instead +re: {} + +# -- Runner parameters +# @default -- See below +runner: + # -- Enable the runner + enabled: true + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + + # -- Set image + image: + registry: quay.io + repository: codefresh/venona + tag: 1.10.2 + + # -- Init container + init: + image: + registry: quay.io + repository: codefresh/cli + tag: 0.85.0-rootless + + resources: + limits: + memory: 512Mi + cpu: '1' + requests: + memory: 256Mi + cpu: '0.2' + + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: false + image: + registry: quay.io + repository: codefresh/codefresh-shell + tag: 0.0.2 + env: + RECONCILE_INTERVAL: 300 + resources: {} + + # -- Add additional env vars + env: {} + # E.g. + # env: + # WORKFLOW_CONCURRENCY: 50 # The number of workflow creation and termination tasks the Runner can handle in parallel. Defaults to 50 + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: true + runAsUser: 10001 + runAsGroup: 10001 + fsGroup: 10001 + + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + # -- Set requests and limits + resources: {} + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + +# -- Volume Provisioner parameters +# @default -- See below +volumeProvisioner: + # -- Enable volume-provisioner + enabled: true + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: Recreate + # -- Set pod annotations + podAnnotations: {} + + # -- Set image + image: + registry: quay.io + repository: codefresh/dind-volume-provisioner + tag: 1.35.0 + # -- Add additional env vars + env: {} + # E.g. + # env: + # THREADINESS: 4 # The number of PVC requests the dind-volume-provisioner can process in parallel. Defaults to 4 + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + # E.g. + # serviceAccount: + # annotations: + # eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: true + runAsUser: 3000 + runAsGroup: 3000 + fsGroup: 3000 + + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + + # -- `dind-lv-monitor` DaemonSet parameters + # (local volumes cleaner) + # @default -- See below + dind-lv-monitor: + enabled: true + image: + registry: quay.io + repository: codefresh/dind-volume-utils + tag: 1.29.4 + podAnnotations: {} + podSecurityContext: + enabled: true + runAsUser: 1000 + fsGroup: 1000 + containerSecurityContext: {} + env: {} + resources: {} + nodeSelector: {} + tolerations: + - key: 'codefresh/dind' + operator: 'Exists' + effect: 'NoSchedule' + volumePermissions: + enabled: true + image: + registry: docker.io + repository: alpine + tag: 3.18 + resources: {} + securityContext: + runAsUser: 0 # auto + + # `dind-volume-cleanup` CronJob parameters + # (external volumes cleaner) + # @default -- See below + dind-volume-cleanup: + enabled: true + image: + registry: quay.io + repository: codefresh/dind-volume-cleanup + tag: 1.2.0 + env: {} + concurrencyPolicy: Forbid + schedule: "*/10 * * * *" + successfulJobsHistory: 3 + failedJobsHistory: 1 + suspend: false + podAnnotations: {} + podSecurityContext: + enabled: true + fsGroup: 3000 + runAsGroup: 3000 + runAsUser: 3000 + nodeSelector: {} + affinity: {} + tolerations: [] + +# Storage parameters for volume-provisioner +# @default -- See below +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: local + # -- Set filesystem type (`ext4`/`xfs`) + fsType: "ext4" + + # Storage parametrs example for local volumes on the K8S nodes filesystem (i.e. `storage.backend=local`) + # https://kubernetes.io/docs/concepts/storage/volumes/#local + # @default -- See below + local: + # -- Set volume path on the host filesystem + volumeParentDir: /var/lib/codefresh/dind-volumes + + # Storage parameters example for aws ebs disks (i.e. `storage.backend=ebs`/`storage.backend=ebs-csi`) + # https://aws.amazon.com/ebs/ + # https://codefresh.io/docs/docs/installation/codefresh-runner/#aws-backend-volume-configuration + # @default -- See below + ebs: + # -- Set EBS volume type (`gp2`/`gp3`/`io1`) (required) + volumeType: "gp2" + # -- Set EBS volumes availability zone (required) + availabilityZone: "us-east-1a" + # -- Enable encryption (optional) + encrypted: "false" + # -- Set KMS encryption key ID (optional) + kmsKeyId: "" + + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: + + # E.g. + # ebs: + # volumeType: gp3 + # availabilityZone: us-east-1c + # encrypted: false + # iops: "5000" + # # I/O operations per second. Only effetive when gp3 volume type is specified. + # # Default value - 3000. + # # Max - 16,000 + # throughput: "500" + # # Throughput in MiB/s. Only effective when gp3 volume type is specified. + # # Default value - 125. + # # Max - 1000. + # ebs: + # volumeType: gp2 + # availabilityZone: us-east-1c + # encrypted: true + # kmsKeyId: "1234abcd-12ab-34cd-56ef-1234567890ab" + # accessKeyId: "MYKEYID" + # secretAccessKey: "MYACCESSKEY" + + # Storage parameters example for gce disks + # https://cloud.google.com/compute/docs/disks#pdspecs + # https://codefresh.io/docs/docs/installation/codefresh-runner/#gke-google-kubernetes-engine-backend-volume-configuration + # @default -- See below + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-ssd" + # -- Set GCP volume availability zone + availabilityZone: "us-west1-a" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: "" + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g. + # gcedisk: + # volumeType: pd-ssd + # availabilityZone: us-central1-c + # serviceAccountJson: |- + # { + # "type": "service_account", + # "project_id": "...", + # "private_key_id": "...", + # "private_key": "...", + # "client_email": "...", + # "client_id": "...", + # "auth_uri": "...", + # "token_uri": "...", + # "auth_provider_x509_cert_url": "...", + # "client_x509_cert_url": "..." + # } + + # Storage parameters example for Azure Disks + # https://codefresh.io/docs/docs/installation/codefresh-runner/#install-codefresh-runner-on-azure-kubernetes-service-aks + # @default -- See below + azuredisk: + # -- Set storage type (`Premium_LRS`) + skuName: Premium_LRS + cachingMode: None + # availabilityZone: northeurope-1 + # resourceGroup: + # DiskIOPSReadWrite: 500 + # DiskMBpsReadWrite: 100 + + mountAzureJson: false + +# -- Set runtime parameters +# @default -- See below + +runtime: + # -- Set annotation on engine Service Account + # Ref: https://codefresh.io/docs/docs/administration/codefresh-runner/#injecting-aws-arn-roles-into-the-cluster + serviceAccount: + create: true + annotations: {} + # E.g. + # serviceAccount: + # annotations: + # eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" + + # -- Set parent runtime to inherit. + # Should not be changes. Parent runtime is controlled from Codefresh side. + runtimeExtends: + - system/default/hybrid/k8s_low_limits + # -- Runtime description + description: "" + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the engine role + rules: [] + + # -- (for On-Premise only) Enable agent + agent: true + # -- (for On-Premise only) Set inCluster runtime + inCluster: true + # -- (for On-Premise only) Assign accounts to runtime (list of account ids) + accounts: [] + + # -- Parameters for DinD (docker-in-docker) pod (aka "runtime" pod). + dind: + # -- Set dind image. + image: + registry: quay.io + repository: codefresh/dind + tag: 26.1.4-1.28.7 # use `latest-rootless/rootless/26.1.4-1.28.7-rootless` tags for rootless-dind + pullPolicy: IfNotPresent + # -- Set dind resources. + resources: + requests: null + limits: + cpu: 400m + memory: 800Mi + # -- Set termination grace period. + terminationGracePeriodSeconds: 30 + # -- PV claim spec parametes. + pvcs: + # -- Default dind PVC parameters + dind: + # -- PVC name prefix. + # Keep `dind` as default! Don't change! + name: dind + # -- PVC storage class name. + # Change ONLY if you need to use storage class NOT from Codefresh volume-provisioner + storageClassName: '{{ include "dind-volume-provisioner.storageClassName" . }}' + # -- PVC size. + volumeSize: 16Gi + # -- PV reuse selector. + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#volume-reuse-policy + reuseVolumeSelector: codefresh-app,io.codefresh.accountName + reuseVolumeSortOrder: pipeline_id + # -- PV annotations. + annotations: {} + # E.g.: + # annotations: + # codefresh.io/volume-retention: 7d + # -- Set additional env vars. + env: + DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE: true + # -- Set pod annotations. + podAnnotations: {} + # -- Set pod labels. + podLabels: {} + # -- Set node selector. + nodeSelector: {} + # -- Set affinity + affinity: {} + # -- Set tolerations. + tolerations: [] + # -- Set scheduler name. + schedulerName: "" + # -- Set service account for pod. + serviceAccount: codefresh-engine + # -- Keep `true` as default! + userAccess: true + # -- Add extra volumes + userVolumes: {} + # E.g.: + # userVolumes: + # regctl-docker-registry: + # name: regctl-docker-registry + # secret: + # items: + # - key: .dockerconfigjson + # path: config.json + # secretName: regctl-docker-registry + # optional: true + # -- Add extra volume mounts + userVolumeMounts: {} + # E.g.: + # userVolumeMounts: + # regctl-docker-registry: + # name: regctl-docker-registry + # mountPath: /home/appuser/.docker/ + # readOnly: true + + # -- Parameters for Engine pod (aka "pipeline" orchestrator). + engine: + # -- Set image. + image: + registry: quay.io + repository: codefresh/engine + tag: 1.174.13 + pullPolicy: IfNotPresent + # -- Set container command. + command: + - npm + - run + - start + # -- Set resources. + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 1000m + memory: 2048Mi + # -- Set termination grace period. + terminationGracePeriodSeconds: 180 + # -- Set system(base) runtime images. + # @default -- See below. + runtimeImages: + COMPOSE_IMAGE: quay.io/codefresh/compose:v2.28.1-1.5.0 + CONTAINER_LOGGER_IMAGE: quay.io/codefresh/cf-container-logger:1.11.7 + DOCKER_BUILDER_IMAGE: quay.io/codefresh/cf-docker-builder:1.3.14 + DOCKER_PULLER_IMAGE: quay.io/codefresh/cf-docker-puller:8.0.18 + DOCKER_PUSHER_IMAGE: quay.io/codefresh/cf-docker-pusher:6.0.16 + DOCKER_TAG_PUSHER_IMAGE: quay.io/codefresh/cf-docker-tag-pusher:1.3.14 + FS_OPS_IMAGE: quay.io/codefresh/fs-ops:1.2.3 + GIT_CLONE_IMAGE: quay.io/codefresh/cf-git-cloner:10.1.28 + KUBE_DEPLOY: quay.io/codefresh/cf-deploy-kubernetes:16.1.11 + PIPELINE_DEBUGGER_IMAGE: quay.io/codefresh/cf-debugger:1.3.6 + TEMPLATE_ENGINE: quay.io/codefresh/pikolo:0.14.1 + CR_6177_FIXER: 'quay.io/codefresh/alpine:edge' + GC_BUILDER_IMAGE: 'quay.io/codefresh/cf-gc-builder:0.5.3' + COSIGN_IMAGE_SIGNER_IMAGE: 'quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2' + # -- Set additional env vars. + env: + # -- Interval to check the exec status in the container-logger + CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS: 1000 + # -- Timeout while doing requests to the Docker daemon + DOCKER_REQUEST_TIMEOUT_MS: 30000 + # -- If "true", composition images will be pulled sequentially + FORCE_COMPOSE_SERIAL_PULL: false + # -- Level of logging for engine + LOGGER_LEVEL: debug + # -- Enable debug-level logging of outgoing HTTP/HTTPS requests + LOG_OUTGOING_HTTP_REQUESTS: false + # -- Enable emitting metrics from engine + METRICS_PROMETHEUS_ENABLED: true + # -- Enable legacy metrics + METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS: false + # -- Enable collecting process metrics + METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS: false + # -- Host for Prometheus metrics server + METRICS_PROMETHEUS_HOST: '0.0.0.0' + # -- Port for Prometheus metrics server + METRICS_PROMETHEUS_PORT: 9100 + # -- Set workflow limits. + workflowLimits: + # -- Maximum time allowed to the engine to wait for the pre-steps (aka "Initializing Process") to succeed; seconds. + MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS: 600 + # -- Maximum time for workflow execution; seconds. + MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION: 86400 + # -- Maximum time allowed to workflow to spend in "elected" state; seconds. + MAXIMUM_ELECTED_STATE_AGE_ALLOWED: 900 + # -- Maximum retry attempts allowed for workflow. + MAXIMUM_RETRY_ATTEMPTS_ALLOWED: 20 + # -- Maximum time allowed to workflow to spend in "terminating" state until force terminated; seconds. + MAXIMUM_TERMINATING_STATE_AGE_ALLOWED: 900 + # -- Maximum time allowed to workflow to spend in "terminating" state without logs activity until force terminated; seconds. + MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE: 300 + # -- Time since the last health check report after which workflow is terminated; seconds. + TIME_ENGINE_INACTIVE_UNTIL_TERMINATION: 300 + # -- Time since the last health check report after which the engine is considered unhealthy; seconds. + TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY: 60 + # -- Time since the last workflow logs activity after which workflow is terminated; seconds. + TIME_INACTIVE_UNTIL_TERMINATION: 2700 + # -- Set pod annotations. + podAnnotations: {} + # -- Set pod labels. + podLabels: {} + # -- Set node selector. + nodeSelector: {} + # -- Set affinity + affinity: {} + # -- Set tolerations. + tolerations: [] + # -- Set scheduler name. + schedulerName: "" + # -- Set service account for pod. + serviceAccount: codefresh-engine + # -- Set extra env vars + userEnvVars: [] + # E.g. + # userEnvVars: + # - name: GITHUB_TOKEN + # valueFrom: + # secretKeyRef: + # name: github-token + # key: token + + # -- Parameters for `runtime-patch` post-upgrade/install hook + # @default -- See below + patch: + enabled: true + image: + registry: quay.io + repository: codefresh/cli + tag: 0.85.0-rootless + rbac: + enabled: true + annotations: {} + affinity: {} + nodeSelector: {} + podSecurityContext: {} + resources: {} + tolerations: [] + ttlSecondsAfterFinished: 180 + env: + HOME: /tmp + + # -- Parameters for `gencerts-dind` post-upgrade/install hook + # @default -- See below + gencerts: + enabled: true + image: + registry: quay.io + repository: codefresh/kubectl + tag: 1.28.4 + rbac: + enabled: true + annotations: {} + affinity: {} + nodeSelector: {} + podSecurityContext: {} + resources: {} + tolerations: [] + ttlSecondsAfterFinished: 180 + + # -- DinD pod daemon config + # @default -- See below + dindDaemon: + hosts: + - unix:///var/run/docker.sock + - tcp://0.0.0.0:1300 + tlsverify: true + tls: true + tlscacert: /etc/ssl/cf-client/ca.pem + tlscert: /etc/ssl/cf/server-cert.pem + tlskey: /etc/ssl/cf/server-key.pem + insecure-registries: + - 192.168.99.100:5000 + metrics-addr: 0.0.0.0:9323 + experimental: true + +# App-Proxy parameters +# Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#app-proxy-installation +# @default -- See below +appProxy: + # -- Enable app-proxy + enabled: false + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + + # -- Set image + image: + registry: quay.io + repository: codefresh/cf-app-proxy + tag: 0.0.47 + # -- Add additional env vars + env: {} + + # Set app-proxy ingress parameters + # @default -- See below + ingress: + # -- Set path prefix for ingress (keep empty for default `/` path) + pathPrefix: "" + # -- Set ingress class + class: "" + # -- Set DNS hostname the ingress will use + host: "" + # -- Set k8s tls secret for the ingress object + tlsSecret: "" + # -- Set extra annotations for ingress object + annotations: {} + # E.g. + # ingress: + # pathPrefix: "/cf-app-proxy" + # class: "nginx" + # host: "mydomain.com" + # tlsSecret: "tls-cert-app-proxy" + # annotations: + # nginx.ingress.kubernetes.io/whitelist-source-range: 123.123.123.123/130 + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Additional service account annotations + annotations: {} + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Add custom rule to the role + rules: [] + + # -- Set security context for the pod + podSecurityContext: {} + + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + # -- Set requests and limits + resources: {} + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + +# Monitor parameters +# @default -- See below +monitor: + # -- Enable monitor + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#install-monitoring-component + enabled: false + + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + + # -- Set image + image: + registry: quay.io + repository: codefresh/cf-k8s-agent + tag: 1.3.18 + # -- Add additional env vars + env: {} + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Add custom rule to the role + rules: [] + + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + podSecurityContext: {} + + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + +# -- Add serviceMonitor +# @default -- See below +serviceMonitor: + main: + # -- Enable service monitor for dind pods + enabled: false + nameOverride: dind + selector: + matchLabels: + app: dind + endpoints: + - path: /metrics + targetPort: 9100 + relabelings: + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + +# -- Add podMonitor (for engine pods) +# @default -- See below +podMonitor: + main: + # -- Enable pod monitor for engine pods + enabled: false + nameOverride: engine + selector: + matchLabels: + app: runtime + podMetricsEndpoints: + - path: /metrics + targetPort: 9100 + + runner: + # -- Enable pod monitor for runner pod + enabled: false + nameOverride: runner + selector: + matchLabels: + codefresh.io/application: runner + podMetricsEndpoints: + - path: /metrics + targetPort: 8080 + + volume-provisioner: + # -- Enable pod monitor for volumeProvisioner pod + enabled: false + nameOverride: volume-provisioner + selector: + matchLabels: + codefresh.io/application: volume-provisioner + podMetricsEndpoints: + - path: /metrics + targetPort: 8080 + +# -- Event exporter parameters +# @default -- See below +event-exporter: + # -- Enable event-exporter + enabled: false + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: Recreate + # -- Set pod annotations + podAnnotations: {} + + # -- Set image + image: + registry: docker.io + repository: codefresh/k8s-event-exporter + tag: latest + # -- Add additional env vars + env: {} + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: false + + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + +# -- Array of extra objects to deploy with the release +extraResources: [] +# E.g. +# extraResources: +# - apiVersion: rbac.authorization.k8s.io/v1 +# kind: ClusterRole +# metadata: +# name: codefresh-role +# rules: +# - apiGroups: [ "*"] +# resources: ["*"] +# verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +# - apiVersion: v1 +# kind: ServiceAccount +# metadata: +# name: codefresh-user +# namespace: "{{ .Release.Namespace }}" +# - apiVersion: rbac.authorization.k8s.io/v1 +# kind: ClusterRoleBinding +# metadata: +# name: codefresh-user +# roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: codefresh-role +# subjects: +# - kind: ServiceAccount +# name: codefresh-user +# namespace: "{{ .Release.Namespace }}" +# - apiVersion: v1 +# kind: Secret +# type: kubernetes.io/service-account-token +# metadata: +# name: codefresh-user-token +# namespace: "{{ .Release.Namespace }}" +# annotations: +# kubernetes.io/service-account.name: "codefresh-user" diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/.helmignore b/charts/intel/intel-device-plugins-operator/0.31.1/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/Chart.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/Chart.yaml new file mode 100644 index 0000000000..a76edd3667 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/Chart.yaml @@ -0,0 +1,13 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel Device Plugins Operator + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-operator +apiVersion: v2 +appVersion: 0.31.1 +description: A Helm chart for Intel Device Plugins Operator for Kubernetes +icon: file://assets/icons/intel-device-plugins-operator.png +kubeVersion: '>=1.19-0' +name: intel-device-plugins-operator +type: application +version: 0.31.1 diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/LICENSE b/charts/intel/intel-device-plugins-operator/0.31.1/LICENSE new file mode 100644 index 0000000000..9aa5290ebc --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/LICENSE @@ -0,0 +1,14 @@ +Copyright 2023 Intel Corporation +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/README.md b/charts/intel/intel-device-plugins-operator/0.31.1/README.md new file mode 100644 index 0000000000..648f7f56ea --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/README.md @@ -0,0 +1,54 @@ +# Intel Device Plugins Operator Helm Chart + +[Intel Device Plugins for Kubernetes](https://github.com/intel/intel-device-plugins-for-kubernetes) Helm charts for installing the operator. Operator installation is manadtory after which each device plugin can be installed via its own Helm chart. +## Prerequisites +- [cert-manager](https://cert-manager.io/docs/installation/helm) +- [Node Feature Discovery NFD](https://kubernetes-sigs.github.io/node-feature-discovery/master/get-started/deployment-and-usage.html) [optional] + +## Get Helm Repository Info +``` +helm repo add intel https://intel.github.io/helm-charts/ +helm repo update +``` + +You can execute `helm search repo intel` command to see pulled charts [optional]. + +## Install Helm Chart +CRDs of the device plugin operator are installed as part of the chart. + +``` +helm install device-plugin-operator intel/intel-device-plugins-operator [flags] +``` + +## Upgrade Chart +``` +helm upgrade device-plugin-operator intel/intel-device-plugins-operator [flags] +``` + +## Uninstall Chart +``` +helm uninstall device-plugin-operator +``` +CRDs are not uninstalled. + +## Configuration +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments: + +```console +helm show values intel/intel-device-plugins-operator +``` + +You may also run `helm show values` on this chart's dependencies for additional options. + +|parameter| value | +|---------|-----------| +| `manager.image.hub` | `intel` | +| `manager.image.tag` | `` | +| `kubeRbacProxy.image.hub` | `quay.io` | +| `kubeRbacProxy.image.hubRepo` | `brancz` | +| `kubeRbacProxy.image.tag` | `v0.18.1` | +| `kubeRbacProxy.image.pullPolicy` | `IfNotPresent` | +| `privateRegistry.registryUrl` | `` | +| `privateRegistry.registryUser` | `` | +| `privateRegistry.registrySecret` | `` | +| `pullPolicy` | `IfNotPresent` | \ No newline at end of file diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dlbdeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dlbdeviceplugins.yaml new file mode 100644 index 0000000000..bfd11bfde9 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dlbdeviceplugins.yaml @@ -0,0 +1,190 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: dlbdeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: DlbDevicePlugin + listKind: DlbDevicePluginList + plural: dlbdeviceplugins + singular: dlbdeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + DlbDevicePlugin is the Schema for the dlbdeviceplugins API. It represents + the DLB device plugin responsible for advertising Intel DLB hardware resources to + the kubelet. + 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: DlbDevicePluginSpec defines the desired state of DlbDevicePlugin. + properties: + image: + description: Image is a container image with DLB device plugin executable. + type: string + initImage: + description: InitImage is a container image with a script that initializes + devices. + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: DlbDevicePluginStatus defines the observed state of DlbDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dsadeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dsadeviceplugins.yaml new file mode 100644 index 0000000000..f964961fa8 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_dsadeviceplugins.yaml @@ -0,0 +1,200 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: dsadeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: DsaDevicePlugin + listKind: DsaDevicePluginList + plural: dsadeviceplugins + singular: dsadeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + DsaDevicePlugin is the Schema for the dsadeviceplugins API. It represents + the DSA device plugin responsible for advertising Intel DSA hardware resources to + the kubelet. + 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: DsaDevicePluginSpec defines the desired state of DsaDevicePlugin. + properties: + image: + description: Image is a container image with DSA device plugin executable. + type: string + initImage: + description: InitImage is an initcontainer image to configure and + enable DSA devices and workqueues with idxd-config (accel-config) + utility + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + provisioningConfig: + description: ProvisioningConfig is a ConfigMap used to pass the DSA + devices and workqueues configuration into idxd-config initcontainer. + type: string + sharedDevNum: + description: SharedDevNum is a number of containers that can share + the same DSA device. + minimum: 1 + type: integer + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: DsaDevicePluginStatus defines the observed state of DsaDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_fpgadeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_fpgadeviceplugins.yaml new file mode 100644 index 0000000000..b4e6a99f22 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_fpgadeviceplugins.yaml @@ -0,0 +1,197 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: fpgadeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: FpgaDevicePlugin + listKind: FpgaDevicePluginList + plural: fpgadeviceplugins + singular: fpgadeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + FpgaDevicePlugin is the Schema for the fpgadeviceplugins API. It represents + the FPGA device plugin responsible for advertising Intel FPGA hardware resources to + the kubelet. + 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: FpgaDevicePluginSpec defines the desired state of FpgaDevicePlugin. + properties: + image: + description: Image is a container image with FPGA device plugin executable. + type: string + initImage: + description: InitImage is a container image with tools used to initialize + the host before starting FPGA workloads on it. + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + mode: + description: Mode is a mode of the plugin's operation. + enum: + - af + - region + - regiondevel + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: FpgaDevicePluginStatus defines the observed state of FpgaDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_gpudeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_gpudeviceplugins.yaml new file mode 100644 index 0000000000..4dd89c0f12 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_gpudeviceplugins.yaml @@ -0,0 +1,214 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: gpudeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: GpuDevicePlugin + listKind: GpuDevicePluginList + plural: gpudeviceplugins + singular: gpudeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + GpuDevicePlugin is the Schema for the gpudeviceplugins API. It represents + the GPU device plugin responsible for advertising Intel GPU hardware resources to + the kubelet. + 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: GpuDevicePluginSpec defines the desired state of GpuDevicePlugin. + properties: + enableMonitoring: + description: |- + EnableMonitoring enables the monitoring resource ('i915_monitoring') + which gives access to all GPU devices on given node. Typically used with Intel XPU-Manager. + type: boolean + image: + description: Image is a container image with GPU device plugin executable. + type: string + initImage: + description: InitImage is a container image with tools (e.g., GPU + NFD source hook) installed on each node. + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + preferredAllocationPolicy: + description: |- + PreferredAllocationPolicy sets the mode of allocating GPU devices on a node. + See documentation for detailed description of the policies. Only valid when SharedDevNum > 1 is set. + Not applicable with ResourceManager. + enum: + - balanced + - packed + - none + type: string + resourceManager: + description: ResourceManager handles the fractional resource management + for multi-GPU nodes. Enable only for clusters with GPU Aware Scheduling. + type: boolean + sharedDevNum: + description: SharedDevNum is a number of containers that can share + the same GPU device. + minimum: 1 + type: integer + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: GpuDevicePluginStatus defines the observed state of GpuDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_iaadeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_iaadeviceplugins.yaml new file mode 100644 index 0000000000..beb5c64a76 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_iaadeviceplugins.yaml @@ -0,0 +1,199 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: iaadeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: IaaDevicePlugin + listKind: IaaDevicePluginList + plural: iaadeviceplugins + singular: iaadeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + IaaDevicePlugin is the Schema for the iaadeviceplugins API. It represents + the IAA device plugin responsible for advertising Intel IAA hardware resources to + the kubelet. + 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: IaaDevicePluginSpec defines the desired state of IaaDevicePlugin. + properties: + image: + description: Image is a container image with IAA device plugin executable. + type: string + initImage: + description: InitImage is an initcontainer image to configure and + enable IAA devices and workqueues with accel-config utility + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + provisioningConfig: + description: ProvisioningConfig is a ConfigMap used to pass the IAA + configuration into idxd initcontainer. + type: string + sharedDevNum: + description: SharedDevNum is a number of containers that can share + the same IAA device. + minimum: 1 + type: integer + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: IaaDevicePluginStatus defines the observed state of IaaDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_qatdeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_qatdeviceplugins.yaml new file mode 100644 index 0000000000..a9cb80dc74 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_qatdeviceplugins.yaml @@ -0,0 +1,230 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: qatdeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: QatDevicePlugin + listKind: QatDevicePluginList + plural: qatdeviceplugins + singular: qatdeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + QatDevicePlugin is the Schema for the qatdeviceplugins API. It represents the QAT device + plugin responsible for advertising Intel QuickAssist Technology hardware resources + to the kubelet. + 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: QatDevicePluginSpec defines the desired state of QatDevicePlugin. + properties: + dpdkDriver: + description: DpdkDriver is a DPDK device driver for configuring the + QAT device. + enum: + - igb_uio + - vfio-pci + type: string + image: + description: Image is a container image with QAT device plugin executable. + type: string + initImage: + description: InitImage is a container image with a script that initialize + devices. + type: string + kernelVfDrivers: + description: KernelVfDrivers is a list of VF device drivers for the + QuickAssist devices in the system. + items: + description: KernelVfDriver is a VF device driver for QuickAssist + devices. + enum: + - dh895xccvf + - c6xxvf + - c3xxxvf + - d15xxvf + - 4xxxvf + - 420xxvf + - c4xxxvf + type: string + type: array + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + maxNumDevices: + description: MaxNumDevices is a maximum number of QAT devices to be + provided to the QuickAssist device plugin + minimum: 1 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + preferredAllocationPolicy: + description: |- + PreferredAllocationPolicy sets the mode of allocating QAT devices on a node. + See documentation for detailed description of the policies. + enum: + - balanced + - packed + type: string + provisioningConfig: + description: ProvisioningConfig is a ConfigMap used to pass the configuration + of QAT devices into qat initcontainer. + type: string + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: QatDevicePluginStatus defines the observed state of QatDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_sgxdeviceplugins.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_sgxdeviceplugins.yaml new file mode 100644 index 0000000000..33823b0892 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/deviceplugin.intel.com_sgxdeviceplugins.yaml @@ -0,0 +1,201 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: sgxdeviceplugins.deviceplugin.intel.com +spec: + group: deviceplugin.intel.com + names: + kind: SgxDevicePlugin + listKind: SgxDevicePluginList + plural: sgxdeviceplugins + singular: sgxdeviceplugin + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredNumberScheduled + name: Desired + type: integer + - jsonPath: .status.numberReady + name: Ready + type: integer + - jsonPath: .spec.nodeSelector + name: Node Selector + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + SgxDevicePlugin is the Schema for the sgxdeviceplugins API. It represents + the SGX device plugin responsible for advertising SGX device nodes to + the kubelet. + 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: SgxDevicePluginSpec defines the desired state of SgxDevicePlugin. + properties: + enclaveLimit: + description: EnclaveLimit is a number of containers that can share + the same SGX enclave device. + minimum: 1 + type: integer + image: + description: Image is a container image with SGX device plugin executable. + type: string + initImage: + description: |- + InitImage is a container image with tools (i.e., SGX NFD source hook) installed on each node. + Recommendation is to leave this unset and prefer the SGX NodeFeatureRule instead. + type: string + logLevel: + description: LogLevel sets the plugin's log level. + minimum: 0 + type: integer + nodeSelector: + additionalProperties: + type: string + description: NodeSelector provides a simple way to constrain device + plugin pods to nodes with particular labels. + type: object + provisionLimit: + description: ProvisionLimit is a number of containers that can share + the same SGX provision device. + minimum: 1 + type: integer + tolerations: + description: Specialized nodes (e.g., with accelerators) can be Tainted + to make sure unwanted pods are not scheduled on them. Tolerations + can be set for the plugin pod to neutralize the Taint. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + status: + description: SgxDevicePluginStatus defines the observed state of SgxDevicePlugin. + properties: + controlledDaemonSet: + description: ControlledDaemoSet references the DaemonSet controlled + by the operator. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + desiredNumberScheduled: + description: |- + The total number of nodes that should be running the device plugin + pod (including nodes correctly running the device plugin pod). + format: int32 + type: integer + nodeNames: + description: The list of Node names where the device plugin pods are + running. + items: + type: string + type: array + numberReady: + description: |- + The number of nodes that should be running the device plugin pod and have one + or more of the device plugin pod running and ready. + format: int32 + type: integer + required: + - desiredNumberScheduled + - numberReady + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_acceleratorfunctions.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_acceleratorfunctions.yaml new file mode 100644 index 0000000000..b0bca116c5 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_acceleratorfunctions.yaml @@ -0,0 +1,68 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: acceleratorfunctions.fpga.intel.com +spec: + group: fpga.intel.com + names: + kind: AcceleratorFunction + listKind: AcceleratorFunctionList + plural: acceleratorfunctions + shortNames: + - af + singular: acceleratorfunction + scope: Namespaced + versions: + - name: v2 + schema: + openAPIV3Schema: + description: |- + AcceleratorFunction is a specification for an Accelerator Function resource + provided by a FPGA-based programmable hardware accelerator. + 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: AcceleratorFunctionSpec contains actual specs for AcceleratorFunction. + properties: + afuId: + pattern: ^[0-9a-f]{8,40}$ + type: string + interfaceId: + pattern: ^[0-9a-f]{8,32}$ + type: string + mode: + pattern: ^af|region$ + type: string + required: + - afuId + - interfaceId + - mode + type: object + status: + description: AcceleratorFunctionStatus is an empty object used to satisfy + operator-sdk. + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_fpgaregions.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_fpgaregions.yaml new file mode 100644 index 0000000000..061863672d --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/crds/fpga.intel.com_fpgaregions.yaml @@ -0,0 +1,59 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: fpgaregions.fpga.intel.com +spec: + group: fpga.intel.com + names: + kind: FpgaRegion + listKind: FpgaRegionList + plural: fpgaregions + shortNames: + - fpga + singular: fpgaregion + scope: Namespaced + versions: + - name: v2 + schema: + openAPIV3Schema: + description: |- + FpgaRegion is a specification for a FPGA region resource which can be programmed + with a bitstream. + 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: FpgaRegionSpec contains actual specs for FpgaRegion. + properties: + interfaceId: + pattern: ^[0-9a-f]{8,32}$ + type: string + required: + - interfaceId + type: object + status: + description: FpgaRegionStatus is an empty object used to satisfy operator-sdk. + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/templates/NOTES.txt b/charts/intel/intel-device-plugins-operator/0.31.1/templates/NOTES.txt new file mode 100644 index 0000000000..43a8375798 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/templates/NOTES.txt @@ -0,0 +1,3 @@ +Thank you for installing {{ .Chart.Name }}. + +The next step would be to install the device (plugin) specific chart. diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/templates/operator.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/templates/operator.yaml new file mode 100644 index 0000000000..5be8da4ac6 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/templates/operator.yaml @@ -0,0 +1,726 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: inteldeviceplugins-leader-election-role + namespace: {{ .Release.Namespace | quote }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: inteldeviceplugins-gpu-manager-role +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: inteldeviceplugins-manager-role +rules: +- apiGroups: + - "" + resources: + - nodes/proxy + verbs: + - get + - list +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - watch +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create +- apiGroups: + - coordination.k8s.io + resourceNames: + - d1c7b6d5.intel.com + resources: + - leases + verbs: + - get + - update +- apiGroups: + - deviceplugin.intel.com + resources: + - dlbdeviceplugins + - dsadeviceplugins + - fpgadeviceplugins + - gpudeviceplugins + - iaadeviceplugins + - qatdeviceplugins + - sgxdeviceplugins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - deviceplugin.intel.com + resources: + - dlbdeviceplugins/finalizers + - dsadeviceplugins/finalizers + - fpgadeviceplugins/finalizers + - gpudeviceplugins/finalizers + - iaadeviceplugins/finalizers + - qatdeviceplugins/finalizers + - sgxdeviceplugins/finalizers + verbs: + - update +- apiGroups: + - deviceplugin.intel.com + resources: + - dlbdeviceplugins/status + - dsadeviceplugins/status + - fpgadeviceplugins/status + - gpudeviceplugins/status + - iaadeviceplugins/status + - qatdeviceplugins/status + - sgxdeviceplugins/status + verbs: + - get + - patch + - update +- apiGroups: + - fpga.intel.com + resources: + - acceleratorfunctions + - fpgaregions + verbs: + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + verbs: + - create + - delete + - get + - list + - watch +- apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: inteldeviceplugins-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: inteldeviceplugins-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: inteldeviceplugins-leader-election-rolebinding + namespace: {{ .Release.Namespace | quote }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: inteldeviceplugins-leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: inteldeviceplugins-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: inteldeviceplugins-manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: inteldeviceplugins-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: inteldeviceplugins-proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: inteldeviceplugins-controller-manager-metrics-service + namespace: {{ .Release.Namespace | quote }} +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager +--- +{{- if .Values.privateRegistry.registrySecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-operator-private-registry +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .Values.privateRegistry.registryUrl (printf "%s:%s" .Values.privateRegistry.registryUser .Values.privateRegistry.registrySecret | b64enc) | b64enc }} +{{- end }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: inteldeviceplugins-controller-manager + namespace: {{ .Release.Namespace | quote }} +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + {{- if .Values.privateRegistry.registrySecret }} + imagePullSecrets: + - name: {{ .Release.Name }}-operator-private-registry + {{- end }} + containers: + - args: + {{- if .Values.controllerExtraArgs }} + {{- with .Values.controllerExtraArgs }} + {{- tpl . $ | trim | nindent 8 }} + {{- end }} + {{- end }} + env: + - name: DEVICEPLUGIN_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: "{{ .Values.manager.image.hub }}/intel-deviceplugin-operator:{{ .Values.manager.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.manager.image.pullPolicy }} + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + resources: + {{- toYaml .Values.resources | nindent 10 }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + - --v=10 + image: "{{ .Values.kubeRbacProxy.image.hub }}/{{ .Values.kubeRbacProxy.image.hubRepo }}/kube-rbac-proxy:{{ .Values.kubeRbacProxy.image.tag }}" + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + nodeSelector: {{- .Values.nodeSelector | toYaml | nindent 8 }} + serviceAccountName: default + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: inteldeviceplugins-serving-cert + namespace: {{ .Release.Namespace | quote }} +spec: + dnsNames: + - inteldeviceplugins-webhook-service.{{ .Release.Namespace }}.svc + - inteldeviceplugins-webhook-service.{{ .Release.Namespace }}.svc.cluster.local + issuerRef: + kind: Issuer + name: inteldeviceplugins-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: inteldeviceplugins-selfsigned-issuer + namespace: {{ .Release.Namespace | quote }} +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/inteldeviceplugins-serving-cert + name: inteldeviceplugins-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-dlbdeviceplugin + failurePolicy: Fail + name: mdlbdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - dlbdeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-dsadeviceplugin + failurePolicy: Fail + name: mdsadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - dsadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-fpgadeviceplugin + failurePolicy: Fail + name: mfpgadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - fpgadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-gpudeviceplugin + failurePolicy: Fail + name: mgpudeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - gpudeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-iaadeviceplugin + failurePolicy: Fail + name: miaadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - iaadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-qatdeviceplugin + failurePolicy: Fail + name: mqatdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - qatdeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate-deviceplugin-intel-com-v1-sgxdeviceplugin + failurePolicy: Fail + name: msgxdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - sgxdeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /pods + failurePolicy: Ignore + name: fpga.mutator.webhooks.intel.com + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - pods + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /mutate--v1-pod + failurePolicy: Ignore + name: sgx.mutator.webhooks.intel.com + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - pods + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/inteldeviceplugins-serving-cert + name: inteldeviceplugins-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-dlbdeviceplugin + failurePolicy: Fail + name: vdlbdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - dlbdeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-dsadeviceplugin + failurePolicy: Fail + name: vdsadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - dsadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-fpgadeviceplugin + failurePolicy: Fail + name: vfpgadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - fpgadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-gpudeviceplugin + failurePolicy: Fail + name: vgpudeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - gpudeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-iaadeviceplugin + failurePolicy: Fail + name: viaadeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - iaadeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-qatdeviceplugin + failurePolicy: Fail + name: vqatdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - qatdeviceplugins + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: inteldeviceplugins-webhook-service + namespace: {{ .Release.Namespace | quote }} + path: /validate-deviceplugin-intel-com-v1-sgxdeviceplugin + failurePolicy: Fail + name: vsgxdeviceplugin.kb.io + rules: + - apiGroups: + - deviceplugin.intel.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - sgxdeviceplugins + sideEffects: None diff --git a/charts/intel/intel-device-plugins-operator/0.31.1/values.yaml b/charts/intel/intel-device-plugins-operator/0.31.1/values.yaml new file mode 100644 index 0000000000..bef85a2492 --- /dev/null +++ b/charts/intel/intel-device-plugins-operator/0.31.1/values.yaml @@ -0,0 +1,28 @@ +nodeSelector: + kubernetes.io/arch: amd64 + +manager: + image: + hub: intel + tag: "" + pullPolicy: IfNotPresent + +kubeRbacProxy: + image: + hub: quay.io + hubRepo: brancz + tag: v0.18.1 + pullPolicy: IfNotPresent + +privateRegistry: + registryUrl: "" + registryUser: "" + registrySecret: "" + +resources: + limits: + cpu: 100m + memory: 120Mi + requests: + cpu: 100m + memory: 100Mi diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/.helmignore b/charts/intel/intel-device-plugins-qat/0.31.1/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/Chart.yaml b/charts/intel/intel-device-plugins-qat/0.31.1/Chart.yaml new file mode 100644 index 0000000000..d605e9170b --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/Chart.yaml @@ -0,0 +1,13 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel QAT Device Plugin + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-qat +apiVersion: v2 +appVersion: 0.31.1 +description: A Helm chart for Intel QAT Device Plugin +icon: file://assets/icons/intel-device-plugins-qat.png +kubeVersion: '>=1.19-0' +name: intel-device-plugins-qat +type: application +version: 0.31.1 diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/LICENSE b/charts/intel/intel-device-plugins-qat/0.31.1/LICENSE new file mode 100644 index 0000000000..9aa5290ebc --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/LICENSE @@ -0,0 +1,14 @@ +Copyright 2023 Intel Corporation +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/README.md b/charts/intel/intel-device-plugins-qat/0.31.1/README.md new file mode 100644 index 0000000000..04b0b5232c --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/README.md @@ -0,0 +1,50 @@ +# Intel QAT Device Plugin Helm Chart + +## Get Helm Repository Info +``` +helm repo add intel https://intel.github.io/helm-charts/ +helm repo update +``` + +You can execute `helm search repo intel` command to see pulled charts [optional]. + +## Dependencies + +QAT Device Plugin depends on Node Feature Discovery (NFD). See NFD's Helm install page [here](https://kubernetes-sigs.github.io/node-feature-discovery/v0.12/deployment/helm.html?highlight=helm#deployment). If you do not want to use NFD in you cluster, you'll need to change the nodeSelector in the [values](values.yaml) file to match nodes with QAT device. + +## Install Helm Chart +``` +helm install qat-device-plugin intel/intel-device-plugins-qat [flags] +``` + +## Upgrade Chart +``` +helm upgrade qat-device-plugin intel/intel-device-plugins-qat [flags] +``` + +## Uninstall Chart +``` +helm uninstall qat-device-plugin +``` + +## Configuration +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments: + +```console +helm show values intel/intel-device-plugins-qat +``` + +You may also run `helm show values` on this chart's dependencies for additional options. + +|parameter| value | +|---------|-----------| +| `image.hub` | `intel` | +| `image.tag` | `` | +| `initImage.hub` | `intel` | +| `initImage.tag` | `` | +| `dpdkDriver` | `vfio-pci` | +| `kernelVfDrivers` | `4xxxvf`, `420xxvf` | +| `maxNumDevices` | `128` | +| `logLevel` | `4` | +| `nodeFeatureRule` | `true` | +| `tolerations` | `` | diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/questions.yaml b/charts/intel/intel-device-plugins-qat/0.31.1/questions.yaml new file mode 100644 index 0000000000..74461ffa82 --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/questions.yaml @@ -0,0 +1,6 @@ +questions: +- variable: nodeFeatureRule + default: false + type: boolean + label: Enable Node Feature Discovery feature labels + description: "When Node Feature Discovery (NFD) is deployed, enable QAT node labeling using NFD feature rules." diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/templates/NOTES.txt b/charts/intel/intel-device-plugins-qat/0.31.1/templates/NOTES.txt new file mode 100644 index 0000000000..c5615c64c6 --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/templates/NOTES.txt @@ -0,0 +1 @@ +Thank you for installing {{ .Chart.Name }}. diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/templates/qat.yaml b/charts/intel/intel-device-plugins-qat/0.31.1/templates/qat.yaml new file mode 100644 index 0000000000..b569f3d28d --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/templates/qat.yaml @@ -0,0 +1,53 @@ +{{- /* +based on +deployments/operator/samples/deviceplugin_v1_qatdeviceplugin.yaml +*/}} + +apiVersion: deviceplugin.intel.com/v1 +kind: QatDevicePlugin +metadata: + name: {{ .Values.name }} + annotations: {{ toYaml .Values.annotations | nindent 4 }} +spec: + image: "{{ .Values.image.hub }}/intel-qat-plugin:{{ .Values.image.tag | default .Chart.AppVersion }}" + initImage: "{{ .Values.initImage.hub }}/intel-qat-initcontainer:{{ .Values.initImage.tag | default .Chart.AppVersion }}" + dpdkDriver: {{ .Values.dpdkDriver }} + kernelVfDrivers: + {{- range .Values.kernelVfDrivers }} + - {{ . }} + {{- end }} + maxNumDevices: {{ .Values.maxNumDevices }} + logLevel: {{ .Values.logLevel }} + nodeSelector: {{ .Values.nodeSelector | toYaml | nindent 4 }} + tolerations: {{- .Values.tolerations | toYaml | nindent 4 }} + +--- +{{ if eq .Values.nodeFeatureRule true }} +apiVersion: nfd.k8s-sigs.io/v1alpha1 +kind: NodeFeatureRule +metadata: + name: intel-dp-qat-device +spec: + rules: + - name: "intel.qat" + labels: + "intel.feature.node.kubernetes.io/qat": "true" + matchFeatures: + - feature: pci.device + matchExpressions: + vendor: {op: In, value: ["8086"]} + device: {op: In, value: ["37c8", "4940", "4942", "4944", "4946"]} + class: {op: In, value: ["0b40"]} + - feature: kernel.loadedmodule + matchExpressions: + intel_qat: {op: Exists} + matchAny: + - matchFeatures: + - feature: kernel.loadedmodule + matchExpressions: + vfio_pci: {op: Exists} + - matchFeatures: + - feature: kernel.enabledmodule + matchExpressions: + vfio-pci: {op: Exists} +{{ end }} diff --git a/charts/intel/intel-device-plugins-qat/0.31.1/values.yaml b/charts/intel/intel-device-plugins-qat/0.31.1/values.yaml new file mode 100644 index 0000000000..98ca374e49 --- /dev/null +++ b/charts/intel/intel-device-plugins-qat/0.31.1/values.yaml @@ -0,0 +1,23 @@ +name: qatdeviceplugin-sample + +image: + hub: intel + tag: "" + +initImage: + hub: intel + tag: "" + +dpdkDriver: vfio-pci +kernelVfDrivers: + - 4xxxvf + - 420xxvf +maxNumDevices: 128 +logLevel: 4 + +nodeSelector: + intel.feature.node.kubernetes.io/qat: 'true' + +tolerations: + +nodeFeatureRule: true diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/.helmignore b/charts/intel/intel-device-plugins-sgx/0.31.1/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/Chart.yaml b/charts/intel/intel-device-plugins-sgx/0.31.1/Chart.yaml new file mode 100644 index 0000000000..f15b1b2835 --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/Chart.yaml @@ -0,0 +1,13 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel SGX Device Plugin + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-sgx +apiVersion: v2 +appVersion: 0.31.1 +description: A Helm chart for Intel SGX Device Plugin +icon: file://assets/icons/intel-device-plugins-sgx.png +kubeVersion: '>=1.19-0' +name: intel-device-plugins-sgx +type: application +version: 0.31.1 diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/LICENSE b/charts/intel/intel-device-plugins-sgx/0.31.1/LICENSE new file mode 100644 index 0000000000..9aa5290ebc --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/LICENSE @@ -0,0 +1,14 @@ +Copyright 2023 Intel Corporation +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/README.md b/charts/intel/intel-device-plugins-sgx/0.31.1/README.md new file mode 100644 index 0000000000..0cbd391ac3 --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/README.md @@ -0,0 +1,40 @@ +# Intel SGX Device Plugin Helm Chart + +## Get Helm Repository Info +``` +helm repo add intel https://intel.github.io/helm-charts/ +helm repo update +``` + +You can execute `helm search repo intel` command to see pulled charts [optional]. + +## Install Helm Chart +``` +helm install sgx-device-plugin intel/intel-device-plugins-sgx [flags] +``` +## Upgrade Chart +``` +helm upgrade sgx-device-plugin intel/intel-device-plugins-sgx [flags] +``` + +## Uninstall Chart +``` +helm uninstall sgx-device-plugin +``` + +## Configuration +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments: + +```console +helm show values intel/intel-device-plugins-sgx +``` + +You may also run `helm show values` on this chart's dependencies for additional options. + +|parameter| value | +|---------|-----------| +| `image.hub` | `intel` | +| `image.tag` | `` | +| `enclaveLimit` | `110` | +| `provisionLimit` | `110` | +| `logLevel` | `4` | \ No newline at end of file diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/questions.yaml b/charts/intel/intel-device-plugins-sgx/0.31.1/questions.yaml new file mode 100644 index 0000000000..402e94820a --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/questions.yaml @@ -0,0 +1,6 @@ +questions: +- variable: nodeFeatureRule + default: false + type: boolean + label: Enable Node Feature Discovery feature labels + description: "When Node Feature Discovery (NFD) is deployed, enable SGX node labeling using NFD feature rules." diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/templates/sgx.yaml b/charts/intel/intel-device-plugins-sgx/0.31.1/templates/sgx.yaml new file mode 100644 index 0000000000..5fde596adf --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/templates/sgx.yaml @@ -0,0 +1,43 @@ +{{- /* +based on +deployments/operator/samples/deviceplugin_v1_sgxdeviceplugin.yaml +*/}} + +apiVersion: deviceplugin.intel.com/v1 +kind: SgxDevicePlugin +metadata: + name: {{ .Values.name }} + annotations: {{ toYaml .Values.annotations | nindent 4 }} +spec: + image: "{{ .Values.image.hub }}/intel-sgx-plugin:{{ .Values.image.tag | default .Chart.AppVersion }}" + enclaveLimit: {{ .Values.enclaveLimit }} + provisionLimit: {{ .Values.provisionLimit }} + logLevel: {{ .Values.logLevel }} + nodeSelector: {{- .Values.nodeSelector | toYaml | nindent 4 }} + tolerations: {{- .Values.tolerations | toYaml | nindent 4 }} + +--- +{{ if eq .Values.nodeFeatureRule true }} +apiVersion: nfd.k8s-sigs.io/v1alpha1 +kind: NodeFeatureRule +metadata: + name: intel-dp-sgx-device +spec: + rules: + - name: "intel.sgx" + labels: + "intel.feature.node.kubernetes.io/sgx": "true" + extendedResources: + sgx.intel.com/epc: "@cpu.security.sgx.epc" + matchFeatures: + - feature: cpu.cpuid + matchExpressions: + SGX: {op: Exists} + SGXLC: {op: Exists} + - feature: cpu.security + matchExpressions: + sgx.enabled: {op: IsTrue} + - feature: kernel.config + matchExpressions: + X86_SGX: {op: Exists} +{{ end }} diff --git a/charts/intel/intel-device-plugins-sgx/0.31.1/values.yaml b/charts/intel/intel-device-plugins-sgx/0.31.1/values.yaml new file mode 100644 index 0000000000..5da974c994 --- /dev/null +++ b/charts/intel/intel-device-plugins-sgx/0.31.1/values.yaml @@ -0,0 +1,16 @@ +name: sgxdeviceplugin-sample + +image: + hub: intel + tag: "" + +enclaveLimit: 110 +provisionLimit: 110 +logLevel: 4 + +nodeSelector: + intel.feature.node.kubernetes.io/sgx: 'true' + +tolerations: + +nodeFeatureRule: true \ No newline at end of file diff --git a/charts/linkerd/linkerd-control-plane/2024.10.2/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.10.2/Chart.yaml index 919347265b..d64e1c9095 100644 --- a/charts/linkerd/linkerd-control-plane/2024.10.2/Chart.yaml +++ b/charts/linkerd/linkerd-control-plane/2024.10.2/Chart.yaml @@ -2,7 +2,6 @@ annotations: catalog.cattle.io/auto-install: linkerd-crds catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd Control Plane - catalog.cattle.io/featured: "5" catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/.helmignore b/charts/linkerd/linkerd-control-plane/2024.10.3/.helmignore new file mode 100644 index 0000000000..79c90a8063 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/.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/linkerd/linkerd-control-plane/2024.10.3/Chart.lock b/charts/linkerd/linkerd-control-plane/2024.10.3/Chart.lock new file mode 100644 index 0000000000..a0cb7ec8c5 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +digest: sha256:8e42f9c9d4a2dc883f17f94d6044c97518ced19ad0922f47b8760e47135369ba +generated: "2021-12-06T11:42:50.784240359-05:00" diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/Chart.yaml new file mode 100644 index 0000000000..c37dae0e43 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + catalog.cattle.io/auto-install: linkerd-crds + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd Control Plane + catalog.cattle.io/featured: "5" + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-control-plane +apiVersion: v2 +appVersion: edge-24.10.3 +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' +home: https://linkerd.io +icon: file://assets/icons/linkerd-control-plane.png +keywords: +- service-mesh +kubeVersion: '>=1.22.0-0' +maintainers: +- email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ +name: linkerd-control-plane +sources: +- https://github.com/linkerd/linkerd2/ +type: application +version: 2024.10.3 diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/README.md b/charts/linkerd/linkerd-control-plane/2024.10.3/README.md new file mode 100644 index 0000000000..b15e6987f5 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/README.md @@ -0,0 +1,321 @@ +# linkerd-control-plane + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2024.10.3](https://img.shields.io/badge/Version-2024.10.3-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square) + +**Homepage:** + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Prerequisite: linkerd-crds chart + +Before installing this chart, please install the `linkerd-crds` chart, which +creates all the CRDs that the components from the current chart require. + +## Prerequisite: identity certificates + +The identity component of Linkerd requires setting up a trust anchor +certificate, and an issuer certificate with its key. These need to be provided +to Helm by the user (unlike when using the `linkerd install` CLI which can +generate these automatically). You can provide your own, or follow [these +instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new +ones. + +Alternatively, both trust anchor and identity issuer certificates may be +derived from in-cluster resources. Existing CA (trust anchor) certificates +**must** live in a `ConfigMap` resource named `linkerd-identity-trust-roots`. +Issuer certificates **must** live in a `Secret` named +`linkerd-identity-issuer`. Both resources should exist in the control-plane's +install namespace. In order to use an existing CA, Linkerd needs to be +installed with `identity.externalCA=true`. To use an existing issuer +certificate, Linkerd should be installed with +`identity.issuer.scheme=kubernetes.io/tls`. + +A more comprehensive description is in the [automatic certificate rotation +guide](https://linkerd.io/2.12/tasks/automatically-rotating-control-plane-tls-credentials/#a-note-on-third-party-cert-management-solutions). + +Note that the provided certificates must be ECDSA certificates. + +## Adding Linkerd's Helm repository + +Included here for completeness-sake, but should have already been added when +`linkerd-base` was installed. + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the chart + +You must provide the certificates and keys described in the preceding section, +and the same expiration date you used to generate the Issuer certificate. + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + linkerd/linkerd-control-plane +``` + +Note that you require to install this chart in the same namespace you installed +the `linkerd-base` chart. + +## Setting High-Availability + +Besides the default `values.yaml` file, the chart provides a `values-ha.yaml` +file that overrides some default values as to set things up under a +high-availability scenario, analogous to the `--ha` option in `linkerd install`. +Values such as higher number of replicas, higher memory/cpu limits and +affinities are specified in that file. + +You can get ahold of `values-ha.yaml` by fetching the chart files: + +```bash +helm fetch --untar linkerd/linkerd-control-plane +``` + +Then use the `-f` flag to provide the override file, for example: + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + -f linkerd2/values-ha.yaml + linkerd/linkerd-control-plane +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Extensions for Linkerd + +The current chart installs the core Linkerd components, which grant you +reliability and security features. Other functionality is available through +extensions. Check the corresponding docs for each one of the following +extensions: + +* Observability: + [Linkerd-viz](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/README.md) +* Multicluster: + [Linkerd-multicluster](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/README.md) +* Tracing: + [Linkerd-jaeger](https://github.com/linkerd/linkerd2/blob/main/jaeger/charts/linkerd-jaeger/README.md) + +## Requirements + +Kubernetes: `>=1.22.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../partials | partials | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| clusterDomain | string | `"cluster.local"` | Kubernetes DNS Domain name to use | +| clusterNetworks | string | `"10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8"` | The cluster networks for which service discovery is performed. This should include the pod and service networks, but need not include the node network. By default, all IPv4 private networks and all accepted IPv6 ULAs are specified so that resolution works in typical Kubernetes environments. | +| cniEnabled | bool | `false` | enabling this omits the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed | +| commonLabels | object | `{}` | Labels to apply to all resources | +| controlPlaneTracing | bool | `false` | enables control plane tracing | +| controlPlaneTracingNamespace | string | `"linkerd-jaeger"` | namespace to send control plane traces to | +| controller.podDisruptionBudget | object | `{"maxUnavailable":1}` | sets pod disruption budget parameter for all deployments | +| controller.podDisruptionBudget.maxUnavailable | int | `1` | Maximum number of pods that can be unavailable during disruption | +| controllerGID | int | `-1` | Optional customisation of the group ID for the control plane components (the group ID will be omitted if lower than 0) | +| controllerImage | string | `"cr.l5d.io/linkerd/controller"` | Docker image for the destination and identity components | +| controllerImageVersion | string | `""` | Optionally allow a specific container image Tag (or SHA) to be specified for the controllerImage. | +| controllerLogFormat | string | `"plain"` | Log format for the control plane components | +| controllerLogLevel | string | `"info"` | Log level for the control plane components | +| controllerReplicas | int | `1` | Number of replicas for each control plane pod | +| controllerUID | int | `2103` | User ID for the control plane components | +| debugContainer.image.name | string | `"cr.l5d.io/linkerd/debug"` | Docker image for the debug container | +| debugContainer.image.pullPolicy | string | imagePullPolicy | Pull policy for the debug container image | +| debugContainer.image.version | string | linkerdVersion | Tag for the debug container image | +| deploymentStrategy | object | `{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"}}` | default kubernetes deployment strategy | +| destinationController.livenessProbe.timeoutSeconds | int | `1` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.interval.seconds | int | `10` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.timeout.seconds | int | `3` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.while_idle | bool | `true` | | +| destinationController.readinessProbe.timeoutSeconds | int | `1` | | +| disableHeartBeat | bool | `false` | Set to true to not start the heartbeat cronjob | +| disableIPv6 | bool | `true` | disables routing IPv6 traffic in addition to IPv4 traffic through the proxy (IPv6 routing only available as of proxy-init v2.3.0 and linkerd-cni v1.4.0) | +| enableEndpointSlices | bool | `true` | enables the use of EndpointSlice informers for the destination service; enableEndpointSlices should be set to true only if EndpointSlice K8s feature gate is on | +| enableH2Upgrade | bool | `true` | Allow proxies to perform transparent HTTP/2 upgrading | +| enablePSP | bool | `false` | Add a PSP resource and bind it to the control plane ServiceAccounts. Note PSP has been deprecated since k8s v1.21 | +| enablePodAntiAffinity | bool | `false` | enables pod anti affinity creation on deployments for high availability | +| enablePodDisruptionBudget | bool | `false` | enables the creation of pod disruption budgets for control plane components | +| enablePprof | bool | `false` | enables the use of pprof endpoints on control plane component's admin servers | +| identity.externalCA | bool | `false` | If the linkerd-identity-trust-roots ConfigMap has already been created | +| identity.issuer.clockSkewAllowance | string | `"20s"` | Amount of time to allow for clock skew within a Linkerd cluster | +| identity.issuer.issuanceLifetime | string | `"24h0m0s"` | Amount of time for which the Identity issuer should certify identity | +| identity.issuer.scheme | string | `"linkerd.io/tls"` | | +| identity.issuer.tls | object | `{"crtPEM":"","keyPEM":""}` | Which scheme is used for the identity issuer secret format | +| identity.issuer.tls.crtPEM | string | `""` | Issuer certificate (ECDSA). It must be provided during install. | +| identity.issuer.tls.keyPEM | string | `""` | Key for the issuer certificate (ECDSA). It must be provided during install | +| identity.kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| identity.kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | +| identity.livenessProbe.timeoutSeconds | int | `1` | | +| identity.readinessProbe.timeoutSeconds | int | `1` | | +| identity.serviceAccountTokenProjection | bool | `true` | Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token | +| identityTrustAnchorsPEM | string | `""` | Trust root certificate (ECDSA). It must be provided during install. | +| identityTrustDomain | string | clusterDomain | Trust domain used for identity | +| imagePullPolicy | string | `"IfNotPresent"` | Docker image pull policy | +| imagePullSecrets | list | `[]` | For Private docker registries, authentication is needed. Registry secrets are applied to the respective service accounts | +| kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | +| linkerdVersion | string | `"linkerdVersionValue"` | control plane version. See Proxy section for proxy version | +| networkValidator.connectAddr | string | `""` | Address to which the network-validator will attempt to connect. This should be an IP that the cluster is expected to be able to reach but a port it should not, e.g., a public IP for public clusters and a private IP for air-gapped clusters with a port like 20001. If empty, defaults to 1.1.1.1:20001 and [fd00::1]:20001 for IPv4 and IPv6 respectively. | +| networkValidator.enableSecurityContext | bool | `true` | Include a securityContext in the network-validator pod spec | +| networkValidator.listenAddr | string | `""` | Address to which network-validator listens to requests from itself. If empty, defaults to 0.0.0.0:4140 and [::]:4140 for IPv4 and IPv6 respectively. | +| networkValidator.logFormat | string | plain | Log format (`plain` or `json`) for network-validator | +| networkValidator.logLevel | string | debug | Log level for the network-validator | +| networkValidator.timeout | string | `"10s"` | Timeout before network-validator fails to validate the pod's network connectivity | +| nodeSelector | object | `{"kubernetes.io/os":"linux"}` | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information | +| podAnnotations | object | `{}` | Additional annotations to add to all pods | +| podLabels | object | `{}` | Additional labels to add to all pods | +| podMonitor.controller.enabled | bool | `true` | Enables the creation of PodMonitor for the control-plane | +| podMonitor.controller.namespaceSelector | string | `"matchNames:\n - {{ .Release.Namespace }}\n - linkerd-viz\n - linkerd-jaeger\n"` | Selector to select which namespaces the Endpoints objects are discovered from | +| podMonitor.enabled | bool | `false` | Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) | +| podMonitor.labels | object | `{}` | Labels to apply to all pod Monitors | +| podMonitor.proxy.enabled | bool | `true` | Enables the creation of PodMonitor for the data-plane | +| podMonitor.scrapeInterval | string | `"10s"` | Interval at which metrics should be scraped | +| podMonitor.scrapeTimeout | string | `"10s"` | Iimeout after which the scrape is ended | +| podMonitor.serviceMirror.enabled | bool | `true` | Enables the creation of PodMonitor for the Service Mirror component | +| policyController.image.name | string | `"cr.l5d.io/linkerd/policy-controller"` | Docker image for the policy controller | +| policyController.image.pullPolicy | string | imagePullPolicy | Pull policy for the policy controller container image | +| policyController.image.version | string | linkerdVersion | Tag for the policy controller container image | +| policyController.livenessProbe.timeoutSeconds | int | `1` | | +| policyController.logLevel | string | `"info"` | Log level for the policy controller | +| policyController.probeNetworks | list | `["0.0.0.0/0","::/0"]` | The networks from which probes are performed. By default, all networks are allowed so that all probes are authorized. | +| policyController.readinessProbe.timeoutSeconds | int | `1` | | +| policyController.resources | object | `{"cpu":{"limit":"","request":""},"ephemeral-storage":{"limit":"","request":""},"memory":{"limit":"","request":""}}` | policy controller resource requests & limits | +| policyController.resources.cpu.limit | string | `""` | Maximum amount of CPU units that the policy controller can use | +| policyController.resources.cpu.request | string | `""` | Amount of CPU units that the policy controller requests | +| policyController.resources.ephemeral-storage.limit | string | `""` | Maximum amount of ephemeral storage that the policy controller can use | +| policyController.resources.ephemeral-storage.request | string | `""` | Amount of ephemeral storage that the policy controller requests | +| policyController.resources.memory.limit | string | `""` | Maximum amount of memory that the policy controller can use | +| policyController.resources.memory.request | string | `""` | Maximum amount of memory that the policy controller requests | +| policyValidator.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `policyValidator.crtPEM`. If `policyValidator.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| policyValidator.crtPEM | string | `""` | Certificate for the policy validator. If not provided and not using an external secret then Helm will generate one. | +| policyValidator.externalSecret | bool | `false` | Do not create a secret resource for the policyValidator webhook. If this is set to `true`, the value `policyValidator.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `policyValidator.injectCaFrom` or `policyValidator.injectCaFromSecret` (see below). | +| policyValidator.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| policyValidator.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| policyValidator.keyPEM | string | `""` | Certificate key for the policy validator. If not provided and not using an external secret then Helm will generate one. | +| policyValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook | +| priorityClassName | string | `""` | Kubernetes priorityClassName for the Linkerd Pods | +| profileValidator.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `profileValidator.crtPEM`. If `profileValidator.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| profileValidator.crtPEM | string | `""` | Certificate for the service profile validator. If not provided and not using an external secret then Helm will generate one. | +| profileValidator.externalSecret | bool | `false` | Do not create a secret resource for the profileValidator webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). | +| profileValidator.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| profileValidator.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| profileValidator.keyPEM | string | `""` | Certificate key for the service profile validator. If not provided and not using an external secret then Helm will generate one. | +| profileValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook | +| prometheusUrl | string | `""` | url of external prometheus instance (used for the heartbeat) | +| proxy.await | bool | `true` | If set, the application container will not start until the proxy is ready | +| proxy.control.streams.idleTimeout | string | `"5m"` | The timeout between consecutive updates from the control plane. | +| proxy.control.streams.initialTimeout | string | `"3s"` | The timeout for the first update from the control plane. | +| proxy.control.streams.lifetime | string | `"1h"` | The maximum duration for a response stream (i.e. before it will be reinitialized). | +| proxy.cores | int | `0` | The `cpu.limit` and `cores` should be kept in sync. The value of `cores` must be an integer and should typically be set by rounding up from the limit. E.g. if cpu.limit is '1500m', cores should be 2. | +| proxy.defaultInboundPolicy | string | "all-unauthenticated" | The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny", "audit" | +| proxy.disableInboundProtocolDetectTimeout | bool | `false` | When set to true, disables the protocol detection timeout on the inbound side of the proxy by setting it to a very high value | +| proxy.disableOutboundProtocolDetectTimeout | bool | `false` | When set to true, disables the protocol detection timeout on the outbound side of the proxy by setting it to a very high value | +| proxy.enableExternalProfiles | bool | `false` | Enable service profiles for non-Kubernetes services | +| proxy.enableShutdownEndpoint | bool | `false` | Enables the proxy's /shutdown admin endpoint | +| proxy.gid | int | `-1` | Optional customisation of the group id under which the proxy runs (the group ID will be omitted if lower than 0) | +| proxy.image.name | string | `"cr.l5d.io/linkerd/proxy"` | Docker image for the proxy | +| proxy.image.pullPolicy | string | imagePullPolicy | Pull policy for the proxy container image | +| proxy.image.version | string | linkerdVersion | Tag for the proxy container image | +| proxy.inbound.server.http2.keepAliveInterval | string | `"10s"` | The interval at which PINGs are issued to remote HTTP/2 clients. | +| proxy.inbound.server.http2.keepAliveTimeout | string | `"3s"` | The timeout within which keep-alive PINGs must be acknowledged on inbound HTTP/2 connections. | +| proxy.inboundConnectTimeout | string | `"100ms"` | Maximum time allowed for the proxy to establish an inbound TCP connection | +| proxy.inboundDiscoveryCacheUnusedTimeout | string | `"90s"` | Maximum time allowed before an unused inbound discovery result is evicted from the cache | +| proxy.livenessProbe | object | `{"initialDelaySeconds":10,"timeoutSeconds":1}` | LivenessProbe timeout and delay configuration | +| proxy.logFormat | string | `"plain"` | Log format (`plain` or `json`) for the proxy | +| proxy.logHTTPHeaders | `off` or `insecure` | `"off"` | If set to `off`, will prevent the proxy from logging HTTP headers. If set to `insecure`, HTTP headers may be logged verbatim. Note that setting this to `insecure` is not alone sufficient to log HTTP headers; the proxy logLevel must also be set to debug. | +| proxy.logLevel | string | `"warn,linkerd=info,hickory=error"` | Log level for the proxy | +| proxy.nativeSidecar | bool | `false` | Enable KEP-753 native sidecars This is an experimental feature. It requires Kubernetes >= 1.29. If enabled, .proxy.waitBeforeExitSeconds should not be used. | +| proxy.opaquePorts | string | `"25,587,3306,4444,5432,6379,9300,11211"` | Default set of opaque ports - SMTP (25,587) server-first - MYSQL (3306) server-first - Galera (4444) server-first - PostgreSQL (5432) server-first - Redis (6379) server-first - ElasticSearch (9300) server-first - Memcached (11211) clients do not issue any preamble, which breaks detection | +| proxy.outbound.server.http2.keepAliveInterval | string | `"10s"` | The interval at which PINGs are issued to local application HTTP/2 clients. | +| proxy.outbound.server.http2.keepAliveTimeout | string | `"3s"` | The timeout within which keep-alive PINGs must be acknowledged on outbound HTTP/2 connections. | +| proxy.outboundConnectTimeout | string | `"1000ms"` | Maximum time allowed for the proxy to establish an outbound TCP connection | +| proxy.outboundDiscoveryCacheUnusedTimeout | string | `"5s"` | Maximum time allowed before an unused outbound discovery result is evicted from the cache | +| proxy.ports.admin | int | `4191` | Admin port for the proxy container | +| proxy.ports.control | int | `4190` | Control port for the proxy container | +| proxy.ports.inbound | int | `4143` | Inbound port for the proxy container | +| proxy.ports.outbound | int | `4140` | Outbound port for the proxy container | +| proxy.readinessProbe | object | `{"initialDelaySeconds":2,"timeoutSeconds":1}` | ReadinessProbe timeout and delay configuration | +| proxy.requireIdentityOnInboundPorts | string | `""` | | +| proxy.resources.cpu.limit | string | `""` | Maximum amount of CPU units that the proxy can use | +| proxy.resources.cpu.request | string | `""` | Amount of CPU units that the proxy requests | +| proxy.resources.ephemeral-storage.limit | string | `""` | Maximum amount of ephemeral storage that the proxy can use | +| proxy.resources.ephemeral-storage.request | string | `""` | Amount of ephemeral storage that the proxy requests | +| proxy.resources.memory.limit | string | `""` | Maximum amount of memory that the proxy can use | +| proxy.resources.memory.request | string | `""` | Maximum amount of memory that the proxy requests | +| proxy.shutdownGracePeriod | string | `""` | Grace period for graceful proxy shutdowns. If this timeout elapses before all open connections have completed, the proxy will terminate forcefully, closing any remaining connections. | +| proxy.startupProbe.failureThreshold | int | `120` | | +| proxy.startupProbe.initialDelaySeconds | int | `0` | | +| proxy.startupProbe.periodSeconds | int | `1` | | +| proxy.uid | int | `2102` | User id under which the proxy runs | +| proxy.waitBeforeExitSeconds | int | `0` | If set the injected proxy sidecars in the data plane will stay alive for at least the given period before receiving the SIGTERM signal from Kubernetes but no longer than the pod's `terminationGracePeriodSeconds`. See [Lifecycle hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) for more info on container lifecycle hooks. | +| proxyInit.closeWaitTimeoutSecs | int | `0` | | +| proxyInit.ignoreInboundPorts | string | `"4567,4568"` | Default set of inbound ports to skip via iptables - Galera (4567,4568) | +| proxyInit.ignoreOutboundPorts | string | `"4567,4568"` | Default set of outbound ports to skip via iptables - Galera (4567,4568) | +| proxyInit.image.name | string | `"cr.l5d.io/linkerd/proxy-init"` | Docker image for the proxy-init container | +| proxyInit.image.pullPolicy | string | imagePullPolicy | Pull policy for the proxy-init container image | +| proxyInit.image.version | string | `"v2.4.1"` | Tag for the proxy-init container image | +| proxyInit.iptablesMode | string | `"legacy"` | Variant of iptables that will be used to configure routing. Currently, proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will control which utility binary will be called. The host must support whichever mode will be used | +| proxyInit.kubeAPIServerPorts | string | `"443,6443"` | Default set of ports to skip via iptables for control plane components so they can communicate with the Kubernetes API Server | +| proxyInit.logFormat | string | plain | Log format (`plain` or `json`) for the proxy-init | +| proxyInit.logLevel | string | info | Log level for the proxy-init | +| proxyInit.privileged | bool | false | Privileged mode allows the container processes to inherit all security capabilities and bypass any security limitations enforced by the kubelet. When used with 'runAsRoot: true', the container will behave exactly as if it was running as root on the host. May escape cgroup limits and see other processes and devices on the host. | +| proxyInit.runAsGroup | int | `65534` | This value is used only if runAsRoot is false; otherwise runAsGroup will be 0 | +| proxyInit.runAsRoot | bool | `false` | Allow overriding the runAsNonRoot behaviour () | +| proxyInit.runAsUser | int | `65534` | This value is used only if runAsRoot is false; otherwise runAsUser will be 0 | +| proxyInit.skipSubnets | string | `""` | Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy | +| proxyInit.xtMountPath.mountPath | string | `"/run"` | | +| proxyInit.xtMountPath.name | string | `"linkerd-proxy-init-xtables-lock"` | | +| proxyInjector.caBundle | string | `""` | Bundle of CA certificates for proxy injector. If not provided nor injected with cert-manager, then Helm will use the certificate generated for `proxyInjector.crtPEM`. If `proxyInjector.externalSecret` is set to true, this value, injectCaFrom, or injectCaFromSecret must be set, as no certificate will be generated. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. | +| proxyInjector.crtPEM | string | `""` | Certificate for the proxy injector. If not provided and not using an external secret then Helm will generate one. | +| proxyInjector.externalSecret | bool | `false` | Do not create a secret resource for the proxyInjector webhook. If this is set to `true`, the value `proxyInjector.caBundle` must be set or the ca bundle must injected with cert-manager ca injector using `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). | +| proxyInjector.injectCaFrom | string | `""` | Inject the CA bundle from a cert-manager Certificate. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) for more information. | +| proxyInjector.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. | +| proxyInjector.keyPEM | string | `""` | Certificate key for the proxy injector. If not provided and not using an external secret then Helm will generate one. | +| proxyInjector.livenessProbe.timeoutSeconds | int | `1` | | +| proxyInjector.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]},{"key":"kubernetes.io/metadata.name","operator":"NotIn","values":["kube-system","cert-manager"]}]}` | Namespace selector used by admission webhook. | +| proxyInjector.objectSelector | object | `{"matchExpressions":[{"key":"linkerd.io/control-plane-component","operator":"DoesNotExist"},{"key":"linkerd.io/cni-resource","operator":"DoesNotExist"}]}` | Object selector used by admission webhook. | +| proxyInjector.readinessProbe.timeoutSeconds | int | `1` | | +| proxyInjector.timeoutSeconds | int | `10` | Timeout in seconds before the API Server cancels a request to the proxy injector. If timeout is exceeded, the webhookfailurePolicy is used. | +| revisionHistoryLimit | int | `10` | Specifies the number of old ReplicaSets to retain to allow rollback. | +| runtimeClassName | string | `""` | Runtime Class Name for all the pods | +| spValidator | object | `{"livenessProbe":{"timeoutSeconds":1},"readinessProbe":{"timeoutSeconds":1}}` | SP validator configuration | +| webhookFailurePolicy | string | `"Ignore"` | Failure policy for the proxy injector | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/README.md.gotmpl b/charts/linkerd/linkerd-control-plane/2024.10.3/README.md.gotmpl new file mode 100644 index 0000000000..19da2a82d6 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/app-readme.md b/charts/linkerd/linkerd-control-plane/2024.10.3/app-readme.md new file mode 100644 index 0000000000..351eac5f0d --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/.helmignore b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/Chart.yaml new file mode 100644 index 0000000000..23cfc167e3 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md new file mode 100644 index 0000000000..10805c9b94 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md.gotmpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/README.md.gotmpl new file mode 100644 index 0000000000..37f5101061 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/NOTES.txt b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/NOTES.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_affinity.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_affinity.tpl new file mode 100644 index 0000000000..5dde1da473 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_capabilities.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_capabilities.tpl new file mode 100644 index 0000000000..a595d74c1f --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_debug.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_debug.tpl new file mode 100644 index 0000000000..4df8cc77bc --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_helpers.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_helpers.tpl new file mode 100644 index 0000000000..b6cdc34d08 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_metadata.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_metadata.tpl new file mode 100644 index 0000000000..04d2f1beab --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_network-validator.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_network-validator.tpl new file mode 100644 index 0000000000..276056395f --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_nodeselector.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 0000000000..4cde0ab16e --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 0000000000..9651b3bd1a --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-init.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 0000000000..a307b14073 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,98 @@ +{{- define "partials.proxy-init" -}} +args: +{{- if (.Values.proxyInit.iptablesMode | default "legacy" | eq "nft") }} +- --firewall-bin-path +- "iptables-nft" +- --firewall-save-bin-path +- "iptables-nft-save" +{{- else if not (eq .Values.proxyInit.iptablesMode "legacy") }} +{{ fail (printf "Unsupported value \"%s\" for proxyInit.iptablesMode\nValid values: [\"nft\", \"legacy\"]" .Values.proxyInit.iptablesMode) }} +{{end -}} +{{- if .Values.disableIPv6 }} +- --ipv6=false +{{- end }} +- --incoming-proxy-port +- {{.Values.proxy.ports.inbound | quote}} +- --outgoing-proxy-port +- {{.Values.proxy.ports.outbound | quote}} +- --proxy-uid +- {{.Values.proxy.uid | quote}} +{{- if ge (int .Values.proxy.gid) 0 }} +- --proxy-gid +- {{.Values.proxy.gid | quote}} +{{- end }} +- --inbound-ports-to-ignore +- "{{.Values.proxy.ports.control}},{{.Values.proxy.ports.admin}}{{ternary (printf ",%s" (.Values.proxyInit.ignoreInboundPorts | toString)) "" (not (empty .Values.proxyInit.ignoreInboundPorts)) }}" +{{- if .Values.proxyInit.ignoreOutboundPorts }} +- --outbound-ports-to-ignore +- {{.Values.proxyInit.ignoreOutboundPorts | quote}} +{{- end }} +{{- if .Values.proxyInit.closeWaitTimeoutSecs }} +- --timeout-close-wait-secs +- {{ .Values.proxyInit.closeWaitTimeoutSecs | quote}} +{{- end }} +{{- if .Values.proxyInit.logFormat }} +- --log-format +- {{ .Values.proxyInit.logFormat }} +{{- end }} +{{- if .Values.proxyInit.logLevel }} +- --log-level +- {{ .Values.proxyInit.logLevel }} +{{- end }} +{{- if .Values.proxyInit.skipSubnets }} +- --subnets-to-ignore +- {{ .Values.proxyInit.skipSubnets | quote }} +{{- end }} +image: {{.Values.proxyInit.image.name}}:{{.Values.proxyInit.image.version}} +imagePullPolicy: {{.Values.proxyInit.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-init +{{ include "partials.resources" .Values.proxy.resources }} +securityContext: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + capabilities: + add: + - NET_ADMIN + - NET_RAW + {{- if .Values.proxyInit.capabilities -}} + {{- if .Values.proxyInit.capabilities.add }} + {{- toYaml .Values.proxyInit.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxyInit.capabilities.drop -}} + {{- include "partials.proxy-init.capabilities.drop" . | nindent 4 -}} + {{- end }} + {{- end }} + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + privileged: true + {{- else }} + privileged: false + {{- end }} + {{- if .Values.proxyInit.runAsRoot }} + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + {{- else }} + runAsNonRoot: true + runAsUser: {{ .Values.proxyInit.runAsUser | int | eq 0 | ternary 65534 .Values.proxyInit.runAsUser }} + runAsGroup: {{ .Values.proxyInit.runAsGroup | int | eq 0 | ternary 65534 .Values.proxyInit.runAsGroup }} + {{- end }} + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if or (not .Values.cniEnabled) .Values.proxyInit.saMountPath }} +volumeMounts: +{{- end -}} +{{- if not .Values.cniEnabled }} +- mountPath: {{.Values.proxyInit.xtMountPath.mountPath}} + name: {{.Values.proxyInit.xtMountPath.name}} +{{- end -}} +{{- if .Values.proxyInit.saMountPath }} +- mountPath: {{.Values.proxyInit.saMountPath.mountPath}} + name: {{.Values.proxyInit.saMountPath.name}} + readOnly: {{.Values.proxyInit.saMountPath.readOnly}} +{{- end -}} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy.tpl new file mode 100644 index 0000000000..4dcf12dee2 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_proxy.tpl @@ -0,0 +1,271 @@ +{{ define "partials.proxy" -}} +{{ if and .Values.proxy.nativeSidecar .Values.proxy.waitBeforeExitSeconds }} +{{ fail "proxy.nativeSidecar and waitBeforeExitSeconds cannot be used simultaneously" }} +{{- end }} +{{- if not (has .Values.proxy.logHTTPHeaders (list "insecure" "off" "")) }} +{{- fail "logHTTPHeaders must be one of: insecure | off" }} +{{- end }} +{{- $trustDomain := (.Values.identityTrustDomain | default .Values.clusterDomain) -}} +env: +- name: _pod_name + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: _pod_ns + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +{{- if .Values.proxy.cores }} +- name: LINKERD2_PROXY_CORES + value: {{.Values.proxy.cores | quote}} +{{- end }} +{{ if .Values.proxy.requireIdentityOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_IDENTITY + value: {{.Values.proxy.requireIdentityOnInboundPorts | quote}} +{{ end -}} +{{ if .Values.proxy.requireTLSOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_TLS + value: {{.Values.proxy.requireTLSOnInboundPorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_SHUTDOWN_ENDPOINT_ENABLED + value: {{.Values.proxy.enableShutdownEndpoint | quote}} +- name: LINKERD2_PROXY_LOG + value: "{{.Values.proxy.logLevel}}{{ if not (eq .Values.proxy.logHTTPHeaders "insecure") }},[{headers}]=off,[{request}]=off{{ end }}" +- name: LINKERD2_PROXY_LOG_FORMAT + value: {{.Values.proxy.logFormat | quote}} +- name: LINKERD2_PROXY_DESTINATION_SVC_ADDR + value: {{ternary "localhost.:8086" (printf "linkerd-dst-headless.%s.svc.%s.:8086" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_POLICY_SVC_ADDR + value: {{ternary "localhost.:8090" (printf "linkerd-policy.%s.svc.%s.:8090" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_POLICY_WORKLOAD + value: | + {"ns":"$(_pod_ns)", "pod":"$(_pod_name)"} +- name: LINKERD2_PROXY_INBOUND_DEFAULT_POLICY + value: {{.Values.proxy.defaultInboundPolicy}} +- name: LINKERD2_PROXY_POLICY_CLUSTER_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_INITIAL_TIMEOUT + value: {{((.Values.proxy.control).streams).initialTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_IDLE_TIMEOUT + value: {{((.Values.proxy.control).streams).idleTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_LIFETIME + value: {{((.Values.proxy.control).streams).lifetime | default "" | quote}} +{{ if .Values.proxy.inboundConnectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.inboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundConnectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.outboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.outboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.inboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.inboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.disableOutboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +{{ if .Values.proxy.disableInboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +- name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.control}}" +- name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.admin}}" +{{- /* Deprecated, superseded by LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS since proxy's v2.228.0 (deployed since edge-24.4.5) */}} +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDR + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}" +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}{{ if not .Values.disableIPv6}},[::1]:{{.Values.proxy.ports.outbound}}{{ end }}" +- name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.inbound}}" +- name: LINKERD2_PROXY_INBOUND_IPS + valueFrom: + fieldRef: + fieldPath: status.podIPs +- name: LINKERD2_PROXY_INBOUND_PORTS + value: {{ .Values.proxy.podInboundPorts | quote }} +{{ if .Values.proxy.isGateway -}} +- name: LINKERD2_PROXY_INBOUND_GATEWAY_SUFFIXES + value: {{printf "svc.%s." .Values.clusterDomain}} +{{ end -}} +{{ if .Values.proxy.isIngress -}} +- name: LINKERD2_PROXY_INGRESS_MODE + value: "true" +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES + {{- $internalDomain := printf "svc.%s." .Values.clusterDomain }} + value: {{ternary "." $internalDomain .Values.proxy.enableExternalProfiles}} +- name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_INBOUND_ACCEPT_USER_TIMEOUT + value: 30s +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_USER_TIMEOUT + value: 30s +{{- /* Configure inbound and outbound parameters, e.g. for HTTP/2 servers. */}} +{{ range $proxyK, $proxyV := (dict "inbound" .Values.proxy.inbound "outbound" .Values.proxy.outbound) -}} +{{ range $scopeK, $scopeV := $proxyV -}} +{{ range $protoK, $protoV := $scopeV -}} +{{ range $paramK, $paramV := $protoV -}} +- name: LINKERD2_PROXY_{{snakecase $proxyK | upper}}_{{snakecase $scopeK | upper}}_{{snakecase $protoK | upper}}_{{snakecase $paramK | upper}} + value: {{ quote $paramV }} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ if .Values.proxy.opaquePorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION + value: {{.Values.proxy.opaquePorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_CONTEXT + value: | + {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)", "pod":"$(_pod_name)"} +- name: _pod_sa + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName +- name: _l5d_ns + value: {{.Release.Namespace}} +- name: _l5d_trustdomain + value: {{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_DIR + value: /var/run/linkerd/identity/end-entity +- name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS +{{- /* +Pods in the `linkerd` namespace are not injected by the proxy injector and instead obtain +the trust anchor bundle from the `linkerd-identity-trust-roots` configmap. This should not +be used in other contexts. +*/}} +{{- if .Values.proxy.loadTrustBundleFromConfigMap }} + valueFrom: + configMapKeyRef: + name: linkerd-identity-trust-roots + key: ca-bundle.crt +{{ else }} + value: | + {{- required "Please provide the identity trust anchors" .Values.identityTrustAnchorsPEM | trim | nindent 4 }} +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_TOKEN_FILE +{{- if .Values.identity.serviceAccountTokenProjection }} + value: /var/run/secrets/tokens/linkerd-identity-token +{{ else }} + value: /var/run/secrets/kubernetes.io/serviceaccount/token +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_SVC_ADDR + value: {{ternary "localhost.:8080" (printf "linkerd-identity-headless.%s.svc.%s.:8080" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-identity")}} +- name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME + value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_SVC_NAME + value: linkerd-identity.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_DESTINATION_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_POLICY_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +{{ if .Values.proxy.accessLog -}} +- name: LINKERD2_PROXY_ACCESS_LOG + value: {{.Values.proxy.accessLog | quote}} +{{ end -}} +{{ if .Values.proxy.shutdownGracePeriod -}} +- name: LINKERD2_PROXY_SHUTDOWN_GRACE_PERIOD + value: {{.Values.proxy.shutdownGracePeriod | quote}} +{{ end -}} +{{ if .Values.proxy.additionalEnv -}} +{{ toYaml .Values.proxy.additionalEnv }} +{{ end -}} +{{ if .Values.proxy.experimentalEnv -}} +{{ toYaml .Values.proxy.experimentalEnv }} +{{ end -}} +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +livenessProbe: + httpGet: + path: /live + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.livenessProbe.timeoutSeconds }} +name: linkerd-proxy +ports: +- containerPort: {{.Values.proxy.ports.inbound}} + name: linkerd-proxy +- containerPort: {{.Values.proxy.ports.admin}} + name: linkerd-admin +readinessProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.readinessProbe.timeoutSeconds }} +{{- if and .Values.proxy.nativeSidecar .Values.proxy.await }} +startupProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.startupProbe.initialDelaySeconds}} + periodSeconds: {{.Values.proxy.startupProbe.periodSeconds}} + failureThreshold: {{.Values.proxy.startupProbe.failureThreshold}} +{{- end }} +{{- if .Values.proxy.resources }} +{{ include "partials.resources" .Values.proxy.resources }} +{{- end }} +securityContext: + allowPrivilegeEscalation: false + {{- if .Values.proxy.capabilities -}} + {{- include "partials.proxy.capabilities" . | nindent 2 -}} + {{- end }} + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.proxy.uid}} +{{- if ge (int .Values.proxy.gid) 0 }} + runAsGroup: {{.Values.proxy.gid}} +{{- end }} + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if and (not .Values.proxy.nativeSidecar) (or .Values.proxy.await .Values.proxy.waitBeforeExitSeconds) }} +lifecycle: +{{- if .Values.proxy.await }} + postStart: + exec: + command: + - /usr/lib/linkerd/linkerd-await + - --timeout=2m + - --port={{.Values.proxy.ports.admin}} +{{- end }} +{{- if .Values.proxy.waitBeforeExitSeconds }} + preStop: + exec: + command: + - /bin/sleep + - {{.Values.proxy.waitBeforeExitSeconds | quote}} +{{- end }} +{{- end }} +volumeMounts: +- mountPath: /var/run/linkerd/identity/end-entity + name: linkerd-identity-end-entity +{{- if .Values.identity.serviceAccountTokenProjection }} +- mountPath: /var/run/secrets/tokens + name: linkerd-identity-token +{{- end }} +{{- if .Values.proxy.saMountPath }} +- mountPath: {{.Values.proxy.saMountPath.mountPath}} + name: {{.Values.proxy.saMountPath.name}} + readOnly: {{.Values.proxy.saMountPath.readOnly}} +{{- end -}} +{{- if .Values.proxy.nativeSidecar }} +restartPolicy: Always +{{- end -}} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_pull-secrets.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 0000000000..0c9aa4f01c --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_resources.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_resources.tpl new file mode 100644 index 0000000000..1fd6789fd7 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_tolerations.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_tolerations.tpl new file mode 100644 index 0000000000..c2292b1464 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_trace.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_trace.tpl new file mode 100644 index 0000000000..dee059541f --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_validate.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_validate.tpl new file mode 100644 index 0000000000..ba772c2fee --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_volumes.tpl b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_volumes.tpl new file mode 100644 index 0000000000..9684cf2409 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/templates/_volumes.tpl @@ -0,0 +1,20 @@ +{{ 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 -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/values.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/charts/partials/values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/questions.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/questions.yaml new file mode 100644 index 0000000000..4ae27870a3 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/NOTES.txt b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/NOTES.txt new file mode 100644 index 0000000000..4bd1be9fc0 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/config-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/config-rbac.yaml new file mode 100644 index 0000000000..5f5c34203e --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/config.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/config.yaml new file mode 100644 index 0000000000..a9cea5f421 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/destination-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination-rbac.yaml new file mode 100644 index 0000000000..38488cd048 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination-rbac.yaml @@ -0,0 +1,327 @@ +--- +### +### Destination Controller Service +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-destination + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["list", "get", "watch"] +- apiGroups: [""] + resources: ["pods", "endpoints", "services", "nodes"] + verbs: ["list", "get", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["workload.linkerd.io"] + resources: ["externalworkloads"] + verbs: ["list", "get", "watch"] +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["create", "get", "update", "patch"] + {{- if .Values.enableEndpointSlices }} +- apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["list", "get", "watch", "create", "update", "patch", "delete"] + {{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-destination + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-destination +subjects: +- kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-destination + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +--- +{{- $host := printf "linkerd-sp-validator.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.profileValidator.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-sp-validator-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.profileValidator.crtPEM)) (empty .Values.profileValidator.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.profileValidator.keyPEM)) (empty .Values.profileValidator.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.profileValidator }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: linkerd-sp-validator-webhook-config + {{- if or (.Values.profileValidator.injectCaFrom) (.Values.profileValidator.injectCaFromSecret) }} + annotations: + {{- if .Values.profileValidator.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.profileValidator.injectCaFrom }} + {{- end }} + {{- if .Values.profileValidator.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.profileValidator.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-sp-validator.linkerd.io + namespaceSelector: + {{- toYaml .Values.profileValidator.namespaceSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-sp-validator + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.profileValidator.injectCaFrom) (empty .Values.profileValidator.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.profileValidator.caBundle)) (empty .Values.profileValidator.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["linkerd.io"] + apiVersions: ["v1alpha1", "v1alpha2"] + resources: ["serviceprofiles"] + sideEffects: None +--- +{{- $host := printf "linkerd-policy-validator.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.policyValidator.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-policy-validator-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.policyValidator.crtPEM)) (empty .Values.policyValidator.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.policyValidator.keyPEM)) (empty .Values.policyValidator.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.policyValidator }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: linkerd-policy-validator-webhook-config + {{- if or (.Values.policyValidator.injectCaFrom) (.Values.policyValidator.injectCaFromSecret) }} + annotations: + {{- if .Values.policyValidator.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.policyValidator.injectCaFrom }} + {{- end }} + {{- if .Values.policyValidator.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.policyValidator.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-policy-validator.linkerd.io + namespaceSelector: + {{- toYaml .Values.policyValidator.namespaceSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-policy-validator + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.policyValidator.injectCaFrom) (empty .Values.policyValidator.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.policyValidator.caBundle)) (empty .Values.policyValidator.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["policy.linkerd.io"] + apiVersions: ["*"] + resources: + - authorizationpolicies + - httproutes + - networkauthentications + - meshtlsauthentications + - serverauthorizations + - servers + - operations: ["CREATE", "UPDATE"] + apiGroups: ["gateway.networking.k8s.io"] + apiVersions: ["*"] + resources: + - httproutes + - grpcroutes + sideEffects: None +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: linkerd-policy + labels: + app.kubernetes.io/part-of: Linkerd + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - apiGroups: + - policy.linkerd.io + resources: + - authorizationpolicies + - httproutes + - meshtlsauthentications + - networkauthentications + - servers + - serverauthorizations + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + - grpcroutes + verbs: + - get + - list + - watch + - apiGroups: + - policy.linkerd.io + resources: + - httproutes/status + verbs: + - patch + - apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes/status + - grpcroutes/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/linkerd/linkerd-control-plane/2024.10.3/templates/destination.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination.yaml new file mode 100644 index 0000000000..4be0d21abc --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/destination.yaml @@ -0,0 +1,435 @@ +--- +### +### Destination Controller Service +### +kind: Service +apiVersion: v1 +metadata: + name: linkerd-dst + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8086 + targetPort: 8086 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-dst-headless + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8086 + targetPort: 8086 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-sp-validator + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: sp-validator + port: 443 + targetPort: sp-validator +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-policy + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: destination + ports: + - name: grpc + port: 8090 + targetPort: 8090 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-policy-validator + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: destination + ports: + - name: policy-https + port: 443 + targetPort: policy-https +{{- if .Values.enablePodDisruptionBudget }} +--- +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: linkerd-dst + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + linkerd.io/control-plane-component: destination +{{- end }} +--- +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-destination" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.destinationProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.destinationProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.destinationProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: destination + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-destination + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 6}} + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/destination-rbac.yaml") . | sha256sum }} + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: destination + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "destination" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + 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 + - 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 + - args: + - --admin-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9990 + - --control-plane-namespace={{.Release.Namespace}} + - --grpc-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:8090 + - --server-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9443 + - --server-tls-key=/var/run/linkerd/tls/tls.key + - --server-tls-certs=/var/run/linkerd/tls/tls.crt + - --cluster-networks={{.Values.clusterNetworks}} + - --identity-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}} + - --cluster-domain={{.Values.clusterDomain}} + - --default-policy={{.Values.proxy.defaultInboundPolicy}} + - --log-level={{.Values.policyController.logLevel | default "linkerd=info,warn"}} + - --log-format={{.Values.controllerLogFormat}} + - --default-opaque-ports={{.Values.proxy.opaquePorts}} + {{- if .Values.policyController.probeNetworks }} + - --probe-networks={{.Values.policyController.probeNetworks | join ","}} + {{- end}} + {{- range .Values.policyController.additionalArgs }} + - {{ . }} + {{- end }} + {{- range .Values.policyController.experimentalArgs }} + - {{ . }} + {{- end }} + image: {{.Values.policyController.image.name}}:{{.Values.policyController.image.version | default .Values.linkerdVersion}} + imagePullPolicy: {{.Values.policyController.image.pullPolicy | default .Values.imagePullPolicy}} + livenessProbe: + httpGet: + path: /live + port: admin-http + {{- with (.Values.policyController.livenessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + name: policy + ports: + - containerPort: 8090 + name: grpc + - containerPort: 9990 + name: admin-http + - containerPort: 9443 + name: policy-https + readinessProbe: + failureThreshold: 7 + httpGet: + path: /ready + port: admin-http + initialDelaySeconds: 10 + {{- with (.Values.policyController.readinessProbe).timeoutSeconds }} + timeoutSeconds: {{ . }} + {{- end }} + {{- if .Values.policyController.resources }} + {{- include "partials.resources" .Values.policyController.resources | nindent 8 }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.controllerUID}} + {{- if ge (int .Values.controllerGID) 0 }} + runAsGroup: {{.Values.controllerGID}} + {{- end }} + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /var/run/linkerd/tls + name: policy-tls + readOnly: true + 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 + {{ 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/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat-rbac.yaml new file mode 100644 index 0000000000..7b127543f4 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat.yaml new file mode 100644 index 0000000000..9565376239 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/heartbeat.yaml @@ -0,0 +1,94 @@ +{{ 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 + 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 +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity-rbac.yaml new file mode 100644 index 0000000000..6efdb4e104 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/identity.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity.yaml new file mode 100644 index 0000000000..070cadd1ee --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/identity.yaml @@ -0,0 +1,272 @@ +{{if .Values.identity -}} +--- +### +### Identity Controller Service +### +{{ if and (.Values.identity.issuer) (eq .Values.identity.issuer.scheme "linkerd.io/tls") -}} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-identity-issuer + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + crt.pem: {{b64enc (required "Please provide the identity issuer certificate" .Values.identity.issuer.tls.crtPEM | trim)}} + key.pem: {{b64enc (required "Please provide the identity issue private key" .Values.identity.issuer.tls.keyPEM | trim)}} +--- +{{- end}} +{{ if not (.Values.identity.externalCA) -}} +kind: ConfigMap +apiVersion: v1 +metadata: + name: linkerd-identity-trust-roots + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + ca-bundle.crt: |-{{.Values.identityTrustAnchorsPEM | trim | nindent 4}} +--- +{{- end}} +kind: Service +apiVersion: v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + type: ClusterIP + selector: + linkerd.io/control-plane-component: identity + ports: + - name: grpc + port: 8080 + targetPort: 8080 +--- +kind: Service +apiVersion: v1 +metadata: + name: linkerd-identity-headless + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + clusterIP: None + selector: + linkerd.io/control-plane-component: identity + ports: + - name: grpc + port: 8080 + targetPort: 8080 +--- +{{- if .Values.enablePodDisruptionBudget }} +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + linkerd.io/control-plane-component: identity +--- +{{- end }} +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-identity" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.identityProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.identityProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.identityProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: identity + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-identity + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 6}} + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "identity" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + 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 + {{- $_ := 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 + {{ 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/linkerd/linkerd-control-plane/2024.10.3/templates/namespace.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/namespace.yaml new file mode 100644 index 0000000000..61461c1327 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/podmonitor.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/podmonitor.yaml new file mode 100644 index 0000000000..fd2b5d6ceb --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector-rbac.yaml new file mode 100644 index 0000000000..c2c84c5c17 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector.yaml new file mode 100644 index 0000000000..34b1d3ba42 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/proxy-injector.yaml @@ -0,0 +1,222 @@ +--- +### +### Proxy Injector +### +{{- $tree := deepCopy . }} +{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}} +{{ $_ := set $tree.Values.proxy "component" "linkerd-proxy-injector" -}} +{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}} +{{- if not (empty .Values.proxyInjectorProxyResources) }} +{{- $c := dig "cores" .Values.proxy.cores .Values.proxyInjectorProxyResources }} +{{- $_ := set $tree.Values.proxy "cores" $c }} +{{- $r := merge .Values.proxyInjectorProxyResources .Values.proxy.resources }} +{{- $_ := set $tree.Values.proxy "resources" $r }} +{{- end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + app.kubernetes.io/name: proxy-injector + app.kubernetes.io/part-of: Linkerd + app.kubernetes.io/version: {{.Values.linkerdVersion}} + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} +spec: + replicas: {{.Values.controllerReplicas}} + revisionHistoryLimit: {{.Values.revisionHistoryLimit}} + selector: + matchLabels: + linkerd.io/control-plane-component: proxy-injector + {{- if .Values.deploymentStrategy }} + strategy: + {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- end }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/proxy-injector-rbac.yaml") . | sha256sum }} + {{ include "partials.annotations.created-by" . }} + {{- include "partials.proxy.annotations" . | nindent 8}} + {{- with .Values.podAnnotations }}{{ toYaml . | trim | nindent 8 }}{{- end }} + config.linkerd.io/opaque-ports: "8443" + config.linkerd.io/default-inbound-policy: "all-unauthenticated" + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + linkerd.io/workload-ns: {{.Release.Namespace}} + {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}} + {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }} + spec: + {{- with .Values.runtimeClassName }} + runtimeClassName: {{ . | quote }} + {{- end }} + {{- if .Values.tolerations -}} + {{- include "linkerd.tolerations" . | nindent 6 }} + {{- end -}} + {{- include "linkerd.node-selector" . | nindent 6 }} + {{- $_ := set $tree "component" "proxy-injector" -}} + {{- include "linkerd.affinity" $tree | nindent 6 }} + 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 + 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 + {{ 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/linkerd/linkerd-control-plane/2024.10.3/templates/psp.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/templates/psp.yaml new file mode 100644 index 0000000000..db91fea675 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/values-ha.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/values-ha.yaml new file mode 100644 index 0000000000..e3b8cbc070 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/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/linkerd/linkerd-control-plane/2024.10.3/values.yaml b/charts/linkerd/linkerd-control-plane/2024.10.3/values.yaml new file mode 100644 index 0000000000..9ead4d0e52 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.10.3/values.yaml @@ -0,0 +1,664 @@ +# Default values for linkerd. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Kubernetes DNS Domain name to use +clusterDomain: cluster.local + +# -- The cluster networks for which service discovery is performed. This should +# include the pod and service networks, but need not include the node network. +# +# By default, all IPv4 private networks and all accepted IPv6 ULAs are +# specified so that resolution works in typical Kubernetes environments. +clusterNetworks: "10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8" +# -- Docker image pull policy +imagePullPolicy: IfNotPresent +# -- Specifies the number of old ReplicaSets to retain to allow rollback. +revisionHistoryLimit: 10 +# -- Log level for the control plane components +controllerLogLevel: info +# -- Log format for the control plane components +controllerLogFormat: plain +# -- enables control plane tracing +controlPlaneTracing: false +# -- namespace to send control plane traces to +controlPlaneTracingNamespace: linkerd-jaeger +# -- control plane version. See Proxy section for proxy version +linkerdVersion: edge-24.10.3 +# -- default kubernetes deployment strategy +deploymentStrategy: + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% +# -- enables the use of EndpointSlice informers for the destination service; +# enableEndpointSlices should be set to true only if EndpointSlice K8s feature +# gate is on +enableEndpointSlices: true +# -- enables pod anti affinity creation on deployments for high availability +enablePodAntiAffinity: false +# -- enables the use of pprof endpoints on control plane component's admin +# servers +enablePprof: false +# -- enables the creation of pod disruption budgets for control plane components +enablePodDisruptionBudget: false +# -- disables routing IPv6 traffic in addition to IPv4 traffic through the +# proxy (IPv6 routing only available as of proxy-init v2.3.0 and linkerd-cni +# v1.4.0) +disableIPv6: true + +controller: + # -- sets pod disruption budget parameter for all deployments + podDisruptionBudget: + # -- Maximum number of pods that can be unavailable during disruption + maxUnavailable: 1 +# -- enabling this omits the NET_ADMIN capability in the PSP +# and the proxy-init container when injecting the proxy; +# requires the linkerd-cni plugin to already be installed +cniEnabled: false +# -- Trust root certificate (ECDSA). It must be provided during install. +identityTrustAnchorsPEM: | +# -- Trust domain used for identity +# @default -- clusterDomain +identityTrustDomain: "" +kubeAPI: &kubeapi + # -- Maximum QPS sent to the kube-apiserver before throttling. + # See [token bucket rate limiter + # implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) + clientQPS: 100 + # -- Burst value over clientQPS + clientBurst: 200 +# -- Additional annotations to add to all pods +podAnnotations: {} +# -- Additional labels to add to all pods +podLabels: {} +# -- Labels to apply to all resources +commonLabels: {} +# -- Kubernetes priorityClassName for the Linkerd Pods +priorityClassName: "" +# -- Runtime Class Name for all the pods +runtimeClassName: "" + +# policy controller configuration +policyController: + image: + # -- Docker image for the policy controller + name: cr.l5d.io/linkerd/policy-controller + # -- Pull policy for the policy controller container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the policy controller container image + # @default -- linkerdVersion + version: "" + + # -- Log level for the policy controller + logLevel: info + + # -- The networks from which probes are performed. + # + # By default, all networks are allowed so that all probes are authorized. + probeNetworks: + - 0.0.0.0/0 + - "::/0" + + # -- policy controller resource requests & limits + resources: + cpu: + # -- Maximum amount of CPU units that the policy controller can use + limit: "" + # -- Amount of CPU units that the policy controller requests + request: "" + memory: + # -- Maximum amount of memory that the policy controller can use + limit: "" + # -- Maximum amount of memory that the policy controller requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the policy controller can use + limit: "" + # -- Amount of ephemeral storage that the policy controller requests + request: "" + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# proxy configuration +proxy: + # -- Enable service profiles for non-Kubernetes services + enableExternalProfiles: false + # -- Maximum time allowed for the proxy to establish an outbound TCP + # connection + outboundConnectTimeout: 1000ms + # -- Maximum time allowed for the proxy to establish an inbound TCP + # connection + inboundConnectTimeout: 100ms + # -- Maximum time allowed before an unused outbound discovery result + # is evicted from the cache + outboundDiscoveryCacheUnusedTimeout: "5s" + # -- Maximum time allowed before an unused inbound discovery result + # is evicted from the cache + inboundDiscoveryCacheUnusedTimeout: "90s" + # -- When set to true, disables the protocol detection timeout on the + # outbound side of the proxy by setting it to a very high value + disableOutboundProtocolDetectTimeout: false + # -- When set to true, disables the protocol detection timeout on the inbound + # side of the proxy by setting it to a very high value + disableInboundProtocolDetectTimeout: false + image: + # -- Docker image for the proxy + name: cr.l5d.io/linkerd/proxy + # -- Pull policy for the proxy container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy container image + # @default -- linkerdVersion + version: "" + # -- Enables the proxy's /shutdown admin endpoint + enableShutdownEndpoint: false + # -- Log level for the proxy + logLevel: warn,linkerd=info,hickory=error + # -- Log format (`plain` or `json`) for the proxy + logFormat: plain + # -- (`off` or `insecure`) If set to `off`, will prevent the proxy from + # logging HTTP headers. If set to `insecure`, HTTP headers may be logged + # verbatim. Note that setting this to `insecure` is not alone sufficient to + # log HTTP headers; the proxy logLevel must also be set to debug. + logHTTPHeaders: "off" + ports: + # -- Admin port for the proxy container + admin: 4191 + # -- Control port for the proxy container + control: 4190 + # -- Inbound port for the proxy container + inbound: 4143 + # -- Outbound port for the proxy container + outbound: 4140 + # -- The `cpu.limit` and `cores` should be kept in sync. The value of `cores` + # must be an integer and should typically be set by rounding up from the + # limit. E.g. if cpu.limit is '1500m', cores should be 2. + cores: 0 + resources: + cpu: + # -- Maximum amount of CPU units that the proxy can use + limit: "" + # -- Amount of CPU units that the proxy requests + request: "" + memory: + # -- Maximum amount of memory that the proxy can use + limit: "" + # -- Maximum amount of memory that the proxy requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the proxy can use + limit: "" + # -- Amount of ephemeral storage that the proxy requests + request: "" + # -- User id under which the proxy runs + uid: 2102 + # -- (int) Optional customisation of the group id under which the proxy runs (the group ID will be omitted if lower than 0) + gid: -1 + + # -- If set the injected proxy sidecars in the data plane will stay alive for + # at least the given period before receiving the SIGTERM signal from + # Kubernetes but no longer than the pod's `terminationGracePeriodSeconds`. + # See [Lifecycle + # hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) + # for more info on container lifecycle hooks. + waitBeforeExitSeconds: 0 + # -- If set, the application container will not start until the proxy is + # ready + await: true + requireIdentityOnInboundPorts: "" + # -- Default set of opaque ports + # - SMTP (25,587) server-first + # - MYSQL (3306) server-first + # - Galera (4444) server-first + # - PostgreSQL (5432) server-first + # - Redis (6379) server-first + # - ElasticSearch (9300) server-first + # - Memcached (11211) clients do not issue any preamble, which breaks detection + opaquePorts: "25,587,3306,4444,5432,6379,9300,11211" + # -- Grace period for graceful proxy shutdowns. If this timeout elapses before all open connections have completed, the proxy will terminate forcefully, closing any remaining connections. + shutdownGracePeriod: "" + # -- The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", + # "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny", "audit" + # @default -- "all-unauthenticated" + defaultInboundPolicy: "all-unauthenticated" + # -- Enable KEP-753 native sidecars + # This is an experimental feature. It requires Kubernetes >= 1.29. + # If enabled, .proxy.waitBeforeExitSeconds should not be used. + nativeSidecar: false + # -- Native sidecar proxy startup probe parameters. + # -- LivenessProbe timeout and delay configuration + livenessProbe: + initialDelaySeconds: 10 + timeoutSeconds: 1 + # -- ReadinessProbe timeout and delay configuration + readinessProbe: + initialDelaySeconds: 2 + timeoutSeconds: 1 + startupProbe: + initialDelaySeconds: 0 + periodSeconds: 1 + failureThreshold: 120 + # Configures general properties of the proxy's control plane clients. + control: + # Configures limits on API response streams. + streams: + # -- The timeout for the first update from the control plane. + initialTimeout: "3s" + # -- The timeout between consecutive updates from the control plane. + idleTimeout: "5m" + # -- The maximum duration for a response stream (i.e. before it will be + # reinitialized). + lifetime: "1h" + inbound: + server: + http2: + # -- The interval at which PINGs are issued to remote HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on inbound HTTP/2 connections. + keepAliveTimeout: "3s" + outbound: + server: + http2: + # -- The interval at which PINGs are issued to local application HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on outbound HTTP/2 connections. + keepAliveTimeout: "3s" + +# proxy-init configuration +proxyInit: + # -- Variant of iptables that will be used to configure routing. Currently, + # proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will + # control which utility binary will be called. The host must support + # whichever mode will be used + iptablesMode: "legacy" + # -- Default set of inbound ports to skip via iptables + # - Galera (4567,4568) + ignoreInboundPorts: "4567,4568" + # -- Default set of outbound ports to skip via iptables + # - Galera (4567,4568) + ignoreOutboundPorts: "4567,4568" + # -- Default set of ports to skip via iptables for control plane + # components so they can communicate with the Kubernetes API Server + kubeAPIServerPorts: "443,6443" + # -- Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy + skipSubnets: "" + # -- Log level for the proxy-init + # @default -- info + logLevel: "" + # -- Log format (`plain` or `json`) for the proxy-init + # @default -- plain + logFormat: "" + image: + # -- Docker image for the proxy-init container + name: cr.l5d.io/linkerd/proxy-init + # -- Pull policy for the proxy-init container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy-init container image + version: v2.4.1 + closeWaitTimeoutSecs: 0 + # -- Privileged mode allows the container processes to inherit all security + # capabilities and bypass any security limitations enforced by the kubelet. + # When used with 'runAsRoot: true', the container will behave exactly as if + # it was running as root on the host. May escape cgroup limits and see other + # processes and devices on the host. + # @default -- false + privileged: false + # -- Allow overriding the runAsNonRoot behaviour () + runAsRoot: false + # -- This value is used only if runAsRoot is false; otherwise runAsUser will be 0 + runAsUser: 65534 + # -- This value is used only if runAsRoot is false; otherwise runAsGroup will be 0 + runAsGroup: 65534 + xtMountPath: + mountPath: /run + name: linkerd-proxy-init-xtables-lock + +# network validator configuration +# This runs on a host that uses iptables to reroute network traffic. The validator +# ensures that iptables is correctly routing requests before we start linkerd. +networkValidator: + # -- Log level for the network-validator + # @default -- debug + logLevel: debug + # -- Log format (`plain` or `json`) for network-validator + # @default -- plain + logFormat: plain + # -- Address to which the network-validator will attempt to connect. This should be an IP + # that the cluster is expected to be able to reach but a port it should not, e.g., a public IP + # for public clusters and a private IP for air-gapped clusters with a port like 20001. + # If empty, defaults to 1.1.1.1:20001 and [fd00::1]:20001 for IPv4 and IPv6 respectively. + connectAddr: "" + # -- Address to which network-validator listens to requests from itself. + # If empty, defaults to 0.0.0.0:4140 and [::]:4140 for IPv4 and IPv6 respectively. + listenAddr: "" + # -- Timeout before network-validator fails to validate the pod's network connectivity + timeout: "10s" + # -- Include a securityContext in the network-validator pod spec + enableSecurityContext: true + +# -- For Private docker registries, authentication is needed. +# Registry secrets are applied to the respective service accounts +imagePullSecrets: [] +# - name: my-private-docker-registry-login-secret + +# -- Allow proxies to perform transparent HTTP/2 upgrading +enableH2Upgrade: true + +# -- Add a PSP resource and bind it to the control plane ServiceAccounts. Note +# PSP has been deprecated since k8s v1.21 +enablePSP: false + +# -- Failure policy for the proxy injector +webhookFailurePolicy: Ignore + +# controllerImage -- Docker image for the destination and identity components +controllerImage: cr.l5d.io/linkerd/controller +# -- Optionally allow a specific container image Tag (or SHA) to be specified for the controllerImage. +controllerImageVersion: "" + +# -- Number of replicas for each control plane pod +controllerReplicas: 1 +# -- User ID for the control plane components +controllerUID: 2103 +# -- (int) Optional customisation of the group ID for the control plane components (the group ID will be omitted if lower than 0) +controllerGID: -1 + +# destination configuration +# set resources for the sp-validator and its linkerd proxy respectively +# see proxy.resources for details. +# destinationResources -- CPU, Memory and Ephemeral Storage resources required by destination (see `proxy.resources` for sub-fields) +#destinationResources: +# destinationProxyResources -- CPU, Memory and Ephemeral Storage resources required by proxy injected into destination pod (see `proxy.resources` for sub-fields) +#destinationProxyResources: + +destinationController: + meshedHttp2ClientProtobuf: + keep_alive: + interval: + seconds: 10 + timeout: + seconds: 3 + while_idle: true + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# debug configuration +debugContainer: + image: + # -- Docker image for the debug container + name: cr.l5d.io/linkerd/debug + # -- Pull policy for the debug container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the debug container image + # @default -- linkerdVersion + version: "" + +identity: + # -- If the linkerd-identity-trust-roots ConfigMap has already been created + externalCA: false + + # -- Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token + serviceAccountTokenProjection: true + + issuer: + scheme: linkerd.io/tls + + # -- Amount of time to allow for clock skew within a Linkerd cluster + clockSkewAllowance: 20s + + # -- Amount of time for which the Identity issuer should certify identity + issuanceLifetime: 24h0m0s + + # -- Which scheme is used for the identity issuer secret format + tls: + # -- Issuer certificate (ECDSA). It must be provided during install. + crtPEM: | + + # -- Key for the issuer certificate (ECDSA). It must be provided during + # install + keyPEM: | + + kubeAPI: *kubeapi + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the identity controller (see `proxy.resources` for sub-fields) +#identityResources: +# -|- CPU, Memory and Ephemeral Storage resources required by proxy injected into identity pod (see `proxy.resources` for sub-fields) +#identityProxyResources: + +# heartbeat configuration +# disableHeartBeat -- Set to true to not start the heartbeat cronjob +disableHeartBeat: false +# -- Config for the heartbeat cronjob +# heartbeatSchedule: "0 0 * * *" + +# proxy injector configuration +proxyInjector: + # -- Timeout in seconds before the API Server cancels a request to the proxy + # injector. If timeout is exceeded, the webhookfailurePolicy is used. + timeoutSeconds: 10 + # -- Do not create a secret resource for the proxyInjector webhook. + # If this is set to `true`, the value `proxyInjector.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook. + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + - key: kubernetes.io/metadata.name + operator: NotIn + values: + - kube-system + - cert-manager + + # -- Object selector used by admission webhook. + objectSelector: + matchExpressions: + - key: linkerd.io/control-plane-component + operator: DoesNotExist + - key: linkerd.io/cni-resource + operator: DoesNotExist + + # -- Certificate for the proxy injector. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the proxy injector. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `proxyInjector.crtPEM`. + # If `proxyInjector.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the proxy injector (see +#`proxy.resources` for sub-fields) +#proxyInjectorResources: +#-|- CPU, Memory and Ephemeral Storage resources required by proxy injected into the proxy injector +#pod (see `proxy.resources` for sub-fields) +#proxyInjectorProxyResources: + +# service profile validator configuration +profileValidator: + # -- Do not create a secret resource for the profileValidator webhook. + # If this is set to `true`, the value `proxyInjector.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `proxyInjector.injectCaFrom` or `proxyInjector.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + + # -- Certificate for the service profile validator. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the service profile validator. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `profileValidator.crtPEM`. + # If `profileValidator.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + +# policy validator configuration +policyValidator: + # -- Do not create a secret resource for the policyValidator webhook. + # If this is set to `true`, the value `policyValidator.caBundle` must be set + # or the ca bundle must injected with cert-manager ca injector using + # `policyValidator.injectCaFrom` or `policyValidator.injectCaFromSecret` (see below). + externalSecret: false + + # -- Namespace selector used by admission webhook + namespaceSelector: + matchExpressions: + - key: config.linkerd.io/admission-webhooks + operator: NotIn + values: + - disabled + + # -- Certificate for the policy validator. If not provided and not using an external secret + # then Helm will generate one. + crtPEM: | + + # -- Certificate key for the policy validator. If not provided and not using an external secret + # then Helm will generate one. + keyPEM: | + + # -- Bundle of CA certificates for proxy injector. + # If not provided nor injected with cert-manager, + # then Helm will use the certificate generated for `policyValidator.crtPEM`. + # If `policyValidator.externalSecret` is set to true, this value, injectCaFrom, or + # injectCaFromSecret must be set, as no certificate will be generated. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector) for more information. + caBundle: | + + # -- Inject the CA bundle from a cert-manager Certificate. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-certificate-resource) + # for more information. + injectCaFrom: "" + + # -- Inject the CA bundle from a Secret. + # If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. + # The Secret must have the CA Bundle stored in the `ca.crt` key and have + # the `cert-manager.io/allow-direct-injection` annotation set to `true`. + # See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) + # for more information. + injectCaFromSecret: "" + +# -- NodeSelector section, See the [K8S +# documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) +# for more information +nodeSelector: + kubernetes.io/os: linux + +# -- SP validator configuration +spValidator: + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# -|- CPU, Memory and Ephemeral Storage resources required by the SP validator (see +#`proxy.resources` for sub-fields) +#spValidatorResources: + +# -|- Tolerations section, See the +# [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) +# for more information +#tolerations: + +# -|- NodeAffinity section, See the +# [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) +# for more information +#nodeAffinity: + +# -- url of external prometheus instance (used for the heartbeat) +prometheusUrl: "" + +# Prometheus Operator PodMonitor configuration +podMonitor: + # -- Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) + enabled: false + # -- Interval at which metrics should be scraped + scrapeInterval: 10s + # -- Iimeout after which the scrape is ended + scrapeTimeout: 10s + # -- Labels to apply to all pod Monitors + labels: {} + controller: + # -- Enables the creation of PodMonitor for the control-plane + enabled: true + # -- Selector to select which namespaces the Endpoints objects are discovered from + namespaceSelector: | + matchNames: + - {{ .Release.Namespace }} + - linkerd-viz + - linkerd-jaeger + serviceMirror: + # -- Enables the creation of PodMonitor for the Service Mirror component + enabled: true + proxy: + # -- Enables the creation of PodMonitor for the data-plane + enabled: true diff --git a/charts/linkerd/linkerd-crds/2024.10.3/.helmignore b/charts/linkerd/linkerd-crds/2024.10.3/.helmignore new file mode 100644 index 0000000000..79c90a8063 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/.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/linkerd/linkerd-crds/2024.10.3/Chart.lock b/charts/linkerd/linkerd-crds/2024.10.3/Chart.lock new file mode 100644 index 0000000000..a62a030631 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/Chart.yaml b/charts/linkerd/linkerd-crds/2024.10.3/Chart.yaml new file mode 100644 index 0000000000..572ecaf583 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd CRDs + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-crds +apiVersion: v2 +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' +home: https://linkerd.io +icon: file://assets/icons/linkerd-crds.png +keywords: +- service-mesh +kubeVersion: '>=1.22.0-0' +maintainers: +- email: cncf-linkerd-dev@lists.cncf.io + name: Linkerd authors + url: https://linkerd.io/ +name: linkerd-crds +sources: +- https://github.com/linkerd/linkerd2/ +type: application +version: 2024.10.3 diff --git a/charts/linkerd/linkerd-crds/2024.10.3/README.md b/charts/linkerd/linkerd-crds/2024.10.3/README.md new file mode 100644 index 0000000000..ebf31b6f3f --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/README.md @@ -0,0 +1,71 @@ +# linkerd-crds + +Linkerd gives you observability, reliability, and security +for your microservices — with no code change required. + +![Version: 2024.10.3](https://img.shields.io/badge/Version-2024.10.3-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` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-crds/2024.10.3/README.md.gotmpl b/charts/linkerd/linkerd-crds/2024.10.3/README.md.gotmpl new file mode 100644 index 0000000000..88be739549 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/app-readme.md b/charts/linkerd/linkerd-crds/2024.10.3/app-readme.md new file mode 100644 index 0000000000..59010a6b21 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/.helmignore b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/Chart.yaml b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/Chart.yaml new file mode 100644 index 0000000000..23cfc167e3 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md new file mode 100644 index 0000000000..10805c9b94 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md.gotmpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/README.md.gotmpl new file mode 100644 index 0000000000..37f5101061 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/NOTES.txt b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/NOTES.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_affinity.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_affinity.tpl new file mode 100644 index 0000000000..5dde1da473 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_capabilities.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_capabilities.tpl new file mode 100644 index 0000000000..a595d74c1f --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_debug.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_debug.tpl new file mode 100644 index 0000000000..4df8cc77bc --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_helpers.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_helpers.tpl new file mode 100644 index 0000000000..b6cdc34d08 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_metadata.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_metadata.tpl new file mode 100644 index 0000000000..04d2f1beab --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_network-validator.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_network-validator.tpl new file mode 100644 index 0000000000..276056395f --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_nodeselector.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 0000000000..4cde0ab16e --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 0000000000..9651b3bd1a --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-init.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 0000000000..a307b14073 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy-init.tpl @@ -0,0 +1,98 @@ +{{- define "partials.proxy-init" -}} +args: +{{- if (.Values.proxyInit.iptablesMode | default "legacy" | eq "nft") }} +- --firewall-bin-path +- "iptables-nft" +- --firewall-save-bin-path +- "iptables-nft-save" +{{- else if not (eq .Values.proxyInit.iptablesMode "legacy") }} +{{ fail (printf "Unsupported value \"%s\" for proxyInit.iptablesMode\nValid values: [\"nft\", \"legacy\"]" .Values.proxyInit.iptablesMode) }} +{{end -}} +{{- if .Values.disableIPv6 }} +- --ipv6=false +{{- end }} +- --incoming-proxy-port +- {{.Values.proxy.ports.inbound | quote}} +- --outgoing-proxy-port +- {{.Values.proxy.ports.outbound | quote}} +- --proxy-uid +- {{.Values.proxy.uid | quote}} +{{- if ge (int .Values.proxy.gid) 0 }} +- --proxy-gid +- {{.Values.proxy.gid | quote}} +{{- end }} +- --inbound-ports-to-ignore +- "{{.Values.proxy.ports.control}},{{.Values.proxy.ports.admin}}{{ternary (printf ",%s" (.Values.proxyInit.ignoreInboundPorts | toString)) "" (not (empty .Values.proxyInit.ignoreInboundPorts)) }}" +{{- if .Values.proxyInit.ignoreOutboundPorts }} +- --outbound-ports-to-ignore +- {{.Values.proxyInit.ignoreOutboundPorts | quote}} +{{- end }} +{{- if .Values.proxyInit.closeWaitTimeoutSecs }} +- --timeout-close-wait-secs +- {{ .Values.proxyInit.closeWaitTimeoutSecs | quote}} +{{- end }} +{{- if .Values.proxyInit.logFormat }} +- --log-format +- {{ .Values.proxyInit.logFormat }} +{{- end }} +{{- if .Values.proxyInit.logLevel }} +- --log-level +- {{ .Values.proxyInit.logLevel }} +{{- end }} +{{- if .Values.proxyInit.skipSubnets }} +- --subnets-to-ignore +- {{ .Values.proxyInit.skipSubnets | quote }} +{{- end }} +image: {{.Values.proxyInit.image.name}}:{{.Values.proxyInit.image.version}} +imagePullPolicy: {{.Values.proxyInit.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-init +{{ include "partials.resources" .Values.proxy.resources }} +securityContext: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + capabilities: + add: + - NET_ADMIN + - NET_RAW + {{- if .Values.proxyInit.capabilities -}} + {{- if .Values.proxyInit.capabilities.add }} + {{- toYaml .Values.proxyInit.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxyInit.capabilities.drop -}} + {{- include "partials.proxy-init.capabilities.drop" . | nindent 4 -}} + {{- end }} + {{- end }} + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.privileged }} + privileged: true + {{- else }} + privileged: false + {{- end }} + {{- if .Values.proxyInit.runAsRoot }} + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + {{- else }} + runAsNonRoot: true + runAsUser: {{ .Values.proxyInit.runAsUser | int | eq 0 | ternary 65534 .Values.proxyInit.runAsUser }} + runAsGroup: {{ .Values.proxyInit.runAsGroup | int | eq 0 | ternary 65534 .Values.proxyInit.runAsGroup }} + {{- end }} + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if or (not .Values.cniEnabled) .Values.proxyInit.saMountPath }} +volumeMounts: +{{- end -}} +{{- if not .Values.cniEnabled }} +- mountPath: {{.Values.proxyInit.xtMountPath.mountPath}} + name: {{.Values.proxyInit.xtMountPath.name}} +{{- end -}} +{{- if .Values.proxyInit.saMountPath }} +- mountPath: {{.Values.proxyInit.saMountPath.mountPath}} + name: {{.Values.proxyInit.saMountPath.name}} + readOnly: {{.Values.proxyInit.saMountPath.readOnly}} +{{- end -}} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy.tpl new file mode 100644 index 0000000000..4dcf12dee2 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_proxy.tpl @@ -0,0 +1,271 @@ +{{ define "partials.proxy" -}} +{{ if and .Values.proxy.nativeSidecar .Values.proxy.waitBeforeExitSeconds }} +{{ fail "proxy.nativeSidecar and waitBeforeExitSeconds cannot be used simultaneously" }} +{{- end }} +{{- if not (has .Values.proxy.logHTTPHeaders (list "insecure" "off" "")) }} +{{- fail "logHTTPHeaders must be one of: insecure | off" }} +{{- end }} +{{- $trustDomain := (.Values.identityTrustDomain | default .Values.clusterDomain) -}} +env: +- name: _pod_name + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: _pod_ns + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: _pod_nodeName + valueFrom: + fieldRef: + fieldPath: spec.nodeName +{{- if .Values.proxy.cores }} +- name: LINKERD2_PROXY_CORES + value: {{.Values.proxy.cores | quote}} +{{- end }} +{{ if .Values.proxy.requireIdentityOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_IDENTITY + value: {{.Values.proxy.requireIdentityOnInboundPorts | quote}} +{{ end -}} +{{ if .Values.proxy.requireTLSOnInboundPorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_REQUIRE_TLS + value: {{.Values.proxy.requireTLSOnInboundPorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_SHUTDOWN_ENDPOINT_ENABLED + value: {{.Values.proxy.enableShutdownEndpoint | quote}} +- name: LINKERD2_PROXY_LOG + value: "{{.Values.proxy.logLevel}}{{ if not (eq .Values.proxy.logHTTPHeaders "insecure") }},[{headers}]=off,[{request}]=off{{ end }}" +- name: LINKERD2_PROXY_LOG_FORMAT + value: {{.Values.proxy.logFormat | quote}} +- name: LINKERD2_PROXY_DESTINATION_SVC_ADDR + value: {{ternary "localhost.:8086" (printf "linkerd-dst-headless.%s.svc.%s.:8086" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_POLICY_SVC_ADDR + value: {{ternary "localhost.:8090" (printf "linkerd-policy.%s.svc.%s.:8090" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-destination")}} +- name: LINKERD2_PROXY_POLICY_WORKLOAD + value: | + {"ns":"$(_pod_ns)", "pod":"$(_pod_name)"} +- name: LINKERD2_PROXY_INBOUND_DEFAULT_POLICY + value: {{.Values.proxy.defaultInboundPolicy}} +- name: LINKERD2_PROXY_POLICY_CLUSTER_NETWORKS + value: {{.Values.clusterNetworks | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_INITIAL_TIMEOUT + value: {{((.Values.proxy.control).streams).initialTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_IDLE_TIMEOUT + value: {{((.Values.proxy.control).streams).idleTimeout | default "" | quote}} +- name: LINKERD2_PROXY_CONTROL_STREAM_LIFETIME + value: {{((.Values.proxy.control).streams).lifetime | default "" | quote}} +{{ if .Values.proxy.inboundConnectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.inboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundConnectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_TIMEOUT + value: {{.Values.proxy.outboundConnectTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.outboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.outboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.inboundDiscoveryCacheUnusedTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DISCOVERY_IDLE_TIMEOUT + value: {{.Values.proxy.inboundDiscoveryCacheUnusedTimeout | quote}} +{{ end -}} +{{ if .Values.proxy.disableOutboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_OUTBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +{{ if .Values.proxy.disableInboundProtocolDetectTimeout -}} +- name: LINKERD2_PROXY_INBOUND_DETECT_TIMEOUT + value: "365d" +{{ end -}} +- name: LINKERD2_PROXY_CONTROL_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.control}}" +- name: LINKERD2_PROXY_ADMIN_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.admin}}" +{{- /* Deprecated, superseded by LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS since proxy's v2.228.0 (deployed since edge-24.4.5) */}} +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDR + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}" +- name: LINKERD2_PROXY_OUTBOUND_LISTEN_ADDRS + value: "127.0.0.1:{{.Values.proxy.ports.outbound}}{{ if not .Values.disableIPv6}},[::1]:{{.Values.proxy.ports.outbound}}{{ end }}" +- name: LINKERD2_PROXY_INBOUND_LISTEN_ADDR + value: "{{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:{{.Values.proxy.ports.inbound}}" +- name: LINKERD2_PROXY_INBOUND_IPS + valueFrom: + fieldRef: + fieldPath: status.podIPs +- name: LINKERD2_PROXY_INBOUND_PORTS + value: {{ .Values.proxy.podInboundPorts | quote }} +{{ if .Values.proxy.isGateway -}} +- name: LINKERD2_PROXY_INBOUND_GATEWAY_SUFFIXES + value: {{printf "svc.%s." .Values.clusterDomain}} +{{ end -}} +{{ if .Values.proxy.isIngress -}} +- name: LINKERD2_PROXY_INGRESS_MODE + value: "true" +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES + {{- $internalDomain := printf "svc.%s." .Values.clusterDomain }} + value: {{ternary "." $internalDomain .Values.proxy.enableExternalProfiles}} +- name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE + value: 10000ms +- name: LINKERD2_PROXY_INBOUND_ACCEPT_USER_TIMEOUT + value: 30s +- name: LINKERD2_PROXY_OUTBOUND_CONNECT_USER_TIMEOUT + value: 30s +{{- /* Configure inbound and outbound parameters, e.g. for HTTP/2 servers. */}} +{{ range $proxyK, $proxyV := (dict "inbound" .Values.proxy.inbound "outbound" .Values.proxy.outbound) -}} +{{ range $scopeK, $scopeV := $proxyV -}} +{{ range $protoK, $protoV := $scopeV -}} +{{ range $paramK, $paramV := $protoV -}} +- name: LINKERD2_PROXY_{{snakecase $proxyK | upper}}_{{snakecase $scopeK | upper}}_{{snakecase $protoK | upper}}_{{snakecase $paramK | upper}} + value: {{ quote $paramV }} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ if .Values.proxy.opaquePorts -}} +- name: LINKERD2_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION + value: {{.Values.proxy.opaquePorts | quote}} +{{ end -}} +- name: LINKERD2_PROXY_DESTINATION_CONTEXT + value: | + {"ns":"$(_pod_ns)", "nodeName":"$(_pod_nodeName)", "pod":"$(_pod_name)"} +- name: _pod_sa + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName +- name: _l5d_ns + value: {{.Release.Namespace}} +- name: _l5d_trustdomain + value: {{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_DIR + value: /var/run/linkerd/identity/end-entity +- name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS +{{- /* +Pods in the `linkerd` namespace are not injected by the proxy injector and instead obtain +the trust anchor bundle from the `linkerd-identity-trust-roots` configmap. This should not +be used in other contexts. +*/}} +{{- if .Values.proxy.loadTrustBundleFromConfigMap }} + valueFrom: + configMapKeyRef: + name: linkerd-identity-trust-roots + key: ca-bundle.crt +{{ else }} + value: | + {{- required "Please provide the identity trust anchors" .Values.identityTrustAnchorsPEM | trim | nindent 4 }} +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_TOKEN_FILE +{{- if .Values.identity.serviceAccountTokenProjection }} + value: /var/run/secrets/tokens/linkerd-identity-token +{{ else }} + value: /var/run/secrets/kubernetes.io/serviceaccount/token +{{ end -}} +- name: LINKERD2_PROXY_IDENTITY_SVC_ADDR + value: {{ternary "localhost.:8080" (printf "linkerd-identity-headless.%s.svc.%s.:8080" .Release.Namespace .Values.clusterDomain) (eq (toString .Values.proxy.component) "linkerd-identity")}} +- name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME + value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_IDENTITY_SVC_NAME + value: linkerd-identity.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_DESTINATION_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +- name: LINKERD2_PROXY_POLICY_SVC_NAME + value: linkerd-destination.{{.Release.Namespace}}.serviceaccount.identity.{{.Release.Namespace}}.{{$trustDomain}} +{{ if .Values.proxy.accessLog -}} +- name: LINKERD2_PROXY_ACCESS_LOG + value: {{.Values.proxy.accessLog | quote}} +{{ end -}} +{{ if .Values.proxy.shutdownGracePeriod -}} +- name: LINKERD2_PROXY_SHUTDOWN_GRACE_PERIOD + value: {{.Values.proxy.shutdownGracePeriod | quote}} +{{ end -}} +{{ if .Values.proxy.additionalEnv -}} +{{ toYaml .Values.proxy.additionalEnv }} +{{ end -}} +{{ if .Values.proxy.experimentalEnv -}} +{{ toYaml .Values.proxy.experimentalEnv }} +{{ end -}} +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +livenessProbe: + httpGet: + path: /live + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.livenessProbe.timeoutSeconds }} +name: linkerd-proxy +ports: +- containerPort: {{.Values.proxy.ports.inbound}} + name: linkerd-proxy +- containerPort: {{.Values.proxy.ports.admin}} + name: linkerd-admin +readinessProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{.Values.proxy.readinessProbe.timeoutSeconds }} +{{- if and .Values.proxy.nativeSidecar .Values.proxy.await }} +startupProbe: + httpGet: + path: /ready + port: {{.Values.proxy.ports.admin}} + initialDelaySeconds: {{.Values.proxy.startupProbe.initialDelaySeconds}} + periodSeconds: {{.Values.proxy.startupProbe.periodSeconds}} + failureThreshold: {{.Values.proxy.startupProbe.failureThreshold}} +{{- end }} +{{- if .Values.proxy.resources }} +{{ include "partials.resources" .Values.proxy.resources }} +{{- end }} +securityContext: + allowPrivilegeEscalation: false + {{- if .Values.proxy.capabilities -}} + {{- include "partials.proxy.capabilities" . | nindent 2 -}} + {{- end }} + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: {{.Values.proxy.uid}} +{{- if ge (int .Values.proxy.gid) 0 }} + runAsGroup: {{.Values.proxy.gid}} +{{- end }} + seccompProfile: + type: RuntimeDefault +terminationMessagePolicy: FallbackToLogsOnError +{{- if and (not .Values.proxy.nativeSidecar) (or .Values.proxy.await .Values.proxy.waitBeforeExitSeconds) }} +lifecycle: +{{- if .Values.proxy.await }} + postStart: + exec: + command: + - /usr/lib/linkerd/linkerd-await + - --timeout=2m + - --port={{.Values.proxy.ports.admin}} +{{- end }} +{{- if .Values.proxy.waitBeforeExitSeconds }} + preStop: + exec: + command: + - /bin/sleep + - {{.Values.proxy.waitBeforeExitSeconds | quote}} +{{- end }} +{{- end }} +volumeMounts: +- mountPath: /var/run/linkerd/identity/end-entity + name: linkerd-identity-end-entity +{{- if .Values.identity.serviceAccountTokenProjection }} +- mountPath: /var/run/secrets/tokens + name: linkerd-identity-token +{{- end }} +{{- if .Values.proxy.saMountPath }} +- mountPath: {{.Values.proxy.saMountPath.mountPath}} + name: {{.Values.proxy.saMountPath.name}} + readOnly: {{.Values.proxy.saMountPath.readOnly}} +{{- end -}} +{{- if .Values.proxy.nativeSidecar }} +restartPolicy: Always +{{- end -}} +{{- end }} diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_pull-secrets.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 0000000000..0c9aa4f01c --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_resources.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_resources.tpl new file mode 100644 index 0000000000..1fd6789fd7 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_tolerations.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_tolerations.tpl new file mode 100644 index 0000000000..c2292b1464 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_trace.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_trace.tpl new file mode 100644 index 0000000000..dee059541f --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_validate.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_validate.tpl new file mode 100644 index 0000000000..ba772c2fee --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_volumes.tpl b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_volumes.tpl new file mode 100644 index 0000000000..9684cf2409 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/templates/_volumes.tpl @@ -0,0 +1,20 @@ +{{ 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 -}} diff --git a/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/values.yaml b/charts/linkerd/linkerd-crds/2024.10.3/charts/partials/values.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/linkerd/linkerd-crds/2024.10.3/templates/NOTES.txt b/charts/linkerd/linkerd-crds/2024.10.3/templates/NOTES.txt new file mode 100644 index 0000000000..4ff5c1818a --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_grpcroutes.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_grpcroutes.yaml new file mode 100644 index 0000000000..0050aac88b --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_httproutes.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/gateway.networking.k8s.io_httproutes.yaml new file mode 100644 index 0000000000..b695c51d50 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/authorization-policy.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/authorization-policy.yaml new file mode 100644 index 0000000000..7d86520e2e --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/httproute.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/httproute.yaml new file mode 100644 index 0000000000..6d2e8b07ef --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/meshtls-authentication.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/meshtls-authentication.yaml new file mode 100644 index 0000000000..58ee815f59 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/network-authentication.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/network-authentication.yaml new file mode 100644 index 0000000000..cef15d3c40 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/server-authorization.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/server-authorization.yaml new file mode 100644 index 0000000000..33fb659002 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/policy/server.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/policy/server.yaml new file mode 100644 index 0000000000..0af41224a0 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/v1alpha1 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/linkerd/linkerd-crds/2024.10.3/templates/serviceprofile.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/serviceprofile.yaml new file mode 100644 index 0000000000..ad12c96a3a --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/templates/workload/external-workload.yaml b/charts/linkerd/linkerd-crds/2024.10.3/templates/workload/external-workload.yaml new file mode 100644 index 0000000000..2e6e43ae60 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/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/linkerd/linkerd-crds/2024.10.3/values.yaml b/charts/linkerd/linkerd-crds/2024.10.3/values.yaml new file mode 100644 index 0000000000..362145168d --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.10.3/values.yaml @@ -0,0 +1 @@ +enableHttpRoutes: true diff --git a/charts/speedscale/speedscale-operator/2.2.556/.helmignore b/charts/speedscale/speedscale-operator/2.2.556/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/speedscale/speedscale-operator/2.2.556/Chart.yaml b/charts/speedscale/speedscale-operator/2.2.556/Chart.yaml new file mode 100644 index 0000000000..55f74c1d61 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/Chart.yaml @@ -0,0 +1,27 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator +apiVersion: v1 +appVersion: 2.2.556 +description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. +home: https://speedscale.com +icon: file://assets/icons/speedscale-operator.png +keywords: +- speedscale +- test +- testing +- regression +- reliability +- load +- replay +- network +- traffic +kubeVersion: '>= 1.17.0-0' +maintainers: +- email: support@speedscale.com + name: Speedscale Support +name: speedscale-operator +version: 2.2.556 diff --git a/charts/speedscale/speedscale-operator/2.2.556/LICENSE b/charts/speedscale/speedscale-operator/2.2.556/LICENSE new file mode 100644 index 0000000000..b78723d62f --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Speedscale + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/speedscale/speedscale-operator/2.2.556/README.md b/charts/speedscale/speedscale-operator/2.2.556/README.md new file mode 100644 index 0000000000..6ca25eed9d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/README.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.2.556/app-readme.md b/charts/speedscale/speedscale-operator/2.2.556/app-readme.md new file mode 100644 index 0000000000..6ca25eed9d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/app-readme.md @@ -0,0 +1,111 @@ +![GitHub Tag](https://img.shields.io/github/v/tag/speedscale/operator-helm) + + +# Speedscale Operator + +The [Speedscale](https://www.speedscale.com) Operator is a [Kubernetes operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) +that watches for deployments to be applied to the cluster and takes action based on annotations. The operator +can inject a proxy to capture traffic into or out of applications, or setup an isolation test environment around +a deployment for testing. The operator itself is a deployment that will be always present on the cluster once +the helm chart is installed. + +## Prerequisites + +- Kubernetes 1.20+ +- Helm 3+ +- Appropriate [network and firewall configuration](https://docs.speedscale.com/reference/networking) for Speedscale cloud and webhook traffic + +## Get Repo Info + +```bash +helm repo add speedscale https://speedscale.github.io/operator-helm/ +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +An API key is required. Sign up for a [free Speedscale trial](https://speedscale.com/free-trial/) if you do not have one. + +```bash +helm install speedscale-operator speedscale/speedscale-operator \ + -n speedscale \ + --create-namespace \ + --set apiKey= \ + --set clusterName= +``` + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +### Pre-install job failure + +We use pre-install job to check provided API key and provision some of the required resources. + +If the job failed during the installation, you'll see the following error during install: + +``` +Error: INSTALLATION FAILED: failed pre-install: job failed: BackoffLimitExceeded +``` + +You can inspect the logs using this command: + +```bash +kubectl -n speedscale logs job/speedscale-operator-pre-install +``` + +After fixing the error, uninstall the helm release, delete the failed job +and try installing again: + +```bash +helm -n speedscale uninstall speedscale-operator +kubectl -n speedscale delete job speedscale-operator-pre-install +``` + +## Uninstall Chart + +```bash +helm -n speedscale uninstall speedscale-operator +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +CRDs created by this chart are not removed by default and should be manually cleaned up: + +```bash +kubectl delete crd trafficreplays.speedscale.com +``` + +## Upgrading Chart + +```bash +helm repo update +helm -n speedscale upgrade speedscale-operator speedscale/speedscale-operator +``` + +Resources capturing traffic will need to be rolled to pick up the latest +Speedscale sidecar. Use the rollout restart command for each namespace and +resource type: + +```bash +kubectl -n rollout restart deployment +``` + +With Helm v3, CRDs created by this chart are not updated by default +and should be manually updated. +Consult also the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading an existing Release to a new version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + + +## Help + +Speedscale docs information available at [docs.speedscale.com](https://docs.speedscale.com) or join us +on the [Speedscale community Slack](https://join.slack.com/t/speedscalecommunity/shared_invite/zt-x5rcrzn4-XHG1QqcHNXIM~4yozRrz8A)! diff --git a/charts/speedscale/speedscale-operator/2.2.556/questions.yaml b/charts/speedscale/speedscale-operator/2.2.556/questions.yaml new file mode 100644 index 0000000000..29aee38958 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/questions.yaml @@ -0,0 +1,9 @@ +questions: +- variable: apiKey + default: "fffffffffffffffffffffffffffffffffffffffffffff" + description: "An API key is required to connect to the Speedscale cloud." + required: true + type: string + label: API Key + group: Authentication + diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/NOTES.txt b/charts/speedscale/speedscale-operator/2.2.556/templates/NOTES.txt new file mode 100644 index 0000000000..cabb59b175 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/NOTES.txt @@ -0,0 +1,12 @@ +Thank you for installing the Speedscale Operator! + +Next you'll need to add the Speedscale Proxy Sidecar to your deployments. +See https://docs.speedscale.com/setup/sidecar/install/ + +If upgrading use the rollout restart command for each namespace and resource +type to ensure Speedscale sidecars are updated: + + kubectl -n rollout restart deployment + +Once your deployment is running the sidecar your service will show up on +https://app.speedscale.com/. diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/admission.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/admission.yaml new file mode 100644 index 0000000000..301748a61d --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/admission.yaml @@ -0,0 +1,209 @@ +{{- $cacrt := "" -}} +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-webhook-certs") -}} +{{- if $s -}} +{{- $cacrt = index $s.data "ca.crt" | default (index $s.data "tls.crt") | b64dec -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $altNames := list ( printf "speedscale-operator.%s" .Release.Namespace ) ( printf "speedscale-operator.%s.svc" .Release.Namespace ) -}} +{{- $ca := genCA "speedscale-operator" 3650 -}} +{{- $cert := genSignedCert "speedscale-operator" nil $altNames 3650 $ca -}} +{{- $cacrt = $ca.Cert -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate + failurePolicy: Ignore + name: sidecar.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + reinvocationPolicy: IfNeeded + rules: + - apiGroups: + - apps + - batch + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - deployments + - statefulsets + - daemonsets + - jobs + - replicasets + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - pods + - apiGroups: + - argoproj.io + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + - DELETE + resources: + - rollouts + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /mutate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: speedscale-operator-replay + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ $cacrt | b64enc }} + service: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + path: /validate-speedscale-com-v1-trafficreplay + failurePolicy: Fail + name: replay.speedscale.com + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: "NotIn" + values: + - kube-system + - kube-node-lease + {{- if .Values.namespaceSelector }} + - key: kubernetes.io/metadata.name + operator: "In" + values: + {{- range .Values.namespaceSelector }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + - apiGroups: + - speedscale.com + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - trafficreplays + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-webhook-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + ca.crt: {{ $cacrt | b64enc }} + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/configmap.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/configmap.yaml new file mode 100644 index 0000000000..04dfda91aa --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/configmap.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +data: + CLUSTER_NAME: {{ .Values.clusterName }} + IMAGE_PULL_POLICY: {{ .Values.image.pullPolicy }} + IMAGE_PULL_SECRETS: "" + IMAGE_REGISTRY: {{ .Values.image.registry }} + IMAGE_TAG: {{ .Values.image.tag }} + INSTANCE_ID: '{{- $cm := (lookup "v1" "ConfigMap" .Release.Namespace "speedscale-operator") -}}{{ if $cm }}{{ $cm.data.INSTANCE_ID }}{{ else }}{{ ( printf "%s-%s" .Values.clusterName uuidv4 ) }}{{ end }}' + LOG_LEVEL: {{ .Values.logLevel }} + SPEEDSCALE_DLP_CONFIG: {{ .Values.dlp.config }} + SPEEDSCALE_FILTER_RULE: {{ .Values.filterRule }} + TELEMETRY_INTERVAL: 1s + WITH_DLP: {{ .Values.dlp.enabled | quote }} + WITH_INSPECTOR: {{ .Values.dashboardAccess | quote }} + API_KEY_SECRET_NAME: {{ .Values.apiKeySecret | quote }} + DEPLOY_DEMO: {{ .Values.deployDemo | quote }} + GLOBAL_ANNOTATIONS: {{ .Values.globalAnnotations | toJson | quote }} + GLOBAL_LABELS: {{ .Values.globalLabels | toJson | quote }} + {{- if .Values.http_proxy }} + HTTP_PROXY: {{ .Values.http_proxy }} + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY: {{ .Values.https_proxy }} + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY: {{ .Values.no_proxy }} + {{- end }} + PRIVILEGED_SIDECARS: {{ .Values.privilegedSidecars | quote }} + DISABLE_SMARTDNS: {{ .Values.disableSidecarSmartReverseDNS | quote }} + SIDECAR_CONFIG: {{ .Values.sidecar | toJson | quote }} + FORWARDER_CONFIG: {{ .Values.forwarder | toJson | quote }} + TEST_PREP_TIMEOUT: {{ .Values.operator.test_prep_timeout }} + CONTROL_PLANE_TIMEOUT: {{ .Values.operator.control_plane_timeout }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/crds/trafficreplays.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/crds/trafficreplays.yaml new file mode 100644 index 0000000000..aea3315479 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/crds/trafficreplays.yaml @@ -0,0 +1,525 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: trafficreplays.speedscale.com +spec: + group: speedscale.com + names: + kind: TrafficReplay + listKind: TrafficReplayList + plural: trafficreplays + shortNames: + - replay + singular: trafficreplay + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.active + name: Active + type: boolean + - jsonPath: .spec.mode + name: Mode + type: string + - jsonPath: .status.conditions[-1:].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: TrafficReplay is the Schema for the trafficreplays API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: TrafficReplaySpec defines the desired state of TrafficReplay + properties: + buildTag: + description: |- + BuildTag links a unique tag, build hash, etc. to the generated + traffic replay report. That way you can connect the report results to the + version of the code that was tested. + type: string + cleanup: + description: |- + Cleanup is the name of cleanup mode used for this TrafficReplay. Set to + "none" to leave resources in the state they were during the replay. The + default mode "inventory" will revert the environment to the state it was + before the replay. + enum: + - inventory + - all + - none + type: string + collectLogs: + description: |- + CollectLogs enables or disables log collection from target + workload. Defaults to true. + DEPRECATED: use TestReport.ActualConfig.Cluster.CollectLogs + type: boolean + configChecksum: + description: |- + ConfigChecksum, managed my the operator, is the SHA1 checksum of the + configuration. + type: string + customURL: + description: |- + CustomURL specifies a custom URL to send *ALL* traffic to. Use + Workload.CustomURI to send traffic to a specific URL for only that + workload. + type: string + generatorLowData: + description: |- + GeneratorLowData forces the generator into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + mode: + description: Mode is the name of replay mode used for this TrafficReplay. + enum: + - full-replay + - responder-only + - generator-only + type: string + needsReport: + description: Indicates whether a responder-only replay needs a report. + type: boolean + proxyMode: + description: |- + ProxyMode defines proxy operational mode used with injected sidecar. + DEPRECATED + type: string + responderLowData: + description: |- + ResponderLowData forces the responder into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + secretRefs: + description: |- + SecretRefs hold the references to the secrets which contain + various secrets like (e.g. short-lived JWTs to be used by the generator + for authorization with HTTP calls). + items: + description: |- + LocalObjectReference contains enough information to locate the referenced + Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + sidecar: + description: |- + Sidecar defines sidecar specific configuration. + DEPRECATED: use Workloads + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS inbound + private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys to use + for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar mutual + TLS. + properties: + private: + description: Private is the filename of the mutual TLS + private key. + type: string + public: + description: Public is the filename of the mutual TLS + public key. + type: string + secret: + description: Secret is a secret with the mutual TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + snapshotID: + description: |- + SnapshotID is the id of the traffic snapshot for this + TrafficReplay. + type: string + testConfigID: + description: |- + TestConfigID is the id of the replay configuration to be used + by the generator and responder for the TrafficReplay. + type: string + timeout: + description: |- + Timeout is the time to wait for replay test to finish. Defaults + to value of the `TIMEOUT` setting of the operator. + type: string + ttlAfterReady: + description: |- + TTLAfterReady provides a TTL (time to live) mechanism to limit + the lifetime of TrafficReplay object that have finished the execution and + reached its final state (either complete or failed). + type: string + workloadRef: + description: |- + WorkloadRef is the reference to the target workload (SUT) for + TrafficReplay. The operations will be performed in the namespace of the + target object. + DEPRECATED: use Workloads + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to "Deployment". + type: string + name: + description: |- + Name of the referenced object. Required when defining for a test unless a + custom URI is provided. Always required when defining mocks. + type: string + namespace: + description: Namespace of the referenced object. Defaults to the + TrafficReplay namespace. + type: string + required: + - name + type: object + workloads: + description: |- + Workloads define target workloads (SUT) for a TrafficReplay. Many + workloads may be provided, or none. Workloads may be modified and + restarted during replay to configure communication with a responder. + items: + description: |- + Workload represents a Kubernetes workload to be targeted during replay and + associated settings. + properties: + customURI: + description: |- + CustomURI will be target of the traffic instead of directly targeting + workload. This is required if a Ref is not specified. + type: string + inTrafficKey: + description: 'DEPRECATED: use Tests' + type: string + inTrafficKeys: + description: 'DEPRECATED: use Tests' + items: + type: string + type: array + mocks: + description: |- + Mocks are strings used to identify slices of outbound snapshot traffic to + mock for this workload and maps directly to a snapshot's `OutTraffic` + field. Snapshot egress traffic can be split across multiple slices where + each slice contains part of the traffic. A workload may specify multiple + keys and multiple workloads may specify the same key. + + + Only the traffic slices defined here will be mocked. A workload with no + keys defined will not mock any traffic. Pass '*' to mock all traffic. + + + Mock strings may only match part of the snapshot's `OutTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `OutTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com`. Multiple mocks must be specified for multiple keys + unless using '*'. + items: + type: string + type: array + outTrafficKeys: + description: 'DEPRECATED: use Mocks' + items: + type: string + type: array + ref: + description: |- + Ref is a reference to a cluster workload, like a deployment or a + statefulset. This is required unless a CustomURI is specified. + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to + "Deployment". + type: string + name: + description: |- + Name of the referenced object. Required when defining for a test unless a + custom URI is provided. Always required when defining mocks. + type: string + namespace: + description: Namespace of the referenced object. Defaults + to the TrafficReplay namespace. + type: string + required: + - name + type: object + routing: + description: Routing configures how workloads route egress traffic + to responders + enum: + - hostalias + - nat + type: string + sidecar: + description: |- + TODO: this is not implemented, come back and replace deprecated Sidecar with workload specific settings + Sidecar defines sidecar specific configuration. + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS + inbound private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys + to use for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar + mutual TLS. + properties: + private: + description: Private is the filename of the mutual + TLS private key. + type: string + public: + description: Public is the filename of the mutual + TLS public key. + type: string + secret: + description: Secret is a secret with the mutual + TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + tests: + description: |- + Tests are strings used to identify slices of inbound snapshot traffic this + workload is targeting and maps directly to a snapshot's `InTraffic` field. + Snapshot ingress traffic can be split across multiple slices where each + slice contains part of the traffic. A key must only be specified once + across all workloads, but a workload may specify multiple keys. Pass '*' + to match all keys. + + + Test strings may only match part of the snapshot's `InTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `InTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com` + + + This field is optional in the spec to provide support for single-workload + and legacy replays, but must be specified for multi-workload replays in + order to provide deterministic replay configuration. + items: + type: string + type: array + type: object + type: array + required: + - snapshotID + - testConfigID + type: object + status: + default: + observedGeneration: -1 + description: TrafficReplayStatus defines the observed state of TrafficReplay + properties: + active: + description: Active indicates whether this traffic replay is currently + underway or not. + type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + finishedTime: + description: Information when the traffic replay has finished. + format: date-time + type: string + initializedTime: + description: Information when the test environment was successfully + prepared. + format: date-time + type: string + lastHeartbeatTime: + description: 'DEPRECATED: will not be set' + format: date-time + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + reconcileFailures: + description: |- + ReconcileFailures is the number of times the traffic replay controller + experienced an error during the reconciliation process. The traffic + replay will be deleted if too many errors occur. + format: int64 + type: integer + reportID: + description: The id of the traffic replay report created. + type: string + reportURL: + description: The url to the traffic replay report. + type: string + startedTime: + description: Information when the traffic replay has started. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/deployments.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/deployments.yaml new file mode 100644 index 0000000000..e5f3292579 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/deployments.yaml @@ -0,0 +1,132 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + operator.speedscale.com/ignore: "true" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} + name: speedscale-operator + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + strategy: + type: Recreate + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - command: + - /operator + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - configMapRef: + name: speedscale-operator + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core + # When a key exists in multiple sources, the value associated with the last source will take precedence. + # Values defined by an Env with a duplicate key will take precedence. + - configMapRef: + name: speedscale-operator-override + optional: true + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/operator:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: health-check + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 5 + name: operator + ports: + - containerPort: 443 + name: webhook-server + - containerPort: 8081 + name: health-check + readinessProbe: + failureThreshold: 10 + httpGet: + path: /readyz + port: health-check + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + resources: {{- toYaml .Values.operator.resources | nindent 10 }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: false + # Run as root to bind 443 https://github.com/kubernetes/kubernetes/issues/56374 + runAsUser: 0 + volumeMounts: + - mountPath: /tmp + name: tmp + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + hostNetwork: {{ .Values.hostNetwork }} + securityContext: + runAsNonRoot: true + serviceAccountName: speedscale-operator + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: tmp + - name: webhook-certs + secret: + secretName: speedscale-webhook-certs + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/hooks.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/hooks.yaml new file mode 100644 index 0000000000..3e8231f194 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/hooks.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "4" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-pre-install + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + # ensure valid settings before the chart reports a successfull install + {{- if .Values.http_proxy }} + HTTP_PROXY={{ .Values.http_proxy | quote }} \ + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY={{ .Values.https_proxy | quote }} \ + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY={{ .Values.no_proxy | quote }} \ + {{- end }} + speedctl init --overwrite --no-rcfile-update \ + --api-key $SPEEDSCALE_API_KEY \ + --app-url $SPEEDSCALE_APP_URL + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/speedscale-cli:{{ .Values.image.tag }}' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: speedscale-cli + resources: {} + restartPolicy: Never + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/rbac.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/rbac.yaml new file mode 100644 index 0000000000..e1ea42d999 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/rbac.yaml @@ -0,0 +1,244 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +rules: +- apiGroups: + - apps + resources: + - deployments + - statefulsets + - daemonsets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - replicasets + verbs: + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - get + - list +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - pods + - services + - serviceaccounts + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - metrics.k8s.io + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - envoyfilters + - sidecars + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - security.istio.io + resources: + - peerauthentications + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - speedscale.com + resources: + - trafficreplays/status + verbs: + - get + - update + - patch +- apiGroups: + - argoproj.io + resources: + - rollouts + verbs: + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: speedscale-operator + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator +subjects: +- kind: ServiceAccount + name: speedscale-operator + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/secrets.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/secrets.yaml new file mode 100644 index 0000000000..1fb6999e4c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/secrets.yaml @@ -0,0 +1,18 @@ +--- +{{ if .Values.apiKey }} +apiVersion: v1 +kind: Secret +metadata: + name: speedscale-apikey + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +type: Opaque +data: + SPEEDSCALE_API_KEY: {{ .Values.apiKey | b64enc }} + SPEEDSCALE_APP_URL: {{ .Values.appUrl | b64enc }} +{{ end }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/services.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/services.yaml new file mode 100644 index 0000000000..f9da2c25c1 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/services.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: {{ toYaml .Values.globalAnnotations | nindent 4 }} + {{- end }} +spec: + ports: + - port: 443 + protocol: TCP + selector: + app: speedscale-operator + controlplane.speedscale.com/component: operator +status: + loadBalancer: {} diff --git a/charts/speedscale/speedscale-operator/2.2.556/templates/tls.yaml b/charts/speedscale/speedscale-operator/2.2.556/templates/tls.yaml new file mode 100644 index 0000000000..4a24562884 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/templates/tls.yaml @@ -0,0 +1,183 @@ +{{- $crt := "" -}} +{{- $key := "" -}} +{{- $s := (lookup "v1" "Secret" .Release.Namespace "speedscale-certs") -}} +{{- if $s -}} +{{- $crt = index $s.data "tls.crt" | b64dec -}} +{{- $key = index $s.data "tls.key" | b64dec -}} +{{ else }} +{{- $cert := genCA "Speedscale" 3650 -}} +{{- $crt = $cert.Cert -}} +{{- $key = $cert.Key -}} +{{- end -}} +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "5" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-create-jks + namespace: {{ .Release.Namespace }} + labels: + {{- if .Values.globalLabels }} +{{ toYaml .Values.globalLabels | indent 4}} + {{- end }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 30 + template: + metadata: + annotations: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + creationTimestamp: null + labels: + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 8}} + {{- end }} + spec: + containers: + - args: + - |- + keytool -keystore /usr/lib/jvm/jre/lib/security/cacerts -importcert -noprompt -trustcacerts -storepass changeit -alias speedscale -file /etc/ssl/speedscale/tls.crt + kubectl -n ${POD_NAMESPACE} delete secret speedscale-jks || true + kubectl -n ${POD_NAMESPACE} create secret generic speedscale-jks --from-file=cacerts.jks=/usr/lib/jvm/jre/lib/security/cacerts + + # in case we're in istio + curl -X POST http://127.0.0.1:15000/quitquitquit || true + command: + - sh + - -c + volumeMounts: + - mountPath: /etc/ssl/speedscale + name: speedscale-tls-out + readOnly: true + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + envFrom: + - secretRef: + name: '{{ ne .Values.apiKeySecret "" | ternary .Values.apiKeySecret "speedscale-apikey" }}' + optional: false + image: '{{ .Values.image.registry }}/amazoncorretto' + imagePullPolicy: {{ .Values.image.pullPolicy }} + name: create-jks + resources: {} + restartPolicy: Never + serviceAccountName: speedscale-operator-provisioning + volumes: + - name: speedscale-tls-out + secret: + secretName: speedscale-certs + {{- if .Values.affinity }} + affinity: {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{ toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} +--- +apiVersion: v1 +automountServiceAccountToken: true +kind: ServiceAccount +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "1" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + labels: + app: speedscale-operator + controlplane.speedscale.com/component: operator + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "2" + creationTimestamp: null + name: speedscale-operator-provisioning +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "3" + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-operator-provisioning +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: speedscale-operator-provisioning +subjects: +- kind: ServiceAccount + name: speedscale-operator-provisioning + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + helm.sh/hook: pre-install + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} + creationTimestamp: null + name: speedscale-certs + namespace: {{ .Release.Namespace }} +type: kubernetes.io/tls +data: + tls.crt: {{ $crt | b64enc }} + tls.key: {{ $key | b64enc }} diff --git a/charts/speedscale/speedscale-operator/2.2.556/values.yaml b/charts/speedscale/speedscale-operator/2.2.556/values.yaml new file mode 100644 index 0000000000..dc2c404d28 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.556/values.yaml @@ -0,0 +1,138 @@ +# An API key is required to connect to the Speedscale cloud. +# If you need a key email support@speedscale.com. +apiKey: "" + +# A secret name can be referenced instead of the api key itself. +# The secret must be of the format: +# +# type: Opaque +# data: +# SPEEDSCALE_API_KEY: +# SPEEDSCALE_APP_URL: +apiKeySecret: "" + +# Speedscale domain to use. +appUrl: "app.speedscale.com" + +# The name of your cluster. +clusterName: "my-cluster" + +# Speedscale components image settings. +image: + registry: gcr.io/speedscale + tag: v2.2.556 + pullPolicy: Always + +# Log level for Speedscale components. +logLevel: "info" + +# Namespaces to be watched by Speedscale Operator as a list of names. +namespaceSelector: [] + +# Instructs operator to deploy resources necessary to interact with your cluster from the Speedscale dashboard. +dashboardAccess: true + +# Filter Rule to apply to the Speedscale Forwarder +filterRule: "standard" + +# Data Loss Prevention settings. +dlp: + # Instructs operator to enable data loss prevention features + enabled: false + + # Configuration for data loss prevention + config: "standard" + +# If the operator pod/webhooks need to be on the host network. +# This is only needed if the control plane cannot connect directly to a pod +# for eg. if Calico is used as EKS's default networking +# https://docs.tigera.io/calico/3.25/getting-started/kubernetes/managed-public-cloud/eks#install-eks-with-calico-networking +hostNetwork: false + +# A set of annotations to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# annotation.first: value +# annotation.second: value +globalAnnotations: {} + +# A set of labels to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# label1: value +# label2: value +globalLabels: {} + +# A full affinity object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity +affinity: {} + +# The list of tolerations as detailed: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ +tolerations: [] + +# A nodeselector object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/ +nodeSelector: {} + +# Deploy a demo app at startup. Set this to an empty string to not deploy. +# Valid values: ["java", ""] +deployDemo: "java" + +# Proxy connection settings if required by your network. These translate to standard proxy environment +# variables HTTP_PROXY, HTTPS_PROXY, and NO_PROXY +http_proxy: "" +https_proxy: "" +no_proxy: "" + +# control if sidecar init containers should run with privileged set +privilegedSidecars: false + +# control if the sidecar should enable/disable use of the smart dns lookup feature (requires NET_ADMIN) +disableSidecarSmartReverseDNS: false + +# Operator settings. These limits are recommended unless you have a cluster +# with a very large number of workloads (for eg. 10k+ deployments, replicasets, etc.). +operator: + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + # how long to wait for the SUT to become ready + test_prep_timeout: 10m + # timeout for deploying & upgrading control plane components + control_plane_timeout: 5m + + +# Default sidecar settings. Example: +# sidecar: +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# ephemeral-storage: 100Mi +# requests: +# cpu: 10m +# memory: 32Mi +# ephemeral-storage: 100Mi +# ignore_src_hosts: example.com, example.org +# ignore_src_ips: 8.8.8.8, 1.1.1.1 +# ignore_dst_hosts: example.com, example.org +# ignore_dst_ips: 8.8.8.8, 1.1.1.1 +# insert_init_first: false +# tls_out: false +# reinitialize_iptables: false +sidecar: {} + +# Forwarder settings +# forwarder: +# resources: +# limits: +# cpu: 500m +# memory: 500M +# requests: +# cpu: 300m +# memory: 250M +forwarder: {} diff --git a/index.yaml b/index.yaml index 6546023833..7db6575baf 100644 --- a/index.yaml +++ b/index.yaml @@ -4274,6 +4274,38 @@ entries: - assets/cerbos/cerbos-0.37.0.tgz version: 0.37.0 cf-runtime: + - annotations: + artifacthub.io/changes: | + - kind: fixed + description: "ensure all env vars are quoted for engine and dind pods" + artifacthub.io/containsSecurityUpdates: "false" + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Codefresh + catalog.cattle.io/kube-version: '>=1.18-0' + catalog.cattle.io/release-name: cf-runtime + apiVersion: v2 + created: "2024-10-19T00:34:57.521322017Z" + dependencies: + - name: cf-common + repository: oci://quay.io/codefresh/charts + version: 0.16.0 + description: A Helm chart for Codefresh Runner + digest: 03c81d6e5a75b7407ebb03685b9e5d3830866890a79d98601b69c4305d2a549a + home: https://codefresh.io/ + icon: file://assets/icons/cf-runtime.png + keywords: + - codefresh + - runner + kubeVersion: '>=1.18-0' + maintainers: + - name: codefresh + url: https://codefresh-io.github.io/ + name: cf-runtime + sources: + - https://github.com/codefresh-io/venona + urls: + - assets/codefresh/cf-runtime-6.4.7.tgz + version: 6.4.7 - annotations: artifacthub.io/changes: | - kind: fixed @@ -15856,6 +15888,23 @@ entries: - assets/instana/instana-agent-1.2.60.tgz version: 1.2.60 intel-device-plugins-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel Device Plugins Operator + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-operator + apiVersion: v2 + appVersion: 0.31.1 + created: "2024-10-19T00:34:58.28033816Z" + description: A Helm chart for Intel Device Plugins Operator for Kubernetes + digest: be859f9cfc4e7b790eb3509d482dc88427638fbf590a99a2663e32614c3400bf + icon: file://assets/icons/intel-device-plugins-operator.png + kubeVersion: '>=1.19-0' + name: intel-device-plugins-operator + type: application + urls: + - assets/intel/intel-device-plugins-operator-0.31.1.tgz + version: 0.31.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Intel Device Plugins Operator @@ -15970,6 +16019,23 @@ entries: - assets/intel/intel-device-plugins-operator-0.26.1.tgz version: 0.26.1 intel-device-plugins-qat: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel QAT Device Plugin + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-qat + apiVersion: v2 + appVersion: 0.31.1 + created: "2024-10-19T00:34:58.283074907Z" + description: A Helm chart for Intel QAT Device Plugin + digest: 5f5d6cf87c99be951586dd7340c70770aeaceb9949284abba8d8e51611ec96b7 + icon: file://assets/icons/intel-device-plugins-qat.png + kubeVersion: '>=1.19-0' + name: intel-device-plugins-qat + type: application + urls: + - assets/intel/intel-device-plugins-qat-0.31.1.tgz + version: 0.31.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Intel QAT Device Plugin @@ -16084,6 +16150,23 @@ entries: - assets/intel/intel-device-plugins-qat-0.26.1.tgz version: 0.26.1 intel-device-plugins-sgx: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Intel SGX Device Plugin + catalog.cattle.io/kube-version: '>=1.19-0' + catalog.cattle.io/release-name: intel-device-plugins-sgx + apiVersion: v2 + appVersion: 0.31.1 + created: "2024-10-19T00:34:58.28519936Z" + description: A Helm chart for Intel SGX Device Plugin + digest: cc0d180ac6c273a03c80792329a1a8ee36ae994c528d9b2748bd1b91e61339d8 + icon: file://assets/icons/intel-device-plugins-sgx.png + kubeVersion: '>=1.19-0' + name: intel-device-plugins-sgx + type: application + urls: + - assets/intel/intel-device-plugins-sgx-0.31.1.tgz + version: 0.31.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Intel SGX Device Plugin @@ -24378,6 +24461,38 @@ entries: catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 + appVersion: edge-24.10.3 + created: "2024-10-19T00:35:00.302033459Z" + 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: 525b51748732d64c2de24e2d002a9c7c3705229e3c03df309fe1f173add8c052 + 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/linkerd/linkerd-control-plane-2024.10.3.tgz + version: 2024.10.3 + - annotations: + catalog.cattle.io/auto-install: linkerd-crds + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd Control Plane + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-control-plane + apiVersion: v2 appVersion: edge-24.10.2 created: "2024-10-11T00:35:29.425930832Z" dependencies: @@ -24386,7 +24501,7 @@ entries: version: 0.1.0 description: 'Linkerd gives you observability, reliability, and security for your microservices — with no code change required. ' - digest: 4373a5cbbc95629a7f43ed53ecb8927146b6dab35f9abc89cebd4f9f4e1b7f34 + digest: 23fc840c78bfb29f6d9468f7037582e74d470afabca1e59b2fbc8604e58b6077 home: https://linkerd.io icon: file://assets/icons/linkerd-control-plane.png keywords: @@ -25641,6 +25756,36 @@ entries: - assets/linkerd/linkerd-control-plane-1.12.5.tgz version: 1.12.5 linkerd-crds: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd CRDs + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-crds + apiVersion: v2 + created: "2024-10-19T00:35:00.363628645Z" + 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: 8e1eeeadb03617ced9a561208189425611e6c43f81eed47a9df527cc446fab20 + 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/linkerd/linkerd-crds-2024.10.3.tgz + version: 2024.10.3 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd CRDs @@ -38280,6 +38425,37 @@ entries: - assets/redpanda/redpanda-4.0.33.tgz version: 4.0.33 speedscale-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator + apiVersion: v1 + appVersion: 2.2.556 + created: "2024-10-19T00:35:01.993569462Z" + description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. + digest: 8d49d85bcbfe14c86f94395f55e48d0fca8742bb30437f9342bf997365d4994f + home: https://speedscale.com + icon: file://assets/icons/speedscale-operator.png + keywords: + - speedscale + - test + - testing + - regression + - reliability + - load + - replay + - network + - traffic + kubeVersion: '>= 1.17.0-0' + maintainers: + - email: support@speedscale.com + name: Speedscale Support + name: speedscale-operator + urls: + - assets/speedscale/speedscale-operator-2.2.556.tgz + version: 2.2.556 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Speedscale Operator @@ -45356,4 +45532,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2024-10-18T00:35:22.797392069Z" +generated: "2024-10-19T00:34:57.105248961Z"