From 2f77170aba712426c45431e5237fbacf5720a177 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 7 Sep 2024 00:52:44 +0000 Subject: [PATCH] Added chart versions: kubemq/kubemq-cluster: - 2.4.0 linkerd/linkerd-control-plane: - 2024.9.1 linkerd/linkerd-crds: - 2024.9.1 speedscale/speedscale-operator: - 2.2.372 --- assets/kubemq/kubemq-cluster-2.4.0.tgz | Bin 0 -> 3783 bytes .../linkerd-control-plane-2024.8.3.tgz | Bin 31373 -> 31365 bytes .../linkerd-control-plane-2024.9.1.tgz | Bin 0 -> 31555 bytes assets/linkerd/linkerd-crds-2024.9.1.tgz | Bin 0 -> 113052 bytes .../speedscale-operator-2.2.372.tgz | Bin 0 -> 16768 bytes .../kubemq/kubemq-cluster/2.4.0/.helmignore | 22 + charts/kubemq/kubemq-cluster/2.4.0/Chart.lock | 9 + charts/kubemq/kubemq-cluster/2.4.0/Chart.yaml | 17 + charts/kubemq/kubemq-cluster/2.4.0/README.md | 26 + .../kubemq/kubemq-cluster/2.4.0/app-readme.md | 43 + .../kubemq-cluster/2.4.0/questions.yaml | 37 + .../2.4.0/templates/_helpers.tpl | 50 + .../2.4.0/templates/kubemqcluster.yaml | 21 + .../2.4.0/templates/role_binding.yaml | 12 + .../2.4.0/templates/service_account.yaml | 5 + .../kubemq/kubemq-cluster/2.4.0/values.yaml | 1 + .../kubemq-cluster/2.4.0/values_example.yaml | 117 + .../linkerd-control-plane/2024.8.3/Chart.yaml | 1 - .../2024.9.1/.helmignore | 22 + .../linkerd-control-plane/2024.9.1/Chart.lock | 6 + .../linkerd-control-plane/2024.9.1/Chart.yaml | 29 + .../linkerd-control-plane/2024.9.1/README.md | 321 + .../2024.9.1/README.md.gotmpl | 133 + .../2024.9.1/app-readme.md | 14 + .../2024.9.1/charts/partials/.helmignore | 21 + .../2024.9.1/charts/partials/Chart.yaml | 5 + .../2024.9.1/charts/partials/README.md | 9 + .../2024.9.1/charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 267 + .../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.9.1/charts/partials/values.yaml | 0 .../2024.9.1/questions.yaml | 19 + .../2024.9.1/templates/NOTES.txt | 19 + .../2024.9.1/templates/config-rbac.yaml | 16 + .../2024.9.1/templates/config.yaml | 39 + .../2024.9.1/templates/destination-rbac.yaml | 327 + .../2024.9.1/templates/destination.yaml | 435 ++ .../2024.9.1/templates/heartbeat-rbac.yaml | 78 + .../2024.9.1/templates/heartbeat.yaml | 94 + .../2024.9.1/templates/identity-rbac.yaml | 49 + .../2024.9.1/templates/identity.yaml | 273 + .../2024.9.1/templates/namespace.yaml | 18 + .../2024.9.1/templates/podmonitor.yaml | 128 + .../templates/proxy-injector-rbac.yaml | 120 + .../2024.9.1/templates/proxy-injector.yaml | 222 + .../2024.9.1/templates/psp.yaml | 119 + .../2024.9.1/values-ha.yaml | 63 + .../2024.9.1/values.yaml | 664 ++ .../linkerd/linkerd-crds/2024.9.1/.helmignore | 22 + .../linkerd/linkerd-crds/2024.9.1/Chart.lock | 6 + .../linkerd/linkerd-crds/2024.9.1/Chart.yaml | 26 + .../linkerd/linkerd-crds/2024.9.1/README.md | 71 + .../linkerd-crds/2024.9.1/README.md.gotmpl | 59 + .../linkerd-crds/2024.9.1/app-readme.md | 9 + .../2024.9.1/charts/partials/.helmignore | 21 + .../2024.9.1/charts/partials/Chart.yaml | 5 + .../2024.9.1/charts/partials/README.md | 9 + .../2024.9.1/charts/partials/README.md.gotmpl | 14 + .../charts/partials/templates/NOTES.txt | 0 .../charts/partials/templates/_affinity.tpl | 38 + .../partials/templates/_capabilities.tpl | 16 + .../charts/partials/templates/_debug.tpl | 15 + .../charts/partials/templates/_helpers.tpl | 14 + .../charts/partials/templates/_metadata.tpl | 17 + .../partials/templates/_network-validator.tpl | 45 + .../partials/templates/_nodeselector.tpl | 4 + .../partials/templates/_proxy-config-ann.tpl | 18 + .../charts/partials/templates/_proxy-init.tpl | 98 + .../charts/partials/templates/_proxy.tpl | 267 + .../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.9.1/charts/partials/values.yaml | 0 .../linkerd-crds/2024.9.1/templates/NOTES.txt | 6 + .../gateway.networking.k8s.io_grpcroutes.yaml | 1507 +++++ .../gateway.networking.k8s.io_httproutes.yaml | 3881 ++++++++++++ .../policy/authorization-policy.yaml | 99 + .../2024.9.1/templates/policy/httproute.yaml | 5328 +++++++++++++++++ .../policy/meshtls-authentication.yaml | 87 + .../policy/network-authentication.yaml | 53 + .../policy/server-authorization.yaml | 266 + .../2024.9.1/templates/policy/server.yaml | 319 + .../2024.9.1/templates/serviceprofile.yaml | 274 + .../templates/workload/external-workload.yaml | 303 + .../linkerd/linkerd-crds/2024.9.1/values.yaml | 1 + .../speedscale-operator/2.2.372/.helmignore | 23 + .../speedscale-operator/2.2.372/Chart.yaml | 27 + .../speedscale-operator/2.2.372/LICENSE | 201 + .../speedscale-operator/2.2.372/README.md | 111 + .../speedscale-operator/2.2.372/app-readme.md | 111 + .../2.2.372/questions.yaml | 9 + .../2.2.372/templates/NOTES.txt | 12 + .../2.2.372/templates/admission.yaml | 209 + .../2.2.372/templates/configmap.yaml | 41 + .../templates/crds/trafficreplays.yaml | 515 ++ .../2.2.372/templates/deployments.yaml | 132 + .../2.2.372/templates/hooks.yaml | 73 + .../2.2.372/templates/rbac.yaml | 244 + .../2.2.372/templates/secrets.yaml | 18 + .../2.2.372/templates/services.yaml | 22 + .../2.2.372/templates/tls.yaml | 183 + .../speedscale-operator/2.2.372/values.yaml | 133 + index.yaml | 118 +- 121 files changed, 19391 insertions(+), 3 deletions(-) create mode 100644 assets/kubemq/kubemq-cluster-2.4.0.tgz create mode 100644 assets/linkerd/linkerd-control-plane-2024.9.1.tgz create mode 100644 assets/linkerd/linkerd-crds-2024.9.1.tgz create mode 100644 assets/speedscale/speedscale-operator-2.2.372.tgz create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/.helmignore create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/Chart.lock create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/Chart.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/README.md create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/app-readme.md create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/questions.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/templates/_helpers.tpl create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/templates/kubemqcluster.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/templates/role_binding.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/templates/service_account.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/values.yaml create mode 100644 charts/kubemq/kubemq-cluster/2.4.0/values_example.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/.helmignore create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/Chart.lock create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/Chart.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/README.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/app-readme.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/.helmignore create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/Chart.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_affinity.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_capabilities.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_debug.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_helpers.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_metadata.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_network-validator.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_resources.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_tolerations.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_trace.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_validate.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_volumes.tpl create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/values.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/questions.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/config-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/config.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/destination-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/destination.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/namespace.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/podmonitor.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector-rbac.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/templates/psp.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/values-ha.yaml create mode 100644 charts/linkerd/linkerd-control-plane/2024.9.1/values.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/.helmignore create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/Chart.lock create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/Chart.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/README.md create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/app-readme.md create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/.helmignore create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/Chart.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md.gotmpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_affinity.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_capabilities.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_debug.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_helpers.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_metadata.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_network-validator.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_nodeselector.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-init.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_pull-secrets.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_resources.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_tolerations.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_trace.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_validate.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_volumes.tpl create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/charts/partials/values.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/NOTES.txt create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_grpcroutes.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_httproutes.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/authorization-policy.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/httproute.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/meshtls-authentication.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/network-authentication.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server-authorization.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/serviceprofile.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/templates/workload/external-workload.yaml create mode 100644 charts/linkerd/linkerd-crds/2024.9.1/values.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/.helmignore create mode 100644 charts/speedscale/speedscale-operator/2.2.372/Chart.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/LICENSE create mode 100644 charts/speedscale/speedscale-operator/2.2.372/README.md create mode 100644 charts/speedscale/speedscale-operator/2.2.372/app-readme.md create mode 100644 charts/speedscale/speedscale-operator/2.2.372/questions.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/NOTES.txt create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/admission.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/configmap.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/crds/trafficreplays.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/deployments.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/hooks.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/rbac.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/secrets.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/services.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/templates/tls.yaml create mode 100644 charts/speedscale/speedscale-operator/2.2.372/values.yaml diff --git a/assets/kubemq/kubemq-cluster-2.4.0.tgz b/assets/kubemq/kubemq-cluster-2.4.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..81cd065e7ecc0181493c18ff585d91c4d7d5c5e7 GIT binary patch literal 3783 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 literal 0 HcmV?d00001 diff --git a/assets/linkerd/linkerd-control-plane-2024.8.3.tgz b/assets/linkerd/linkerd-control-plane-2024.8.3.tgz index 24067c043eaa2ff2ce677fca24d080d5d3f98875..f7a5918b8868492f38d51557733d31f1f9637b04 100644 GIT binary patch delta 28236 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 P00960itGPJ0Pq3;kO{t8 delta 28260 zcmV)EK)}C+^#P6b0g$|Z^?Lu*KR7x%81(lKkNW@A>mMHU`u~J__kn!fCl?a)f9l;C zSG94!lMhZ)Cb6U}6~h)n5tcY%lQ6M9(!;bfPCAI#-jhvEGAPe?F02%m)et(asajY%3& zB8II1X^^THC{9#wags4XCF6@B8qU;$^{xTt}3VjnbC+dRgFeOp#S|p{};NU za*9%hA{G-AO>sIQh?8G)%1IoeN3AJSjhRZBiD9>E`YEKWMI%L*X$}^GNYPc#M7Oas z!z`V&uF2wt@fdo^P!Y_Cn6}j5{nR*iGfY#7X-YWMA~Q^XlOc-IXdGBPF`565gbFD_ z^;pq{PQrF{vhZbUH1FKgTe5y7e0P+FgWP#A3hCwhr?d4 z{n=O4@9WdN{^vN!iMTHTaP|5>8XWcZtLy*h@In56`y`(obV|lJPbAV~4pM^M5L-Lw za!LiF0%7zY$1k1-W5#D#N)iJvPz6p&lweNK9CM0C2@yy#G$JTdvOtVzDj7%}NRnBW zU`a&SYVDvPKtJRo!c!uNK&LM+&?!@UJTM>yA{V3;CApA<>o-GWeyV*5CkDvnl-TDe zCGv)U@oU|Nn<K0iPir*kX|h=4}D#Zg2u z)wgHo^CR^7`LRHllhzIr8HqrsMuH(Z#S(FUB3P0unF47TG(~ce(FiAgzmjxLIZJ0G zl_Ip=`O@GWqISO*!hhW-oqn$u9v#4sey`JiJP7+o`q!Y-f2w{x(LawmcdFf}~Q{y)Z2kx&!msh;YPz z#)u^`dPP8Rx*!tf6emjlQ{Qlsup7i#l8kV49kw``Q?&(sPlej2p3@nXL)7nS6wlcN zC1g$#m=+3+;dKaj9g5g2W2x$)mU)p7{+vyot2#qO(=jUm>G{7Uq;H|pP?e&ohDB1n zR>}Y*#}Q2@wl=6im-^Wdjd3D~uC3O8h{$jR3q_DLZh!*|u?f;kTe%Q(f5BuzxQ1PZ zWH$E@T@ZrKIeWKI`UK27)dv|oF@;(NT%&RXu+3UP*ToEtNtUpMnxIHXwa-r$t>W2* zd8AZR)txk*yv|}QiF$z048MDw;yI>?>dhe<9R7=aev$Kuc~a_dHEOvaYK}dBOXG~u zR9+-%eXDWiGs1-%+?WVSQxM!O7x-&y$3QAowk$;PJwc=z70GjgSeh<z!D&i9kk= zZSA0mVg*znmbRK84*^oS4VpDcitu<$Q>EHPoM<{(>h-8@%9Ku~$aE5@g$B<#i;q)D zj}45{1Z(K6EaPm9h#^kVyKMGHU8VLEZ#J}$s#WV9STO+j1%5|o`OIU(8HKk#|zamW8xxlD@y4zm`ibK=3Mez zNW>YF$QZdIwRCr%ot$1A?}g}0qM0f&0utslCb8`z13PTdn52@*1;9E^qbcL!{Mm~k z`rzY;wIq})`ALnVt<~B=|HHCS#i@Pis0`XlOzk$9RF2QjhUmY4sBbu6UG$Lch3Cw;e)d+?FKlvCagP3*r+@24?x_=B~7~Hgx#2h&^3gRaKiRl2(_7%lk6(}-9gPsu+j?KEq3G!^BFiGOrNFA^qTA5;;h1C?j|E&fg z!6T9^tNa{Z7WJMNg|jSK*y2jW1A9{|VzU`btJOR)FXNPPDih!1&f48{>7Y^?pr02)>2ly6R{=6ib$`WG1X?JOdGc+$UEsXL zlRM!m3sX?QSz1)T8#Hz4wyD>WvN&riE9I$BnmC&ottsPdL_qoQbR|^@Dpr-6LXY7@ z%jTY<0u#S~0H-am^dA}?ym0CWv@t+dCFz;p?cuQ7D;iwyOS@2j;S(VR`Y)OfY4eq{ z+A45Smg_GSCWvP&Po>)OPR?JWJk{G1mdl0@1A8`*60LBU(KnEfw}WVDHz3SU2Xsbe zj4#%Zt_$6f?%RM}Lp;f*WJWkn0;Qk1g})S`E00Wn`Fk6Rtst*xl@kCfoK8J+a+($* zoB=@zzceuxmXxJ|$H1*CriH9}@lFz+;^f>^vXV%p&wyv!CFd9bP$n57LMow4Oc72I z%jJmWX^bvU&H+rs(p0N*E#%&w=!chdMpzC?bgwrPcR)+i6n!XFG0MffCM9G z59pO3VzrP(`J(}IZjhb=PDgiX4&Rk-+fx94?`~wDrJGQ_narY)ua;9>iRH41F;TOrEaV4ijiiC-(Z zrZc6{HRFq~3FnOapp4mW_XgJZRY^aEj@qObKKxB}nAsF^#BJ`z&E4gn#v@w|%Y^T$~8zS-N5} z%0Vei*hB&NUe_O@HXD!IV07S8cUj00Ua2y&B(V*WOIBrW*xrcttj6EgFg=8}-oPk(Dm~fV7t3jjP zqTt!7X3ir5=0_!b(J@lun_9$wI7Pj^7D5w6Nzixdrsd2aZ~Q_)X4CMM$Pr;KthF-6 z5*w>D;1Lop5KibEgad$wB}xbu8r+1=NviZ~%AEYjNkr(}S~4%rzPo((>ID*XqB_Ct zA)ev@zDR}=mQHLljJ@WOKv$CRnekwJ$8ki?0oetKSQ?8f&ye?=j!CqCh!V{Vri@*S z!d_Px=^%2CMWP$AG$NUVFD5Fh_!=Y@S^$u`JH;z_um@Na^PH(htF7C*6SO?y0BBMa zp*NVyZ=jQ&y`$1JL9-oioI22YFXGBK6{|r}JD`x5OO&UQCf;&q zvZmxSV`Kd>wGtz2ZQg8ux6X7mXV@6A4F8%Fy#wkZ=;FoYIocf@b`GCB-ZKu8U`)A? zrofB;xcJ+1w7b9GJ8FFY9Zm?xX!qdY;GnUtMZBYv)72(+^Asw?+ zN1WhmQONg78H4_^y{bE@qk5vFTH8^r=_ryrik=Q2bX^ZK+^#1zxhCqtI8W?)H4>*5 zx3qv{6bh2yOsLglw74WeEF-C>tO_*6bM0jGC>{5fp4rr4_2So`JYBj6o zb27s;Rlhv2YHC}5!n`HwPpM0AMe1p#J>N!K&_NQ~1(Gs!bpfMug#<}RBn85X4TK2& zm@3I(h}t+w0-VdKn#2*7ByM*A3B1VD$}7}14u)!-;zdK9n4}B!7w0jRx(&WY(TGYZ zV!DW1Rz`nWQrb7K{P65Nczn2z6q}n9$CaU!3tmYPd8cN726!V9>wN{Ejq?(DT=ieS zM*VQ`R5Qr4u~Av#4hmt!!$xLg1tT@-N8mOE8>go0LX%a|a$c5bw)0f6r##cf(ZZuA zLZ{HEje`hr4h8HSR+3*#5?r-OsXg8+KQz)Ul@uqZs=VW+eJv*&fW>2r_Z7i0-3Bo) zRak*}uoSU>>5O#%H07DUz3KW0jWJDfPA;dMh$&0rA?gqGu5n_>B?9RnL*Nt{;gS+j zGA2B#25J}{r{!O*Xc!&A1FCJ%YG*q>KU2bS#!^9$kctmNU1WchggUk0ZRc7I16VG~ zA@;$u8Tmwo28QXN!dSE~+RMO?h`b@1(J3_%W}`-bBGzGv6XKc2W;GNuBbvr$K4^bZ zwHweb4M}JjFO^nAbI>?3?Vj%7 zgHcs~544P^kvo+xx=z_mLgERD)eN%cL`z3`2dU6f9eSsV-h1yrBwk70`rIAm44Jp+ zDQnetK?YUR{#i<;Gw=U|ImKE{qM5WNFzlwV_TNk901+D~5~7owbCSwrp*LV3tB$ka zd5Q>?Q>8nl=utYBj}X(}6EeZk;t`s$m}ujFfcgelt%V(ytM0g5>X3>NO)+0M+9C*b z(AB3*n9v20XG&BBFi@S$2`|9gMyd5?TKs~=Y7uIhmPx_XGrE9PN?g+n%_(+Zs-o@T z(c{j+;n5SNDbPvEIH?HSuEbKw+c(tQ7O695Z;Ev}vU=>287v#Wq&=oYE9eoM&GJ-# z2bjRg9L0w{0Yz;nTHI?PWFkEDZ%69Ee&^`mVBb-C!e%oZ2%-cErNkyw0LdaBr9^@j zJI4u)(aG8AD{C;-HpL>g?V4s8iP30LGMTE!7aEK&xGu5b!aT1DhTxa>t-ED7n;LMN zfm=haXWIqvn_KiNaYyS7{Fab)R{m;#K+{Jt^)E*Bc*R7*0=VJ3zX`m64S8!8NSU=Xz1NzbBDPlxE}@Noa&e$adeBT~Zy zj|)Walucbc~c35%|k^k5=kG$X~5RU@H{Mhccm zmLjaTUSP7hnjD23p#)DQEx7m`8;0uK0U80sBa56{*~3AgN@d}kz`VA90je%Q#>m?@ z#2CZefe(Z2CkubLoSB=VK|xD(3WH~;#MJGRP5JtI91X0!ER&0l zwYos>^h5Y>2n7Mr8-32B|JFQHxF8~0z0`rvM5qSSIY|~q7goKd`rO~ZYhzIFeT`d;wHnK?d$-Hvy{Qc2!P>s6@Z&R7fwf(lo9xu%UA2l(GI4qLV& zG1k%zCle|pr$ls+XIWFq8x?S{iNMJ(^hy_m&6q0@5d!#>uBT@>>^H7IU7Qn+BE>^R zas1~(*B_!cZxvO4hM&$BftkpGrYta&ErNtiXc}m=rXhN_Q@odI{%8Ud6<U~mGqN+f6+5p?}Tpm{$W^z9jii*vi9no-g=s^&0%5bbtiymdoHQ^u}MVRxnq zG!+G?2wgh^0d|={>z`{;Ew+*4&F7Z@R+E_P1(<5rxN+7gISK{~+jDDCRrCZ2b(e8; z@%{0hVV1=ix60uvOG8*C>L))OR@+nE!3D0*j%l?E*l&3tGZuT`%t#~=2pSfbQd=e>92h|FUvk^$`m5L;i1mqoR{Z=HgqH~%wp!l1a;*J~h$)G` zmogiifLRXW+(!9WOT5>FWIuyxZRjk>#zf}~W}@OFD7LeDk$v;%W=a$CGmR6E?SlMQ znSJo=#4?DD4+l6<8))QK>|CLYSHRu79=<93bz%ROSr0wOw|~CLz7HhNg$$I|DvVR& z1iPjdHpr(z>5xC8+Wh>RTtQ|BZ zn9C8tRd>sd4bA>u75GLK2m*BBovoJ43+4ecS^&?9xPG z*a&Aed{>$@ZQZ*n)ej^5O`gUHxpK=>5N*@9VlqY}D4`3u845-ZwbX+dPVoeP`mh+B zTeto%(`8&Rp*cW|USl|(Q6V6W zN6~98y~BkmZnGW8X!`73#tGDL2j;HHVrZsb=xs?3bYMW+AWfDL4z@j|y`H7!sZRf5 znY247ANxqkTl$Ph9Ak+?MekOBu;GZYSO`gG?wLPsuIGLnsjlC!+ZYtyqR+g}Dy2UP zoaR+))hQ$5C6mwIQ6ZOsN@-wM$m;seHRtV4&ALA_fV9b~NCb@t=) z#SX#(@-}+pTKj+_yNL9)Epr_5&B-xxocokTUfi!=t|9Ul2e$9`%CWpk=%Xx!8D?0y zKdKrVP;zo?89CT`ahWuKZ!&)526T5^3W*@XfnsWbqA0+D*RO6>H^eV{ZT82jbX0p) z!}Y+rIpji_uc~d(&=r+HpfIzlNLk=UAIEWFr!DQtms5iLnFKyiLzY={LNX3ApP>S) zX?_JfL9KOF!!DjWjiBrVG3AO<)3Yos5m}N2KHuo-?z_dn+Kt|Sm>%ya_E*G6C0w+2 zHV~{!oW_^??_2C|utx7}qXCo%ZBPR$XQY=pP|f?gdvXg&wFKZnSiOs|`o+nrt0bzW zbL?{qSD%=$-<6zITC9ztR=LE3wDlluJxE)BY-!7QZkEaj@cIrSLb?8JB!n`APaz{z zFg8dDD@r`b2_?&aOJ#a}Zt>vL(DOTz3aYECCKOZ(JjetOGQopP@W+-3)P}@jx4GCP z)g9NSH$ObNXziewMR`L?(lsd#3hAPYg`krZA{SB?lkSfC*Y1FOt!CO=sYlz;IFAc_ zOn?I+e|I0Yq>FP;GTPXZBr?Y@%a$2M&Lv9-H)k-a9oyP}fv&wx#|rILRg(g;p{eeb zn5HtoX&gw0w7axhcZxymAP z3#tMupoKAind^?VPr5jx-JO}POVddhF;19(BP#QLSis$L5zp15FlV*Hsxu-vjY@gh zBaSn0XXnD@I#K3g?5$+98-)%&`70)$)zGp?6cW|oe*l(N5(4;MNA$MiD z9(^$KHpup%CKOSE0eb%)g|GCbYhj^7{Pvqy#KyAC=|38tn#&4YHp_y7Js|97i($8qRn>1lqUmu{5Xn{Ccz{zZ%0&@$bKp8}T9=Fg&7yQ{;UFeG$z9vZIoFFnMd?BYw z88?Zsi=$y3INA>>J(v>GZG&xCeKaM}b*lx3{OCVG8c4yEViE%sgHY+QsnVfsw|E>B zRmcXe+Ir5|wW808C;uJ&LccOR};`{R7p@qaBO27H90TVDr% zx9YeL@-*kQK*6F(=vy{%M&Tg37@{}r_S;hKRP?5;fzEIy;N6_?5j>v|St_iBYxJgFrl$RN za}K@P`oG&vkZace;P9w=|M%mg{=@qJBp;)u-6Jy4y1!}gU>@Vix&f(W6CL}vy*}}F| zQ>+SgzXDqSe*Eg?*~{<#86871mQ+wUx()sub4VOZti=AQ5?U{xU!ZHUD6(yFGGlWS z6FwzeA8nd53MZyVL`jr7WkIrkfb4%t%?(=!ebq&QqlJ!%WiZHG{e-Fo!3(6Dv5YZY#f*gHM30amZ{ZBbTy+-s3m z%CASe3z-^fTzjXE45+WMfQ7q8*>SCI)tt%yeB%Y9>jA0`7 zbt5bJ;#8+~`@eUp_|{6m>ivIkuwS+R4EjgChyDMPeANEGv$LcAN2lJY^x_D)HIhOx zBP!_S2a3SP%ENLoqIvHQys38!;h|jn%a9(L<$s(RIlrU>*bEj#R+NB*3fb-;PzE}v zeS>8*ZNIGo9ht`!sK2m((XGJMA!RHUs)D-_R{gN|SgG&e7`+XyVx;O=4FY?Y)@{Jw zFn*md9EYp$STCF{d@D>5V>~u<&@6;HCT&72gVz$Hhd}4#Ro^lt4f+;t^|v9X(q`B4G9v39xwl}nk}kj$Wb`_T4{)>93y9I^0Nbl{~|&$kJ?N672rfzL79;B zf+XV$I!Q@m6ZHI7Ml~ImE#QvF$MkQ*0hFy?X0jNghg_j_EOjih(G6D z^|!m;5e zt3y5Y%HJGbX-#cIj)@H2h$zF%H>b@9UZc?c_#-efdqhBqD9dN7rv@xPrr?=(mUeQj z>Jkf(y?|bSFRkKE1{%2pY|E+j3asCa)F*>f#_ev~wS$f!rgR-|s;tK?N|#opAAx?K zyxsuagxlMq$eUwsqL0hjG~d6saGJ7hYCdB*=_@j)DVP><$)rSHu9BO-BMYgSt0 zM<_5yRBX|cSI>?wpQ+-n&rj92w?Nw}MHt?e(cvV2n_`0$z7I^vwMw&~F?lu~g9wja zvXrbB_{;_SpHb*rd9dRSGGDc24|3m^EcZ2@|7lZ^cXD)cp%yBC?S;+6Td zB42IMY$W!rIEJ{lS+Q+|z7=3@BlE2Vw^HO=+on56d>g|4vkH8*t?RZzU!|Wvn#@-v z_~XfceXAGb=7m3o)VJgu^#;8DcPsPhmQ;NcHPP>@nw)12PM2)H0E`l0-c6;<;8GF$ zz+5g+z6HR(oDngViD-VQFOe%DE58!AZ2tsHa)TG4A&sUJ-%+u7Kh)Yf;aTJ!G)uo* z#zFcP^mW^0g~Q@33(G>160ZU_9@>!f-!&tD_EtbKxb76bNs(O{GW8~7u^uk%aB|Nl zM7D}!K%)2o4ya{#aF&T>m|~J8Y%zlz{!0-yLai;(Whj=f#D@8~X2GpltmS35W`*8M z-!5II%X*`sy`>d-PjqcOIt4vlA+gLGX~;fO7x*a4J#kxN8l2k8$f&ut@2~e89Nx=+ zsL*cjCe&J|Y@|eY!QEK3bun+`H~e#Q@B4Qsn{F(e#*pKs*=@Yqjk~Gq_^xLfG!6Rp z-@pY;Y~;EAb4`|8ZuQ0oeGA@m%hBC#-9HTcN8%DC_E78PPsAWTchKb*^w}c*_amME z+kf0UsGa{idVKg0|NALEw~GI@Pc{#K#S-$tm$I!dMPwubp9%O>jCci?gNl-!a{Eq# zYsxi`)#IcT5P+rL4AGO`lOy;kF`p25UOcQtFe`pHEaGXefkiy+-F+H7u!-M)1)DH~ z1?gkfu!&O9#zQRUEIy@z=Wu!MH+ekK?(1SMH54e#D~)3H`H%;Z^bRv>hTpwT@f_1c zjaMlYp*XZ3X7ynT%QZD&qMj1^x!2>{_j%=*5k#U%PDuHTypFl?&mOBXUL4`O1#+jw zexPaGM(x6H)P|$RYwJ4-+eILMok!}1P;M}l-@q+_&)!jcQ(qhPOl??0DU(&V_|ofw z_lf{L=o30olyV|U0p;%&kc&~Qhv&ep=?lQ{m&g_8s`!lX3E2QgnKHf|Zdbgmu$n4n zj+BC?vP=|)!&5ihB~EfL>19^i3N^E;GLj8#iuGbHuS5~-P`+u{aGc+NFL%4^*)Pl5 zxVsC!=&Jfn@qm((IaPi4Jr$Di#dA8N(y!L^{^KybHMQ0ZgozKTO<=vy^gL}ht*T-; zj+!Fg3TAmBIhJIyF#O*98a7XZ#%kM5hSCpf%rw32tz%d#)wQfXkcCg=Gn4*k=r8bH zh>J#l2`>%Y@Ea`1IhIp@)NW3f(B1>0#S{+?k3>H6vz^|CbF3dT4?r7m+n#z-0Z{7r zjK#-A#dV$jq}u7G>uQ%am>ahPnvPhWTETvZ+BivqJoN$^NZhtlxwIT^yCCkeBuaIy z6tGFPJFlg6W3(BAY#1KP;u-<-Je71tP7*AHYF06()i;`nq7UeQ*PKbOTx~K`*nyX< z+a;W-beHXNuV{Jx9@WvU0RTHm3qg&1OigqXQ+sfVK4v+gm`(SbU~ex6<)M8?~SGo*Z?a^q%%Q zPYw?DJ5S-Cr%!u->YpR^&*Szs2v{xw{Hb*np5SOoUZ=Sru}Q++M(snf6;Q*FR>OSS zs{%E-wAAMk?q`WnN=U3zen%0>q`m9`Nv`HX)Pcy`8-gO7K9Y!&WPw6mqQ|I> z+IuVbRDdv_l#-M;Z6yH;a2)fmht42wo)*$!ktp)(HJe0$0&VLErh_2QCLG65r(_5D ze(+kqDOXH?*d$2EoFrd2NvO}+I#uRtmWRj=BR8Vr3vW~QSJ5Lk(XW%$>e1Yphb;Oi-=UjjdC>=l~L zkG_8Y-anvb?w2|{pC2_=$uiEyweCAtPrU}f)dp36fnr=7j`1dE;icURXGBa%{JoUf z-~j!}@2nM)8pwA@)8RmGV`T$AxZ-S*w>Ue={Gh7YAp?bt|Iw5OY zgX5U0XE-_L6H#sgh#&Wbh`h@Pr*Id@?NL=uIjwEevvj@{VXC}`lBDxtT}0dteWeAS zAX!L%kiq(vZ-jY8Q|^MhX&`D_g%c?`j!1h25=es5to5I%SHJx>T>8!!Q5v{Vqm{*> zJI|A(ehj7Zs-%+4NlJt`=WIlZDWKEUe@CSM$esb}X8M{y?>X4vl_2IlRuU`4$tg+j z!WbBbsNZuXnhjDGTxs`oqV0Lvf$Rfv9;C*9Dizvv6&%lK3T(^0;snREu|v!mpUWvH zV#<t|Y zUT-TM@mwFQFL$AKy1ml6^Qq(Un;I9<+Re$jsMkV&YTBe8e}S9S<7Jyv(;a`e+oT#HJ#12cs7(r? zIZN^xdBO6uyn<%x$+@0~bIiLuPu<-}I%$gixYbmCYzL~;`}IvR2=rnCtIPZMC3&k* zw9#+B^+NdX{d+_bLC|l%y%`SQs$bA)!>3Q5_DWzYo`5ZEHh~gb?@W*jm$FcQ^;ylk zH`M249$cA-;F>JHZW^tw$%L}3UPQPQ%WFnlmb=!NdyL6a)D1o_&6`TcI-Ae^TYGM0 zgK{<^?X9X=hnnoF6$nkjn}+6DQW7J>c}=o@gs*8jW}O?%({=;o?M1x1n7tZvnRv~} z)YfK)wrX&KmrYY*4VVw;7e-Tm)b6zF`$?m%XDQr!vnDOYaAz}8O~k9AfD({$q$KVw zDL21Yv2O{I%oT3HbI+ops?;FyGzeUNNa7Y&3XdsoY^A7IX`rWGu}SExg;1hTEIT$( zzFzcR#~4asTBS;x2%0OvJ&2nR;%1|`Y1YvOBBz?$Ck<|Re?mto)%W~=`hf<^+>&OC zE0o9EyC}7U7p!tTd!|C~s;t5n9kDbe5!~`Grv&}r0s_(T`578PI4Qi*K3yu1YNtaX znR&s;fcID7C@3K=nX>4fkCJq#kQB-oll=iB}yiSj{uw^=Ea)+eZ6`w+DDmIAyT| zbq7e_R<@ot8uUK8&%U|;3o0oC9`C}JilVi5m1N1)n#++c-AR@+y%j7^ln%DPTzRJ5 zvPUmXLZfnRas!t6F((!1OLE-%a2vSjX0TV}QZ2Zv?=;_v!xoHx!1PD>PQoqh+Ov1( zN##u`cePx3oG#flNzXa^MKiojX?FA)XaUyX0;%BL3-z1fx6RrP<>x-8{_y!opLO{k zrUY|2B3Rzx3g|WWzaI4W2i5!E2EB*;51-^y=f}q6_`MqVZxj~@%rPpPCgOWLCD5yH zj!)q6{bXCRbJ5Lzx%)k!_t`Ic`xqBJf3$2!e}F+KG88rB9%^dmCW(x;zW8csN8VTN zvd!|4`eb}@BemgjaoyK+E!{oeBY3N&wN;+MFM33(YiCt71t9N(Uin_ zVlB18{2Zco(ciVgzx~#3Rox?{nkdGSoTdK~X?LE|feRECN1^a~qZmd=;^hiuCy z&r!qabJTFw`F3-vq2@NHmK&$n#)pmjgXY8FOODsyZnCW-b&Ip>%joq74{hRQD3Nt* zM+0g+&fM6}<;S$q7Du(wGC%J|N4C+{$F}>uO&hy^&H3#r)D@2Y)qd$Up6S)Dnxh$N+vy0;0cd%^et%Gzr%Tq(8q_OJ3fv3w za`>!&?)A3A->$yd=Gbz*@}>t+pZ1>i8W1eD7xVZe=yMNc&fA)L*Ue*8Gd)$Ap|9py%Z`e6AW8IMazi{=|-2of>NS51yn#B%x zDC8CfcCYG}VQ*>nag}d#^RGHAxvL;Zh{N5J6Yt7$5mkN;+(S5pq^mx6Sj?76a zo9XN*nM6iY<6c|pHG7y-pXsKqb-lK=@6PMAPvN>-hVI@xcVFBjxF5&e=iY}NKA-2a zsr+{b_um@%Z*XvM(69Rc`i~z!$bX-I;?o%NTyy!YkPM-WcJaXV;end&WOt4@2!8IvFHqO3pol*Pda^4h97HAEkR zCFul?(`d@LD2CpruQUtpaFNZQPbB@~Zm;orXoKs{n(i`sa;LkD+|vH&7vTM|=fZtl zZFMhm;r(a3x9EYJ+-U}Vfj1X_xekB-WnWuVoE-d^cNSSJ_i|xTGca_2w-qfxP^Rfm zaaB=?ZF~_imtPT~D;U#djJ$wZuU7%e1PAR+t6evk7z!^1Hg8Z6^CIU1cWYMS zPm+|pK=u(blK%7zCGERQtl;ca&LE1jKW4`e^n1}3-57{4G zY(m$$ckQRVB7@5N4WW8&xNZ%0*++*q%AsejZ-hQuDM(i8eS*6h;annDK~zpv&hM`esEBojJVq zC0(CreAt)IFMdoL2ICW4#rXTUZu6t$lKh>geQjDscg*>UBrBwrTjYFQO4kfu!+|}- zj zRwU>p8Bdq(>YJrDCvC1Bn2qKIE{S~`#pn1%o1}t=e5b#|e5dcY^~t?=~X z=L^X=Y5n;3m(N~Zw3J4wcs}~+f8+wil#5n4p|T7A>Q=PE(LZ<>{>i=r(pr0_OEEnkP^qFXdMr>+@G$y#K z%W?M0?~21Tum4xij!$1a3up1Y;8?x>A0Hm{4lC>b;PHd~=hJ+4P~!|}f3<7^(g~dt zffg)BY$OPuYdv}a>zukDaAk!NVq0UzRRxY_G~&!2uSEa*fBrAM)>DQe784XrmD1!C z^|00Y=QmC`9rOkV;gfLx?e0{{ObokSI-7)IN=Xufj5@;cgmg{$z!nc^ssj)qn(n^~ zxk$*@;{?k<{F-A<_FDh^f94WA12}U@k=Ec)%r%9?6B7LUzlZ-G{`(qC zMXdlTnOf@+w!Zr6dp08(o{-^JU!nhIu@n;azqVRCJLqpYjjq8L4nr2RCq|erW zupC7=MLbVk0$P^By=z{Og$*mFDZ+}stM6>AhM%Re2+;+BE9P2ne=Laah=lOu3x$`& z=)0W8_$?&lp0b;;)%uokG*dfK#AX>MQ>8o2NtxUZ((QmLC8q>ge65&8 z;_VwJU#njswEmnEm~4Vd-^I*hq2?l&t(HC}h-fM#P7+15R68EL?Vt<-S1QibWIizbR!6a=qNd2 z1OYa48k4vs8Ty_iGc;O26LUd0+Rf91UK>Fp7rLK|&a^#ze}zt-pXtt2^RhJ|Dd7sY zzJd_vlBq=z;Urn?g~(1{gIe<-c9V7xWP48%c7xtP)s{lQbA9t*u_irzbkLPpT#K$n z8TcLD-BXQp)THF5#Zo08wT=@wps(bgWYIw*rsk&CxzL|(gzCL2&72xR zYS7fCqAx55e<6DIjtUsUlVh~&BiSp@im$$!KY zj|o=;ldHLrY7n}AI&S{EAhHp$V5|sw<-#3oCZ&_(f3GFa$yJCh>rl-Ff~D1Bv6x1T z9Ir4qT35{%`F~x@5Ok#`QzH)~BH|SMh=>Z?LvP$#D6LU$JSD9Ou-wLp9`@~AFRiCQ z*G(s7!61-{2qI3P88{K$9UP=gl7OXwoKmHoV=fngm&-+Q>_D(2ht;+hwpuS4tYPdz z@qklQf7XGT&m)4Kot$1AmtJXpcpQUrVtV|M&`gGtj0q|kU$k0h+Myf66``=XsGdxS zgmCXb;A_%Bqg>k2n&LS@IDznFG$JHLI9AmalPV_g2n%wBZm=kZHf*)7uC7K{OqGVj zR5fUrBBpf9gxb@U+@y99F>FmO#ZbfYK08M0f2|+upeido(E2lfgCchKQVbCK&2m6^ z=l7~&Ki6a-%rqL2*v_DglZZf<3LVzdX{o~MmB2HC$h(Z{BWWmBQ%gyNxzOz1=gX+h zAiVy8CfnuPD0!kFbzSkFErfzV5E&?~%<)Y)v~>)uPMkk`@pXj5h|6WA?MgGLwp0c4 ze`Lc-I#ZRcbWIje$%f@Ni_b7l#WYu)Wp-Xrvz-88WeYnB(A7mQa8=JuvsOvo(DM)~ z>jiOPHuUf5WEvdnumX_Il~f~WOax%ChbgY++IYCYv*Zf2-cB)<>;w{ulk0-P6zj4r z=qP>mY)&|bVuH;I(7FP`z-I+ftDL42f1yN;JXJgzx2B3n>=B7XL{g<)E0F^yY@*a` z6X1Il1XFy4m|=QpT}}vF+7yaJ9!(Wu1)p$~=ChHOPVGtbj(s&FGsYL)D9ceoXG)I2 zY1}gB7U32?PT<5S9aFnXu!N2Z3rTrGB*Igc#0p2Xr6|5V61Q4kp_3_zt`WsH6rl; zFmKK}rbd-T6OL2Z z##)})r)2d(V}fPQm16QE+|fSH(?}aEw5o%OP&YZ{ET2qUg;z8*>&%eCiO3ju9hJ_n zTS9O&1?w=J)F4KoQ){FEe=}4o_DA1NHEba_i-S4+My#e^ImL=*soiqp!BXp$|c7UO(!KPe!*n2J$_xQirGB% zaH=`?N{cqYh+3_}R5V0a|N3=591Ox<(7S52KA=}lK1Uzmx?uDHf4PI6tq+0s`B3_! zzO_EUHa6^b!?0_kG|>m-eeH$)u!lYXRazuaSbrc2-Q^-v=*%Go_*SS=7I__OP$H`J z!P>u0nd&F>0SU>~ME!uS+U8Z5um~sZEA+v)o1MPAK&BF0yJ9p|52Oi25@n$MUI_nn zpLF`YUU+l>Kl;5+fB*3y>>uf0gHHdc`t?NrJnD?&UT-+;J^?CV@t3m!=tZShw=vP8 z*q(A%%QYi>%=nDNq4D}?Ey;$-N|U-k^8j7ng4Svk7=uzKOI1>}XWE1W2z|4#Qey|q zWM}8|1Gw*4+f@vuV1!B%lO2J?s@M^F{rtEpr3t2`G)}C(e~B~%wVy3ArP=x;Lee?q zEQNU+>VYU?bv~dGV~H982>Vjq2;q^=D9Au+RE=`sXvO(Yced>O;v88;syN40oX6ER zFzw8?a-zOwsIb+kXHIHKDaw+3qQEmxUG*#t&=5Z&;)BV=sT%YCH`NmJLP5x~WT8PN zo*72N009X?e?ARkS=uW^*y2b_jM7$U22@0al;(Pw&%oR*v?0(Y(zpQ;fu?w|Yr^#be@C7dIa9W)dXzSxE)oGHF51xxcoek#?nxFp^b}#sTOx14Y9cQ21nn}8 zF1|nBGbZnX4}VoU&|(fNWX(-a7L&L&8Ee%6mk4K(EEC_|>{m^r|Z++$m~ zHfLL9>b|Tg+J($khO8D=8RTn!o(}rGeFgBf(v~)viV9XtM)|}Ya@M&XAWiYQSq2qX zO|Vfu@kV|ruFNHt@G(|$PM{A+{V;*@OOa?2mZF;1XMb-wABNiQ@nj?Lwd8bV_Na!3 zrjyrMj3sFgfnQ$ad_vm8_Tcbe+MOB$g~z`E7q!Nq!m{llbb0d(A#NR?6zp?G#FWI} zOPLK$AlLIbXOfNbad=Hg_A^fCoP_rF!%(=~x^`Oj6cu-cZ!-7R!SCM(^qVP7$j>x} zoxtv^;D2#&z^kEs4g2o}(MREV4qK#>xU^?f4Zn-;L*@z_A1sJbb ziwfL3CS8p}2NIioXB0c{g#9kmwSZWvSgYH?uz*djkc#Gma6jw;Gxz14c|SY|%|4=~ z4SNmdMM8D%j%pjMP-0w;3QwsH(k^wvMdNm2B!Ak!Hlr)tJHqRGXdJEL`QeGMsxg`< zsa8pjsn?Y62d}dU$8pmfIEGzFbr97ejuFaLho>ULpv`}Od3oL)pd6rBk*he#(=cO> z;}~I(Or7&jb@_;@P1NO8D6`yhOr{7CShp37DHhsY5|fM*$OVQ3O(TM?p9nQ}gT7A^ zi+_((Nsmo}xK#qBRxcz)Hk%v*k2**!X-V>e$N@T;qLN9ugtS2vT#6W4?@*1Kg+XCl zN-OrIayF`!=*%32d{&6qOI}qwmtc4#biN*#s%jc5gbg0dCd6msl1kC!=|#>%4C7mJ zih%km>{^PpWlcEpb7gDi}yylN)fFfQbgj@Yl7@yNI0jOon2h-lH8GE4#g9EGLo;RyqJG2&sl-WdrcIP(gG{&*pcD%j8 ztX(0?wt zW%ojKCY`6&`EiY6VieMFDMH<(282J5LOg1SJlO(y8MjHk66(7nzR9_ep5)mBi9tUF z=K*+6%taG_JHJ>33tktZN6%O1)H|UVwc)F(W`HxQ7cLs69YOU5vLc{S z9$gcOz`F{5DiGo93954NnrGN6q<^liCNP*VZQPsp2Vf$~B~7~Hgxwha(lrE-aKiS= zO(qZ`D*2kM^_iOD4}bQIoc=9A%ykQvzi#PWu2pCBSn7 zxr>Ps(Lr7kQ4`aW5)zX*MCg?+wlEA#Tl*joQ^y_XAPc2-L%_K;Lz+?7TjD*e`9JTn_E$kBv1kfrZ43!M zl$>n9rA&4Z2`6Dky~{@ljTCTbw*F^Pe(ChX3 zp_1I-5NF5oCFqfU2>%VCkaiAh^MC)Xd8Y7yrlwwMmCUW1WDz!xh?Y+;%1NUd_`M0V zLb#e>%G$u|*N`!y%R#@JFRNHSj5jmFP1Jm?>29rVp` z_*VbARsSVT<~=a(m48ipyI1h=3NenZei1Ca(i>B~Vaw3eOnutw(`4t{5U4SHR6S8z zd^KqXEI+G*9Am1z3+9n?7d;ZTv|XBXZug{=#r~CIz93wd4VY*T6WL^wK3hH0zZ=aZW)NXEF0SJ ztkAyoQC<)`tbcFR#$;F8R_{HhZ7=OU&SpeTNiNWjaKRbl=uOe5#VpNS!GM{9VNB+F zzS<{UoYC&iOxIPSMZ`E^a5TC*?}r84y(;O+mQF&&yG`BNeA0>5?qw>)SmNe+BqEM8 za%N1w%`D2j#defuL~ z=q0OWX_(%b4~<$$>7G@3bI*vb8f@Z*GrEA6sdWYJyLd|Kh??9QOAv4CH@PLUrpibxN|u=FeiIGUq*0UotDj3V?xXu% zK<|noz|-p8?M&cKRQxYCB6xLc!YIo_DA^#TT8$7pOXJD;Ym}!tWW=6)sLwH+x}cyn zm<_p267qI9UAxyHtqsQ(jdyjCse2EdP^V{;E`QmIl1;lt&%G`BLsz(mPB564xBND^ z8=CFu>E=?{23+Qant0_SJDv@|T_viNu$0Nd@|7}f-r5sc{)NzRIw94kd_c>$vG2ZR z%^pP27O$%s>0sf$0@Ssa>Ev>m66D-j`j`U$9G&(yX6^&BMFQ3vl8QU)j8z;#VJ3 zr7x=o`j97_pVQrJof4H%bw`Ox0b4wlyGG;BpJRiSiNWba%(aOa>7SKwE%o;<0~JH?V`g z%@Sx>%-eN`t_1T4RzDok+30rrE6)hvScE=?XiUm(l3s%JU$7cDzG%oXj9Be(#eWIE zD}5yA3SAQ&y6*>Zr)x5j zl*3nLG=26i;{+~M7wzGj_w8%4Xb;=LsgCj10?Oy*cz*y<#rjxY^Q58MbDn_p$o5oGUVn$`q!B^8 zr64>PdA(;5xJ!x~T@gl2)u7<57*Il(g#(l}}M zY8=koAwlUpyYdD?Jy|M9ltXw;4(9;;Y*l^)NVp2Kz6-1o`M#(jw>>zfTu6iV7Fd(U zFwp+g;hP!LSZtiUIvNA%!+*y@u_pxx?b48h*4Is?V|$#KcF}*3c&|(eC*E0so<(@& zEXG%`2(O^vmFJSWCK*Ich(#J{7HXoB#;&m4yK$=E6UE2i45o%*PB|tEyS`@2lh7{xt{8lzy4ej@J^M57Lx+iQ@oeugn z%9S|~3kM=ohOYD_c~@o$3y6n_Lxg^WL*V8P{WADYGu95WM|11t?TdywF-aEyGLNZj z`%O3JCrr~hlPqG%DUl?S#VoH$eScRiD+%xJsD)X{00+eY2o6{nU`%h3<~C|1Xzb3x z>-9fcxQuQ(f*&I|$bSn_@p=x`$aj|&elq&)72S7jfH{?6n6bIkLo9c`S9eFP`lT!l zJoVEhs;uInK_Sm)%2x{n0v3RBTU>JwMrA(+tkHfJ24bp!@8tUhh>b2-55v|@-gQv*dJA>@W`C%J!vP%NI!@?(O#}ku zVys0WoS(h?PPYBMH@J;$SiI7PTI8Jlja<}8J#L)8x86q+H)Z7b1r%5D-8 zPY5Lcv@FL`XQH+sXGe{~6a98c>_l%SN-inboUL=YJS37KA|xIqR7~|wr>VF+Irm7Y z6R_f*{xUUBdw(lvF?&5uQ4SCs`XWIBCqnICR3I{^pePptibCy?R=QxkA{RH@5Z)g4 zd!4p)H~E-nIwGe8$_^}Ka#aveDQ3+lVT*&t%c2pR)^)-rYF>O#a12L~&=nhxbxjI_ zBM0Vq)n?;yyO481F;gWwnmc8j%pq#pp@7az*kl43&3`U0&y_f>aD+Yxhoe&}V{&Lh zeGq4wAVkckLWYLCvX`|KUH zv)T)whTI0iMUX%peYvB)Be{H%>#%e9hyrx+;_@8rYG`}r9DiM)m5uYUfJx99=N$uTtI3XOP zU4^{%@|+1d;RI?c!fFLxk(dg!tLUx0dzN4!X>>s_kEUo>HK$G)nE{K5(Jr)f&u$qg z)>cK>Ss-h3rWBY_LTnnR5hwUsc=DOo+eS3|$a@`R#wn?|%eTEXBz@d{&xxw5H$Gcz z&412!hpo>MvUKmWB{tst4!7C+9R6Evez4X;E&n<}_n`l@uOpV#V>slnOd_j7vf957 z3-oJyLE%==?7r?nJJ{FOQ~hEqXs)Mxa6p{Zv^Isu36AMjGQ(h*$ndHRYIB*vuy}hK z_Q%vI`Zb1JoN2XH?=M77_mLkA)qvu*v43z{XDUTIRiJ;YptrTny8r05^^LuI7~9%r z-ABCIvW8f7`#X*!a!xp9aRUN)i!%LMEyam?FwPSL*g_G7kIgO`lHg1b;do?{AeLz> zsofj2)50d2jyoR3AnHoOXEZggNXuO(X!7=ZH()^j+ zCB2z=UxUO#3II}fr+5VqDvl{s@;fADGQ5en{OZ|d^9X9_+n1JaCI^Q{k2?p4M^Bn0 zcA?-Di;LM*}(!l)9{0>BHXKaH6IcqF#)qm82O$hI7 zgsUQ{*vvds4c54;_IwZ?gjzeRS>iSVZMygci>rAy$OK2)DESlS6zko?4GHWY));Ct z*Sj17Hbqg0PIQu7#g=`dbpAb_r-)F_g3I?M(=(&!(d8H;debRTbV~S;5Ca2aJ!`@l_n^e zz5m)fwgq+-wPK2QFhrnH3FouaQ##`;B0}hFPBfhoPW6Ermq$7B?tiM*X_*!o76N1g zR|rHjb5xY1AWFpch8;Ej36Y_8*T4jUVR+>3|JJu~Kk8IyGjj?g81fEBGFc!j5FLZp z6ALx_K`>EY8FOA8HCaRS;{Rvw+MC<9wfz3gPk~FfYbzsC55KzF&F-j{(rThuUP(!B zC+(;-2}x`yf+Z+BzJH4Mv+v+Vf+Q$Xj-$TB&ZHIz92~q3fO8Jc&w2n5+g1R9&5RF8 zTz!Bc1c*+Xqyg9wgqd7Yl+sO}U|J{Z%EkHl=@~oc3+J~u2#yHDm7txwnDxR-7ISW-SJKcNV4lPEnG*`Q8uZ3u?lSFbas~5) zO9LP9p_@@$-h3(P%xCejD(s=pbSX{f{;m8;3$py(oJqH~+`*Id z&+kbpb@1-@T=p#2{y2`KbwM|+Us=J(B3(f_Eh9(cec=Zj!IfUu<(_|%XR4~fSmW&8 zT2eEm0sZ1rT)J{S1o1VBD9EIqG>%NhcjFPcEibG0h<~y9v`VK<8`&Vw!p22|p|TWl zq%|lXH*L$ep@*rDC(}Q3QKK03Lg!0Eg4r}8U0oJST=B~FC4A@2cn{&7OKSAJOT^N{ zkgL;EJWqJ6cy>NaBK{-K0X)X~1V%iB6*-?JAyPx+dAF9$8!BA;gw<_Vti~(}plLvS zu}Z+w#(!~vc+Fm=YL<=+NEK(4C{LyK(~8&gPnu@z^l066grsdOS!Nhqk(h{F^t@j} zZ$+`0z+KC1waun7I-ZZhdxV@{8*%c60AOtJe6>FnVEZ#aCHU{=0!6F9`@Ax?zK>_K0TEmU9$BJwh9=3Jn=f==g2zWq@m)fo<%v|OxbpW~%UQr; zTG2xQOOjYICc2zZm2$}u(1)%cCp%!CACpU8A4$K?qrA281knCAz~^8@Dj{xHuK z%BN7mK%cPPyVGsd-)62_){NKkD_jE@yz?~JkY~x>`;no0y7VH3hJGL>|xk^^s4$#Nh zl){iEE;z2H!%6BUy@)|d))VN^)0D0OVN-JWE(n0=A%Gn~Ws~RnyY5Wc-C{&CCf> zt*gPV=UYI4JIj9Laz;p6h&2R9w0~NKe($;RSA5j+pKCzr2FvnrgYFEUycU$m|7QCv zBmdjY^X5VR@8z)qmwQ)sz+FpAP3+^YyszdOLCFCH@bM9At#MuKTH_m?$H#nx9Up6e zZugIm11qwBm&eD9E;hlj=36V+5#+FTCpc=FEwg!;_h0a-Hvc8{@Ds`?oB!nR%>2JR zJw43-eLU6rKc3)?>(R77F{6*s4rT1#|LxXc|G%GS*Z!YzZ=nY>s(dY^r2pHkcE5r!j@$Dz?aw^M|<6C|&}Pz08<=G00J)0~qhr{jSROM!=BH z!Q*302mo%Ia^qMVr9qA3jpik}>#8e^_AP+4bUK5tY4>RrD{4YcgGyvQ~cd=xq z9O(>yv`ZT*8>^_Po~@yj&6I7Vf+k8eP`X`~2uEb^r)@R{fW9N#AhJW!M-I%4$+WqU zaT2uIZKJKFrVAvA-8n#ET!PY|sV<)ouu|Oe zayixQB;(?lcX!cLHMi6+Q%;UKCHYLVm(k~cfBxPg?eOgI?7aTV;i)R_0m}A&m#vG; z`hV8EJU^`e`*_H_6>?8>H;>sa_cRlM@*5)dGeRYw&9oecEd!S0h#VO?U63m$vCkMG zNzhbbzzI>n(vrWbvq1Az;6eOpur;KpF==?Ids8E2b~lC6OR8n${|bpn2k*uf|KkZ#h|3$u0+g0DT?XiW2Yol5)V}D zMn&>8NRKEsiAP$>Kq{(fmU@O5**sExW8(RKyIyEgP~JBXW0EEI`e6Y#rf7&3c;xrP z5QW+iy8s0JJ2yl=yO1F6I|U)Sg}4rXFar1SWj~hQ_2-u7xwjq-iG#HiIt<%O!;jDw zyJ{P)R+jH18+tto*HG8e5#p=o%Swx#=l>FV0Sv1y{q!lD{}&h8`G4AK9sGay@@zJZ z7vRoab?BwFx$D8`A%HJeq*?4G?0^>yy2dnKXp_LBSH{TLvOp&8qw7=1wm1fVB#Rlt z^~N-|0l$L<^j53hZr9Jx>t|=Wi2P-fsiC)8dR=d~^}2q3uGjUmGhO4wD})p#>`Z7m zLl;M>L)sz&Cm!^{NrYGmVAg7&87KLQ#kuXg1W5w$2~d)R7|`npy{|kAM+w)^yE*;pC1%xYfr_jC6D z&htNxhxKgwzt&mK{&(7LAN2owd6ZKt34Vu77SO{nZn+vn)*r>6sESD}x3=fL%dPY$ zLFoF?Jk~AHtKQ&itb-G9l3J*1mG=p?+73H#xl*~BuswQgc1$)KYH-1S((Q``Hj1!q zc`(c<1hF-TMuZHCpVJYGv&2ynfWp8_t9^cvYT^Qa5+cHAV!X~B4VVn)H}$E0FLoMC zUwf4=!nr<?i$Gvl%wdF~%R#ukp&M6p&g*A%>DWCxe?x~L?b+P0me!&5> zO%Vmoq>9TS<>nWD?T+GqMXgrrhNhw|!IKmnwTF}#*jVZ1r zymo!(@sajW!|K7`^oI$xh<|_Bp zsVSEze^$(*%BGHpPZ6zP`_ZrvY_|MFxY;D?@3#^L*(M+zM-9+e zg$vAb8HR@z28(v931d@HgC|fDRADD_QoR_<$8kXaP zw7yw$N|prA&~=t*MmyX%%gn^uNx3S?jD>RL@T5=j?a6hCH-u zDX$^~tk_uo754+Xum8w_@%HU`>_4q+{Ll7T>-4bx@8yx#|CHHR^l@TsPn-@4tnE@z z>TTBczuFY~Q$IV;e|CUk*IW(D_bbSNQvL7cd3OF^oE_{xdwI6me_4F}oZf_Fp|=`M zJ6MC%3lUa-of?aC;raQ{3D!n@{d==9#Xf)%g5)qR5oA!LW#stv@+>hbZgPJy!W>jo z$^DknShnZnpHr+>h3EIQdE)%1=Ip8(;F9@&adDQN|EK2%|BwAVB`ynW#0Q?Q;{yfsD#+K~C=WYn^EzdK5Ze8C9tZ1njKo@ijk~EzRTX(Q9 zE&a-EoBRafqX?Y9-xR(SDP{$WT$9X{jIplzt2>fEFY71$XT5|i&W&vQQ6Gv#Yr4VR#Y?6hki-N1;@fED`U{S7Mk=Ej zIh9F&77bkpAVLAHqXnu+K|(;6NwA!2*HL6-Fu}6KbLU_I!HMff8Li90b!wmqQbZR@ zqKh2SRFhC1X-%*ZA%nrel0l`e6_<^Vk9GBTif;e-nArB>0aT|34n=3HFmQz_vQ}e; zK{|q0f#g!UjQ}-<=-IKd9rHHn;i4AFRCo)2dak0e3wcq326JZkq1&KojC;$C3=D0o zrH<;Q_`|z!lhPGs^h^b%rm|ElBUUM;l~Khd#2Km-AVW3sGB^%nraw#aN=g~2ShZ$1 z17n=m?&h(8E9h^Fu!?u}>LJV5=csiRs@c4zw`3sZlSu%w%rCM~+shTOY3)|IayX@b z?aFJ4xgRC~2Dt7|XWibL!L{^z-2FkN^iz8x(#5HA>ckhc*auUcsmKj8Q`xF~Q2)sk z>$wrK-DlHGv0@bSlJ!%4&ViHsE*L6u0_ET+Y^?+~mZp`3rZt@4DV7B%b9mhE-}GWHlyt7n3xPUx`&9}GJLmB z%}`EFwFX!*R4(EE4teVpq_1!%=ZM7j*Llip>4{N#)v{d)=u2CS3GlK!Y^_l-Ra&i5 z-lX)Ed6V;S-jdEQ&mvOp$>A&WJnj7doc`bK^UGGw|NG*g|KHCeU3*KfenQuON9L8~ zJDHslgZr7d4z#VzqGpmkJHwRfe{^HaM(KR5t7uS1A7xIPY9ML+R16(}-PPKQEFYpZ z_8xTg2ALk7mR4pVCA;eHr_-D7DZ&{BC)8b4cYV6ogF4XBe69msPdv*Q!nr-eo4 z?f^jmd;*j}o~5s-KUWW}EY}BrotKi!o9xNOzvuV5+@FknuhE`?{#{2ixLBG6$eGcL zl>Aoas}S+)wR0DG&KS;xCpi7h1YioM0iIRA2Q$A>`*B zR@!iI{klKywP!cu(LaBjU5&>5i6}JhDZ;WBoEGz?aqoT+k7(ql4)8{QF&l&HZ%1#h zd$XI-csiMl`~UlPFz(L=z5exdF#WNZzwHIed`rtlA>Y&C&QX(e&2Tj#5Qq>3QAejaP(b5G^hFz z2k^&7)}%^>VpgCeqJ#*4kox-#!&1bLKfgrtdHwx{8xGiS-WrdO`W6;vuSVlH-Dz?4 z6J11UjeGscba36B4o26r$-Aprx7QoXgk#@y580NtL^v`#-!#9u&~xs)hJz!6RL+AK zn_puS-`V7UJh?e$fKO(Ec3dFJNRvul`kCp;{V{7&xk*H0y;iM%rZ?l!tHH28yY5ea z7>!>mM=~O35 zSnudxo$$q?rq_WUAV+#+%i4DDEj3Z+)A0XOHx_LLAs$VOfdMmr9iUVo|_H~Q(txuaKPLrB0}pJiiB-5jm?7(-YscFB5gD zLyA-wC7RGp^#91WUVCvbVEbg!=ZH`Cece*dOB9K4g- zqt$GGHk+%$5tZBcY$3s=;uQy3fk3#dSiS>d=aU%63D*HM7bbPrA;F8=SUt=*Nf9OcP?v>X#?ed zlo?Vp(DYO}?oeqYXOxxZTl$QvQD?n@wD30WtgsQT{rHZVP_#Fo?-+4}{OuH{XSX~KBo`W%PGW?n7wMe5w7 z@sQCTO^HlA&hV%B8d}p(xf@bdu4m&NMvFWFkURiOCzCEvAR2Gl2tm;xB><0 zzXeaBBU_=OqP`Gk zS<8Z&IZ64vXZ|F0{qM$_YG1;C(7T3cv2CW2t@UJ*wpV5M9SNbziYLr1X5WRD4R2uR zqH318@5bG${_Lhd9*nkF=3z^vS~VJ}v^4jeLCa2; zA;e4MIVA+L$;50LR zds>%9Y$^!bgndtcg|DZ#Xttt+974eSQJ$UWf4noSeg~vv{Abz7@MT&95@W&u7YR**B&uGvLo#$!N4AIYO)aQ+;^XGbYmj4m*V8~;GS6l%~ z_kS0y-2SiGI>`ThJgWGAQWrBhF1=9U6iJ^G9m!N|J6P} zJ?#JX@~G~A|Hg8FbhWN9rSZXXL&nN9v~1TG_Dh52F>K_qP@pg4zf9@QO+I_d&U$B? zxeGeNirP6pzqtG+ZRPv>uHK*j>^%Q@XzpD^K_efSpn( zFMMX(>uhnek;=i^I@_#>1Vf+_Ubad<@$-<5hNz`*G^85-q?t0Y$ zZ>UBm8-B0Ad??Mjf|6;3BvJZD78Ny9IM@g$A7wzVY_zCdV8TSf+@xu6cRyx>RN>%c ztds$>dqQgVmDHaGP#8&tX_&dP6j0=sAU+0vE<0q$4kzDSoHd((--$T_?K7}M>kzZ6 zEi9L{c3kM&uoHVjo8Gx&y6A5$^bSw_RFnVa5_&5ZMDz(|l%4<2bLao&W$SSM-^+6Z zZmfvxl_(k@>tb+Ewotcg*Q5K}z_LGB3y4kah~Fz=L9sEwOXzuE;h|eVf&5&5Q3oM^ z^r#mD6>8FZ%XhRR;KKzKV&EhQ;oSWQ9j4>|N6iEy-+Q1jwF1Tx2w+HrA~ZF#H8YYYaxKGsYw<2AB2S`C&iM8EzSwDX03GZ6qoHbz8B5T6F> zngavqJIN)tV?Lga>A~#ifTfIeg}*7TMg ztGfti2iBO>|2<)$ z49cbAmt4Lj6SDc zVQyr3R8em|NM&qo0PMY4dmFd5D0rUvD{!cNuBBX7anN9!`{vxMNKT>?+tIe<>~q@3 z9f4IKiKtkp04U1bcKW-o{nAhOz5m<&1^pBH!@^Lgfs-T;NyL1y$SPp1g=t|;SV(Ak zO?VtcER~!kL6+c@bWf(3%W#2b$v@r0r`PNCjt&msZ?D&@{_P+2djHfvI669dJm?=B zJ^rU&|L~xH@K30BAIR5zav?GQr{0}$RU7v^`QS8V5=+WbF>E0eVTltq2_r0JLPE;A zIF~G-sgO8HhR9799*f4}5#f@KDT#+D!(66>Hx`Sj$P&B=QamF=^jt$lCwda1b2SMY z>x~JPIakozhwa7^*ZGJ9bHW8>=@32o*RT8GU=a3#-lN9ioFoJbVhCwm4K19}pDe)n zpoO!{{Uq^(1cQU{Y1nVYBqM1|(ufiGT=Tt7x zm~mA>4akf}oT+LwA_D#I|M|br4V6=rG8D0xplFKI2|=9vno~~V5It&5nQF{b%1jKq zUDHn?Wi1*hx=eGh5JZZudM3J!of&57q;*XeH;l*7ONNSIM#QwG2JffFv72Fg~pgv#>^=W@!p>f@XzO|9v+R?P#!avtfiwN)on zPV-S1v6+Qp{|>q>xyZ;6;VesN1WT;thIY_1ljd`;|5f}yVbS${;8?@|d;5byjsN!s z5B&dAd~U_G!d2{wPKc00B&K+9cr<)M4hG|=(bM<<58~*_ll^i3@%ZUMeAGKQh@L(^ z>_3S}+<%JW-qXQgeDHYmK7!{*N9XJ*@vv z@!3JAWQ_AfB0c6HCD;wIwSz9FR3Iu4M*ngA;(0J;e1@eYG4KLa;FLrO<^;_#r+AbQ zfh0pCf-)rw#E7Pnfz*K{nPmxwuxCP+>R%2;f~Ck?>@@1RjGQA$XRQYK4PQYAZ?a$#$ywS&G{pjdZr2jL_^ zXXo<+lyN%8vVaI^)LR@yBvXBRc0NBsub&?aggI&LAd!&>glZ%hl2a@ZCxRupk|~ge zK~p3b8I5q__bW-~l(Te3QYk{)oi7dEA!_$~A^g{U(&_hl;n4y7==VDP$Ahqcq<;-M z{io{J6aDk3Gmd+`;jsIpt%u;0Mc0HQI>Qr!vOGyp#u6GWTJYpNPm*)^Jw#{YmrR~> zB1kHA-3!w)p*t|2jR;3r63`ej~1WDrtIIs|# zAicDe3o-W>OeTbD*kwp&a}Uu4A?Td5cMGLYz`Rp^kiipEsAa%4Dn|g@tOayk%+Q!* z30tTMiiA}A{AAH8o?VznN;Os8Nz=*eEXIXFelbxWSEykTeCs-Ex7ywss7pQf13R6yFm>s!@?V zCy1rV0?{g_AD#$g^w`!8nkZI41!8Hd3GxsimD`|MlcWfb$23){UBro|lcipd>ZVNT zWQt5Dfm&$roU`~imGs!aC{3`2-pVr0#)uf=6urx4|098LJcF68x{YvQF)9#C@f2+s zLknXncn)eW8s+hXnC2{9a;{aUuHrXPuY@tCLZjp?mo%MdUMsok%1Cih9LE%pN~YPh zeJ7`!<&&wRwY6hr{w~zEpkTP&bVOJ{Q&hn~bupR`!u_xZ?B0o8DQ)ebc|SY|dwVTI zoC0db-D4zb%0m;Birw5pN^Zw9qV_`7RccT1WVk^KHQDz@S{0XS$7M4r^{BsmcKP%1>5H?MD8iYUps<-u2|B+x zZ|y*L!G>IPr0Nu~1)!TLNfAwdQ9KENn{sNQ8uNw5<8?U7l6+!!5>9ZuP%SeiE)ut* zls<#G6qjbsCC`OKoH2=vktBaG0h|VOMsS+a~VNPQb+b%M&!xoK6Dydum ztm8DAGA_=ay%?eoK8{#RLb;Nk)HvE&tsV3~EDKef+Lw;XpsmEzZi7kX`21{${)_sC zGuo<*_TSDg)E=U_)|3Eq4sb^G+C?KWW}Lw6mr{*j2msW6@J6z0qUK(7O(asB8YOD) z1)JXvqO&X^YUaVRe!JUXGbuKLZj?}x%3#8}qlAsR^Zo$Dja<^CJ5Jb*SqNQ22ni=_ zuZ57V_P6s3wc+<{+rG)UFcK|NVqa0rRe>TyH0T*I;n=KuoFLC83X>#`jno0_p_M7d zSy)Zc``>C15$zg)Oc`Jg_&lA~u_`v|7y*^D<5ur*d(UU?J50 z?)ETi`8o&O)nYI~0NATMm2^fByh09yqQeNoqQ!NYa&5&5LApZ)r&3?Xbdqyz^wRH7 zN{kFdLyz^8WJpVV4KS3~7K(_6$sw5RtldqQ4l1Pq`gtLgE(h*$6(GY@_g7p+poJ2d zC*QW)1iL;r}nljEt1e6a?S5lRr zVpXXr^cYUGZ0;#4F!2j;+5$`eq2a*`r;b1y17uZ_p84G#4!gag!S%kh3k4WH5mKQ4 zqWO?EUpcF-0vBbu{!(Frc*gQnsx9y2{58r`y***MZ1^y+X9FqG3Wphe1NnG6h?aH( z!u)hVXJp3sVh!oK&>iW%4cIlrlWa<6gySSo`k7n!OA)&A$dtdgq1X!YidLy5Mdu_X zLY%8PtQ(}bBg^FliC7v7CHkmZIHqgMIf1gmIn^^K_i3TT86=c2O%r3WNm&|r8-jK1 zw2)O--bun!oSd6VRuG}WqM z3%R!^`r)N+G3?R3nYaU5ny$cVUN#_IgVP8(YAU55cDdFSh9gcyo=7d(2t?*I0yCK6 znxMYmXiB`6pW2Ei>cwjS>nLg;iuQH{*>-PDkEyttoTt5lp1&)R+S6h?h0bJ#MY4z~ z%|aSWfSJ!qC6rS=r(FLD8iV>+jOb{gpo6$h0SQLX9?(-kWNRUd@<#*a+#o##oR03& z9KI{vwxfRwPw zM9sp@J^`=nqg3TRehr8{G9L+mUI@PmrRQQg|Tr12t5zMok#blI&l9{lH0`R@AKSXUd9=E}W!KLul z%`waf^>o>Ugt9PkhUlRGv|rS<5hrVF8@T}L_lo*vrLV7Vn2V}^uwT@-3~oID%TWPB z&zIn@qAU+V@VNqG$yLNS5m%l~@|t84(G-bA8ii2DsK#Sk7~om+1#GjDObFK=5Dmw; z7m|e?tI+~+MUk2xdFFN=J^G5A5IqYgA);gCC{(lNQU9>ln?33vK<$w^P@@hHhp2a7 zvb*leyUObT<)g^xo6@%_s81-1Z{M|Rp{=AIfV~!KgNad|#>9I0y!v?x;XO1SqCvkm zaNL%rau5BO8T(9>3(00wID@l-FySoER)a>nMZvRE&74OB%$Z8~qGP1QH?@dyih6r3 zgeHoTpzqXI%b7vm_=SMXrr|4*Bf?x*>t~E5Hdb!HBP3oRtke$0CjcInC?QyAa1*7F zD1DtWCqHr$5jwY)(TlV1E}y-6fdrkXPH=mOr#OIblc9vA6Wa`9|9K?Pl_Y#-JR;w5 z9FcQCX7rdV&oKC$j!CqL63q;zj9rVu{#O|NAQF&8q8qU^BAJ9QCd#Y$8YC840Fb&n z#VdHQ2UryIoT)~ut=qaMv^?SfXi^lRH<-$App%}xqlGHgE00qLTJJ@0`KD(zC~5~3 z5_5_2RMNzoE>vqAU|((_gcU+#)JN7deP(R7Kc-eG>qIW=D z1YNwiJV(2O!_MK8$9u-%5{xMq(iC{{9~Xanj&}F=dq<7$zrzXP80{V$92_(jIA=mm zH~}b!2m6D@La#_n1=>B@fBdxZ{j&rMNuvvbc{D}4PxpJhW@^a{G?y6d_WOf=f6tg} z1&Wz=xC@c1Eoz~pB0L)uH9D&ktFVT zGY2M?U23q{0d2DApeSKNt!CAHPG*><>X!#rO>IkXH8z)JCb2(L$IKq;|?G7M;7kOHFh1$kpQLR(F zXs8pDbfNy@Jf>2&!Ph7nQ7J`C7jetV=r2o3`{tD&o}CAe5BHH`b93UjGL&+`FexJM z)XV_yMq<6W-~)1ACy%TC>({6s4xVZTc{VmGOWZ*rjCk0{jI3a!CjAK9uwXOQbX{n& zDq7CV63up=D)y9T+Spon2u0`=8sC!T=CG3dVv^vhO{&rIMN=v%PEJ+%!mQO>Y8BHy zhmvV*1H?_Xv_{N>rHD;utOKAa&;0F8*GFiKX_9ktIpsu5SrQLXf1r1b6GJW$NCzzf zr^pDGl!%fs;ZZg4!|*sQ|7u0U=m;KAZPQje+wu9C5{@&L3W9`Gd=Tm)`=cb(sReI4 z*J>ERa#0Sk6rRnJPD zHuFLIr>fn6c4YQqq<`_pz|HPij5w<+7FN{kuU zA0B~9W#V(3D5*?uNatrSzte6H*tB9D#WYjOwNh-m1ApB0=bDi0SS_$K0}X<-T!%Mw z`3;p*ni`C%dZ1-Qjohhp(RIph5)w~HtY(lkCt5nnJ4l6=>d-q?^xk_1BKu16*5~dh zXUM!oPg$$J3tFg}_Rmr(oq7K!%qiAt63wJFfnhg=bv%G%4iK?{Cm}k?IVY)17J38r zvFf-Do~MXVIaRt-iXNq7`3N!nJs}euEgqp6i-|T4sBeJPTG(N^>W<5$4$&CV6!V3n zErQSqU46=g355`OrbJZ$1J%i#@B+MTlv;15#V=T_7NMqTnG{StqYGH2#5K*(oMH#2 zD%u_%J?djt&m?9i=C1Hp77+N}y0mY(fQ)Eb>uG zBxtd7oX{AZoSnY122*WQEMn!ZX_k=~jTR-7sd{{&!T5sf5*see^O|4?es15oTZXf# z0k;{rHRO7>T>!tiMZXfqwBEpP30Y_5uLekc6jT3lB=h2&TzC-nA=-us@^?(F|Koy0 zzMg%~>6|7+aa-UEppghCCusN?XAu!X$ClG{N;uV#1@>&4p8Ga{Oo>}ciX1I6ECeV^ z?hqUZQ6{#Bq@Y{5`KKm+LZlXq!JPyw7Z%JTo~Os+6=Oz;f7GeaXc!u(b>tn6WU@e5 zAUf6_I*D$uP;3}9HuaS;=Xp{bGej@&0ud46jG#!Hn2f}y!_Wz0iho-MZ#qOv%laIj z53M$5t)h^*4zDN=$Dj+cIpLhf)?@^&e#uhRaNRn}a*lTY+a?F#U|$#A$NRk}|Eidv z*M%j_pjK&%G!?qfZRD)0qTWEIzv^zdp~6rH20^=>^eh_nbcl`)5BCr52hDddA~ihl zxIpwyzF>JO&#|1E5M}ohb$On83*AN}7Aq=vr+Isju;^Mz4<@okGg2&BHL}WRq+pq3 zDZ+Z|1ty!T$x+A=O7K+Df{XXDVXe*`pbH={(+9welr}t)xXwLtnpR}zI5s_ER#C9V@!1fsCh&@tGivW zR`Zza{Z2YnqwfW8oSEZD*zI^HES2P)v|fce?u^B7z^HJgmuuQcbb$XY3VvG!+zuX)5SUAC{jFB6vv4! zbp0WE^Hx!1_~~pBn28){$^t{#B1qVTrh!Il8lrbQ#e1pdk0#Jj@x@eRSgBK1=gYGR z$7)M7XAHg{yoM*F)HTPkT1)2_=S4sw2q83$O~gq3#L$IqoE%+(GF|#%- zLyL)295XDF@yeA^`sw-W4tk+ow1QxbnKWi~hgvmC^^ zjqd;iOw0!M8!u?Y-jZ%`{vQjlqTe78YjfLQSUnj7sScRWQJ!a zmfviAcEN$#K*6_C> z;!GAv^`Stp5E2u?d~)pex*{S3y)io}GFvIqN0WYHNj?J>@{2awu8cyESX@Kgfi|xO z8PjGOu9Rj2LT`t=72mXkZT=8txv-SK}Jf2BsjJ_-1aqqXdx>FnPy$8 zr{@l!l|PU+p&KK-h!i7n3Z`nFOhl{I5RU^f$L>Tmm=u-niNrOzu?BPO4iOn`KnG)~ zau?U+1_UXbMr4Qvy%kuBGx@5SHuAv`Zq*lkux?SDcmwt!M6M7#cTSqY!L&CkA^W>N z?GG(?+P$6Jw2BM_L8B{bZjh8+W@ozKt*v${zds2>demaQkhnG%((&i%qvKTeq~~-i4s1ebo`T&8BKA)ppb!G$okJ5y4e& z(2k89|6UdNMimGGbm1K(m&}Vb0xMquinH_=HfmKtluoZ|9_uDvy|zc(MKmRGuJ+k> z5B1Pj>c7I93!+^PR*J`H1SNC$B86)!A+=(|36RVjoFa zM3@nYV=Qr~=-mo7oTwHHA<4`=^T*Bg+>ayG^&55@gTh<%nb%pRY)^qx3~Q}AWkkGW z^4U8oc=zEgPv_@+) zhaslJOiJ5vjgo7!%uw=is44B%=FmDUayCXM$G%7)yi_ZWCd+L~bfw_ZU&bCfO=RUl zb7EfvaVggTag|*`FJ3tUDzD3|;mTu#`b?H|PM8kzIZX!{V|<3g#s-v}99u>X zrf6Iy&6|uLxdGiBmoX!VaG;o4pePD(;PtCp)eYg*UYq^#DxFMU)o?wqZVtIf>#J%T zG;~EJ5Gc&7Dl#;<(Z_LIn2}3+^5v8ue;YMNgGPf%-J)v$}F zP9rE!K}@-#)buP%OGK7rfzLO(y8CW1uy&*WI7grFszy}AQYEakb~X^yOPtHsBko&` zXfVF-Y@<<>2yIX+Do4VX`isr`x;J(U$+raHLHNCk@cYHdzN;j(rE~0a3u~X4u-}#3 zR$8o$qFcGdgY@DyXimnov+F@E{XB$OI2E!5>>DP#Y49-R5F*e0N-%-u&?7qP2r! z7Uc~YUDsrED5Q%n7J^Pvh^$CiOu9SjU%LbDwVG*fr5OrF#!&V{GDmZ#raa< zMr0+y=1VhVl_=lXjO8_|Y!*`FT(X34bKa#os;wOu*V~M$&@N&%BO&vg8e55JDg&Ix zfpkc_OUHDlC{$3ZCZSD8{;|nzoQi=>slJ@<)T41|rd-YwNN-~Whm5lskyDZj9ix#( z1nrh$Qk=7WuT>O&%@eyK&kG1Y8sQ9`v-kx|X_L%uUsRGs)-$U&!F^KzF1oXzDzMUF z7?Zi~So@@lGuqvm>AEzXgc0L}2{;Zk?}r84Jr`YGJ$7_fI~Y78lGCV^Q$ON31K+Iv zR-F0m_grGfGn+qPEKAvYbQOpbNMjF(mmbo=Sdxl4V=*!fQALv?6Ei~P1nuj3zZk(v zjBa4haC-e@q8trO$DHBL2ld^aez3ZM#`mm9(GMPC9cORzMw%3nE*`P0RG<$=*ayV| zbd(|@GeGa(qwtl!FfS|=nBRW$ir7HEIsHe&Q*+UT3u}8pIcK-*I2-Y&VW6@TyV{2? zlj`<(V7py5I-mRco;!bknUc=_xil<(vnaICxkvv+h@x&P^Lf3W{>|I?@V zTzX03%f|(7us|Y@A|eE2A1ZE0K>y$W`~Upkt=1(&Q<7wG38fZe^+it>oHfkPh?qj5 z2&WbZtSoo!=kqg2Ca4d`4wb$UsU0;C&)r3fJWqW~h6VEhiD|2KoWQ=Tj}3Gju9)4V z3B&sO2!%vT^vMEF()$#cTR;WMAhf%MHoD-^*6KoE%u%|04Z3Q(lTs=?JzcCE7y`@204z8boMm6uZu4x(%(JPjamW_rS7VlVFfYoIfn33xXrd<4%YM3xF`0Uf<*m#JyL-JC;jw*K#S z6X=@tKR7(9-v9piXz;N9Kgma}f4!{TDXj8+=l#-BuU(J5`Q+b_PdGhpSiSh?55rm< z-}Eli58xj7;^O+!yMEXUdjW~zNS^V^fvT=3`-{1_b@(-k3CT`awy-VL6stnruYlIS zAHRBe_VT-bM#qpmClwTq+Ji&N9O}msE3tp7gx1UF7wDQSitK2d%-Gxn$xjK_XUFD@ z!U^{gQ4*z2F_A35YN}M_u!Yc9T@*N4=oqOPcP3DKhO@S9Kxog+g!)ueC=+37nTGzX z+L)(;$dz^4+8Nh&2-VoFM_-F=TZg+=;r58V<0Knk^-A9sWyMLq7HOsYdbGQcS)(>i z-tBaIUImTQ!-(1qH46GmSKlomqM;qd0&TA@rWxD1QbOVhDasfoVqZ71k}pnmTDSju zr@(Km1gzfw2M7CA`_G_%)PLCjKgmb!|2sQ7>VI_VWf3Tj{98jS6f>fNUVfkmY^*#i z7bBYY?!cRRw-6r6wZ9DMp;`XNnUV8LI)KeMLS#h=NT`tQ4gzJMgW5M(M$`7&D$tR6 zT!H!v8{Gtr67$~H`eCFB)>DJ z5H7uvk{`%ob3(Loya6GmpugOj*07xwHU_fD9@Jh6TLJOsybJv{NfDb(`=&ZgCx6$t zPD*h`8?G8dXJH;RE0}S#yRxFs^Ul&vu2o%P0Yc_73%#_8J2YtI z66P(Z)+?}nH&UMrQct(LZPyMuhPc#qys5Gtw*N zzdk=z-`)aks}x~)TSkYIY>Ew1_&zWt*DB3|#^l*}3?e*w$x^aj;L}Fn&nfh+JehI_ znXlTi2f6P{mirnHFf}lO=5qHY^;N(x75bXX-HXgu@ydK!k*~IBHWK?*oTJ>^tk^a} z-wH6dk@?nwTPgCbZPOhjz71jjSp~k@)^%H%Abkt^x^1$;VR4p)Wg$t4 zR{nh|>|pcq`2M!@7CuMC-bld)J2mv%V0=My4Z#Ze+r`~U~kGCVlT#4=1V z$r84h!JP|B5jH}tEzo5smaoKy`MGAnty!$)Ww&O9-b>#uU8c)=qoKW}6?so|Z9F;! zJzXKO%o}OQK2jI>D9b%@TVfiV+RMnOxwh}G_ZuAE%c#(9?-er`8Z-_1w&uVEO>E@3{&P*1TW{CSUpZk0RdR*%@955JvoA( z67va>=f%TXRNhu|Aea&yCqz6r)^0r+B0CL?-RgHb87eUi50l|bN%~S zdp*8=pI43zEt=?6E52#Sy++Aa`2q2b#uh)Gqu+Z8&PYw!WjVT?Ep3 zq;3f122=SBT$}jp9koeE+o)%1!x~DNth&XQUhlkD1n5DZ(21gy6Hy8%f46{qj$%DL z2X0MY0EWLrt~gi4XM|741~|%;@$GQC;%$Z1R55d;6f~7(qA(nuy5TNyl6y%nv)Wdu znN^jMY-m%g7jtIHUC(}5*2dl4{Y6*RZ}tb2oXn}}yYH!xj4z(k z8I^vuruQF*;jO8)W*|&_P;CP1g{J3eyJ=Mw!*SFU@m4U)3(2u0lZD~;=GU-!8Z=hh zZZed9SYxK?ZEqdJTB)vO^?@vWBA=P`M?-&s??PNO`b&6e;D+B|LC&$9qIPq-g!Uc~ zEv9&IcqHvmg+xFCx3V>3_XDmK0Dz5AFC)G|jU01tx!dz({&~(J| z)C%@P)W%5?7t_WW(@S7S{-v=c%MK za*|*nRI`dPt-jGr6n#Ly=1h9!YLlVD4!nflF5yh2yKI+xMa%Q|sE%$8D9Gw<8SYA8 z6#=3wyVYd26)0^EaWl@%7}a#dG$mXNm(x%YVz80}n`Tc4Z&o&^0!qzhNUfs-CBNlt z<`RIl^_AmWK+9NsrdPMp@kAT7pY)y_b)NK|_Bu}v4)!}w;h(2Zd+MJf_0Qw>HV9ZQ z0sN_T6`tT|N?xbAAhAj0-A3(0u@z9mkXFNd+N%OJxwO>h5-ymDQA$Xx(}qV8$)vrl z0!gmsLezoC+Z%!+oIa9>lVpKdisTea#5kS6#7ZaTwu*w4YK`4Seud`6SKTezXqc}X z9mRUv&8&Buu8VMnM>L_5Dv5eGrjbPLn6s>HPV=;npFeN!?bVop(o!1cuTI{3L1PkO zUQ`NJanoXXIhLon;5jE_`VQ@Kk`XM?W7J0Ny%l^aK$uTTiO!q0k^lubj``O^XAn0} z3+b>(6#4ahh7-Qh2#KHuYB^6@#i*ZAcAtM8Y!sYfb{NW_j@Sl+-VQpEdRh&E`j6zklx^P%{@~ot@8*8mnX(XX9G;ovWu_ z1K@gusz5O=4##+tv+&aHg)<_iB>rB?Y;XdH4bC}}Y?P173HpOT7X$;`AkgO$f(&y! zQ+Z67Z&M{TFl=O=|8e(fV!E! z=g@l&c6cR-d5@LEN^x>Z61*sGHR^dKHXQD-MC4vrzA4Ecu=*`Z)#cVW&dsJV+py9; z?u6{~vMbvMQ(&O(Rnr|&`eRNTT~0X>Q$B5Zr_(V~+Zs(} zgU{u=v(r7)D~}Z5FaerPNWN=8Qt3g2;7Yu6PUkcs6Y@+%IDzpfZgcW_TPe=xI@>_G z3$-Wml{P4xYF4>a2ye)H@pYcjj2Fcs))QylHcIrRWPL0r< zCHaiJV0l_z#548eTrY(==3Snr?$#`wPRD-Sf-XO{16At%x?~UpdOd?Z?*03c&{>#Z z&~Lx>Liq3fdqfgJ&~LxJ84lm7U(jj8r%#{uN?fl?#xOppttvrzRZ-n%!{ zXQv)qnTX(;EWU0Ut**(Dv#ef3xD?B4MqHM=)))efFzB=1Ja+pOjdq;IvtP8woW{0SYU zR9~p=N8>E>-J9LMP~~s$$JfGcuqrykGaY;P$rS+`5ld4NN%d7u3HrfB&7$M;Gc0iF|1S?oaF0n)dX4X}*{y^kK)(%kY7-}J5MTaO1b+N%j0y(u1R{%*)N*mZA!DF*FX!f1{X*L?_Q|i48LvGb|^o0 zE{EHE*5&`063pd@V0nitz}MXWd(ht>RPX;A40;dwKR(5$&T5Qd@q0D!->3;5m}7J{ zP006lN}yNY9G}4B`^mOt=c1c)_j^F|vtRW0F)sT4XxXs-0E19usA|YP)zr>S5?O71 z@!8UjyszAKo8_VP$@t<%YQyE?y07V4!h60)@>WZ0t2~om^oUf~&Z_K=U*HnD+s%O= ziT8PD)qD(J#)vnI#oOK*=mGrPlFh?slh4NT|E;tCt(E^D_iFNgZ@+)|Apd`g&t2sI zn&=OgPJ6o{XX(GO(MMyn%d#evnd=vkGvUkQ_xGD}iltG`IZ301^$@=zS%M?ds`?6C zPh3P(66c9ELJIS9h}uPe*9!mkTf0@Y{FiE?7)x@N{vRwQsNFk2{X_JX`mc?4Q+9(s zpjj^DDVF2~i|KfQ+W)oRo3-~)`}O5XyMXR&Mk0?QBE&dP{*8@9InzV0h0rf-bXm$T z;2*LsXC6Qe*#}U=S@+w`sfL={oLX<3UK<}a?+==j0W3+caJ$L2j?^u37c8UKA3U^) z0lh@lt!?P2@i=>9JC`5RM_U}#N6TE88y(q4TOZr+_cm?pHs|-NP*?b{SG!!-I9*qJ zA)1yUC*TUQyznKN6YhJ|YyMQ^c@>)1^(T&YN4Jf5N7b_le$$?xz5LRr zn6aqYgvCVAu7fz0G8=Zg#Z$YOf;s(1DCSYPG@Nw!wD+{vfVg=^H3?76VftvBu%QN6 zl?`$e0$1|fRk1^;F@6XNs)}(;rAf&Ye!PsKh?M|qi<|EZc7dmSG4!Tr`oQUqz{iDb0-E?G5 zQrS#rN692Ini}`oQm@&=occ^RwXN&5t$lZ1r+o_7-7<9d=DGXgCc*tU?mqWE^zivS zpH1bzJGlSW$bW;w-hS==uR;Ie{;yB+X^iHrx%^g0hEPVkc;I^ZT4XSY4tx*ol3(j# z!}?_?qA8hqMqkRhG7;@PM~K}N6u;VjGYB-AKhK_UFU~#NrxBN7MrMk5drdzlzvh&a z7`4x#F(~6~PGfD8b+btKI*JIFbPP#G+e>GFIWm6!?1gFE9yH^cEbf3CWt`5jBnr%G z)YoKDMqL~xEu%zuT&oASl!Hr?{%B z#5TT&n9DDak===v|L$(6Dh=0y0WibRCFmauYys zma4J>DipuCCO5~n@DjM%9=-ySE>Nz0r)=c`3X~)k;Eb9pVSdd-69Fc-P^e4&?fhaT zkWtQsTo2|ORctLs-1sVwQvC4-vBx&<*a~L)Hnj*gL^M)^RyHmdQ@vg1yEQBEC&|BF zAo~c(*Z+i>*8LQMw@EVSCi7A3j>D%;@@Ns?F`;@L%tKDc zhn$WsETii@+xAoD2SMfihEP2ZWVZ&p?4Cp$WyUi%mqMSdl%Xs2KEXAma3yJPtJ^P2 z*$W$6RmnoxAURm^A~v^=YU4j8E`y}jiE~a6m71xU4OGCzp`NfaW;`P3^7)0nRMbOf z&aHh(H-{Sc{pItEAJc}x_yo6*{yuJ+{U{kCf9J_uo0icXGr1zk3YqB^nOv9BHN)3% zRuM5d^%$_7UOo`CD;T2kkv`f;XG*leDYPV0!m*GDrwa{8(*e>Qni5X-wqM%6=Tx$P z)KsPw`F%+q(`CE*W=ThzYX@edd4Wq}-$tQHe$ghW;313Y?=Xw$`}Y>t=Z;N%_-$X@uP7hIAsf*fRTlY8Oct(YyH@G3ADPz~7gye1k z1l_NSom2^5Mh6mzyptr2NnEnWKpeebMX5}iAL0p#(Tv5U5cV<_2UbX_L{b>F5PBMd zl(`NqfrEicXKQEHsu8!Fc4!^r^RO5Eu|Mnhf5ze&OQ~f1_8Y)z{@?E%J+9^dJ3M^g z|DWUo%3{Xi7kZGD*60Ne*r#5|o?=d31>;^UKl36fNx@D0UF6(lO+i0qgky!|yudeR z+-)?anORFYXeK93ClLC_AV8~#iYs6?>_P3yg6vlmrmo-O!*ilJ7d+@F6 z*$W$92D?+1G?D8~qFTL@`|@C2EG~Ng8w7jDFNoUx#IrQc77@(hjRhyRo)7v>LXS2HW)J&Z;h)q z9?+XV|H4MMq2T(7P*puDY(>BmbJj4B+Y+`6`V8L{_-^=L_dX)+tH9rd_ z&){6f&p*pq=9T?Re@nFG>dTsNYpl!0Xfw38X#xPa_xzk|?SICV{|oF-x61;#*8bP4 z=KmQC4v!w}f1l!05lP=I(3u(Q&y(`8Jl}3{e7y$X%Q*YF=@n-i=1$z=rnC-fn-imI zYznJd)1_w}1XmAsQ2S<6Oc{%pKw1IXXft1642s|rBA;g`e%&EjHULM zkXVv&p1|*0G^tF_$P}tx)W})-3mZWZ_`4CJ&y(TukGh1mPCZ#`$NB`6ej?3wd%J7! zHXV*lQgz-v5$5W~-&?kjFLbWlPf|>mg=5NuM8mI9#%U_Y73+y>efTdT6!WOPq;qxp z3asEvNP0n%@dcfvBt9Wr8Wa$lR)RtQ-#ZE zRT@i~1Oz(&-5MHxAd3x2(7E*1kzh*s%dBbb+L>UYazYbmPFY%uRyJa5HA{*O?BQfW zA+K zYcbNxfLOa;M7RuQIK>mfi|DE#!oircStV?GHLzNKi;W1;t=Wtm!?HJ|%7?r>_r@QD zRk@J0R@%l_fR{lA~&v)uk?5_^AHIS9+s@#AJ9EUn>tNk_N> z;yvahT!rONn3%9(=l>$o6Mmtx{!JdWw z651=Q!*8%4=U7frd#UHIy$5uQDIOdiiF{U0Lh^@COvp0)Yfb{4UkA0H931SoR}}wL zc?=u%n}=+M57`XwDVt%F;I#tlEpr-foJgkrD%45azNFkT2M7C|r%#_AwpZqc$yi(@ zvA|J8GKq`^jU-nc8Fj$aa6?dp(?=3G+be z!2FRjw|E*w!!#}B4*JOHUTPevo-=gGzVS&jhgwl{o3x>KN_M%z6_*6HpbEgdJ2QBL)UV5tKr8On0$XTsA-BX95mu`wtWx6l-R&q?Jrm(n-U z7=*-J<{9h>XLSLfjrI?35Ad9D%3=rV4v@aB?4E5j=zVl`Gwwp8`CgdOaR?UWKcCQB~{~({W@n0glL*`HYS(E?saQ~>1|E>SH zfAo<5?UQ`UIoS;8I>W}tX?r@yrj#mPsVZrRJf}x)*GuzyUQ9wp3OU3inuFhA2ldRd{VI|2I%Fp z%b$-=U!1*kzg`{xy`aDl0O?cXQ_iwF1Z{PkG9h1TQ%=zg_3-Tcr2OFg?6i0w$a9+J z@AM?$d7_oWV{x1=TE6+Q)u7~7+h_s2$c236Sv6f<;$1FM!&U9{!YX+y-1-?#C&cBK znbCBJ`n{g@TFme}FqA)i`t(*Liag5_q8(2rZhIq2);7f>weQny-VET>>g4amf5p^=$d0jKy~AvUdPI@{gB4 z$^|La(z_Ro%~pHoEr-mWSpS2LT=j@|#=p>wm~(zU0%a|AkYM%(OG-9&oJD z|9XdmO8%ez;nCpX{Ld%(?4WZjCE+PJ74?v#n<+`rD5r_y`zXWFHJ%U=wsz3vlnM~x zz-cF@BuUUDVIys&q3NUpsRAXP69~5Op5rub?VyxQfYqVh3~bCK)|~vG_d=x91;kRQ zsYp_ihg$R`Y=x&6KVL}3N$bbIzkK%UqNOxa#q-fmCl@HDT(rUom0kE(x1tq}{=vKO zuYE9`bk#rhr+qP}nwrv~l$#?#9ac;V*ySn;f zt81^do@bYAfz1BEx$eP<9+aATLHp#EUTD#`&KDK()Ah@005wa(=fPWF3c8_B$E`hJ@t;}EesjyI_4%NvK(y_2!SHBGaG38Nr=eepp6@P59q zrG{$)D>FAOlUR8@ExlNYGV>4()N~60rVOY^FMX_=TwG>%9I$o%U;@EuOy=V%+sE>0 z0s0(J_fYS6oS{QCU{Vk~?#S3HGmD{UzN@<%>ae|&hlzzTz^*l{y6OnKNs#*&7*KVQ z3vL{+6IyJ^-|Yz>4_ably~b_&RWG!L=cKQMkur=krIW5%DU2Ps#alp&6El&G?wSnH z!&+=J|5bSh&AvOz>eoY4d);uw1f@r4H*3D9Qs)1sNI;!aymGs34q!&4z0wl!IQr98 z4{lq`n%>;lAxxda6;r&0W1HNpg zR57|pV1iK{1~IqSFN>*wRhf0c*eiaC)n6WSW+yWhfFoh+(-*G`OI%;}yC#A+wl~pdw z+&1U{Sp)l`yykOx=ZzYn$EfpyebV~WdY&*Og{X!+^P;M=II}fyb#2*@X~jV|=~nDi z?Wri`fZ4AF`-h#OnW#{3Ly=h;jI+Ni=2^Htbndd%LQWbX7B+LVRp=2ztOkc}L49-7 zcBkigp~Xg`-POjS?TQIyagfMJ_AvCy!a-MgzisFemnnZ|$1j7a+Z+fZe7IDnEGy++ z6erZ{BaRA#AyLf2{e*Mi;T6dxdgldX%jTz1*QUBmHYpSWKk1Qm{p+5CNAk3c@F1UrhHv(j)+312;YkGn^<~0I*!ke8 zQkP>{k7$A&C2nOZKP(}fD-{%NcHXG`_epXWOYl%f^eIjlwW6$uv|roI<>tXy*%VP1 zAdql%Z=J}a5Zhk;I)g4GNEiy2sCxWLj=Rncp-SSF!_AZY-;={EhQ1s=TKo{;(eE05{Sk`c>C`Vcm>!e5xOyBvk`pzQQxZ7(DfyyG5DO* zbUbI6R#naHSfcPs=+XR!MPx6SByIBPl&i9LPE6)FlN(O+&l)*wcE02e>VE^Lq`wZ0 zRxhuxNORX5usvubi=2!G?q}6wCVgz%r2Gw0LN%e3)vinHXEgyNvV)EE>XX`HyEBSY zzSzhyS#?8>cLEG4!OS)k_A47FF~$An{}P?x+x+0>Lp&gH87 z{M#kOkwnc|RY%Enp6E)GZIjw<2F97vTE<}x$I7vC>4z7Lyu5d8Pd&MD%HQ| zGW9W^NHV=CP(yG}FNovNHkJp3Q}S~j(dI~N1*x&JUQEXJhmB&o;U!JiXbIV|f?qJ= zDiVq2DOnZ*u_)6#i|xp=SrPq?2>1yx6M@#GI&UfTi*8{Ro4d*a=7O$~qisvYrUlwp z-eI~%Q6!@LRr`NG=vqar)@T*wK2{5llZ_zBP>@l3_Zijmdh1k#ukk$Dm2E+g1%})_<&S2N#)gj2w}F4h?Pz!-+sNSmMWsV<5|iRmp}Eb_sFK5Q#=Ag zk1Yv*`BtwVtEARKQC!hH_O)bWb$1FBBWrgUvZ^2yLW>|9=YHuz0i1K3-n(?&oPhY> zyJP`(gqH+odI7S4viB!p0MB{kfvzpbB_A-g{#{Cg;HgNFS_{)${;CP5`~(}xzsc9g z!MwY7GrBQRD5->Yp$8zH9sgwuG|YhiYei`?T@OTs@@lA6G=!Wq`cH_`dZW3a0#~FT zD2bea7cf}9isbS2*-BjmU0lQ}TiG4P3f7cE$6U1bP9ik_(_caEIOl$p2F`rBNQPn9 zz)&BGTTpv()0CqE7Rpf$duBs(YOa;npZmBPBIO+Ys+qyd?2K2F%?|n9CgrtDWu~$g z5>!88iTqv`Ih`lkU$cn(#QOnbgXP!{-@liTAA|%)>y7dR$+EFT2Y`T~h4_9MYOK=; zukMRWMb4|5LHsQ2iN_vkGrgO1jYPBL^R2*0KR|71a$rV^=MUk)XN&ML``cd}Sc%)E zX--^ihVjRqp&F;jQ$Es}LCxN)_sbRFYX;N!h_;XMikEf7B4;_KZbdsRxrT|9=$UkY z+dfrLFWu{ES_1A5#Q2$dhKD867HcIMq zumw~ok7;soPw(XziC?UTrc{55&r&&iV>gYpTG>tYjL*9(sn&xgRf8+hRk02&eX`-- zyS%_}o92_*)52j?rA6Ncvu>8#_y}d5+BG21)MRQE`0hl#sj4T*(C?U39inp)#J?E* z?xo_)kdyP%S4?d=3BuQ}?~F)pn+^kwpG$*7iW62eBFW4V{Pc7W{|T)RJn!D>t!H6_ z295LpV>Aut_Sat#Ik}_!BC^5DLA`89d@sxvTFddG$(;;Mjeqr|YUqPDg5y7tu28(~Jgs!Y!y##{V0US=CA8fu7 z@-6POCE2(6@2d0&`c=fVkHmkqgbPbJ`r9yr8t@>lz6jV-xeCw{F6OYVz zmqI$Qp+(^{9apHZtM;%+)_hPe1NfJ;-M4HvA&_}%i;bs-H%49JrPFze)&WVekpQJV zswKho9=TSt&5cO6uN4!gpc)A`FNCN96&`*bBvm;v6fy%ECibWF`>$PR_B{@pbjgug zvU2!*&246E|MOdABTQ-79X;J_S4c`oO4VaKrGR=dD4~12E(TBEf~w+zSe!sIAj>dLrvssTAViWp3pIlQCsXz;d`ueI~Xw85fHfQ5!b#^YGuL zX);WF=&W|=;_~!in7;wsP0)91t%WuKXiY5mk&J^zdthwCy}hk(SR)R)F1>@_yph%1++A@~y8_VB zLSp5jZq%D~sJCbg;Bz$?S|@9bQpyz+t?9s+qkj#35pp8W+F0dGib^(WJg}n@+A4$y z-198V{k?==3kC6URxWr~-Zi4px;Rlz4XmyyZqVVDFQ3r?B#Xy#xS)p6v z&ckflt0*_|Whuv?$3A29+%ltrFBAUL3d@B{#4kZ)PwckPNjoM z(-eQn#nk|gaUd{ zq-HfvA%h~5J*!7lUxNGIS?%yujulATEU6QBwBdGb;-Vq3z$JZPfF?SZb@+f{yass} zt-UeIHx$U)^mLcHGR`~=qI*i8g1q6v{5O|4F@{WEWeibE1oG6c*2FP*;yu#KQr9=i zyf_f}spTFxH^WiaXD=lvC46bSIepIPWVdhck~kJ-Nl#B9CY92BB6^{5<$X`{r+ zVe3H4o$3&s;{Cz$PcN@aeXR5Gd9_*aIB#UB1q!HJ>=t z?WDXk@e2#>Y}r8F`xGrwtJy;0(syR6Z%n0F?;IgZEQ;9`lqTZXw(?G8SUK2AV%!0y zWdPT?`)J>*mInsyFAk*nUY)G@xww7A~UfHoLM3qZ0qoju;MaB$vlaA!!r8x zsttTikIC?*_O)o7R1isi{`B?e@_O>qI3<&G9%JPi*u}2LA_c07?WXtJn=FMD5E-pR zQS`fHXF7f#^x64b`Q$Cr$b%d2Q8r;#T2Gzu`dTAL!CN_0{+&QLB?V2^ZZb7n<_IBM zx*t`)(x`-tQAZXQaCgm;Pen!-K{Ucw(TeKNctq)Wz+1(EM~>sgJzu+johP$jVfi6hd1kSX{cJ)xWI>3qMo{YO8>7o~6~I<4ym~YDb|fkQLlDL_ zHZqvT?oebnV8#`R2BvzvoIX+a%3@cm0~Al@(MlL8O%M%4EfV58@FkWn*o94dtNC}; z^vupTc_`7I786Z_ z?ONWbc^QrC8M})jGg_YIGkqwyaS1yaHsc+O$~HRzS!?0>8*WdUTY927u8olzGu?m{$1R%(qT$ca4u<6 zmrRGIjZsRN^eNW?taFz9A}KF4d^A4yp}tgm-*1rj1TVWS_DzO=A|kUcuau*2ps)Un z6H{dyFe}7>?9}F)Qjy@cDqgTi{TwU4La&fqee0xw5+sSQ&FDIgexhM}>)vhRlk&FG z0dDMHi9r{aTp!!+W$&78o$tuI6?kOBJhq-%C_(GRBU` z#~VdANrmAKG(>tQEv>WYknn(p5%06u1uG*OVKitOKntO*c`PfXagupFgH|gL#TkwK zkE@)0WRa2@OhO+%!7|-4Zo%IFk=W%-=p>}SKS*%uBxDui3epywHMn9?fWYTAhS+Ym z)QsixTCJco9k_|+sq34>nz^nod(YuF>oL10H*;HiIaKlfTVU^#v;VlVwQVY!S@W*E zlHqO@g+0ve*dWOhY|3E5=Ghbp)$MA%^1^wryRJ0jO1i#LqAr`$MV2bJ1ynjnI5JpM zrmEW(`u$YUUY!$lB!nsgcU46ywliOoNK%sV{JI~Tf0-ysuqgRvVVQiSE}y(b*SqQzgu> zmf@o)7bT1|mt7K1uIt2!CO%Sx-Wn-Lff|p(gF+OQU@|hkH<`7NINe5TOW2!K4wWFluogR*(=46#@|jPQaN5BulXoWE28>zgkv20hG+N>BL@@~O zIl;3>gBV^Rj$-WZTdg%G?v9<6sh7r!>{>rBuEjMv^5f5 zK5BF{&^OnnNQ(OrEB&_qE(L`tYa5IDU8CMdu)sYy8awl;Coc!1XbW{5}r+c180L5eF{ zIS?nI?yT8DHr9+w`3fld7At~lAP!+P~cl-!hzPA+zxKOc#)E4&Nd~`_R%E& zFy4P^t-WO84>1<8(7uAukn)XFdialXm{9)TeEJ(V}s#9PiazaB<5HW znZOBV!;Lzd2Av^IShjg|)P`Jw7Cj5OXXhkoERC&HDre9-4kpFL0{PT?W8Q+zn3pIZ z2*;xo4=n|a{N^T2Vv#?NqDp>TgFi&?1cr_*3-%x@A|!04e%%@@kbpC}5dBwOj|Q)f zrD%@rAfg8PB$6RQunbP}?fvw~b01-5I$$K^Lp&vfjlV8tuS;i_z}5(C|3Z_tEsd*uj8Nh zRjuRep}nb3kLw~Q2M5O~F8(C~m)J*4j!X9Si$|8X&+N^`)L3Ym{s}h8F9kiKa1f`v z{d?Ri3p3B-NB!KSXrQjwn1~);u?`rCt1VX!qMvARQ0Mb~CN$?Bm<3J01-NcpDokU= zX;qhJvObZylOSVEv6&iMi7Zwca75S~eWTNi4Ho6Hs>rGap}k8nYN~>e6?qjB#jB>< zecH-~DC@_lCmtyUby@2~OBWld*QVD@Sb4>7J}s-MY1fdd8Kiorn+7uW?N9iYwGZ5H zEo<-EWg$8Mm*C5f6s{(qc*&5%v`(71La2Vd2Lblc(o^hEb@sCRT9A8nWm)OVfX1x4 zmhHL`eE1==h@ z1X2W=rP*z%7OS}Iy~$_-7}PHN%x;sZSYmqYC=ms?%s0Y!Q?RCxt+M#K zT?M#Tp5jFYC&1fwmzk!jSr-*CJ!qIYP8A0Tc(9dribKTr)sx{=e9a^T5rOB$yUd3*HKK;p zR$r3lPUch;`weS69dqI(m?4pY`N0|;I4XIt+;u~Q1)%~JUd!IW;49-Ql^(i^j~lt6 z*uP!%OdKOE&SL1(?MTD_)VH|)T(VguZWlLFoP%#H)AKloLQNF4c6shLh1M-e`_lMZ z3jN+P-ta;-UxZ1np{*H2Cc!?o$g@K z)fysVp`#lW?*DUOb%fv`uku$iE$ZU$&SS0)N)vI-xrd`^J%`OxA-ZQ13lR-5%FGow z(Gr|7;yj8M_yn5JlCL`#bel)2S}egaq;S#9gcheV;L5~x&j6RprZL($I__juOCAS$ z>RbOA=QWa@w7ij6IOJQs!NtS9{5^GrrsQE9O~9co^tep$pUbMt(_`_~qx0_d6V7~} zI&kM{)W#Tp$#;d8zV|9QT{%71=o!Z0<`8pLWigy;yARP%lq_>bQSL0VArvalQGE`O z(`9LEv$1^t@tYrw!yqRL`z2Bfb~K{Wnd1e? zfJ`bF=Yab}=0ct%vAetL43z0;=iP@Zg5MLL5LoYm+G>X%c}^4jNjcoxxhN;4wf!$u82HTnd$r^&)kZk!Db+ty zn%ZU$q20PxTaFs0Z>w+WgsBhtAC7M6<&h_w(n)eYdISqXAB$aYigRf*`-Up(e{#+g z)91gm-V&0H-a0rFs;%3yD9&Yak7MElHKB2hMr!M2`UB0jFsv{JrpuhY za&+{BZ_l2idNxIZX;hz5Y+JFHs{W~wO&rD=ve-}!3JUoa$eHSJ)Dr8MPDOz>O-q_S zKzz@pl1ACsEu=xx{MC7m)c#}^@b5+;?UfO={S$7+&=0K_p@eE5^jU|{;4(&mR>*B` z;4gL(dw;hNDBj%k9CL1Eb;pkLPy$dg1ZhhyV-vXF0Ew4Fw-7*2lxY(PlV%{_7C|bmOroc1S}#OQOz?4szW^=rnIiPmL(6Pd zo`dGypHS-fUN96rnNlfE45-&X4C3)uj5n^SFWi$Kc*kGx58ff}Jp-LPf46V`zsGu} zir4?&?ay1DsV~?^N`?BnF>f{wSuT;#=gw!yB6W*}UBeu*iw-lzfQ1X6hKtdY41(+Xjx~>G_yhJ1W`lKtlLTqmMc81GLCeAG)6$JxTSZkJ#u64{Y%>|69@znd|L|lIZ z%>uP6)%)L`> zJ>!kZmV^||+E!#4%U(CU{XM4jPTZD}vJUy{Ihu(9654bVI^`2Gws;Hwv~dG=nxFf5 z^yc#LPsP0V#ZiOP8Mh%rh(~Us9+=RL-@v5S82anWZ&1aiY~nDE;EHCQ^+;p}ozH5xiSy)x_R^1LF_a)p?wXfu0oY%V32`)+suXGM+4 zsd$XPqy-m=Q;bH2xnNP;wu3P>`V-e@!@?yhh3Vhj*kOQ&nLmJanUmW3(AHjbKlK+9 zvcyh3@I0sILy*)0A4u?DI%l#U>eWn$2d_+UY3C?7>h`EqWed!x(I~?^F~Ox2*oY{H z5l#>E$z$OHAnjlJMTlUuQ91C5o0;@qxP)6#$SC$;dF9?_rC;w2@Z(~dfgo5;!GXA< z1wf738JgS^4$79pD{!w0C6fpY77ED3N+gxsB6+&+80PG7 zeE-lq`?le4q+M&HRahgWs=X!7$CBHs(UT9@A7l5x`AV*!9M&%+ngf|DISkQ5`Mwef z^Y$7g+<-s+>iAfes(ol;qp>(h8czzC#Go1Ozo(78m{Z1@;IPI;>XX^YoN7es#9HB- z7)rTTCxX&4{5_KYGQDcQ~qI*hop=v?~6$g zww%s-RL#~V+up+yz1iP!yj13va@sI6!z;m3W#1_E}mq#>af(>_43(f+o!K& z`%`UkK#sD&;(KAI?G12$YV;e>a<_N^F!FDq7ftwHjlMbfHNM&j=-Ar&_Tio>{_>&g z48p%`eQt^k|B;)tbAIT()Oyxm0IGN@(Cp?GVG+e`Ac9w$j2JR{J^r!wXHE?R5k}6U ziXb`6q~Td!PR4aScIgk_n1ro-c?N6yfu;}Ti*ZO8`sa%n#O0kJxDzRMgAw{*Fn<}$ z6^J6(ZKr+cWNpOxl5_=(j07SA-On2$z=0gG>D_va<3#u6Y%_?L4ss0gW%G9C(T?$7 zRyscPFmlfV*@bkjWo9@evdofOms(fW7ke}Z&$BCa$4RDBE(ol-q!}VFbmi1(Y5;*( zI|Ym)i~)5}{w8lY7E0$I=I;+eu)0S7QRnwkAc}2+`~&gBm^~mpz26YEl$t^pNAXnh zEhq%55>(RTBLiaD40ixudLm8OZ*z5BQ*K&WWKfg!N zt*W!V0I7WRq+}&*zWXemHM(8^*zvh+i#8Ls=s<=mc&9{i--E-yGUcgE8uBjgje**$ zC*u+p$l73#KSEIy!DR3tHladz=)P2Pz>{kr5bjHcR$}Xijq0kx$!<-KGOJWIn<3UC z<>vi#wkyX#j?Y_nzxJd(;0q*DQqg!jO+$<5R9Z;KwrU#hm~Qn}k>% z6Vny0Y|g0IhLbeb03}$z%8F@02AURI?tac`;Pf#17*%%iG38)FiR8CKEM%5Eco71* znYv4q9j7oGLyU{%`3ZsuW77o%`6=Yzd4nqY3K2(6h`C9Y^5y2ikSetEV)G9@CImBW z7W*GV)O8$*>k_;!t^RH?jt5J0@+fP4AN%>qgj7V-2>|zvDEyrM>#{5zKtb{L=CgWs z{pr(kU#VJ!vAeAGxLVXyc2e*&6>HENH8MHR{V?d7hZ}V<4`xl(hEG@h2{D}JVKH^J zk$vMKEs;D7(KiG(`#?-e7A{>fMnL)N_9uy~(E~sUz;_9UdBAINe$C3p{~HRpeAe}21%3s?l#ql=ol4wR<0EB~ zkxKrZNjtL*ltn577Jvn!=PvUcMvg8r3wCTV^!+mh)M#8LxIFa9Qpq`>%-D5BNm|lV zzPzWucig@y_8PNg04to-s{r_p5bm#^$$hmbNpgdf0r_ZGg_8%N@FX@yQEU)DQFY*X z155KIi0LlKBjW^nS^oPqJ#`ld*9iSw^vX-9pso*IyGl8a{9jCVD&E3WF#cMt!$&@0 zmchONfe2?1p`Vh`(5k^MFK-(M4Q>%IB`tHv)qVQ?DNUU!I^%6I95fg*=sh7gEl?lcL!1$Pvi~3h zsc0hS{|UoV<+@$5q%bEe2_k(A_HKjH2c=3IQXZk3fg z-Ch(%YfMb#OahFTp!sI4t~@ zb=6LMuijte&K_Q24j5)vft-(QnCTtYtD! zg_LdB{KP17V9N^mlA0343{V$qsO9upiY3oWR2r=t03Kpl1uh!Ao?ST)J4@K%wS`HY z5!LxSnH^KH_35aMJru%E-^8bf|9tSRHAY0*?0HU_DUMgSIRhv+SUm z8I07OO=#(bUoieqXlsr=-7JkKe*lR>%?-2)#UA9U#-XfCJPf9`Qhn|Rg6Eedf3OwZ zSPTjoU*U}rIuSOCb}Pl7rk9X2i7Hhoa984+MNnu<`g)5} z^&)42@<}@WKK8j*sV`+$u*jeMUIB@E*V_#Suj|$)El@c(j;ShW93Ntk z0urj&JK+{=sVKj@qJqqGGVjQS#VJ}xeb-5qN@TYSZZCVm{ICgO763PwmV8<%z`8&h zDW-~IFtoa_-smKMW=UpraA2qe$P&EZ=PPdRG6G^t^ZT=~#s#En9&W|j`ZqReSERHx zv6`eelE}onvJ!Gr# zhPgdIGrO#CQ93!@n3TGxlDWJykn~R$h;pX5LNJiuzM%V;r85MsqMU7%I6B!TrIPO4BfivkX=9tzw(;eofHyf4b%w~;C%z1q zV6=+Nl+K)UNBGRgoA|tZ@>%%k`DIBaOAW3i)mi#gXj1bb`C0l~MPtp`@FL=U`uAUr zXJ&{Kv@GJ4Q|h}tow>A~cjRPunIb2LFeuX2EaSz|VQ2b=o}q(uB)9LAa7XM;mO#>W z9#k(at9tZ^o+2LLVx)CF^oqw)av(^RDOZ}Tf0qrkebtWkETf{!#w}FtiwkR}GSbCv zlU=->(bF8H;Pu*0%ufI(kAQ3Zo=eov9yynHz!lGUQ_JN!g|nBFZeQ=zRU*gc%&KD? zsAELB%*!RNQn}9!S!&ey+xp_1el*{DCj5dx{c9Omo3C3$5y1e^9@!m%^cO@LUiL9# zOYFSP(@VnG(F@#Oq%Y`kwS@=p#sv{dsv8%ZpLub%aps;jU)VxFiQ|heDG(p9Xzu8| z+GNfJE<-Mc?>5cQk<8N}@=k=O&Ys+G&u{ga+}&nVbs<`vkku{|46323$rU8@70VL* z`z;#Qr0m^GTY4at(Na#$!?Q%pNU>;Km${+hOD4HG(vt6Tgy>j~o7?%|fQa8;6%W@> zM@y$U{JJ?&)$fKHW^+n`rVN~P=u#-#U(EP(l%u%2RSxVB7(3D1I+=QU(beP!7Zu-t zZXE5W8pxBch%xnJE?(grd!+(zhL(YKx zPY2f4RP7fA*&e*-nTZLsX|@OB%l(dAf>xsAj8cX~2S`L9vX*8>t;#VR2t z26;sVA#m}Bfp9~r_>mmFy|c!*zhrQbpSE+@F)_#3=rwvV)!W&!;}tpQ#MGAmwh81Q zFiH~oPWL@Nzc1e(KR+eOGGB{7R{g$l6IQRi7QT-MX@3gB=)>qVISG&qd}OQ~E@lB? zhtFt`|LT=$50Mo_GnDV6v&33EnoQ~KOzTFbQTMQe3wwL(X^xE3FO`zOb*XCQ zvXv9NN1J-02r^@<$%8gA+ZDgM=_+YKVfvlEyu5rMAyT;EQuW*Z0rVlTTCm6WZ&lUR2EQ+oh{v(cn$Ny0Zm3BPX4PM0_D ztLd{ZgX^jRzxDac6x%WyGn5GuF1d!^Ex?J2HGgQ81ls?^N zoSdpp7_SHm(p%6b;$<|f`27Cu#2Uhl)`tfC5uPyM-+tKG?_4FNlE;jw)2AKgsT(aJ zl}%V^Z#aAu6UsQ&WThxrL@3Kop6IS*yu>tFOu2n6HU!d$bhwwtFLd zQ>`eTThLKrUDf&X~iFmhtY4xgIRcd9dStUzrH+m=cKcY<96 zoD?qSX`PEDA?(XMrDb-J+Bp_g3V3ij<95RBM?^la@~bwDRQkb;!xdqvdlcikrm-rr zluJIad66%cuAG1x?^$p4m!i=H97d-pH@3*{YDxrtKA}>m-3%vOEc~1WBc^6Z^ePib_vX4mg^6K-u5l!7E{IfYl-a3uHQJueT zhSP2I`sX@=-I{HR=c=CEGMm(nUht^$eoliTrZw3SnH5Z-ytm_>*mPAZG5xxj_+HX) z9HuTQ(9K{wZO0#(yM}CGPGUDRWH)9rb|T0hHB>iBYJ4znSnT=N7{GvjnTzOXgFmw8 ziaf<`gwusCGYA+~(5B@ms9^>|Y>sq19Dv+w%d1l<2j57i{kIj4A~(uqR9`08hm?B6 zg!vOZjprbzY}Zz*`5DHCEex5OGR)==Mq#xt>6qT_-nnJ?*&QVHBK%zx4o=}ZMC<|? z?udQdr+2rQOp*J>tzXXyu**&GFSW*sRrpcTn#rEydBzkEP;s2kTQDGRhf6OYKc{c- z(e#E6vTFpxl~t>6NF76iY-Qf|#O>KeNi)H{ApyUZH}p76ZAAlXNYR`5Wjru;flO$7AOm z+vAVNMl^W2*GQ~6A@9Z8yCc|dpSBlgJpT~fw0~~}Q5#_ljfjy5Ss}$Pd@l9`AP8Sj zd<*}+oBHZW>9!4v_N;zoVCZ;zMYu}&-%jjf=Wz%7>b@3Ul}Af@9KLCd}IL5~xiRIpZoLkt#G&1(uOZSCL4}#Ok(4yG>Lbt zU+D!8JTmDd?;26>ok-5#+CRb_{$ajwVXND88iAwln?j{kfOE9Vy$?uX0CezuyOt(cWbo&HQc zO?dVW#Js0b9e<*a4*~q0`puZe)h2w6#bsG*gkF0#PIYnn?rvC*`n4Fyd$z03--gs|0Z*a^D9p#O9V)gEz{%w#*z{;QWs%*=%of zAySeLigXJ`)ta=k-1uSi)`m*&UZz?oy@gDM2YgM)SB!E*2609qmT`SjxlTjT6ze#5 z7CN18^tSkbRY}Sua=b+}k37RZqs*)tNz&5bE6K29#hpC`Fgf{{`RbG9(t!wQYDfTV zxJw@NtkxHxU`XJ*1WM*R#t_Xg#9q*0@ZKhyJKgWSNKS@~SJF_WiHOG+?L3s0w)OW( za=n>>dK?vqSc9OD!=jVRNVR_0;}pU}QkWLOwQZp=HPqyPH~J2XQ_Bo&CKUzdw}ROd zczniYab*6u<`->1?I}zUoML)$8A$qTns2r3&O9jX zoc9!TB3e5^-DOBg(01`>ppRH?#A674%WaOnm~7`2O6nA-MVEZ@#|-UKiE7@RNGq&Q+8l7)E?H z2O?Qu{=eK8&mZ7@#^uXoyqXR;Q^@4NWdNwo7cB-i*rv;+tlH5}GdvtcUG0j{^v>Z8 zU)NNTy3_2x2#NnB!anze2ZJAWviIET!ma^3=aYbf&aWEB){LKlrdnad1)AX8F=e=% zwmB#Duq84W)){*NZk-25U5K2b-CcyzmZ|!Qm>*8c2)Bb_ex^8m2NGl#9IRVQpVrFS zE+8m9yeZDc zVQyr3R8em|NM&qo0POu+lN-5}FN*tFp8}gRvsIFdtc%o=yY`;#e_7o-t-n%s+@SohD_f&t9#0S>Yad@Pl^$oOuW^NqM--{G!euik#8J0JhBU5M$eD794i z6?^T3G7go_)j%`IDn_d-*68g~(HpCsOFEM^lDxn4U|X znZA^wP+C^1++DGy%*55%+3YMa8(*IVkNLix&n{-?k5ZwNQWh0<*9~taZ-r(%wPk9d zMS05?GLzNrl$9cr{y%u0GA)vADXSe@s?xk*CUGN^QkmCeA~gGd{{R0MyOY(L<%%UL z6)aivd?i>Z{<4*&NN4QvqqQ=FSyxq|ug=cwG|Z%WBooswI~=aHsPvinO`nBxH7oMf zqYq+tr^*y2$9{ae5qkZ|EXhxuE3)A-uei)b32%uFm)R9d@?<%6EAVZ#R;8KRZJBiq;_Ok!>T0zu6I@C6#!6P}?P8XwjeFVs|K-`EYFCIW#)~48 z3GC`eejzW$DkzRbs z)AKJcFPG0>E`Ia!#kqL?^5WSS&t81F;Ez@!7bUOE{WG~dzr2{9|7Lpe^8Lm6)$_}% zFD_>kKyHO~Jn&$dF}y8t+L{?9L8w$A_a^ULSQ^Zz-T z$oV1@-&R%eR&6VxuUJ)X#qkjRXK9V|Ux`hT@k;2kAAWrQ^}AX1vAU0MjGh1I7j6CT zi^~@;kLUk$wD)V#khmGBLhO!frngBVv|etrY{z7-E1qQ{W&iL0{eS)+j~=}@TFR!# zL?u{N(_o1zg?ktT@KaWR?#6O;$BI%VLZqxx?ius)Bi{j)SEb6PMaFYsdm?k7|MSmm z_7=6k*$;dpbiotBe)$Ci7Wj|ZqetH<^+Dz0%f<%K*ewpn&|@#W<+Al$o=&lcM}%>pCT?fLBG>|*4lWX><7nc;(zUr*TN&l9A|Sit8REOnY|LwTQ(!h6s`sq*)7BXq?##TCo9 zS>`P{!O8EPphvn8i5bwURNLaJeyy{(H)T6@_1LbN$oXH?Lr0+F|PI zpPRpbCv^pXE3$3Lv#`_fvzGa4oAEOE!z0FYq6%@v{B)$XS_>VTGM=Wei+T2@lzAn} zS1Q|Xa^Ky5)hd6(tMwI|nHiX^m9AiX@OL+Z-?n~5Yn84_nXmQy>Db zOSzpM{PXPv&x$p_#Nj7vvEf(tc~yw~`pxT~p1o`Q%vi(PIq{QguJ2o!3e7mX<2!SU z!wj<3+c&SkP$_h!XYBo2YIhj4C(7U+Pk3PsqvrJuuadQYlPz}4P24FGwH8YIUmH=a zReBnbR3-cN{rfj(muxL~D#{uAMrM^LHA{HT7J_XxiW_EC4*Bnl(i>W?)W0uPmZ>}FDs;$NH&0u$d#B2j%_rag{@wfegvmTL&k4<{ zHLn=YnKTj`FfH@8>wqEKJXzQ_TV1h4S-ROZ1hj`=1c!QR9^OYO3 zecs)$n>VGZRHCx?yF%OqG|gNxo+P5E+*V6eo{I$O8$!ws-duD%W7}d?@>H;8sW$k^ z#q1(nDLE4}b}Ko%{_f2WGxmCkGuxcIR0$nUAk5jRc`|3Rf^G#QwWxr|d~yw473q9p z`iYy}EZ%(b4bQZgPe3|0&+_F`BxWTI9~HSMVK4zv&Pu_x%9&cS`Q-Px?TJX;T+Jsl zwm0M7U;j_WGp(AF7LaXEpP9R^IioLUK)kuI?RCY~bTev~Q{9S^C7Cd4z{iTcfAxl) ztS^&r#heIocXu}{m&sJ5vQp(tm8-LInVA2*{Nnlf-)S7y^x5p<$-vGC2sif#?hF`0 zQA$;^54pO_M7k2}B)SXFpM*!_;7!)KwVJCLzsX2jE9^aKkB#oEfFjO$Fym&aT9Xbt z@E!K-ihXU_c0Kr_RD~!jFSXcHQ;W*MudPe<%H>}@ZS8nEsC-q6CP+Ev zngpXTdNDVA$4X(o!nS!p&)SXI_o)0Yn?k#noz-Tu&1JPip;Ruml`8dFDsDw~rsZnN z%VaGpkyP7KoEeP(x}Kx>yh;DAv`U43-016`6om~W#(@zmr*- z@G?dHmz~@^o!vY!YJ+J135Bns5WHlIT?a*3X^|}%$B{2(sVjUHrH96-rIY5RNR6to zPz`A(T%_;ZWo5XYUlh0)OM$&w2*xXB^yrGITp+)M+qZE9+I!7kjE$uRViix;sH}ut zn6Yn^(HXe80{1s62)$3)b=_r{mgi z>S9qaB76hpY=4$f7P?s~HQWDN2T7wFn<;9{sN+xYjeeFhex8EkXZB|BW^>k4Rj*Ue z^6zay&*|hUR}h01Zb>E$d-X5*E_H3yryVoc_=Ps4D^W^#oC_KADfP#j>6JFo#}b5@~PMq)h<|e+EQ7~SA672mF?VZ zTCIf^th%$7S$)b6}qz1v*22!Oy%Bf zz$#~+?!K)J1$-3+I&jp2z$SABHtL0_WWqE2uIcT3^0RmE{&EKY{-tRcaBZFgeN;|G zYCi;rq-#$ntv%hvVf`+D47>bs=PvKr)6QtnLHCs^#bobyS&2=D8q%kTn5_d#3BLKi zYMK6pFA-v zaz1ebpHEKrJJCrNI@X?AE%Q~jr-|XmTle^ruA>Nt@ds5|8JBBTh|=t;i}Pn+0OvFR zzPNmL$~b!p)Sr4+gO;6&Gnlx{o+(+v&1p95BHwOADHDhj;R!f>RLLGsA6sLUAql}9 z=07`$iahmp!fui?_JgwQzu4wUwMABECf4rZu1&DvAHNg%s#;&M%P*dFJs@-Uz(vQO z3tm;C%&*wLoXnrjXHS0nM?U@Q`Si;_o&0fX|Mk@U^yIfE^V#6vpFI8hEVrBBa-?TDYSUrcR}yCflJ@XLirfhM<}DXDAYHhcp-RQORB64iAsjOgq} za%%NItEJR_zt-1+547ypu30_ZnTbEh97U5lEXv)DD$jQKceS+R_M_~x)yya6x|&bC ze?V&2ReN(>zj^I=wD)VuKK*jJt+pi?JRH5i9Sdf~j=n)4+S>3Dm3JzwD5Ie_5s08z z37wVsEzcwfhJMw-Ba@08g^k`Fuu&!KnVhNIOd}HPAh+3Ol`JyBQmHGMCl#~eV3&Cz z@bzlozRkg&`oUg$Iaa%uxS#uO2?%>wD6OTrgTb&^@RC;|+YR;?ZLz)95WBvK$}(PP z6eRbYy)E5Pt4&*y?V@i;Cu^lNbax}l6&@m9!4TZBsHdV*D`D8Nb(?mYTdZ(oZi-VI zoT%ZFMVYu|YBS#s_8K@%y5#Ll!x4u*qNBg&Duy8hEDe53N~Lvu4!aBkYMZCLltMep zq7jgsgP)b#Oz0VVW#~*joq%6<^|G`F9rvk(v$2Dp1LwKDyUaS8b*dnwq4(6*pZ2t% z4@-dhKmXDY6uA?6+=Y|5Wn43ZqRR2}C9?>I9?Iz~0fP!#PHm z^vzz!#Z45#{X*OGguG-aO;2-Rh}I+IF0fA7!yo1+cDtZ-SsN%Acg&&o0oprhgfMJ# zD-92E{caxLNi>4*?{oR?ZW1_u%?-UZz+GpZG=sSZ>2D$2Ik2i*w$+s={~KPprP>y- z@4ypiUg#vcFq>v&7*HwNl&mBm11ZKz0`T2V;jrHyg$*O;I{##L;*xK(YB;d2`^82` z7mVHVN%)|@MsXa~jQwFPa<^oz`7!W+ky- zCvFR%b#6X+haq9}32tlG;U}lKqP-;DjtKZ5bVafGWIkahV1}^*@slwv9$U9AZ$CGt z1F(jm|M&m)FApH(voS%RbvnxH=J%UD{iNOHJnN3_H1|~)Q}JqO51e=g8NSjC1)2KW z^5jR*(p=!NenME77k%q8qMxzG(wRlLp8hYpF4xol<(J0(`^j$)UK@zvBu)Acfmz7e ze|jhW;dV4(=17Bv+nPRdyxfD~;}4xot#|MtvkSO-9VerV!mvlZBjzA%4MDL_%xg=K zV87`l*%B=SL2^i3>~W`IJ6LG!rPy4FanW&%$FOdZk|aR(k{l5%31Z~Xx;OaTXXQt| z_r$@RXozpL2dPawRHlwCes{juLU`?oH(qPja@a9S~qq-4&vi zAbUwV$r701ADqLbEf#~>)NV%02yB0Q3G*!TUM6k-W%ju3UhUkp6TvG~D2F6u<^rYN zCWNUwK{ycz7^)}qiJ8gA7|U6TL~S-8lK0a^Iqb%))r||U4i?PxeIeHTR;n@#n;AIo z&Isp>>)8(7ah}nk!ACPy6iPd1otw0iPU{}FJJ8Y1>nHk&w?+UwN^0d)@YBM^Gt=`9 z?Jqy{ITe`IlpE~cUYq89=&9xIIMqP_>#%?sL=M!m=Heqe0-C?S{-4$WGPqXOTxKoN zEj4L25GLKv@V!s7+voPn#&u?@*-x3uSI&9UKFa7N?LW7&a#$DHuE&s3A8W&qN?~|p(%)0` zyx6-b@uRac#Kv5oZ@sDVw7wzx)hiXo1ifCe3p0I}9sRQ|VZwC!KvZ{+c~|CXEz}$~5Tv2-JdbAf@TQ^GgQeiE7et-iOQ*2!!-sZ>uZgtwLFw^gU^j+dh zRc?57Wj6k^%l?aAZnm2%_Tq~#o_*1O)8j+K(z7{Ws~%pYYwkz8umg74)*!f}78tJk z85LZT!6o~;oxBuScA6jUKcLNEuLe0AnqZgsst)`xMEh5~bzz)@3;d*gf3)t2QE7Ml z{%GG5y(A;=k1>NA^C#0EcF=6_A_y-NG^{tw=tJY)_1_h(Rc_o1LnpFrdumx{tWXBI zcY)z;U~7uJlBEc?=EL1ca@mY=bq7d>!se)i>TWJ3tK+@&pZ#9?999{k{`g>aNQZOS zb!8zeS#J=-UMh*785_jVaV7T$F@Qa$D}Dhjp?iOry6CCyo@35(-$jhw`1&L7=zrsq zehXG2a~W~(&V*DF{CKHP+0te{n;VFu1A2dZ(R^G!-qL)wk#(cRhUb+`&|>BDp}Q?C zVP1F;FzABsG&`|V_vAFp#+DU3v4eTi&3?=fm#VzuW$Mzj+-Hnd+4RCKFqiwSSnAGz zr7?$0@IFnR`d$4(W9}>=qr^7T$p>x&oBd^T`4(mYtS$E&F0*YZyqeB{B;xt?b1Rj@ zy}aT1j)nbfh&eF7{xJ?YW$4fvVpp%1>?zN(r{=OPr7*Ys-X|;_9{@gJk%YaTX=OOI z+uETaB1|GTXZF;7y(;-~DU*?lu#R{%M9eP7U{R3>c?%&TH^2Yk`lsvH-(COiyRUDo za4^iSh45k9^|-?@N*6bTCSSf#+lt+F$1q^q@DG@5hB0|9%5G7-#Kc=q3<)oOCA!Ax z^ndVuR@*WcaNhiW91sV2WpgMXG#-S@JE_e!6}d%8uxHf8=GbG=Kew-!b{qz>k-tQhq0Iu^q0CUqkj5r{Sa%%tFS*( z2hr^Njzrj@|Ll>0dP!RcGRdn5PUnFz6E^`?Xs=1an>f+K@>}nPjBW2)ZgUo)VXng5 zQF}A?&j4e*aX0rLy|KFyn`O^KkF@Vd2h)OMfV4nh#hJKud7t2`7l}DngM=BVT(Fh1 zo%LRMaX@Oe5Ec{8Oo~si93knBuIrzY@^Z|9H{;fi0IpDjX|#+2on2ES0%5+3W5{OzowboNJGfVZuVl( zYhK8FH8T?TD9zQDmpre8IJ2liJV$q%J7s%5y-||Z%7(|MpO0aqaWiawo4ggv@u%DD z?d#xe@F(U>6&UVz+FE}q=V*1y1trWUIQIF(YXUPimO8xO>MLxr*?}ZSIiK_-g0x#>R>BNMK@Hz)$u?) zBH8Aev;L60&iuLVwr7SlUkD7br?_5}qwm_v4cNy75?>|Rx!Ub>#MiakXe33u4aMKJ zTVwMK$Ju>DxHnw0+lEbiobAM{wTrbRZy8D~%icTVw#jz8m)N^@i(a>(*t~XYtlMa) z-a{UV1ExE2W`-=e?KVY*Kzh<_bJByo$)wFTXgqE=D~x>$m_6TvlqJe|x!`RVnGjV7 zkhy4=9>hcA)n~;~q~}4SEW!}nizJdHheOz6FG`*s#_1;4X=+}!_oTcPxgFeze!@-w zFqF*fz?=HS$xq%qEtZ(@C9Ls$Fg9&(Ud|Z>Qn}>(6 zFj44co9FCxUTmu?_D^5GXJ<>LJbECR@@6+hnpe!cKhN1Kl`rM$3YLet8Zbr>v=8=V zI(Ib~uz7jG%hLt_>wIE9@Z+|+*G`{W>ePOb6yCZu_MYh~p4R~4%?R}74`h;zeOs?1 zNVo_$#{=bO$KHPPDuUw6%V+0wVJ`NpVvD)tu#v*jqbMR;qnkj?uR;5LI>XC!)REHN zyr-6HC|Y2YlSifRdPo9k+m0p8~2vk-6dlBfjL4SgowwC&s` z4szIBqqjU8@|SnFfMZ~0p~W;XAK1Bs_eh#`XIKLnC~$HwBLq%t9&G2`cA*U)&MQ9) zpWMx07WQzABB_4074Et)TV+~r6Vm%sb_fB3JDpZmLjJ|-4EaJPzVJXPZ zM=1u_1_3jMzS0$e($_vtgSr=))YWvui|GfkBj4yz6Sx04?DB?F^fj}t8+Ch_hCF!- z?{-T2JwO=U%@6?LVL;4(@6IZjs#-GJK}Z#G@i_3I@baaf*d{4^^H zn3Pw%DtYojl(jUIs>Dk)moF|}c!}nDlteS2nAEcS1qko)GXLcjTkx{}?`P(}{~Bm6 z5xfTWB>Q8a-CWEkr=hm7;Ed81g#+y5M{NZ|0{Vi5RtW#1B)3Jge;t(HZ z2oJQo**q3lF$P%LV82H1wf!48F(RBTB@RmO3G`u z_kd#t6;?Bs*(-9rwHBuN_~bSg($+{oOme9ma@h_3LTBI>updhi zU~xye!tP4#gPuq7yxR@RT4p;)-D+Wi`qeK7v%K4l_XFl7q^ltfss@{llBP*;MiKY4Ii$&Q?!6u;}+m6kY zyRtk2t{ji?N+$Zf(3-D&%+*b}I5$DDi(Vku?iQ4qwXjnNAe@Sd%gp9_*|eiYCN@!) z)qQ}9W~z%zX*r1INa_pycr|bYsN4Jy<49DvEelZ$Coy{2g9Ylc2MN|?;r@Y_|>?bInUWw zIAo2Gt2K=4o!PXFDr?cC4PPvCo`Fr$)(m&J7*Kta?sPKk)eqO-f6c!7;T>S9m8q`N zeKL6~nA%oarg3v?r_})mLz2FOwm0{LTrOp@&EiasfI|;;bn${QcdaNlfan#Z*u>4} zdw^=pw1NR!30(6fh+a00E_O!25Bch+ucw#imlxB^FJ3-BM*sm=u(aI3^%H5zZ6}t# z`}W7*fArG<1-P!;vU{r z=xC#NF}LR-sQ~P76WD{K1h7MIgIMRygQN$rPq};#T!w>ZYY^|Zt5Gq44`I>{kzgMK zJ=%rV#td$tp@-1GQB>~`?V%>?fl-mCc(mdus*PTT*a|JxIBrT&>z3M^o1I4J?L3FA zRZxY zb`W2Qo!ACT0c=2b2zJu_g^2)KFS5hH+FwgFbX^BUfv* z5sOmZS{w19j3mILb;}N@4l!#^0T}ISB+${$7sr4|yV^K7>3)m#LPojLB0Y+6sf&vk zPei0!4jogUo^IGh=K=B!E&Rm635O5qEITG0(%pH-q{IGBACnH*G4}Bo``FWtv5&{t z$7Af{G4}Bo`{*7&#y(;@CLJD=4v$HP$E3q!(%~`b@R)RXRJ=#qQSpvxN5%W7cpnw- zqvCy3yxrqR#T(my)TBdrINF!_epd)4Q;}D7M20K-j9y97NGpZKiD&HEUON~$aQ)^r zdgaX}3RQ83%B6=b$+*_8DiK^9r4V8GEf%%VP_A-HBKb;{;>wnQaJ|8TNfI#9(cucU zuUys*JuD@VK)jDzYX zaJ#$cUceuB@b4s=W(~Z%nKs9?T@bEA&m6nY61A%KTD0aFvu@4&`ruzt(k2#4`{#+; zmVAXtOKw7i&oHVn^+_bBeIdwKzLW`;Juh8BIhv#cg`Kc-IG;YW`BvnqGCg!nF{@^iM6p-`-K5l0DI}=$fm$W6V^+zRU~}g1 zDy=iFOPdx+kNUH8!m=LvgDeWDx0>rp@bq-NfLx=OtzvReLeAFUL0$k)L|p#OLTqSg zz(i6P!UQu)Vb3DpZfId*-9cEyS37Rhm^bl`AiWWuEwaQ%68@;%| zPDnbXrkTbjmc~@`8A>JXPY)d-+OGBzn-Ch2h4T55K8YK6Jh3w>l`|@N(5(He>*UG%ONfk2fvnRS$2P!l z(&JbnV+3_t>LTTCh)7nXr^JxWWA~VXNaRi9?wWf{ER0~{(Gn)O68*_?{y^P5jbIIx?X<@-WbbnCg|Nu(j6_$j*vR{;GE&WggD|^ z8BcnPNOKGGw8n&xzQnfvWVKPX!Kbp_Q+gUPA+42UMj+aqg4Ul_Hnx|8q_a;?B%`N} zMI?(6)5E9P0k+vWb+pI0}yWQ@b1#@G~vw`kghpaUBaHdH)~>cq}mjO!qr^c zk)NID?Z$M5XTCnWxBu3NcN)U}MrA^?8FQ6S|0+slvw==62%9Zg45ji#p=D;6<#=$T z8L#-rlPa9Mur@YJMIq25VX?}JQY4a4XPM`1+nJSH^^Doob7*a@5BpdtJsm@D%Z)$mH<8_n4#Bcu*y4D}ih?H0QoI%xk$6Nd?-x zD$V^mAVjGkOv_o?ZAkRCG}myp3%9)G`pH1}QnKdxN;pXx9aVL)^Q91}IkI6De4Z6+ z&a`25{oLLLa9Y#E6n#Jp;<=?pmr&?$Ym`?X+$ptB9i7(f`n&HMzSi0`6S(GjN%DRF zFRqrePQw&8ur>c2lvYP4=vX_jP`?i{NaSw z8?KTYq2Ue*wpjmGEqi@L4WkuF{~3v!ZJMp!idhG`ODVVR88bj#i9(NI+c+#n|~%&uBu92iIp_cp!wG{ zlN(uqWuFh0R<4BwUdVhkYaWEN&<`zjliLhXwfD8-H#Pt7pD|igvj{v{F&3kSw_3tJ zQSdjOo%S8G5t*dkXI5#LPjHO$N#MkdaqxHza6Z}WO0&m3L77{}%zP4fe=OZuE~@!t zRCQ>~LFmf`blas1JusrKW3*#>QyoY$YV#cy)aWtoM1&uEAd zt3k^kxo5n?$gLbTmE^J0q8z4pR7ajM8*}85(a>IEphVdfViI#}cE+e2QVXOPTnMG1 z09%NPnm2Cjr(Y=~6rqQ}4~jqza)>|#s-u`eZ7d)H%~7CcKzSI0rWad=(&s(&85*h#t<(1?D@QT4 z2VxT>WSJ;T8R8)%p~(a&F$9uC^(+qRs2@S59~p)~cCeH8yUmU&*-<5HwWCUQRLPDi z*-$&GWTU-1s$>D>zj>7`&aFZztR7oZ-8)J~#z-xuzE>!HgziWg_0AIMBP2hefI3|) ziYb~NS1ui6O$n!qR4Bb&0G*<5^_58PDSb|nhWm=64-`G8@RyE?*u&+{X(okMLG0l| z=Ohibr+RgFiF4Y>ON-rmuf@&pzpVNF7c`G3W$sFp6Q~l3nD1`@()fqUmv?LclFr>z zxO})EIe~J!Wcf%bastKuV&#+~MG$LJ-zqZ3b>tpfq$kb>OtG+b;2NaWzEF&FPK$`9=Avy)f z-VbWB2eCvn*{Kj;7WyFyLPwT@rlxD$x559IvN76@DI3T5AVNFF2X(h&e9$pI=olX~ z)Q<5%qrE%E2L+V>=Hr8oDI3R>jbqBjF=gYJvT;n=II3jWjw)GaJE~+ymF%dJ9aXZP z2ahV*=qK(qWn;W7ZEQi>y4dW<*a)R)+ht})C=M5)?I|!jN^_+AYloLu z#a$nuaBF1AR$7W6typWj5bFrh!7{D=C0IwTs^kK#-D$06DIo6m$tPv$xNdhXR zX2f7oz0XrK0+j$_&4};uVSjjC?vt+ZslBOUh+mc4OfbIUGS`)1wQ6JDSiODoD#(=m znkQ>pPy%93vBZrSN}`i9F{`Ujc+PJX9VVCT=~JHXo<4;tGS*0AXP}>`W|~)0`^uY* z44;aBu*EY}mfc;hU|XCmc{Xj&J-3=!_ll`vX}yP>G1|rXZ#|!3v!Y zF60cEkBtV)c)1dKw2q%z!uEg4Uafh_6BGk;XNfdJ$W=bQlUbVZGG%L}D-0j&&rg2GPEi zC^w+V$Qan2B z+e~s+tocGzGU1sW1E{D!`|N{X&fwp_3~A{-vu*aQ3=_mbbX;?dQYCEiUYhkvbS_VQ zRG@B&uj2m}+5!I|0!QTY;03w0~}a`hUTW2)**TFl+W<=T3BsZ4m~v%3c*L}azK z{BCpyWDw5l_BLc{(DFxKm{O!dJb572dKs7+K zmW@zM{u*j5dO69tM7-EF9rY2;C$>w@i)((lKp*bS%}u}NL~~ZUp3v>2$bWT^qS9E5 zb;zE_4LGfR>tdkGw)k=i<({y%(WqvLJqd&qvnLynIBwh^XNhdw(hj#UR5TlWkmTv! zE4*bDP|G6B9~VMC?DsZMU1gByAq;e2X2w}-vOB#PbM zReg$VrsXasQS9*B4!DX*Bs<*618!mx$+j!`o?XNbJE)#aD@whn<+mt-QDSInc+l-fAW6eNX7Hrh& zke&1d2mPn>$+&|;bk0Yfl!)o0I^;=yXPP6P?1U#f;Hl2{Lmlsg!5`*uw-PN~Li`?H z2)IN`Ubo`#84?~@2rEd=5KPb4k+#TaVG@6qc|@2o2Vb7rQRzUf7(Nwrk%%&1Y)>Hi zDLmasVvn;@&h6_Z%fzy>h)$|x3L(@^8wyk~SH#%r>xyN9gSG|Tbl6{JF71LlseuB! zlB;|gf=&@G?|>s+oL@etU5GRW;}o<{Su>}3KHQ?^J8vpGt{G9@iIjFaB7#0y*a7(02(ISF29&H@Bckn>NN@^?g!@I!4>eBKJ zI{nvk!3}|qTf2GI-{51-+$emv1_)s%TfOC3wsYvYx@)uhFsH6melXCSx`;A6CmyBB z{IP=~yj==&G_bZgPu8|`7K;fTr)cA^Dh~cVFcafbT^{ZhEUd-9%lXO~sB!QB7t7NW zObQSp4~BqF)Loc24-XR%N0W&bE(r%anx_uqbX%mnbL8e}{3_Rs7iO7DGot|r4bW00 zTe*7e+QCDZjuT1sZ`;1JWZX4LTZ?&*&aNe zeYbJqeb(N)-IKe6jcS8&CAnBkDC_iuw<^K0X*!@S{n z_Z-I=U7Kf;+YXxZ*U4LhGs*Mtyd(Lp{Wqpa1knpU?7YdiUe&&qzG2ie-8+!hU>X47 z&2&&8^jI@~%VowFG8?i08g80t#q`#qS~o}|teNX*=rkILT{b&N0ZgZAIt9)ZPn3&(2XQnmFE`;!fLsn<$;W z!yr>juzPE)46rxjL(64XA_Qm^6-&{ep7G>^MRj${dMmZWQ#G()01lrg**3*K0+*?#A`5_nUr z`K_?$hgIr+eDyW(x_1d%fJ3-k%|_$MAk|PbXQ$NaWrDXJd~Z6PviIg^U6tEGT?D2p zz-UipX?n5MkjrZB9V3A=yE9!Cx0f!~b5CEAh`__mA)y|MR=59fn1FZxV7Q8R_ySfu}iakYa%B^ZtvYJ zfBg8T+5T|HkHMFxt+5#SX$zL5lw0kqrDcr}?GPk{p9$JolX>-G*zoQ#UC#-dY#hrK z3mio3EL#!5OI_St0;`g5HoTHaosv7AV&{&mMS20U&M@Lv<*~MWzCNZSgx{1Z-6lqy zR?9j?v0i43wlt^Q=CO#6#jtjS%|*L`1s|f@bPdxCEZFHyUMC|`Y{bzp&Q@CvL9!w> z>^;PdqFzUwz2X~@z2aIp{)Nm9uZlpu0Uk$YZli7?a#+D$=*^!_>KL3qpI-gxY4D$; zpx;E6+GW#jx5j2~T4nqmI(3Td`*JHzv1ry9r~7>l*ZJ^NQO#-?93dK>eeA;y}r^Sq39C zYqvrsKM3Yk7`&PmBH^Iol?z#wyxdVTf3PJyw^hTI+YGVSX1Pkm{!RDf)P}wfoAIz! zilrh^`7)DO_CRioZqM@y8A>Tu;vBjx09Hs{Nddn!{%ekzWVX|>(@W9`Q&UqVQ;SA ze|u^zzV;7B8Z$owzlSocy)=Vz7zm7c;y}{s?J<+;ELDj~a>3{pjtNw`b(d(md?l2N>shwxR@wQ0? z!5?i_pV5Yf-4w9j_Fb-MP4079_H9Fob#S!8r;iEit))5mawETQyvpZ(t*k*|VBTNh zb3SR-%6hMy`)`n9vzjM6!Lo$f!V-q18Ns#bI{sw1pEz<`f7&R#nM`Dr?P^4r`GlQ> z_O2(R*r?WgLU!_FwH`6c zIER-W4Bn%b0wWp&?yYoD$-!k|$%dPGMwqLSpsCuA+oc-OPEy zyD9+=`f^zvSI{a=Sos(Me;6?Az;IQ6OAhP5cWarfv3PvYp@rJ!DK7zQ;#FT^`C-DQ zTM#M+5-O9+T6PK;bx>7atBt4zF&dZ)=@Lz^y^9|+cM@nEs!=Z z2(2wA2%7|j7c7rgi-5KS-$>HQ><1WL}A~C`BdGy7*_dYa+T>%ioCMrx6VatM>=z z@ofDO)oB;GG{PRtcyUaSRpX-5wEdSk6Dn^epAUBwCyZAvaIUDQ&}~?l$`c5mh2-#I z<<>NKvMLS-%^$%`=Fq8IdY$>C}VZg%E=f--Anz0mMa- z?Z!a-c+E^<&|EWB6bj)nY*@CL+!OtT-K~|zVvr}DLD+S(-dFE2hh<-=YK`?T%{0D4 zXmYQ55@mINg&P{sW1kC@f&ZrtR)k3y1G-??t$G&GxZK1>w81prU;j^*td*IBky&+H z2$^0xEO@5IE9#>z9B;qA1uXV7qt)B5Hz*ctKa515vQW(GiV3Y($w)0WJlynpm_aEY&r9gByP!Uyo2kH_)sx_YN{1ExK#Xu` zz~;yQZtVb9;$tC72?sR$A45V0wE!%+>?^6*D=jQQ*Bq8EP=su`r7>|w(Lzj(E}O0W zTMGcM+i-Ao8Q}0f7?y||T*~r}nxqZNwcFr|eP{RKAk81(NVYDilW-F}>AJIquYhKq zcG{gae5v~TEbh)4wp6+M%->mfP(2d?Ev{9xc|?@-(QzKU>96Sxcn!HNji4x?%#yNtBxTt^T=+vMy9Md`W^CSv!Yy)|5(Oqqd=jkuY=Yai$hIZV?0<&1#Q8SM9^t1&`V%7jxu1XjdCEkd zvR{6A^ypWSEtB~NQKr+RO!ZkMHburOq0f4KN`GEgRZ${5?T&9U$~Vr>&(B{xe-8hD zetzEk|CcXcUS2-`)y4A{FJ8R7ymWY`YI{)NvwFmb% z(#+nGOE&unr7*O4ep^+Ay)b|I<>Mb7h4*JOsRuQ^?l773r)I?{MX7#jRwu-Yy^vF9?{?a^<({lo#@> z*k;+;#g~`J-g`Il*`JhIW2GoE zo(MMi^@L6SJVB~Pb-tW@dU^ivkBXuu^sHntt6uKv0CyU~~$jI{1FGHRIv zK1c8e!&MVyc)p*G^pOX!hYn2{PmvSz>`e(W{414hH@WZbziO4g;YM7TnHiWl+0flL zID_A|e%-s-8-#pP!rsC(h+g}cJS69V5mHf%KyO29F$H7?!EwA=Sw$k zSJ>@sGo`nt8RT5o3H}>%e$keTC3aYeTg@qr5KR6Cf7%w1L}!HU+lz&$_yr1jI4sv8 zT()E_Hr(B;su210o7X=*d)N56qZ}yq-noAmcdi{wR1S+JyAU;(Bpb*Hs0S7{=Cwxj z@$kxvo!13UA(8Ubq7#Ce@RzM9cSii+n^2H-HIC}o+cX66uQ!}4Y~53kEz!Cy>auOy zwr$(CZO*c7+qP%fwr$&-r`EcA$GH*rK8_weA2YM(AMIzTFaeDlJmxa6+L(Fs2S|P`2xbcl z7CDq2b6+l$&}cmq@&}<>$5&OEF1J<8-oo$ktU6N?gc_I&oZZKKaekc*{)|(2=(Q3> z=4A3@`OqGqW!=VE^E$p|Vz^bQgim_7W^ATeN7gf=+LRQj>3epn8%%fh9%-%E?-X=+ z6u0p#H@O!dctHmceWG6%g51pVEy8V_*1k8iky~x{rk-l=FF2*vGg3Na5Csu7^w|<~ zLRm;+hwRwIQBSv?Jvz4LIYNZbh@NLXiil%CqOj}HcMv^5J3*O6y0^$D`V8v z_v-(ZejvEpq&A+dqQle=>eCoT3BA5P;2dxFOACwj5@;SrAAjXtuTjW+#W$588iJZh%9-V z0)?l+|6>5VQLzcK_Le(4Gj=r$c{|`HqHr@qUbEjO9j;=G?rH6GEXK-Q6hYlToi|J}4>29#1I-24?BYGd-=ujL5A?M!!!-s% zfv{Zx22kYv_7>dKP0m{s^4{{hj_kp@T<-;m;62Zx@g_OdL3UFW;fq25(hpjesPFLtw`qI8xpVB7rx8fY`w*-TALsV12)X8&hPPGAl)9!j*7c}1x|aNaF)>Y^brE=`uB+qE z6?8Q2zmt@8gR+v1AUsjCd61JQaRy`F6W22UksVAaOa#nBZAQ;Ca6 zV*yL@-vn0=n>_nC=4XFM8_Fh6MJry%$n~#^W4Hjpcl-m+dH-1UxSq7j=YQ)xcj}8U z;u60iG33T3&zA6K*l=uy?Px#tR3oac!r^AWq?5k1bu@?1(|YU|uOTu|(X_YBYLc=; zEcC^ZO=QQ|eenUtj7O2I9S#;6!axa zdLPnJe~gIxN$eas_PIqsp^bYXeNr^J=%ko8Mifh07V}_0cN?MYdXh+SN3_j92SWV& zWjn+~wuON?BJOa%MT9}hbGZwM9TKs`sT{8p2*N$)`A!Hrr&Ugu{uB@ha?H-^uzO-4 z|7}3ueO0q9?L(n63g4lCWmwd&@V`iHm8|I%mma;gt}zIS!HQB$ri^jZD}i#=4tP~qy})SUNplV+`9(e2hxcj4d^qy)oC`9 z(hRNPZ-(fKY2u@&X<7HWbpob&*lEUd-bVTr6TbRxj^N5LvY7M4$m0mt*o#Rb=(-U& z^!}fMYtIJ@3;!=GjKb%qcIay5m%Fg{2=I3M^W$12fxXQp_XUXkg;KPb{~thRofp$R z!(|EPe5<124OfknXX{Ej@h?=Ju6Q!b7aXq#&lys6#*n4m6#^sx!ry`5 z4N%75_6iTkM*x65pX5hua0&;)f}xX5SO5$j(CBWQJCnh=SP&iv4F^UI;6Fm^UH}vj zLy+H{2cQ_>n!TF@9+(1SN|Nh89LwWhA4pgr5=`;}H$prC0&&<%PBQB~!50OVzX498 zmA^d>99N&-t2qlU0LNV2v@th;O+FySoOC)A%N#HngG2a;02o*R$jA8$08Dfc#FM-r zdb@mNTUUi-@@ffVI+^n1Apkp&qV$}y-f-BG0#Qm zCEn*V7af3*diF94`dMrZ4Sm0KGq?~*N`B%-ymzxeLVmuw61Pb2yOJHnNtJg;e2jXZ z^Y3;)aaJ?I=UVU=8p4tHzYc+K`#Et7d&{VQ0ci=n{xG=CD{u4Cd$inig*G7Dd$jxk z7UbZK1N`hYG9P3_weOD&W@XA5L`Gt+_b%ZxmCiymzoCewyZDBccntImi+-<`cb_*J ze3T-yn1wPz+<%|hUr$cf%7KrZ$z96`ITOh%B&T*W5?=@kw;5?uImV@5?0iD_12zd1 zCf5mC`q7O#X$SDi=qLN@ezyMOeux#vk%TC^8AcuU8rLRymu8kef4h2GQS_qB1(;An zFe+j5o!VR3f9F>9d9aiaC#(FS4d-XKSO;eRV}9_`^iI6<>Ro*DpZxog*t|+p43F&e z+56yvJ}yrr@;2(!N%iSW`GvT4l3WFfhb~&imZg8mkG0$y`st}_%8?2TkYz9OKp5jH zG29`wt^ndjV1wQEwgTfJtVr62K{uLd8;)M`K@E9mF;*{F4WX4^LrURBYDWx?ySIaY z&{4`Xy+46tJo{}iTKaADc;kysrvt7x%E#;Cr77-6qeDk0Z4w;DL^A6Bb~zZRhx3x^ zIg#FF@`M+N>!~4)8OV2;vw?~6rsWv{Yq4O5$#Tv`2C&Or)bGG-78rVYK|Rnvk2QGlp6V-dNw5`Pmm@F-Bl)$S??CRjNB`}r z_#TUjN>L|1DA+nJFzwDd@eYJ;uD%{!nYSZq69Tpgk1RG;EvpH=qHD+(&hnPJ*{dPd zTs#>$t3CJ+j?{z>Wt7ZTWOWZPI3*JqUnB@zsp68stU34PnE^Pz7W=%de4J$$~z)Jh1=9b1H*^jpwwkjce9{4aR&{NyQXaaVehvDyfi=aEZ zl0j(4e<+9`=}z)UetD)d8%3>?M=POH+Qh^NLF^JI+SYPj!XIVLQQlzMPoo1pURTul ziI8!52=qkBpZI}kVX!ORrhJNH7tl4THECApZ&Fmcn!67+pLf1iRs5EiB{OSY{8{pm#MO~TC4pZdNdbxCO`lrZ`0r+jp-EJRCN@Xum#dj;V^3TJM zSO7h+Ttc0jZO>eR!BOctJPLmR5UKwwdVpU#-J*D_){N*lZHf(EC?cl@5XjQ9Nk#J6~(k8Dr9Dh%Tt+EYiCxL$+)T0A~Jq@n-~EX z&6}hPkyO}SN<1#x9!ohiq@81S9&D-`Xa7KRgbw!!kbpb}GYOviiGbo-rqg z@TyIBT?=9JWv{lx@^@tQ(_Hlfh$?+sQE#~#woWwfP&IA`v;`U`;$+R z@9yxYodb8?P4xxoV^oTtAy?6Eg3;Xx+c3yY7xZeBa`Ex(~ z-ur#~^Shf?D}6MyZFT<&F1{bPOBl@F_q740E@~CGGktcp_e-YAbFO*x40>&hsK*;8 zHSo(P*~s4(!OZxKTGx@<_xg~f&uO-}wiH`WhCdwlT`VrM{d?9hjXk@tjuG0?H~sHF zw4LkZ7wQ-1$g=X8iX zV6P-!Mg_V$(&goZZBN&I9>^<4Y_#f}7V-LkEvdm7wrBfln=vZtb=%xvyg?1D-Ak%@ z5)trr^x7L|;MzRGSdlrNd(A*xNOzfF7WB&M^ra!$k=^5@-he6g;M-E-BHQf$o5XJ_ zI{3}ui|g@H?fg$N-+mX)(fSYlq!RAU%+TuVR{Bv?#qkJ;6<0ai^UAQjunfzyUMspd zZg(P-E}%x;1me<8$h2jrB4EI&wGc+)&D?QUVKV+eoR)bdPN^~I{19Q~6hfqhd($~> zy%A9j@b$J3C&mZH_+p4){SZ38R;BGtybnYN{54u%m>17}h=FBABvEIm-P#WDM@H2} zWUy#$+sXh--d|KL(>9<7BO(-zJkDbyLfc^W6I6-oyBWI+tBPi`f;+@VRz8VGqLG#W>{cYP@|FV* za@{CggtFeNd`HKvk`@VhV7i|g&D-gVkYh#-hC`^8XbiaYphwUEj{qm2edu<=W3Im+ z&kEGCvRCJBw#XKlCYr?N(=g=;#wHn)jGGCzbWqHlBjf!%`24EhRQJaN9Ky!5^3o+w zW(TUYH6_(@Yiq;O)nPEc{TASUnwKz5I>V-kBZ)YFHEVK1kKv>i^%%^dFAycG{oMy1 zATC3V!GJozx(8%_@)$vEhT@G-|NH6r>EY|a$m@N0^7IsfW^W=b6}$Zpk!;6x_LBSV z&)es1Phd4tnl`#!48?|9(1mZdw%frE+G&Cj!8%bNp;2p%r}HfzpWg@dK{|CR}cfVDetAm?#`yuq5|fpzqO> z1-cftWqUvY9uSncVz)3wcxH8&vS{uBV{B15n-}H)eY478JEdriww1k|8&*NvBTlQD zAR#{9oJVy==j42F9D#zP%3!4sGyK~lV2;9~!{06}R^GuDKXpAESc9!) zxC812fI9x0sUF8#HsJ|0`DzrCH}GXLnpj;F(Xg3|xTPFnftN66==+3si-H4krFyOBOb)C z_T!yMl~f`^#s<03Y+?ufT25^a^|qu_BO@^z4m!7~bys|z$GLrNyc2qBb?cg{qAJJq z%+7$2H7R~R3Bc;PUCQ9&_J|mZ`$iNmrkXJ*w%$cCbz{n%bjGfmC8}`oWkzGpsc>B4 zZl79|Q}EF#n>LF8GYH?t?G4qLN>|vmLFpbufN0}y)Fam5r-Pi3o|+Xq`7s_v?BHNA zj*6W^y>5|$>_EbZQR>&)WRc@es z!65^ahXTT5!|%tBVQRcrGz1tlC`kNUt@mzmYTYLDMCWUXsKR$G+pvIhqiVE~#rpfD zdC_5eb&uogN)^lyj&aPn2Cg*+F`I>egmUS%-kyqDIfI?$0KU4zog(Knm#d0`L}j6i(@$Xw8EDWbrKs|ccN*E z_Q;TY#np1(2x;nSAgS^k5-hd{l#nG_mG%dA@YE%$H|abZ|osdV)pBH4q8iX4*S^-ay^Dy z-@|mahchN@&YlE^O5}PZn?_QA}woCjiy;f?70- z1rZWNgJOv+Co7lxb*J-bXLd$H%o$NC?Ed6wrQCEa({cVa1hoM5(ca zK5OS8Hm}{ba;Nf*A>=Zpzz#r-(4)VE4>*=to13SkN>Wtb*fxDk<4!PD-m|u;zmV@* zZdY;@F|X=newi5odRoD}wR#-ytc-jI0m71X_j9&GE+OEo`vJ~4cs!l85iRWIYgB=R z8Pk0XKXTj2ZBwSV74j_2_dw1lts*fdmb}`}aJ2*Nl$2$+h4E)PW5UC&>sl}Zs4=`}ZZaBZ|6jvrOJt($qCxj&d*R`N^L)_0B*M#guyKcQmqS9{7b z7GWMka1WuP-UA2S24nJYL(+GvNE{mXy%*m6FX4Pk-Mg)Q8?kJ-(2O{!?~Ut%65|c3 z+Q_yYC<$hV^(%SAZX1{-61^|FZO4(f+yQKlh*;!(Bsuz#=>O4s}~uN;sGvDO$;S@hts3^P=J2 zKIIhh*=@Rb$~j$=j&{f$CwZ;r{Qh0Ef}Y{s=Mi^Fz0r>2ew>}DIGANQuo#Y%N>F6+ z$mhdt?uwcL&Uf1+q5~gv<2RXjN9^RjaltR>>f-)|1!m$P4a9*O^4vt>xQIis5Jvy~ zo$mb=P7rzxvHiP)PssgubKKmv_}LAy%d6r&o~`&WcPZ|FWb^Ow=+`9v+_Hw3@Kk7?^@Q>HYE;(iCCdWA&C z8paU069>m+p-c^CqmWuw&vW&t2b z7e33Q)>(3lPy$wJkB5M|a5yzi)47~oPh0Pcvw=1u?m#uM=>L2Qfl(C3# z9j3ZPWljM1RUj|!&^tLZ7KKJ$&Wv%M@zxC+ij398S}Cs5Yd-dj^^RIV5j zPEVL5tw0%+wW-p7Dra6GkVfpG=DQjKR(%I3Y=B(F63vY1Gi2Jj6|%;IJA(mzycgkZ zMD_8yDigKXF=Ts{Y4OkD?$K9(9WGsQWAFkYN3;^}C)Im7;h+%Zn=Q=}2Xek3T=}Bk zcf=M-|Ivy?e!VlUq#jaTXg;!_GH(8|^(SqCw83MVij^W3F z1hrlmQ4)n~$bLTdj^XeKK^TaB60*6(BY0vXNcjGD)9v73MhQixa;Tep z%CtG3qF}2M}$B365jvG~6t6X}J5P zV4H|`7ZB{O!C71bvVzATX4$9)~6~tS~_Wu(0N{R5w+_IK*AKb_7 zj53|m6?o#c=onfB3=SvoGI9$N{l86r#KJ>G0pKaG&<&yAe1<>6PwoE-=&MNfloR>X z*6*w(5TtBdrz~I>a4K2UwR!A7tk*a1@TN=_p^aIicViQ7sw3IgPi;d?cS7uZqqauT zn5w6hVQ&Hq4quKyEr=?VrQ{cF!6J&Do|VLmv5kAvkc$8M%9B%X8U*zIp>bt2MmeDZ zb}P#^w~p2X-DS-w+iHPLi0O@M&);f~a?v#|sH*<}%k`EYB`af^E=`M=ZSi{+tqvBT zx$z+9P+;gR#Muds8)=b_MuaIx8HjUgg%y1l8x`9-((#*$DU61CjVcX{01J1@hMxxm zV{c9hyb-<*0u0oD{Zrb$9X;c?K3xnOql zXebUfE%YA{t~wrbV#J4N*F7I{pxTYA-&G(i&iHk^b;mA`TLdf~ zPA4)4I=fFe4B0b}5Gw&JAC!56z7MOL1pXg*X+lB!<{=Zj?H(~feUAuIwNS_aumAS{ zftL%g5N6DQ{Weo7X2QasUh5{)bXx0R{ww`)f&k3)kuq?%pJv%b&K#JY##w|uE8c^D z0aj6)JykP5T?N9G1Pk+>EjEh3Y%Oc|ZP4vJC!do7KstXbqEqfHR?b)gcx~OTmBN>B zG*9EeiUX@PoJ(P=E>B?X< z{}`%x(o6Q_1u#GxZ@&hG1xdW?RemBqb?-mc%pL0`BH(9HH#^#OC~^nS(Q)|NLxkEI z0-evf9qdmnb65QbUN%~2ns^tce6p)sMe`h`$3RU?|vq6#n)$Hy9vkKy=|U z8xdV}_F1B*Q_D{G-i0lzc89GwwjPRZxBdbk; z9z;5Zgu+?>vtSw@jG_nl9lPcUi&i|T_^b+IDw zRBvg0Nq@;8coLNrWdb^r{&Z&aJh&A32nZ*FNQ-4e4K-Xb zupP^wVvs8Ir5IgztfH3F&i*e4&2bwr;_=pd&XZ^BTDWqQS2$TAX3qn$X)8`d+I~-IpC|Nnt+xjxcX|& zyNWQqPgu{JC7wJeBpTsus_tHz*|qwv{LWP#$dvigWoEoQ{3=N*2gPV$Xj)5u^L`R+ zXkX4R5!x!^j~uo?F0UYo%R$>=8ZP2`h+a=8j*BjYLLfGosHLe&*KT5#tzyQvn9^7c zvyNR*0I-$=ZN-0mn5#oU@_!c=NE7!@mL*XYETK_gq zN>CVwBjoWHUkPClE8P?#!X7{)UW8#73soE*L*e~c1Kx|k^(S#d2wf{H(b$2v({MR+ zMp45+B)wYnkoiIqB?!W;7fVpaLY9;RYb-SO6CKVJ-}fCR@b!fnL3dPw8+|SmvJ?-n zKyKn0yIW_aVGFo`aBw3hU{id%3Z44FQCLGHSR-U(33y=ma;2|kEt&Q1TV)Zj)+)PI zREssTf*&H2$X`)(SQ!$Q0BVE-sDvbmxX{Fz@Z$Gy<)>f`xH2HWbhHmKtfoI}}O6mPv1OS!2_5diM3=sYuenQ2ih2w1N`Jj*;X zZZR_fMHsZwyj;hTD)Ffy&fxkk*u8?WOM}J!;i)m{JJ&-`?2C9f7j_gNp+WFsNEP7% zm)0`vz|;g*3=2GyKZS^yz4Mr)wF#7gMBi`i=Saz>19c>ePGuoPlDv%va^-i#M3s^$ zrU=tW^7AN0dK=p1({HugJ4v|KZ^2_Ym0)dzo_I@}0)@ep|CFJjC#Vd9WKc`O_gaB% zZY0wtW zK&5*CcUqj0)tUCePD;_!qBn(XTq=M0MGOUdV)TQ3F^Wh$$To_VdLIeC#p;kBXdx1* z*l%^kKrugy-8^zo;;VIs6%~SBp&#jwNm#pSF{cA;7c9{XDa8cFEkwQnDYpQ~FI3C{ zrL+)8NSH_(l%fzYq)B4|nNsP6aEG)?Bq$TE!n-uikuDi5wF~Vj)Uh4Dd+yH{0m$x1 zOE;H{Yjo0n>aUD8D|vo$;|<4W8m22duk6$3gV3j>69pnmXlIZYAOj@NDd1pXuma~_ zt^n@_Y!w1iDpcG9-?0E+DP%sjJx8s(M|B?7*F8>flj0g{v;IWMEL7o?@VD9NPpdHA3g9KolCDcJAe%T@4tprj{x_# zi-Gev&;ntM^a=roJH|kv1jYPc|0-_l+#_BVDBZx~W9~g4W9Rk_GK4X4e0-((rC4d1!RX>o;)@5$F zo&NI)A8i!I6o(&KMZqdmisPWnZ^E=O)0RbtrBWi347QRHaU)9xgu%~5n@u_bTKg?^J)$~y>e6mANuFX;+cBA=F?bPX~Y+>hyR$co4twPA(80Mk03jL_5Nty#6{9DUxy`#o{vQN zXL03BzB`-5N$~*u`cG3pXmUZ3-JCd?&tMCv^(*p=d2X3s=*~JXzcoy-K$CmC~J|T?1-GBsq64d_DX=to@ewE40i=8V=v$ZHWyefOhSVt0VY%H#CqtCKfD(m-fnz$02It*^`FIX&5H=9ai@IMh5cSb{Ist~I{5oaK4`p$qH%i-uD5jx>(Qld_wPtLkn^^H2XlRYv~^4b6cag$5`74Sph|siV=HKa zeua__qc-sJALC*dbPrI{CBjjy`+0F$?xIVX>!Ej+Ik3*H*M%#pFL{Fi6I+S z;Na$1Cf!3wtkqA}ta;%J0L=lXxjn!Pq;BQUMK(T0;$rTyX}@}VC$aFEtoZE5@hfF& zHRH`XZCLI~aK@48mGq#=G#Phi!xPr(nQ5MZjhZoKRk)Qh({LU2cdO&F96@ zHW!_RFuhiA8PkcbMB?VbWC^aG%Wb~I4+@p!0zA|HDp^%m^K z+0wm4ZY@VnLl9e{?;>guv(Upo<6GIyO|e>^d(Rws#X*yi#zE0UZ%G#RByq=0OWylJ z>MG_}xwSS`kVq0EgkgYulNYAE?|)02Er1|a1tjD-{P(4sM4k$l>$A5U%pM;P&VRr9 z61o?YvXC9hZt{>%dpdqVvJlUcPj+@)j;n*aAx!nx-0OrcXvB`Ps#euC4ldpcr=NhH zlLB)3*>!K9ff**S@w1)7PfVpsEW>(*l>rLkyAy@MMpQ))wZjV$|;V>dMfZ=%{O z{Mcizldfm;c#B0{H<<^cb5V=hiSf@zsx;3~S!5EV>ld*~$nw{tsrWCHV&&faTxX&k zot%i}LAG?0T}oP|HinMNC3c#crrZzg53PvU$(IGeEwq&-c47o(XYE>5c5Gp^gn3Qd zf?^Zv)VpXuJRE#nrm1(M9P2H)Z*i$I@jx`~! z41zqs;rEG(fdbv9;smEJwpdHU_EQZl-Voz$uQ_%7Q4 z58zbQd*LAz1n(zI;#AFYu*~ByS=sSq+-0IF!a2+1Xx9QgbZ3xt=Pd!r>$1?5fT0Pu z(5AGyZC4NXO1P?AZpe2cYH%8!!~vR5%;B1p19^i2kG$?@EFiY8abn_lx@oJCs;v5D zLvfIP@g-}~nZ=A0xTc}z@g-?a1DCV~-6_~RIRBX)48QE&Kfg@4tL2*#%iBPX#RJ@s zeeiGTZD~cF{zSAEs7J=N ze<9OZB+`nv&I+nX7!D-_6k4{4!K%wXNvGOEmzjY=B&Q9fbMpDUa~nbm37wgHwZf<( z-&b!`yhm?5+)2q= z(X=e@IaJa5QPE;1A_x}ZG;|SlG!gXxB~)(s8T!GgYCNJFBf}FT8R7|%G~^5?NQ_b! z^`#8cB!X{#{0y_0K(W*l5^FF4;xJU@bvNqKgfS^NdcHV?0*-0lb=R)7%u;Z0(E=hw ze~2~eJbO?>@%}-8qep)cB$h85JKm5<6QV1(@pXDVAIRu;baiz(a>|R*Ok3#syTsxl+0V#I6KBAd9z!@^%gurPQKk3xYc!>kdBoDHp_zng38d>#WM$L$t2%$FCY z?{3?Fo{x3Jm*Yl8w_N2ks6eadlg0{+Te_sKS1x}IP$U5msel5f+wJas;}>>%Ke!-8 z{444HAjeFVoXZgDLr2$=&Ti;+u$-ju<#C@6fxSbhCB(KDZ8^f2RiWye=bZ*mu!U1j&CWI=x!99a=L0Hk}4F z0U_;Q7KXR1w5YmhS(LX`%IS$Pl}84_k^<~#+vwhHQ$K>m+jog&%9gQg@APyvZ5?!jaTj^n2yM0Z*s8nn= zOzx0CD#;M&x{FDlJCw6lkNvXpT0q-T{Y`&}aIbseUKKtmtp2&ChhdFeJ}zd~7HlZn zbX301fxkE#WSRQOkh@uf|C{e#BgCcvhtdS3RcAYCoqU4SLLl?dh2e;BnHB45kRR2L z&Esz;=5}NK^Rr{aeMk&PIwl68^gP4XQrgQAZn9anRXvOVwNR?_7d)(Gs!$9{gJubpba4t6!)n)j?|+C$n1@l zOSmJ)oEBkuts-zD`C6YBnxD!GO>ZE)NY4xqf zU^e4+rH8!@*s~H$m!{c9TJ=_n61hBIx*_mSvqDx)7fu9Lc4hqyO*xAcDtlsFRTj!y zig16G6nOzI@&Hpd)yV;)s6m*X$yMWI`k<{d7_(3f)ph()r_HnwRJS}@H=76Jlts4x z26#{ft<^0)*tKi$o+Z3USDP^;AqJRM$u!Sqh4MO^bE&3J+fwvQ%f^{fZRszl<$WSe zabTWmXMJzfB3q^RGnn-<=FeZ~EKxJ<27nzdjpZf8h8`2g8@QKbjk0^WDxLRDxXQpx z1%*EO`jbmpx>Nw>w_`}9<>m_mxefC+Z^t$V1yUb1w#f_vkjmqZi(sme=PwoHl>%OB z-O8X=A%dZb+{v9h42yNZeXvYL^r5RgZ>(6dcL0&V|cWYoqM&v;*H&9C6Qw zO!veK3p4o;e^+u#!(DfhUDqFe(vT2w=1e@>B!2P|9frjc2%Xg`FvEKQii><%y}}DLP-&Y;8sE z-%=%Z@ufN-8VlsSSdDk$yI1g<2|{Syi{!>;7LJ0BGgZ?*xulpV&H~-w92HPLWk0I) zx=~nfZvc#r<6|!QR99udO^H-u;m<7JQefnogjLKk$QGY&2h%F7UjTOx>JH0K3&|1$ zPGS^yH>uj=TJf%~9+cOfPkEJ`1+0i$&gc?-k`B_LssvzG zSNggXl%p>|#C_30I_}Y~<3WqzbU9XtHl-VuWAJa_ zE8YiD=rK*8ba9dzGtD@m)E(z2m?Xp{@y}PZTI3u^hwsy)#fYQ-sJM1GYH`-z3ofs({Ig@>z5g6v?5VHq-b zt2R2WnYX9NaHdNy>=vN&_Fy9P1L{kPMI3yyfD92mrHFB2!-XlZNu`&P@pfaM=nsCa zTPrLM&*sU9*RKo7`3X&dej#uD#{Dby@7-5-A(OVXS36>MoA_T#yz1&E>*sc4?KA$) zTnm{sFXnGNdFQL;V0hidK+Ske{h+z-k`@g*DK zfa@`Y6`S4nb#!8!m@hy|b2vV*UddWkT4ye?&R<=k28=~GRs+CeTeuR!EH$8sQc3MZ z8*@{E#!4~@RC<)DO(Gmv|4Q^u)*mDhD@@F81oQIMlEw2G30}A0Fn|PQb@qC5!0y9b z1jC#7egz;cW>vJsNL3V_5nSL0$oBi#OceUAS;)2b2L_d>*Ii~Dg6k0Jb}}10X1i5& z-zU&`ZA#g4)Wy9G8)f?>0Bybug#TB{MI;C5zPT;X9vQu?#V zV|t5Wlz&1ep@joX_Wq-w%SB2X1xzM&ld#xCtvPXvlEM71N@2fa-684i{$51PzI2~mC_!8posF>TJOJf?@Y{vn0r#ol)1a`BJY8OC_}BUn%P`FfGWrU*)3KZ9R1YD+Cyv#0bKpQ z!=tx>!F`UG^WLgzh5PaDz&w+>_&R5^21C8WSFcNn+3kB(&0fLZF!P*<&_HrISU?_v zZr3;VvfP!eIOwfZG=P5-RL%{-hb(Z_pAD|N_3Tm=Y>xt-I(jFq0_K0D59Z(|8v+e? zfy>C4FW8tLNPjM{=30W0uEMMLi!Pmdyk1Yf4rm8X6DA)o>%tZesreB$(%+cxKD9H= zzcUx}o(SW8tKw9=8aeR}qve%yRg!|y2Lfx!zEV@#FuSt0?jMBJXvB=Z#XKY{ArUga zkbVxwbVo{ioLEt+b0e{|LL>|xc2h9Rr<}~aBQ{V>msW0qiLfl^EMBlT}j)M60w3DVL7smy{~+cV~11Tf~vh#%hm zobLWStN;5sIy-ODhyQozZvG9Kpm*P(wqH-MV-#q<@~lQMc_N%)L{&}bo(?p#X*Y)2 zJ?=|5*<*}xfUA@2)8s}O%hj*k2-h!m7deM=^YtJUdkf*!F)xJ%A_IO4Ix4y5;$I-k zdRKPicrc}U4EEVR(?nABSaZJ5h8s`3x%<4d+4)zo=CqL|5PK#4^1TWlzX?z_9*R7b z{Y*zH0mBFCAIXbsNU8gbzC|WOs>FmlI4O8ik^A$)VX7iv-GnBBrR^Dd?Kg}I7)!^) z`&sXCZf@LGlhKAv5t;HRfEjePnW~0`ilP z4;xcn1`jK_AtQ|hDln|7+GYNY38Pi!>6cuq)G@UhH=uNcCG%SNM%5Q1VN&10o-ubU z&cGzh(jaH71lg7b+#EIQEq`irxoSq2IU<5Tmo!B2lfwWM#9g|K?=W4`2^Dw61@0Ox z{lBjJHoXj2`&YIHTVrD~ltm?{(#N8KJ< zqCn}{ywUr`u~3%uZne^?4}@vUl#U}qc%vQ;@^U-1b<315{*d3e0dE(0B(#`y3P&58 zbU7Ri%`sx}Y^9M!Tovq}dlwnH3-WXUV^wscV<%a=p`Yz|*#u#=81X8WjS9N`rh+BH zUhp;?5Q+44KE1uYZXq zDuK3?2Xc3{JppU(2TTNA&D_NwklLzlyZ!Kzc( zv~D$~mlslSgL|~WhBTJ|=rnIcV63n<9|vd?+c{8?kPIbE@~TfY;~sgPU@+(=9=@mOBm;ih!DRb-|Y!JPCFp@#5`Whc(ij0 zH9Y)}u3(>kN5$|sxL{l<03Us-CCTZMSeU>DpF)nf9;q<-&oOX)90YhHcA zpzzJ)g2H|09h2$QGBcI5T~cbh_MqqI7R4Qxig#RqL zz@059j2N@)_W3MjSDBA4mi63Z<$_{my;Xd2s#P{hAm*I1UIGrC?`VC!y_k$xhoTh8 zq?+?BBfY00A10#s`22QWogme*>%ou8NR&PHYKtqE#E|Qum3b1HiYV*JB&ncLgm+X17vJl2dFbZ zy^c4+lSydARePw8ZoYAExKt@;tWj40I--_SJ7-<4)a`Quv5eyh3A{jiwOEj0ahky+ zv$EdSxUo6u>?2sEu+_*+R3Oe5w`Y;ljXrwGSWs3#b9Wt&oMZ~-#2RN5%t2d;+PT@M zO5xbZn>cFsZR3+%#;ifdx-Q_rx)2QB2^~RH(6o2EtD)DYV@N&%NHkYBaMID6w34tkTT*{oMhl(`(*vR zFfQsZO*~i$Q4^BKxJ%UfY~&?Ij@S9rJkwLYt;(sc7J?Irht9)}j?NF(-VdlsW*G!7 zaf!0dqInmFs%k$Y`eC5CR=slYiTS+h8b_PuYv{D@0^*#M{u){_m{toIzQgHFE4{~k zaF>gwPn617p`2;q5sS9YrJ=KMB(CxhE8}fgOl)l^fuqy6-3nHPkn=lI>*vC-DM*pg z8W7i^5c=$!U_Nhi?u9Kn6n*a!75ZI9g`K4AGuVFT^@&{MUo_^!%b#3r{IAhV!8i*S zxfbM`hmMx~F(hV_c7IE9S+@*E6!V)Mm`}VrA2cv9fQqG528I|t!Y*NXWPU1rLXHdC zh63FJ?fNc!7In#?Gg#!sae?pZA}jftlc2sE_xy^%sjsEF?(hIQBsH!Ui6)M$8+?9F z--)@Ex>X%guM~nPoWVdARpmR{q5n%p2sBm~aWpKyXoeOUMjuJE$Sk~XkBoY zr|A!-M`ImMBdOZUo3_#={}{Ynh{hkb;s1k1P@@g-4(=X5KU#~X0NGprZN&s{=+F$} z0(_hsW^CafmghHV*OvqaQVNu<3tI`ksmxcNV^h$MqJW>?4K5y4z*>wq+vXyii4;T-oJ``P69$Z0i}O0@IKEu=!`f z$fS8@uJqHG0L2)W9T`XI?Q-!$PbJbP(IB`|=1mu<&N~>n%zkRUOx}z?Ip5z%{ixHo zgSz&)s^F4@a3!y%D2Z`bko{BzN^Xt4y!SFePLS)eU5rU2*Kj%FV!D0mk+*d9OID`* zm{soB-7I_iw$4I$(0Gbd#*|6G@Uf+Wu2n5fAg(^1Vz5mJEOclOi>!{ZRF#UbT1cC` zDPzurgG!H{kSW|3*-#(N?da}c*g zzXbN^6DAngHBTA1R%KzK+2Uq9|AAF3*j^T1Ra+=g`wVpCuoLDbU1@c8}&)U z{e&EkAv_t&HC^kJ5h$n`tH^mw0fb+{#P-sY8%^JqIdwJMR^7UOnl5IX+N(YJ6}8gr z(e$oZ$WI;e(wu+w)&xokTcrlJqnlDG2j$mEzMameYe^7dpR^24%s-0Ujhk|HabU$3 z|2Et|&I1MCcA>{joGA$ME+??dVH z_vka}msZ4=7hun`;K*mbmlbH90y97QkrH^h%gSRTu%n_hJ5yS$@_dNZeku=WD)(A0>j!zRzUE~u2X-kXV%3}r2l86Z_Iw#uaeBvm zDt_Ue0|sEQSeV(IZMzg*i>}m_H6}?}v=Y1~3 zpnpEq?5ea#wEdZf^5>lKRl&Kclt5>&kNvp@kqqN&?8Iv^^jYiS^-=+2z4Ysn3|A9n zUFqelRk+(z-fYT@Z!23<-mlB-AF}gO!0@8yv!d;yzv!~ZGh&OvzqemcOdreR;*jA^ zHTd1lPBrba8Xap=EZxNo8ddXB9JSdEHB3c0)h)>lCA>op>RB$CUrkhBzmF>SoK-v? zVv9ZFG*W{Y*wk%V|_aA zSlXk%TIAc5hlkk2qH{#=iaw;+Z*lek#U23L&G7oukX7YQSecd|1D`o6wS-`}r|}9V zSMZSRl|hkqSB-hHon I|!*#vnd^>nXdn;?wq02r2|_dp}O7|>!i$xdrDM>&g2+# z`pFDSu)XSKr{Mft#5_V68SZ1}eqdaM(^hX$z2;FQfG5$eMvBYLC0>lv0-2cEnk8Jj z%NTmXb_o9C^L3S_Q92Ff;gQFPD`stSb-G3wTI3XV7+uJ%x;p_dO1CuTNjq-&yOCN% zYscP#)e})usj^WYpwUGIo5oo^ohu|%*i=dHPwFtsep4T5&>}&Tob;ktbTL{n>z7S; zOdexWZ0Vv!hi9MYP}X0e!HMSXu;fw3q$?v)loo)+Jt;Do`_Glj^!T@Ib=2Z4L<48p zjr$C?zJJbtr4`4K`k{JPfAff=K*^e6Btk1ya%yPcnm^LDEhE>frO91}WS=xdS$~$F zwd%*qpJCPh?2>AVUoweqT5P@ma`J51W*0%RkDYyZ*&L{Y)9 zay-1L4tF*t$Ep|)2cHvC+}PZak_4Z-Rv^JZvvsem^__@szG;l*wW|IOI-_l+084P|78LaCRr~d(N4ATjweof>UjMd zGWc*UlWc?3?BH2E?r$|Ft>u~*T6`w8pBIOqaUUk1raKjIz2%>6$Ps-1m~I|wcnk^1qltOi*Un6C@^hYTNY%3GwD6tx=J9^N|S zmn9DFn`<65Acvuzhjm1tv^Ci8OO9Q43~#+U4f9J%J;D7K{J z2xjisH;IsWE0^?L*tKl^$HY*YYcUJU<~NE~5Iee#uvVqy3X~CAp2|?d(|n7Vj1s)R}KL+XoajouQ+` z#4E6HB~+wdHm}?3#k$<3&Jcy@=pIVqB)&+TxH?lzI-Alji8IWQGWPEIuhUn$s$7ZR zL86r{*3y5rnTvs+ETq*A!L?M#2_zQaLRtPV8o{bM^)Xe6?A^*>Lv_O=ZH4Bl z@Bd#GtbwF$!=n_aWzAa_DFC-=`-ZP!uUNv%u} zi$9=ORff2y|BoqB1viMGJTKiQ)RH%C+8NKPxmZuBOg zFkoN&MPErUcmKL2B8Im3B80y5-I7BNeCWG-`Fb*RBL4T97jyf4jiU0693a*0$ARM4 zjWP^}E?vbyE?-e;tE5KlhvS$pbX1RuOYZfv)rHsZ>g1RAN@7zB`Hl9=efw8`qH_q{ zPEye)rh~FlS$$57i3!R^DH9#)+_hM8OYfh$Mbh<(7)6Fh#`F@&Ai@Y+UzU>$L83RX zMczgNSYG7==ow52scv1dmfwvzR{VTQ@UO89fby6}1{p}T%KbOmp!QTKP+2wEGHvm+ zy%-m8jiMhZ!WgnczaJTfAnp^m#3P&5zEz_rA)9Au@5)T6uyV3Rer`z+d8tE>%W7Z) z@ha>rP6cyDIs8kbbp>H!P+6oA{C}y4yj$pB2kMI@L?X#R}?9a!W>NcdEOQ=(3 z%jTOd8AWggVM?kCG$|qa^-LDY4JwIdE*Q)C9sMTs={^kJ^~+?Ro~)LJf`8w5r{hYU zwG5325DYKt#iSO={>E2I1)}^|5!j#8Jn^n$-5QaHH;J86&rL;!@$L~mq((Ki1i~d8 zk?3OK%ZK6MO`Qqil_t)kXeHd`&VxnDTfs;H^2c^(g3CF1hbatTNxb_i_P_M6BYXb`PLesyO7M$;|H3x zLkBuB{u+#aO&ClfD~&b~T!xB4a$BAn;9bTV4rz7EU9_yA^CC+q5;fHQSo-fzM`urO zPbRLfpSa~eNb3Ae^t5V(zQj6hSLur{dOaVFGyBAf zU@Tf-9Kw^$WH#AbB?7b6-$}K-&MySiaE*-Zm}hNrR6uJx;Lns~z{YwOzi>J|67PW= z3HCZp*e<&?Wq+s$il2@H0`e+;Sl76P0wHso!aa!4c6dr$ktc*aLO2chl1Oe5BU~{B zo9OvaJ@d3-2dPL^H&qL_hI*)`9J8LTdh9p(sNsaj+YS(&S11rPv88KjmSul}7j>YJ zd{I_jsP$kK%s*}`D4N-M**yUIKM)Mpp3gs41nmD<5v6ni*L>BR-41zd0+MWUm7L12 zT3h6#|5*_s)8#dbS%Un_Db)QNqWtu0E)?RcBbDm|Q}fv^#uJL{EqpL;jYw;csF4Fm zd5W_Futng$m|{z=rvtO9YlacLhN`A5^J1O!Bz5xEOUXGb*)0P~=U~{EhhIAjCbw-UTaw_|ur>SXI6qI+5^8Y% z4#u6t?udDv?tnbIiy|%*jksGhK|zvCWjm!B-TYZuf!M(dl~Z}i{-|;~_Fqk~C2dX_ zu67}~R;|=jVvwK(OOzGQh|dW;1NdZGCoBdvY#zQ?4w$0|o#qL$N@Fqe4zXdt4`Q_< zw_+eJlfIjWoHRO>NV2RYalP{Uh_S!^)XGzGKRX9!;^z21uNH@CgM$Tyi0*%UuvNR7 zr_n~KO896syY{WP@8LNjl*Z#vJnri zI7`fZJ!x6#qOa_snt&CVn=uw+89C9_t2YH1EHKcclEN}E6oIDPVAb|CYi>pao)B6?I^^QI_@)g z876uVA9H)QlDKlUa{rI%@^FyNAqr7lihPk zWLqSZhnCH~CS%I6bUvk8Wi^`p5vO~Gh9w!21j{L9e0^2A#I+^4je;XuO16l6QTtnQ zERjO8VDqQU;urb}$#H+oy5%u%Pm^-?hzaE#Zz-X@q!%}m#ucOQ#doOBYR;dFNfxnz zmMkQ%FO)i}{9(QTtUU0MJ66yiETYH?_Oz6X6Uf>@_oSo^r z%MCxoK1Flsk>?RFNFK=Gsk7N^eNB0sbTGKaG9CLnFQ1)~GKZ4w)>w|c=!_tFR#LCt zDgSghUWD5(j|Pern0rFZO{zHFN|W-j&)AL`W`OlV>pq==o1(sBz>B;2*U;IG>zZ$O zfyg^vwDNYT!^+`7^w99(&cA5WA1s2GuF;3s&`V?>pTyu!f#HzCYoE-#4h&fJGbQe$ zPVKSo(02nA0J#oFGia}TII#~#L^h;qBG>qqDbgNurRx{DTFWfOsC`-Ss5D8iItDi9 z%?5U~!_6QbP7HeP(ZJ|2Fdnt{ZeulTvNz;lZUOCC*^#P0&n^?mldSXVU^%&Hv;?lC z^80fZU8jZBjouX7XgdZdhv@c} zS8DhUdu2m$4C)Qrv!~no3n#be%E8)M_YBhc#~-zl8{QF^I{yz`X~&U_1-~4UMz)J> z$jy`RzR;27gX_`wU?$gAu82dL7m`iP0`+O5ADj=uiae>%1x zPp_{92y#2dfCVo736~#me+?o)8A5{9fd;Mu3rPEX`4fiD0JnbtZTa|L^YXj>T#7jR z-@AhLu6z(e-6oy>zl9OxfR@jfzWc$$UzjaCO^^kJ_xPg9QuykKa^JpE<5_7d*`Gda zyOBffNW8%vj-Jk5ZE_;vJh)H@a#%9XDr%a%37B8FWJYY^cw=j{6O3lgsKWQ)^lMli zWJXmS=^*i14e_L%iRKWof2ixM1_1mt@XlQ3i_LP7si8l+QkeuqXC%yBiyjF3Pw%J29Si_oJ)$}b2fA(*qS5p{f^*qnQR8< zLyG4t7OMFvp=i)MiOxtI?h}e+LaBDA~qbf`lsR zTQM>uHDI#-feClPH6y#g5=p#js6(wQd^EDooy!H$%W zRo{V01IR`R?vXLPk36INxMDcK#~(JpOc}|}M4zamv~9D6A>F@1S8$1BJhc+?P%*2U z-xr9S)J9;OT@BoXn2ivMr@BZOS?!o)78r1T^}DvP9C6K)_VOkwzZ^?uU@-8y%zp^M z9xC}UIS$JRTqA|sKqX|8kV9`4UwaHWd5cMK&d9&@+?DQU)Fb=z0CGaeI~aE`{BvM- zzpZz3U{rg5WRzsPhnR)M#$K3oIK&$b+||kcUpC*| z!dK+}A1u#Nx8UCG$36isGdsET(@=1XCCIB*jG<%GY_#%S{FR> z9i6if9#_R~`1L-tcbPWV+$=IEoncni7;EFxjU^9wKyD9QdP40`=LWQ=g2qC^3vIW{ zqJ6w$kk(`vqZyyqCy>_vi4j35G=|y0G24P#9Zefe9zA8N_ms=T~wo zd(KiM*@JpxE^^NLO8)|{_x2gu;H-g@#>zBBrH#>;bId%=eCJhK%4Jc)2@K69K zrDh#^Yl$HLz=O3YymcJw@URRi%Y@Syj$}9sX#OOn59jPwqEp1zEaUJ!!&V(mQiv^? zg7-ymr}&jSpYIbm_te68FY0E=;|>R66x?Wx6{$g2i;X3!RqX>rSKG%O`)Iz7nhd)y zNn|X&u9RJge#x$-8z58ACl0WWQTLQYqW>dQa`2n#O+wS()9!_+;-yo0?H6gtBsYSD8{A1Ej){z!S3RB_ z-zCX!ALs&XP%K(xi$u4Cg{;S~ysqL>KLk zm%FnNq8(n4ktON>WOeH%|B_l6gi=a?CWJ9YGo@yq?|s72DOYSL2b~}veY;qRK?c$} zGBZnhi7)IxgEIQP;=}H-I-Yn_)Se-*lfkVOf*Oso~OWhz1pvEY?h+)%MSRZM)0@_zB-KLtlljT0`+5UC{N@Qs%-s`Go-Z z_UC?7dXD>yG|7@AXKC^eIu0Wpl4^t{5eqN*g=ViJjU$&(+Vu%|WeTLF5EYL@os_)G z;2pOBq;aTKGoD$KD;=9s3t2-lyNU@HS{-+v0TiYykzsaZwk5w4wWIPTvWqAA)Ov(^ zi0pz-)q;8vP7}TUSrFo3a4H~NyLIn^YHf#VE^IZY^6xx6F=L)~bW!Za?Fl4l48jjGxNKlbsJjkU+V-o4SQs}&YDEs`2(g}Gv} zx@AKRu3?nQ_o4c_Y-RmH#$+KmM$UQ@vtm<6v)R5%f|i<5&9qS_VX)#d5GreP3V+k7 z^WeGL3=dQq6^Q0Up_TDD#K4DF&X2s5|-7L=zEXafAHyY1CS@TP4m#Bz{7=S`m za9nkf)RFQse=LX?OK7VG^D#-SYgO%6F;%Y=-f1RX9A|5W&+IlkK#R`jzcF8AyvK`1@}=qd;F!ll($j8ltRx+Yt=EQ7vf zy7;snm%?k*9_RHwe^bh}ibI1i_`#jtL(MvZi{kSy!K!VC5+UZEfm7QVctFO&1ybJl z<0F#b`R`yrbKysOqW^It53m3+N2Z_GVDQzj9-8?^v-jELb3F$`bfK5k0QI0r-7gWQ z^m4bZCseNYf%<(r+Wu=e@T@s@RD0F1kA)VFiM?E%K_rIv8Oq(zi!kCUgvhlAc_OS2 zK1$Z-Pd^N-FmwmO--w-jDC1h5XG%v2Bc`#SfXe>5o_xotg4=Gx*zW6~JG55Hj|0z; z|1l#VRXxB4%y85%fy(iBFJ<{J`HvcKm_C&F4aEHQH&$nX6Sr@#dAH717?IU>ZEe{c(HNpfxJ*AXnRrSzh zx(GM8hb5JO6wR-4gaA8|m!M8!qO;g4^|&?Su(J4t$ckQ#KIQ!FT}f`obR*NuC%8DK z4JQr^+p5!wL&LgOZizCixMH5@0R|c_X!>02pWY~11XK*RInO=N>6~VZmJgre-PIf= zXY7fR{sm2c9)H4V%rxBiG=iH7h;Z>7I$mn!SBot19x{k1|U)Q2UWiaDEj;-y4 zL#G@KOiKIW$d!q!XWD@&YD)VVjHb0dF0#gf@F*lv`cI9Z9c4yK_Cn`ZL%;8);sk38CoXjMN%2myl%d775GZoy`QvouY<&B9niK^+iEmrZQMo{BfX3*TKl{erR@- zD=>;Pe*);jQ*bJazF4q$cnAPAI5I_DE?J)PTY-N{DFyN%X>55Y*IiJWaOz`MECta# z?JW=9ECu5(`FVsC3)x32CqBns;-J&Kz1s^YMNa&eu+k&)O&vb4KGk^ym<%*`Z#xJl z6t@ZDP+ow>1&{)C*D8Ezl0HKc|9M9&;47DU53UWzvj|q3EVdXUun4*yS^NoKp62}R z(A!!r9Q{dndo?!N)Y~vm3#3I{Fh5wR6>MIGpI$(ou8(~Q!uw93U6__1=XD793K{?9 z6vF>3%pJPB;*Ou;92$~aUJi^Q`db2%KiX3;!0;COZxR>;cwhlR-Vpxq$)~>nxQbzy zn7Y8%<97h~T6K*j26-t*oizTyib-cUIJP%sji0+8x|z70pB5rc=IpTWsnqKu-xBQ7`PEX|V1yl|^QXWN^y%GV@?Y^<_iVTE3jzTT=e%~6G! zqeA*Mlrw(X)5fqPL%IpXbq9q2j;4an)TY4Wzm`di@&nqU)WG4-&C7zu8PSv!$`Vvn|94S+VW>#8#3+UNmO+-i}S zq}a9CqctE-?@NWC4}d+i23=TMC;OCd!RdWubAf(wetPlWvN%D;U2=8~=n^AI88P2O zUhV{cpzdE;+-g43ty1sLsJ&eAHMtoED5$XxkNjHv;Ufl^q=9X*(C5WUx(tVPZPW{Z z0ch4W5GVX-xFd_sB_yx+C9Yn)UU#+ z(k0le(aC=dAkInMXlBGl?RHmm)M3+k{}2ZM?MY!2Nk7qI!lSwH=|LZZYLQYo4NEeg~D<47Ru&{g?rYUC#4!8@s?;H~zh%GbnR>!|_aLTBX zVZp&Pwt2L!gK+n<`ODSFRm;~Xv(R`ofbc@Yqmc3-ap9P5_2WknMPt&9b1I;s3BXlB z&rt>wzdrrBPOj92h*3HsE3Py>f`Q4gWrh)AxlsSFq=0DEN#*;F|AOARB{$~QDBcq^ zH^$6oD^2QedZWB^(dIfy>R)SB?ZXUBSaAZA;@kr<!IPOEpI!48iK4D(PgWV&?@n~d^Frc79}A8j_5!}G!JRwQql%9ON8<-kLYMP zK-K^H5fBh@|M?MDk{32OYt48d=FhBAmkl@|;@9fv_!&gP#6NyS*RvpD4ZQW;su3V~ zEQ@DVE!BfD@O-wn=50nW_{=}*DX>J~Qj*+_m+#qWAKh-DM&T2ll%+e1bpJ+xI%fzL z$J6KU6+S=X?zITU-`AUMT{f@8u4-G=-KV>?^XDlTLM*2jkRcImZ~Dh=9=_+OZ-vB25<4ZxA|eM4@C>k7!3XEA zHLg7y0tZg@iGRZ$-WIUgK>laL$QlPf0shZrvtf{@1E7z4!0)&E@q11TV_Z2w694nB zx7J5`@wVSb`p~^EkcaqHHibtur7<44o;b|XffzL5+cn+Njp9p}#=3l#t*5Im8}N%g zb{o!`p+qGl1V#gCR=mr25<;QRcRa~D{4+4+GI|uh^d2vJS?3qpWbWVm%DluFE#RDJ zHqG;yW@D~!bJq|VLZa>V;xWi}eWCv0C`Q6Um&UVu4D-yJXX|08G7#gs4FMrAV^qwn zbse^U{qEv!bQ!(|JNxe8vOj>tLf>mQUd7$ol@>h34Z#smkg&X`V9Fz0 z@SnDMb2^%-hswnK)^F})D{PpFh0X%O4zNM;CSq5`hA}aOHu&-#YV?}~oIWHw8V)|_ zI1U@<(kIdPTn(#&y|>o5h;pjq#}7D7dP`p>-)-2|Lp;{e|8pFU?EM7{@uv zJc#^3Hyn<ERX>rSI_qQMQcVuTgy@oqEn1f8C_qKFFtHnXhX`EDNNCyRtUrmTuyR+qmWR z8=`s{r{c%bv=czagAZ{jYjpSmIj;Kr|EP!mSAh8U$d4eP_lP*dTI6ZiV3W_&YU;zr)u^+WU=dOV=4I%6;^XkIlbV{cx1TxJ?dXZB}? zDX7V;1Xk7PKAv2P>K{-gmx-0ZN^AR#u@PIceaAlh_&-{b4$_PSvl;U`pXcaXL9?RK zsZ$EgxX5JVKt25_Wf%j)+MGT=>rw1(=))btAXZf18Dt)71*koHNU0rRzulw?p{LQHM zKUyV{p^yT}1@8spk+y@ScB5|_HJ()Douxy&l{E5>Bp8>`o7vkeRG@Y#6LU4i&{Ep@ z9_*2gd#6zku=fyCRJyTbJwfv5#UGD~OTf6=$`_ge$@+qrCw4s`L59HUFw@puQJhq* zypgknRS{?;X>i30{lLGkqpJxT3C>!q$E`z`@(5J&^0fIBjKQf*xq+aU)?0 z*}qn_=PfH)rF+Qq1!4m-s<-0pm@7K?R%Re$vj+_Kz z38{2_^-C;|fpapsDS$2s-ouw}`7Sd|Oj`tVQ?g zVWan(gHAmRahB<~yV|>nfSbL{3tsSHw`MpVWwf=Jxz|~_&5GLycHQ_`L#f~uJtaKE1h3vnwgmOs`&}vjLkqVjk z+VXU}dv7Uo9VPkjL+)kNcj!k9O)V&DMKzR7m(sw}96Y~ee@S_YX5*(~8ufK6A`it1 zEr+;b_ok|Em$7-8JQs0)FttsvRL;EkrgGd@b{U7tglt^lj6jc4RE6TSa*im$i+s%6 z$_aRq&QYB->owfN@FIABRJYRnpKp@Soc!<`_wV9vx(b(}^!}8Fj>L+V%*1*U{lff+ zxGK8}z0?Vp0w$l?H5X_V0;riMzm57^MM=;G3Jqy%Tie+`PjzqM7*cMy$hw1Z$5!^| zTq4-7GLfo-a+te`=5`KJfGDgv>uHd(x%usV<{_sbz#>F+8V6N7`D1}6&sStq?S;S-~*?Ii1g?a-O1j4v1)Rc8tuNIpy_ll${(UwvNS;isq-{zzE1f{9_`xlooDA@U!|p;rKK)N z7*XjERbv-*Sd1ivf$aiEy#-TY0{ce_Vx=}@x#mi%1D)aR%W_$DcPU2VA<1_)*{7qv zb3hOijAhzxkR})%yjkOSbx9^Vx?o^fb)+Vy6*^=KB8;(E@(h?K5mKXZ z_f>tS*{Y*uvYsi$oLi_Dui&#r%>wxCs1XHA7dqnb16>(c^B88~hfSpt9}fRDh9`5> zhR+XkMWk{5T=XKuKOc(mwk{LTMSaaJf1~w6B4sk*bOZU9!1{|)8Vh;dEFaoG?H(GM zfm%RnC-n*ozE<||EeS+^=!XaS0e2r?%>`CvuSi<8C;azbUj*xh%fDxXwpY7+?HIp} zL67)}0J%rw!<(*YyaKTOU^P*|6mi#U=IqaBuM>9pQM%KGAAtEq?LI>IVt><*-3%voZ9@Pc)iKStV^?o$cqXK&*8sIt|H-6!z8 z=qU@Ht39oh;7RaS#YXsZbDmQ$qifwnySnf0x!8=b=Gov?9 zaa;Cvv7h}{iT6l5MA&@1sGLRp*Eie*1zl*n$rpF~oiLtON6O>rhE+O6t!Kc9K)7M2 zV&|j|k>uj;T7_#PD$n&YH1GD|rtK%6I_8p=6%TIL0SGjJ%&a-RFV=in`T~q?(L1r=4s*ir^c>gj&e66h;M>d2*qZOc-q23`^ zr2H~rimND#IjkMc9Z&_O6J95fd%7bUdO*v75`J1@avgC(avvVx_xf;!iq+DiIp@KP2jqp5 zL#DlNu<6+v<4Mls)jYpEWuj5;6gzLy4=CkyA7mBrrY_5V)n1d*UVi*J?3m7((KB0G zeyV*SKkzh&pZO>@P+iwJ*OwXQlDM*k=RcJYIi;O)eAIiCQtp#^h)l2q?avi?mk@c? zF*rw-M(+aERBo9c&N-7rQJKFljF0jgI(B`m)+<|tpHGYel6o%`6GHz!&G;e$D30d4 zy&F%$(b7>)do-oZG>`@vB9y)WQK$smEcToiSt)=L5D=2nVWlA`hkX550*XUWb=k#w zW4ojPW0z115Sty@&b0DS0wCRQnCf)hbYy7ZX(;gREk}R`Y<)EXeG$*!ZzWcWM)H2) z@+)eCrZ4p%qmu~g+&#2Muo z$(CltGIVtz2Y$kwdGOrdpOzL`0A#Y1!RdYcX!94)EASUJKPZJVr>jQxJA>J9ya#sn zZ}KV0BC7t(c(qI^qKJdfJ^>ddBDLbUP)xWSmC8=mcX%?VX>g@Rj8PLrIr2j}AoqDj zLrenNyxLRTB8BPVAfm*ndyxo>nQKwb)Z1?~twhwg1PYYBIzom$7f@4@Dtpp{d; zo*F65hM{LSz+IcyVGnx!xo3xIkJen%_W(}&vN7%6SBjllWpN9|!LJ`dut&9%BLiX5 z67Eeh^Vy|q$k2d!zTpgLxcs}C+?5S4oRPz-E;r(zH8WoZg6T%O%0Pm_*s_p=gP++p z4;~OhpE0qHez?dr;m3jxY`df8(h6BfIi#+(S$D1JLGjJEI;Y1Vg*;USrs6ME%PNZq zSj~I1TbQyXf74NAG9HhBNN%|~lh=3g|Fksawa_HO-Md+EN4;7YxnfAU<+Cy&LMl*FMyy!34u`cCg(A&4O6urkb*L+o&Hb$ws z<{@~G`Udk3zUB_{2#>$upi&kxBd}k1J4B{(Y&YsTGshi#|E1)s)v{ z*2!TDS828iei;>uOsPlyax1PfD&@#O1lOti1g}cn*BjlGS5`lUzirh&K8~>k8)0=N zp4h=p`}N>9&|sIN@$d8b>}Cu2m_K>_Xu*~*W?=hq{FIqRIrP${%|K|d`N4rJ;`T)M zJa+LVz`gAED?2eLX2OjYQE5>f z-cT|oW5Bc6$8bO#t$<0V11p;9Xai!jISPcxLbQbRh-2~+8Es2)&?E(`IZ@o`Nd~S~ z!5`S{dL4-uXG&n1O5$uaaxk}*s8g##6dzw`NAoSZ)2 zzRXf@k)}fxDm)(V9|b)&<_HL%EzU!_Rop{i&Ex^{SHZNIyzFWAjTcj42;#Z!@&2yapHty=sF)+4&EN_TZ4kbvw}^A_-gdo1*q zS{eHM@6RV4A)uDk)N(w?1zo4bRJD5O| zG_I}b6*BUp*VnV`l_Tw^k|GPXS9^rdt&Pbn@XRmuM$Cc9bDw4O8|zPNil#j3^vS5y ziCeYS&`1U;N{>YR&)@W~Xp2!u7>X0l%s;pOeQxx8pYP=0^7uSo?D>6OSMm|f19+FI zanT30q;DtNw_%LeyeXBWLs$SeQhA+` zKuYib8Iu^j!xNxq*BlG{)dDHS*&uQaioS zfPl!GK?fw_%c=td<0q8WteVVgn^359fp#1n`LTa50~)TU*1Wk(lLRLwH1!OWb^Qa2 zK;qMrVO71#3F$14`o|;FlP=;7k^*U}Ct75L?a$sGIDE0!Vj&t<#+_9N@;R%RQn)V# zCNWOHmZqqGSH=G;VaUA7H+$iC=rgZEK3O(}UJ3{);tqV&P=UL0XzHWS{xc@>1%_f? zc`jI~T|E}F+JjS16oE)NTgZK(+;b(tk`2ZIqC6BUefNtvjheF;92)~po(ibfPno#^$c z+}7z6c+5Iv`4>#naZ=+BU9cqObGFytvw%T@e<_cknRS1yR88##{ts2>6k}VouG_M0 z+qSJ$wr$(CwaT__yH?q@ZQHl@KIh)#&SXCHWG3@zq>ujhueI;t$C(mSb?~PhnoRll zDL0;;@8|RF#MTC+_p}wt+X66ouWk=2WGY1q4Os=n5SpMSBd2-@_=gn0o-98uLr!teDAos3#~ zNjl&!axbI-08^h+_cS}{)Dizxi{}v8j9);l=~-^QP2~FnJbyFe>qiaX)`-t8?<7?t|CgPt*)w&<`Fy ze(TT2QR;}^_r#c)p7-5QYAJcHkH;tdNNH*5Ozsafr*D&n)=Wu-#T)&|i?5oOBY&b9 z3)WX)O(TD$t%URM^s%0>4jRmYyp_DS5Bu9tY00~IUr(>zLsg$OVq6ye5&)Z+H5M)_ zD_X_KBlxgh*(kLhZnk zQzAuKd=uZ7-~fH!Z$w}H^KoJHQ*=7>Q(V&8NE)mcFRtGWxJWV_^Gsws9x*Q{2IS;@ zFYFGrmYwHA%Z+qY89 z($oEXd;3@h?+#Rj=DtU-uiMeYe=3$OgZ=d|Vt(fq;u=(=X_@bOgn|-ek$5++qclWw zRHhy_AEvGfkPexwHdA}aaE7rN$e_@EP_yBm_eS19R#FCJcNspMZ!F*FM37|Up-bc$ zSqwT=L#*~8EvAXGrmDgmZm);cB;w$)SqrY)+=NzmAJPxsTGWQS`$}w!AHozi(QmeS z^peoOdTNf=LDG&&^dL~?Yvy!1gT{g^4b4!;*eN$L3zn%2=BP-qi z$bPyg*X(-YyT+4Jh!u(@q^2)Oi&z}bw>uHdX0~kj@bl^78Q%ph!?sJ}xNF0weZZcu zu1)tu>-Js1*1wsZREkvX>bH|FE01#rWxjL0sCL#7WzmNI<$6NiO&`OZ`7{XAPu#Sa zyp}16m4`+S?r8i=w@3hgzI2QP<`%ZS5n{m2Ko1Q!V-o4m(JN1++uy{A{49iiqo<4K zH4n5}Yd4rT!X2LDqKwAOo}N9uja%wwJ8E6Mw)@Jj^~X$O#p+TV;p(v-FZJi-@GkJ>ACM4Ye%YkMJ_(m)3q9p0_?*7cF0`j#|z$o0_ov1@&;_b?60wpn6` z3whK!aKv=h7#4`XK9<|ovjFn0HWSigU%JRkFPV*S9m1LQ5|FH4A4}X?0OT5VM>2xH zv6A|u^8E;QeuUXjR;JiURFx`?A{W#Jtz)!xOSy-JjH2try+kstSd(;$##Dl*QUj!p z88L=uK$p$0ip2{i*Lm>AwW&ZSroA0l61k+p8ZDmjQHWX~zB2WumX>_mmJGb66Ew=G zMSunagSg>a(PKsZKQ0z=1f1LAmT=Yox>%fGtoX?2oAx3`+bIv3F4!Z3{$dHRgZOkxHh~s8wuY%|>f{$)@9dPk{B9d~@+f`N|u~FjY%8 z!+y5;4d4Qla9s zqv*Q7kXjS7k4*&RMve{|W0sqm$qCV8XK7j36_v7K!_i}OVNht937s@@$eJZ(MRA3h zW(S$25LEg6M2aD2d1XwKn6x1TW9N}aPT4WwvkSLM3ufZ*jFnj)Z;iAit&l=gUho)s z+o}09p!2*{*7}P2*>S{67A0p26ww94wlgeAZWXVMSVE1zvuvhA+?-6p0j?d0W7~n; z%`(;oR~T6ta8ceSNz`W`F!Bik?Ao~*q)qd8NdsHd?klc_WjV|GUbX_O5pxd-+Z@*K zMH38H=}(Q>s%lPGT8qu5rK;=^DSWia5~~J;53q~3G?HeuY&zL&2if&TJl84;sE16> zlY{ITuJ9c|Ib!)!BJ7CnfkFPsG3|LG+=R?y%_wx1aP%ryM06{v2s!9NHf-fDzT-K; z1yl0d4b>JpQI6xk=OHU~*GSo2$XmaShe6mIe7^`*CG9R2F7&o&gBvsgggA}3kqj!f zQdW78DSNFYuJ}i6WTTZ&tx44?{H5^t6rT&ed^Td4SCn_ zjXL_m1y91&2jMrEc6U(OV)uK;1#4@m8FR;BfRUG?A_Q6hwXJ~4c$KM%7-aU{#aSKnTM$g!0x(~S6@z07MA&+?1tB3S8e@jO z)v~f54Gmsuz_O6eIaR#_Y3weaDWY&c#Cb0JuvW_^G+bW=xKxYVNYQsT&6NhZj3rc^ z2jLEPK|s%hpSmpER?H4SIp8VjhFzg?8XgR#NrTgZw4Lpi$Njq6Gd`RdHgQGlUfs-G zDy!5Ft1j%a05)V#tjc6Y$(f6ZaAxUTa-8;VF}`eOb`-5WoOV7&$!t0>XLU6PDPDzk%Jk#UmQsk9IOSA;@b zUt#ng5el7;T=3vO_sstlp%l1GzfS1#Ceozk^y!Ks$lE#B7<{-;B~X64DIYtq!v4+v z#y^iK$8jj7q{=1%HKH`skAh9@fw#C+7X^Qv502`bivg!k0%uLkU;OKWj;XRT>#0>) zkuSH~r0L~fwiG}PN&;pNMJ2sX<#eWKI@Fy87d)5&=gg|4n$Cy3QbFu*yda(YonLkN zyNfLPx0tB>!2~C7GvvCkN8N;{?+9n>7yv+2)-wjB0}@}{tPn2<5jm7G-}VDi#R=$# z`Ugk@2% zv4pf*qsJw-m0wUK!$IA5`XQ^PGDlXi+Ho(k*0rP6)p5L9g&CaS$3DUwR!M&4A5F;l zKA)49YZ>v&so7s4I z@X0xpn(Ge%8WkPL+n5US%S?hXR&Y=YIT#j={>?eoC-Z|(_<>YaBLGjM-!#rL_&?gt zm$PoW4r)k=n04%2nE5_teEk?dgmMpQ`ty6 zmzh=gr9=RQhIZaiW=I!3t0;8-Pd@gP2wnxL#w=u_5V7;R7?S^E0FjWji4e#e{wq8e z!Mc~>MgS&?ZT9;sif<9{VG;T%fXfJ5?}+apOn>gVzqE5w%Tb(r{mG}Km;HL@BT1)b z%d(fG=v>0|65!X094WykkHp7{EU_Z=OL2Qw0fhBow;v+{06R*29d|gM1;Frs7X%P~ znkHN#)<37giBY-F{(n`-=MAqO@Ux#7nXTJ7>TZi+r`pkle-;jot_JLwHW?Narsy1Z zvlqaCHpY4eTJ4gC2&&dx!8Foi?D?XwX5R~UDfxmA3`)ro|C@LXjid?n8)M~Ie3rD? zYrh%641-leEmt(i)5fi{3G68}(mL6E*d)$o)F}Kg&N?nF1U{#`c1k@@+|P+f_(1jF z(+U=O&K7q?r2>R6C)WwbM5K!%afpx6gc$kb;5c+J&v-jhx_Z7*4N+5RoOw?A8}oF} zjZz70i5v;uf27n}Z1V8l?O~tsvv{VYVOcz|1)OS89Km2+h5(ik0ClUvEYuk|`j^3? zFWacz9hfk1QjKY@P)%Va_qtMgIgwLEBwI-(19;askHEFdU8j?1=6oJ(W@Bt)Yi+!W zhyB!IQJT-J%R{KT6-;3%$j`rdfC@&f4d`4$Rb7PXVs}lxS4NK+HRe z=>pUl2Ykc3Ot>PXb(0k9gsqIM*wIG!9uTYjSdHc+1ArC(nAym>Er||KKZIz%0W^r1 z0`Ey5y$O4A#b_PBt8V9DoI?(aD9KGiA;CZe`_y;ao>)-%(%^2a(NZ5C##WRbLQru5 zok_-AmG56_4jDMnbzT=fvy!3PTK^j!T(+Wk^p~B~0*bkHN%0dkSGgl`dZzrvV!tHG ztP4rmX-7rtw-?P0|I*iv8BcTCw~dXhZne()s%{|8^APX9td3gb&`#@nv2;y3=k=ai z?`(M&aq-|znsa8&-IM~~1-fOv3xe||EeemIlI%@35?|c*tapd&Xga+-G_U>9Pge&J zqP1-${k{7Zk^j7Ze5?OEl1~0yvwyH+yCE0%hd+`i{^)nGZcs8`zQCvoOKn7!P~{3X z3&n-Ud}`@FF?@K+Yt&Ize9Tijj9GbNq)SIO;h8Kq?m?9DlR6*3;>pM7!XkCy%MhXj z{-NT=tfEO~eXW)8fiUHUA7!L0Ol>6FrC&K;PGphMA18Kf!_3Q?P$p~Y)0?gm2#;Pe zve6x8QT?1^>wYbwD9id!}k3y>Hz)i?*}(!urEOXu;J7w}t5SLvT9( z_&|EZxqIknz>@!t*LXv2@!!yMnhigx*4@VL#$rq2pdEMfR}Ra>^`8|!u0`*yQob~* zTx3px(H&b3XPf_u9L{S0ugF31bfWq(Eor%l&f-2GSu9h+qkuJIfnK&}7Hi5(0V`&J zfwAKQlH%r$>*owJeQy5qqy6>eCFKaI-)dJnp;vu*YRs9TsXF^)p}#dBtAN8Vlehwf&obO z)9sfP=APjm-YkS42=M1!?0x_3^2~p|4Tt*f_%L=ea{t%k@QW+*D@0MSUQnl)l1c@# zJWuV;6b(IATwT(av^IV+M|Z1aoE%% zUzK7B3cUBEtX0e3!wH#IOT{*K!j*M8VY}Lf;#{^jQiz2E6RT8M{ztB`r6oM`#5Y_D zV&Ky}_An7XAmEyyo9XO472Nyb@F9o?HmS$Px`#!j3RvAYFRBoNgf(PgJf8jv?4HB0 z2NUuq8pxb6A@N{ghRC7%$DR*pft?)lj&A<3)5$Coofe?QS9h#|9B z*7?ZQ3&)ZJFe#bikJj&PvRAL+(RS5)PCgOJs_T?ypZNPXawujjVm|s^7Q63e&EKRw zT#(k-g35_V|BoXL8B>ZCTQYX2_W-X4luUSWjLJ18QWuFvZEavd*2-t67MAH=EN4+$ zGUt>{n7iIYS?l(1aDmy~Fv?8oOD@>UR(foC;S7l?GIgC-ql#^XTZ$Z5%fcq(UF!sV zNIbdU;OHNhhk|fPB>s$0H32VUe*RePAL~!T^R38Vr?9U`Zm*{=$$pBK-Zx`xlQH78 z+h8I|HK1!3u`gr>iNp%`b!Sbr+v>xrpU#!Tww+G3-?*qbJQo?66hd*6VC`36gl@5W z1NWaNzqO&_A53mPpWrG)*SWt16(d4q@NH^W5t94x%?eX;u3>mS(BV<;(FKnYtfsGX z+g$fgu2WcjyMZ`{8zP$ciZ@gvhnoUN0LSfAB>CmNeC2NdAjyL#l=bAHitW?%^i5*u z<&$%oNA(-P!cPt{l5gebkQ%tUN4xWpiY>`n-lII-W$5eLwk}9 z0uK_S`N<#JS47T9buoP9a1D-s=^H(QIq(ch4s|-)5V%h%yhbZER-)Ld zXp|nT6ngJ0!q@%_vkTIQ1Q7k5<;7?&+7d>~-K^)q2h<~$b&h{Fl1z{;SFq|bG7ka- z4W>H`eP;gO@Hsmqog6u68XH4r&&43!K4D1gD zTV;CB)nSAou-r2Y4`xBkN=&U$Jn=?ui<82DBUa z4^mfH#H-H$m({xX9V2(G8#w@Z*T>nzrSr}S#FNiR<$_zTQHXk<4;)$h!PHsrZ$ne2 z%MI|g)1dx9WAXm~kG*$^|6=bWUT449yFWeBt(o0mDn^Z;fR6$)cV5q~lYq~e7u=9p z*nZ=&69pePN-l2X%Ziy%?rOzd)S7o~D`UEXT8f2>JB=p1oy#3W_b2y6r z^Yx9|E$vFnO>#7{DN(SshBI_9c4}-*-C)}LV#~p92_%Ea7;Xw6&qSX1wb5Hz`3{O; zuB?>Q2*3Y3I;L!xojE`E^ya&8>B(+z^0_1!+j~0#J!UEwMj6-VVAP-(^H4DeFj4L6 zSAdc~O;G{~4`wh+QWbmCOxc%CyiQw2iQD`Yox~Rpmj*a1*X^>Cb^{GI3YZmwX^vS3 zTR>1$GInP-Xs~yVTC4D&1=tQsW|)5v$t4S;8lx$edwh z5Z_XjA8W`~6Pd8diNv7B=R@UkD-nC{yBDbp>*j52?K25400qsJFPUQqaXn zqM~Lfh+Ktomb68Y?Q?*!s7}gK6ha?=Y9t>c6IZ&aR|md|&?Xjhs|toz0iCy{_#l>8 zUIQnoQ!WfTomSnoN#5v5Qr~Ev{V!^va*H73Ws5NczqMos^asTt6Y-V~ z33P_@<|t)+$(H9a^y2)BIe<#D@R@}LiyWPTYXA)l^qTS>7*lCg!F1Bg?u%l5pmA2J zW*U?9$X#XenIAKj-q>cFaYYoDo~QIF)^T~H+bB{g-AyR1qR`@(l-A#7NUoEyKe@9B zCl{n;`T&Kv2lvH3pkjsI4>!y7Bgxs7NqC6Wkh@nTmkW4t|%L>lKa3Tu|;y zyJK$8iDoijEB+m=v^@KYcNVk>Sl|lCCHGvJCs^h(w<0?wz{1cZ=f~Qw-{+mwF7oQo_&(w%tNY0o8Ew*7Mrc;$-nTLU? zZ_8-nPxQXU!v0N37d$&as`Sc<4PG2AfClr?Wi(5N=`yp_E4g^Jc-=l80XjnqNe-?v z6GTlVG$U-*s2nwCv(3Qyt=;P0VbKHX-@1Qu*$I-TH#-L=vEw8UF1HlG0V9O1yLqj< z03!f%wgK_B{b%Vyb%}uO1S&?Rq>i?WLB(EnH@3noQV~Q8oh}uvY(uGSPWz1%ZHA1L zRYVq*(zqZa1$mVv=4#>yMy*%m>taFR&{F@+NpAO9)WQ9z0)=7IMV`h!#sIT&@MQ*N zjR?-{&gQeti#k!b1!ct}W=7TX`+CX!-es>~UWaT?Mv2jZYV4m(wsUy5fKtvF;`ax@ zMAc+LHf*Zeic)9=(kx6=K%7X#k*<43{uzy1C1y9QtqKnVyu&9$CgQ@tPyP~^^m0#& zjmRws*AC-7rf?KDV!|HH_wZyBR)*lC3L+$P+auI~9u!>TR|?R?K~O;xkhUj52Wb=W z3tS9P^q)rYE0|i>0lDQ&_!w(p;%xS7$y}MiOIhQOlDuGVVUe+~n^~;#ZMefJKEji* zi@Sd=WO|P1Oh~G{Iod@QL8|qrmcp?y5y<$y<(=8wH}{J1oZ;4857qVVYQm+r8Ijt! zn|JMMy5bp;(#mW8T?(*+fm1|^v*ZXZiWz{tOAx|uk%HkOV2w~pv}&Cy=QEPzE1xc7 z%&Mj?{z2IM1M?1GJL9f8BhEKN!TW;A$wQn(8PobO8=Vb)e9l87bt3r22|qH4>DIUw z74xvmlNDyV%fs+;%!ea17dq}Moe6%j%EQrlEa<_CAEEGTy3GhPnd71GJKjrY#t&eA zxV26XI~wER5I#0&XM~@M^ib!U1ZFn)_TgZ_5Ba*!>q-ST?c!lrIV!Bv#!K3{pPftr zKWXBj$UMsTpn;##_mICH|D#{cgP?b0R>yR9nG|+X%tJ$bWYA6lKQ82<|2-zmEXN0S zawxb)20tNrj}R)sh1l;~mEiRtA!g&%0iQLBhq<>@5-&1nFWLc3Wl%I4`)B9(q_SLQ znr#g22JgOp;umc^k@w-TZ$3-}IZRYGWSGWO=4_}SV(2VeJn|yumJs%46+qwwpNlV} zfcO?)HYtuMNf8Q583|4q2sp-ojcA(p_k)5-QXm@@DhM{FGqeXig3EhVKU&nm5kdyI zn{b!Cxjr3iz>V;*1W&m{FkK{$kZnikOGX4$4ZlquAj-mA`k`w^;A+4i1C^$0K1}V6 z&mY>s9g;+a#Tp!Tl|PtQID8LY^tu_m86O&O$!I2*JSd*an}2MfasgJ(D|6rOt9AXs zYS%UTsn$qSK+QsNNod5SeGK%Vm4|w9%!0_r*Vl#B#Lm585pSx>P$NFFnTsl_W5;u% z#PA#d@0RZfNk_d!e0z~yH|wWIcjxA#Yi4v ze18O!ljTWY)W&0^$QFojmx&!+O|OrW$#F?f>NeGaB+ll-=`P$W(yo5HLU+5t$xV-{ zq2&?>KIM-X{48dtm&ZcVCbgOl+Y>{-xHm#UnEujo?gfNGuR&YVi+n|GBZ z3Ltk%VfC5cZFXb$It)!O<$fV$EWwA|W88Pb$t5ebDk(D~Hp877NsH&a?Ty< zf+_L0ca`5=gg%t=4osZt+u8e+upg z11<7BP!?uoqIM_PWB9vHAqtR5;&SEby_ztwO;r+iymQ0duUpY!ctp2xb1)4=e?+CG z1As$7`dRp>8R7_dLSSa`Ns%GFl#n-Ema!8kQPKRf!Oa~PDr;sHq1I0Y97aU~mI!Q!X z#cy7rHF|{=+)J3_P=(C-FF$jp9g>miG77)IX4KGO zG3HJ-34L(H1`(` zRR1_rq(Pj?yvfS zXw6Av{^Bg^pF9|N<=H2UA#KDE?L}jLO++;kVaRs>3Hr%`X+DfUo1t=4s*%n9(8&$d zQNtz0qBG14&Y`TULJWDz0{c!I!e z8c@^5L1=w7Jsv)$t(#w8UrpL1$nmDlGzJ^VQG=Hq9e9$c>1U=6E`l=o-MTvsm3_3|!gELL;@? z-uonJJFyUvjgpUdYqxob=suP2!;2J62kMs~Sno+4XIp;}Z5|ILH*Fntdjy>arP1W} ztT0iF{Z3vc8%gvz7Hu5RHuY19c@|r%ECx0;6XL_FMf~RlPz&nwi6yc(Ms8_*y&3AMa zV%wVy6&LzwSa80^#R4cSKr8pK+nQX>dNh>Y4N2_dBBaZCF8!&Dj61B&9SuZ!L%|^*gHm%2_OOG;z zl`$0a!XKdAza+*qXNW$6t8b%&6?`z2Q9d zESXHQB?np1`ZlXNTLK#T#ow5DxEf3;w|K7Pa^OID8ggB+ZGbWn;rxeq>RM zWy!?*T(<44)cI)G0UM~Qj9s$=R_|ZQVA$j`szgcNOo}L59V=PC<=MgVEUmcEcA5;v zHD=a#iiNdk9y?(YH9r4~i?p{=EdLD9pmb+kj$Jx|$s3j<%e}y<_F7$Y0jRiv=0f?f znSgW`xRYT`f622N+Q+VXI@?8;bd3Oc;QIH<0%By;mlURpnXO4&2JXQ$`+hn{7>cng zSuMJDi6zxiWJ^mvg=VeVV6%cIK{mDIsor@L#0pI}TO%f`lP|ZXgY+_P)idX3N z>1QLKHvFxl42drrX0G+b=yhCOOF!EAqCwk@y!-iQ^UQmUnH8K;j5pN3=S+m_k|(-K zrcIRqNTn!IWD%iEsM-#Ik!6E&2a0EZQM_??=Hp;`k_AnKanEY7Tdd4+0So^>GabZe zTjdsdTalJ9UAuLnYE`|f`C-C?YkQBZ9$a5%xu1Vt2P79iuZI?^w=?IpuWY`UD8ZX z%F}wE+z@jHdd#@WO92krr5>1%UYO;W4g}dnElAsJLH1=yWz^mn4S;v_Q+Rb-)h+-~TOO&OtWKO5;zk)h@``2c#_kETnxMqE$4X0gdBrTp+emJ(_o1QaCVE^2S>>8ri#GhA|I=)8mPf z7apxL;2?S_Oj{)5hbhnN>8~)KXkYR{rr~lIx3Ez7$anOL$OG&>c!|Zv*Q(FkVo9qs zkbANJctzDk{}_q$#&UQ~sS){2Q5!s>Q?c_)zO%V?oCvY|1io8Jnm+5y9qSbIzV_A@cHI5E zJONaM1%Ju+Rgj)4*sVVHaR$B%k&A{tbU5&VC9|KJhJS+$FHms}-T(ge=M04gX6s$* z5Cm9{q#k{N(KEFL$+8{_M3^GHBF%km@GD$xHo6|(_6$-0ax~5|!vE)CTsBVhRMFWB0ZedtJHN$lMUazU!}+#C4<$4>H=}_}>T}4lM_Y$&2?xw*hJHfQYd~eN)wFqqPS%xq1na{38_`WP`k*vJf@3GBgQK_r%%gm+V{J8P!YP9Bb+!M@%vEooRF zpNEQ^H2L?i^{gMNg97(lSaYjO`?%Z8+XVIT1_h-+4J`eDwu&6R)YXM5jAM@8+#X;i zN;~dC$$;n;a6z!chCmT>V7R-zF8=Bm=c>z;`$oJCChaKpdR*>V@&OVeL zyIL|zXrZrfcM7Tc#ca-!tVRS%3Bz%eM%=D@vO)L?aP2)XYoW{7t}7)YHGZ|5qb#@f zrKl<%kp&3DqIcr!=Xs;=9eqXQ6 zt07OsG7&@cKFknRnGFB-{Q1*hOu0k=6J4Ppgdt#S#&>P1d42;AdM%IRdEMC%us2^+ zLtxU=jEX53A?4whL7S#PyuIDZ5mcweUaX0XAPL~a&LgpDECUopq}@@iw?n8{(Oh!P z7Y$GU)zZfq;2`wr2y_osD9D!Y-iK{9BbSybt6w7#QdDC8zz{3 zqc7D0SB~t5$aBTj`0tMZ@=%tmX$ZWE8a}RzDQL+0!(@&|n?f}bU?na*o|}J&j6~+9 zkpalaDZ~X&ZeEIW@0uH#$pO;5128012oR8#qk$w3Q*4Qc!e2}ACGusgl3^f1`HSx?q-e@Xgsnh`v4%_0$M^>2d#%}Mv$7{O} z4!DyT|Lrj^N+ry)5?nB-a07WYfzpXu6?@k~?V#fr+(Qxl`0AGSLSzZumG?)Dzr?~> z&ja9IQA%Y0YjD@lnyBpWKxP}0G z)Q!G9RI6+liJ#$X4XSCphn)6I3z{~LPuz?97-F*9@s(JsMEy?tdXL8bC={V>t|3P` z$VR0YZk(o>=iTI#T*D3Dx;)!q)%4VNU<-eWOab{;Dj-$u9W7A;G8R4>2?E zs%bzcB14?(`dc-4zh0>!9?2YkF$axqUQpWPDfVWX$_3@kh|i{RTk@{o*f;HO>^oAa zXPRvG5vY-<>ZJ+mtCe&8Vb(qU*wp*rnQ5)$s2=mhd zBIL$zqx=(rh_19Qn{Wx!XP1x;-Mx zouB38FPPAE@^*$9X*n3aj9p0YL%Iir75F|EmFGNhXDafz{d$J9>XD4;zVZOBy8I*b zpup*lQmq5egDX`^L(H`dm>RJgdC|Eyxk?~oq|{Ttz^@~nR*Vl-6vnsY-n$r$4yV-d zx${OyhhYxpvh|6Aq`ZOok;Yf9QvOk6_FiZ`K{>!8q-05%Ozqb8|xpcf#|HEqa;`r(dXU|%q`C4VK+BkrHri6#fwN5%pzTfMf*rfr@pUq}%A$SWMxe>Flh0)sZRA=&5t# z*%O!9DzwyTn|rC9*wiH9RaKa#W;T&a+h<>kYEiREn9k0eS)mJMLKPD4)7mM@b3mRJLGfYAcI| z+`1!hdxV-btz>hZE|1?+uOMvMkMG_6QkugvOcDP8<{T?~Hq%jHxrq|d0ott*T4pju zV=!s#q;ksT*kMG^F0hot6k~0HQW=G!6xtH4$2`WyL{QkDrpQWCSK$X_yuNQd0#H(>qDJ7D5!@E$y6*j42NKAC0T7jG}5y{t^BbT?P{RfXhNZLd0q! z%QYM!`%9S4w`k7Q(;2B=MXiZvt2f5O2AU=d@3ymXP-@CRCo;x8)ictwu`Xh`3#y## zf~ZNGy~{RBq{R6zNz^U$O#u;__nZ#mQd|Pq)xsLz3#|~6^W5kGQb!yifF^>1oC)fO zaY(hK0`aIQX386YP$Tg*17|v}?$mv@ z21HVuG%~rtRo=x5T0*dtaZUj?mx>=E)YF!Hyx1*7c(VbEuJe5Ah2LCqbe$+UngoL> zoOTGLytSRn=D>kc*}0rW%pI&ErLz##^_9dOnG&o2YuXu_h*{ekJHhUp`eNsuA) z5(bYm3OLLdSn#Mr2+)SlG@${2m$+NNDF5UoK?*L;o@snBv%=)(CX7(Jc-)_m#9|lE z^c^O)Lud^G8%_&ST=yhv55J%^@l1-v(CcB;Y`|2Nok|wjKe)KFwk5@Fwz#WV)1rFp zB&{G+4Rf7Ur8TZlF|)@dDSKBdAAvOIsF`{$4dal55_;Ct3$V`F8C{yQbJnR7GFsN- zGuEjS;~LhpQ`UPKAkQkkD9gJI!If0N=M}s_;gf70OADT5x5p6TIwoA!268wV>l3e2!rVC{ zlry_gGG+3-0p`UBtPd^(?lq?5*iE6c&C8}KVzz* zM9vjyP#DhQI*gKiH!;DO4ACrt*4Tbv!Uc(0bR+2|vw==6u-*#DXxSNDB!dsP?{8my(HolPHFw$Q|30&EolZvT}ce$Yg{l12~mVH|# zm{AKf%(pX<>d`a=t4Q^L)y@CD`3RxONd#90@5KK6!O zuC-uA^@p zxaO3ckF3{cZ$${XT^D(d%jOjXQK)HI%f*G=7CQ&MVSf_D=3SmIzaq-Ezh2|Ku|9D%TA}*7r0ocoF*=VS=)BPj8u98=!iPn;|^hvD!i^37F|mKZ-2&C3{+INt5(e`;9vvNN~f4 zI~Fi!z4I_m!_S#>g?M@mV^zGzhO}G^ie;h(zx8qQ{6L6si?=;kh2QTla{+i11~OxvTw6sY6ox^;$r@AK%*( z|L5h&&#RnV&X*xu`gbq?6ra!AXX(%B$iZ)|9!JcUuqnGMeEbQA>YmG4hY$Hf)JgBR zbYuHUosvZSmTZCkM*92)uL3Yz`~;I9$|1?^csHew zoFd;1i>sF+4Ojk_XGZ;FEmyb(<#zp!0N7X~_jte8c{gk3ucg!gGr@<&{8p4zh-loz z`-~M*Iljnw4d!y`D$$4g33NTpsN*P#C^CCShn5d$hV28J?^HAEX{nbRweuZ*Pxe#@ z9bl`3uer{xuC$opZ<~`P>qp?H;i{bN37Jmdzs_1~!e_oGmvl;oKqpf}vjzXpattHuy%O zUhej%V$p97hP-x5|GV<(a>u9NevZHrvC+_jG#1?9&a(iy3}zY&Q({iCNVnHi=tvuo z*!ie87`FF&L?|8W*u1%45JRvxr*-529Om^`EOpYXW>fxYdy_Tn`yqQiU7VM;3;@Fo zo!oIKEU!nhQ_huf(ykAhT2b(cxEB_YV~hd>xA~;d)$v1{mH&z0!xZ9-zbe(?(4g0$ zLZE+>6c~>gmM+p`HK~}immaN2jSc6Fr-r9-SpQ97Q~x)GUEZo@u!oa86^5e0=vI`y z)znGttaX+uy2WLVQA?%)ojd5?qqOqGmpY?Z<&+a{_n#)NdW==sc`K#Z>U0b;8FT)! z0f9H83Z5jlAZO?%AYpV;R7vybk~vDVbKqMZgMXNKc#L5BFl&wvYYU-43VkR?t>d~| zUL9g;+zp#aLmGw8t7o|lE)@TbU}xoPL?Kz2wP_6R>B>v7VQ147@qp@SCfUQT1oFC6 z|M7U=mza2Bk{f}^?~~iWHzS6HLo*;}&|@@IdmEm9>L;48oaI zvgWh@=Z*uLwM07O*bJMrShYU0Hn09uGKbp9dGv5-8vWz99znbE0|0^b$$$zSq+=HF z^{fJ1BHt-W{q zcRLs7;@rJs-psrC&i0Jn`vXHzbch!iF}JGAa+(fXA@zvTe=uqj|6$;yoghoQOYyj5 z!4SiJlbubT>&?Ow;?^vub{(BPb7Y;Bow9bYnd}{gQmG{LjCVt;6++-wB)aqv z6&z)LhUdlmNTAZ4iGZVWmFTrQdm&1`VjzPF zpw?}E!CF(zJjj$%d%pT}E_vBY@#`)o!KypziDljdp+zo9b zj~>gPf(y2lqvlPt6I(QK3q(T!YJ=N#6eL8fDk4W2UIMzf5%F>7pFJtdCN}_)o4}*y z%@*Gnb{!)QE2p1aK`THAuL6jjTqrZ_XFCejx}uSaIt@yj0j12Z{rK=ecccLUSjt<_ z4SzYowGD%ZWcS!3lo@lHkRNkinqqO(%w~nTLwxKyIr|)+C2{twoSF+4E;<(fZSkvu#clf_TZGTHhwn6$-l5axiG}@n}i!&>ucO^GeQU z(;hB{@Z3dJdt~%WM_Nic{JJuVW^v*=*VM8pzrURO#Ko*V$1&g;789^S3jzfrqmN6*F!pN<+d2ArdgR6Wq$0MB19 zfR9S(ek~-EoQ@+s1YYCH=>X2pxPaFKyLLx3hPBf_?+OrJ?!PC%JLJvfrb%XjQ2rm_ zn(L=U_bXU_TtU`S+|2{Wmh@^Xk5kP4_4_Tbed+=cv>|;)3QYpw&a0;9C-wF=B^af{ zI2EvvKnkNcb-o9!gl&o z=2;&5%>zKiDVx%(73o?AENOLjTwBzEAh(pTv~qb?(rGD>Re~ru%q)V!8bJ3uOoGC3 z>$uYM4N*jaJH(+4g0C#p5>SGs^969JF_g%yR4=r(iBXUXNad^$jlp7JMGml&NG^$r zcQnPyu|Lx(@`l1jWCN!&DdkJmU3TnyIOp2TO6}_nZ7Iey-&C#To)uL9l+i@ z!S8q`9W?~bG{wgXmd*)fJFSpaaOXv>&XOx|FFE-=&VZI#^7&d1&C;3Oe3#qAj4PwA z&4E%5a~QKqnCh~HHLF0zp6g2%y=N#HA|Fw>q+0>8fYi(vaiIo*Sp=K0Ds{~tRaAi} ztXjwgHkMrKEImIZIR$AEu*+Un{<(>Z#)X2SjT&b(H4*#D(mn4eDFXi|Z?iKvUGafI zquLj@vBazwM05BvpW8$ohmff+ZVQS9Bai;xumo=8#m{R`JT~N9nm!62B-g=!1S6KT zmg4JK*EJ+M#ve1*Ta-47a4*ar2BlVCET^N_a~{wMga`w z!9dJhS@|+3e$4vkr_U!;WQ9Y?n zQrPBu!O<6En$GYViy>7Q$JXn_A+{kU)bn5*8J$%it$hES`?y96B5~&3e!h&!=-oqH zec562H*60)tQquSP}(STx-#pH7s=30f%IhN4!GL8t1ffZ$0gSzobXQKm&-xD)AX8l zmT!Sj-plltjbR$-V%4H-kI+!yz6Y5V5tFO@eeBc@4`(b5IW4?SR+fva;&@;kX4m_i zZ=?0({^ooe>*)BlrOyrMVv+tOPztV@llE{8hb@Wv2nvSux9~mO4 zDy*~h6tC~F(I?wW>O9IruOZJ!w`1(^*ZE>jhj8mKed-* zY;!D&bCSoN(9S_>BBf2fJev+kE*TpPX37bL7Q z1*&F6-AtT7%b#>&s1$HVI)ZG;Ge}|}dLD~YtD-#6@AHfh4~2vm5k2Nbc*xBqIXoaE zCsfssZ;#4ez>o^JKf}eHPV^m=Ah8!QzWq9;0&F+uJiC;S7Cz)V3?Vripq8nb$IE}p zN5y9x#ye|t@dNl7cl5(RBQ;} zO%CgAKuc_F@yYHQia2z+V~H%P=_jF-_j8u9m2E?(NaqoNix#Bg(O!#6ZrUrna4`k& z(oR`>g|stkdxe%cIQ}50!lc+TL}vN)ARh%W!I#MX@Q{&v2?gu44rMc#akAeX%wbK+ zB~D2oFG;{XftroE%8g00jT5roB5<>i#uA3sT}_6jK&dd5b3f?Je*#wC-{`(xL}STt$CO_Sc!-^t@A#opFvCO{bGa?yY7=cZB+_}ky+ zSi}b#lDR_mlV*nK@+yyZzL4fOac@9K8$?H3o6U<7>$MPXfF;t)8w}@PwdT5*E7nWb z1-3(QUEJ}C)EUlvaM?2H$Aao3E$A^wVkvUeQW7(86wmFs=W~wszc=#5v<6Q{7nOv( ziz*rwK$4;M2cZu7qxSk3q(nk8&J25^Vv*%cK?n8*4QUB(g$v>FSkMpUv9Sjf`EDX! zr?0I@`tP(O+9@D>pU;sb9zjgE&iy3M3oA#^r1Tj?)lTS#u_LeuU^4-UTf$^f1D8bW zyCn};5B57>TU|xa@JXvUnvUEM`fb>z6z5Ut(RZ3dcZ7I=R;1H*;Q)2R#sN$Y4r1F(@~KW zDHDy*bKLI2X-qSma>}VRYhpRhbjJBv%YQltSRcZar55#PAhWiGQrR2rw*yt(go!h7 zU~iH-TU$OKClgcCU$x!JCC@ajN%QGAY%YQsw?!TfgEG4T_m!nedmgfHINW^3=a&ak z3U<|+j&uwCpd>f^A^1NOHNIE*vwyJsk>3XbHVNR47E*<=A>_35AF@P2aC1vPVp zSs29^?o>;oRRuEP{f{~ljdpr*RHIJb3eZK|siYKwLTi(a|6MF3NKi2jxYNn}po}5S4pQBX z^-5Nw9gB$m?P@qE;#uOA8EQ86Ln1Z=6{wX7li!<@Xh##G90=P~f18oeV{lxfahA!w zRnsKzW?4e}mK2f!7@n>?5k^h&vYt!IC3Ouo#Z*xK4#St3^Icz+@)U+6Sc9(H3+58* ztzU!|T=ca@XZfr-s9;N-%CTg?O7QMMd=QJkebNp^-_8-QvgH(*?~#5r+H85(3xNiN zTVYwE*B~qCp76E$99mL5H}|kEvBa!PU8WKDomi(DEZBu@{|5vHUzhf6Ye{{;QJeKz z**i7qX_YJSrxWzo2P+=&(%uI3Vm3shRnqhK*%A!xeA0Ac_M_3}Wdj zNlG<$i+C@*D%9`J2#+6E0h#p2-~SB4lrs0^h&rFM&$OAvljfD7r*m!n)LzCthuc_S zrW2NfXee{uST2f@SJns7eHy%@!&Z@FBrYsrH6)tT-s1*$G+f}Q^eFylLp4phR!96< zbFFBDsTOD32_)G#v3__W@@+BDek#jYU;hXTN%siKaKOd^X!$^dUTr_ON1=%?2hX{s zFBh}se&P*S;D|W+X$9XBks0P)F?}JuzHQDFjG68%X1u_1$iQ(v?nW{NV~EHR{SiaW z{BC0ZsCW3chJ8&;;ryCnc2`Q|0`;4excY9YDeOk6Pq6d>SrYT1-buFDfo__Z`6@PK zjQn}URN^+6z!q!>6YaM>zUpMIU*0)^M{^(GZ?gOSS@zciWuCZ~h^6@D#pvTEkPpMH zB^58?rug+5z%uQX3a@zejBL~lV&Mz&6~k2h#L2qM;^m@w1hwyBnr>+QUb*4^d|3D; zkEaZ$Rm5ghC9bXwZsnpsk?blHbp(YoJ?W!bA{`VfP|d$#d^c1eER?(`oMevIkn!XDu9R`kOl}ctHXBrIGs((M1+u3H z3zx}&@YeSPh6B)x7!*$5Af7@iwTfE&{f&^}>Be1BD6i zsPQL|h|DZOc~`OzX57f;|Ib8Y!TJ9JFwy?I8UQ9bg`}@mTL2j8A0}GvUrh9M$3IN8 z;NV{-`kMK_ndnn&l|ylu>$(x-<>%X_zf833R=}8)I@Q?`1So)sE-e4cMAxR5#Z`A^4MFT-A1PI5MuN_Ts?-rge>U%pU^_Y--9CH?QMF7_|CZR+GJ2t;J^ z^V_#vYmbjZz}(duyzBn;qutqB8zI!q+tu^tb0gT-T1zLh8{h5kPJuIP0(^nu1Pgf3 zlJqN*WpKEFyvg!T?zu7?3-)!*gzup5;nRb_Pv8Y%CHBDEJs)~_CqnUOy(xYjB9V0 z(n!XQIg(52NOQc45$Pa_T~_gi9#iLu4f`?WRfk|Z*L@b`SC<^+zJ_&Z zHT;^PlRiQ+XOD)@|B8aoFX6h^dGH|l^R!Lvx3^Bw{41|&$ikzscBkgODAukZ^lH1= z=}qfmE%}25(@4v4LL0{%x@$>jVhKy5uw=hTQfN?`WijaJWr?0aN$V($1nGP`@LRgQ z#dDz)Pjcy#y!UwwWcQE2I*1t2FbatcHZ9j<_Iro5)T!1!Yey}Fn82nIiPWPc-VluU zB?I^0N4+edW;FuAhmKPA!J?SW?iMnFY$A`;zhhErbA`?9QdrLtoF8%JLv|`V^gJqy zS`|r7H~s1iMy=r3l@0l=+n6d5-1o>;Q4UWh%nF7dO05@Ze+Sc53AQbKk{CQLqAhbS zZkuHk(N6V@y*CM##TCJ#UQbpXxW$(f_25c$`oS7zsihQOG_G^EXsDL7iNb{(pd&~2 z*(=_mNPUt~MPU;PvR=XJy#x~`LZ6vn$d|GDac)j{Ka4A*VJRl&B?DKy-3!-iyI zJyzMcBXNNo3W#6k`_Pq3(w%Eku#ayd%Ov$^85}M+?X1jq@++9+`O}~DAcpp4V4(sD zJWtz$ePnpNrT5!9p@CN@m#p7uGm~?d`%j^nqNy{Pt(+f+D@2Jv7(>zz+>TB88>h-A z^U&=f;OFoskyf3fb zrl`}TjU%lox-5dRHNihd2-ZRXX-^Omr*;T4V^X9H?{rUo$*xNc@O)=UTXqv}OSPomSlexPl@)0vK%$&t z%MXFAd{W=d-5^VNq~xh(H&T>1=*Y6~Vr}BLL4fls<`pJE4=XS*6W7i}RjmkA?gZKL z#pi*tRtRPB8yTnLrIsIy@b@u~@-w1HXpq|{hqC;&(-BPI16en;nw%lMz&d}cL%3d)LTo&@fLPKWXT=&G1H((?N>1=UE zMNG&s#D8i#uxj3g{9>*RmESGfx+Axv<;yo5o4BqE8i4z@LPhnwGq8yMZtC&a&v=#1 z%k?4EQs+}5-~PUNQCz!{o`usMG;~6D*C~&e80PrvP}t@{*RWX#x+$g~9ZSttG^K z!LN5E9g2H@^;hrt26%K)>x^#J0av+%wiP$U+DvIL44gvK%Kpe2JFUTYCz-ZleJy$d zr-vO7xkDlJa4rc+&tw{NZNg!2>cPtAfx0!~7}&G|3RAgG7XZr=Jjs@kzKqLx+E)VL?5A*Si9SGT`i@ zxngyR?ZtB_BZ3fT^6yCwx!n2;H9N{`Bnf^Z6M^bU4UBSSd&R5M>bAhawRBQQhpVIC zTwP|bp4s^Vf3OFVG&fKL@Jjf`3-uK|$X|1}USG$%~Y zjUY0@%;^X*bnXDS-96#`ydm)Vl#!9n;&~f<&hF+os}8{9;#nd$5SgR>(Q{~N?O%2A zSyJn>U`dHP=JU{mleB1|&i#9`%TV+Nely9g9v&--#nYblj%HQ*uHu<;%ponX3?%*a z$@vs-$Q+W+p{6UwN_IY;F8Eq+)Wgn-_Vc}Xct4nAm71WQI7QKjg8$6>cIJzRnomiv z;*sBfO8UM%`;xr1D;TCZ^iZ+4582wU3w9FWhJ0O->L}iO+1`QY#hFv;wYxuAb&d;; zta9E*{xhmPi>++G*C)sDPl#^DJP!JM4mV5hjgMrt)J_?bw85$k1}}4BCeM+QticbI ztg9vn#`8SFastP|C$n6L>Wf^Z6&YID5OplwG`*fI-HOA5O3Xa!_L50BE^0HmDhc1v z6&Ov3mnL66Q(O#?rzMfTWdDY{xR(in8y4L-#y(b=MaB&ZO4OfFY>A(A{EDmWWK%co+M~3$k6lVpoVHz(i^}R@-w%~0Tyep2*Znk zfVd0F)SPDX=a+UMQ;GiWp&G^=1&sXRdm(I&aDt@o!bTs|&A{cXAf5me#AR}w0C;%( z6qmx&@pdfSb8o}6VW&Zj?jpTcPaTdqdwbfF@9MI+=BN+4Ih^l2v3zp$!Re?Ua;};c z>MU!Jnr5ZPNb}KGGMPjv_-|dsN#s5tJI*^>)_HkvZ%Uis-5L9>L^#-Faz`+#yUNND zNO8@vw|R@+2<)53u0gDTjW(*qG#^>ul=dC5U2=0|NQ9j^%a2$1Soun3$EV*OE+@^4 zVBpr&N&I6LnKxWpO_WT*mQ07GQJ}k};;a;nBCt!YPN5f(Jl%b0=y#Qy{^Ir#8)7U* zb@w%+vD*a%Q- zCc2OwI&{TMXfJQ1I{K~pZCee?6(3xomuK^c3wI6t3vIf8`eKhQh4Iyw z^(t4@uqMlnkOni3+Y~I7qg$_Y2hW=$e;Un=G-FD4o}yui{9Fc%D;L3Fd~U!t5^!r0Be(w-2Ww2ktyk52zF z)<_8mc70wf1`iG6nUI#CGsP(kviYwOm5v*!(}*C#GLzwKChW#`F0G=$@AncPO*boB zMiUi!TOjEeo?zs?aX^TTe5V^Hc?dQ9)A5p0r$oE7nDCKAK_DEfZeuJ%GsI4-9%h-Z zpXb}-WP9dG1Od#N<)o4 z*9*R7^l-mdO*oT2vS_AY@bHM*pvbbdP*)+XU`ug3|Gho)`lp&vqv)v=mGlcoI^R4@ zg`a96Z$0OuSn%44+(`Skltb7QHfYvS;1mQCh_wPa?amjQ3&}iq5X9jP32-Nc9B{xF z^%XFs#Vewos2c@Je@xbEEsHm%R@b%NijfzcH_~nlrtP9u@&5=utEB+y5Jq9HX`rG% zJ-1Mu+#{n<u3vGpWVzPBM(2OMOgnE!t^c4acC<`JL}LSPhE;UuR?Z9O0MRWgr|8VvGkg8~Im6yMA zT+_kV3um}tyXQWBF>wUr5g9@F3-0$-J1>Jg3Jf`*DAGqNTnB0pBw>jy7V0Uj_*fNTDrer4yP=3!R(;QNAN*uY z#)x0eexa~GZs6WUDRr7PpsNkutIS8;cuN=GUmL6NDig`tU6-Cq&cNqpZ;T7H-968- z=US}NwIPy|wP;+RtjiF+A=W=z+Amn^#%uNQ7ArQ+Y==X3e|y!(*tOj?!|y!h%wrA= zGZC)2-(%!Z8;>p;z8A4Coq_>Gpc28p#T2uh`aZQuc%AXM?1ix=^Inv3ViPn%3f_UXio)}UOG20nq;mBew|CvEQlXuIMsO#iiFwR-z z3@}?Sk9I9&=jM)|b;JiPqA(1UM+yR-D2OvNVkLDu>+J?iw+e5(;AXyM)k{raa7~G4 zIre?r`EfJU0Lf@ZR;|WV)$Dlx|CyKR;Qte3tQ> zjwW|+H?s?inK8Jb9^cH=F>GPy7GzyLb}0$Z$@5osj9Q<2sXyUBJ=sHcPTyCdRx6LwNS-97|fQ zU%fxE#_t8gb@;dpBtIgfK#}{iz?z<-t;Y+?dhybI1SYS_LWj>eB6=8EYGr$*q{t}- zO(b9OzI-m9+JAkkhxR-tRMt5g8JimcF&|&s6iwTe1Qpz{xda__kH=lsG(6O?vLLI!4W^XokD|$t zdBV9!t|kDiOwlV1!U_|R^OA?+oKpkR(a91s3ZoUAaoyn!RzaF1h$@MWSODRRw@b6C z4SlUBW+@P>)l~eeQ^WVs(#gq!^KA}WEAa&hLAcT)dAUx&hs~z1!gbU<*4qDKk&Mc7 zecYgOlP{t4dtfMy zZ9S7Ga@}pBv^5lS0pAo@Q?0}WN+nzQ%e}=)m#7qH_hM}scV=Hog>z2;_j#70jr?LB?&6Yl!W}J22EyYI4 zFDx{cg-}Wu(*TFs57Urs}Y0lb|q0dEtim#j@n6;uL|i zyuWNGbWOMvQ3Yv2R_igr!E2ly5M&_*sgOddir4F(4Q=oRgz2K?u5Y&&B{13Li~R>_ z-7%U1pvLmT)z<>4JL8_HOa>noCF=qeUY^q*OO zByDmkaJ0}oPR7C0E_(hfV1Q{;F>r8VD$FykHp4d&t{!<7&*D!*6BltXUa~LQ8n8Ha z8nUkzH6wTU)ns2SDS+%M{2#nA zBnCm_yRSI(U5sAjoyD5$l&6|pCrNija6jdRNEpf@S7EDfq1bkKj`$sfd8D;@jW4hi z3N`H5%q8?7ZeV?3XlZFj2gJguSB~?9b&F*_*3Y1E{71=A#MZ&(@&{j<$^?hWg1M!h zI+=1uNj)guB};lD#`VY4CDxRXM#4obTE=ngpu*~S@uZnk9_KvPEcR{d1Orkt=jH6| z`mFT^0=B7-I^q)Q8&=0Ru0Z!h-4A+dd#jVk*PL>A!wXSDw@L}=5US+km|ufl<}$^&5-tkeIi}5 zm4Hva)1zrA)Tj7FZbE;Yo#^cVW7)?;Z825HwuqY5uit++aVW3+Arb$$jhv)~pt+Yz zR|fIX&Nx$_dw{wo3J3E;qvlonG2$D?$)FAkm+_Oph8f7RF0*FL-Kr|E1?|`XD6-Aj z@gMB&3*S()4Tg>wgc`%L+=tCLrKnH(3h6Y-1v9eCS0d~!^Ckm5ApF;hVQ`&-#==a| zXK;A)m5Q*PrsFxJG_?hpBK(0@t&!WT(%9j z<%!^$?C)469Wuw3Rvz#oL{Jmrg_!N#?WgoNg5j|QW4eS+WV#n0X&V?mVB5&=T}Is3 z5S}n1c(2+A;bUteoj^b^sND7$Mkr)V1~(93yr_=Rh9|*TGoBNjPy7DnDw}8GST5EX zVIV1bh16fjt@!wX)%2mAVzwVi&jl$ZG^^_|hOh!FrK*!q#*58NI2*3S%|?DO6oi;+ zCY%E)(7vbeVO#Po)+-?lH4=I(U++mM=Lzw~{kVg4?YL-_WJiTX>HQ1BPlLMSO_`0J zYVFgh&{}(~l%3q6(G`r{I{kMXlJ-efc9x>(^O(JRZT$~~t>QRiB|N_5pL@vmN#2=R#Zr@^hRPCpT7L5>qn zRv8b74&|}@hFArdCmEy4u;j*nt3qb{>JaO8`OL5()S;pGmewkrEZpJ^-6>Rq>&VpX zw&UBkq;iQAy##aTQHFc@rSF!~&VER-UigbW^un%#UMn~VTGZUD>OMVDx-i97ssP<8oDyx61_{K}rxm zs8!5eNPj{DMCA8l>9_Z(!OW4&U{_t$L5f(Pb!rWhX!$NM3bYbFhiPN9beP7ci=S9c z=v;Sgxne~Kw>R7Nc}&|K39=nl{NMjd6S`$3;3-6YgDixA@V#xMDWZ$pj97q-^9pFX zp{KBAMa;KU@<|Pf&NSkngu?VWB%7TH-);=ik>I6o`v2`s+{;%5{G5+PN1OA3TGLsT z!CKM)^bX_|BR5}9PN|Zr;{h4;d2wZcJYjQ~yVonw=K{h&ISZRm)GEUkC%B5iPj9Y3 zSBvV)Cjo&^sU5y9i8piFP43zRJO+VBU#p&0?YnsPeyb;2bb!JsW$#k{K1m+Z$P884^t?{Q+oZpT~@-*>+y-p+a@mkPe zN<-Tjint6h1c!W=Ynee=8FuR&18#y=#&194$<{awBQ5-1sOtT+P|u`1hrFH+(*P(o zD4g@F9(I@c^RyW!f2y(M2fnK|`sxLiWb~9g+rJD7#Fm$1!Y>P^qXU;I?~KR=m`&?b ze-^ijLWOlAz|cEf(5eSuh+jBs}t~l;DRmqr=LXj!RLq+EL5}h;bNGT zO8$`_J*5Rw?QJJ6Jbm$PdtDW+ANThE+Qy>fxP`jT;Qhe3RZ>CyHC%BZ*JG+MpaB2a|Z4RI$TbJP)G>#-+dgK=SXqsQ6VDsJ3=LZHc>ME=ymq>&u!huA}7D+4n$G1SC==(3<0#K&> zf9hNGA~w6O8EXE=w-Bh`KD-F@Ikm8na5iD}ZT;jF=Ih=!59C)ryMHm)>Rar=yGH(m z#JC>T7#j|6_WZ}RNXMhw1gI80GXG&_d9*t&P?$s3>bj6kmBRuIYMsHEB7HJt$zN>D zf_~B(dQgjF*t4Cu^Ri4}UPj~ZGBxhMX?d;5w!<%J!c+9LKpbm2)HN&yof5EaiNQW^ z4d7?8JYXxpIMBkNR!87SMJBrBzgnd2%{^Oo!IO#%J`?2?rwl5Ay6}dnEN2I5jeF;S4S9+pSsW z!g_g4$FH%&@^QEs(Rstm^P&x{+;#D8n$sozei!D^GB(g`JXd#39*Np1SK_YK{TIUl zJzdVicPaMCKZlfe;EtG--ZXLGxQa1WNTa`x^9q3JV`iB+4kSH+vM40k`ujFw-ZXh{*U&4^uUrpag zoE+DD6rwY}(A&C&{btHM5UC4_OGi>`wZhT;eKx)%b%XSUj)l38A_M?%RRZgHWBzr}pevt2Rz}qV=;B)nv@N`4uZ~R^$yW7=E&n($p=aWjhK^bYsc_7@N z68vxc9x{zsZJ86F0}#J|{#fzW{BYv2(CqX+RLmj7d{7x8EQJ09^`&8P$@p~3P{GJwj zMok>oujkh$Ps4^1fb;H8fUuKQ5bc}ac`Y~u(S*Yu=|XMYPJDO=EGJYA|1p=c*Ep*G zgcEd#l(1~6Z0p3oByzaQ7gMp8zyW2X;6bzRsR=92iZsInqgEea{k@k>LR_1ucID|! z!jUVnu!P0clBV5AX15`vgqFMNH|QN?FVhc|KMy_EI-8i-R-{Nc>E$Dd+e!wE2YwqK zI>&QNu>v^<-XngmLExf25uqZ&ZnIc$5IS2HRS z=|M`m$K}^q_2D0RC1qU>yJj$0=!hC&`liv7BOyE10Pm+DHunBp`D+tzU6<+i-C$2O<4>wIpZ=#Awq&sA@MY@Cl!CPRa;W`j`Cd*+p_mue z0}4MuCIZ`~&Ukw?x0<4(F8w?MtF=}SsYVnFW=zsYb!YSgnKFyUs-+)g@%EZjP5<;vp){$&qJDVfpwy_)DM@!tpvL2NF-Tmm4XD zaWmRvoAEN&kER9UVKl3>=}{FJ68Sxu^GUz?SU>aaA|=hrgi#SUUrvs$94#O&LVYph zHasu{7W7pTN@NAIH=m5KXE$uzfS>oV7s}FOraeYad0HNk=#ReWszabh{Le<)@$lK# zi*?=OAdKSN2PvCvr@w`JPX)c{$*8Uj^%%Zh4Lr57)E&XC<2Gx11o6h5JfxAx{Q8&OAWjUIEaq-TU^-x<64}%+yLGd5MFNooczzPm`sJky6bIR;Fz>669q@?^pLLq`R~rpm`+s{8*>O?pBF z73DgiBOZli5;XfQ_v;*fZdX);lf~Fwrs&~44U;E zc?n-`_}nDE6yKba-`xawvB@l+=7NpIoVXD^!@Y`|P>q^3~ZK-T-7{{az?0KA^iCsCtIREj%hs>C~{>%f~zfc!i- zMa6s^(Ptg?-jBmV@bMT6$M_$*3+5Q5JP8&T@pR$|XR|gK;6Jzmj8L$-78bq%&mlgP z6nk-_h-<0T(FbTL%wxNxSNMg=%Ax>pR8YkvMKg%epJ=^GB-|>6>;(?0HVR33C3lrF z#^v8)<9uM~)NXf`SKK&+plVRVVTk4cl+5d&bS@n__Hr{X>euOCcmp#-v!Idr=z-?hYo79fWL08P%8bb;I2_qK< zswup1q$0xgPcH@swL|XZ|Bx^;)jMugQ0$AcjGzBT>h*qbMJfF|Qg7e>Poy42`dUcm z9YnA^ipxE~^RI*f?yBIN%5q&{I04qss+~L5Bx}t5kA#tceii3V@h=DCUOR{0Q{e{6 z1JJ0?ikkB?n zwI!I1jgWzlii-~m7jaFPT#^(9Ui85vpp>b%0rL`nq310I7zNnNfkW5Kb;d>xk$Xz& zJBVupa7@S?g2Ehs?MHAJ&)Og^XzGO>!cwex-)^0hx+^&w+>)a8L0ZdBUdZJL6T7Vc z9*WEe6~(;)3LYBiMriPyYkj`pz>vT1>544K!|eM~<^P>j65 zBNDDC_=)%x;kad?ZvZmq$j?sD7#nd2fS3N^5?~O;J;(MazU_i(-19q*CGQSA51BcH z3L9ab;YI;9HmPDOubbrGlqQiPQVG5_sgd;|V32ZIb;ZV(LM zV0@>(9s`5?F9#zhPxt@E!5DPKo_6||gK?d4X_o?UF!H!%e&!c zOx)ElWd)O8bI~{+`HFFV@i=5g3oN+GhE0LQ+vBGs-N`|+BZv>Wnjla4&s*asxZKSo zqz99)b5Twnk|0{(hp6BEbufr0`xQ*^leq3`8d4$1Cb;O84s*qR68Pia(u{nFchT~k&6KihTtDm;Z;=rB?HkEfP?XC{@)IUMX*Tye>oVy((Lr500$$d zXpLU}uY)n7h~TpTa4_(Z|I5LEpaQ}{`TekBiCaso<<+8Sp>Yd!RD6I~%s}Jjl=LL)`8Lx!Sp*Ak{1GD=;Ef=00CE-pX zx17qoKfch3#|UjaOtE6IRC(beNSOsqA$ZV7g&;Y;uMW)pF#==VcshmV{C7D;zK+S@ zcWq-d0lJB>q^(46RN6Vcy)i3`qtBBxwDZHmp(-fG&d>7|7r#->a>9*zl>#x4P87q$ z(Y#Dhw4a1TP6+LgLKRZt>0U(Z)%5T)>n+XH*3zrO z=COt@FqxmPc_7PPY#>&O)>bPo?bV4X>daHaTivrD4nvpPn@nU+V@gi~+>-Rj1Odmn zmW_qG94YhG{j>+%ii>9fBIzd~{->6VHXv*v)pyQ15F$BA%{{m0wI}I?tA1cUkNI=H zbH3WK95Y~h1^HTm>MZrK;QBxgp=-O@KnBZvp14zeLrtdU|v^_eapk`S@U*QEw9Cbo=*Y7n`5V!x#(+U zqI|IUIZK#swPMCi?J^B31G&mFLr(b*AOnQ>4zz^>NX+4f|qtS-}cYQvcowfT&muRD#yA7@-EXTcCCxAx4h?x z^td-WSsS2ThJm8m`aPB_uIGOmHfuLg3xcy$%{L4|#eG(Cu#z&5B zrB|T*tNOG$V$?xk!yJaQ`j^XD#E8qxYDe#A_Z%mCH)o`@?f^?+_{!auJuRWB44GL} z-F3LW@@&zVN#w)a6wrw;m^bxhRYWT1$DG%yC1Q=Bs=%( z>bnlx(YY%Ni&GZTH>WIF5!hc4HdUbyms4jd%ih(&w@R)VNh-6_Voe9Q{>GIz{_P#x$TBVIh3|pe#0&wW#!)Ht zZ~`bJb=GYa6p&@54$WjjyBsTO>#6DgNcv1wgfB{MNUHP&q8gk!)r8U}yr1+DokymJ zzjqt8zlTtF`LSVLgCNXv&JOXFyEeQ7AA4HsW>y;!B0gf=-yljm=$7%5#tuq3zafnD6=}SBq)>%H2(OI|GGWKptQtT6@_XK4SX%P0^5(F8 zZlY!N7iPtURfxTibc?Nos`}!Af~% zG2Rf#-CSEyiCNE}Fc_Cpq_HNO`kr6p61`XE&~K}Ytku)!N$uU;*-7CB#B&u%WESmq zu_;=tN*Xg;ZVK2Jo>W$@sd>y6vpX#~-WkYve}ZoIm?rMY=;TE?71Q-uUI|Y}&8c{e z^kxtuWE|AA3|g1?$4~NzHy!F=@yy)rLgRbDM-)2v)7`S z94nj_IrEC>&csvOwvW2LfcwHSs&NHY8!DcEWo|J}$NB8YHVMCRm#uA&CF%5B zfV!fwDQa%)gXlXbEWa2y7a?^18ZE4{HLPj?q6??7jij@VTu6-e6PGUeQ;=Bq=~*+r z3L>romAO?7NXDHO;$prj*T8wrc6H|A+M5r)=jW0_Z_QR)khZus`s)ud^V9J%R(UzA z3=Ul=_=tqke_cvpuIu)7dD?$I^r-yULLelSTse3W+zpCJ{A%)Xb+}#V5SXbo% z45Dg`@%?{v-E(X&QMfPs+O~IX+qT{QYTLGL+qP}nyS8oa?sm7g?>WhN@45Hf$z+l> z$s{xL&sy{3`OKF9q=@V>J(jXPz2{h@Z&Cd1d*5$`zwY~RDANdwV(4$t#qKFl&&NQFWiI~dr$nwinQetfm*d$N-mYL5FRB~6@yE(|*zjGBKa7(Jv6&P{Gx>=*Ew|WvWtMw&`v3xd7jQ6iQ!M}LqVE|vczB!z2e>fEaaS2*Rga{^P}ZNpW`9cTEOIw?9| z65r5FyuFh!zx*T~bg%5s+s;$Do-3Zp4!v$rFh9Bu#Jk!_YUey?D&j_8w@@ zo@fz)L1}EWG?LVCIpMU5@>OMbG3m=!ObsBWPM2X&LMeT32ctt}xc!tb2K!N9_b9gQ zRkgSoMC!v=&@BiwxUIKuuj2uU!VqRi9Mdlgzk{rDHL^#EWtb(w!gLOpf}ngCUZ++f zH@U>$4|MC>wOR?StR$A7ycPSa(P7nRb`h`m0jFpB&FsB8N5k!T_WPP)@+`ep4Qv0K zN7rJ_;M^i1$fD;%C4jEXYDRnNbIS7pJFwHbYoZ?3MbBVt)nrX4X{o%gf_1RLzs3$X zLexAijNver|0B#?3tkAaW(8E=SP7Q`;UQbaV^qjHvWmBEg-Ow+QRPJz5mw{9EmD&f zF6HN*K*HY1jm|dj#A~o(bW}kK%0^VP<<#mnn9842Zo`~e0E*x!0*Ycd0e90tn+ZVz zL5fKz3`CQ2fjxA-0@OCAN~2irUT#av#dfnNS*ajpbF2?a42*m6oTAGW2eE?3RWIs! zR)aT(p-jDUj;M65Zgc!ap!vpwgFtseR#KaPG?)^-L3xaxA4@Oescpo+W5c@oo}?eW zl~i6J{vzYwsJ`~vUetr$kGWM3fh2DhtM4nwzkH)X^WsdSlxw)Q)%wS3bnWkOfaIUQ z+I{Ty;fTG)-|+%9qO*5;vOAZ(@6IpZ&2P81v3TZ(=U=ns6%&&PvLzUdm=xFCTwqw) z#t@nVb7tZ1@TIp&s+sEP+bi&tEY0)pNN1>d3Ql=mS4%5zRzwZC6^|pqCAaOjcmIZp zdav}_M19#&ISlh?agw!8#e3a%f*tkW6z$+&>%5Kd4zDuEY*OOh>b#4^ggyMres;G6_0@ZG$kAY2xF*7|VwcAE@vXO_9aXO$6ojHqJFT=Qo1l*8HvHYTmGF{GA~`ZRvLTyeiBl&2pQhdlWE|DrRC0*#$6cy;O9!5rY zQ{rnNfPvhR%E4e-m~+TzX_0f?-R^mxT(s#23%tTQBlD`mY?cAhK-UsRwgS}}utor& z+q=b!k6oMNHjbS)-8fBd-i?hW238MDCNY`o;9!XvBOh$SP%q7~jW=npHd56P?10p1 z6lUspk6`A_*IK~uJ!gWJF=;tijLPjXr#T{F)Ry3!1nkoaOnzg0Q!cRk)Lt#>No%z+ zJ7trM_((#{Rnnh^f4xN_R<)li6bGO_?J#HQbxvZWa^@u{oqdo_N}@vGB7O0T#~kjxGkD zng~IYjp9H6b4+b5Sa~gzLnQhqnUH8pYKeoKI>SP+NILc~q+Hah1dmtAYI?+)qQ=u3 z0bU||(e{_xpbxM@fvcJ6ixDewdG&`LgMu@Oja^Ze{Fq>I#Dvh&REi@w;_mvouR~S> zINK1XH1x%XoarH3X{7resAY^5qBOgbBHLl>)#>*rmJ_-0sVN+24YDTZARKtsi$ z43o#!fsp2+sm8s8ki>pLu@oU0RwDPNVucGMgC)&&dQ-%hZvbn^FK+->LqmH1B zDeknqavcK0^5$A7qEq$HFR9P%1lj6xB@fZ0LS}db=0DWkljtS($g&Q>FLLZh$bKHL zZr^`Y&3J^*J~Z}TQq?v85q2;=uS+n3q{=y%V9f$z2e!_fNI@BkZnyy%#(@JhOS#%BrPxxc17-X)@ zt%9@?QBT+9wrfEuQ^+=Rc8#Y!>oQ9j9SBZ;#)3%@g5d0gv{;D0DCrPIlaq=gKY+VM zS6@doaiL#08SrVSCtP`DAje!CybOHY_xFFy+JFDsI9+|(d;31h?)@vduKvihM$SU9 zorJw!b5+}HZ7xuyBPfhx#cn#_Nt11=##W2dY_`MXvWOsWls1=*C2`cpWlr>hsV7++ zPl~|1iH4ss-oF>c-?ECtJ+t&Ts9Wgp)@LJ)VYM?)#|^{(FE~rcmyw^^PKwOdyLFqN(m&DED*S`M@ zdu4xisAjpZm9v(+Ov)omp4YgVs~#MqmYX)REd+yNRB@kpOFwin-I4qbj6g&kW4n@AfryD3%G4L{-&;&u;broVNK=wAC=bWD8|3Hlnn>S>$W{6lR zg*c49?G$lB_gNR5IF=!z-!E)fw_y@NFd;fmmL#0`5ixBqKpo0(!=#Mk)_vww!If+< z+>`AKyQz+vf=10s^DS3X9wx9p4Ynj`Gl@AHjIbF@*JBTInlo0@b~Ge)1_F?2;E38o z!7>HZF%(vzki$}IiSRR3h)1Znag-RBEJnD#$>j-k)LPQcfn}fs?AY#-*7TAW?h`@nh}Y+v zmY|u-P4Gjt|N2MVl`y6qN7*q$juw7Uq$u2+DY7KgZFm^jys~ zWYnVQu4oYFCqDT%VpQF3^N48^dAf7UF~{lf^)YK%VbfA`KO6&+r^HUVc1zoISCQ$` z3~V%Jx@b6pEzoO#5wwUoD@$y_&pDQ{dPy*X;alax^mXE2&IlU4#h-F=^5IsqSm#!p za&9&|b-6r4P4}=N@oDfpZ0iW>db_JODPmhDpm8N4fkq)+-)sU_kU3+jP#)%lsXImd zU_O_kIYW{em5wBE;#SLFLABIbE3zPufSDZ&brz-B!(yJr8u5a#JnSqohAJWAfmWHJ zWaI`j^(D@?4AvmuN~r1*1tgiS?OStNQ+cS$1VNlYVW2?h&IKX@)iI)qV(qe!*=~l) z5*o4=yohY&7 z>s~m61dXor-6TJ2?*u|!zr+SCg$V+jBSkXi+WqYo{xdB`}uS+52Vw@CV+Q1%&ej3Mlh%H z?cK#m2Os%`X8CH$Cr(K>1%Bk%sOjxhlnp5gU4^VXo*`JHh-~OAS`}6xlWMq)rZ%F455!3}@M^PAk52F6z4Is06q*hI(43G#ZVX_IfW^VDO=OlSC4(7$+6 z7+EKC*qRpSEE_7b7yubgMt5jsbu5#Iyua$jrE@t?`^fwV{Y1GF(I9niPXS7U2zlF3 zN0xf05lk4jvc`Z&`r)U!xOBc)J82qqv6Seyp@B6%HH@ROdj8+Dhw=nYU0ny<6Wy2;-oD~2EEVHPT)sTS*>LfEnRGthR_m4_0 zBEN(Q`;ZHNV1Wc1oC&s%nd21zzDY@R+yqSC54sr_)Y=^I49N;xyFIBwMyAAQ&Z=D3PefO(?!le47_ucs&g)_z>~1RS2xO#qI{dPZ z3$$4jsHxdbw{UJ#k3TCzR8HmRC;z#v1`Sm{AWw(SXV2)S{n;aQXakVNGo2 zt~5fJ0i5VPsGrqj^3&MBe>Me4B&0bpd5CoDWg!sEU9Bn@8$o4W7JE@9t`Z?ZDw@qP z-8~sYl(<+VM4|Wls<@(7a(T~UZvU19QqJE~1*KZ&24!KXwyk4qKmXV|eZx>mFBGRGrI|-4`jt&)7(!o|0iFI#M#J^T!T^jLeBbO)MH?>qS zWu}KS`R2A$&DN5gvRd;UkDeLBL}3qt&~=XW8H!}vy$0&hI3?BM!ispPF*fse@yi4Wa9P~k zfo^cJUul7tZ(YMQn(nFXh0M3QYHIN!lv0W2|OX z0f5+Q7|5l*;=^mLd2|!>?m>Pq+O6_SP%AX(GAPOD-3XW-n*ozxT72VNRdQUr-{{rC zUa1mI9s(oo0V^~C)VtMnkJ5hLR_w)51gtg+0mOmCF3^lO(p5jJ7h!w0cl)9tr%IoG z?!~~8=JyO763ge@rdB zKn-)d2(*n@yDnefTif3c4Besl(!J8gDb2ab7=z=%gufgUKp&pH^tpE+nbt4^OHld~ z0W>Zc+o6o6CY=R!w(5oSQ}Ec-V~fc?F-g{x#idk{-m z!U0AqK496(I|({JAjf3Ww%P!{+Q@9Qx8oK%%5{fDTyd%yj#qBimj@s#*vA$t;cN@cUF5)8>? zt#4YXWp~|XKfeXX473z@*C#e5%=B|2k8_ZVNdo+UrYD0k{b&HuZ3UTQ z0hx1y%<-s{zfGjwnTrL)9Av94d#EVKm1+`LfV8&=qLU8Y3op*Vxgb0 zyuS2sf*$><-2I?2io8*$`K<|K*0P@qKpfOE+&GDmFB<>?BQ0y6vafl4*%rorrA+@Q z*N)`F{An_pzfiBgaErHuw7)3jE5(f;ZPHtklW@gGw7dY7nfZ;=t5S6<|0jM7Tj zA@S6juIca))$rounB7{$?K*$Zfn8cb?HbzZ#3&2KbZISj{x|4yA5iz^R0qCw`EUF1 zKnJ#i45ebXyEE^LLmZgx9eORuCIZOm&B~7JAM@YxW(j9H)${|pgV`Q1ftP8?(niwh zm1}^Gv4d9cIAapmTy@I<8#n-R6HkNhu}rx>ZbjR=y6x1OLD0@EQ(L!s-h|Is?Xe+# zY|342fsz(I?*#O`44nlAXR+2%_Q)ixIOMPLfn5*45o}&VUSUm^RcJ%KF-WepDT!M9 ztA$l4+lo0Aqn15)T9W%veT$FUZ0kejRd^0w5jcryv<=c8G>F0T)roGhZ;(~i*vO~| zIRI`VhxNq62svRBI}T%lz&y@55S=w%svWJe*=k5|-1r$U{J=R`KSI2e9u4n^WU=5p zU>MtpJfWWdC!xo_dNr;i%y-2b<>(<})x0aQg|0bD1yCtybeQocWH(Ct!OEMbIs!Rh zMho^a3F&AsVRoIp$vQcyCX(<8agPnXF5@76$z3JO8aCm@4eJ#WGtc5sbcO(}YyzGs zN-AkA(FteKydki9IMRs@SpsNQ>e^Z)sE2#1eT=|+SRCC1Zu%H>g?V&It5-2ERlr7^;$3?A zkTmTDsq^fS1p6Ee@#WLFPOs#klqKOlL}%5ktxCo=ni{W=I(l<%xwg}9oVIV+v0$>) zv94}ynA7yies;ptvfq0a+syg58xuX}xGnLr3elBb8lGqht3f+@qS2CPFLc%OM(UI@HZHzX`K}G`O7Ox=e7;rUz%A<}| zAoMV+{p6$5l2f4zITr_=!u@@Uk(=J>;Jm;hLc9xZB{4YW~z^FsTytx9rK>dl(&%vnFFgL050fK4PS5Dyri?jN}x-kR&Q=zQP@l(j$4=C;tE9nJ8w@FI0(PRUCVZie=*%rL$Ru0wYT+ewVS8QW=T zIw|-ode1d2Q-;7PD}lhJUwBU}L`QZw#!GUw8`iJz$R7wwjuLHE3}hBKz+}4$&p3TT@tw*IfV@=-UDK4gLE+pR?_RZ%-Nb+cviDFM?t9 zJST~0wfi-XZ?yvdd2?5AwZ8>8L?`O^A&g=eUbiQ}D#+ zJa_Mg4!aK_LOpe9EiV~jwobXePQv7oTe!o;(sc2BrbTfzfBN*yV=|t0XAIKlFra0OVxEar$#g@+B*cF4;=dcUfsXHuHWzK zA$Pyu9elsNKAyf;G3%=A;<(R^l9#PLUEWCcT|#e3gEVffb&>VZ>$Kfi^zFVMBHf%T zP3;+^0XDIf`qc#CwGMu}g=HpurDvZ5T) z-P+?<{FDIzixdd%x{u1(CN?!L37cTUpW|&++rukA<0nsI8>}RZPYQ&BP@-eb-KtqqpXZ& z+Q-M$vVbZ)2Vi$fwm^(;7SVnBW15fym(`uOdcu(CQ0+pbFDQ^dNBAeL`?i}}_j<6q z7IYr<;CE>nYe8@SF}5DuYLSnw;JkuGS;+V!kLO(~qykEr$($RhvYTQlJ%(B1q8pK% z^l>u4dh~TpE{`wMmvgTo+nQaeQE72S%9& z?x*_MRp=7!MsQsnnAm-e=|kcbcbyY)+cRBaARF{9QVA;0u1~i^=OG2&W%zR3Q3*W6{{v=1BF?q)F)DDZ+N;C;68E7$ zacj>kM5(%)MN?u{(PTDeBoBiQHuFzH)Qi)Nu{WYE-@7wIQZdt=(AcLv+8~Jx_!ZVN zNIa=-2hQFl&SdqnJi}hl&`HTvD#ntsE-*m)-DxUq`+Y6=^xP~$;!95di>HQmNO`Y7 z3#tbzp^^U4hX1mtbdJ|A2b-^sxJQmcgF^M}^;tIJpXFijiL{}w{4FyfTID_ArXSp9j?7y6M< zN$nS+?watgSKc&z4euO8ZH=p9t&qcl?sbzy+OyPPjcWr7^-7-gC8d7~Jf^$(U2}VQ zMAji{_dG&u#zMpBw`z_>Fn_ApslR1B&vWg8tw44|mAs7Nu=TXEB>J>wr08AKfGCm& z+*A0`_Rzg81g5DFM}=Gop%FHZk{{u%*eXO{nV_Lh#h;q}{cJe9;%4yVTYh!(F-{T) zdFXOqjbSbrndPCynDEA?qRQg<#;ogd;HKaZoBWQ^4D!iw3SxXTo-s9S{FN4-wNe=a ztuQR=?Dn4NGE7X$bmHhL##l|5F*Us+$Gh;tVDEx$2N12P7fXZ<@*Fp8V(NM)QZBS4 z1rnK-qxeYU{H063T~NWPd8#WT>ZKF1v=YJW0Bh1T{(F${FbN>s9`(5=eFgUu*$!65 zI+@#Jm&G>sqUKelUFMi(_7Op(i%gk6Rst$&S_8znDO&4{U81umom{wq&rG_L@}3hv z4v;W_U0eZ$ROVZ^l&TJ=_3I=t$Oap zsrOcN1Hio@ZcU9L_~CCL&LQdd`4m2KrH^HZO|30Y)@J%T?lgJvC2Vg>``*B*oE`Lj z-d=|%i}-r>Ocn3jksf&3I_)qyz_YL1jO1CGO53r;{%)puQ-cqJ^FMzkOH$Y4RaCd;_CSPKz=VWPm!L-r>m+ilRVn?~#VMfPGCj%D8RgYn;ZIA1ZmW z(UUTcaoACPIi=Q)$O*$gAh{CzSVg`kr;?uic~Woj8h}7xgyyJFlF*7kWnPoMoiuMU z%Qh*zO3DvdT3JmXlN2HrFm>s_LieTZ(r>A(3_O(^)n%+2nLY%QlEkU4ig`R@y0Cz1 zp&_#{@Xh4S5`xh5Jfw`?p5&)2Y(?nGQJ&ccq{kouev56WhWPZESjkUa1-(Qs86=0S z!HdvHHHdXx9TMD&5E#XpocZOzPzBwJb3YY1QbOU$1x_Pay>@NXa zl3~3Df)FNDPF}nkZ=~5^{;axGI?~_cJCV+LpGUtrF9Gz#FYTJGS8h4cmTT->vvMy# zwgY_`EL|^eoi&J09rAO9(ptD)-a0|o%yzuPKei1UZg-V;Za<@KHD1jZ4y;^FQ@Q_& zO~Jn0`!J|E5D*?7zq0`Bi}af9-gi+*i}wSJ5{A-(=Cc-;2?+_#blzU40$+-G7YNg4!)}F&5dt z%7~vsAxNd0Kumw`LW?ZdEQ>MZ?8)Jv&{xdOgsbb{lkvqgX#{1+pn-%rk-$jK)!~&O zrx6I$&N5n{u<(-kGF{ALz^I7=`p#l;VrrK~j1NZjC+mJ344w~8w?u#@-nlD7dhKzS zxmD$C<2SOxPkzuyw#Ru_SJ#B=yvT=PM+z+DAEomqQAHn3baD_!3xk?5ugYzNfDfn3 zQm3G7eR;l?D6-HKClJfDXS!Hp2pP-m5?3Z7ly}t3(ohwZ0A7F0zLZ(YV4T)+r}Y z0|B!fb2zWX@cX#1!Yv`n$H}HJ6imn{@r@=HPVYF*vJg4m)>X6Mv;i<98{U_4Gkj;$ zzfF=UB)TP+K`}c(BCT>RBo41~>OykqtKGL7NcgUBMh$*bsO;1D>*zXAp5koxL6B*p zn2K&P*V(wTl=bCSrJ%8eE9p}?{1QGu#H4f7Rv>KC^-(2}{BgvmV{D_k6a#76IOrd#^=+}XJ96L?g{*KNviw`ZbVPAxQIJ_86s3-x#0t~ZhLiG$4KK16}qXb65ZIEP^C6jXN_v|IB5 zy%LT`X3+owgut8AK;41RjgZi-n_W3$!Z;Mr1=8O_5pEri@3w+?BgSyh8KVx!s(Ut} zogO(DKXNBrxN5z7w#l?78(GvgF*&F!rHJS9r}X)5s*53HAW|9HH7R{+t7>9idzt{v0D6WP({bf_cQ) z%Mo%8SDM3-=2YO3AseXrQOn|neL-?@SaI~&u!HOy-Q|8-;|PJP&Jw*m)|?*LZ5(E9 z4>&iMwu@u4M?`SwQ4#H(>H*I90#(r9BIa){H=<}Mhk#9#Rz>XC{fTDvaT;DRydakP zYrIegT?8RPL|EaDTeQFePfL!0SZ6nQK?k!u9>Y$FAzXPPh}(RxQj5 zmD%67EAp}EWu|%buJY4tvf2S|H2xQJ?Hw9yH_G$NY|}RBpG}$Hg`oRgIAw^GBAdNH zmg7ssx%bz{m`^&k z*z;?sInwN$1O^8ZlR|H+(TcgRKKVsSYak~6k91P&08uYdT23&(R`Wn(L~43-<|R3 z@&qJknMc%Z#rYKDIOQqRD>3VegLcvVNkfZ9wNi7t=U%p4@e8xFah=JZUpKpruc0@c zV}rZR=_GF;ykv~z0je$_y*s_PD=yC6G~QLJngAmxDm3i=Ng7W6Ph0kS&IjB*kpMvw|&nXt`dR4;=)WdX9Ne-VkV-n&g{8!wZ` zS&5iNeL3}5rAN~bZKXTH=Zp`NftIGgi^cr*u&FoVck?`s|jMN1f=jqtWfOJq*U5-LRgj8 zm4P)fS_}m%`8&pd^#VlAtUz-|05hJ;N+dsc+63qbUBO7#ReWQ4Xb* zz!1^I2g0X&VprJFdHe>+WqGctf9Hh<`NK>6Q`tiwkjggtCmAVF#3;UXACIsyG0$q=1qJ(#K`9CTbNVWw(gVx4qm#(-@uMpyQp{YyM-du#d4 z`=={&%?942@|b~N!P~!o}ux@J|(#`2uSKaP4RZAD~pSYfy-tRD+wH> z{nYKY-CBwh8#gu?HO9L(($|o~BBsY70RsKRoID|!H^b3~3QoYT2IOmTGzX;j>D127 zj_1SruY>?{+AwR(cwgTuUPtho>a)(&-uOwd@xbFSIXBLn+Wu`6)9tXAVLPth<(x1B zH;OuOSG#0V5RfsxB_(QzwAyD4U2z9mH1OqDCJ$~5X@#jrbY|1u*^~-c)un^!&1bw@3CBG|LJ@E0_1?vh9_W5 zTT0?HNe?=XjVDs|IQY4Fe(w3Y_&GR+fT>4r=PZ^yP!@151AL6Rz;kDm%8dCSA&JMY zVuE2LGR=DmxI*yWr7;hDV47+=tR`sY7H_=fAWw4jUhrKJTyHQ=FU|JF@I`dG!`t0P zQsa|Sw~3s7)1|zeP3p!GAaz|bzMlxIo0DcXrL{6(ZSBVvJyRc8RI2^`)yk!`9_2Sk z5_qh}8fUo@;{7L6{l&bg3U8Yk*n%G8)IL7T85eHBUE(90E8%EQKm>9mRl<_DzhzuY^0e4jT5)GnC`v*Me097k66m%ph!`s9m3CRcLHStMhZ zg}bdYu4r+z+i&(^k1dxvfN0<1U;esHF75qsRX)tu%d<5;j;cIeH|bc^m<#ZK&2U|6 zWGDqTz^}>lRN*P|t-c+I=aI!O@JY^(vKxwbJgToAEiR` zDjZ(wbq-|{P~@qfrR2JZDLPDjlZxvm;Je@?otM!&H*;h7Eo*72EbF?6uQ2EgpRdB6 z@Qk_EV2XdT+?6N%UNR`oNe|Uo{+NZK@Q&JDRVb zGch6SNuKjj0Us9e+w)Sgz;B`cRl}O}v>Q+#zogJA)`QD-X6L^dI^UeEAX{Oq)Mw!w z&=$0DN;p~JI+>`eDy7S**knhfd}Oj=nj+m=GgN+s9}JEBdZ+#CjRQPg3b$@KWX4&@ z9|gVMnk@#&J(r$9k1qGLV0g-OjiC*VJ(D`#q0@LXp}-`G4|2gSV;Jn|}x<8d}t+akMF^9j&dbUmDj zqo{{>Srb+TyloeE8q<*AIag?}V1&BEhdV1_99%h8x!BaMpBixsc+l#n2y%^%g*3Ci zLQ7;*DF6po8oBpKyG&fRsHg0LG!nqfN%3pQ1HzZ1e$fyPZ5_@9p#)DFr8+rKcWgCf zQqJq0tWxJ!b;UH1d!~%g!pTLl;~hl0Riq|EKHyd{h);b;mBv;8L0`F1j3>^wrVN3y?e(fMt0p#l#jQ+D5yzrwak{oWXK`v^_ zzKr3c&Dy;ku$&dt2v}C`ahWOIZ?3AnSZmR`pzG_$v)dTB@~--$iPNaeB%Do% zK}f+T+=&yMWg4*>>K!U1l?hPR=wIfH2wTGDM(SAbX?C(m7qB5hRIsv&d}fyrJbHbZt1SlB4%x!(^Ne+SkKpvKRqd21KSJ*zS5z~Obcxe~jbc7A7C)_JR= z>zIM}EVGzEgU0P?c%ZY2y>C(3m(BTt!X57wW;yi&H8;3xq4acL2p3{Rc2VmX-gqwO zS1}Di!$5}_fEV*EZ!w6N*u2R__GcHJ%owJpE`mQ`e9s$gIE}_d)9kkB{)B6A&l}By zTxj7MeUkvqx4on}QQNBxheZw-O`Qy75Yq(6)Y+y1P(*m~@gB*`jb`Q71uKSv5{?H9 zfUFyUtt0xHqSVp{gu(Klub6>DvCp+Cie!M`1LYnO3PWni`zWU`O%63ck_>R9L1++g z(%tg16iGKkf(HdRIZdoiAtD+~x|(>R6Q|w~CYuUY;T#SmZxURPETr^L zslc)hbn7$fRBS$Q$Iwz!f>qUA5cIW7zIQ&brS5Oy z(sOsSf4WI;bl;lDE}Jtupr1?@GdMUoE_<&CVm`XUova(&cTkUz^v7upi(Pu$cW~)Y zhCX=yWz2jW8u=%``0jLDYBfsMN4F?Vh*F+Uix@Ww&m>o4i41^5*I8uSvV=oVF<8SV z{Q*w_!7-eaYP*Pmr^P>^bBH|j3Gg;yhS4hmO$Q1{Bm*l|5$VUnQt!G+xY;)-2|>450fED#|!AWVeQhzXhT)BlU7y%xTksvkY=!rRulQ6h(S^gQ$UX4(^Wd{&sWyNfFJx<{6Ys77Z;dcW#Ud za$1a_peJ~D(cwahuuWs7(kaN@p=EH%JnrfCze_FO@`L|cXP6PY*QBz@2B{%!2_jtu zX$e{+G$Y%-#feQ?n>FK2X13otNN6oTk0P2h4NoT1pXgwwjTj&uW3i-TlItQAU^a$Wu{3AomAw^A#4CRo+;$+Rn z%#)oiqojSu(R^Qv_^* z@}~2)dRCbIuX*P!8<55UUW{dJNw511Dj_-yoifRTiqh+mCB9G3x#8 zb2YzeuT3M!Mjr`29V9nA9JOfcL2Ngt&ZpQ>a}c;<%3X8hZ_c{JJ|hlmnfxv1KA%07 z>H5+(`D|1U&)-F+oAwfiW^xRVQ&mIXXc8=#PPx+mf~)3oN2i5wXJknRWSXWr19~x^ z6uvslKDPMDioP{KT${@W#gq>7O%j=p`-uL^3KK8MtXeiG1ffrs&;`N)HVDR{xo5&6 z#1U?8{}!)cD$M25=l`OsZyCCR7=(+e58Bs1N>qE6)5j4ms8+kTEI+mJa8HQ~F>@x8 zFD4vxD&i({s4aaP=1%)1Sv)CNvnQyB!Ve}mT)&?WfoKNk)vgVbXDz3TR!>2W_;hCi zPXpZ8&?({;HC=C(0=v{jg;-+ztizLtluirj@GLTk)Ayl71&gy2FlHFSzDrcPBM7Xx2(0JFnahT@EM;cK9G!{1-G9R2^^%(J!U{rA4(?O!jG;aig8JJ~b2 z4$@`@%yjK_2M9luO0`j!6q~6)=d?Fyt6?x!GuW=;3ZvZ|P?}wKujNa5kI!gJzJ_2e zpA$y{C9**pp8mXPFNUw;2!{hsT^u+hxBqQz;f3YMxS5$hwCDe71}jJHe@qmQ_3Ji6 zMf-Bs`+H{zR)J&Fim2t%Qu%k6dK@_?aGFT+$F(dI+#r69 z+a&+(21K}SmbY~fw(F=0996pOsGW@h6`QuBC%Va71CB9ATP<}E@1LcMX_4%bN4Z=d z(h&06r(xKG;4UU8RMHvmMqM{28CR7^du+Yr@HMAz}$i#3o z+G3Ts$$C2A{uAvV{vux~kzy7AHQ$gD;Lh`R&DR#y`F8AEQqIcG`bP+eLa7MCp9-{) zeJoP>wVmX+WHIv9E$*A=|Akx1X#WGZko`mdf8dtVj{k;RO0NG4w?x?fH{9~~?!V&} zL982A+Ix}cV0>kc%GH}%X0~Q5SC$~7G1EoEkt{(zgA8CLsp45;i~dgW47CH6o#8UY z@Fs&f@WCAEz)k?0XPDs*HViHuriWP#QPuKE?G|}t%P0_@#3(0jY}a3QJ`1p%ab*~H z6T;NJLT(7}bJ5&k@r-f@GFVZo-~3?OYOK|nV8^gx9g8()MOmc<=c3IxfjC}f7HK^d z5RpJ@^w0|816cs6qitj6*4I+vnk0UqjxVR~{Kgb6q7q>cOE4HtNoBjLd5MMlA>E{oG+P>J1dG>SLD& zmrBQumZo>6K*2prz0YaS+FQXmXphk$qaMQ7_DhLGf;bKIfX|C*@zdp^VPU~(vV5>n z`pZdYKE&Tx;g`j_6jwVR@Ec6fekp3$$LZf~cHdEQnVJxOc=v|FW=}{IA&8~GW}GBG zSs^|FzfGt+zyUj}+WUa@!uXq?ZH+;HsDEo~Yt&!A->db>o`*rd-|yec?c8*I`?syP z``gOxmA}YVJg5C{<-Gkrdu_kAH5{G#li0D~*za?`Dr83YxyXbyQ!*p@zkq2E+IetM zIweVB*!%F3m|{zQ^}Sx9XGo@DsY+1zJ9TK7ZUlGe+lcA={Vh4FQB<(x?=a^(5dx+~ zffJ7=cw9(lz=aC+5EXv;N(o_!R|x+?gSGm;Kz&3Q$tQqf)9-_8vR@uqG)J56x~O>j zAh`5Bm#Tm=CBdi0Vb2KXBnA!S5b%AZhaxq5jOjd>PKOtQ7?CU)Y+lNm^d+wd_M}mS zO&>n7htAjgUjYg+_0FAK?&sDphu*7KS&|YOK@O#whvYL;7AKN~pm)||ksuPcnmlZv ztjrI(-GP!}n%gPSv?{8ms&wOwRI)jV_Ro)hd~sR$p_bGea*N)H<$Z7cRA+nE+hDhx zBH&NuI+>MAZjvKpNeZmRB!SMEaeP>o!DOB%BY5eU_8ZXyi|Ie0gL60`Hy|PC3>+m$ zK8qqH7U}%TvowLs2!s4^q+@on(CG!LtC;Se{!Pp1>K~ngRs*1f8^?Lwb%mH|J2czl zDDvYCOdN~IS%nwGz~1(3Q16`{TwIWG>Cq-Xg{*_tX!eSe152tlRY;Jqi9B<&GK)Kh z>Xl}XB*R`Uyl>(xFEF~L7`?qZx!l^QeD-Qe(9QE3orO4aH4UshfnjLP64j`;w8t7` z5Gafjikv>b$$Z!9BeF$jd7NVw1;TvwT2+de&CY?%&V>!Zr;Rd_eQv5=f7CHGxLdYM zZGv1>{@Cu8)8drla_VJs@w@>w7HF>eE!GgrNzZ4ojD4Hv9%wWsaokYe&{=|i8qXXD z$-LPs$9)&H6OLH-N=F=*6>D%Ma8h`z$Wg{&*_xeX+?TE0eWs{^ZBk$r+(9sCUbX#F ztHq5f7cNc%G#mwLTMPF7OwZ`Ft4;U7XXWf(nJ<7T46y0(jyr{YL`nd(Y zn$|4;D`$+ymH%J7eDS&@|G)TpE&o47do=l99i6rO{{Z>_RIXuz{9nDw1^K`7p|boR zkpyJ_zWu_#t|p*z@f|e*C17er`4Z}YvbZgk4VFyKVmhG_NMm3VtC#F+2Ua2HuR^Z3 z_Dc8EMIzVlbeG0fB5$F0J`LiswxMm_zB802`r zsueD9eie_D`96;S(YKxyAL3j1YbCtj7>A|~Zb0TS0Ujdvtp1v^=&v}Az{9auTDPI@ zv}$Dpzmh@!56ID11z|*zY5=`o8-H41Ymn*IXKAFeiqd3?P85QdWzGY&(IxLjHy%#D z+*ciT;pF6-%i2|rb*3S%$8VHj;!bCgRAUw^N4{Ez$lPs!_z_CDS8Y={i^7G@|8gUG zxj`&OH`1k*#Wy+z#3-QZO&PWPW{v3!6|GvL`)XEnkl!LXwyH zA|J`3FYtPdYwk(b+za`AB#ZWPQXNVx>G7=;sLDEp-W^AMbjlL(&(S@F>LQ1X@Kct+ z$L2-R+00yr)?Q)>xz>L~ZS$3ssg=&4LzCXX*rbgh4K^E@;uxz7zXV=yMYavPjna4< zkkkpCQI+RWyP=$;Ox#Xb8$m?1L=hd2Q5tcQ^i?E>SPB(Q!7DUV+0bSApUp%UAL@e< z?D(7`o%AnEc|zi69L-sM;Hp}q+Kejg=d0yZIiU|HeGqcS7R`DgubT+8N4RpGF zt>a3goTBi-^fzO13y^J=*Ws#WgC8*#lA5vvGwJ(PH|iD9?ZQkNI&>SgZ0`CwgpPB* zJ18>sri`x-n6Krb7c6xnv`pfFgq@AgLUJl76~mKok)^3Kxj^*@T6)y61bv0uAZkoj zvJ`^P)ybu$%5IK}0)@FbO=3L)20F?G(b?{iFZOf{YgVs)Z1x`0t;;?w;Z4>EPkEYk z@+#A9b>3yVt;oyFfP09ynQkj)OC5MJOVZiAe*U@H#mK=2sahaHN&=@o+4p^xR+i?< z+Ru7O^bpLdlrpd&mTJO2md#?4KrMA|4uJxX4y=OyP>pj>B@@jbcJ!`kF8=@BJ^u5p&Hw-9tFQC^|2tp5c)5=Me3JIa z{{L&2e?8|K_kQ>y@6liF#4o?h`R&KI?`vWSez{Kj%lPU4;#~7xj7B9QH$Zb1Tq+HH z<*#uOSQE=+0jrOWtmWZccnS#~6je25rz*1VYSt-g%^gfUmgavME02TWz=899puxb^ zSn%W;K1bt#8yDYd6mYGJ?qm#b<}Dv?1aL6`Je}&Xu8fXTsxq0RUFKDZQIyU`B=*qT zlqZOYWss#-<}3gdSasENSAj;!fW-qj(wYkZ;_!LQ66UjT*?}LhXiO(*Oh7yaP$;0> zXSXC?fI^K%BbG*itqQBr&t0Z-5_1x$Dt9+Nz_uw7o8zBSG7xLUZ!E^8-Y`;o4ozXT z)GEc?-Dl$$L7HEzN%JqAG*`YTt3mTLgaCbuBFb9#1>yu6#QoF5Issn~0~Da+`ug%~ z%G_}(Ys$Q)%xlX0^HOGat>U>&9^7<_s`j2yF4BBmzgmr=MTt!BYN@-9K<|(#)00z; zmO8c%NCoGz+mUnm^)n15mcS*4vBes+BrzuU35g>d{*}caLxyW#TQeeSMr6&1tQnD- zxz(YON5%(7g>Ki#LANz>SR;pD3UXLWDQhX^UrI`O;UJ~_5@X{2nS~VTMk7<+)^jE8 zw;~`>6~1&q35UvWUwQOMr8l@(Lv{H(UC%#>@pw#q6fnZ0XSz-;jus}(u6#32!>TZ- zwK!H>kTu8EZ7OSy>sQWkt;Ml5a;Sb{jT|}x`{zRrYjJEXj{TF1W7!DyC9f^{{*J>4 zD*1I8y&EjauGdb&mQQ}EUi{0q8vlP4jWmzhn1_`g zHSupv$}<)8RuQ&2KzD>rre!|7mIdr0%Ka5!li*PBY#d?L83_;$L5fQR0ptmdYB`rS zuHHycIMr-mZOTz$4O>v&ekbQ{YRK%x3Mca6D zDkfWfT*oY);RFuri`~j6+75HJV%0s!_vO4BhcFIVWd?yHsTA3oj}Tzxl^#_$oOmHR z2Ew8<&I!Z_-x}glg$SD}XAguxDP^_FUu!bCwc%C+^Z`n#!s-7hB|Lcx!b|@6o4zF+*vaJB^$<~6{s|P%d|^aGHpeLCyAi`QlP^pXM|5Pexs7r zsm%aT&4sADSd?&T#85aKE)UxGv-og{H!B;S>NzH;9aa^AncF_~l(kD&CzptmB!R+A zm~-Y+sBZ=}bSmY1y;<3f>ZoVdWq3L(Xe%aU>NcrJA~Pm*z+x%s1za7bI(c7%fMQbW zKxsJ7c&_*sI!<({l^STH1)3AV5ho1INh}yZaa$z-f#dXv6$?MbQNXA8V~s32qtOY8 zCdqUU?NmR2@AU(_uU=FE-G1<1F6NHlx}qQv+)O3qQ`g8_9Az1MlZ>ub@d{XXG#Be$KL`u2_yc zo1WRNRy2P|EoTQ+cy4-d%%H~Jh#(xHCrP)Xph2=f^O2bCGaBbo-_DQ*b=M3K;!vYp=UhU%5#n&l@%x3y3^m(;l>vtnl zls}C@yX{ zNK68R(Imw&juJuww10l=p{oY81u!h-4z^CX+*nT*?uN06siKJ0FW!jM=dwciQ@*pk z`?8+3ZWbTyo1hNrd+RiR=woHN(RC%MiVwH=3d^*xT zg@!OIGL1e-ZTng=4DFvtF?@3Aq8L88Tq}kvhO<@-w=MQsF|6BKF}zj`uNA{<#qjbs z_K6k41<9%c+LVPtqSL`h0;%6IL?Vb3^3d@ZbW7s%qK~M#ER`(6@%imb#A5O9Ydw$u zKr{zZz)%dGn!o6v4wwQ0cU_FbhWv}ydS`0nT^F{mMU8x7lh(Bwd96lXtC81ge1U;V{&G8F&O zANg%G%m9tB|1q5-8Cmw+<9G9o=iUf^+<5LKv-!sJ|K0cs{fJ}AQjU%fkNAc+j~R%Q z0+-^Ud@g2x|5YKk-2J~e+CO}IBTnL5Jfa~@7I3|(`5>5ZPW%)SdI=_c!O|Gbs2?+}jYj|9|MUOKD;_b3 z&_(_f!gw{?<85sG=ADT@+uh!M>HX2$dA~VLk~!ZS4(V*-@hJp{dXx=EIGB*3dVZiE zAJ9lj{lE$RnJ0^od@~MlGT=X@I3`;gzj=2h^+xk~s7zlieTe4=*5mDcY&?Jd9h;H4 z;C7!sNB^n8XQ5wz+SvH&EA(F}^*@Spm%s`J%ui<|lJRXD8-HUdC^O?ULV7z`1a)h^ zH?iBs5DjIWW`Hn3#De@u+(VZ{YFplE5Z_9pD!LT?k^p_1(tx~wH<4ctxcvG($Wl{w z=WT4fVKJI9kd0>Zm`q8;1zyh$w~ypJV21^xt^3;un9tw8)29nQc`*C^0LY4Z?iokl zk#L4$GH0A7EM9DE+}zxZFrNx~!vuq?q|rHp!RZN+08SA^;PJhUsfGe?sD6>&0*DH< z!uiC@q8}L1al{iGhFb2mZv=uc!i|k9=^6~rsp?YpV>j}-zRLW-1SOk`Gs+YB^ufiU z(wpnytMkvjjg9@W+(~N`lxv9M4n{%}O`xt3)6c=~W`nT96V+&{i~Q*^&SQ2<10oCY z30Qi`e7WTS54N5~Bud0M^}fJT>tW^09}Hxm=D_rf`>QM zy2^L{WyFdm-;L9Ey#N3{&r+n?HTCD_-o@cu&8^(q2~>Cd^GxutUtHC9d8?bhEe?p8ZOc6_e69j zh#LBADjti!jQYiNUhZ{}8`Q>UL?S#2$#+RIhh%7?+Yw_S`iyRpI3+jeGZG^hbZQ&> zX~HHXA~Cr9faYIfGOcTnt{3Q?z(8<5(Cv=5v+HeVwA?z>5sU8_3;sSdj}JAy7^?TS zepTH1>RUDc?@d@Tn?IoBTduY6|1Vy?eqG@IU%q_tV$J_QMf>y#>8u~92Y1bz5*(0N zMeZk7Za@Pfiu@eL^Gi*qYm3bK7ffd(CaH zx$S=ROKf!}IwdCODXpmFj@o z>_0DG7wtbUcemI4-;=bj41wR3RP#VmVErxS2uCOllNh6rPNqp;ec*I94~hOXXLlqX zr=fO2mH{vt_680DZWP7+qDonyc}uM0XCvo~^K9R~Qp&H)5+=0H$&gLnM**3WD9AkVpFLA2?AbF& z_7|XM&*nJsr_Y{AxZFd}Hl(MBK6e|pdPJbf`lq^YzqHnP|0SFBUh`u{sS zuh;kgDO%(HXZc`H&#sOxz2rVwrjO41zq|e7^&0;_Npsx)4|oi@UKd`{T`kszb;e86Q~IF z>C*s_DA0PC;*pG4KniD4e3czY@qWVohG!x2q{-8>A3&c`64M#_j3OEZVr;w8+R)f* zzC-aa9U~T-9;6NQJbyW=`xgLkZnUaj1+^KZ>0al>uD+^X!~6>Ulm=G`zOd|V@YSC` zH%eQ#4$YPR`*<$ZGnDYIJU|=&_w~-}V*dZ#*V}9Q{}fH!TaEWjw*7g`?iZfDfe7`+q-8t30x> z+45}C8Km+0^hqB|`0YUI0!i$_@1Ee{zI@qhdFs}b+tA#oeq5bGrHRzto~L1W&O+)h zS_d#Uk5)i0029n3=o>RL=qR0R=oCMcMQ>%Kq3-()4#N@lKVGpDHsNQ{Q5>^)<1573 zj35%-(wIfkI;|Lb8K}?uh%;0dSa8BaRq#s(_{eNAw|LyMVe!aH;JxTlYY4{x7 z(%By3BmvLTWHBe-%*3+zQ?iGi4}r!EpKn~wLz-}e#o5A$lR1`dnvf!fMhUah#mJaZ zB_Vp?+oWRoQEz9bzq{Li^{W5!We?raFhnEbi>uMw+3EFrySu%9@71ebzxVQGZ$ouo zfuy(?&L#A7LV1!C(k2Po3`vAG6DFTP-qse{Hjn&P#;@&tgR%$EX9OSxFx2a3@O}&F zEB3xYJRM1}^7SpWW1f+f>3&Q5@RN90bs-+;efk8SzI^F@`UJV^;sx~3>!Hm^ye9kD zDp2#fzgM~cuJ_*r`>tO9xAU@S|J&VpxxLo^KS`^+T6NX7#No5`N9HG4W=oMlw>umQQHezK>Dvo)e5PI)qj8A}Fyig#bV&MmPH-oFx0?MMEU#r*u} ztw3S$Fi%UOfyCj;!&BBvGdWFdJ)q@-*ar^^f&-M!1Dt>pAdLcgOM?_!&WdP;BRVEr z5C^`2rNilz#01?zQVbk&ra|055(2LDmmVlCPhd1R%2N`@1ku?nO{6)Z)J zoBgNc|F!$##r8V>|4CZq4JvK6Qn}}(rqV-YS<%LxB@uBM)QXx#LmOmAiLkQjq)Axa zE2xUh52(^GpqA!Gb62EV|57=?3E`F8gMEbOzY^ja4nuZ#9@ASIk_kByhDfjBwoos z_yZ?#_8cd^z?7<-uIyH(cDYESgwBWxF0*X{mN+^_;Mg`IvoB)24VdHF;C?b9`z(F9hXBEvUMG)$R{UC@n zl%^i-$3LT=QkD>HJ%dFIIkCG7C>Q7H`26;@0^Zy4;D39&f}!r^-_*~%8wcCldwV?1LvQl6RgPal zbeqEJJ#D((f3RYA)&RHM|JScy7VrPg*K7Zer)e!N3-ZQGAxsGZn`QR|ItOJPf6D@5 zN!z`UOt8P`q0fl?gnH39>4C;p@Ze(_lRF%SgAt7ebDT^!2B@du48)`P$2h(vwa>z@ z%C@N_h=?U!WMc*LClGuVq+jG`8)hS}4-1N$0n(zme(z3q8OukE78gNheZ8Wb=d9is_Bn>0!aT9*k8w$L`p0i9F= zos@v)mITHKGvR}T4OBkJ0qRxHO6kYHeCb#Jj)NJEzI@4Sd*%T3!N5_m+0f&$9LPBs z3O^fD?QwU80Z7bL=*$Fgg%TTag8cUmLp%N{b-_d6zm;vA-x5a5tWEKn(jW3qQU*8ct zuvAa-&9ZZHiwVZrjMCLHF*|I)Lm0P=h#r9%D%h%`0r)LTqvRYXQ!)Bm7N#=-U-7+? zBa-5;%aA4MnQ2JXxcxpcU3unFSTTvf+uo71rfN{F(`p@?^}O@g?03}IvdwRQML+pO z4qt`VN&bHn|L@&bU+hiiFt!_2X;q&>pu%HfqOxb#nR8ActFzF*~BGfk6#-9<;bV4|&ps_g3I?Ar`zN%f-z@GoEFl2$4d ziwGq{B?5u;-+hv~RQZ?p->~tx|L&7A9LUdVHNJf5H4O2?o3o3z`&aeT&vX&3Gd?`J zygEMJzdAlUg=)q7hldw7VB?z}G9QOi#uIPv58Hou-7C584g#JG#NCyE*FZMn_#{Nn7*@yXGL)1#}uo?U!j zd4kobPNfS7Z(qvuvyzRyy)?M1w| z*Zb|JA{JY{KI+X`V10*YWUJSTUJj4m?Ei3brTVKsA^dYvxBqK z)1!l{s)cgt!I|W0mAd-~&wjY-IK-MhI*;*?a-ZFjcyWOJDfuB1Ty~cMA0A&Goc(xo z@weJD*u7upNvM5Vat;O!b)14jug)`2<~(ChQ6=pUNz(D)93EX&o}1o_*RO(}<1jRa z?K+AKp}P*E1*e=GUtS%Z7Km#vKk~dKRC$JqE~Mh|-QM1N!(){^&kMG_S{@8kAl*L0iSJWt$I0jcE5&6@6^mr-~5KvuAIkGU1V%~r8jZ^9e6@J zKxyI8^~ryt6QWR$!+?&@FT2UxP;1tkv*7rgJIRGMf<(o^230a(ooG$@w>Tkpc;SL$ zzTLk%`s@DRK3x9r=FRbc=LLMtVJm*RSIsv&&=r0hO=7}Zgye58jxH}hygfU#m>v^p z--ha{gcNH|pf`e&z7Y;H)sfo_r3s=elX$%n%qnU6=w3detP{L_Q}z!Ij?S+>e1CLw zzJGH3qqQD8+uPgQv)U6?Vtg7vD6Dt@zI16!kak(YQGkr|$sEUcMiLV9J|f=419u%E zrVn=^KQ)QDr{mgniDGV!&rTbdTdSikdM_In2o~t*zptExZml1+eo)`E%jEDS$K92(D#`o4@3S;Y zPIKDh@RhASh6O4qGpuER=Rr9h05-B;w3X%!d#2Q=9}bT#!aKah@i0y!6Jw?o?L!g; z16Ad`4dT_s50_UT_D>JKJG;14$Y88vUL4NtOv4&WHMz zNqdJ5uV$k}OkLKgPkDkcgmj}(8U-Pdb*}3ucuiuk}b$kkbITd49 z4??m^9}ncbJwFBqp3IFPe;qyzOae{RJBK4?QtgP<(jDd2)bjbM52CMfH)+4Z*|9nM z{^<0BAm_z()m;{D&=vbgqH|eIT1B~*4>BiyOcFj!#KU}8UQY3S<$9Gs6mk*Bv0yot zVFt%>x*ashRXR{Jff{x4qpCx7=S{j$GtrZ?gZ+~ar~9^sOQBYdx4aB7d-)~Jw!In| zdYw_7Sn~WhiW92q>XAlPv@BE)&Qd-<@%Sut{m(YED!v5!KH>a?O`2|+V687N^Y*GD z-;n`wRy=WKG5a>gJ~<~bWp0+aZ!h)_jy{|pT^ye^SmmmqI}Z&90hML8kD^=sRAth} z{FCjF$^D$f6biqz!Y)3WhyM844jwJpK`H0lG)d;)l0*{hIWT%d@sRw?;-uVhPG7Zw zkg{bQk`OPH=SGFC((c%+ctz5!#CSrwfXP3NN{+zr&IXKkZ)2eAn2`9qVO~bdvQcDT zBs51b+4AIp!1on7{HpdrJhfQ$M&!F*ekTfW%3Glrpl%*S_zqK<9wWj3KYLf#+qTVx z-)sE}Hoiz4M3m(^P7xq@ku@zgpv8*0$W1W}vcyPiBvB)!CRx$__CfqmL|R`?TCZ7k zE(}|u$V1ZSNEDy*9GVx|?#HLzaMS1rge~`K!|Kq=@~f{r92XS5k0@dhiG!XB%;NoV z0VYeyqX1FAa80rozwn0X$Gm+a{V{iNM$hxux4u%IOnTX?!a{7>ddn9ma zd>RzE!2+k?z(l7@GaLZ!<7l}UUm^d_FFVjU;gwOBhRSlD7`K4DO z^F%vbx`4B8ztCq(t)NDp3S*Tk{++(Qta0kF7@Lt5AA zwK5N0&Ni!~FKOE~P)pM8DcS<-y)DjrON{qWeD_8d^qbOD{)?ZwV!py;iI>_dTeNG^kSJxDols5NjJwt>_~L)Trm{ z45?A+qXzZsGU_}n8S4LY5+f=p!MD8w^gjPBUGw?RT`ordzYA&N|EtZl@Z+@uO?irH zr|&mUN9MmLA%pFodOrF_`JbMr{-;F1@%)W#a;AR(d`U zsCs);g-D{R)$k~Kw|l+R5u-y5M_Y#Y3h0dh%u0=qKpvsAQuIjc;!#VV70toMBs9!{ zrdyMyS}*x#lsOt3J=soMt)s>uwD!dQFLS#Ok-`3-?=F3l{D1Cjw!#1UeDwdjkyic} zsAjx^NDA7K(i2@^o_Rh&0WOkL@Rq;PG3obOwhwDo*U~a)NK&oe3RYC3aqwAzd>RS8 zOtz?P(uApo<(8%`;eKKdvMCRau}hhWeNV)O+H-tj-1Lhx%E-*HZ@ zOsE7hL`$cLpam~hf2?vsku?D?Ns^EhT)td^08?hc5a$m6lr*<+{TFrkr#cXYPWhpJ zvQMdVU(wq0uZxW5zGc=SWH*_02-n`VbqMn$vkw2ZCg9Sem=p~Ba`B2;Q2KbQyf=D| zC`ZZ1r|3sJ?f@R+2v7lg=>g>7Ee#;G9X=4k)_v-~bbXIHV8+w9N6qiAYb_Eut@7dZ)0 zibNV~9%U!`uITJQ!OH%BC~t#yKzs&1xzL^0^$u|!6>I2|#7POgAtdDc zVQyr3R8em|NM&qo0PMYccjGpaI6D7ceTo`&_H;X|hwXl4wUgPa*y_X^x9w}WlgUkT z9f*V^v`K&gK)aPrexLmw6aW&Ws7KrVNOsLRleS1a3WcgdRiS`V8Ktp`NJ6_JqmpPL z`v-F(b#Fn^#skQR z)SSx3Vlh)$LKa<4QaXTfK~-?-sH(BnRX(BJYbq5Jd;kys`6cZ2_MZ24ySon?%S)P2 zqNwYofp%LYW8eAquJ>9b%Zi`-z5U+f=liXgsz|a-6vIr}RT!ZU@&w8!7FtEj#P|}y?*ga#M#w8u-43K>X6C!*iAf3LnlMf!0zp&4k^!hZ z%Y@YbcWrpO z&xq%5>0$uwcB}OOhM;I9sYV$A5#cbQAnBA!$|D*B<4RK!gP0-!Bx4p#t!Oee-CxY9 zZz9c=Hq~9(sgNnrgVuxA1JH|%4&XE+zvi^{0Aixap!EO%#%H6^;rL*9Jh~j79bJAq z`e6Y7xT1@HE`L3{e06?2fPauIdnJ>9x`B+bZ$F#>L9sAW(qJxE)C}9y8bF(5S+9x{ ztZBx9I1p323Pnk-G?l%U`{M*w1{Tn)&3vs2an6R@iIF_3WrM>oH8Ja00h6GO* zSlmbf+hioTmLh>HAsmGT7rGEpF_SdX7Td&h+IoPn(^=2dKOhN;`=sY@FDi#hh3viChrq>JE#B!kEP=Ns^Gp zBu$8>vEzZ%@MWj<02GziETXCduf;@l45m~EsE&FlDMxQenkCdo$Fj5Dlu4xrfNDYY zi=vU>vHvtn#DpZnz@mV+e^f@0kO@sb0`S<+UI{Y(-mk)WjIfj-PV*!Iqx^EF7a%6D zQOP$nq6tGT@q|&%Hd;O+QhiO-mFh>1cW5^gan}$ms>w#NXESbIsxB8XRoz?}@-csR zeU~l&hX=_l3SIzekx=QTep{F#!6TY!)mKK&=85T%UbD!w(l?OlF5z*vY`WhHy78iQ z2newgxE~lV{g(NslqY z;o-}plbx1p!1X^ib5A&TYFO-I5~rWFurBupgIlybAanE{d+}fp^42RVhnpLs&;xU zzc^Z*%qb`42RIPz-vCjT58&zUZt8xgG!;gDd%Cy(lHogpC#q^S_Lghx?SJu-SzfX$ zUNJ`njso%NK3XL?fbao?Fxp=sl>SW>PXCr>bDB~~lCIK18lBWYS`;XUQfYXvq20$@ zH_K*RNP4McbZHh>H2`XdX;kk{@k7X2y*SI%0KVwq|2nYO!~cbaVx`}uO;dbLn_N+; zFAY;(y5-i=v-zkKbt&@7D@jdJMiTZLy=0k2+2ITL>2u<-! z3LiIB+kd+IvQfc+f6Z!^y-5sbifY&C{Fi;Yb(FjlfrbR(TWm%~4bR{yyKK{?h| z>y$^djsf(y<(zwT`!yMhIb#QISdth`>IU)*1aEnKEqjvsy<14o_54rbl;IA-ot(kz z`G0SJclT+P|Mz!ypYPuD|HpXVzU{(i5s9RR!I#i(L-&tATK47gt1G&wzf$JKBd5N% zulL%}wocJD^v>;WU9WKcK|3Hf=N0NkRBF}UDVk(c(`pF@!uBOF9@97Qnd+fCsvVJD zBy}5pFMK$UY9(}{u*eSl4wGk3Vj5M)w$!Ar&`{nCys`WhT`ZLcnkWjsDoHes-{1+Y z`A5dI;(A&^MzO{BZ>kY-I~%L@)OEC?YJf`v#fL_?Zcv_D8kkW&W;0IX z1M{M}CTB@7SZv4iw=|ERMWlz#)FuNvt9`L(w_m=ci{b<2G5+1{b}Jjcg}8E_ZtJAF z`U3qC*L$ri#^V9J%r!A;@b{JgD-O?AN;R| zN71ms*B^hhydQSZ>XxWFr`L?$I20npUAW#eqfZ#++G58JBqCqsJWl8U-oEt{inLF8 z1g<-`YIJ>4+rO?;l;@Pcy)GI z{x}>RkKB)%ucjL+v+j*D)H%%)MT=K4p{d}(i`Qb}{|vlC%>!$&S`sF6nxs87cM>F%wCnu3OJBP&O{k{b%+ddZ zPKBgh>ojr#?xTsGcao_U*H&vgAz_0QtCX!1)ha|fI=og(F6X^+1sXpi@0ib5bW!>I zshyDb&nJ7<`~Pj=PD-*5=>Ok+yuZKf|9^TP|NT*(m9pIGO)p7S5$EgFq3u+mKd*M_ z$9Qo3YJ4#|zdRYf9ND3`=7LykJC>uD!>>n|XRnTrFV9Yoj}CsQ6vw#hUKvKcRA)Ro zI3Hb%2iD7DKb()gJ{n(~H?`!$x5IWWhF>?7)MVxgos2JrCkLa;qr(9_wBGh~dB>)h z>y4W>g})zn72dwZ-I^!`{QI*g#jTNE(5?OAc0}I34Yo1N;0t6YqZM=wVg=RaH?om`C0zZ)J8V9%`d-ydCkWBOg{(1lu8wMHy`bTU309bBBA zSL;+`%&Ya>VFgD(%Y+&pPN>#A9GxAX{&0CXdU;yyt_XgtmjC+r^sC|V^kR5% zbb2zbRBK`|E&f{x-q_6X@T<{rQwug0cSYqI@0X#E_H!99S!uXltfI>`G^y%sj#o6Q zwr$l1VcL&AbhFZ4wMeD0v-6|xj*drPj}9-#M~9<>;dvEt%K@3SnLZj1zd9aWj$aPX zFAi(-wl>4%J949KHk?D9QMV<|o-72V~TFC6E zE^Kq>-sc7wr19e$SFzlB5oZ^&QTIv;h!WBV5x zSKI=)uJ|)a93!qtawhTI+JQ*&wDkYHR)U|Qf3c@D(Yfl8NV98Nabga`SJvGy5h9_4 zH?)$`sF#Yk+QQ57g{^OoS*!#i65PVAe(dfIe(t4IDKe|JImV(}wOTm(mT06p&RG@6 z*ai&v&(4XaqB%{8cLa)z^5NOhcaO)F*8r8v0xARnOxch}tbt*M=4X{GjH*_&l}RIF zHLLm`0}c${)_VTETlt16psNM3VMS_zab!bdH$HpP0?+kkk}AqAbK28n?l4cU*7TUl z>)=F4B=|Lz8jnoOIQy+=Tlu4nxK?O;ppuis`dIP6O0DITEI?8`K$P>KJ$K-|R{Euo z6d0e1@`TZhX)l)|k)}CkdeKJ_!zQ^FQuSkF3G@}4b%~7TOw&l`lJ-f)x;PL#y{LL= z{Gf1$R=QoHK>MJuc!%jR0>lsq@h6dRh~+H!kk9kc_@Z#k+L>}Etcs#bc=7XNu*vw8 zN?Wfuv`TrL3C6WOHHn)bpz;Zxeen_cphdme1AC%!QiQtJ3mkDcASq1_jCuHR=gQ2A z>Y7Q|YPP}^sb1KkH~8R}j1^N`;rVe!qYA&pRN(nZ3YyG1wqckPHMeJvJj2c?H>w`3^Cx1--DF|~nE6LY-Ghrc z(QrfXoEx6hFD?g|Q&-5)ah_-|78=@i(#aqHibQN^9vik-uq+eZ$~iVqWEQVHyA9qB zY*jDB{o#ApBABJmo)xxoovDU4Ds#OIIOTb|3|bc$lD3eGhHri!r23ZIt(ficVy~z< z7PBQ<+X^>jb$57Afso*gMg@tO`7ZE`I0_yjXH+*d=1{LwQ@A2MH(A?7(8ot=*k zh8Ls50S5M8P^Hf;2@WHjlf=5JdI#=)gaFhP2nto45JRS!gE@_^RMRYNgQT#94!CpQ zkS;0*;5&ty@Xc@!B3ImTKtr=pxTpVa%z*m&tMlUxqdD;946%tDoDL%c*>vIB0Q1pb zTkz`QouF`{MaW}ubGRgew?pmMwKfb7Mo!=s?HhGN0D(E1%^TXFhBF!E(W0M-8+(U= z$aR)$Lx z`Q@bcviQ2Ku$tyc(v{1r894JsWrD|4b_GwG3KY}TTwo0$abvkz*581KBObHJsGx8& zr+Q8$kQ!hfE!ZZ0MTzoG6M}xamz!s$k)qFhjhEdne`v#Z$*hP67oebA%O_@y@16DOYo2pcZRB?hYA=929b zek4*Z$@tIVnjnt%*6{Nk1 z^bKBDeOX$dO>5UIrm8yYIK=Wol8MNdTt?w@aXendGuyyxOR;OBSMqEF@MpG$SG3Ih z@PL^wj>lU-9&`;?v_bs}pA9osw1*c=y~{|j!h0BqZRdTB!!B9xFb=n`ZM|#uZ(Nf6 zD6Z&2xtsSkjKm&!$1!`q$9ovkT3KtKG&i>FmhAHq3va_hw%NZJ2U(yEXn$=6+I}2= zV*V)yfo$|~xIyG=z~4q?w9jd-8{JiAFe2CP%E5J*^eRh^E_=DcRIZL}wFa=2*46k? zagwRI&_{>2(!7e=o>WL#`v=SvD=N{BB;MS24~Dy#b9aFG_+SY zbNmG$&P?@+)*S|HgXC{DR$J8%)3js;b3>S&95-6rVKYW7<4ny-+ zqnD%8R~Ntd`=9)W;`V@d8mdl)Q%&VLCGp}`1HL#e8z@&bVm`nlKYnAc?m$YTIpIvD z_EpN7LCc)gp&Ahi}ZeM6%h321q@642Npcu6hO%2XtY+v#~k z8G4!`k!A_i)Cim@UWeFV4zIzc_l}>_>1}ZNeOcWTP+|Xi7lQT77d^qF@vDm+Ls}bI z9&ey551ZJlXeQMAS@449!ux1rMOXT@9Tclg?e}Hvt=dj+MC$PDsJZ>y5IjXbR~^Gm{)9q<7_7*Alh+e+lPV`Lf^oc6+w zU7=LOm=t8C*mO4|S9K|Osl2@l7F<+76nGT_$NhWuD*6!waTP#9Z(t!GttMeHyEK62 z;Q|n)M8xpY;_{O2^ifM3vHnw0!e6!B& zw1x7|xFf)CX@j27Fr0_=CEzG%K{b2f-Vu(KmhE~#j6S*&fR$vqC4B4rul7krbhRDi zYwV+!rV+f!&cfoZLe~OVOu1&$1*n8Y#&((-bIE!|3Om8mk`^m_FkqJZjRij$T{|V2 zs@>7r+OPxK~;?V6)p4fVu1uDodCK5=o&Hqm%M z4M4mk5KETZ4H=PjcIp8P%h4Hm=y%V3cPo29&lKXr16Gy3z9HQCG0QOe2wE%KJLSou zYS!6t+SMTjGu%QpnCYQ$6?zo9eD#zfz`iwXhzo5en(#${ku#zcJpBBjnOplgNlKU6 z>Jb>KEC|w|0Jm%Lw0Z^iE2zb$16ieW+Q$XpVVhI|eM6841-Iyh7ee|wdpj1H#?;#2 z)%ay9L@&7Z_ZPUqU`Y!(T3vN8#dJ02EV};Mfm*wRFLuA!?Eq1b2tG6aasdKO51gBz z*X?2L0lf4Ch_ah zc3=bCYXrTs1xeKsEG{)Kn7?B;iAi`NsnHwPFj8^1)v7t(#)Z7eKx-K7?aeg%Y~`q2 zZL&2$^bUsGS{LeOqwXC>ux>YcX#}@0^WG7ZH74H*c(yhC)_bMJvNLa!`M34R)@{DS zx(#^P#uY9_ZdC8BB*3{XTiR0!=Hez$C`@CtrX{TwU81zSR-v~!M9hzSE|ew-Bdcl~ z%&gh~*ogR*aR~O=Ry1Cm9-a=Yddd{4GR!QBhWm`Y8;O*{grIWcayv%Uh(}2yH0^v$ z1FB7h5>-Sr6xZvo>i_OB5wN9@T^IAvfU|dse6Y{v-49qF1&;;MY`K@}hPcJX zaMK9iQ+!1T#0MGQHao@*OF)d+vV$ zKHV5%Qa*+z{&H;kuH*Ylap^k_ZgT|4I}dAXj$9iPVxR3JL+o?s_z?TNW0Z(}w$qlj z!q3JcI!p*wf3{YY@s#M6?qV;+m%YW!ktOyi6llYW#kRv1?&4x!;1}l=UafF@fEd4} zw-~*`t6d`7d5f>;!n)YdyIJwG2_CH`IR_X?lG3j0z$Di|)_b5W>y~cWb#|Y1S(-j9 zzpr-IUX+f}t(~>OT>XiCwKXXJJg(Yxvw?@c4gF)n+{_&#av>RpBFyYM{lq8ac8Up~ zCA7O?gAb4u6FMW2KS1S(Z;iLOXw=t$#^WmX5qJSm0Ax%xl_}#)>GA~fRyOg6_RQYu z{6w>}s2ha$)jouAbFD49x7Tdc53i$-td0vYF=R_)ceWD6e|N7|bLFvj?5A?@-M4qQ zlkfJHk`18{_TCa;OUL|d*4OHJb*3Q7rKDUp95)2Wc}(Sv5nU3vc*i4v<#i^@5N{Wp z&_3`@l#7PVqpOgIjG!ZitqNJ{2Gi^n^QcEe49||h-_FzfiFdo*pE$}0n;;wy(s6B5 zo;F8+fm?IYIwJ}Qw?qv@J6+ibn~dmr!woMzFw(iPRWGrB;`l#}8P{G2;mFFu_ttzd zrTcw2FJm-TtrnL4Twt1*0)eRz4~xmSxakFLe#^xTZ-U!E)7S-(bO1lKhu4H9MpOK$ z-GQIlXHuXl!T8L)Ff#I|_92}~64UsncG+WNoBxjPtd~^I=(lw7rRh6>(S99kNi>}; zzD!NA@N>eHzF1`R%amm1ePb6t1%+RdtlmvQfeL>7Ii;Fh@AXQm{N=S0eDG7d#7{@0 zhPE@k_^I94Uj^pDPi+fz8!96m{M1Gu+C~^?G0CTcpW2g!rmC~oku>WV8|=%n*Pq(I zcxqWUNV~iuT`E)dk0uZ|So#uFCUri{9KcUtEl@O=<7&u8Y!!JEcfb z6j)-liLG&{t2JHA>GsB+2{j_p>H#S5l7-tT@Umx4_zYtKFkL^<-W`5rm;^N|?lyFb znB<~GBWJ86aLu5t8Aa4)pi>8JJ|Be`2ZZQm| zGbkx<=$e&fkiM$aenS)}s$oKmO_^uwQ24^9CFK$A;PskKorKFi?Co1U6+v3QitNqL zy@sJM1^>~h!D;mB9GBwa4zZ>O{VS5%b=z$}R@NcEnEP>T;t|I6vnQ+QgK?JT=>T@u zQifUTXS8`emXxTwlY?!g)MJPdv@=Q4lo;*Zxd|9v15mJ$-Ppy7eoLo7_e15x;}(o{ zCPkb_RN~qnZl8I1y2z;IJQpcT-(>bW1$)<+bu&=H(UvWO)=;i>)*3{m@=1%fFqoAq z7E?)MATY~`BwSM(8)>;tJh(Oj$xX^jwl+wLiV0|j>aOoXDYJ!m+1uS;Lu5s%RlATx zYbyBw{=XlG-T#+#fBX57`(wBJAD5lM&z}cBf8P18&u%Nonv9(CsZgt1FN^D?95r-s zF+~$4ryY1f5=A@kisNdx3Q_BKyW6IzW_<`hV;`%(yS{IW5-e~>_C+OJOhaiG-0ub` zb)n{t=&!v0Ef`#PO$;jG3n)b~s@_XXq_mNn&h=GX(r1H}3SHa^_z8^&+A4Cwv?Ov7 zm}t($w-T0I(P^H*BSk6nxQOZUO0i?t12SO=)98ncX(aeGVa}XNGa)rueUpRba7kzM z4KQ7}wU~lOF;{zg`;W)@Bo--Q{6(t!JOB0Q*PJBiQaK?heUa*&EwqHaXB%1c(T|o9 ze}431*ZuXmf3@>pkACW{{l4?L0WDzfpMUI@yxsfx^PT?+zU|zS)7Rf?v8+-z7OUPc zTK#6%oyJcpyo9v+X@z-s%L_I9;j?BdqZS2FUU5<=-!*T?oT#G7Wg^_Ha<7>Zyxo^1 z?6*4rOO*!7ubC7a!wXUG%ke_{X`VDXXfjDNBH#OH4EcORiPRHH^cLf(1jH19&l76Y z)@5({oj}`0QLn&u+Niu1)@9+DE7jxG$ZXa$*Qt=wh`@*?^o7k%cB_L-=k-R4d(855 zLM5)?EOoBN8qJqCN;foY)I7;-)SxlAq!h(Yoz*>U$IRj_lfn>V&kE|*L2|BZA4+c% zQ^mNOAq1pmS;MXrh$zY%%=Ek9+bl90&rx4A9`mkT;%Tqee#a{|ZPm@U3Yz|NnG}ak z8*dSPmyX>5viB$`U5oEkvNSFOOD`=$4)E4X`$7vzW;A@0Pipzu3e6YZ{?RIxVC!kI z3PR;3bC)XEUr&xd1CO=8Mcs5w<07-1ef_C#p}&7V)w4eTLy+M54jI7K=YQDS-Fv!U z&;Rx0$+P?X4Q;P ztIl+^FoT6?+l%k4VRqMQV&)? zVkMjVqdCWK9ggwY@Ze(ugPa$lsoG!H#jm_1*;0?2a!Ug|D6%P36T`+LQvKq9RKK{- zKPc8E8S7u~_4Xh49~86Rz256aA~*y60KPXGGr_PY`i3d3fbk#{&T6Ec(%l_ibx;JB zqT%UoQ$-_5lPhX$`H04}vf=RnEEyN4iYE(WsEzowzX2l7QF7U!;SH_b3cFo#O{HXU zRn7MSRDn)zdnm5?ZEF7E+qb}JbK<>r8-52&i6R#(N(9i6j9t-1+hsazf(!32F1}%= zM+?BpG?4`x4_2gzd{}MntjJZlMIuZ4BlIZzw)lWj|0oX5HyhXqA857db0p3a+d~iOjLz<99k-++KxN(+I$wb`z z&3USV5ri_Ko)^0uou!N=UN%EeHNjxH@(c&}bhU-ezCVyxe;5E zl9+8dVSBq<&X{R*@xdnS>FW7<`W~jMG`n%(tgW_%W?Sw={0`hLx_i4JD<~S}lIg{P z;F`XvU$v>Qp!t+1i*q6L3zkruUcV-=0A$XG>O^o; zY`KvKaGrA@3ZxLYTRmYsHpCYu?wRKkR9wpCB>a;pl~dJ!`t0%ZC-r_`DJsLgX?t1w zOOf*`@u%jET~7PDNZlKNqfR7ZWK zl71b6pA zQ@@|u({cYaJ?s5{hTrbu`LoZ4{Qpm$JgfQt_MY!QzR&;qQJ%X0Z$fl5FCBpYEhg(d ze{-6oJvDbJ`!h+q806;Cn1x@vcGs+%IRj%tr$W-M73nUaXUDGSMw6qlyUMrH#tmh& zxgJ3K$;b32Zpum9WMo!Rv5o64bUJ-F7o+zEO-OVlrqg4VGHn$$trNH6JA%*Kj7)v8X%&AqC-cU#S|?;oFz|I4Oq{O{5m@_wWf zv|XW#&ZQg4G=*-Li>{2OETLVSQnk`rw@WhCy`l^FYQ{(y1TpE|21dY{~vz;y=odYc#G9M_|c8lIYa--&a}`}NZ5ySsV%K3QNkL%6~2nQ zZP#~mLQ7xKy&VJhcFekGz5XvJB)YTuze)dpzPtaduK(}u@80YGALFU(|2X57e1ATP zcVQE_+`H!@p%vTU7FzNuO}C~bR`tsU25!J7_q$G1(Y7?PadJ(aIiB9p#4`My!pNKr0Mq9Yy1BYV%kMzrOT^tk)gOxjxZUeveAYu- ztr1|#uf<}@5^Zm1?fOfoJ`v1*J8BKY8Ku%p2Ibmi`1nMyMncHg?QN|j2=vB%Dr8n- z#g<`nk?ih1oxcK%{o*)P0~-gOWh<6!ITM)_uUD~dBJ#Spey73RN#p&>ij9im`)5;c zMM9m2#bb8OC|mygK&aNQ82{-!gs~RCn2FvZI#|XAM07~|8ZLM9bG^h{6D+Tp6}KDKTn_C`+q*lV?^!S zw-p~TI@OI7sEJ@NUkypDU;nK8eQN=CmrdnjL-_H>+Yq*=8BR0uYff7p3Cnvp6^FmW z>hCal_Js1t4t#kxr&cF>C6h{>w{PFGh~C8i)?mJy0NBL;&z~>(f1f|uyXXIp@ieGf z6+P-+fx49fepskF0I)6iui)mDat+71r z`Pa$=O%#QqD{6;2qx@h9?QsEP8y-JfZ` z&mK`{on776SL!~X?k^Hh_km!$Q)nG#HPu2S(7mDyybc5Uxsv^aP5Q5|)BbBo@o(?t z?neX#7(mzF_=Q5G%S9$dnrY~2nJZlu!X`7Kl)E66X@D-KMhnOqx|rRn;cQ9YNxmw` z`GiI~fi8#7-d2vg{^8ucKG{dWIj^tOQux-U#w?Th#_(V%i|UPbyHhDr9Kn}v+`ZRV z>dURrSiRAHi;%UC8??5~fu#>}AZWuG>p$h$=D%_{A4$n?f=5D1sB`cM>ff&$>Sx^%n^${6IS%e;Va-@6VxJehN25ol$SI0d|L|ygv;I z?6+@yk|x04s@T1zFqJX}=u-rqeadLC4>id5zIFbmq0W!EeegcG;Dc`QSNpole*gQB zed~6fJ}~+GIZ48+V%s3oZdFw;57LP$cCDPPi7lYdtGYquoND&-Rc-(3Q@bwjpZD-I z`Tu){Pwdrw@1>7T`+rX#Kdt%ypFV#2{NDfnF`fr-W^akdG(&dM?Jc^KoF!(xhKxj4 zWJXo5^}wYWvbh8ms5wm%Frv=hDaiP&1Ck~L6SiTZu<||Oaq9taILzP_-kXrC*5^H)n!kl)v)1STr}Y57 zBa(?+!O`JJwR)Kpc*Spz#gz1IaVcK6deUrMV=SUPrCitH5^@Q!NMs9X z%oPwfoJtT9DA!Ccz$^XOpLdEi)bOd^W|HtC9h1XD005nl1V-5O5)e5z19aESlL%Y! zQ1BSaO9yOfKK#V+B7dU2SEb{Vz1VyF4bUAk8uJ$O)uF$>NY&vA`r9Unb{*Puzy^*i zmzhvhl~^q%+|N)-Dv#z4uuhRAYU0ur5Ntcd%ndWtBqTgyY{yQInzPKbG-Xr0Fl%3Antt?j_uqD~ zJF_y2Fn_YhwbBHQYBNoeD&Hd8feGbw$|6RRYU=@<<&^&*@-{pY691Czop9C(N1R-< zI5%xc2P8RZu21;pm;G|5vr1DA1v=bg)??CE8$d$_qSAd6dd`YeFvn<_R&Z}38P*E{b8M`<1KB04x zOv}9c4ZUo)+C#e-lh3|q*ZV>&iz(oyoD`fz9dw$L1nKBTntB|SkDNQ~gDSIcJ5nkT zW$>g`$t;L})mgg~6|yVrL+}75NC`_!<%B7%I^mL4s6zJUR~3qIyp+`}ArY>Tu7sU_ zv+`MOrO>rPq;~=_QlWwX+n5BEsVK+H*S5zL<)u?2zekBzE=}}}M?!V1o*pB?RmP%R zKF)8&#eV zfkr;2wN5eR(1cPBl13CI`D9V)r({A!f6Z;`TE-(GGa-xX)dTjkf@r@7UmLZ`bbC+? z-)mMdw%+<84^xvd6R=q*l_`nl5FiMQ2$?Lb7KeJl49qbXLb>AdA`3d^=l*Os#0;CfWtMx0S5QZ z&4_0uTU`o8B4b{+smP->S{_b`Q5$K@^0Z^=$Y?Z7&&z19s@ZIp@D-A*Q<_4Ekdm}g zO6@F9MUsdcyS^7m=30eawPQz`D-AOPuK~~68YJIdJa)~O>62}zMMZs^2XkKwKGO8ac(cI+;YH?{*e?Nob?f}SPS zgz=8on*$RbtpmPT8eGtE(P2pt9m8Se>ezAA-7#V@rbch-1j+=Gs4m&fjm^3s8sMpm z)T-uYrKfKLfr|wyv>BCVm5KQq*3T2{n`qb|5%} z&Ng@rnrX&xBVLi%h{jt?j5!s0>0OnQTeE~>y<~cR9?ygm;p*fguR35A;T{!tj5*V*WL#cw}cnc-gqT-t`mJJfe=5J~2 zosc4fRy^nqL)&md$6{Yqb0P_}rwBKlQXc22S6eF-;{}Vg|NL~;Wf~&bJ26Po%uML5 zf{D!ouVr4RKZ}@E`xcETDy<4sThUo>6@Z%KE0_w?h#N-;vXBt7DW=>CwA=;_zI5$1 zY#G^Rg1sU!ZGfgzvdt8Y#l;F!B=pV|tc2~Et^;pg3Bb~BRb>?}wC+fxR+Tn%RB_7` zmP*YMs7%OHGq`}{7ZnNzb6%z$8Q6IOMN{qCc!e&R*s)QL9D)`ZeM6%_#G~LX5F+V}NV~68 zw>{imz_T7)cm+iDY+Q)fqho=RQ(Kig*by>_cN=O}eev5EB*h$aMS~rG3Ig0uA;HnG zE$oTbL)k~^w*rZ-6NO!AD48TQoQbw$OS^fxXUL!aZ^zk9c;#y{j;8%{Jgo< zpkWEHn0nrWL#EJlvYE}lC(__$Q7}OP>SSRJShT#2iB*aLoJBNVN-tQaoG{1xss#0s zft8YIUN=$0(oC!B&jXiR|Kf3tl^wkA?KRiDlo?IM_#&CKb zZp)v(fZ@px@a@sbVFxI)JHT%;X$D!0grQuC12<{OLFgwXUQR9G#-a)hBDpML#S|`% zE{;bXI5|D(9-X{6KRWq(^m24^(Setv^Mh}OCl|x7j*gEmen1j>addGq8r$%Jp=;x8 zcz$to@alMY4ri~<&rZi9yV}~_!h|NqR#Tbais7yU?#x)Luu3i@%cRI8GwL}GcnUfC zN|F9bi5vJ!t+!FBJVjf@i)*G(vRBhAqEL7HtmusmH(HmCSxb9g^x(Kasi}0#$b==h zd46P8UVA|#!eQIs0upp~>Nyp15x83XPL>u@hb{w7X9=589?_jnvD@9LxN-~6&qjKG zWEBMkF-_P66*>fNCWTVPzJ>2n1BtZ4-SKAru+p=#dKV=ESj-5vUrB_sPnVIzH|iyHG$#f{R05H9&)lry#agZMMAr>n z#AaT|nB2Z$+)ZX6S3{4%qqTeD9x5~JL|B5I2@&70B=j9#fzl$&$c%PSC&&%pE?1z9 zf=H%0FO?fy1REkjB9W$sctd>Hj;Y$|ARQV7z3zx}Z4}OQ5??diB%QkO4W*bvf)8$R z&07xmj~)y$W4^(Hm%OIuVYyHTO!58PsP0vEt#1~t-EQ+*XfzkXdO^|CS=nqypDPfU zQj{khm(!o{h}uD9)(z^U-va55rkv@5AB)}F#KR6^k~l9E>MeaEc8un0w_2FOMatP% zOjWjfXb-*-H^vgOreuLI#AncOIX;YN;YqL?Qs~@nTLhiB?zIu5r5HtcQPU}RQA&wk zdWr%va6VyUGqI_a&xYYGzaws@1#V(Gr98GZ<|2t3T<1ilD2cpsU0|_f=UmEix6FBt ziBeP=_HjO{i!Cd3To^^wo8Z-I!4fL*HUnqPbxKPf4;L$- zJW7PJLVqGjbVap-_K!ceOCu#Akzef=o|sUoI;$_RqjDEQUUL>!n%Rch&k@t^R>6evP0cmC5f;mr@j_ja{% z-g2+CGw!TTl!OHV6&g)o5 z1vE=%JvGOLyT^pf2;IN#YcdOOsws|n&S{eFv|6nP!Lk9T{&0NDEWSNlExEtiHE(m<5g{w6lYIYV-kmUuF zV*5~nB5Ks-zrb}|lPjt)`W=rCDKEu~U`xACP}~j>VN@LMt`e{*=5QY})5eH$m!|{c z##$&0J-6_b;hKu-Ji`DxC0vxSrb+|l*GvkIIt!5^=W&Y!OwH7 zd%gYLe{Xf+8`HpJ^VhJ{$lynp9>IpaGD&ajZfFpgT~dRY|N3PO7(`BaMEhl;g&jmm zK&eq8@))~tXY2i(AoO2T4d*lyaKxvg)%xX^UnWG&TR38A-jg^EZ+7zF1dzCt>P%Ew zrXMuo4%`3o%P+0grCG0kEWVW^^aq%c(y;~epG#AyZ%5^BI;!rp^)7KBM|M`=z)VxC zHRLcnJAx~^FjMAro*sWUv6zZzeR&bueWm!p3!&669%#;J1UHqRDvjX%8(uf@bWU0SX0 ztp_NpeK#UyPNS=GBgjv$>7qD^4@#u|AW5m|7WYx~S}nIN@B5@N#JTi-OV^`w(k?{) z&`h1vFUxIaDy5OCww|jqz=>~-%%=eyos2Jr$HzmX%)*P|(edbT;QI@PHVEJtz*oT~ zd!sk*RtKC54AVe4Ym{0Wqmqc3a!yz@Hi~P&3IYtSLJOz}>%A6}CYq}x_Qjs@azB`| zHy%Ye_73FS&rTsAr9GqH@prZ%&g_t1i%ARD7P;{FLT|y0Fg~bpOcmO)pQhVvnn&pR zH>llej1L~btD+kxKW+^Ywun1MlBVL?ETs-f!Mae~va{?8FSFlTqdLI~mYnS!2o=^W zfZ52LMafOKmG5t&k)Q5<+ieaDI(w__09`U8|(x z2G(oAIOMA36zm^E_0%4q&Ep@J_>%E!rfL5--F@>^ zM)EiQr2DULzTW$PevQ64`Pb3Q|MO%aemj@HeKFkm`@JLQf6vq8{~=k{m6XIOeLp^+ zclZB1efsSF{)dn9-1~p-{Xh5qpL_q$z5nOl|8wvEx%dCv`+x5JKllEhd;ib9|L5NS zbN~F$f13P1zvjkTH1vP>+<$-D;{M;qPoLNG|Lr}wkN^27PchenRu`^`#1jz%XDGG6 zFIfX}00960>>NAK08#+}bio>Y literal 0 HcmV?d00001 diff --git a/charts/kubemq/kubemq-cluster/2.4.0/.helmignore b/charts/kubemq/kubemq-cluster/2.4.0/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/kubemq/kubemq-cluster/2.4.0/Chart.lock b/charts/kubemq/kubemq-cluster/2.4.0/Chart.lock new file mode 100644 index 000000000..29fd21449 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: kubemq-crds + repository: https://kubemq-io.github.io/charts + version: 2.3.7 +- name: kubemq-controller + repository: https://kubemq-io.github.io/charts + version: 1.9.3 +digest: sha256:c9b644d18249502f1f7ceb749b408da3844e9a5005da89ed03079cbed68de63b +generated: "2023-03-18T13:35:49.5815949+02:00" diff --git a/charts/kubemq/kubemq-cluster/2.4.0/Chart.yaml b/charts/kubemq/kubemq-cluster/2.4.0/Chart.yaml new file mode 100644 index 000000000..123c1568c --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/Chart.yaml @@ -0,0 +1,17 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: KubeMQ Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: kubemq-cluster +apiVersion: v2 +appVersion: 2.9.3 +description: A Helm chart for KubeMQ Cluster, Kubernetes Message Queue Broker +icon: file://assets/icons/kubemq-cluster.svg +kubeVersion: '>=1.21-0' +maintainers: +- email: info@kubemq.io + name: KubeMQ + url: https://kubemq.io +name: kubemq-cluster +type: application +version: 2.4.0 diff --git a/charts/kubemq/kubemq-cluster/2.4.0/README.md b/charts/kubemq/kubemq-cluster/2.4.0/README.md new file mode 100644 index 000000000..5cc0edafa --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/README.md @@ -0,0 +1,26 @@ +# kubemq-cluster + +`kubemq-cluster` is the Helm chart that installs the KubeMQ Cluster. + +## Installing + +For example: +```console +$ helm repo add kubemq-charts https://kubemq-io.github.io/charts +$ helm install --create-namespace -n kubemq kubemq-cluster kubemq-charts/kubemq-cluster +``` +## Upgrading the charts + +Please refer to the release notes of each version of the helm charts. +These can be found [here](https://github.com/kubemq/helm-charts/releases). + +## Uninstalling the charts + +To uninstall/delete kubemq-cluster use the following command: + +```console +$ helm uninstall -n kubemq kubemq-cluster +``` +The commands remove all the Kubernetes components associated with the chart. + +If you want to keep the history use `--keep-history` flag. diff --git a/charts/kubemq/kubemq-cluster/2.4.0/app-readme.md b/charts/kubemq/kubemq-cluster/2.4.0/app-readme.md new file mode 100644 index 000000000..c6ad8983c --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/app-readme.md @@ -0,0 +1,43 @@ +# KubeMQ Charts +KubeMQ is a Cloud Native, enterprise grade message queue broker for distributed services architecture. + +KubeMQ is delivered as a small, lightweight Docker container, designed for any type of workload and architecture running in Kubernetes or any other containers orchestration system which support Docker. + +## HELM +KubeMQ Helm charts required Helm v3. Please download/upgrade from [https://github.com/helm/helm](https://github.com/helm/helm) + +## Add KubeMQ Helm Repository + +``` +$ helm repo add kubemq-charts https://kubemq-io.github.io/charts +``` + +Verify KubeMQ helm repository charts is properly configured by: + +## Update KubeMQ Helm Repository +``` +$ helm repo update +``` + +## Install KubeMQ Cluster Chart + +``` console +$ helm install kubemq-crds kubemq-charts/kubemq-crds +$ helm install --wait --create-namespace -n kubemq kubemq-controller kubemq-charts/kubemq-controller +$ helm install --wait -n kubemq kubemq-cluster --set key={your-license-key} kubemq-charts/kubemq-cluster +``` + +## Uninstall KubeMQ Cluster Chart + +To uninstall/delete the kubemq-release deployment: + +``` console +$ helm uninstall -n kubemq kubemq-cluster +$ helm uninstall -n kubemq kubemq-controller +$ helm uninstall kubemq-crds +``` + +``` + +## Documentation +Please visit [https://docs.kubemq.io](https://docs.kubemq.io) for more information about KubeMQ. diff --git a/charts/kubemq/kubemq-cluster/2.4.0/questions.yaml b/charts/kubemq/kubemq-cluster/2.4.0/questions.yaml new file mode 100644 index 000000000..0b61881ac --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/questions.yaml @@ -0,0 +1,37 @@ +questions: + - variable: key + default: "" + required: true + label: KubeMQ Key + type: string + description: "KubeMQ Key - Register at https://kubemq.io" + group: "General Settings" + - variable: replicas + default: 3 + required: true + label: replicas + type: int + description: "Number of replicas of KubeMQ Nodes" + group: "General Settings" + - variable: image.image + default: "kubemq/kubemq:latest" + required: false + label: Image Repository + type: string + description: "KubeMQ Image Repository" + group: "General Settings" + - variable: volume.size + default: "" + required: false + label: Persistent Volume Size + type: string + description: "You can set this to a specific size, e.g. 10Gi, or leave it blank for not using persistent storage" + group: "General Settings" + - variable: volume.storageClass + default: "" + required: false + label: Persistent Volume Storage Class + type: string + description: "You can set this to a specific storage class, e.g. local-path, or leave it blank for using the default storage class" + group: "General Settings" + diff --git a/charts/kubemq/kubemq-cluster/2.4.0/templates/_helpers.tpl b/charts/kubemq/kubemq-cluster/2.4.0/templates/_helpers.tpl new file mode 100644 index 000000000..e1d2688d0 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/templates/_helpers.tpl @@ -0,0 +1,50 @@ +{{/* vim: set filetype=mustache: */}} + + +{{/*{{- define "kubemq.name" -}}*/}} +{{/*{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}*/}} +{{/*{{- end -}}*/}} + +{{- define "kubemq.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | 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 "kubemq.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 -}} + +{{/* +Generate chart secret name +*/}} +{{- define "kubemq.secretName" -}} +{{ default (include "kubemq.fullname" .) .Values.existingSecret }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "mychart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "mychart.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{- define "kubemq.crbName" -}} +{{- printf "kubemq-operator-%s-crb" .Release.Namespace -}} +{{- end -}} diff --git a/charts/kubemq/kubemq-cluster/2.4.0/templates/kubemqcluster.yaml b/charts/kubemq/kubemq-cluster/2.4.0/templates/kubemqcluster.yaml new file mode 100644 index 000000000..0f16dcb20 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/templates/kubemqcluster.yaml @@ -0,0 +1,21 @@ +apiVersion: core.k8s.kubemq.io/v1beta1 +kind: KubemqCluster +metadata: + name: {{ include "kubemq.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ include "kubemq.fullname" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +spec: + {{- if or .Values.key .Values.license }} + {{- if .Values.key }} + key: {{ .Values.key }} + {{- else if .Values.license }} + license: {{ .Values.license }} + {{- end }} + {{- else }} + {{- fail "Either .Values.key or .Values.license must be provided" }} + {{- end }} +{{ toYaml .Values | indent 2 }} \ No newline at end of file diff --git a/charts/kubemq/kubemq-cluster/2.4.0/templates/role_binding.yaml b/charts/kubemq/kubemq-cluster/2.4.0/templates/role_binding.yaml new file mode 100644 index 000000000..d4e4f7940 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/templates/role_binding.yaml @@ -0,0 +1,12 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kubemq-cluster-rb +subjects: + - kind: ServiceAccount + name: kubemq-cluster + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: kubemq-cluster-role + apiGroup: rbac.authorization.k8s.io diff --git a/charts/kubemq/kubemq-cluster/2.4.0/templates/service_account.yaml b/charts/kubemq/kubemq-cluster/2.4.0/templates/service_account.yaml new file mode 100644 index 000000000..6710ba6ce --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/templates/service_account.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubemq-cluster + namespace: {{ .Release.Namespace }} diff --git a/charts/kubemq/kubemq-cluster/2.4.0/values.yaml b/charts/kubemq/kubemq-cluster/2.4.0/values.yaml new file mode 100644 index 000000000..819fa5922 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/values.yaml @@ -0,0 +1 @@ +key: diff --git a/charts/kubemq/kubemq-cluster/2.4.0/values_example.yaml b/charts/kubemq/kubemq-cluster/2.4.0/values_example.yaml new file mode 100644 index 000000000..518fa8711 --- /dev/null +++ b/charts/kubemq/kubemq-cluster/2.4.0/values_example.yaml @@ -0,0 +1,117 @@ +# Number of replicas of KubeMQ Nodes - https://docs.kubemq.io/configuration/cluster/default-template +replicas: 3 + +# KubeMQ license key +key: kubemq license key + +# KubeMQ license data - https://docs.kubemq.io/configuration/cluster/set-license +license: kubemq license data + +# KubeMQ Volume Configuration - https://docs.kubemq.io/configuration/cluster/set-persistence-volume +volume: + size: 10Gi + storageClass: default + +# KubeMQ docker image - https://docs.kubemq.io/configuration/cluster/set-cluster-image +image: + image: kubemq/kubemq:latest + pullPolicy: Always + + +# KubeMQ Api interface - https://docs.kubemq.io/configuration/cluster/set-api-interface +api: + disabled: false + expose: NodePort + nodePort: 32080 + port: 8080 + +# KubeMQ gRPC interface - https://docs.kubemq.io/configuration/cluster/set-grpc-interface +grpc: + disabled: false + expose: NodePort + nodePort: 32000 + port: 50000 + bodyLimit: 10000000 +# KubeMQ REST interface - https://docs.kubemq.io/configuration/cluster/set-rest-interface +rest: + bodyLimit: 1000000 + disabled: true + expose: NodePort + nodePort: 32090 + port: 9090 + +# KubeMQ Authentication Configuration - https://docs.kubemq.io/configuration/cluster/set-authentication +authentication: + key: jwt + type: jwt token type + +# KubeMQ Authorization Configuration - https://docs.kubemq.io/configuration/cluster/set-authorization +authorization: + autoReload: 300000 + policy: policy type + url: remote url + +# KubeMQ Health Configuration - https://docs.kubemq.io/configuration/cluster/set-health-probe +health: + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 4 + successThreshold: 1 + timeoutSeconds: 10 + +# KubeMQ Logging Configuration - https://docs.kubemq.io/configuration/cluster/set-logs +log: + file: path to log file + level: 1 + +# KubeMQ NodeSelectors Configuration - https://docs.kubemq.io/configuration/cluster/set-node-selectors +nodeSelectors: + keys: + key: value + +# KubeMQ Queue Configuration - https://docs.kubemq.io/configuration/cluster/set-queues-settings +queue: + defaultVisibilitySeconds: 0 + defaultWaitTimeoutSeconds: 0 + maxDelaySeconds: 0 + maxExpirationSeconds: 0 + maxReQueues: 0 + maxReceiveMessagesRequest: 0 + maxVisibilitySeconds: 0 + maxWaitTimeoutSeconds: 0 + +# KubeMQ Resources Configuration - https://docs.kubemq.io/configuration/cluster/set-resources-limits +resources: + limitsCpu: "3" + limitsEphemeralStorage: 100Gi + limitsMemory: 2Gi + requestsCpu: "3" + requestsEphemeralStorage: 200Gi + requestsMemory: 4Gi + +# KubeMQ Routing Configuration - https://docs.kubemq.io/configuration/cluster/set-routing +routing: + autoReload: 300000 + data: routing data + url: routing url + +# KubeMQ Cluster Configuration - when standalone is true, KubeMQ will run as a single node +standalone: false + +# KubeMQ Store Configuration - https://docs.kubemq.io/configuration/cluster/set-store-settings +store: + clean: true + maxChannelSize: 0 + maxChannels: 0 + maxMessages: 0 + maxSubscribers: 0 + messagesRetentionMinutes: 0 + path: path to store + purgeInactiveMinutes: 0 + +# KubeMQ TLS Configuration - https://docs.kubemq.io/configuration/cluster/set-tls +tls: + ca: ca data + cert: cert data + key: key data + diff --git a/charts/linkerd/linkerd-control-plane/2024.8.3/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.8.3/Chart.yaml index f3088b0a5..31e4eca96 100644 --- a/charts/linkerd/linkerd-control-plane/2024.8.3/Chart.yaml +++ b/charts/linkerd/linkerd-control-plane/2024.8.3/Chart.yaml @@ -2,7 +2,6 @@ annotations: catalog.cattle.io/auto-install: linkerd-crds catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd Control Plane - catalog.cattle.io/featured: "5" catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/.helmignore b/charts/linkerd/linkerd-control-plane/2024.9.1/.helmignore new file mode 100644 index 000000000..79c90a806 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +OWNERS +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/Chart.lock b/charts/linkerd/linkerd-control-plane/2024.9.1/Chart.lock new file mode 100644 index 000000000..a0cb7ec8c --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/Chart.yaml new file mode 100644 index 000000000..c456a8e77 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1 +dependencies: +- name: partials + repository: file://./charts/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.9.1 diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/README.md b/charts/linkerd/linkerd-control-plane/2024.9.1/README.md new file mode 100644 index 000000000..8eb64e659 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1](https://img.shields.io/badge/Version-2024.9.1-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) +![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square) + +**Homepage:** + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Prerequisite: linkerd-crds chart + +Before installing this chart, please install the `linkerd-crds` chart, which +creates all the CRDs that the components from the current chart require. + +## Prerequisite: identity certificates + +The identity component of Linkerd requires setting up a trust anchor +certificate, and an issuer certificate with its key. These need to be provided +to Helm by the user (unlike when using the `linkerd install` CLI which can +generate these automatically). You can provide your own, or follow [these +instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new +ones. + +Alternatively, both trust anchor and identity issuer certificates may be +derived from in-cluster resources. Existing CA (trust anchor) certificates +**must** live in a `ConfigMap` resource named `linkerd-identity-trust-roots`. +Issuer certificates **must** live in a `Secret` named +`linkerd-identity-issuer`. Both resources should exist in the control-plane's +install namespace. In order to use an existing CA, Linkerd needs to be +installed with `identity.externalCA=true`. To use an existing issuer +certificate, Linkerd should be installed with +`identity.issuer.scheme=kubernetes.io/tls`. + +A more comprehensive description is in the [automatic certificate rotation +guide](https://linkerd.io/2.12/tasks/automatically-rotating-control-plane-tls-credentials/#a-note-on-third-party-cert-management-solutions). + +Note that the provided certificates must be ECDSA certificates. + +## Adding Linkerd's Helm repository + +Included here for completeness-sake, but should have already been added when +`linkerd-base` was installed. + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the chart + +You must provide the certificates and keys described in the preceding section, +and the same expiration date you used to generate the Issuer certificate. + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + linkerd/linkerd-control-plane +``` + +Note that you require to install this chart in the same namespace you installed +the `linkerd-base` chart. + +## Setting High-Availability + +Besides the default `values.yaml` file, the chart provides a `values-ha.yaml` +file that overrides some default values as to set things up under a +high-availability scenario, analogous to the `--ha` option in `linkerd install`. +Values such as higher number of replicas, higher memory/cpu limits and +affinities are specified in that file. + +You can get ahold of `values-ha.yaml` by fetching the chart files: + +```bash +helm fetch --untar linkerd/linkerd-control-plane +``` + +Then use the `-f` flag to provide the override file, for example: + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + -f linkerd2/values-ha.yaml + linkerd/linkerd-control-plane +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Extensions for Linkerd + +The current chart installs the core Linkerd components, which grant you +reliability and security features. Other functionality is available through +extensions. Check the corresponding docs for each one of the following +extensions: + +* Observability: + [Linkerd-viz](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/README.md) +* Multicluster: + [Linkerd-multicluster](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/README.md) +* Tracing: + [Linkerd-jaeger](https://github.com/linkerd/linkerd2/blob/main/jaeger/charts/linkerd-jaeger/README.md) + +## Requirements + +Kubernetes: `>=1.22.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../partials | partials | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| clusterDomain | string | `"cluster.local"` | Kubernetes DNS Domain name to use | +| clusterNetworks | string | `"10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,fd00::/8"` | The cluster networks for which service discovery is performed. This should include the pod and service networks, but need not include the node network. By default, all IPv4 private networks and all accepted IPv6 ULAs are specified so that resolution works in typical Kubernetes environments. | +| cniEnabled | bool | `false` | enabling this omits the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed | +| commonLabels | object | `{}` | Labels to apply to all resources | +| controlPlaneTracing | bool | `false` | enables control plane tracing | +| controlPlaneTracingNamespace | string | `"linkerd-jaeger"` | namespace to send control plane traces to | +| controller.podDisruptionBudget | object | `{"maxUnavailable":1}` | sets pod disruption budget parameter for all deployments | +| controller.podDisruptionBudget.maxUnavailable | int | `1` | Maximum number of pods that can be unavailable during disruption | +| controllerGID | int | `-1` | Optional customisation of the group ID for the control plane components (the group ID will be omitted if lower than 0) | +| controllerImage | string | `"cr.l5d.io/linkerd/controller"` | Docker image for the destination and identity components | +| controllerImageVersion | string | `""` | Optionally allow a specific container image Tag (or SHA) to be specified for the controllerImage. | +| controllerLogFormat | string | `"plain"` | Log format for the control plane components | +| controllerLogLevel | string | `"info"` | Log level for the control plane components | +| controllerReplicas | int | `1` | Number of replicas for each control plane pod | +| controllerUID | int | `2103` | User ID for the control plane components | +| debugContainer.image.name | string | `"cr.l5d.io/linkerd/debug"` | Docker image for the debug container | +| debugContainer.image.pullPolicy | string | imagePullPolicy | Pull policy for the debug container image | +| debugContainer.image.version | string | linkerdVersion | Tag for the debug container image | +| deploymentStrategy | object | `{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"}}` | default kubernetes deployment strategy | +| destinationController.livenessProbe.timeoutSeconds | int | `1` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.interval.seconds | int | `10` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.timeout.seconds | int | `3` | | +| destinationController.meshedHttp2ClientProtobuf.keep_alive.while_idle | bool | `true` | | +| destinationController.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.9.1/README.md.gotmpl b/charts/linkerd/linkerd-control-plane/2024.9.1/README.md.gotmpl new file mode 100644 index 000000000..19da2a82d --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/README.md.gotmpl @@ -0,0 +1,133 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Prerequisite: linkerd-crds chart + +Before installing this chart, please install the `linkerd-crds` chart, which +creates all the CRDs that the components from the current chart require. + +## Prerequisite: identity certificates + +The identity component of Linkerd requires setting up a trust anchor +certificate, and an issuer certificate with its key. These need to be provided +to Helm by the user (unlike when using the `linkerd install` CLI which can +generate these automatically). You can provide your own, or follow [these +instructions](https://linkerd.io/2/tasks/generate-certificates/) to generate new +ones. + +Alternatively, both trust anchor and identity issuer certificates may be +derived from in-cluster resources. Existing CA (trust anchor) certificates +**must** live in a `ConfigMap` resource named `linkerd-identity-trust-roots`. +Issuer certificates **must** live in a `Secret` named +`linkerd-identity-issuer`. Both resources should exist in the control-plane's +install namespace. In order to use an existing CA, Linkerd needs to be +installed with `identity.externalCA=true`. To use an existing issuer +certificate, Linkerd should be installed with +`identity.issuer.scheme=kubernetes.io/tls`. + +A more comprehensive description is in the [automatic certificate rotation +guide](https://linkerd.io/2.12/tasks/automatically-rotating-control-plane-tls-credentials/#a-note-on-third-party-cert-management-solutions). + +Note that the provided certificates must be ECDSA certificates. + +## Adding Linkerd's Helm repository + +Included here for completeness-sake, but should have already been added when +`linkerd-base` was installed. + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the chart + +You must provide the certificates and keys described in the preceding section, +and the same expiration date you used to generate the Issuer certificate. + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + linkerd/linkerd-control-plane +``` + +Note that you require to install this chart in the same namespace you installed +the `linkerd-base` chart. + +## Setting High-Availability + +Besides the default `values.yaml` file, the chart provides a `values-ha.yaml` +file that overrides some default values as to set things up under a +high-availability scenario, analogous to the `--ha` option in `linkerd install`. +Values such as higher number of replicas, higher memory/cpu limits and +affinities are specified in that file. + +You can get ahold of `values-ha.yaml` by fetching the chart files: + +```bash +helm fetch --untar linkerd/linkerd-control-plane +``` + +Then use the `-f` flag to provide the override file, for example: + +```bash +helm install linkerd-control-plane -n linkerd \ + --set-file identityTrustAnchorsPEM=ca.crt \ + --set-file identity.issuer.tls.crtPEM=issuer.crt \ + --set-file identity.issuer.tls.keyPEM=issuer.key \ + -f linkerd2/values-ha.yaml + linkerd/linkerd-control-plane +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Extensions for Linkerd + +The current chart installs the core Linkerd components, which grant you +reliability and security features. Other functionality is available through +extensions. Check the corresponding docs for each one of the following +extensions: + +* Observability: + [Linkerd-viz](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/README.md) +* Multicluster: + [Linkerd-multicluster](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/README.md) +* Tracing: + [Linkerd-jaeger](https://github.com/linkerd/linkerd2/blob/main/jaeger/charts/linkerd-jaeger/README.md) + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/app-readme.md b/charts/linkerd/linkerd-control-plane/2024.9.1/app-readme.md new file mode 100644 index 000000000..351eac5f0 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/app-readme.md @@ -0,0 +1,14 @@ +# Linkerd 2 Chart + +Linkerd is an ultra light, ultra simple, ultra powerful service mesh. Linkerd +adds security, observability, and reliability to Kubernetes, without the +complexity. + +This particular Helm chart only installs the control plane core. You will also need to install the +linkerd-crds chart. This chart should be automatically installed along with any other dependencies. +If it is not installed as a dependency, install it first. + +To gain access to the observability features, please install the linkerd-viz chart. +Other extensions are available (multicluster, jaeger) under the linkerd Helm repo. + +Full documentation available at: https://linkerd.io/2/overview/ diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/.helmignore b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/Chart.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/Chart.yaml new file mode 100644 index 000000000..23cfc167e --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: 'A Helm chart containing Linkerd partial templates, depended by the ''linkerd'' + and ''patch'' charts. ' +name: partials +version: 0.1.0 diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md new file mode 100644 index 000000000..10805c9b9 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md.gotmpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md.gotmpl new file mode 100644 index 000000000..37f510106 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/README.md.gotmpl @@ -0,0 +1,14 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/NOTES.txt b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_affinity.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_affinity.tpl new file mode 100644 index 000000000..5dde1da47 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_affinity.tpl @@ -0,0 +1,38 @@ +{{ define "linkerd.pod-affinity" -}} +podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: topology.kubernetes.io/zone + weight: 100 + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: kubernetes.io/hostname +{{- end }} + +{{ define "linkerd.node-affinity" -}} +nodeAffinity: +{{- toYaml .Values.nodeAffinity | trim | nindent 2 }} +{{- end }} + +{{ define "linkerd.affinity" -}} +{{- if or .Values.enablePodAntiAffinity .Values.nodeAffinity -}} +affinity: +{{- end }} +{{- if .Values.enablePodAntiAffinity -}} +{{- include "linkerd.pod-affinity" . | nindent 2 }} +{{- end }} +{{- if .Values.nodeAffinity -}} +{{- include "linkerd.node-affinity" . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_capabilities.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_capabilities.tpl new file mode 100644 index 000000000..a595d74c1 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_capabilities.tpl @@ -0,0 +1,16 @@ +{{- define "partials.proxy.capabilities" -}} +capabilities: + {{- if .Values.proxy.capabilities.add }} + add: + {{- toYaml .Values.proxy.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxy.capabilities.drop }} + drop: + {{- toYaml .Values.proxy.capabilities.drop | trim | nindent 4 }} + {{- end }} +{{- end -}} + +{{- define "partials.proxy-init.capabilities.drop" -}} +drop: +{{ toYaml .Values.proxyInit.capabilities.drop | trim }} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_debug.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_debug.tpl new file mode 100644 index 000000000..4df8cc77b --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_debug.tpl @@ -0,0 +1,15 @@ +{{- define "partials.debug" -}} +image: {{.Values.debugContainer.image.name}}:{{.Values.debugContainer.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.debugContainer.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-debug +terminationMessagePolicy: FallbackToLogsOnError +# some environments require probes, so we provide some infallible ones +livenessProbe: + exec: + command: + - "true" +readinessProbe: + exec: + command: + - "true" +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_helpers.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_helpers.tpl new file mode 100644 index 000000000..b6cdc34d0 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_helpers.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Splits a coma separated list into a list of string values. +For example "11,22,55,44" will become "11","22","55","44" +*/}} +{{- define "partials.splitStringList" -}} +{{- if gt (len (toString .)) 0 -}} +{{- $ports := toString . | splitList "," -}} +{{- $last := sub (len $ports) 1 -}} +{{- range $i,$port := $ports -}} +"{{$port}}"{{ternary "," "" (ne $i $last)}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_metadata.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_metadata.tpl new file mode 100644 index 000000000..04d2f1bea --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_metadata.tpl @@ -0,0 +1,17 @@ +{{- define "partials.annotations.created-by" -}} +linkerd.io/created-by: {{ .Values.cliVersion | default (printf "linkerd/helm %s" ( (.Values.image).version | default .Values.linkerdVersion)) }} +{{- end -}} + +{{- define "partials.proxy.annotations" -}} +linkerd.io/proxy-version: {{.Values.proxy.image.version | default .Values.linkerdVersion}} +cluster-autoscaler.kubernetes.io/safe-to-evict: "true" +linkerd.io/trust-root-sha256: {{ .Values.identityTrustAnchorsPEM | sha256sum }} +{{- end -}} + +{{/* +To add labels to the control-plane components, instead update at individual component manifests as +adding here would also update `spec.selector.matchLabels` which are immutable and would fail upgrades. +*/}} +{{- define "partials.proxy.labels" -}} +linkerd.io/proxy-{{.workloadKind}}: {{.component}} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_network-validator.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_network-validator.tpl new file mode 100644 index 000000000..276056395 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_network-validator.tpl @@ -0,0 +1,45 @@ +{{- define "partials.network-validator" -}} +name: linkerd-network-validator +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion }} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +{{ include "partials.resources" .Values.proxy.resources }} +{{- if or .Values.networkValidator.enableSecurityContext }} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault +{{- end }} +command: + - /usr/lib/linkerd/linkerd2-network-validator +args: + - --log-format + - {{ .Values.networkValidator.logFormat }} + - --log-level + - {{ .Values.networkValidator.logLevel }} + - --connect-addr + {{- if .Values.networkValidator.connectAddr }} + - {{ .Values.networkValidator.connectAddr | quote }} + {{- else if .Values.disableIPv6}} + - "1.1.1.1:20001" + {{- else }} + - "[fd00::1]:20001" + {{- end }} + - --listen-addr + {{- if .Values.networkValidator.listenAddr }} + - {{ .Values.networkValidator.listenAddr | quote }} + {{- else if .Values.disableIPv6}} + - "0.0.0.0:4140" + {{- else }} + - "[::]:4140" + {{- end }} + - --timeout + - {{ .Values.networkValidator.timeout }} + +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_nodeselector.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 000000000..4cde0ab16 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_nodeselector.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.node-selector" -}} +nodeSelector: +{{- toYaml .Values.nodeSelector | trim | nindent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 000000000..9651b3bd1 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl @@ -0,0 +1,18 @@ +{{- define "partials.proxy.config.annotations" -}} +{{- with .cpu }} +{{- with .request -}} +config.linkerd.io/proxy-cpu-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-cpu-limit: {{. | quote}} +{{- end}} +{{- end}} +{{- with .memory }} +{{- with .request }} +config.linkerd.io/proxy-memory-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-memory-limit: {{. | quote}} +{{- end}} +{{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-init.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 000000000..a307b1407 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/charts/partials/templates/_proxy.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy.tpl new file mode 100644 index 000000000..7880b394c --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_proxy.tpl @@ -0,0 +1,267 @@ +{{ 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 +{{- /* 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.9.1/charts/partials/templates/_pull-secrets.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 000000000..0c9aa4f01 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_pull-secrets.tpl @@ -0,0 +1,6 @@ +{{- define "partials.image-pull-secrets"}} +{{- if . }} +imagePullSecrets: +{{ toYaml . | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_resources.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_resources.tpl new file mode 100644 index 000000000..1fd6789fd --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_resources.tpl @@ -0,0 +1,28 @@ +{{- define "partials.resources" -}} +{{- $ephemeralStorage := index . "ephemeral-storage" -}} +resources: + {{- if or (.cpu).limit (.memory).limit ($ephemeralStorage).limit }} + limits: + {{- with (.cpu).limit }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).limit }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).limit }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} + {{- if or (.cpu).request (.memory).request ($ephemeralStorage).request }} + requests: + {{- with (.cpu).request }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).request }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).request }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_tolerations.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_tolerations.tpl new file mode 100644 index 000000000..c2292b146 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_trace.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_trace.tpl new file mode 100644 index 000000000..dee059541 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_trace.tpl @@ -0,0 +1,5 @@ +{{ define "partials.linkerd.trace" -}} +{{ if .Values.controlPlaneTracing -}} +- -trace-collector=collector.{{.Values.controlPlaneTracingNamespace}}.svc.{{.Values.clusterDomain}}:55678 +{{ end -}} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_validate.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_validate.tpl new file mode 100644 index 000000000..ba772c2fe --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_validate.tpl @@ -0,0 +1,19 @@ +{{- define "linkerd.webhook.validation" -}} + +{{- if and (.injectCaFrom) (.injectCaFromSecret) -}} +{{- fail "injectCaFrom and injectCaFromSecret cannot both be set" -}} +{{- end -}} + +{{- if and (or (.injectCaFrom) (.injectCaFromSecret)) (.caBundle) -}} +{{- fail "injectCaFrom or injectCaFromSecret cannot be set if providing a caBundle" -}} +{{- end -}} + +{{- if and (.externalSecret) (empty .caBundle) (empty .injectCaFrom) (empty .injectCaFromSecret) -}} +{{- fail "if externalSecret is set, then caBundle, injectCaFrom, or injectCaFromSecret must be set" -}} +{{- end }} + +{{- if and (or .injectCaFrom .injectCaFromSecret .caBundle) (not .externalSecret) -}} +{{- fail "if caBundle, injectCaFrom, or injectCaFromSecret is set, then externalSecret must be set" -}} +{{- end -}} + +{{- end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_volumes.tpl b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/templates/_volumes.tpl new file mode 100644 index 000000000..9684cf240 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/charts/partials/values.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/charts/partials/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/questions.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/questions.yaml new file mode 100644 index 000000000..4ae27870a --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/questions.yaml @@ -0,0 +1,19 @@ +questions: +- variable: identityTrustAnchorsPEM + label: "Trust root certificate (ECDSA)" + description: "Root certificate used to support mTLS connections between meshed pods" + required: true + type: multiline + group: Identity +- variable: identity.issuer.tls.crtPEM + label: "Issuer certificate (ECDSA)" + description: "Intermediate certificate, rooted on identityTrustAnchorsPEM, used to sign the Linkerd proxies' CSR" + required: true + type: multiline + group: Identity +- variable: identity.issuer.tls.keyPEM + label: "Key for the issuer certificate (ECDSA)" + description: "Private key for the certificate entered on crtPEM" + required: true + type: multiline + group: Identity diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/NOTES.txt b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/NOTES.txt new file mode 100644 index 000000000..4bd1be9fc --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/NOTES.txt @@ -0,0 +1,19 @@ +The Linkerd control plane was successfully installed 🎉 + +To help you manage your Linkerd service mesh you can install the Linkerd CLI by running: + + curl -sL https://run.linkerd.io/install | sh + +Alternatively, you can download the CLI directly via the Linkerd releases page: + + https://github.com/linkerd/linkerd2/releases/ + +To make sure everything works as expected, run the following: + + linkerd check + +The viz extension can be installed by running: + + helm install linkerd-viz linkerd/linkerd-viz + +Looking for more? Visit https://linkerd.io/2/getting-started/ diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config-rbac.yaml new file mode 100644 index 000000000..5f5c34203 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config-rbac.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} + name: ext-namespace-metadata-linkerd-config + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["linkerd-config"] diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config.yaml new file mode 100644 index 000000000..a9cea5f42 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/config.yaml @@ -0,0 +1,39 @@ +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: linkerd-config + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: controller + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +data: + linkerd-crds-chart-version: linkerd-crds-1.0.0-edge + values: | + {{- $values := deepCopy .Values }} + {{- /* + WARNING! All sensitive or private data such as TLS keys must be removed + here to avoid it being publicly readable. + */ -}} + {{- if kindIs "map" $values.identity.issuer.tls -}} + {{- $_ := unset $values.identity.issuer.tls "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.profileValidator -}} + {{- $_ := unset $values.profileValidator "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.proxyInjector -}} + {{- $_ := unset $values.proxyInjector "keyPEM"}} + {{- end -}} + {{- if kindIs "map" $values.policyValidator -}} + {{- $_ := unset $values.policyValidator "keyPEM"}} + {{- end -}} + {{- if (empty $values.identityTrustDomain) -}} + {{- $_ := set $values "identityTrustDomain" $values.clusterDomain}} + {{- end -}} + {{- $_ := unset $values "partials"}} + {{- $_ := unset $values "configs"}} + {{- $_ := unset $values "stage"}} + {{- toYaml $values | trim | nindent 4 }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/destination-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/destination-rbac.yaml new file mode 100644 index 000000000..38488cd04 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/templates/destination.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/destination.yaml new file mode 100644 index 000000000..4be0d21ab --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/templates/heartbeat-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat-rbac.yaml new file mode 100644 index 000000000..7b127543f --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat-rbac.yaml @@ -0,0 +1,78 @@ +{{ if not .Values.disableHeartBeat -}} +--- +### +### Heartbeat RBAC +### +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["linkerd-config"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: Role + name: linkerd-heartbeat + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: linkerd-heartbeat + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: linkerd-heartbeat + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: ClusterRole + name: linkerd-heartbeat + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-heartbeat + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: heartbeat + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/heartbeat.yaml new file mode 100644 index 000000000..956537623 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/templates/identity-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity-rbac.yaml new file mode 100644 index 000000000..6efdb4e10 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity-rbac.yaml @@ -0,0 +1,49 @@ +--- +### +### Identity Controller Service RBAC +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-identity + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: ["create"] +# TODO(ver) Restrict this to the Linkerd namespace. See +# https://github.com/linkerd/linkerd2/issues/9367 +- apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-identity + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-identity +subjects: +- kind: ServiceAccount + name: linkerd-identity + namespace: {{.Release.Namespace}} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-identity + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: identity + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity.yaml new file mode 100644 index 000000000..8ed15e630 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/identity.yaml @@ -0,0 +1,273 @@ +{{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.9.1/templates/namespace.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/namespace.yaml new file mode 100644 index 000000000..61461c132 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/namespace.yaml @@ -0,0 +1,18 @@ +{{- if eq .Release.Service "CLI" -}} +--- +### +### Linkerd Namespace +### +kind: Namespace +apiVersion: v1 +metadata: + name: {{ .Release.Namespace }} + annotations: + linkerd.io/inject: disabled + labels: + linkerd.io/is-control-plane: "true" + config.linkerd.io/admission-webhooks: disabled + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- /* linkerd-init requires extended capabilities and so requires priviledged mode */}} + pod-security.kubernetes.io/enforce: {{ ternary "restricted" "privileged" .Values.cniEnabled }} +{{ end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/podmonitor.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/podmonitor.yaml new file mode 100644 index 000000000..fd2b5d6ce --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/podmonitor.yaml @@ -0,0 +1,128 @@ +{{- $podMonitor := .Values.podMonitor -}} +{{- if and $podMonitor.enabled $podMonitor.controller.enabled }} +--- +### +### Prometheus Operator PodMonitor for Linkerd control-plane +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-controller" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: {{ tpl .Values.podMonitor.controller.namespaceSelector . | nindent 4 }} + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_container_port_name + action: keep + regex: admin-http + - sourceLabels: + - __meta_kubernetes_pod_container_name + action: replace + targetLabel: component +{{- end }} +{{- if and $podMonitor.enabled $podMonitor.serviceMirror.enabled }} +--- +### +### Prometheus Operator PodMonitor for Linkerd Service Mirror (multi-cluster) +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-service-mirror" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: + any: true + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_label_linkerd_io_control_plane_component + - __meta_kubernetes_pod_container_port_name + action: keep + regex: linkerd-service-mirror;admin-http$ + - sourceLabels: + - __meta_kubernetes_pod_container_name + action: replace + targetLabel: component +{{- end }} +{{- if and $podMonitor.enabled $podMonitor.proxy.enabled }} +--- +### +### Prometheus Operator PodMonitor Linkerd data-plane +### +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: "linkerd-proxy" + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{ .Release.Namespace }} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + {{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +spec: + namespaceSelector: + any: true + selector: + matchLabels: {} + podMetricsEndpoints: + - interval: {{ $podMonitor.scrapeInterval }} + scrapeTimeout: {{ $podMonitor.scrapeTimeout }} + relabelings: + - sourceLabels: + - __meta_kubernetes_pod_container_name + - __meta_kubernetes_pod_container_port_name + - __meta_kubernetes_pod_label_linkerd_io_control_plane_ns + action: keep + regex: ^linkerd-proxy;linkerd-admin;{{ .Release.Namespace }}$ + - sourceLabels: [ __meta_kubernetes_namespace ] + action: replace + targetLabel: namespace + - sourceLabels: [ __meta_kubernetes_pod_name ] + action: replace + targetLabel: pod + - sourceLabels: [ __meta_kubernetes_pod_label_linkerd_io_proxy_job ] + action: replace + targetLabel: k8s_job + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_job + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_(.+) + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + replacement: __tmp_pod_label_$1 + - action: labelmap + regex: __tmp_pod_label_linkerd_io_(.+) + replacement: __tmp_pod_label_$1 + - action: labeldrop + regex: __tmp_pod_label_linkerd_io_(.+) + - action: labelmap + regex: __tmp_pod_label_(.+) +{{- end }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector-rbac.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector-rbac.yaml new file mode 100644 index 000000000..c2c84c5c1 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector-rbac.yaml @@ -0,0 +1,120 @@ +--- +### +### Proxy Injector RBAC +### +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-proxy-injector + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +- apiGroups: [""] + resources: ["namespaces", "replicationcontrollers"] + verbs: ["list", "get", "watch"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["list", "watch"] +- apiGroups: ["extensions", "apps"] + resources: ["deployments", "replicasets", "daemonsets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["extensions", "batch"] + resources: ["cronjobs", "jobs"] + verbs: ["list", "get", "watch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: linkerd-{{.Release.Namespace}}-proxy-injector + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +subjects: +- kind: ServiceAccount + name: linkerd-proxy-injector + namespace: {{.Release.Namespace}} + apiGroup: "" +roleRef: + kind: ClusterRole + name: linkerd-{{.Release.Namespace}}-proxy-injector + apiGroup: rbac.authorization.k8s.io +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +{{- include "partials.image-pull-secrets" .Values.imagePullSecrets }} +--- +{{- $host := printf "linkerd-proxy-injector.%s.svc" .Release.Namespace }} +{{- $ca := genSelfSignedCert $host (list) (list $host) 365 }} +{{- if (not .Values.proxyInjector.externalSecret) }} +kind: Secret +apiVersion: v1 +metadata: + name: linkerd-proxy-injector-k8s-tls + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} + annotations: + {{ include "partials.annotations.created-by" . }} +type: kubernetes.io/tls +data: + tls.crt: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.proxyInjector.crtPEM)) (empty .Values.proxyInjector.crtPEM) }} + tls.key: {{ ternary (b64enc (trim $ca.Key)) (b64enc (trim .Values.proxyInjector.keyPEM)) (empty .Values.proxyInjector.keyPEM) }} +--- +{{- end }} +{{- include "linkerd.webhook.validation" .Values.proxyInjector }} +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: linkerd-proxy-injector-webhook-config + {{- if or (.Values.proxyInjector.injectCaFrom) (.Values.proxyInjector.injectCaFromSecret) }} + annotations: + {{- if .Values.proxyInjector.injectCaFrom }} + cert-manager.io/inject-ca-from: {{ .Values.proxyInjector.injectCaFrom }} + {{- end }} + {{- if .Values.proxyInjector.injectCaFromSecret }} + cert-manager.io/inject-ca-from-secret: {{ .Values.proxyInjector.injectCaFromSecret }} + {{- end }} + {{- end }} + labels: + linkerd.io/control-plane-component: proxy-injector + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +webhooks: +- name: linkerd-proxy-injector.linkerd.io + namespaceSelector: + {{- toYaml .Values.proxyInjector.namespaceSelector | trim | nindent 4 }} + objectSelector: + {{- toYaml .Values.proxyInjector.objectSelector | trim | nindent 4 }} + clientConfig: + service: + name: linkerd-proxy-injector + namespace: {{ .Release.Namespace }} + path: "/" + {{- if and (empty .Values.proxyInjector.injectCaFrom) (empty .Values.proxyInjector.injectCaFromSecret) }} + caBundle: {{ ternary (b64enc (trim $ca.Cert)) (b64enc (trim .Values.proxyInjector.caBundle)) (empty .Values.proxyInjector.caBundle) }} + {{- end }} + failurePolicy: {{.Values.webhookFailurePolicy}} + admissionReviewVersions: ["v1", "v1beta1"] + rules: + - operations: [ "CREATE" ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods", "services"] + scope: "Namespaced" + sideEffects: None + timeoutSeconds: {{ .Values.proxyInjector.timeoutSeconds | default 10 }} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/proxy-injector.yaml new file mode 100644 index 000000000..34b1d3ba4 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1/templates/psp.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/psp.yaml new file mode 100644 index 000000000..db91fea67 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/templates/psp.yaml @@ -0,0 +1,119 @@ +{{ if .Values.enablePSP -}} +--- +### +### Control Plane PSP +### +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: linkerd-{{.Release.Namespace}}-control-plane + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: "runtime/default" + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +spec: + {{- if or .Values.proxyInit.closeWaitTimeoutSecs .Values.proxyInit.runAsRoot }} + allowPrivilegeEscalation: true + {{- else }} + allowPrivilegeEscalation: false + {{- end }} + readOnlyRootFilesystem: true + {{- if empty .Values.cniEnabled }} + allowedCapabilities: + - NET_ADMIN + - NET_RAW + {{- end}} + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + seLinux: + rule: RunAsAny + runAsUser: + {{- if .Values.cniEnabled }} + rule: MustRunAsNonRoot + {{- else }} + rule: RunAsAny + {{- end }} + runAsGroup: + {{- if .Values.cniEnabled }} + rule: MustRunAs + ranges: + - min: 1000 + max: 999999 + {{- else }} + rule: RunAsAny + {{- end }} + supplementalGroups: + rule: MustRunAs + ranges: + {{- if .Values.cniEnabled }} + - min: 10001 + max: 65535 + {{- else }} + - min: 1 + max: 65535 + {{- end }} + fsGroup: + rule: MustRunAs + ranges: + {{- if .Values.cniEnabled }} + - min: 10001 + max: 65535 + {{- else }} + - min: 1 + max: 65535 + {{- end }} + volumes: + - configMap + - emptyDir + - secret + - projected + - downwardAPI + - persistentVolumeClaim +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: linkerd-psp + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +rules: +- apiGroups: ['policy', 'extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - linkerd-{{.Release.Namespace}}-control-plane +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: linkerd-psp + namespace: {{ .Release.Namespace }} + labels: + linkerd.io/control-plane-ns: {{.Release.Namespace}} + {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }} +roleRef: + kind: Role + name: linkerd-psp + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: linkerd-destination + namespace: {{.Release.Namespace}} +{{ if not .Values.disableHeartBeat -}} +- kind: ServiceAccount + name: linkerd-heartbeat + namespace: {{.Release.Namespace}} +{{ end -}} +- kind: ServiceAccount + name: linkerd-identity + namespace: {{.Release.Namespace}} +- kind: ServiceAccount + name: linkerd-proxy-injector + namespace: {{.Release.Namespace}} +{{ end -}} diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/values-ha.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/values-ha.yaml new file mode 100644 index 000000000..e3b8cbc07 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/values-ha.yaml @@ -0,0 +1,63 @@ +# This values.yaml file contains the values needed to enable HA mode. +# Usage: +# helm install -f values-ha.yaml + +# -- Create PodDisruptionBudget resources for each control plane workload +enablePodDisruptionBudget: true + +controller: + # -- sets pod disruption budget parameter for all deployments + podDisruptionBudget: + # -- Maximum number of pods that can be unavailable during disruption + maxUnavailable: 1 + +# -- Specify a deployment strategy for each control plane workload +deploymentStrategy: + rollingUpdate: + maxUnavailable: 1 + maxSurge: 25% + +# -- add PodAntiAffinity to each control plane workload +enablePodAntiAffinity: true + +# nodeAffinity: + +# proxy configuration +proxy: + resources: + cpu: + request: 100m + memory: + limit: 250Mi + request: 20Mi + +# controller configuration +controllerReplicas: 3 +controllerResources: &controller_resources + cpu: &controller_resources_cpu + limit: "" + request: 100m + memory: + limit: 250Mi + request: 50Mi +destinationResources: *controller_resources + +# identity configuration +identityResources: + cpu: *controller_resources_cpu + memory: + limit: 250Mi + request: 10Mi + +# heartbeat configuration +heartbeatResources: *controller_resources + +# proxy injector configuration +proxyInjectorResources: *controller_resources +webhookFailurePolicy: Fail + +# service profile validator configuration +spValidatorResources: *controller_resources + +# flag for linkerd check +highAvailability: true diff --git a/charts/linkerd/linkerd-control-plane/2024.9.1/values.yaml b/charts/linkerd/linkerd-control-plane/2024.9.1/values.yaml new file mode 100644 index 000000000..976a241c6 --- /dev/null +++ b/charts/linkerd/linkerd-control-plane/2024.9.1/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.9.1 +# -- default kubernetes deployment strategy +deploymentStrategy: + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% +# -- enables the use of EndpointSlice informers for the destination service; +# enableEndpointSlices should be set to true only if EndpointSlice K8s feature +# gate is on +enableEndpointSlices: true +# -- enables pod anti affinity creation on deployments for high availability +enablePodAntiAffinity: false +# -- enables the use of pprof endpoints on control plane component's admin +# servers +enablePprof: false +# -- enables the creation of pod disruption budgets for control plane components +enablePodDisruptionBudget: false +# -- disables routing IPv6 traffic in addition to IPv4 traffic through the +# proxy (IPv6 routing only available as of proxy-init v2.3.0 and linkerd-cni +# v1.4.0) +disableIPv6: true + +controller: + # -- sets pod disruption budget parameter for all deployments + podDisruptionBudget: + # -- Maximum number of pods that can be unavailable during disruption + maxUnavailable: 1 +# -- enabling this omits the NET_ADMIN capability in the PSP +# and the proxy-init container when injecting the proxy; +# requires the linkerd-cni plugin to already be installed +cniEnabled: false +# -- Trust root certificate (ECDSA). It must be provided during install. +identityTrustAnchorsPEM: | +# -- Trust domain used for identity +# @default -- clusterDomain +identityTrustDomain: "" +kubeAPI: &kubeapi + # -- Maximum QPS sent to the kube-apiserver before throttling. + # See [token bucket rate limiter + # implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) + clientQPS: 100 + # -- Burst value over clientQPS + clientBurst: 200 +# -- Additional annotations to add to all pods +podAnnotations: {} +# -- Additional labels to add to all pods +podLabels: {} +# -- Labels to apply to all resources +commonLabels: {} +# -- Kubernetes priorityClassName for the Linkerd Pods +priorityClassName: "" +# -- Runtime Class Name for all the pods +runtimeClassName: "" + +# policy controller configuration +policyController: + image: + # -- Docker image for the policy controller + name: cr.l5d.io/linkerd/policy-controller + # -- Pull policy for the policy controller container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the policy controller container image + # @default -- linkerdVersion + version: "" + + # -- Log level for the policy controller + logLevel: info + + # -- The networks from which probes are performed. + # + # By default, all networks are allowed so that all probes are authorized. + probeNetworks: + - 0.0.0.0/0 + - "::/0" + + # -- policy controller resource requests & limits + resources: + cpu: + # -- Maximum amount of CPU units that the policy controller can use + limit: "" + # -- Amount of CPU units that the policy controller requests + request: "" + memory: + # -- Maximum amount of memory that the policy controller can use + limit: "" + # -- Maximum amount of memory that the policy controller requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the policy controller can use + limit: "" + # -- Amount of ephemeral storage that the policy controller requests + request: "" + + livenessProbe: + timeoutSeconds: 1 + readinessProbe: + timeoutSeconds: 1 + +# proxy configuration +proxy: + # -- Enable service profiles for non-Kubernetes services + enableExternalProfiles: false + # -- Maximum time allowed for the proxy to establish an outbound TCP + # connection + outboundConnectTimeout: 1000ms + # -- Maximum time allowed for the proxy to establish an inbound TCP + # connection + inboundConnectTimeout: 100ms + # -- Maximum time allowed before an unused outbound discovery result + # is evicted from the cache + outboundDiscoveryCacheUnusedTimeout: "5s" + # -- Maximum time allowed before an unused inbound discovery result + # is evicted from the cache + inboundDiscoveryCacheUnusedTimeout: "90s" + # -- When set to true, disables the protocol detection timeout on the + # outbound side of the proxy by setting it to a very high value + disableOutboundProtocolDetectTimeout: false + # -- When set to true, disables the protocol detection timeout on the inbound + # side of the proxy by setting it to a very high value + disableInboundProtocolDetectTimeout: false + image: + # -- Docker image for the proxy + name: cr.l5d.io/linkerd/proxy + # -- Pull policy for the proxy container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy container image + # @default -- linkerdVersion + version: "" + # -- Enables the proxy's /shutdown admin endpoint + enableShutdownEndpoint: false + # -- Log level for the proxy + logLevel: warn,linkerd=info,hickory=error + # -- Log format (`plain` or `json`) for the proxy + logFormat: plain + # -- (`off` or `insecure`) If set to `off`, will prevent the proxy from + # logging HTTP headers. If set to `insecure`, HTTP headers may be logged + # verbatim. Note that setting this to `insecure` is not alone sufficient to + # log HTTP headers; the proxy logLevel must also be set to debug. + logHTTPHeaders: "off" + ports: + # -- Admin port for the proxy container + admin: 4191 + # -- Control port for the proxy container + control: 4190 + # -- Inbound port for the proxy container + inbound: 4143 + # -- Outbound port for the proxy container + outbound: 4140 + # -- The `cpu.limit` and `cores` should be kept in sync. The value of `cores` + # must be an integer and should typically be set by rounding up from the + # limit. E.g. if cpu.limit is '1500m', cores should be 2. + cores: 0 + resources: + cpu: + # -- Maximum amount of CPU units that the proxy can use + limit: "" + # -- Amount of CPU units that the proxy requests + request: "" + memory: + # -- Maximum amount of memory that the proxy can use + limit: "" + # -- Maximum amount of memory that the proxy requests + request: "" + ephemeral-storage: + # -- Maximum amount of ephemeral storage that the proxy can use + limit: "" + # -- Amount of ephemeral storage that the proxy requests + request: "" + # -- User id under which the proxy runs + uid: 2102 + # -- (int) Optional customisation of the group id under which the proxy runs (the group ID will be omitted if lower than 0) + gid: -1 + + # -- If set the injected proxy sidecars in the data plane will stay alive for + # at least the given period before receiving the SIGTERM signal from + # Kubernetes but no longer than the pod's `terminationGracePeriodSeconds`. + # See [Lifecycle + # hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) + # for more info on container lifecycle hooks. + waitBeforeExitSeconds: 0 + # -- If set, the application container will not start until the proxy is + # ready + await: true + requireIdentityOnInboundPorts: "" + # -- Default set of opaque ports + # - SMTP (25,587) server-first + # - MYSQL (3306) server-first + # - Galera (4444) server-first + # - PostgreSQL (5432) server-first + # - Redis (6379) server-first + # - ElasticSearch (9300) server-first + # - Memcached (11211) clients do not issue any preamble, which breaks detection + opaquePorts: "25,587,3306,4444,5432,6379,9300,11211" + # -- Grace period for graceful proxy shutdowns. If this timeout elapses before all open connections have completed, the proxy will terminate forcefully, closing any remaining connections. + shutdownGracePeriod: "" + # -- The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", + # "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny", "audit" + # @default -- "all-unauthenticated" + defaultInboundPolicy: "all-unauthenticated" + # -- Enable KEP-753 native sidecars + # This is an experimental feature. It requires Kubernetes >= 1.29. + # If enabled, .proxy.waitBeforeExitSeconds should not be used. + nativeSidecar: false + # -- Native sidecar proxy startup probe parameters. + # -- LivenessProbe timeout and delay configuration + livenessProbe: + initialDelaySeconds: 10 + timeoutSeconds: 1 + # -- ReadinessProbe timeout and delay configuration + readinessProbe: + initialDelaySeconds: 2 + timeoutSeconds: 1 + startupProbe: + initialDelaySeconds: 0 + periodSeconds: 1 + failureThreshold: 120 + # Configures general properties of the proxy's control plane clients. + control: + # Configures limits on API response streams. + streams: + # -- The timeout for the first update from the control plane. + initialTimeout: "3s" + # -- The timeout between consecutive updates from the control plane. + idleTimeout: "5m" + # -- The maximum duration for a response stream (i.e. before it will be + # reinitialized). + lifetime: "1h" + inbound: + server: + http2: + # -- The interval at which PINGs are issued to remote HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on inbound HTTP/2 connections. + keepAliveTimeout: "3s" + outbound: + server: + http2: + # -- The interval at which PINGs are issued to local application HTTP/2 clients. + keepAliveInterval: "10s" + # -- The timeout within which keep-alive PINGs must be acknowledged on outbound HTTP/2 connections. + keepAliveTimeout: "3s" + +# proxy-init configuration +proxyInit: + # -- Variant of iptables that will be used to configure routing. Currently, + # proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will + # control which utility binary will be called. The host must support + # whichever mode will be used + iptablesMode: "legacy" + # -- Default set of inbound ports to skip via iptables + # - Galera (4567,4568) + ignoreInboundPorts: "4567,4568" + # -- Default set of outbound ports to skip via iptables + # - Galera (4567,4568) + ignoreOutboundPorts: "4567,4568" + # -- Default set of ports to skip via iptables for control plane + # components so they can communicate with the Kubernetes API Server + kubeAPIServerPorts: "443,6443" + # -- Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy + skipSubnets: "" + # -- Log level for the proxy-init + # @default -- info + logLevel: "" + # -- Log format (`plain` or `json`) for the proxy-init + # @default -- plain + logFormat: "" + image: + # -- Docker image for the proxy-init container + name: cr.l5d.io/linkerd/proxy-init + # -- Pull policy for the proxy-init container image + # @default -- imagePullPolicy + pullPolicy: "" + # -- Tag for the proxy-init container image + version: v2.4.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.9.1/.helmignore b/charts/linkerd/linkerd-crds/2024.9.1/.helmignore new file mode 100644 index 000000000..79c90a806 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +OWNERS +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/linkerd/linkerd-crds/2024.9.1/Chart.lock b/charts/linkerd/linkerd-crds/2024.9.1/Chart.lock new file mode 100644 index 000000000..a62a03063 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: partials + repository: file://../partials + version: 0.1.0 +digest: sha256:8e42f9c9d4a2dc883f17f94d6044c97518ced19ad0922f47b8760e47135369ba +generated: "2021-08-17T10:42:52.610449255-05:00" diff --git a/charts/linkerd/linkerd-crds/2024.9.1/Chart.yaml b/charts/linkerd/linkerd-crds/2024.9.1/Chart.yaml new file mode 100644 index 000000000..e084abbec --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Linkerd CRDs + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: linkerd-crds +apiVersion: v2 +dependencies: +- name: partials + repository: file://./charts/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.9.1 diff --git a/charts/linkerd/linkerd-crds/2024.9.1/README.md b/charts/linkerd/linkerd-crds/2024.9.1/README.md new file mode 100644 index 000000000..c383a5ab7 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/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.9.1](https://img.shields.io/badge/Version-2024.9.1-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +**Homepage:** + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Adding Linkerd's Helm repository + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the linkerd-crds chart + +This installs the `linkerd-crds` chart, which only persists the CRDs that +Linkerd requires. + +After installing this chart, you need then to install the +`linkerd-control-plane` chart in the same namespace, which provides all the +linkerd core control components. + +```bash +helm install linkerd-crds -n linkerd --create-namespace linkerd/linkerd-crds +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +## Requirements + +Kubernetes: `>=1.22.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../partials | partials | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| enableHttpRoutes | bool | `true` | | + +---------------------------------------------- +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.9.1/README.md.gotmpl b/charts/linkerd/linkerd-crds/2024.9.1/README.md.gotmpl new file mode 100644 index 000000000..88be73954 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/README.md.gotmpl @@ -0,0 +1,59 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +## Quickstart and documentation + +You can run Linkerd on any Kubernetes cluster in a matter of seconds. See the +[Linkerd Getting Started Guide][getting-started] for how. + +For more comprehensive documentation, start with the [Linkerd +docs][linkerd-docs]. + +## Adding Linkerd's Helm repository + +```bash +# To add the repo for Linkerd edge releases: +helm repo add linkerd https://helm.linkerd.io/edge +``` + +## Installing the linkerd-crds chart + +This installs the `linkerd-crds` chart, which only persists the CRDs that +Linkerd requires. + +After installing this chart, you need then to install the +`linkerd-control-plane` chart in the same namespace, which provides all the +linkerd core control components. + +```bash +helm install linkerd-crds -n linkerd --create-namespace linkerd/linkerd-crds +``` + +## Get involved + +* Check out Linkerd's source code at [GitHub][linkerd2]. +* Join Linkerd's [user mailing list][linkerd-users], [developer mailing + list][linkerd-dev], and [announcements mailing list][linkerd-announce]. +* Follow [@linkerd][twitter] on Twitter. +* Join the [Linkerd Slack][slack]. + +[getting-started]: https://linkerd.io/2/getting-started/ +[linkerd2]: https://github.com/linkerd/linkerd2 +[linkerd-announce]: https://lists.cncf.io/g/cncf-linkerd-announce +[linkerd-dev]: https://lists.cncf.io/g/cncf-linkerd-dev +[linkerd-docs]: https://linkerd.io/2/overview/ +[linkerd-users]: https://lists.cncf.io/g/cncf-linkerd-users +[slack]: http://slack.linkerd.io +[twitter]: https://twitter.com/linkerd + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/app-readme.md b/charts/linkerd/linkerd-crds/2024.9.1/app-readme.md new file mode 100644 index 000000000..59010a6b2 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/app-readme.md @@ -0,0 +1,9 @@ +# Linkerd 2 CRDs Chart + +Linkerd is an ultra light, ultra simple, ultra powerful service mesh. Linkerd +adds security, observability, and reliability to Kubernetes, without the +complexity. + +This particular Helm chart only installs Linkerd CRDs. + +Full documentation available at: https://linkerd.io/2/overview/ diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/.helmignore b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/Chart.yaml b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/Chart.yaml new file mode 100644 index 000000000..23cfc167e --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: 'A Helm chart containing Linkerd partial templates, depended by the ''linkerd'' + and ''patch'' charts. ' +name: partials +version: 0.1.0 diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md new file mode 100644 index 000000000..10805c9b9 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md @@ -0,0 +1,9 @@ +# partials + +A Helm chart containing Linkerd partial templates, +depended by the 'linkerd' and 'patch' charts. + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md.gotmpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md.gotmpl new file mode 100644 index 000000000..37f510106 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/README.md.gotmpl @@ -0,0 +1,14 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} +{{ template "chart.appVersionBadge" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/NOTES.txt b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_affinity.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_affinity.tpl new file mode 100644 index 000000000..5dde1da47 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_affinity.tpl @@ -0,0 +1,38 @@ +{{ define "linkerd.pod-affinity" -}} +podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: topology.kubernetes.io/zone + weight: 100 + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: {{ default "linkerd.io/control-plane-component" .label }} + operator: In + values: + - {{ .component }} + topologyKey: kubernetes.io/hostname +{{- end }} + +{{ define "linkerd.node-affinity" -}} +nodeAffinity: +{{- toYaml .Values.nodeAffinity | trim | nindent 2 }} +{{- end }} + +{{ define "linkerd.affinity" -}} +{{- if or .Values.enablePodAntiAffinity .Values.nodeAffinity -}} +affinity: +{{- end }} +{{- if .Values.enablePodAntiAffinity -}} +{{- include "linkerd.pod-affinity" . | nindent 2 }} +{{- end }} +{{- if .Values.nodeAffinity -}} +{{- include "linkerd.node-affinity" . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_capabilities.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_capabilities.tpl new file mode 100644 index 000000000..a595d74c1 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_capabilities.tpl @@ -0,0 +1,16 @@ +{{- define "partials.proxy.capabilities" -}} +capabilities: + {{- if .Values.proxy.capabilities.add }} + add: + {{- toYaml .Values.proxy.capabilities.add | trim | nindent 4 }} + {{- end }} + {{- if .Values.proxy.capabilities.drop }} + drop: + {{- toYaml .Values.proxy.capabilities.drop | trim | nindent 4 }} + {{- end }} +{{- end -}} + +{{- define "partials.proxy-init.capabilities.drop" -}} +drop: +{{ toYaml .Values.proxyInit.capabilities.drop | trim }} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_debug.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_debug.tpl new file mode 100644 index 000000000..4df8cc77b --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_debug.tpl @@ -0,0 +1,15 @@ +{{- define "partials.debug" -}} +image: {{.Values.debugContainer.image.name}}:{{.Values.debugContainer.image.version | default .Values.linkerdVersion}} +imagePullPolicy: {{.Values.debugContainer.image.pullPolicy | default .Values.imagePullPolicy}} +name: linkerd-debug +terminationMessagePolicy: FallbackToLogsOnError +# some environments require probes, so we provide some infallible ones +livenessProbe: + exec: + command: + - "true" +readinessProbe: + exec: + command: + - "true" +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_helpers.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_helpers.tpl new file mode 100644 index 000000000..b6cdc34d0 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_helpers.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Splits a coma separated list into a list of string values. +For example "11,22,55,44" will become "11","22","55","44" +*/}} +{{- define "partials.splitStringList" -}} +{{- if gt (len (toString .)) 0 -}} +{{- $ports := toString . | splitList "," -}} +{{- $last := sub (len $ports) 1 -}} +{{- range $i,$port := $ports -}} +"{{$port}}"{{ternary "," "" (ne $i $last)}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_metadata.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_metadata.tpl new file mode 100644 index 000000000..04d2f1bea --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_metadata.tpl @@ -0,0 +1,17 @@ +{{- define "partials.annotations.created-by" -}} +linkerd.io/created-by: {{ .Values.cliVersion | default (printf "linkerd/helm %s" ( (.Values.image).version | default .Values.linkerdVersion)) }} +{{- end -}} + +{{- define "partials.proxy.annotations" -}} +linkerd.io/proxy-version: {{.Values.proxy.image.version | default .Values.linkerdVersion}} +cluster-autoscaler.kubernetes.io/safe-to-evict: "true" +linkerd.io/trust-root-sha256: {{ .Values.identityTrustAnchorsPEM | sha256sum }} +{{- end -}} + +{{/* +To add labels to the control-plane components, instead update at individual component manifests as +adding here would also update `spec.selector.matchLabels` which are immutable and would fail upgrades. +*/}} +{{- define "partials.proxy.labels" -}} +linkerd.io/proxy-{{.workloadKind}}: {{.component}} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_network-validator.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_network-validator.tpl new file mode 100644 index 000000000..276056395 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_network-validator.tpl @@ -0,0 +1,45 @@ +{{- define "partials.network-validator" -}} +name: linkerd-network-validator +image: {{.Values.proxy.image.name}}:{{.Values.proxy.image.version | default .Values.linkerdVersion }} +imagePullPolicy: {{.Values.proxy.image.pullPolicy | default .Values.imagePullPolicy}} +{{ include "partials.resources" .Values.proxy.resources }} +{{- if or .Values.networkValidator.enableSecurityContext }} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault +{{- end }} +command: + - /usr/lib/linkerd/linkerd2-network-validator +args: + - --log-format + - {{ .Values.networkValidator.logFormat }} + - --log-level + - {{ .Values.networkValidator.logLevel }} + - --connect-addr + {{- if .Values.networkValidator.connectAddr }} + - {{ .Values.networkValidator.connectAddr | quote }} + {{- else if .Values.disableIPv6}} + - "1.1.1.1:20001" + {{- else }} + - "[fd00::1]:20001" + {{- end }} + - --listen-addr + {{- if .Values.networkValidator.listenAddr }} + - {{ .Values.networkValidator.listenAddr | quote }} + {{- else if .Values.disableIPv6}} + - "0.0.0.0:4140" + {{- else }} + - "[::]:4140" + {{- end }} + - --timeout + - {{ .Values.networkValidator.timeout }} + +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_nodeselector.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_nodeselector.tpl new file mode 100644 index 000000000..4cde0ab16 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_nodeselector.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.node-selector" -}} +nodeSelector: +{{- toYaml .Values.nodeSelector | trim | nindent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl new file mode 100644 index 000000000..9651b3bd1 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-config-ann.tpl @@ -0,0 +1,18 @@ +{{- define "partials.proxy.config.annotations" -}} +{{- with .cpu }} +{{- with .request -}} +config.linkerd.io/proxy-cpu-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-cpu-limit: {{. | quote}} +{{- end}} +{{- end}} +{{- with .memory }} +{{- with .request }} +config.linkerd.io/proxy-memory-request: {{. | quote}} +{{end}} +{{- with .limit -}} +config.linkerd.io/proxy-memory-limit: {{. | quote}} +{{- end}} +{{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-init.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy-init.tpl new file mode 100644 index 000000000..a307b1407 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/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.9.1/charts/partials/templates/_proxy.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy.tpl new file mode 100644 index 000000000..7880b394c --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_proxy.tpl @@ -0,0 +1,267 @@ +{{ 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 +{{- /* 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.9.1/charts/partials/templates/_pull-secrets.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_pull-secrets.tpl new file mode 100644 index 000000000..0c9aa4f01 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_pull-secrets.tpl @@ -0,0 +1,6 @@ +{{- define "partials.image-pull-secrets"}} +{{- if . }} +imagePullSecrets: +{{ toYaml . | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_resources.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_resources.tpl new file mode 100644 index 000000000..1fd6789fd --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_resources.tpl @@ -0,0 +1,28 @@ +{{- define "partials.resources" -}} +{{- $ephemeralStorage := index . "ephemeral-storage" -}} +resources: + {{- if or (.cpu).limit (.memory).limit ($ephemeralStorage).limit }} + limits: + {{- with (.cpu).limit }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).limit }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).limit }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} + {{- if or (.cpu).request (.memory).request ($ephemeralStorage).request }} + requests: + {{- with (.cpu).request }} + cpu: {{. | quote}} + {{- end }} + {{- with (.memory).request }} + memory: {{. | quote}} + {{- end }} + {{- with ($ephemeralStorage).request }} + ephemeral-storage: {{. | quote}} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_tolerations.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_tolerations.tpl new file mode 100644 index 000000000..c2292b146 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_tolerations.tpl @@ -0,0 +1,4 @@ +{{- define "linkerd.tolerations" -}} +tolerations: +{{ toYaml .Values.tolerations | trim | indent 2 }} +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_trace.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_trace.tpl new file mode 100644 index 000000000..dee059541 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_trace.tpl @@ -0,0 +1,5 @@ +{{ define "partials.linkerd.trace" -}} +{{ if .Values.controlPlaneTracing -}} +- -trace-collector=collector.{{.Values.controlPlaneTracingNamespace}}.svc.{{.Values.clusterDomain}}:55678 +{{ end -}} +{{- end }} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_validate.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_validate.tpl new file mode 100644 index 000000000..ba772c2fe --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_validate.tpl @@ -0,0 +1,19 @@ +{{- define "linkerd.webhook.validation" -}} + +{{- if and (.injectCaFrom) (.injectCaFromSecret) -}} +{{- fail "injectCaFrom and injectCaFromSecret cannot both be set" -}} +{{- end -}} + +{{- if and (or (.injectCaFrom) (.injectCaFromSecret)) (.caBundle) -}} +{{- fail "injectCaFrom or injectCaFromSecret cannot be set if providing a caBundle" -}} +{{- end -}} + +{{- if and (.externalSecret) (empty .caBundle) (empty .injectCaFrom) (empty .injectCaFromSecret) -}} +{{- fail "if externalSecret is set, then caBundle, injectCaFrom, or injectCaFromSecret must be set" -}} +{{- end }} + +{{- if and (or .injectCaFrom .injectCaFromSecret .caBundle) (not .externalSecret) -}} +{{- fail "if caBundle, injectCaFrom, or injectCaFromSecret is set, then externalSecret must be set" -}} +{{- end -}} + +{{- end -}} diff --git a/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_volumes.tpl b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/templates/_volumes.tpl new file mode 100644 index 000000000..9684cf240 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/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.9.1/charts/partials/values.yaml b/charts/linkerd/linkerd-crds/2024.9.1/charts/partials/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/NOTES.txt b/charts/linkerd/linkerd-crds/2024.9.1/templates/NOTES.txt new file mode 100644 index 000000000..4ff5c1818 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/NOTES.txt @@ -0,0 +1,6 @@ +The linkerd-crds chart was successfully installed 🎉 + +To complete the linkerd core installation, please now proceed to install the +linkerd-control-plane chart in the {{ .Release.Namespace }} namespace. + +Looking for more? Visit https://linkerd.io/2/getting-started/ diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_grpcroutes.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_grpcroutes.yaml new file mode 100644 index 000000000..0050aac88 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_grpcroutes.yaml @@ -0,0 +1,1507 @@ +{{- if .Values.enableHttpRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + creationTimestamp: null + name: grpcroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GRPCRoute provides a way to route gRPC requests. This includes + the capability to match requests by hostname, gRPC service, gRPC method, + or HTTP/2 header. Filters can be used to specify additional processing steps. + Backends specify where matching requests will be routed. \n GRPCRoute falls + under extended support within the Gateway API. Within the following specification, + the word \"MUST\" indicates that an implementation supporting GRPCRoute + must conform to the indicated requirement, but an implementation not supporting + this route type need not follow the requirement unless explicitly indicated. + \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` + MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, + i.e. via ALPN. If the implementation does not support this, then it MUST + set the \"Accepted\" condition to \"False\" for the affected listener with + a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 + connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` + with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, + https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade + from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). + If the implementation does not support this, then it MUST set the \"Accepted\" + condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". + Implementations MAY also accept HTTP/2 connections with an upgrade from + HTTP/1, i.e. without prior knowledge. \n Support: Extended" + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GRPCRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostnames to match against + the GRPC Host header to select a GRPCRoute to process the request. + This matches the RFC 1123 definition of a hostname with 2 notable + exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed + with a wildcard label (`*.`). The wildcard label MUST appear by + itself as the first label. \n If a hostname is specified by both + the Listener and GRPCRoute, there MUST be at least one intersecting + hostname for the GRPCRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + GRPCRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches GRPCRoutes + that have either not specified any hostnames or have specified at + least one hostname that matches the Listener hostname. For example, + `test.example.com` and `*.example.com` would both match. On the + other hand, `example.com` and `test.example.net` would not match. + \n Hostnames that are prefixed with a wildcard label (`*.`) are + interpreted as a suffix match. That means that a match for `*.example.com` + would match both `test.example.com`, and `foo.test.example.com`, + but not `example.com`. \n If both the Listener and GRPCRoute have + specified hostnames, any GRPCRoute hostnames that do not match the + Listener hostname MUST be ignored. For example, if a Listener specified + `*.example.com`, and the GRPCRoute specified `test.example.com` + and `test.example.net`, `test.example.net` MUST NOT be considered + for a match. \n If both the Listener and GRPCRoute have specified + hostnames, and none match with the criteria above, then the GRPCRoute + MUST NOT be accepted by the implementation. The implementation MUST + raise an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute + is attached to a Listener and that listener already has another + Route (B) of the other type attached and the intersection of the + hostnames of A and B is non-empty, then the implementation MUST + accept exactly one of these two routes, determined by the following + criteria, in order: \n * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by \"{namespace}/{name}\". + \n The rejected Route MUST raise an 'Accepted' condition with a + status of 'False' in the corresponding RouteParentStatus. \n Support: + Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged. \n Note that for ParentRefs that cross namespace + boundaries, there are specific rules. Cross-namespace references + are only valid if they are explicitly allowed by something in the + namespace they are referring to. For example, Gateway has the AllowedRoutes + field, and ReferenceGrant provides a generic way to enable any other + kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the core + API group (such as for a \"Service\" kind referent), Group + must be explicitly set to \"\" (empty string). \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified, this refers to the local namespace of the Route. + \n Note that there are specific rules for ParentRefs which + cross namespace boundaries. Cross-namespace references are + only valid if they are explicitly allowed by something in + the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It + can be interpreted differently based on the type of parent + resource. \n When the parent resource is a Gateway, this targets + all listeners listening on the specified port that also support + this kind of Route(and select this Route). It's not recommended + to set `Port` unless the networking behaviors specified in + a Route must apply to a specific port as opposed to a listener(s) + whose port(s) may be changed. When both Port and SectionName + are specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY choose + to support other parent resources. Implementations supporting + other types of parent resources MUST clearly document how/if + Port is interpreted. \n For the purpose of status, an attachment + is considered successful as long as the parent resource accepts + it partially. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - method: + type: Exact + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: GRPCRouteRule defines the semantics for matching a + gRPC request based on conditions (matches), processing it (filters), + and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If *all* entries in BackendRefs are invalid, and there + are also no filters specified in this route rule, *all* traffic + which matches this rule MUST receive an `UNAVAILABLE` status. + \n See the GRPCBackendRef definition for the rules about what + makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef + is invalid, `UNAVAILABLE` statuses MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive an `UNAVAILABLE` + status. \n For example, if two backends are specified with + equal weights, and one is invalid, 50 percent of traffic MUST + receive an `UNAVAILABLE` status. Implementations may choose + how that 50 percent is determined. \n Support: Core for Kubernetes + Service \n Support: Implementation-specific for any other + resource \n Support for weight: Core" + items: + description: GRPCBackendRef defines how a GRPCRoute forwards + a gRPC request. + properties: + filters: + description: "Filters defined at this level MUST be executed + if and only if the request is being forwarded to the + backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in GRPCRouteRule.)" + items: + description: GRPCRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. GRPCRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API + group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for + a filter that mirrors requests. Requests are sent + to the specified destination, but responses from + that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource + where mirrored requests are sent. \n If the + referent cannot be found, this BackendRef + is invalid and must be dropped from the Gateway. + The controller must ensure the \"ResolvedRefs\" + condition on the Route status is set to `status: + False` and not configure this backend in the + underlying implementation. \n If there is + a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: + False`, with the \"RefNotPermitted\" reason + and not configure this backend in the underlying + implementation. \n In either error case, the + Message of the `ResolvedRefs` Condition should + be used to provide more detail about the problem. + \n Support: Extended for Kubernetes Service + \n Support: Implementation-specific for any + other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core + API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to + CNAME DNS records that may live outside + of the cluster and as such are difficult + to reason about in terms of conformance. + They also may not be safe to forward to + (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with + a type other than ExternalName) \n Support: + Implementation-specific (Services with + type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace + of the backend. When unspecified, the + local namespace is inferred. \n Note that + when a namespace different than the local + namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant + documentation for details. \n Support: + Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination + port number to use for this resource. + Port is required when the referent is + a Kubernetes Service. In this case, the + port number is the service port number, + not the target port. For other resources, + destination port might be derived from + the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + supporting GRPCRoute MUST support core filters. + \n - Extended: Filter types and their corresponding + configuration defined by \"Support: Extended\" + in this package, e.g. \"RequestMirror\". Implementers + are encouraged to support extended filters. \n + - Implementation-specific: Filters that are defined + and supported by specific vendors. In the future, + filters showing convergence in behavior across + multiple implementations will be considered for + inclusion in extended or core conformance levels. + Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` + MUST be set to \"ExtensionRef\" for custom filters. + \n Implementers are encouraged to define custom + implementation types to extend the core API with + implementation-specific behavior. \n If a reference + to a custom filter type cannot be resolved, the + filter MUST NOT be skipped. Instead, requests + that would have been processed by that filter + MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n ExternalName services + can refer to CNAME DNS records that may live outside + of the cluster and as such are difficult to reason about + in terms of conformance. They also may not be safe to + forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName Services. + \n Support: Core (Services with a type other than ExternalName) + \n Support: Implementation-specific (Services with type + ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace different than the local + namespace is specified, a ReferenceGrant object is required + in the referent namespace to allow that namespace's + owner to accept the reference. See the ReferenceGrant + documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number + to use for this resource. Port is required when the + referent is a Kubernetes Service. In this case, the + port number is the service port number, not the target + port. For other resources, destination port might be + derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support for this field varies based + on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations that support GRPCRoute. - Implementers + are encouraged to support extended filters. - Implementation-specific + custom filters have no API guarantees across implementations. + \n Specifying a core filter multiple times has unspecified + or implementation-specific conformance. Support: Core" + items: + description: GRPCRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + GRPCRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For + example, "gateway.networking.k8s.io". When unspecified + or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter + that mirrors requests. Requests are sent to the specified + destination, but responses from that destination are + ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where + mirrored requests are sent. \n If the referent cannot + be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure + the \"ResolvedRefs\" condition on the Route status + is set to `status: False` and not configure this + backend in the underlying implementation. \n If + there is a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: False`, + with the \"RefNotPermitted\" reason and not configure + this backend in the underlying implementation. \n + In either error case, the Message of the `ResolvedRefs` + Condition should be used to provide more detail + about the problem. \n Support: Extended for Kubernetes + Service \n Support: Implementation-specific for + any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". When + unspecified or empty string, core API group + is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to CNAME + DNS records that may live outside of the cluster + and as such are difficult to reason about in + terms of conformance. They also may not be safe + to forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with a + type other than ExternalName) \n Support: Implementation-specific + (Services with type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the + backend. When unspecified, the local namespace + is inferred. \n Note that when a namespace different + than the local namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept the + reference. See the ReferenceGrant documentation + for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port + number to use for this resource. Port is required + when the referent is a Kubernetes Service. In + this case, the port number is the service port + number, not the target port. For other resources, + destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n Support: + Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations supporting GRPCRoute MUST support + core filters. \n - Extended: Filter types and their + corresponding configuration defined by \"Support: Extended\" + in this package, e.g. \"RequestMirror\". Implementers + are encouraged to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior + across multiple implementations will be considered for + inclusion in extended or core conformance levels. Filter-specific + configuration for such filters is specified using the + ExtensionRef field. `Type` MUST be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged to + define custom implementation types to extend the core + API with implementation-specific behavior. \n If a reference + to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have + been processed by that filter MUST receive a HTTP error + response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: "Matches define conditions used for matching the + rule against incoming gRPC requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - method: service: foo.bar headers: values: + version: 2 - method: service: foo.bar.v2 ``` \n For a request + to match against this rule, it MUST satisfy EITHER of the + two conditions: \n - service of foo.bar AND contains the header + `version: 2` - service of foo.bar.v2 \n See the documentation + for GRPCRouteMatch on how to specify multiple match conditions + to be ANDed together. \n If no matches are specified, the + implementation MUST match every gRPC request. \n Proxy or + Load Balancer routing configuration generated from GRPCRoutes + MUST prioritize rules based on the following criteria, continuing + on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. + Precedence MUST be given to the rule with the largest number + of: \n * Characters in a matching non-wildcard hostname. * + Characters in a matching hostname. * Characters in a matching + service. * Characters in a matching method. * Header matches. + \n If ties still exist across multiple Routes, matching precedence + MUST be determined in order of the following criteria, continuing + on ties: \n * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by \"{namespace}/{name}\". + \n If ties still exist within the Route that has been given + precedence, matching precedence MUST be granted to the first + matching rule meeting the above criteria." + items: + description: "GRPCRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a gRPC request only if its service is `foo` + AND it contains the `version: v1` header: \n ``` matches: + - method: type: Exact service: \"foo\" headers: - name: + \"version\" value \"v1\" \n ```" + properties: + headers: + description: Headers specifies gRPC request header matchers. + Multiple match values are ANDed together, meaning, a + request MUST match all the specified headers to select + the route. + items: + description: GRPCHeaderMatch describes how to select + a gRPC route by matching gRPC request headers. + properties: + name: + description: "Name is the name of the gRPC Header + to be matched. \n If multiple entries specify + equivalent header names, only the first entry + with an equivalent name MUST be considered for + a match. Subsequent entries with an equivalent + header name MUST be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: Method specifies a gRPC request service/method + matcher. If this field is not specified, all services + and methods will match. + properties: + method: + description: "Value of the method to match against. + If left empty or omitted, will match all services. + \n At least one of Service and Method MUST be a + non-empty string." + maxLength: 1024 + type: string + service: + description: "Value of the service to match against. + If left empty or omitted, will match any service. + \n At least one of Service and Method MUST be a + non-empty string." + maxLength: 1024 + type: string + type: + default: Exact + description: "Type specifies how to match against + the service and/or method. Support: Core (Exact + with service and method specified) \n Support: Implementation-specific + (Exact with method specified but no service specified) + \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - RegularExpression + type: string + type: object + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, \n type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: \"Available\", \"Progressing\", + and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields + }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the + core API group (such as for a \"Service\" kind referent), + Group must be explicitly set to \"\" (empty string). \n + Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) \n Support: Implementation-specific (Other + Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified, this refers to the local namespace of + the Route. \n Note that there are specific rules for ParentRefs + which cross namespace boundaries. Cross-namespace references + are only valid if they are explicitly allowed by something + in the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides + a generic way to enable any other kind of cross-namespace + reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +{{- end }} + diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_httproutes.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_httproutes.yaml new file mode 100644 index 000000000..b695c51d5 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/gateway.networking.k8s.io_httproutes.yaml @@ -0,0 +1,3881 @@ +{{- if .Values.enableHttpRoutes }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1923 + gateway.networking.k8s.io/bundle-version: v0.7.1 + gateway.networking.k8s.io/channel: experimental + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and + will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute used to process + the request. Implementations MUST ignore any port value specified + in the HTTP Host header while performing a match. \n Valid values + for Hostnames are determined by RFC 1123 definition of a hostname + with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at + least one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n In the event that multiple HTTPRoutes specify + intersecting hostnames (e.g. overlapping wildcard matching and exact + matching hostnames), precedence must be given to rules from the + HTTPRoute with the largest number of: \n * Characters in a matching + non-wildcard hostname. * Characters in a matching hostname. \n If + ties exist across multiple Routes, the matching precedence rules + for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged. \n Note that for ParentRefs that cross namespace + boundaries, there are specific rules. Cross-namespace references + are only valid if they are explicitly allowed by something in the + namespace they are referring to. For example, Gateway has the AllowedRoutes + field, and ReferenceGrant provides a generic way to enable any other + kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the core + API group (such as for a \"Service\" kind referent), Group + must be explicitly set to \"\" (empty string). \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified, this refers to the local namespace of the Route. + \n Note that there are specific rules for ParentRefs which + cross namespace boundaries. Cross-namespace references are + only valid if they are explicitly allowed by something in + the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It + can be interpreted differently based on the type of parent + resource. \n When the parent resource is a Gateway, this targets + all listeners listening on the specified port that also support + this kind of Route(and select this Route). It's not recommended + to set `Port` unless the networking behaviors specified in + a Route must apply to a specific port as opposed to a listener(s) + whose port(s) may be changed. When both Port and SectionName + are specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY choose + to support other parent resources. Implementations supporting + other types of parent resources MUST clearly document how/if + Port is interpreted. \n For the purpose of status, an attachment + is considered successful as long as the parent resource accepts + it partially. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches), processing it (filters), + and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If *all* entries in BackendRefs are invalid, and there + are also no filters specified in this route rule, *all* traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined. + \n Support: Core for Kubernetes Service \n Support: Extended + for Kubernetes ServiceImport \n Support: Implementation-specific + for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should + forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API + group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for + a filter that mirrors requests. Requests are sent + to the specified destination, but responses from + that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource + where mirrored requests are sent. \n If the + referent cannot be found, this BackendRef + is invalid and must be dropped from the Gateway. + The controller must ensure the \"ResolvedRefs\" + condition on the Route status is set to `status: + False` and not configure this backend in the + underlying implementation. \n If there is + a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: + False`, with the \"RefNotPermitted\" reason + and not configure this backend in the underlying + implementation. \n In either error case, the + Message of the `ResolvedRefs` Condition should + be used to provide more detail about the problem. + \n Support: Extended for Kubernetes Service + \n Support: Implementation-specific for any + other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core + API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to + CNAME DNS records that may live outside + of the cluster and as such are difficult + to reason about in terms of conformance. + They also may not be safe to forward to + (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with + a type other than ExternalName) \n Support: + Implementation-specific (Services with + type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace + of the backend. When unspecified, the + local namespace is inferred. \n Note that + when a namespace different than the local + namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant + documentation for details. \n Support: + Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination + port number to use for this resource. + Port is required when the referent is + a Kubernetes Service. In this case, the + port number is the service port number, + not the target port. For other resources, + destination port might be derived from + the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" would + be modified to \"/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a + filter that modifies a request during forwarding. + \n Support: Extended" + properties: + hostname: + description: "Hostname is the value to be used + to replace the Host header value during forwarding. + \n Support: Extended" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n + Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" would + be modified to \"/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n ExternalName services + can refer to CNAME DNS records that may live outside + of the cluster and as such are difficult to reason about + in terms of conformance. They also may not be safe to + forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName Services. + \n Support: Core (Services with a type other than ExternalName) + \n Support: Implementation-specific (Services with type + ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace different than the local + namespace is specified, a ReferenceGrant object is required + in the referent namespace to allow that namespace's + owner to accept the reference. See the ReferenceGrant + documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number + to use for this resource. Port is required when the + referent is a Kubernetes Service. In this case, the + port number is the service port number, not the target + port. For other resources, destination port might be + derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support for this field varies based + on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or implementation-specific + conformance. \n All filters are expected to be compatible + with each other except for the URLRewrite and RequestRedirect + filters, which may not be combined. If an implementation can + not support other combinations of filters, they must clearly + document that limitation. In all cases where incompatible + or unsupported filters are specified, implementations MUST + add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For + example, "gateway.networking.k8s.io". When unspecified + or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter + that mirrors requests. Requests are sent to the specified + destination, but responses from that destination are + ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where + mirrored requests are sent. \n If the referent cannot + be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure + the \"ResolvedRefs\" condition on the Route status + is set to `status: False` and not configure this + backend in the underlying implementation. \n If + there is a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: False`, + with the \"RefNotPermitted\" reason and not configure + this backend in the underlying implementation. \n + In either error case, the Message of the `ResolvedRefs` + Condition should be used to provide more detail + about the problem. \n Support: Extended for Kubernetes + Service \n Support: Implementation-specific for + any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". When + unspecified or empty string, core API group + is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to CNAME + DNS records that may live outside of the cluster + and as such are difficult to reason about in + terms of conformance. They also may not be safe + to forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with a + type other than ExternalName) \n Support: Implementation-specific + (Services with type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the + backend. When unspecified, the local namespace + is inferred. \n Note that when a namespace different + than the local namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept the + reference. See the ReferenceGrant documentation + for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port + number to use for this resource. Port is required + when the referent is a Kubernetes Service. In + this case, the port number is the service port + number, not the target port. For other resources, + destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname in the `Host` header of + the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify + the path of the incoming request. The modified path + is then used to construct the `Location` header. + When empty, the request path is used as-is. \n Support: + Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the value + with which to replace the full path of a request + during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the + value with which to replace the prefix match + of a request during a rewrite or redirect. For + example, a request to \"/foo/bar\" with a prefix + match of \"/foo\" would be modified to \"/bar\". + \n Note that this matches the behavior of the + PathPrefix match type. This matches full path + elements. A path element refers to the list + of labels in the path split by the `/` separator. + When specified, a trailing `/` is ignored. For + example, the paths `/abc`, `/abc/`, and `/abc/def` + would all match the prefix `/abc`, but the path + `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. + Additional types may be added in a future release + of the API. \n Note that values may be added + to this enum, implementations must ensure that + unknown values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the Route + to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. \n If + no port is specified, the redirect port MUST be + derived using the following rules: \n * If redirect + scheme is not-empty, the redirect port MUST be the + well-known port associated with the redirect scheme. + Specifically \"http\" to port 80 and \"https\" to + port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway + SHOULD be used. * If redirect scheme is empty, the + redirect port MUST be the Gateway Listener port. + \n Implementations SHOULD NOT add the port number + in the 'Location' header in the following cases: + \n * A Location header that will use HTTP (whether + that is determined via the Listener protocol or + the Scheme field) _and_ use port 80. * A Location + header that will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) _and_ + use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Scheme redirects can affect the port of the redirect, + for more information, refer to the documentation + for the port field of this filter. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause a + crash. \n Unknown values here must result in the + implementation setting the Accepted Condition for + the Route to `status: False`, with a Reason of `UnsupportedValue`. + \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. \n Unknown + values here must result in the implementation setting + the Accepted Condition for the Route to `status: + False`, with a Reason of `UnsupportedValue`. \n + Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n Support: + Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations must support core filters. \n - + Extended: Filter types and their corresponding configuration + defined by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged to support + extended filters. \n - Implementation-specific: Filters + that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior + across multiple implementations will be considered for + inclusion in extended or core conformance levels. Filter-specific + configuration for such filters is specified using the + ExtensionRef field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged to + define custom implementation types to extend the core + API with implementation-specific behavior. \n If a reference + to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have + been processed by that filter MUST receive a HTTP error + response. \n Note that values may be added to this enum, + implementations must ensure that unknown values will + not cause a crash. \n Unknown values here must result + in the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter + that modifies a request during forwarding. \n Support: + Extended" + properties: + hostname: + description: "Hostname is the value to be used to + replace the Host header value during forwarding. + \n Support: Extended" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: + Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the value + with which to replace the full path of a request + during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the + value with which to replace the prefix match + of a request during a rewrite or redirect. For + example, a request to \"/foo/bar\" with a prefix + match of \"/foo\" would be modified to \"/bar\". + \n Note that this matches the behavior of the + PathPrefix match type. This matches full path + elements. A path element refers to the list + of labels in the path split by the `/` separator. + When specified, a trailing `/` is ignored. For + example, the paths `/abc`, `/abc/`, and `/abc/def` + would all match the prefix `/abc`, but the path + `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. + Additional types may be added in a future release + of the API. \n Note that values may be added + to this enum, implementations must ensure that + unknown values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the Route + to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" + value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request + to match against this rule, a request must satisfy EITHER + of the two conditions: \n - path prefixed with `/foo` AND + contains the header `version: v2` - path prefix of `/v2/foo` + \n See the documentation for HTTPRouteMatch on how to specify + multiple match conditions that should be ANDed together. \n + If no matches are specified, the default is a prefix path + match on \"/\", which has the effect of matching every HTTP + request. \n Proxy or Load Balancer routing configuration generated + from HTTPRoutes MUST prioritize matches based on the following + criteria, continuing on ties. Across all rules specified on + applicable Routes, precedence must be given to the match having: + \n * \"Exact\" path match. * \"Prefix\" path match with largest + number of characters. * Method match. * Largest number of + header matches. * Largest number of query param matches. \n + Note: The precedence of RegularExpression path matches are + implementation-specific. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within an HTTPRoute, matching precedence MUST + be granted to the FIRST matching rule (in list order) with + a match meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: \n path: value: \"/foo\" headers: - name: \"version\" + value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Implementation-specific (RegularExpression) + \n Since RegularExpression HeaderMatchType has + implementation-specific conformance, implementations + can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's + documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n If multiple entries specify equivalent query + param names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST + be ignored. \n If a query param is repeated in + an HTTP request, the behavior is purposely left + undefined, since different data planes have different + capabilities. However, it is *recommended* that + implementations should match against the first + value of the param if the data plane supports + it, as this behavior is expected in other load + balancing contexts outside of the Gateway API. + \n Users SHOULD NOT route traffic based on repeated + query params to guard themselves against potential + differences in the implementations." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Implementation-specific + (RegularExpression) \n Since RegularExpression + QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, + PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, \n type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: \"Available\", \"Progressing\", + and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields + }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the + core API group (such as for a \"Service\" kind referent), + Group must be explicitly set to \"\" (empty string). \n + Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) \n Support: Implementation-specific (Other + Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified, this refers to the local namespace of + the Route. \n Note that there are specific rules for ParentRefs + which cross namespace boundaries. Cross-namespace references + are only valid if they are explicitly allowed by something + in the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides + a generic way to enable any other kind of cross-namespace + reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute used to process + the request. Implementations MUST ignore any port value specified + in the HTTP Host header while performing a match. \n Valid values + for Hostnames are determined by RFC 1123 definition of a hostname + with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at + least one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n In the event that multiple HTTPRoutes specify + intersecting hostnames (e.g. overlapping wildcard matching and exact + matching hostnames), precedence must be given to rules from the + HTTPRoute with the largest number of: \n * Characters in a matching + non-wildcard hostname. * Characters in a matching hostname. \n If + ties exist across multiple Routes, the matching precedence rules + for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged. \n Note that for ParentRefs that cross namespace + boundaries, there are specific rules. Cross-namespace references + are only valid if they are explicitly allowed by something in the + namespace they are referring to. For example, Gateway has the AllowedRoutes + field, and ReferenceGrant provides a generic way to enable any other + kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the core + API group (such as for a \"Service\" kind referent), Group + must be explicitly set to \"\" (empty string). \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified, this refers to the local namespace of the Route. + \n Note that there are specific rules for ParentRefs which + cross namespace boundaries. Cross-namespace references are + only valid if they are explicitly allowed by something in + the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It + can be interpreted differently based on the type of parent + resource. \n When the parent resource is a Gateway, this targets + all listeners listening on the specified port that also support + this kind of Route(and select this Route). It's not recommended + to set `Port` unless the networking behaviors specified in + a Route must apply to a specific port as opposed to a listener(s) + whose port(s) may be changed. When both Port and SectionName + are specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY choose + to support other parent resources. Implementations supporting + other types of parent resources MUST clearly document how/if + Port is interpreted. \n For the purpose of status, an attachment + is considered successful as long as the parent resource accepts + it partially. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches), processing it (filters), + and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If *all* entries in BackendRefs are invalid, and there + are also no filters specified in this route rule, *all* traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined. + \n Support: Core for Kubernetes Service \n Support: Extended + for Kubernetes ServiceImport \n Support: Implementation-specific + for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should + forward an HTTP request. + properties: + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API + group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for + a filter that mirrors requests. Requests are sent + to the specified destination, but responses from + that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource + where mirrored requests are sent. \n If the + referent cannot be found, this BackendRef + is invalid and must be dropped from the Gateway. + The controller must ensure the \"ResolvedRefs\" + condition on the Route status is set to `status: + False` and not configure this backend in the + underlying implementation. \n If there is + a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: + False`, with the \"RefNotPermitted\" reason + and not configure this backend in the underlying + implementation. \n In either error case, the + Message of the `ResolvedRefs` Condition should + be used to provide more detail about the problem. + \n Support: Extended for Kubernetes Service + \n Support: Implementation-specific for any + other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". + When unspecified or empty string, core + API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to + CNAME DNS records that may live outside + of the cluster and as such are difficult + to reason about in terms of conformance. + They also may not be safe to forward to + (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with + a type other than ExternalName) \n Support: + Implementation-specific (Services with + type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace + of the backend. When unspecified, the + local namespace is inferred. \n Note that + when a namespace different than the local + namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferenceGrant + documentation for details. \n Support: + Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination + port number to use for this resource. + Port is required when the referent is + a Kubernetes Service. In this case, the + port number is the service port number, + not the target port. For other resources, + destination port might be derived from + the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" would + be modified to \"/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a + filter that modifies a request during forwarding. + \n Support: Extended" + properties: + hostname: + description: "Hostname is the value to be used + to replace the Host header value during forwarding. + \n Support: Extended" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n + Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" would + be modified to \"/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n ExternalName services + can refer to CNAME DNS records that may live outside + of the cluster and as such are difficult to reason about + in terms of conformance. They also may not be safe to + forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName Services. + \n Support: Core (Services with a type other than ExternalName) + \n Support: Implementation-specific (Services with type + ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace different than the local + namespace is specified, a ReferenceGrant object is required + in the referent namespace to allow that namespace's + owner to accept the reference. See the ReferenceGrant + documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number + to use for this resource. Port is required when the + referent is a Kubernetes Service. In this case, the + port number is the service port number, not the target + port. For other resources, destination port might be + derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support for this field varies based + on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or implementation-specific + conformance. \n All filters are expected to be compatible + with each other except for the URLRewrite and RequestRedirect + filters, which may not be combined. If an implementation can + not support other combinations of filters, they must clearly + document that limitation. In all cases where incompatible + or unsupported filters are specified, implementations MUST + add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.example.net\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For + example, "gateway.networking.k8s.io". When unspecified + or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter + that mirrors requests. Requests are sent to the specified + destination, but responses from that destination are + ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where + mirrored requests are sent. \n If the referent cannot + be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure + the \"ResolvedRefs\" condition on the Route status + is set to `status: False` and not configure this + backend in the underlying implementation. \n If + there is a cross-namespace reference to an *existing* + object that is not allowed by a ReferenceGrant, + the controller must ensure the \"ResolvedRefs\" + \ condition on the Route is set to `status: False`, + with the \"RefNotPermitted\" reason and not configure + this backend in the underlying implementation. \n + In either error case, the Message of the `ResolvedRefs` + Condition should be used to provide more detail + about the problem. \n Support: Extended for Kubernetes + Service \n Support: Implementation-specific for + any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. + For example, "gateway.networking.k8s.io". When + unspecified or empty string, core API group + is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: "Kind is the Kubernetes resource + kind of the referent. For example \"Service\". + \n Defaults to \"Service\" when not specified. + \n ExternalName services can refer to CNAME + DNS records that may live outside of the cluster + and as such are difficult to reason about in + terms of conformance. They also may not be safe + to forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName + Services. \n Support: Core (Services with a + type other than ExternalName) \n Support: Implementation-specific + (Services with type ExternalName)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the + backend. When unspecified, the local namespace + is inferred. \n Note that when a namespace different + than the local namespace is specified, a ReferenceGrant + object is required in the referent namespace + to allow that namespace's owner to accept the + reference. See the ReferenceGrant documentation + for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port + number to use for this resource. Port is required + when the referent is a Kubernetes Service. In + this case, the port number is the service port + number, not the target port. For other resources, + destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname in the `Host` header of + the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify + the path of the incoming request. The modified path + is then used to construct the `Location` header. + When empty, the request path is used as-is. \n Support: + Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the value + with which to replace the full path of a request + during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the + value with which to replace the prefix match + of a request during a rewrite or redirect. For + example, a request to \"/foo/bar\" with a prefix + match of \"/foo\" would be modified to \"/bar\". + \n Note that this matches the behavior of the + PathPrefix match type. This matches full path + elements. A path element refers to the list + of labels in the path split by the `/` separator. + When specified, a trailing `/` is ignored. For + example, the paths `/abc`, `/abc/`, and `/abc/def` + would all match the prefix `/abc`, but the path + `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. + Additional types may be added in a future release + of the API. \n Note that values may be added + to this enum, implementations must ensure that + unknown values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the Route + to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. \n If + no port is specified, the redirect port MUST be + derived using the following rules: \n * If redirect + scheme is not-empty, the redirect port MUST be the + well-known port associated with the redirect scheme. + Specifically \"http\" to port 80 and \"https\" to + port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway + SHOULD be used. * If redirect scheme is empty, the + redirect port MUST be the Gateway Listener port. + \n Implementations SHOULD NOT add the port number + in the 'Location' header in the following cases: + \n * A Location header that will use HTTP (whether + that is determined via the Listener protocol or + the Scheme field) _and_ use port 80. * A Location + header that will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) _and_ + use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Scheme redirects can affect the port of the redirect, + for more information, refer to the documentation + for the port field of this filter. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause a + crash. \n Unknown values here must result in the + implementation setting the Accepted Condition for + the Route to `status: False`, with a Reason of `UnsupportedValue`. + \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. \n Unknown + values here must result in the implementation setting + the Accepted Condition for the Route to `status: + False`, with a Reason of `UnsupportedValue`. \n + Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n Support: + Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: foo + \n Config: add: - name: \"my-header\" value: \"bar,baz\" + \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + set: - name: \"my-header\" value: \"bar\" \n Output: + GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations must support core filters. \n - + Extended: Filter types and their corresponding configuration + defined by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged to support + extended filters. \n - Implementation-specific: Filters + that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior + across multiple implementations will be considered for + inclusion in extended or core conformance levels. Filter-specific + configuration for such filters is specified using the + ExtensionRef field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged to + define custom implementation types to extend the core + API with implementation-specific behavior. \n If a reference + to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have + been processed by that filter MUST receive a HTTP error + response. \n Note that values may be added to this enum, + implementations must ensure that unknown values will + not cause a crash. \n Unknown values here must result + in the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter + that modifies a request during forwarding. \n Support: + Extended" + properties: + hostname: + description: "Hostname is the value to be used to + replace the Host header value during forwarding. + \n Support: Extended" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: + Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the value + with which to replace the full path of a request + during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the + value with which to replace the prefix match + of a request during a rewrite or redirect. For + example, a request to \"/foo/bar\" with a prefix + match of \"/foo\" would be modified to \"/bar\". + \n Note that this matches the behavior of the + PathPrefix match type. This matches full path + elements. A path element refers to the list + of labels in the path split by the `/` separator. + When specified, a trailing `/` is ignored. For + example, the paths `/abc`, `/abc/`, and `/abc/def` + would all match the prefix `/abc`, but the path + `/abcd` would not." + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. + Additional types may be added in a future release + of the API. \n Note that values may be added + to this enum, implementations must ensure that + unknown values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the Route + to `status: False`, with a Reason of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\" + value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request + to match against this rule, a request must satisfy EITHER + of the two conditions: \n - path prefixed with `/foo` AND + contains the header `version: v2` - path prefix of `/v2/foo` + \n See the documentation for HTTPRouteMatch on how to specify + multiple match conditions that should be ANDed together. \n + If no matches are specified, the default is a prefix path + match on \"/\", which has the effect of matching every HTTP + request. \n Proxy or Load Balancer routing configuration generated + from HTTPRoutes MUST prioritize matches based on the following + criteria, continuing on ties. Across all rules specified on + applicable Routes, precedence must be given to the match having: + \n * \"Exact\" path match. * \"Prefix\" path match with largest + number of characters. * Method match. * Largest number of + header matches. * Largest number of query param matches. \n + Note: The precedence of RegularExpression path matches are + implementation-specific. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within an HTTPRoute, matching precedence MUST + be granted to the FIRST matching rule (in list order) with + a match meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: \n path: value: \"/foo\" headers: - name: \"version\" + value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Implementation-specific (RegularExpression) + \n Since RegularExpression HeaderMatchType has + implementation-specific conformance, implementations + can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's + documentation to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n If multiple entries specify equivalent query + param names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST + be ignored. \n If a query param is repeated in + an HTTP request, the behavior is purposely left + undefined, since different data planes have different + capabilities. However, it is *recommended* that + implementations should match against the first + value of the param if the data plane supports + it, as this behavior is expected in other load + balancing contexts outside of the Gateway API. + \n Users SHOULD NOT route traffic based on repeated + query params to guard themselves against potential + differences in the implementations." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Implementation-specific + (RegularExpression) \n Since RegularExpression + QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, + PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, \n type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: \"Available\", \"Progressing\", + and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields + }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, + \"gateway.networking.k8s.io\" is inferred. To set the + core API group (such as for a \"Service\" kind referent), + Group must be explicitly set to \"\" (empty string). \n + Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) \n Support: Implementation-specific (Other + Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified, this refers to the local namespace of + the Route. \n Note that there are specific rules for ParentRefs + which cross namespace boundaries. Cross-namespace references + are only valid if they are explicitly allowed by something + in the namespace they are referring to. For example: Gateway + has the AllowedRoutes field, and ReferenceGrant provides + a generic way to enable any other kind of cross-namespace + reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +{{- end }} + diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/authorization-policy.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/authorization-policy.yaml new file mode 100644 index 000000000..7d86520e2 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/authorization-policy.yaml @@ -0,0 +1,99 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: authorizationpolicies.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: AuthorizationPolicy + plural: authorizationpolicies + singular: authorizationpolicy + shortNames: [authzpolicy] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied server + resources. + type: object + required: [targetRef, requiredAuthenticationRefs] + properties: + targetRef: + description: >- + TargetRef references a resource to which the authorization + policy applies. + type: object + required: [kind, name] + # Modified from the gateway API. + # Copyright 2020 The Kubernetes Authors + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: >- + Kind is the kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + requiredAuthenticationRefs: + description: >- + RequiredAuthenticationRefs enumerates a set of required + authentications. ALL authentications must be satisfied for + the authorization to apply. If any of the referred objects + cannot be found, the authorization will be ignored. + type: array + items: + type: object + required: [kind, name] + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes core API group is inferred." + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: >- + Kind is the kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: >- + Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: >- + Name is the name of the referent. When unspecified, + this authentication refers to the local namespace. + maxLength: 253 + type: string diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/httproute.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/httproute.yaml new file mode 100644 index 000000000..6d2e8b07e --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/httproute.yaml @@ -0,0 +1,5328 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: httproutes.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + names: + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may + be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + \ that have either not specified any hostnames or have specified + at least one hostname that matches the Listener hostname. For + example, `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "port" + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches) and processing it (filters). + properties: + backendRefs: + type: array + items: + type: object + properties: + name: + type: string + port: + type: integer + namespace: + type: string + default: "default" + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n All filters are expected to be compatible with each other + except for the URLRewrite and RequestRedirect filters, which + may not be combined. If an implementation can not support + other combinations of filters, they must clearly document + that limitation. In all cases where incompatible or unsupported + filters are specified, implementations MUST add a warning + condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: add: - name: \"my-header\" value: + \"bar\" \n Output: GET /foo HTTP/1.1 my-header: + foo my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + \ my-header2: bar my-header3: baz \n Config: + \ remove: [\"my-header1\", \"my-header3\"] \n Output: + \ GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: - name: \"my-header\" value: \"bar\" + \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. When empty, + port (if specified) of the request is used. \n Support: + Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations must support core filters. \n\n " + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - + name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" + ``` \n For a request to match against this rule, a request + must satisfy EITHER of the two conditions: \n - path prefixed + with `/foo` AND contains the header `version: v2` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Proxy or Load Balancer routing configuration + generated from HTTPRoutes MUST prioritize rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * Characters in a matching path. * Header matches. + * Query param matches. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom conformance, + implementations can support POSIX, PCRE or any + other dialects of regular expressions. Please + read the implementation's documentation to determine + the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Custom (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified (or empty string), this refers to the + local namespace of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may + be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + \ that have either not specified any hostnames or have specified + at least one hostname that matches the Listener hostname. For + example, `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "port" + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches) and processing it (filters). + properties: + backendRefs: + type: array + items: + type: object + properties: + name: + type: string + port: + type: integer + namespace: + type: string + default: "default" + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n All filters are expected to be compatible with each other + except for the URLRewrite and RequestRedirect filters, which + may not be combined. If an implementation can not support + other combinations of filters, they must clearly document + that limitation. In all cases where incompatible or unsupported + filters are specified, implementations MUST add a warning + condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: add: - name: \"my-header\" value: + \"bar\" \n Output: GET /foo HTTP/1.1 my-header: + foo my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + \ my-header2: bar my-header3: baz \n Config: + \ remove: [\"my-header1\", \"my-header3\"] \n Output: + \ GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: - name: \"my-header\" value: \"bar\" + \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. When empty, + port (if specified) of the request is used. \n Support: + Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - + name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" + ``` \n For a request to match against this rule, a request + must satisfy EITHER of the two conditions: \n - path prefixed + with `/foo` AND contains the header `version: v2` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Proxy or Load Balancer routing configuration + generated from HTTPRoutes MUST prioritize rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * Characters in a matching path. * Header matches. + * Query param matches. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom conformance, + implementations can support POSIX, PCRE or any + other dialects of regular expressions. Please + read the implementation's documentation to determine + the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Custom (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified (or empty string), this refers to the + local namespace of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta2 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may + be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + \ that have either not specified any hostnames or have specified + at least one hostname that matches the Listener hostname. For + example, `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port specifies the destination + port number to use for this resource. + Port is required when the referent is + a Kubernetes Service. In this case, the + port number is the service port number, + not the target port. For other resources, + destination port might be derived from + the referent resource or this field. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches) and processing it (filters). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If *all* entries in BackendRefs are invalid, and there + are also no filters specified in this route rule, *all* traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined. + \n Support: Core for Kubernetes Service \n Support: Implementation-specific + for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should + forward an HTTP request. + properties: + group: + default: "" + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". Defaults to "Service" when + not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferenceGrant + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferenceGrant documentation for details. \n Support: + Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number + to use for this resource. Port is required when the + referent is a Kubernetes Service. In this case, the + port number is the service port number, not the target + port. For other resources, destination port might be + derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support for this field varies based + on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n All filters are expected to be compatible with each other + except for the URLRewrite and RequestRedirect filters, which + may not be combined. If an implementation can not support + other combinations of filters, they must clearly document + that limitation. In all cases where incompatible or unsupported + filters are specified, implementations MUST add a warning + condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: add: - name: \"my-header\" value: + \"bar\" \n Output: GET /foo HTTP/1.1 my-header: + foo my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + \ my-header2: bar my-header3: baz \n Config: + \ remove: [\"my-header1\", \"my-header3\"] \n Output: + \ GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: - name: \"my-header\" value: \"bar\" + \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. When empty, + port (if specified) of the request is used. \n Support: + Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - + name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" + ``` \n For a request to match against this rule, a request + must satisfy EITHER of the two conditions: \n - path prefixed + with `/foo` AND contains the header `version: v2` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Proxy or Load Balancer routing configuration + generated from HTTPRoutes MUST prioritize rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * Characters in a matching path. * Header matches. + * Query param matches. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom conformance, + implementations can support POSIX, PCRE or any + other dialects of regular expressions. Please + read the implementation's documentation to determine + the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Custom (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified (or empty string), this refers to the + local namespace of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta3 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes + the capability to match requests by hostname, path, header, or query param. + Filters can be used to specify additional processing steps. Backends specify + where matching requests should be routed. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may + be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n If a hostname is specified + by both the Listener and HTTPRoute, there must be at least one intersecting + hostname for the HTTPRoute to be attached to the Listener. For example: + \n * A Listener with `test.example.com` as the hostname matches + HTTPRoutes that have either not specified any hostnames, or have + specified at least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + \ that have either not specified any hostnames or have specified + at least one hostname that matches the Listener hostname. For + example, `*.example.com`, `test.example.com`, and `foo.test.example.com` + would all match. On the other hand, `example.com` and `test.example.net` + would not match. \n Hostnames that are prefixed with a wildcard + label (`*.`) are interpreted as a suffix match. That means that + a match for `*.example.com` would match both `test.example.com`, + and `foo.test.example.com`, but not `example.com`. \n If both the + Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames + that do not match the Listener hostname MUST be ignored. For example, + if a Listener specified `*.example.com`, and the HTTPRoute specified + `test.example.com` and `test.example.net`, `test.example.net` must + not be considered for a match. \n If both the Listener and HTTPRoute + have specified hostnames, and none match with the criteria above, + then the HTTPRoute is not accepted. The implementation must raise + an 'Accepted' Condition with a status of `False` in the corresponding + RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network + host. This matches the RFC 1123 definition of a hostname with + 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname + may be prefixed with a wildcard label (`*.`). The wildcard label + must appear by itself as the first label. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + \n Note that as per RFC1035 and RFC1123, a *label* must consist + of lower case alphanumeric characters or '-', and must start and + end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) + that a Route wants to be attached to. Note that the referenced parent + resource needs to allow this for the attachment to be complete. + For Gateways, that means the Gateway needs to allow attachment from + Routes of this kind and namespace. \n The only kind of parent resource + with \"Core\" support is Gateway. This API may be extended in the + future to support additional kinds of parent resources such as one + of the route kinds. \n It is invalid to reference an identical parent + more than once. It is valid to reference multiple distinct sections + within the same parent resource, such as 2 Listeners within a Gateway. + \n It is possible to separately reference multiple distinct objects + that may be collapsed by an implementation. For example, some implementations + may choose to merge compatible Gateway Listeners together. If that + is the case, the list of routes attached to those resources should + also be merged." + items: + description: "ParentReference identifies an API object (usually + a Gateway) that can be considered a parent of this resource (usually + a route). The only kind of parent resource with \"Core\" support + is Gateway. This API may be extended in the future to support + additional kinds of parent resources, such as HTTPRoute. \n The + API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid." + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core + (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When + unspecified (or empty string), this refers to the local namespace + of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port specifies the destination + port number to use for this resource. + Port is required when the referent is + a Kubernetes Service. In this case, the + port number is the service port number, + not the target port. For other resources, + destination port might be derived from + the referent resource or this field. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the + target resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match both + specified values. \n Implementations MAY choose to support + attaching Routes to other resources. If that is the case, + they MUST clearly document how SectionName is interpreted. + \n When unspecified (empty string), this will reference the + entire resource. For the purpose of status, an attachment + is considered successful if at least one section in the parent + resource accepts it. For example, Gateway listeners can restrict + which Routes can attach to them by Route kind, namespace, + or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this + Route, the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions (matches) and processing it (filters). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If *all* entries in BackendRefs are invalid, and there + are also no filters specified in this route rule, *all* traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined. + \n Support: Core for Kubernetes Service \n Support: Implementation-specific + for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should + forward an HTTP request. + properties: + group: + default: "" + description: Group is the group of the referent. For example, + "gateway.networking.k8s.io". When unspecified or empty + string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". Defaults to "Service" when + not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferenceGrant + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferenceGrant documentation for details. \n Support: + Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number + to use for this resource. Port is required when the + referent is a Kubernetes Service. In this case, the + port number is the service port number, not the target + port. For other resources, destination port might be + derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support for this field varies based + on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + filters: + description: "Filters defined at this level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Implementation-specific + (For broader support of filters, use the Filters field + in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps + that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway + implementations. Some examples include request or + response modification, implementing authentication + strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type + of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for + a filter that responds to the request with an + HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be + used in the value of the `Location` header + in the response. When empty, the hostname + in the `Host` header of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in + the value of the `Location` header in the + response. \n If no port is specified, the + redirect port MUST be derived using the following + rules: \n * If redirect scheme is not-empty, + the redirect port MUST be the well-known port + associated with the redirect scheme. Specifically + \"http\" to port 80 and \"https\" to port + 443. If the redirect scheme does not have + a well-known port, the listener port of the + Gateway SHOULD be used. * If redirect scheme + is empty, the redirect port MUST be the Gateway + Listener port. \n Implementations SHOULD NOT + add the port number in the 'Location' header + in the following cases: \n * A Location header + that will use HTTP (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 80. * A Location header that + will use HTTPS (whether that is determined + via the Listener protocol or the Scheme field) + _and_ use port 443. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used + in the value of the `Location` header in the + response. When empty, the scheme of the request + is used. \n Scheme redirects can affect the + port of the redirect, for more information, + refer to the documentation for the port field + of this filter. \n Note that values may be + added to this enum, implementations must ensure + that unknown values will not cause a crash. + \n Unknown values here must result in the + implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status + code to be used in response. \n Note that + values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result + in the implementation setting the Accepted + Condition for the Route to `status: False`, + with a Reason of `UnsupportedValue`. \n Support: + Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema + for a filter that modifies response headers. \n + Support: Extended" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo HTTP/1.1 + my-header: foo \n Config: add: - name: \"my-header\" + value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 + my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + my-header2: bar my-header3: baz \n Config: + remove: [\"my-header1\", \"my-header3\"] \n + Output: GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: - name: \"my-header\" + value: \"bar\" \n Output: GET /foo HTTP/1.1 + my-header: bar" + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: "Name is the name of the + HTTP Header to be matched. Name matching + MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an + equivalent name MUST be considered for + a match. Subsequent entries with an + equivalent header name MUST be ignored. + Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Implementation-specific: + Filters that are defined and supported by specific + vendors. In the future, filters showing convergence + in behavior across multiple implementations will + be considered for inclusion in extended or core + conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior. + \n If a reference to a custom filter type cannot + be resolved, the filter MUST NOT be skipped. Instead, + requests that would have been processed by that + filter MUST receive a HTTP error response. \n + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause + a crash. \n Unknown values here must result in + the implementation setting the Accepted Condition + for the Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n All filters are expected to be compatible with each other + except for the URLRewrite and RequestRedirect filters, which + may not be combined. If an implementation can not support + other combinations of filters, they must clearly document + that limitation. In all cases where incompatible or unsupported + filters are specified, implementations MUST add a warning + condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that + must be completed during the request or response lifecycle. + HTTPRouteFilters are meant as an extension point to express + processing that may be done in Gateway implementations. + Some examples include request or response modification, + implementing authentication strategies, rate-limiting, and + traffic shaping. API guarantee/conformance is defined based + on the type of the filter. + properties: + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" + properties: + add: + description: "Add adds the given header(s) (name, + value) to the request before the action. It appends + to any existing values associated with the header + name. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: add: - name: \"my-header\" value: + \"bar\" \n Output: GET /foo HTTP/1.1 my-header: + foo my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of Remove + is a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + \n Input: GET /foo HTTP/1.1 my-header1: foo + \ my-header2: bar my-header3: baz \n Config: + \ remove: [\"my-header1\", \"my-header3\"] \n Output: + \ GET /foo HTTP/1.1 my-header2: bar" + items: + type: string + maxItems: 16 + type: array + set: + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: - name: \"my-header\" value: \"bar\" + \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case + insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent + header names, the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST + be ignored. Due to the case-insensitivity + of header names, \"foo\" and \"Foo\" are considered + equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter + that responds to the request with an HTTP redirection. + \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used + in the value of the `Location` header in the response. + When empty, the hostname of the request is used. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to + modify the path of the incoming request. The + modified path is then used to construct the + `Location` header. When empty, the request + path is used as-is. \n Support: Extended" + properties: + replaceFullPath: + description: ReplaceFullPath specifies the + value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies + the value with which to replace the prefix + match of a request during a rewrite or + redirect. For example, a request to \"/foo/bar\" + with a prefix match of \"/foo\" and a + ReplacePrefixMatch of \"/xyz\" would be + modified to \"/xyz/bar\". \n Note that + this matches the behavior of the PathPrefix + match type. This matches full path elements. + A path element refers to the list of labels + in the path split by the `/` separator. + When specified, a trailing `/` is ignored. + For example, the paths `/abc`, `/abc/`, + and `/abc/def` would all match the prefix + `/abc`, but the path `/abcd` would not. + \n Request Path | Prefix Match | Replace + Prefix | Modified Path -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | + /xyz/bar /foo/bar | /foo | + /xyz/ | /xyz/bar /foo/bar | + /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | + /xyz/bar /foo | /foo | + /xyz | /xyz /foo/ | /foo + \ | /xyz | /xyz/ /foo/bar + \ | /foo | | + /bar /foo/ | /foo | | / /foo | /foo | + | / /foo/ | /foo + \ | / | / /foo | + /foo | / | /" + maxLength: 1024 + type: string + type: + description: "Type defines the type of path + modifier. Additional types may be added + in a future release of the API. \n Note + that values may be added to this enum, + implementations must ensure that unknown + values will not cause a crash. \n Unknown + values here must result in the implementation + setting the Accepted Condition for the + Route to `status: False`, with a Reason + of `UnsupportedValue`." + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value + of the `Location` header in the response. When empty, + port (if specified) of the request is used. \n Support: + Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the + value of the `Location` header in the response. + When empty, the scheme of the request is used. \n + Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to + be used in response. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\"." + enum: + - RequestHeaderModifier + - RequestRedirect + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: - + name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" + ``` \n For a request to match against this rule, a request + must satisfy EITHER of the two conditions: \n - path prefixed + with `/foo` AND contains the header `version: v2` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Proxy or Load Balancer routing configuration + generated from HTTPRoutes MUST prioritize rules based on the + following criteria, continuing on ties. Precedence must be + given to the the Rule with the largest number of: \n * Characters + in a matching non-wildcard hostname. * Characters in a matching + hostname. * Characters in a matching path. * Header matches. + * Query param matches. \n If ties still exist across multiple + Routes, matching precedence MUST be determined in order of + the following criteria, continuing on ties: \n * The oldest + Route based on creation timestamp. * The Route appearing first + in alphabetical order by \"{namespace}/{name}\". \n If ties + still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria. \n When no rules matching + a request have been successfully attached to the parent a + request is coming from, a HTTP 404 status code MUST be returned." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: v1` header: \n + ``` match: path: value: \"/foo\" headers: - name: + \"version\" value \"v1\" ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + description: HTTPHeaderMatch describes how to select + a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, \"foo\" and \"Foo\" are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression) \n Since + RegularExpression HeaderMatchType has custom conformance, + implementations can support POSIX, PCRE or any + other dialects of regular expressions. Please + read the implementation's documentation to determine + the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: PathPrefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, PathPrefix) + \n Support: Custom (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: + description: HTTPQueryParamMatch describes how to select + a HTTP route by matching HTTP query parameters. + properties: + name: + description: Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: + Extended (Exact) \n Support: Custom (RegularExpression) + \n Since RegularExpression QueryParamMatchType + has custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation + to determine the supported dialect." + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + timeouts: + description: "Timeouts defines the timeouts that can be configured + for an HTTP request. \n Support: Core \n " + properties: + backendRequest: + description: "BackendRequest specifies a timeout for an + individual request from the gateway to a backend service. + Typically used in conjunction with automatic retries, + if supported by an implementation. Default is the value + of Request timeout. \n Support: Extended" + format: duration + type: string + request: + description: "Request specifies a timeout for responding + to client HTTP requests, disabled by default. \n For example, + the following rule will timeout if a client request is + taking longer than 10 seconds to complete: \n ``` rules: + - timeouts: request: 10s backendRefs: ... ``` \n Support: + Core" + format: duration + type: string + type: object + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) + that are associated with the route, and the status of the route + with respect to each parent. When this route attaches to a parent, + the controller that manages the parent must add an entry to this + list when the controller first sees the route and should update + the entry as appropriate when the route or gateway is modified. + \n Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this + API can only populate Route status for the Gateways/parent resources + they are responsible for. \n A maximum of 32 Gateways will be represented + in this list. An empty list means the route has not been attached + to any Gateway." + items: + description: RouteParentStatus describes the status of a route with + respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with + respect to the Gateway. Note that the route's availability + is also subject to the Gateway's own status conditions and + listener status. \n If the Route's ParentRef specifies an + existing Gateway that supports Routes of this kind AND that + Gateway's controller has sufficient access, then that Gateway's + controller MUST set the \"Accepted\" condition on the Route, + to indicate whether the route has been accepted or rejected + by the Gateway, and why. \n A Route MUST be considered \"Accepted\" + if at least one of the Route's rules is implemented by the + Gateway. \n There are a number of cases where the \"Accepted\" + condition may not be set due to lack of controller visibility, + that includes when: \n * The Route refers to a non-existent + parent. * The Route is of a type that the controller does + not support. * The Route is in a namespace the the controller + does not have access to." + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates + the name of the controller that wrote this status. This corresponds + with the controllerName field on GatewayClass. \n Example: + \"example.net/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid + Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Controllers MUST populate this field when writing status. + Controllers should ensure that entries to status populated + with their ControllerName are cleaned up when they are no + longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec + that this RouteParentStatus struct describes the status of. + properties: + group: + default: policy.linkerd.io + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: + Core (Gateway) Support: Custom (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. + When unspecified (or empty string), this refers to the + local namespace of the Route. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. + It can be interpreted differently based on the type of + parent resource. \n When the parent resource is a Gateway, + this targets all listeners listening on the specified + port that also support this kind of Route(and select this + Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to + a specific port as opposed to a listener(s) whose port(s) + may be changed. When both Port and SectionName are specified, + the name and port of the selected listener must match + both specified values. \n Implementations MAY choose to + support other parent resources. Implementations supporting + other types of parent resources MUST clearly document + how/if Port is interpreted. \n For the purpose of status, + an attachment is considered successful as long as the + parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them + by Route kind, namespace, or hostname. If 1 of 2 Gateway + listeners accept attachment from the referencing Route, + the Route MUST be considered successfully attached. If + no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within + the target resource. In the following resources, SectionName + is interpreted as the following: \n * Gateway: Listener + Name. When both Port (experimental) and SectionName are + specified, the name and port of the selected listener + must match both specified values. \n Implementations MAY + choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName + is interpreted. \n When unspecified (empty string), this + will reference the entire resource. For the purpose of + status, an attachment is considered successful if at least + one section in the parent resource accepts it. For example, + Gateway listeners can restrict which Routes can attach + to them by Route kind, namespace, or hostname. If 1 of + 2 Gateway listeners accept attachment from the referencing + Route, the Route MUST be considered successfully attached. + If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/meshtls-authentication.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/meshtls-authentication.yaml new file mode 100644 index 000000000..58ee815f5 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/meshtls-authentication.yaml @@ -0,0 +1,87 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: meshtlsauthentications.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: MeshTLSAuthentication + plural: meshtlsauthentications + singular: meshtlsauthentication + shortNames: [meshtlsauthn] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + MeshTLSAuthentication defines a list of authenticated client IDs + to be referenced by an `AuthorizationPolicy`. If a client + connection has the mutually-authenticated identity that matches + ANY of the of the provided identities, the connection is + considered authenticated. + type: object + oneOf: + - required: [identities] + - required: [identityRefs] + properties: + identities: + description: >- + Authorizes clients with the provided proxy identity strings + (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + minItems: 1 + items: + type: string + identityRefs: + type: array + minItems: 1 + items: + type: object + required: + - kind + properties: + group: + description: >- + Group is the group of the referent. When empty, the + Kubernetes core API group is inferred." + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: >- + Kind is the kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: >- + Name is the name of the referent. When unspecified, + this refers to all resources of the specified Group + and Kind in the specified namespace. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: >- + Name is the name of the referent. When unspecified, + this authentication refers to the local namespace. + maxLength: 253 + type: string diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/network-authentication.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/network-authentication.yaml new file mode 100644 index 000000000..cef15d3c4 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/network-authentication.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: networkauthentications.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: NetworkAuthentication + plural: networkauthentications + singular: networkauthentication + shortNames: [netauthn, networkauthn] + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + NetworkAuthentication defines a list of authenticated client + networks to be referenced by an `AuthorizationPolicy`. If a + client connection originates from ANY of the of the provided + networks, the connection is considered authenticated. + type: object + required: [networks] + properties: + networks: + type: array + items: + type: object + required: [cidr] + properties: + cidr: + description: >- + The CIDR of the network to be authorized. + type: string + except: + description: >- + A list of IP networks/addresses not to be included in + the above `cidr`. + type: array + items: + type: string diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server-authorization.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server-authorization.yaml new file mode 100644 index 000000000..33fb65900 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server-authorization.yaml @@ -0,0 +1,266 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serverauthorizations.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + scope: Namespaced + names: + kind: ServerAuthorization + plural: serverauthorizations + singular: serverauthorization + shortNames: [saz, serverauthz, srvauthz] + versions: + - name: v1alpha1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/v1alpha1 ServerAuthorization is deprecated; use policy.linkerd.io/v1beta1 ServerAuthorization" + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied servers. + type: object + required: [server, client] + properties: + server: + description: >- + Identifies servers in the same namespace for which this + authorization applies. + + Only one of `name` or `selector` may be specified. + type: object + oneOf: + - required: [name] + - required: [selector] + properties: + name: + description: References a `Server` instance by name + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + selector: + description: >- + A label query over servers on which this authorization applies. + type: object + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + client: + description: Describes clients authorized to access a server. + type: object + properties: + networks: + description: >- + Limits the client IP addresses to which this + authorization applies. If unset, the server chooses a + default (typically, all IPs or the cluster's pod + network). + type: array + items: + type: object + required: [cidr] + properties: + cidr: + type: string + except: + type: array + items: + type: string + unauthenticated: + description: >- + Authorizes unauthenticated clients to access a server. + type: boolean + meshTLS: + type: object + properties: + unauthenticatedTLS: + type: boolean + description: >- + Indicates that no client identity is required for + communication. + + This is mostly important for the identity + controller, which must terminate TLS connections + from clients that do not yet have a certificate. + identities: + description: >- + Authorizes clients with the provided proxy identity + strings (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + items: + type: string + pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$' + serviceAccounts: + description: >- + Authorizes clients with the provided proxy identity + service accounts (as provided via MTLS) + type: array + items: + type: object + required: [name] + properties: + name: + description: The ServiceAccount's name. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + namespace: + description: >- + The ServiceAccount's namespace. If unset, the + authorization's namespace is used. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + - name: v1beta1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + description: >- + Authorizes clients to communicate with Linkerd-proxied servers. + type: object + required: [server, client] + properties: + server: + description: >- + Identifies servers in the same namespace for which this + authorization applies. + + Only one of `name` or `selector` may be specified. + type: object + oneOf: + - required: [name] + - required: [selector] + properties: + name: + description: References a `Server` instance by name + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + selector: + description: >- + A label query over servers on which this authorization applies. + type: object + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + client: + description: Describes clients authorized to access a server. + type: object + properties: + networks: + description: >- + Limits the client IP addresses to which this + authorization applies. If unset, the server chooses a + default (typically, all IPs or the cluster's pod + network). + type: array + items: + type: object + required: [cidr] + properties: + cidr: + type: string + except: + type: array + items: + type: string + unauthenticated: + description: >- + Authorizes unauthenticated clients to access a server. + type: boolean + meshTLS: + type: object + properties: + unauthenticatedTLS: + type: boolean + description: >- + Indicates that no client identity is required for + communication. + + This is mostly important for the identity + controller, which must terminate TLS connections + from clients that do not yet have a certificate. + identities: + description: >- + Authorizes clients with the provided proxy identity + strings (as provided via MTLS) + + The `*` prefix can be used to match all identities in + a domain. An identity string of `*` indicates that + all authentication clients are authorized. + type: array + items: + type: string + pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$' + serviceAccounts: + description: >- + Authorizes clients with the provided proxy identity + service accounts (as provided via MTLS) + type: array + items: + type: object + required: [name] + properties: + name: + description: The ServiceAccount's name. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + namespace: + description: >- + The ServiceAccount's namespace. If unset, the + authorization's namespace is used. + type: string + pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$' + additionalPrinterColumns: + - name: Server + type: string + description: The server that this grants access to + jsonPath: .spec.server.name diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server.yaml new file mode 100644 index 000000000..0af41224a --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/policy/server.yaml @@ -0,0 +1,319 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: servers.policy.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: policy.linkerd.io + names: + kind: Server + plural: servers + singular: server + shortNames: [srv] + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/v1alpha1 Server is deprecated; use policy.linkerd.io/v1beta1 Server" + schema: + openAPIV3Schema: + type: object + required: [spec] + properties: + spec: + type: object + required: + - podSelector + - port + properties: + podSelector: + type: object + description: >- + Selects pods in the same namespace. + oneOf: + - required: [matchExpressions] + - required: [matchLabels] + properties: + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + matchExpressions: + type: array + items: + type: object + required: [key, operator] + properties: + key: + type: string + operator: + type: string + enum: [In, NotIn, Exists, DoesNotExist] + values: + type: array + items: + type: string + port: + description: >- + A port name or number. Must exist in a pod spec. + x-kubernetes-int-or-string: true + proxyProtocol: + description: >- + Configures protocol discovery for inbound connections. + + Supersedes the `config.linkerd.io/opaque-ports` annotation. + type: string + default: unknown + - name: v1beta1 + served: true + storage: false + deprecated: true + deprecationWarning: "policy.linkerd.io/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.9.1/templates/serviceprofile.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/serviceprofile.yaml new file mode 100644 index 000000000..ad12c96a3 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/serviceprofile.yaml @@ -0,0 +1,274 @@ +--- +### +### Service Profile CRD +### +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serviceprofiles.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: linkerd.io + versions: + - name: v1alpha1 + served: true + storage: false + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Spec is the custom resource spec + required: + - routes + properties: + dstOverrides: + type: array + required: + - authority + - weight + items: + type: object + description: WeightedDst is a weighted alternate destination. + properties: + authority: + type: string + weight: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + opaquePorts: + type: array + items: + type: string + retryBudget: + type: object + required: + - minRetriesPerSecond + - retryRatio + - ttl + description: RetryBudget describes the maximum number of retries that should be issued to this service. + properties: + minRetriesPerSecond: + format: int32 + type: integer + retryRatio: + type: number + format: float + ttl: + type: string + routes: + type: array + items: + type: object + description: RouteSpec specifies a Route resource. + required: + - condition + - name + properties: + condition: + type: object + description: RequestMatch describes the conditions under which to match a Route. + properties: + pathRegex: + type: string + method: + type: string + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + isRetryable: + type: boolean + name: + type: string + timeout: + type: string + responseClasses: + type: array + items: + type: object + required: + - condition + description: ResponseClass describes how to classify a response (e.g. success or failures). + properties: + condition: + type: object + description: ResponseMatch describes the conditions under + which to classify a response. + properties: + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + description: Range describes a range of integers (e.g. status codes). + properties: + max: + format: int32 + type: integer + min: + format: int32 + type: integer + isFailure: + type: boolean + - name: v1alpha2 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Spec is the custom resource spec + properties: + dstOverrides: + type: array + required: + - authority + - weight + items: + type: object + description: WeightedDst is a weighted alternate destination. + properties: + authority: + type: string + weight: + x-kubernetes-int-or-string: true + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + opaquePorts: + type: array + items: + type: string + retryBudget: + type: object + required: + - minRetriesPerSecond + - retryRatio + - ttl + description: RetryBudget describes the maximum number of retries that should be issued to this service. + properties: + minRetriesPerSecond: + format: int32 + type: integer + retryRatio: + type: number + format: float + ttl: + type: string + routes: + type: array + items: + type: object + description: RouteSpec specifies a Route resource. + required: + - condition + - name + properties: + condition: + type: object + description: RequestMatch describes the conditions under which to match a Route. + properties: + pathRegex: + type: string + method: + type: string + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + isRetryable: + type: boolean + name: + type: string + timeout: + type: string + responseClasses: + type: array + items: + type: object + required: + - condition + description: ResponseClass describes how to classify a response (e.g. success or failures). + properties: + condition: + type: object + description: ResponseMatch describes the conditions under + which to classify a response. + properties: + all: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + any: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true + not: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + type: object + description: Range describes a range of integers (e.g. status codes). + properties: + max: + format: int32 + type: integer + min: + format: int32 + type: integer + isFailure: + type: boolean + scope: Namespaced + preserveUnknownFields: false + names: + plural: serviceprofiles + singular: serviceprofile + kind: ServiceProfile + shortNames: + - sp diff --git a/charts/linkerd/linkerd-crds/2024.9.1/templates/workload/external-workload.yaml b/charts/linkerd/linkerd-crds/2024.9.1/templates/workload/external-workload.yaml new file mode 100644 index 000000000..2e6e43ae6 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/templates/workload/external-workload.yaml @@ -0,0 +1,303 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: externalworkloads.workload.linkerd.io + annotations: + {{ include "partials.annotations.created-by" . }} + labels: + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + linkerd.io/control-plane-ns: {{.Release.Namespace}} +spec: + group: workload.linkerd.io + names: + categories: + - external + kind: ExternalWorkload + listKind: ExternalWorkloadList + plural: externalworkloads + singular: externalworkload + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: false + schema: + openAPIV3Schema: + description: >- + An ExternalWorkload describes a single workload (i.e. a deployable unit) external + to the cluster that should be enrolled in the mesh. + type: object + required: [spec] + properties: + apiVerson: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + meshTls: + description: meshTls describes TLS settings associated with an + external workload. + properties: + identity: + type: string + description: identity of the workload. Corresponds to the + identity used in the workload's certificate. It is used + by peers to perform verification in the mTLS handshake. + minLength: 1 + maxLength: 253 + serverName: + type: string + description: serverName is the name of the workload in DNS + format. It is used by the workload to terminate TLS using + SNI. + minLength: 1 + maxLength: 253 + type: object + required: + - identity + - serverName + ports: + type: array + description: ports describes a list of ports exposed by the + workload + items: + properties: + name: + type: string + description: name must be an IANA_SVC_NAME and unique + within the ports set. Each named port can be referred + to by services. + port: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: protocol exposed by the port. Must be UDP or + TCP. Defaults to TCP. + type: string + default: "TCP" + type: object + required: + - port + workloadIPs: + type: array + description: workloadIPs contains a list of IP addresses that + can be used to send traffic to the workload. + items: + type: object + properties: + ip: + type: string + # TODO: relax this in the future when ipv6 is supported + # an external workload (like a pod) should only + # support 2 interfaces + maxItems: 1 + type: object + required: + - meshTls + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + lastProbeTime: + description: lastProbeTime is the last time the + healthcheck endpoint was probed. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + status: + description: status of the condition (one of True, False, Unknown) + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of the condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last + transition. Producers of specific condition types may + define expected values and meanings for this field, and + whether the values are considered a guaranteed API. The + value should be a CamelCase string. This field may not + be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + message: + description: message is a human readable message + indicating details about the transition. This may be an + empty string. + maxLength: 32768 + type: string + required: + - status + - type + additionalPrinterColumns: + - jsonPath: .spec.meshTls.identity + name: Identity + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: >- + An ExternalWorkload describes a single workload (i.e. a deployable unit) external + to the cluster that should be enrolled in the mesh. + type: object + required: [spec] + properties: + apiVerson: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + meshTLS: + description: meshTLS describes TLS settings associated with an + external workload. + properties: + identity: + type: string + description: identity of the workload. Corresponds to the + identity used in the workload's certificate. It is used + by peers to perform verification in the mTLS handshake. + minLength: 1 + maxLength: 253 + serverName: + type: string + description: serverName is the name of the workload in DNS + format. It is used by the workload to terminate TLS using + SNI. + minLength: 1 + maxLength: 253 + type: object + required: + - identity + - serverName + ports: + type: array + description: ports describes a list of ports exposed by the + workload + items: + properties: + name: + type: string + description: name must be an IANA_SVC_NAME and unique + within the ports set. Each named port can be referred + to by services. + port: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: protocol exposed by the port. Must be UDP or + TCP. Defaults to TCP. + type: string + default: "TCP" + type: object + required: + - port + workloadIPs: + type: array + description: workloadIPs contains a list of IP addresses that + can be used to send traffic to the workload. This field may + hold a maximum of two entries. If one entry, it can be an + IPv4 or IPv6 address; if two entries it should contain one + IPv4 address and one IPv6 address. + items: + type: object + properties: + ip: + type: string + maxItems: 2 + type: object + required: + - meshTLS + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + lastProbeTime: + description: lastProbeTime is the last time the + healthcheck endpoint was probed. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the last time the + condition transitioned from one status to another. + format: date-time + type: string + status: + description: status of the condition (one of True, False, Unknown) + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of the condition in CamelCase or in + foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last + transition. Producers of specific condition types may + define expected values and meanings for this field, and + whether the values are considered a guaranteed API. The + value should be a CamelCase string. This field may not + be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + message: + description: message is a human readable message + indicating details about the transition. This may be an + empty string. + maxLength: 32768 + type: string + required: + - status + - type + additionalPrinterColumns: + - jsonPath: .spec.meshTLS.identity + name: Identity + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date diff --git a/charts/linkerd/linkerd-crds/2024.9.1/values.yaml b/charts/linkerd/linkerd-crds/2024.9.1/values.yaml new file mode 100644 index 000000000..362145168 --- /dev/null +++ b/charts/linkerd/linkerd-crds/2024.9.1/values.yaml @@ -0,0 +1 @@ +enableHttpRoutes: true diff --git a/charts/speedscale/speedscale-operator/2.2.372/.helmignore b/charts/speedscale/speedscale-operator/2.2.372/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/.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.372/Chart.yaml b/charts/speedscale/speedscale-operator/2.2.372/Chart.yaml new file mode 100644 index 000000000..2b61782a2 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372 +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.372 diff --git a/charts/speedscale/speedscale-operator/2.2.372/LICENSE b/charts/speedscale/speedscale-operator/2.2.372/LICENSE new file mode 100644 index 000000000..b78723d62 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/README.md b/charts/speedscale/speedscale-operator/2.2.372/README.md new file mode 100644 index 000000000..6ca25eed9 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/app-readme.md b/charts/speedscale/speedscale-operator/2.2.372/app-readme.md new file mode 100644 index 000000000..6ca25eed9 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/questions.yaml b/charts/speedscale/speedscale-operator/2.2.372/questions.yaml new file mode 100644 index 000000000..29aee3895 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/NOTES.txt b/charts/speedscale/speedscale-operator/2.2.372/templates/NOTES.txt new file mode 100644 index 000000000..cabb59b17 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/admission.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/admission.yaml new file mode 100644 index 000000000..301748a61 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/configmap.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/configmap.yaml new file mode 100644 index 000000000..af735e288 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/templates/configmap.yaml @@ -0,0 +1,41 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: speedscale-operator + namespace: {{ .Release.Namespace }} + annotations: + argocd.argoproj.io/hook: PreSync + {{- if .Values.globalAnnotations }} +{{ toYaml .Values.globalAnnotations | indent 4}} + {{- end }} +data: + CLUSTER_NAME: {{ .Values.clusterName }} + IMAGE_PULL_POLICY: {{ .Values.image.pullPolicy }} + IMAGE_PULL_SECRETS: "" + IMAGE_REGISTRY: {{ .Values.image.registry }} + IMAGE_TAG: {{ .Values.image.tag }} + INSTANCE_ID: '{{- $cm := (lookup "v1" "ConfigMap" .Release.Namespace "speedscale-operator") -}}{{ if $cm }}{{ $cm.data.INSTANCE_ID }}{{ else }}{{ ( printf "%s-%s" .Values.clusterName uuidv4 ) }}{{ end }}' + LOG_LEVEL: {{ .Values.logLevel }} + SPEEDSCALE_DLP_CONFIG: {{ .Values.dlp.config }} + SPEEDSCALE_FILTER_RULE: {{ .Values.filterRule }} + TELEMETRY_INTERVAL: 1s + WITH_DLP: {{ .Values.dlp.enabled | quote }} + WITH_INSPECTOR: {{ .Values.dashboardAccess | quote }} + API_KEY_SECRET_NAME: {{ .Values.apiKeySecret | quote }} + DEPLOY_DEMO: {{ .Values.deployDemo | quote }} + GLOBAL_ANNOTATIONS: {{ .Values.globalAnnotations | toJson | quote }} + GLOBAL_LABELS: {{ .Values.globalLabels | toJson | quote }} + {{- if .Values.http_proxy }} + HTTP_PROXY: {{ .Values.http_proxy }} + {{- end }} + {{- if .Values.https_proxy }} + HTTPS_PROXY: {{ .Values.https_proxy }} + {{- end }} + {{- if .Values.no_proxy }} + NO_PROXY: {{ .Values.no_proxy }} + {{- end }} + PRIVILEGED_SIDECARS: {{ .Values.privilegedSidecars | quote }} + DISABLE_SMARTDNS: {{ .Values.disableSidecarSmartReverseDNS | quote }} + SIDECAR_CONFIG: {{ .Values.sidecar | toJson | quote }} + FORWARDER_CONFIG: {{ .Values.forwarder | toJson | quote }} diff --git a/charts/speedscale/speedscale-operator/2.2.372/templates/crds/trafficreplays.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/crds/trafficreplays.yaml new file mode 100644 index 000000000..5743c5a13 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/templates/crds/trafficreplays.yaml @@ -0,0 +1,515 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: trafficreplays.speedscale.com +spec: + group: speedscale.com + names: + kind: TrafficReplay + listKind: TrafficReplayList + plural: trafficreplays + shortNames: + - replay + singular: trafficreplay + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.active + name: Active + type: boolean + - jsonPath: .spec.mode + name: Mode + type: string + - jsonPath: .status.conditions[-1:].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: TrafficReplay is the Schema for the trafficreplays API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: TrafficReplaySpec defines the desired state of TrafficReplay + properties: + buildTag: + description: |- + BuildTag links a unique tag, build hash, etc. to the generated + traffic replay report. That way you can connect the report results to the + version of the code that was tested. + type: string + cleanup: + description: |- + Cleanup is the name of cleanup mode used for this + TrafficReplay. + enum: + - inventory + - all + - none + type: string + collectLogs: + description: |- + CollectLogs enables or disables log collection from target + workload. Defaults to true. + DEPRECATED: use TestReport.ActualConfig.Cluster.CollectLogs + type: boolean + configChecksum: + description: |- + ConfigChecksum, managed my the operator, is the SHA1 checksum of the + configuration. + type: string + customURL: + description: CustomURL allows to specify custom URL to the SUT. + type: string + generatorLowData: + description: |- + GeneratorLowData forces the generator into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + mode: + description: Mode is the name of replay mode used for this TrafficReplay. + enum: + - full-replay + - responder-only + - generator-only + type: string + needsReport: + description: Indicates whether a responder-only replay needs a report. + type: boolean + proxyMode: + description: |- + ProxyMode defines proxy operational mode used with injected sidecar. + DEPRECATED + type: string + responderLowData: + description: |- + ResponderLowData forces the responder into a high + efficiency/low data output mode. This is ideal for high volume + performance tests. Defaults to false. + DEPRECATED + type: boolean + secretRefs: + description: |- + SecretRefs hold the references to the secrets which contain + various secrets like (e.g. short-lived JWTs to be used by the generator + for authorization with HTTP calls). + items: + description: |- + LocalObjectReference contains enough information to locate the referenced + Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + sidecar: + description: |- + Sidecar defines sidecar specific configuration. + DEPRECATED: use Workloads + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS inbound + private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys to use + for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar mutual + TLS. + properties: + private: + description: Private is the filename of the mutual TLS + private key. + type: string + public: + description: Public is the filename of the mutual TLS + public key. + type: string + secret: + description: Secret is a secret with the mutual TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + snapshotID: + description: |- + SnapshotID is the id of the traffic snapshot for this + TrafficReplay. + type: string + testConfigID: + description: |- + TestConfigID is the id of the replay configuration to be used + by the generator and responder for the TrafficReplay. + type: string + timeout: + description: |- + Timeout is the time to wait for replay test to finish. Defaults + to value of the `TIMEOUT` setting of the operator. + type: string + ttlAfterReady: + description: |- + TTLAfterReady provides a TTL (time to live) mechanism to limit + the lifetime of TrafficReplay object that have finished the execution and + reached its final state (either complete or failed). + type: string + workloadRef: + description: |- + WorkloadRef is the reference to the target workload (SUT) for + TrafficReplay. The operations will be performed in the namespace of the + target object. + DEPRECATED: use Workloads + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to "Deployment". + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. Defaults to the + TrafficReplay namespace. + type: string + required: + - name + type: object + workloads: + description: |- + Workloads define target workloads (SUT) for a TrafficReplay. Many + workloads may be provided, or none. Workloads may be modified and + restarted during replay to configure communication with a responder. + items: + description: |- + Workload represents a Kubernetes workload to be targeted during replay and + associated settings. + properties: + customURI: + description: CustomURI will be target of the traffic instead + of directly targeting workload + type: string + inTrafficKey: + description: 'DEPRECATED: use InTrafficKeys' + type: string + inTrafficKeys: + description: 'DEPRECATED: use Tests' + items: + type: string + type: array + mocks: + description: |- + Mocks are strings used to identify slices of outbound snapshot traffic to + mock for this workload and maps directly to a snapshot's `OutTraffic` + field. Snapshot egress traffic can be split across multiple slices where + each slice contains part of the traffic. A workload may specify multiple + keys and multiple workloads may specify the same key. + + + Only the traffic slices defined here will be mocked. A workload with no + keys defined will not mock any traffic. Pass '*' to mock all traffic. + + + Mock strings may only match part of the snapshot's `OutTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `OutTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com`. Multiple mocks must be specified for multiple keys + unless using '*'. + items: + type: string + type: array + outTrafficKeys: + description: 'DEPRECATED: use Mocks' + items: + type: string + type: array + ref: + description: |- + Ref is a reference to a cluster workload, like a deployment or a + statefulset. + properties: + apiVersion: + description: API version of the referenced object. + type: string + kind: + description: Kind of the referenced object. Defaults to + "Deployment". + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. Defaults + to the TrafficReplay namespace. + type: string + required: + - name + type: object + routing: + description: Routing configures how workloads route egress traffic + to responders + enum: + - hostalias + - nat + type: string + sidecar: + description: |- + TODO: this is not implemented, come back and replace deprecated Sidecar with workload specific settings + Sidecar defines sidecar specific configuration. + properties: + inject: + description: 'DEPRECATED: do not use' + type: boolean + patch: + description: Patch is .yaml file patch for the Workload + format: byte + type: string + tls: + properties: + in: + description: In provides configuration for sidecar inbound + TLS. + properties: + private: + description: Private is the filename of the TLS + inbound private key. + type: string + public: + description: Public is the filename of the TLS inbound + public key. + type: string + secret: + description: Secret is a secret with the TLS keys + to use for inbound traffic. + type: string + type: object + mutual: + description: Mutual provides configuration for sidecar + mutual TLS. + properties: + private: + description: Private is the filename of the mutual + TLS private key. + type: string + public: + description: Public is the filename of the mutual + TLS public key. + type: string + secret: + description: Secret is a secret with the mutual + TLS keys. + type: string + type: object + out: + description: |- + Out enables or disables TLS out on the + sidecar during replay. + type: boolean + type: object + type: object + tests: + description: |- + Tests are strings used to identify slices of inbound snapshot traffic this + workload is targeting and maps directly to a snapshot's `InTraffic` field. + Snapshot ingress traffic can be split across multiple slices where each + slice contains part of the traffic. A key must only be specified once + across all workloads, but a workload may specify multiple keys. Pass '*' + to match all keys. + + + Test strings may only match part of the snapshot's `InTraffic` key if the + string matches exactly one key. For example, the test string + `foo.example.com` would match the `InTraffic` key of + my-service:foo.example.com:8080, as long as no other keys would match + `foo.example.com` + + + This field is optional in the spec to provide support for single-workload + and legacy replays, but must be specified for multi-workload replays in + order to provide deterministic replay configuration. + items: + type: string + type: array + type: object + type: array + required: + - snapshotID + - testConfigID + type: object + status: + default: + observedGeneration: -1 + description: TrafficReplayStatus defines the observed state of TrafficReplay + properties: + active: + description: Active indicates whether this traffic replay is currently + underway or not. + type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + finishedTime: + description: Information when the traffic replay has finished. + format: date-time + type: string + initializedTime: + description: Information when the test environment was successfully + prepared. + format: date-time + type: string + lastHeartbeatTime: + description: 'DEPRECATED: will not be set' + format: date-time + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + reconcileFailures: + description: |- + ReconcileFailures is the number of times the traffic replay controller + experienced an error during the reconciliation process. The traffic + replay will be deleted if too many errors occur. + format: int64 + type: integer + reportID: + description: The id of the traffic replay report created. + type: string + reportURL: + description: The url to the traffic replay report. + type: string + startedTime: + description: Information when the traffic replay has started. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/speedscale/speedscale-operator/2.2.372/templates/deployments.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/deployments.yaml new file mode 100644 index 000000000..e5f329257 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/hooks.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/hooks.yaml new file mode 100644 index 000000000..3e8231f19 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/rbac.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/rbac.yaml new file mode 100644 index 000000000..e1ea42d99 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/secrets.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/secrets.yaml new file mode 100644 index 000000000..1fb6999e4 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/services.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/services.yaml new file mode 100644 index 000000000..f9da2c25c --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/templates/tls.yaml b/charts/speedscale/speedscale-operator/2.2.372/templates/tls.yaml new file mode 100644 index 000000000..4a2456288 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/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.372/values.yaml b/charts/speedscale/speedscale-operator/2.2.372/values.yaml new file mode 100644 index 000000000..e841aa678 --- /dev/null +++ b/charts/speedscale/speedscale-operator/2.2.372/values.yaml @@ -0,0 +1,133 @@ +# An API key is required to connect to the Speedscale cloud. +# If you need a key email support@speedscale.com. +apiKey: "" + +# A secret name can be referenced instead of the api key itself. +# The secret must be of the format: +# +# type: Opaque +# data: +# SPEEDSCALE_API_KEY: +# SPEEDSCALE_APP_URL: +apiKeySecret: "" + +# Speedscale domain to use. +appUrl: "app.speedscale.com" + +# The name of your cluster. +clusterName: "my-cluster" + +# Speedscale components image settings. +image: + registry: gcr.io/speedscale + tag: v2.2.372 + pullPolicy: Always + +# Log level for Speedscale components. +logLevel: "info" + +# Namespaces to be watched by Speedscale Operator as a list of names. +namespaceSelector: [] + +# Instructs operator to deploy resources necessary to interact with your cluster from the Speedscale dashboard. +dashboardAccess: true + +# Filter Rule to apply to the Speedscale Forwarder +filterRule: "standard" + +# Data Loss Prevention settings. +dlp: + # Instructs operator to enable data loss prevention features + enabled: false + + # Configuration for data loss prevention + config: "standard" + +# If the operator pod/webhooks need to be on the host network. +# This is only needed if the control plane cannot connect directly to a pod +# for eg. if Calico is used as EKS's default networking +# https://docs.tigera.io/calico/3.25/getting-started/kubernetes/managed-public-cloud/eks#install-eks-with-calico-networking +hostNetwork: false + +# A set of annotations to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# annotation.first: value +# annotation.second: value +globalAnnotations: {} + +# A set of labels to be applied to all Speedscale related deployments, +# services, jobs, pods, etc. +# +# Example: +# label1: value +# label2: value +globalLabels: {} + +# A full affinity object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity +affinity: {} + +# The list of tolerations as detailed: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ +tolerations: [] + +# A nodeselector object as detailed: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/ +nodeSelector: {} + +# Deploy a demo app at startup. Set this to an empty string to not deploy. +# Valid values: ["java", ""] +deployDemo: "java" + +# Proxy connection settings if required by your network. These translate to standard proxy environment +# variables HTTP_PROXY, HTTPS_PROXY, and NO_PROXY +http_proxy: "" +https_proxy: "" +no_proxy: "" + +# control if sidecar init containers should run with privileged set +privilegedSidecars: false + +# control if the sidecar should enable/disable use of the smart dns lookup feature (requires NET_ADMIN) +disableSidecarSmartReverseDNS: false + +# Operator settings. These limits are recommended unless you have a cluster +# with a very large number of workloads (for eg. 10k+ deployments, replicasets, etc.). +operator: + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + +# Default sidecar settings. Example: +# sidecar: +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# ephemeral-storage: 100Mi +# requests: +# cpu: 10m +# memory: 32Mi +# ephemeral-storage: 100Mi +# ignore_src_hosts: example.com, example.org +# ignore_src_ips: 8.8.8.8, 1.1.1.1 +# ignore_dst_hosts: example.com, example.org +# ignore_dst_ips: 8.8.8.8, 1.1.1.1 +# insert_init_first: false +# tls_out: false +# reinitialize_iptables: false +sidecar: {} + +# Forwarder settings +# forwarder: +# resources: +# limits: +# cpu: 500m +# memory: 500M +# requests: +# cpu: 300m +# memory: 250M +forwarder: {} diff --git a/index.yaml b/index.yaml index 1c85da614..67bce00f2 100644 --- a/index.yaml +++ b/index.yaml @@ -21337,6 +21337,27 @@ entries: - assets/kong/kong-2.24.0.tgz version: 2.24.0 kubemq-cluster: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: KubeMQ Cluster + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: kubemq-cluster + apiVersion: v2 + appVersion: 2.9.3 + created: "2024-09-07T00:50:03.259100852Z" + description: A Helm chart for KubeMQ Cluster, Kubernetes Message Queue Broker + digest: 7cd8d3a43880451624a3e9b87e6b33e69ae5058c485387a9086394b737b0f08d + icon: file://assets/icons/kubemq-cluster.svg + kubeVersion: '>=1.21-0' + maintainers: + - email: info@kubemq.io + name: KubeMQ + url: https://kubemq.io + name: kubemq-cluster + type: application + urls: + - assets/kubemq/kubemq-cluster-2.4.0.tgz + version: 2.4.0 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: KubeMQ Cluster @@ -22697,6 +22718,38 @@ entries: catalog.cattle.io/kube-version: '>=1.22.0-0' catalog.cattle.io/release-name: linkerd-control-plane apiVersion: v2 + appVersion: edge-24.9.1 + created: "2024-09-07T00:50:03.400970914Z" + dependencies: + - name: partials + repository: file://./charts/partials + version: 0.1.0 + description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' + digest: 1a868c80a2c80fb24a052531c289d5395f5852e0907a2e7168bbc7987c0a6bd3 + 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.9.1.tgz + version: 2024.9.1 + - 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.8.3 created: "2024-08-30T00:51:02.41787433Z" dependencies: @@ -22705,7 +22758,7 @@ entries: version: 0.1.0 description: 'Linkerd gives you observability, reliability, and security for your microservices — with no code change required. ' - digest: 53da2413df66c421f9ae6920f7d0084e368a53d3643173bfec0b2bdf5e6329b3 + digest: 96d28f425d324cefbfb6e790cdff57ea95449d9ca0eef4d961ed7c12d93f59c9 home: https://linkerd.io icon: file://assets/icons/linkerd-control-plane.png keywords: @@ -23800,6 +23853,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-09-07T00:50:03.475255427Z" + dependencies: + - name: partials + repository: file://./charts/partials + version: 0.1.0 + description: 'Linkerd gives you observability, reliability, and security for your + microservices — with no code change required. ' + digest: efea83faa83b529a5ecaeb7cbcf6bccbb940bc94e1201eb84a400a504f5d8859 + 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.9.1.tgz + version: 2024.9.1 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Linkerd CRDs @@ -35397,6 +35480,37 @@ entries: - assets/btp/sextant-2.2.21.tgz version: 2.2.21 speedscale-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Speedscale Operator + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: speedscale-operator + apiVersion: v1 + appVersion: 2.2.372 + created: "2024-09-07T00:50:04.837963476Z" + description: Stress test your APIs with real world scenarios. Collect and replay + traffic without scripting. + digest: 70e61122741e8115a380ef5326977b4dae00283226037b95be750d5e8d7f9dcc + 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.372.tgz + version: 2.2.372 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: Speedscale Operator @@ -41833,4 +41947,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2024-09-06T00:51:23.779121068Z" +generated: "2024-09-07T00:50:00.028083031Z"