mirror of https://github.com/fluxcd/flagger.git
Merge pull request #1557 from fluxcd/gatewayapi-v1
gatewayapi: add support for `v1`
This commit is contained in:
commit
e9b8dee726
|
|
@ -6,55 +6,64 @@ This guide shows you how to use [Gateway API](https://gateway-api.sigs.k8s.io/)
|
|||
|
||||
## Prerequisites
|
||||
|
||||
Flagger requires a Kubernetes cluster **v1.19** or newer and any mesh/ingress that implements the `v1beta1` version of Gateway API. We'll be using Contour for the sake of this tutorial, but you can use any other implementation.
|
||||
Flagger requires a Kubernetes cluster **v1.19** or newer and any mesh/ingress that implements the `v1beta1` or the `v1` version of Gateway API.
|
||||
We'll be using Istio for the sake of this tutorial, but you can use any other implementation.
|
||||
|
||||
> Note: Flagger supports `v1alpha2` version of Gateway API, but the alpha version has been deprecated and support will be dropped in a future release.
|
||||
|
||||
Install Contour, its Gateway provisioner and Gateway API CRDs in the `projectcontour` namespace:
|
||||
Install the Gateway API CRDs
|
||||
|
||||
```bash
|
||||
https://raw.githubusercontent.com/projectcontour/contour/release-1.23/examples/render/contour-gateway-provisioner.yaml
|
||||
kubectl apply -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.0.0"
|
||||
```
|
||||
|
||||
> Alternatively, you can also install the Gateway API CRDs from the upstream project:
|
||||
Install Istio:
|
||||
|
||||
```bash
|
||||
kubectl apply -k github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.6.0
|
||||
istioctl install --set profile=minimal -y
|
||||
|
||||
# Suggestion: Please change release-1.20 in below command, to your real istio version.
|
||||
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml
|
||||
```
|
||||
|
||||
Install Flagger in the `flagger-system` namespace:
|
||||
|
||||
```bash
|
||||
kubectl apply -k github.com/fluxcd/flagger//kustomize/gatewayapi
|
||||
kubectl create ns flagger-system
|
||||
|
||||
helm repo add flagger https://flagger.app
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace flagger-system \
|
||||
--set prometheus.install=false \
|
||||
--set meshProvider=gatewayapi:v1 \
|
||||
--set metricsServer=http://prometheus.istio-system:9090
|
||||
```
|
||||
|
||||
Create a `GatewayClass` that specifies information about the Gateway controller:
|
||||
> Note: The above installation sets the mesh provider to be `gatewayapi:v1`. If your Gateway API implementation uses the `v1beta1` CRDs, then
|
||||
set the `--meshProvider` value to `gatewayapi:v1beta1`.
|
||||
|
||||
```yaml
|
||||
kind: GatewayClass
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: contour
|
||||
spec:
|
||||
controllerName: projectcontour.io/gateway-controller
|
||||
Create a namespace for the `Gateway`:
|
||||
|
||||
```bash
|
||||
kubectl create ns istio-ingress
|
||||
```
|
||||
|
||||
Create a `Gateway` that configures load balancing, traffic ACL, etc:
|
||||
|
||||
```yaml
|
||||
kind: Gateway
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: contour
|
||||
namespace: projectcontour
|
||||
name: gateway
|
||||
namespace: istio-ingress
|
||||
spec:
|
||||
gatewayClassName: contour
|
||||
gatewayClassName: istio
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
- name: default
|
||||
hostname: "*.example.com"
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
```
|
||||
|
||||
## Bootstrap
|
||||
|
|
@ -90,13 +99,15 @@ metadata:
|
|||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://flagger-prometheus:9090
|
||||
address: http://prometheus.istio-system:9090
|
||||
query: |
|
||||
histogram_quantile(0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
istio_request_duration_milliseconds_bucket{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
|
|
@ -110,21 +121,25 @@ metadata:
|
|||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://flagger-prometheus:9090
|
||||
address: http://prometheus.istio-system:9090
|
||||
query: |
|
||||
100 - sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
envoy_response_code!~"5.*"
|
||||
istio_requests_total{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
istio_requests_total{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
|
|
@ -137,7 +152,7 @@ Save the above resource as metric-templates.yaml and then apply it:
|
|||
kubectl apply -f metric-templates.yaml
|
||||
```
|
||||
|
||||
Create a canary custom resource \(replace "localproject.contour.io" with your own domain\):
|
||||
Create a canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
|
|
@ -166,11 +181,11 @@ spec:
|
|||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
|
|
@ -213,7 +228,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
Save the above resource as podinfo-canary.yaml and then apply it:
|
||||
|
|
@ -243,26 +258,27 @@ httproutes.gateway.networking.k8s.io/podinfo
|
|||
|
||||
## Expose the app outside the cluster
|
||||
|
||||
Find the external address of Contour's Envoy load balancer:
|
||||
Find the external address of Istio's load balancer:
|
||||
|
||||
```bash
|
||||
export ADDRESS="$(kubectl -n projectcontour get svc/envoy -ojson \
|
||||
export ADDRESS="$(kubectl -n istio-ingress get svc/gateway-istio -ojson \
|
||||
| jq -r ".status.loadBalancer.ingress[].hostname")"
|
||||
echo $ADDRESS
|
||||
```
|
||||
|
||||
Configure your DNS server with a CNAME record \(AWS\) or A record \(GKE/AKS/DOKS\) and point a domain e.g. `localproject.contour.io` to the LB address.
|
||||
Configure your DNS server with a CNAME record \(AWS\) or A record \(GKE/AKS/DOKS\) and point a domain e.g. `www.example.com` to the LB address.
|
||||
|
||||
Now you can access the podinfo UI using your domain address.
|
||||
|
||||
Note that you should be using HTTPS when exposing production workloads on internet. You can obtain free TLS certs from Let's Encrypt, read this [guide](https://github.com/stefanprodan/eks-contour-ingress) on how to configure cert-manager to secure Contour with TLS certificates.
|
||||
Note that you should be using HTTPS when exposing production workloads on internet. You can obtain free TLS certs from Let's Encrypt, read this
|
||||
[guide](https://github.com/stefanprodan/istio-gke) on how to configure cert-manager to secure Istio with TLS certificates.
|
||||
|
||||
If you're using a local cluster via kind/k3s you can port forward the Envoy LoadBalancer service:
|
||||
```bash
|
||||
kubectl port-forward -n projectcontour svc/envoy 8080:80
|
||||
kubectl port-forward -n istio-ingress svc/gateway-istio 8080:80
|
||||
```
|
||||
|
||||
Now you can access podinfo via `curl -H "Host: localproject.contour.io" localhost:8080`
|
||||
Now you can access podinfo via `curl -H "Host: www.example.com" localhost:8080`
|
||||
|
||||
## Automated canary promotion
|
||||
|
||||
|
|
@ -390,7 +406,7 @@ For more information you can read the [deployment strategies docs](../usage/depl
|
|||
|
||||
> **Note:** The implementation must have support for the [`ResponseHeaderModifier`](https://github.com/kubernetes-sigs/gateway-api/blob/3d22aa5a08413222cb79e6b2e245870360434614/apis/v1beta1/httproute_types.go#L651) API.
|
||||
|
||||
Create a canary custom resource \(replace localproject.contour.io with your own domain\):
|
||||
Create a canary custom resource \(replace www.example.com with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
|
|
@ -419,11 +435,11 @@ spec:
|
|||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
|
|
@ -473,7 +489,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
Save the above resource as podinfo-canary-session-affinity.yaml and then apply it:
|
||||
|
|
@ -489,7 +505,7 @@ kubectl -n test set image deployment/podinfo \
|
|||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1
|
||||
```
|
||||
|
||||
You can load `localproject.contour.io` in your browser and refresh it until you see the requests being served by `podinfo:6.0.1`.
|
||||
You can load `www.example.com` in your browser and refresh it until you see the requests being served by `podinfo:6.0.1`.
|
||||
All subsequent requests after that will be served by `podinfo:6.0.1` and not `podinfo:6.0.0` because of the session affinity
|
||||
configured by Flagger in the HTTPRoute object.
|
||||
|
||||
|
|
@ -499,7 +515,7 @@ Besides weighted routing, Flagger can be configured to route traffic to the cana
|
|||
|
||||

|
||||
|
||||
Create a canary custom resource \(replace "localproject.contour.io" with your own domain\):
|
||||
Create a canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
|
|
@ -528,11 +544,11 @@ spec:
|
|||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
|
|
@ -575,7 +591,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io -H 'X-Canary: insider' http://envoy.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com -H 'X-Canary: insider' http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
The above configuration will run an analysis for ten minutes targeting those users that have an insider cookie.
|
||||
|
|
@ -654,11 +670,11 @@ spec:
|
|||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval
|
||||
interval: 1m
|
||||
|
|
@ -683,7 +699,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
With the above configuration, Flagger will run a canary release with the following steps:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ chmod +x ${CODEGEN_PKG}/generate-internal-groups.sh
|
|||
|
||||
${CODEGEN_PKG}/generate-groups.sh client,deepcopy,informer,lister \
|
||||
github.com/fluxcd/flagger/pkg/client github.com/fluxcd/flagger/pkg/apis \
|
||||
"flagger:v1beta1 appmesh:v1beta2 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 smi:v1alpha2 smi:v1alpha3 gloo/gloo:v1 gloo/gateway:v1 projectcontour:v1 traefik:v1alpha1 kuma:v1alpha1 gatewayapi:v1alpha2 gatewayapi:v1beta1 keda:v1alpha1 apisix:v2" \
|
||||
"flagger:v1beta1 appmesh:v1beta2 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 smi:v1alpha2 smi:v1alpha3 gloo/gloo:v1 gloo/gateway:v1 projectcontour:v1 traefik:v1alpha1 kuma:v1alpha1 gatewayapi:v1beta1 gatewayapi:v1 keda:v1alpha1 apisix:v2" \
|
||||
--output-base "${TEMP_DIR}" \
|
||||
--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt
|
||||
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@ spec:
|
|||
args:
|
||||
- -log-level=info
|
||||
- -include-label-prefix=app.kubernetes.io
|
||||
- -mesh-provider=gatewayapi:v1beta1
|
||||
- -mesh-provider=gatewayapi:v1
|
||||
- -metrics-server=http://flagger-prometheus:9090
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
// Package v1 contains API Schema definitions for the
|
||||
// gateway.networking.k8s.io API group.
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
package v1
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,12 +1,9 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
|
@ -14,7 +11,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
// LocalObjectReference identifies an API object within the namespace of the
|
||||
// referrer.
|
||||
|
|
@ -25,8 +22,8 @@ package v1alpha2
|
|||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
type LocalObjectReference struct {
|
||||
// Group is the group of the referent. For example, "networking.k8s.io".
|
||||
// When unspecified (empty string), core API group is inferred.
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When unspecified or empty string, core API group is inferred.
|
||||
Group Group `json:"group"`
|
||||
|
||||
// Kind is kind of the referent. For example "HTTPRoute" or "Service".
|
||||
|
|
@ -46,8 +43,8 @@ type LocalObjectReference struct {
|
|||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
type SecretObjectReference struct {
|
||||
// Group is the group of the referent. For example, "networking.k8s.io".
|
||||
// When unspecified (empty string), core API group is inferred.
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When unspecified or empty string, core API group is inferred.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=""
|
||||
|
|
@ -65,9 +62,9 @@ type SecretObjectReference struct {
|
|||
// Namespace is the namespace of the backend. When unspecified, the local
|
||||
// namespace is inferred.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferencePolicy object
|
||||
// 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 ReferencePolicy documentation
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
//
|
||||
// Support: Core
|
||||
|
|
@ -80,9 +77,9 @@ type SecretObjectReference struct {
|
|||
// specific to BackendRef. It includes a few additional fields and features
|
||||
// than a regular ObjectReference.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferencePolicy object
|
||||
// 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 ReferencePolicy documentation
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
//
|
||||
// The API object must be valid in the cluster; the Group and Kind must
|
||||
|
|
@ -92,14 +89,15 @@ type SecretObjectReference struct {
|
|||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
type BackendObjectReference struct {
|
||||
// Group is the group of the referent. For example, "networking.k8s.io".
|
||||
// When unspecified (empty string), core API group is inferred.
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When unspecified or empty string, core API group is inferred.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=""
|
||||
Group *Group `json:"group,omitempty"`
|
||||
|
||||
// Kind is kind of the referent. For example "HTTPRoute" or "Service".
|
||||
// Defaults to "Service" when not specified.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Service
|
||||
|
|
@ -111,9 +109,9 @@ type BackendObjectReference struct {
|
|||
// Namespace is the namespace of the backend. When unspecified, the local
|
||||
// namespace is inferred.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferencePolicy object
|
||||
// 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 ReferencePolicy documentation
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
//
|
||||
// Support: Core
|
||||
|
|
@ -122,7 +120,8 @@ type BackendObjectReference struct {
|
|||
Namespace *Namespace `json:"namespace,omitempty"`
|
||||
|
||||
// Port specifies the destination port number to use for this resource.
|
||||
// Port is required when the referent is a Kubernetes Service.
|
||||
// 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.
|
||||
//
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/flagger/pkg/apis/gatewayapi"
|
||||
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
// SchemeGroupVersion is the identifier for the API which includes
|
||||
// the name of the group and the version of the API
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: gatewayapi.GroupName, Version: "v1alpha2"}
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: gatewayapi.GroupName, Version: "v1"}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
|
|
@ -0,0 +1,738 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ParentReference identifies an API object (usually a Gateway) that can be considered
|
||||
// a parent of this resource (usually a route). There are two kinds of parent resources
|
||||
// with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
//
|
||||
// This API may be extended in the future to support additional kinds of parent
|
||||
// resources.
|
||||
//
|
||||
// The API object must be valid in the cluster; the Group and Kind must
|
||||
// be registered in the cluster for this reference to be valid.
|
||||
type ParentReference struct {
|
||||
// Group is the group of the referent.
|
||||
// When unspecified, "gateway.networking.k8s.io" is inferred.
|
||||
// To set the core API group (such as for a "Service" kind referent),
|
||||
// Group must be explicitly set to "" (empty string).
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +kubebuilder:default=gateway.networking.k8s.io
|
||||
// +optional
|
||||
Group *Group `json:"group,omitempty"`
|
||||
|
||||
// Kind is kind of the referent.
|
||||
//
|
||||
// There are two kinds of parent resources with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
//
|
||||
// Support for other resources is Implementation-Specific.
|
||||
//
|
||||
// +kubebuilder:default=Gateway
|
||||
// +optional
|
||||
Kind *Kind `json:"kind,omitempty"`
|
||||
|
||||
// Namespace is the namespace of the referent. When unspecified, this refers
|
||||
// to the local namespace of the Route.
|
||||
//
|
||||
// Note that there are specific rules for ParentRefs which cross namespace
|
||||
// boundaries. Cross-namespace references are only valid if they are explicitly
|
||||
// allowed by something in the namespace they are referring to. For example:
|
||||
// Gateway has the AllowedRoutes field, and ReferenceGrant provides a
|
||||
// generic way to enable any other kind of cross-namespace reference.
|
||||
//
|
||||
// <gateway:experimental:description>
|
||||
// ParentRefs from a Route to a Service in the same namespace are "producer"
|
||||
// routes, which apply default routing rules to inbound connections from
|
||||
// any namespace to the Service.
|
||||
//
|
||||
// ParentRefs from a Route to a Service in a different namespace are
|
||||
// "consumer" routes, and these routing rules are only applied to outbound
|
||||
// connections originating from the same namespace as the Route, for which
|
||||
// the intended destination of the connections are a Service targeted as a
|
||||
// ParentRef of the Route.
|
||||
// </gateway:experimental:description>
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
Namespace *Namespace `json:"namespace,omitempty"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
//
|
||||
// Support: Core
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// SectionName is the name of a section within the target resource. In the
|
||||
// following resources, SectionName is interpreted as the following:
|
||||
//
|
||||
// * Gateway: Listener Name. When both Port (experimental) and SectionName
|
||||
// are specified, the name and port of the selected listener must match
|
||||
// both specified values.
|
||||
// * Service: Port Name. When both Port (experimental) and SectionName
|
||||
// are specified, the name and port of the selected listener must match
|
||||
// both specified values. Note that attaching Routes to Services as Parents
|
||||
// is part of experimental Mesh support and is not supported for any other
|
||||
// purpose.
|
||||
//
|
||||
// Implementations MAY choose to support attaching Routes to other resources.
|
||||
// If that is the case, they MUST clearly document how SectionName is
|
||||
// interpreted.
|
||||
//
|
||||
// When unspecified (empty string), this will reference the entire resource.
|
||||
// For the purpose of status, an attachment is considered successful if at
|
||||
// least one section in the parent resource accepts it. For example, Gateway
|
||||
// listeners can restrict which Routes can attach to them by Route kind,
|
||||
// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from
|
||||
// the referencing Route, the Route MUST be considered successfully
|
||||
// attached. If no Gateway listeners accept attachment from this Route, the
|
||||
// Route MUST be considered detached from the Gateway.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
SectionName *SectionName `json:"sectionName,omitempty"`
|
||||
|
||||
// Port is the network port this Route targets. It can be interpreted
|
||||
// differently based on the type of parent resource.
|
||||
//
|
||||
// When the parent resource is a Gateway, this targets all listeners
|
||||
// listening on the specified port that also support this kind of Route(and
|
||||
// select this Route). It's not recommended to set `Port` unless the
|
||||
// networking behaviors specified in a Route must apply to a specific port
|
||||
// as opposed to a listener(s) whose port(s) may be changed. When both Port
|
||||
// and SectionName are specified, the name and port of the selected listener
|
||||
// must match both specified values.
|
||||
//
|
||||
// <gateway:experimental:description>
|
||||
// When the parent resource is a Service, this targets a specific port in the
|
||||
// Service spec. When both Port (experimental) and SectionName are specified,
|
||||
// the name and port of the selected port must match both specified values.
|
||||
// </gateway:experimental:description>
|
||||
//
|
||||
// Implementations MAY choose to support other parent resources.
|
||||
// Implementations supporting other types of parent resources MUST clearly
|
||||
// document how/if Port is interpreted.
|
||||
//
|
||||
// For the purpose of status, an attachment is considered successful as
|
||||
// long as the parent resource accepts it partially. For example, Gateway
|
||||
// listeners can restrict which Routes can attach to them by Route kind,
|
||||
// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment
|
||||
// from the referencing Route, the Route MUST be considered successfully
|
||||
// attached. If no Gateway listeners accept attachment from this Route,
|
||||
// the Route MUST be considered detached from the Gateway.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
Port *PortNumber `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// CommonRouteSpec defines the common attributes that all Routes MUST include
|
||||
// within their spec.
|
||||
type CommonRouteSpec struct {
|
||||
// ParentRefs references the resources (usually Gateways) that a Route wants
|
||||
// to be attached to. Note that the referenced parent resource needs to
|
||||
// allow this for the attachment to be complete. For Gateways, that means
|
||||
// the Gateway needs to allow attachment from Routes of this kind and
|
||||
// namespace. For Services, that means the Service must either be in the same
|
||||
// namespace for a "producer" route, or the mesh implementation must support
|
||||
// and allow "consumer" routes for the referenced Service. ReferenceGrant is
|
||||
// not applicable for governing ParentRefs to Services - it is not possible to
|
||||
// create a "producer" route for a Service in a different namespace from the
|
||||
// Route.
|
||||
//
|
||||
// There are two kinds of parent resources with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// <gateway:experimental:description>
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
// </gateway:experimental:description>
|
||||
// This API may be extended in the future to support additional kinds of parent
|
||||
// resources.
|
||||
//
|
||||
// ParentRefs must be _distinct_. This means either that:
|
||||
//
|
||||
// * They select different objects. If this is the case, then parentRef
|
||||
// entries are distinct. In terms of fields, this means that the
|
||||
// multi-part key defined by `group`, `kind`, `namespace`, and `name` must
|
||||
// be unique across all parentRef entries in the Route.
|
||||
// * They do not select different objects, but for each optional field used,
|
||||
// each ParentRef that selects the same object must set the same set of
|
||||
// optional fields to different values. If one ParentRef sets a
|
||||
// combination of optional fields, all must set the same combination.
|
||||
//
|
||||
// Some examples:
|
||||
//
|
||||
// * If one ParentRef sets `sectionName`, all ParentRefs referencing the
|
||||
// same object must also set `sectionName`.
|
||||
// * If one ParentRef sets `port`, all ParentRefs referencing the same
|
||||
// object must also set `port`.
|
||||
// * If one ParentRef sets `sectionName` and `port`, all ParentRefs
|
||||
// referencing the same object must also set `sectionName` and `port`.
|
||||
//
|
||||
// It is possible to separately reference multiple distinct objects that may
|
||||
// be collapsed by an implementation. For example, some implementations may
|
||||
// choose to merge compatible Gateway Listeners together. If that is the
|
||||
// case, the list of routes attached to those resources should also be
|
||||
// merged.
|
||||
//
|
||||
// Note that for ParentRefs that cross namespace boundaries, there are specific
|
||||
// rules. Cross-namespace references are only valid if they are explicitly
|
||||
// allowed by something in the namespace they are referring to. For example,
|
||||
// Gateway has the AllowedRoutes field, and ReferenceGrant provides a
|
||||
// generic way to enable other kinds of cross-namespace reference.
|
||||
//
|
||||
// <gateway:experimental:description>
|
||||
// ParentRefs from a Route to a Service in the same namespace are "producer"
|
||||
// routes, which apply default routing rules to inbound connections from
|
||||
// any namespace to the Service.
|
||||
//
|
||||
// ParentRefs from a Route to a Service in a different namespace are
|
||||
// "consumer" routes, and these routing rules are only applied to outbound
|
||||
// connections originating from the same namespace as the Route, for which
|
||||
// the intended destination of the connections are a Service targeted as a
|
||||
// ParentRef of the Route.
|
||||
// </gateway:experimental:description>
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
// <gateway:standard:validation:XValidation:message="sectionName must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '')) : true))">
|
||||
// <gateway:standard:validation:XValidation:message="sectionName must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName))))">
|
||||
// <gateway:experimental:validation:XValidation:message="sectionName or port must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))">
|
||||
// <gateway:experimental:validation:XValidation:message="sectionName or port must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port))))">
|
||||
ParentRefs []ParentReference `json:"parentRefs,omitempty"`
|
||||
}
|
||||
|
||||
// PortNumber defines a network port.
|
||||
//
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
type PortNumber int32
|
||||
|
||||
// BackendRef defines how a Route should forward a request to a Kubernetes
|
||||
// resource.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// <gateway:experimental:description>
|
||||
//
|
||||
// When the BackendRef points to a Kubernetes Service, implementations SHOULD
|
||||
// honor the appProtocol field if it is set for the target Service Port.
|
||||
//
|
||||
// Implementations supporting appProtocol SHOULD recognize the Kubernetes
|
||||
// Standard Application Protocols defined in KEP-3726.
|
||||
//
|
||||
// If a Service appProtocol isn't specified, an implementation MAY infer the
|
||||
// backend protocol through its own means. Implementations MAY infer the
|
||||
// protocol from the Route type referring to the backend Service.
|
||||
//
|
||||
// If a Route is not able to send traffic to the backend using the specified
|
||||
// protocol then the backend is considered invalid. Implementations MUST set the
|
||||
// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason.
|
||||
//
|
||||
// </gateway:experimental:description>
|
||||
//
|
||||
// Note that when the BackendTLSPolicy object is enabled by the implementation,
|
||||
// there are some extra rules about validity to consider here. See the fields
|
||||
// where this struct is used for more information about the exact behavior.
|
||||
type BackendRef struct {
|
||||
// BackendObjectReference references a Kubernetes object.
|
||||
BackendObjectReference `json:",inline"`
|
||||
|
||||
// Weight specifies the proportion of requests forwarded to the referenced
|
||||
// backend. This is computed as weight/(sum of all weights in this
|
||||
// BackendRefs list). For non-zero values, there may be some epsilon from
|
||||
// the exact proportion defined here depending on the precision an
|
||||
// implementation supports. Weight is not a percentage and the sum of
|
||||
// weights does not need to equal 100.
|
||||
//
|
||||
// If only one backend is specified and it has a weight greater than 0, 100%
|
||||
// of the traffic is forwarded to that backend. If weight is set to 0, no
|
||||
// traffic should be forwarded for this entry. If unspecified, weight
|
||||
// defaults to 1.
|
||||
//
|
||||
// Support for this field varies based on the context where used.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=1
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
// +kubebuilder:validation:Maximum=1000000
|
||||
Weight *int32 `json:"weight,omitempty"`
|
||||
}
|
||||
|
||||
// RouteConditionType is a type of condition for a route.
|
||||
type RouteConditionType string
|
||||
|
||||
// RouteConditionReason is a reason for a route condition.
|
||||
type RouteConditionReason string
|
||||
|
||||
const (
|
||||
// This condition indicates whether the route has been accepted or rejected
|
||||
// by a Gateway, and why.
|
||||
//
|
||||
// Possible reasons for this condition to be True are:
|
||||
//
|
||||
// * "Accepted"
|
||||
//
|
||||
// Possible reasons for this condition to be False are:
|
||||
//
|
||||
// * "NotAllowedByListeners"
|
||||
// * "NoMatchingListenerHostname"
|
||||
// * "NoMatchingParent"
|
||||
// * "UnsupportedValue"
|
||||
//
|
||||
// Possible reasons for this condition to be Unknown are:
|
||||
//
|
||||
// * "Pending"
|
||||
//
|
||||
// Controllers may raise this condition with other reasons,
|
||||
// but should prefer to use the reasons listed above to improve
|
||||
// interoperability.
|
||||
RouteConditionAccepted RouteConditionType = "Accepted"
|
||||
|
||||
// This reason is used with the "Accepted" condition when the Route has been
|
||||
// accepted by the Gateway.
|
||||
RouteReasonAccepted RouteConditionReason = "Accepted"
|
||||
|
||||
// This reason is used with the "Accepted" condition when the route has not
|
||||
// been accepted by a Gateway because the Gateway has no Listener whose
|
||||
// allowedRoutes criteria permit the route
|
||||
RouteReasonNotAllowedByListeners RouteConditionReason = "NotAllowedByListeners"
|
||||
|
||||
// This reason is used with the "Accepted" condition when the Gateway has no
|
||||
// compatible Listeners whose Hostname matches the route
|
||||
RouteReasonNoMatchingListenerHostname RouteConditionReason = "NoMatchingListenerHostname"
|
||||
|
||||
// This reason is used with the "Accepted" condition when there are
|
||||
// no matching Parents. In the case of Gateways, this can occur when
|
||||
// a Route ParentRef specifies a Port and/or SectionName that does not
|
||||
// match any Listeners in the Gateway.
|
||||
RouteReasonNoMatchingParent RouteConditionReason = "NoMatchingParent"
|
||||
|
||||
// This reason is used with the "Accepted" condition when a value for an Enum
|
||||
// is not recognized.
|
||||
RouteReasonUnsupportedValue RouteConditionReason = "UnsupportedValue"
|
||||
|
||||
// This reason is used with the "Accepted" when a controller has not yet
|
||||
// reconciled the route.
|
||||
RouteReasonPending RouteConditionReason = "Pending"
|
||||
|
||||
// This reason is used with the "Accepted" condition when there
|
||||
// are incompatible filters present on a route rule (for example if
|
||||
// the URLRewrite and RequestRedirect are both present on an HTTPRoute).
|
||||
RouteReasonIncompatibleFilters RouteConditionReason = "IncompatibleFilters"
|
||||
)
|
||||
|
||||
const (
|
||||
// This condition indicates whether the controller was able to resolve all
|
||||
// the object references for the Route.
|
||||
//
|
||||
// Possible reasons for this condition to be True are:
|
||||
//
|
||||
// * "ResolvedRefs"
|
||||
//
|
||||
// Possible reasons for this condition to be False are:
|
||||
//
|
||||
// * "RefNotPermitted"
|
||||
// * "InvalidKind"
|
||||
// * "BackendNotFound"
|
||||
// * "UnsupportedProtocol"
|
||||
//
|
||||
// Controllers may raise this condition with other reasons,
|
||||
// but should prefer to use the reasons listed above to improve
|
||||
// interoperability.
|
||||
RouteConditionResolvedRefs RouteConditionType = "ResolvedRefs"
|
||||
|
||||
// This reason is used with the "ResolvedRefs" condition when the condition
|
||||
// is true.
|
||||
RouteReasonResolvedRefs RouteConditionReason = "ResolvedRefs"
|
||||
|
||||
// This reason is used with the "ResolvedRefs" condition when
|
||||
// one of the Listener's Routes has a BackendRef to an object in
|
||||
// another namespace, where the object in the other namespace does
|
||||
// not have a ReferenceGrant explicitly allowing the reference.
|
||||
RouteReasonRefNotPermitted RouteConditionReason = "RefNotPermitted"
|
||||
|
||||
// This reason is used with the "ResolvedRefs" condition when
|
||||
// one of the Route's rules has a reference to an unknown or unsupported
|
||||
// Group and/or Kind.
|
||||
RouteReasonInvalidKind RouteConditionReason = "InvalidKind"
|
||||
|
||||
// This reason is used with the "ResolvedRefs" condition when one of the
|
||||
// Route's rules has a reference to a resource that does not exist.
|
||||
RouteReasonBackendNotFound RouteConditionReason = "BackendNotFound"
|
||||
|
||||
// This reason is used with the "ResolvedRefs" condition when one of the
|
||||
// Route's rules has a reference to a resource with an app protocol that
|
||||
// is not supported by this implementation.
|
||||
RouteReasonUnsupportedProtocol RouteConditionReason = "UnsupportedProtocol"
|
||||
)
|
||||
|
||||
const (
|
||||
// This condition indicates that the Route contains a combination of both
|
||||
// valid and invalid rules.
|
||||
//
|
||||
// When this happens, implementations MUST take one of the following
|
||||
// approaches:
|
||||
//
|
||||
// 1) Drop Rule(s): With this approach, implementations will drop the
|
||||
// invalid Route Rule(s) until they are fully valid again. The message
|
||||
// for this condition MUST start with the prefix "Dropped Rule" and
|
||||
// include information about which Rules have been dropped. In this
|
||||
// state, the "Accepted" condition MUST be set to "True" with the latest
|
||||
// generation of the resource.
|
||||
// 2) Fall Back: With this approach, implementations will fall back to the
|
||||
// last known good state of the entire Route. The message for this
|
||||
// condition MUST start with the prefix "Fall Back" and include
|
||||
// information about why the current Rule(s) are invalid. To represent
|
||||
// this, the "Accepted" condition MUST be set to "True" with the
|
||||
// generation of the last known good state of the resource.
|
||||
//
|
||||
// Reverting to the last known good state should only be done by
|
||||
// implementations that have a means of restoring that state if/when they
|
||||
// are restarted.
|
||||
//
|
||||
// This condition MUST NOT be set if a Route is fully valid, fully invalid,
|
||||
// or not accepted. By extension, that means that this condition MUST only
|
||||
// be set when it is "True".
|
||||
//
|
||||
// Possible reasons for this condition to be True are:
|
||||
//
|
||||
// * "UnsupportedValue"
|
||||
//
|
||||
// Controllers may raise this condition with other reasons, but should
|
||||
// prefer to use the reasons listed above to improve interoperability.
|
||||
RouteConditionPartiallyInvalid RouteConditionType = "PartiallyInvalid"
|
||||
)
|
||||
|
||||
// RouteParentStatus describes the status of a route with respect to an
|
||||
// associated Parent.
|
||||
type RouteParentStatus struct {
|
||||
// ParentRef corresponds with a ParentRef in the spec that this
|
||||
// RouteParentStatus struct describes the status of.
|
||||
ParentRef ParentReference `json:"parentRef"`
|
||||
|
||||
// ControllerName is a domain/path string that indicates the name of the
|
||||
// controller that wrote this status. This corresponds with the
|
||||
// controllerName field on GatewayClass.
|
||||
//
|
||||
// Example: "example.net/gateway-controller".
|
||||
//
|
||||
// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are
|
||||
// valid Kubernetes names
|
||||
// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).
|
||||
//
|
||||
// Controllers MUST populate this field when writing status. Controllers should ensure that
|
||||
// entries to status populated with their ControllerName are cleaned up when they are no
|
||||
// longer necessary.
|
||||
ControllerName GatewayController `json:"controllerName"`
|
||||
|
||||
// Conditions describes the status of the route with respect to the Gateway.
|
||||
// Note that the route's availability is also subject to the Gateway's own
|
||||
// status conditions and listener status.
|
||||
//
|
||||
// If the Route's ParentRef specifies an existing Gateway that supports
|
||||
// Routes of this kind AND that Gateway's controller has sufficient access,
|
||||
// then that Gateway's controller MUST set the "Accepted" condition on the
|
||||
// Route, to indicate whether the route has been accepted or rejected by the
|
||||
// Gateway, and why.
|
||||
//
|
||||
// A Route MUST be considered "Accepted" if at least one of the Route's
|
||||
// rules is implemented by the Gateway.
|
||||
//
|
||||
// There are a number of cases where the "Accepted" condition may not be set
|
||||
// due to lack of controller visibility, that includes when:
|
||||
//
|
||||
// * The Route refers to a non-existent parent.
|
||||
// * The Route is of a type that the controller does not support.
|
||||
// * The Route is in a namespace the controller does not have access to.
|
||||
//
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
// +kubebuilder:validation:MaxItems=8
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// RouteStatus defines the common attributes that all Routes MUST include within
|
||||
// their status.
|
||||
type RouteStatus struct {
|
||||
// Parents is a list of parent resources (usually Gateways) that are
|
||||
// associated with the route, and the status of the route with respect to
|
||||
// each parent. When this route attaches to a parent, the controller that
|
||||
// manages the parent must add an entry to this list when the controller
|
||||
// first sees the route and should update the entry as appropriate when the
|
||||
// route or gateway is modified.
|
||||
//
|
||||
// Note that parent references that cannot be resolved by an implementation
|
||||
// of this API will not be added to this list. Implementations of this API
|
||||
// can only populate Route status for the Gateways/parent resources they are
|
||||
// responsible for.
|
||||
//
|
||||
// A maximum of 32 Gateways will be represented in this list. An empty list
|
||||
// means the route has not been attached to any Gateway.
|
||||
//
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
Parents []RouteParentStatus `json:"parents"`
|
||||
}
|
||||
|
||||
// Hostname is the fully qualified domain name of a network host. This matches
|
||||
// the RFC 1123 definition of a hostname with 2 notable exceptions:
|
||||
//
|
||||
// 1. IPs are not allowed.
|
||||
// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard
|
||||
// label must appear by itself as the first label.
|
||||
//
|
||||
// Hostname can be "precise" which is a domain name without the terminating
|
||||
// dot of a network host (e.g. "foo.example.com") or "wildcard", which is a
|
||||
// domain name prefixed with a single wildcard label (e.g. `*.example.com`).
|
||||
//
|
||||
// Note that as per RFC1035 and RFC1123, a *label* must consist of lower case
|
||||
// alphanumeric characters or '-', and must start and end with an alphanumeric
|
||||
// character. No other punctuation is allowed.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type Hostname string
|
||||
|
||||
// PreciseHostname is the fully qualified domain name of a network host. This
|
||||
// matches the RFC 1123 definition of a hostname with 1 notable exception that
|
||||
// numeric IP addresses are not allowed.
|
||||
//
|
||||
// Note that as per RFC1035 and RFC1123, a *label* must consist of lower case
|
||||
// alphanumeric characters or '-', and must start and end with an alphanumeric
|
||||
// character. No other punctuation is allowed.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type PreciseHostname string
|
||||
|
||||
// Group refers to a Kubernetes Group. It must either be an empty string or a
|
||||
// RFC 1123 subdomain.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "" - empty string implies core Kubernetes API group
|
||||
// * "gateway.networking.k8s.io"
|
||||
// * "foo.example.com"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com/bar" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type Group string
|
||||
|
||||
// Kind refers to a Kubernetes Kind.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "Service"
|
||||
// * "HTTPRoute"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "invalid/kind" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Pattern=`^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$`
|
||||
type Kind string
|
||||
|
||||
// ObjectName refers to the name of a Kubernetes object.
|
||||
// Object names can have a variety of forms, including RFC1123 subdomains,
|
||||
// RFC 1123 labels, or RFC 1035 labels.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
type ObjectName string
|
||||
|
||||
// Namespace refers to a Kubernetes namespace. It must be a RFC 1123 label.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L187
|
||||
//
|
||||
// This is used for Namespace name validation here:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/api/validation/generic.go#L63
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com" - "." is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
type Namespace string
|
||||
|
||||
// SectionName is the name of a section in a Kubernetes resource.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example.com"
|
||||
// * "foo.example.com"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com/bar" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
type SectionName string
|
||||
|
||||
// GatewayController is the name of a Gateway API controller. It must be a
|
||||
// domain prefixed path.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example.com/bar"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com" - must include path
|
||||
// * "foo.example.com" - must include path
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
|
||||
type GatewayController string
|
||||
|
||||
// AnnotationKey is the key of an annotation in Gateway API. This is used for
|
||||
// validation of maps such as TLS options. This matches the Kubernetes
|
||||
// "qualified name" validation that is used for annotations and other common
|
||||
// values.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * example
|
||||
// * example.com
|
||||
// * example.com/path
|
||||
// * example.com/path.html
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * example~ - "~" is an invalid character
|
||||
// * example.com. - can not start or end with "."
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/?)*$`
|
||||
type AnnotationKey string
|
||||
|
||||
// AnnotationValue is the value of an annotation in Gateway API. This is used
|
||||
// for validation of maps such as TLS options. This roughly matches Kubernetes
|
||||
// annotation validation, although the length validation in that case is based
|
||||
// on the entire size of the annotations struct.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=0
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
type AnnotationValue string
|
||||
|
||||
// AddressType defines how a network address is represented as a text string.
|
||||
// This may take two possible forms:
|
||||
//
|
||||
// * A predefined CamelCase string identifier (currently limited to `IPAddress` or `Hostname`)
|
||||
// * A domain-prefixed string identifier (like `acme.io/CustomAddressType`)
|
||||
//
|
||||
// Values `IPAddress` and `Hostname` have Extended support.
|
||||
//
|
||||
// The `NamedAddress` value has been deprecated in favor of implementation
|
||||
// specific domain-prefixed strings.
|
||||
//
|
||||
// All other values, including domain-prefixed values have Implementation-specific support,
|
||||
// which are used in implementation-specific behaviors. Support for additional
|
||||
// predefined CamelCase identifiers may be added in future releases.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
|
||||
type AddressType string
|
||||
|
||||
// HeaderName is the name of a header or query parameter.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=256
|
||||
// +kubebuilder:validation:Pattern=`^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$`
|
||||
// +k8s:deepcopy-gen=false
|
||||
type HeaderName string
|
||||
|
||||
// Duration is a string value representing a duration in time. The format is as specified
|
||||
// in GEP-2257, a strict subset of the syntax parsed by Golang time.ParseDuration.
|
||||
//
|
||||
// +kubebuilder:validation:Pattern=`^([0-9]{1,5}(h|m|s|ms)){1,4}$`
|
||||
type Duration string
|
||||
|
||||
const (
|
||||
// A textual representation of a numeric IP address. IPv4
|
||||
// addresses must be in dotted-decimal form. IPv6 addresses
|
||||
// must be in a standard IPv6 text representation
|
||||
// (see [RFC 5952](https://tools.ietf.org/html/rfc5952)).
|
||||
//
|
||||
// This type is intended for specific addresses. Address ranges are not
|
||||
// supported (e.g. you can not use a CIDR range like 127.0.0.0/24 as an
|
||||
// IPAddress).
|
||||
//
|
||||
// Support: Extended
|
||||
IPAddressType AddressType = "IPAddress"
|
||||
|
||||
// A Hostname represents a DNS based ingress point. This is similar to the
|
||||
// corresponding hostname field in Kubernetes load balancer status. For
|
||||
// example, this concept may be used for cloud load balancers where a DNS
|
||||
// name is used to expose a load balancer.
|
||||
//
|
||||
// Support: Extended
|
||||
HostnameAddressType AddressType = "Hostname"
|
||||
|
||||
// A NamedAddress provides a way to reference a specific IP address by name.
|
||||
// For example, this may be a name or other unique identifier that refers
|
||||
// to a resource on a cloud provider such as a static IP.
|
||||
//
|
||||
// The `NamedAddress` type has been deprecated in favor of implementation
|
||||
// specific domain-prefixed strings.
|
||||
//
|
||||
// Support: Implementation-specific
|
||||
NamedAddressType AddressType = "NamedAddress"
|
||||
)
|
||||
|
|
@ -19,64 +19,13 @@ limitations under the License.
|
|||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AddressMatch) DeepCopyInto(out *AddressMatch) {
|
||||
*out = *in
|
||||
if in.Type != nil {
|
||||
in, out := &in.Type, &out.Type
|
||||
*out = new(AddressType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressMatch.
|
||||
func (in *AddressMatch) DeepCopy() *AddressMatch {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AddressMatch)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AddressRouteMatches) DeepCopyInto(out *AddressRouteMatches) {
|
||||
*out = *in
|
||||
if in.SourceAddresses != nil {
|
||||
in, out := &in.SourceAddresses, &out.SourceAddresses
|
||||
*out = make([]AddressMatch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.DestinationAddresses != nil {
|
||||
in, out := &in.DestinationAddresses, &out.DestinationAddresses
|
||||
*out = make([]AddressMatch, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressRouteMatches.
|
||||
func (in *AddressRouteMatches) DeepCopy() *AddressRouteMatches {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AddressRouteMatches)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BackendObjectReference) DeepCopyInto(out *BackendObjectReference) {
|
||||
*out = *in
|
||||
|
|
@ -198,6 +147,37 @@ func (in *HTTPHeader) DeepCopy() *HTTPHeader {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPHeaderFilter) DeepCopyInto(out *HTTPHeaderFilter) {
|
||||
*out = *in
|
||||
if in.Set != nil {
|
||||
in, out := &in.Set, &out.Set
|
||||
*out = make([]HTTPHeader, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Add != nil {
|
||||
in, out := &in.Add, &out.Add
|
||||
*out = make([]HTTPHeader, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Remove != nil {
|
||||
in, out := &in.Remove, &out.Remove
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeaderFilter.
|
||||
func (in *HTTPHeaderFilter) DeepCopy() *HTTPHeaderFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPHeaderFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPHeaderMatch) DeepCopyInto(out *HTTPHeaderMatch) {
|
||||
*out = *in
|
||||
|
|
@ -248,6 +228,16 @@ func (in *HTTPPathMatch) DeepCopy() *HTTPPathMatch {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPPathModifier) DeepCopyInto(out *HTTPPathModifier) {
|
||||
*out = *in
|
||||
if in.ReplaceFullPath != nil {
|
||||
in, out := &in.ReplaceFullPath, &out.ReplaceFullPath
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.ReplacePrefixMatch != nil {
|
||||
in, out := &in.ReplacePrefixMatch, &out.ReplacePrefixMatch
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -282,37 +272,6 @@ func (in *HTTPQueryParamMatch) DeepCopy() *HTTPQueryParamMatch {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPRequestHeaderFilter) DeepCopyInto(out *HTTPRequestHeaderFilter) {
|
||||
*out = *in
|
||||
if in.Set != nil {
|
||||
in, out := &in.Set, &out.Set
|
||||
*out = make([]HTTPHeader, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Add != nil {
|
||||
in, out := &in.Add, &out.Add
|
||||
*out = make([]HTTPHeader, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Remove != nil {
|
||||
in, out := &in.Remove, &out.Remove
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestHeaderFilter.
|
||||
func (in *HTTPRequestHeaderFilter) DeepCopy() *HTTPRequestHeaderFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPRequestHeaderFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPRequestMirrorFilter) DeepCopyInto(out *HTTPRequestMirrorFilter) {
|
||||
*out = *in
|
||||
|
|
@ -346,7 +305,7 @@ func (in *HTTPRequestRedirectFilter) DeepCopyInto(out *HTTPRequestRedirectFilter
|
|||
if in.Path != nil {
|
||||
in, out := &in.Path, &out.Path
|
||||
*out = new(HTTPPathModifier)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
|
|
@ -377,6 +336,7 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
|
|||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +363,12 @@ func (in *HTTPRouteFilter) DeepCopyInto(out *HTTPRouteFilter) {
|
|||
*out = *in
|
||||
if in.RequestHeaderModifier != nil {
|
||||
in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier
|
||||
*out = new(HTTPRequestHeaderFilter)
|
||||
*out = new(HTTPHeaderFilter)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ResponseHeaderModifier != nil {
|
||||
in, out := &in.ResponseHeaderModifier, &out.ResponseHeaderModifier
|
||||
*out = new(HTTPHeaderFilter)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.RequestMirror != nil {
|
||||
|
|
@ -536,6 +501,11 @@ func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Timeouts != nil {
|
||||
in, out := &in.Timeouts, &out.Timeouts
|
||||
*out = new(HTTPRouteTimeouts)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -595,18 +565,44 @@ func (in *HTTPRouteStatus) DeepCopy() *HTTPRouteStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPRouteTimeouts) DeepCopyInto(out *HTTPRouteTimeouts) {
|
||||
*out = *in
|
||||
if in.Request != nil {
|
||||
in, out := &in.Request, &out.Request
|
||||
*out = new(Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.BackendRequest != nil {
|
||||
in, out := &in.BackendRequest, &out.BackendRequest
|
||||
*out = new(Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteTimeouts.
|
||||
func (in *HTTPRouteTimeouts) DeepCopy() *HTTPRouteTimeouts {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPRouteTimeouts)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPURLRewriteFilter) DeepCopyInto(out *HTTPURLRewriteFilter) {
|
||||
*out = *in
|
||||
if in.Hostname != nil {
|
||||
in, out := &in.Hostname, &out.Hostname
|
||||
*out = new(Hostname)
|
||||
*out = new(PreciseHostname)
|
||||
**out = **in
|
||||
}
|
||||
if in.Path != nil {
|
||||
in, out := &in.Path, &out.Path
|
||||
*out = new(HTTPPathModifier)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -660,6 +656,11 @@ func (in *ParentReference) DeepCopyInto(out *ParentReference) {
|
|||
*out = new(SectionName)
|
||||
**out = **in
|
||||
}
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
*out = new(PortNumber)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -679,7 +680,7 @@ func (in *RouteParentStatus) DeepCopyInto(out *RouteParentStatus) {
|
|||
in.ParentRef.DeepCopyInto(&out.ParentRef)
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
*out = make([]metav1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
// +k8s:deepcopy-gen=package
|
||||
|
||||
// Package v1alpha2 contains API Schema definitions for the
|
||||
// gateway.networking.k8s.io API group.
|
||||
|
||||
package v1alpha2
|
||||
|
|
@ -1,881 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:resource:categories=gateway-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:printcolumn:name="Hostnames",type=string,JSONPath=`.spec.hostnames`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
|
||||
// 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.
|
||||
type HTTPRoute struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the desired state of HTTPRoute.
|
||||
Spec HTTPRouteSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// HTTPRouteList contains a list of HTTPRoute.
|
||||
type HTTPRouteList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HTTPRoute `json:"items"`
|
||||
}
|
||||
|
||||
// HTTPRouteSpec defines the desired state of HTTPRoute
|
||||
type HTTPRouteSpec struct {
|
||||
CommonRouteSpec `json:",inline"`
|
||||
|
||||
// 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:
|
||||
//
|
||||
// 1. IPs are not allowed.
|
||||
// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard
|
||||
// label must appear by itself as the first label.
|
||||
//
|
||||
// If a hostname is specified by both the Listener and HTTPRoute, there
|
||||
// must be at least one intersecting hostname for the HTTPRoute to be
|
||||
// attached to the Listener. For example:
|
||||
//
|
||||
// * A Listener with `test.example.com` as the hostname matches HTTPRoutes
|
||||
// that have either not specified any hostnames, or have specified at
|
||||
// least one of `test.example.com` or `*.example.com`.
|
||||
// * A Listener with `*.example.com` as the hostname matches HTTPRoutes
|
||||
// that have either not specified any hostnames or have specified at least
|
||||
// one hostname that matches the Listener hostname. For example,
|
||||
// `test.example.com` and `*.example.com` would both match. On the other
|
||||
// hand, `example.com` and `test.example.net` would not match.
|
||||
//
|
||||
// If both the Listener and HTTPRoute have specified hostnames, any
|
||||
// HTTPRoute hostnames that do not match the Listener hostname MUST be
|
||||
// ignored. For example, if a Listener specified `*.example.com`, and the
|
||||
// HTTPRoute specified `test.example.com` and `test.example.net`,
|
||||
// `test.example.net` must not be considered for a match.
|
||||
//
|
||||
// If both the Listener and HTTPRoute have specified hostnames, and none
|
||||
// match with the criteria above, then the HTTPRoute is not accepted. The
|
||||
// implementation must raise an 'Accepted' Condition with a status of
|
||||
// `False` in the corresponding RouteParentStatus.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Hostnames []Hostname `json:"hostnames,omitempty"`
|
||||
|
||||
// Rules are a list of HTTP matchers, filters and actions.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
// +kubebuilder:default={{matches: {{path: {type: "PathPrefix", value: "/"}}}}}
|
||||
Rules []HTTPRouteRule `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteRule defines semantics for matching an HTTP request based on
|
||||
// conditions (matches), processing it (filters), and forwarding the request to
|
||||
// an API object (backendRefs).
|
||||
type HTTPRouteRule struct {
|
||||
// Matches define conditions used for matching the rule against incoming
|
||||
// HTTP requests. Each match is independent, i.e. this rule will be matched
|
||||
// if **any** one of the matches is satisfied.
|
||||
//
|
||||
// For example, take the following matches configuration:
|
||||
//
|
||||
// ```
|
||||
// matches:
|
||||
// - path:
|
||||
// value: "/foo"
|
||||
// headers:
|
||||
// - name: "version"
|
||||
// value: "v2"
|
||||
// - path:
|
||||
// value: "/v2/foo"
|
||||
// ```
|
||||
//
|
||||
// For a request to match against this rule, a request must satisfy
|
||||
// EITHER of the two conditions:
|
||||
//
|
||||
// - path prefixed with `/foo` AND contains the header `version: v2`
|
||||
// - path prefix of `/v2/foo`
|
||||
//
|
||||
// See the documentation for HTTPRouteMatch on how to specify multiple
|
||||
// match conditions that should be ANDed together.
|
||||
//
|
||||
// If no matches are specified, the default is a prefix
|
||||
// path match on "/", which has the effect of matching every
|
||||
// HTTP request.
|
||||
//
|
||||
// Proxy or Load Balancer routing configuration generated from HTTPRoutes
|
||||
// MUST prioritize rules based on the following criteria, continuing on
|
||||
// ties. Precedence must be given to the the Rule with the largest number
|
||||
// of:
|
||||
//
|
||||
// * Characters in a matching non-wildcard hostname.
|
||||
// * Characters in a matching hostname.
|
||||
// * Characters in a matching path.
|
||||
// * Header matches.
|
||||
// * Query param matches.
|
||||
//
|
||||
// If ties still exist across multiple Routes, matching precedence MUST be
|
||||
// determined in order of the following criteria, continuing on ties:
|
||||
//
|
||||
// * The oldest Route based on creation timestamp.
|
||||
// * The Route appearing first in alphabetical order by
|
||||
// "{namespace}/{name}".
|
||||
//
|
||||
// If ties still exist within the Route that has been given precedence,
|
||||
// matching precedence MUST be granted to the first matching rule meeting
|
||||
// the above criteria.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=8
|
||||
// +kubebuilder:default={{path:{ type: "PathPrefix", value: "/"}}}
|
||||
Matches []HTTPRouteMatch `json:"matches,omitempty"`
|
||||
|
||||
// Filters define the filters that are applied to requests that match
|
||||
// this rule.
|
||||
//
|
||||
// The effects of ordering of multiple behaviors are currently unspecified.
|
||||
// This can change in the future based on feedback during the alpha stage.
|
||||
//
|
||||
// Conformance-levels at this level are defined based on the type of filter:
|
||||
//
|
||||
// - ALL core filters MUST be supported by all implementations.
|
||||
// - Implementers are encouraged to support extended filters.
|
||||
// - Implementation-specific custom filters have no API guarantees across
|
||||
// implementations.
|
||||
//
|
||||
// Specifying a core filter multiple times has unspecified or custom
|
||||
// conformance.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Filters []HTTPRouteFilter `json:"filters,omitempty"`
|
||||
|
||||
// BackendRefs defines the backend(s) where matching requests should be
|
||||
// sent.
|
||||
|
||||
// If unspecified or invalid (refers to a non-existent resource or a Service
|
||||
// with no endpoints), the rule performs no forwarding. If there are also no
|
||||
// filters specified that would result in a response being sent, a HTTP 503
|
||||
// status code is returned. 503 responses must be sent so that the overall
|
||||
// weight is respected; if an invalid backend is requested to have 80% of
|
||||
// requests, then 80% of requests must get a 503 instead.
|
||||
//
|
||||
// Support: Core for Kubernetes Service
|
||||
// Support: Custom for any other resource
|
||||
//
|
||||
// Support for weight: Core
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
BackendRefs []HTTPBackendRef `json:"backendRefs,omitempty"`
|
||||
}
|
||||
|
||||
// PathMatchType specifies the semantics of how HTTP paths should be compared.
|
||||
// Valid PathMatchType values are:
|
||||
//
|
||||
// * "Exact"
|
||||
// * "PathPrefix"
|
||||
// * "RegularExpression"
|
||||
//
|
||||
// PathPrefix and Exact paths must be syntactically valid:
|
||||
//
|
||||
// - Must begin with the `/` character
|
||||
// - Must not contain consecutive `/` characters (e.g. `/foo///`, `//`).
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Exact;PathPrefix;RegularExpression
|
||||
type PathMatchType string
|
||||
|
||||
const (
|
||||
// Matches the URL path exactly and with case sensitivity.
|
||||
PathMatchExact PathMatchType = "Exact"
|
||||
|
||||
// Matches based on a URL path prefix split by `/`. Matching is
|
||||
// case sensitive and done on a path element by element basis. 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.
|
||||
//
|
||||
// "PathPrefix" is semantically equivalent to the "Prefix" path type in the
|
||||
// Kubernetes Ingress API.
|
||||
PathMatchPathPrefix PathMatchType = "PathPrefix"
|
||||
|
||||
// Matches if the URL path matches the given regular expression with
|
||||
// case sensitivity.
|
||||
//
|
||||
// Since `"RegularExpression"` has custom conformance, implementations
|
||||
// can support POSIX, PCRE, RE2 or any other regular expression dialect.
|
||||
// Please read the implementation's documentation to determine the supported
|
||||
// dialect.
|
||||
PathMatchRegularExpression PathMatchType = "RegularExpression"
|
||||
)
|
||||
|
||||
// HTTPPathMatch describes how to select a HTTP route by matching the HTTP request path.
|
||||
type HTTPPathMatch struct {
|
||||
// Type specifies how to match against the path Value.
|
||||
//
|
||||
// Support: Core (Exact, PathPrefix)
|
||||
//
|
||||
// Support: Custom (RegularExpression)
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=PathPrefix
|
||||
Type *PathMatchType `json:"type,omitempty"`
|
||||
|
||||
// Value of the HTTP path to match against.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default="/"
|
||||
// +kubebuilder:validation:MaxLength=1024
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// HeaderMatchType specifies the semantics of how HTTP header values should be
|
||||
// compared. Valid HeaderMatchType values are:
|
||||
//
|
||||
// * "Exact"
|
||||
// * "RegularExpression"
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Exact;RegularExpression
|
||||
type HeaderMatchType string
|
||||
|
||||
// HeaderMatchType constants.
|
||||
const (
|
||||
HeaderMatchExact HeaderMatchType = "Exact"
|
||||
HeaderMatchRegularExpression HeaderMatchType = "RegularExpression"
|
||||
)
|
||||
|
||||
// HTTPHeaderName is the name of an HTTP header.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "Authorization"
|
||||
// * "Set-Cookie"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo
|
||||
// headers are not currently supported by this type.
|
||||
// - "/invalid" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=256
|
||||
// +kubebuilder:validation:Pattern=`^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$`
|
||||
type HTTPHeaderName string
|
||||
|
||||
// HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request
|
||||
// headers.
|
||||
type HTTPHeaderMatch struct {
|
||||
// Type specifies how to match against the value of the header.
|
||||
//
|
||||
// Support: Core (Exact)
|
||||
//
|
||||
// Support: Custom (RegularExpression)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Exact
|
||||
Type *HeaderMatchType `json:"type,omitempty"`
|
||||
|
||||
// Name is the name of the HTTP Header to be matched. Name matching MUST be
|
||||
// case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
//
|
||||
// If multiple entries specify equivalent header names, only the first
|
||||
// entry with an equivalent name MUST be considered for a match. Subsequent
|
||||
// entries with an equivalent header name MUST be ignored. Due to the
|
||||
// case-insensitivity of header names, "foo" and "Foo" are considered
|
||||
// equivalent.
|
||||
//
|
||||
// When a header is repeated in an HTTP request, it is
|
||||
// implementation-specific behavior as to how this is represented.
|
||||
// Generally, proxies should follow the guidance from the RFC:
|
||||
// https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding
|
||||
// processing a repeated header, with special handling for "Set-Cookie".
|
||||
Name HTTPHeaderName `json:"name"`
|
||||
|
||||
// Value is the value of HTTP Header to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// QueryParamMatchType specifies the semantics of how HTTP query parameter
|
||||
// values should be compared. Valid QueryParamMatchType values are:
|
||||
//
|
||||
// * "Exact"
|
||||
// * "RegularExpression"
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Exact;RegularExpression
|
||||
type QueryParamMatchType string
|
||||
|
||||
// QueryParamMatchType constants.
|
||||
const (
|
||||
QueryParamMatchExact QueryParamMatchType = "Exact"
|
||||
QueryParamMatchRegularExpression QueryParamMatchType = "RegularExpression"
|
||||
)
|
||||
|
||||
// HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP
|
||||
// query parameters.
|
||||
type HTTPQueryParamMatch struct {
|
||||
// Type specifies how to match against the value of the query parameter.
|
||||
//
|
||||
// Support: Extended (Exact)
|
||||
//
|
||||
// Support: Custom (RegularExpression)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Exact
|
||||
Type *QueryParamMatchType `json:"type,omitempty"`
|
||||
|
||||
// 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).
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=256
|
||||
Name string `json:"name"`
|
||||
|
||||
// Value is the value of HTTP query param to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=1024
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// HTTPMethod describes how to select a HTTP route by matching the HTTP
|
||||
// method as defined by
|
||||
// [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-4) and
|
||||
// [RFC 5789](https://datatracker.ietf.org/doc/html/rfc5789#section-2).
|
||||
// The value is expected in upper case.
|
||||
// +kubebuilder:validation:Enum=GET;HEAD;POST;PUT;DELETE;CONNECT;OPTIONS;TRACE;PATCH
|
||||
type HTTPMethod string
|
||||
|
||||
const (
|
||||
HTTPMethodGet HTTPMethod = "GET"
|
||||
HTTPMethodHead HTTPMethod = "HEAD"
|
||||
HTTPMethodPost HTTPMethod = "POST"
|
||||
HTTPMethodPut HTTPMethod = "PUT"
|
||||
HTTPMethodDelete HTTPMethod = "DELETE"
|
||||
HTTPMethodConnect HTTPMethod = "CONNECT"
|
||||
HTTPMethodOptions HTTPMethod = "OPTIONS"
|
||||
HTTPMethodTrace HTTPMethod = "TRACE"
|
||||
HTTPMethodPatch HTTPMethod = "PATCH"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// For example, the match below will match a HTTP request only if its path
|
||||
// starts with `/foo` AND it contains the `version: v1` header:
|
||||
//
|
||||
// ```
|
||||
// match:
|
||||
//
|
||||
// path:
|
||||
// value: "/foo"
|
||||
// headers:
|
||||
// - name: "version"
|
||||
// value "v1"
|
||||
//
|
||||
// ```
|
||||
type HTTPRouteMatch struct {
|
||||
// Path specifies a HTTP request path matcher. If this field is not
|
||||
// specified, a default prefix match on the "/" path is provided.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default={type: "PathPrefix", value: "/"}
|
||||
Path *HTTPPathMatch `json:"path,omitempty"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Headers []HTTPHeaderMatch `json:"headers,omitempty"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
QueryParams []HTTPQueryParamMatch `json:"queryParams,omitempty"`
|
||||
|
||||
// Method specifies HTTP method matcher.
|
||||
// When specified, this route will be matched only if the request has the
|
||||
// specified method.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
Method *HTTPMethod `json:"method,omitempty"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type HTTPRouteFilter struct {
|
||||
// Type identifies the type of filter to apply. As with other API fields,
|
||||
// types are classified into three conformance levels:
|
||||
//
|
||||
// - Core: Filter types and their corresponding configuration defined by
|
||||
// "Support: Core" in this package, e.g. "RequestHeaderModifier". All
|
||||
// implementations must support core filters.
|
||||
//
|
||||
// - Extended: Filter types and their corresponding configuration defined by
|
||||
// "Support: Extended" in this package, e.g. "RequestMirror". Implementers
|
||||
// are encouraged to support extended filters.
|
||||
//
|
||||
// - Custom: Filters that are defined and supported by specific vendors.
|
||||
// In the future, filters showing convergence in behavior across multiple
|
||||
// implementations will be considered for inclusion in extended or core
|
||||
// conformance levels. Filter-specific configuration for such filters
|
||||
// is specified using the ExtensionRef field. `Type` should be set to
|
||||
// "ExtensionRef" for custom filters.
|
||||
//
|
||||
// Implementers are encouraged to define custom implementation types to
|
||||
// extend the core API with implementation-specific behavior.
|
||||
//
|
||||
// If a reference to a custom filter type cannot be resolved, the filter
|
||||
// MUST NOT be skipped. Instead, requests that would have been processed by
|
||||
// that filter MUST receive a HTTP error response.
|
||||
//
|
||||
// +unionDiscriminator
|
||||
// +kubebuilder:validation:Enum=RequestHeaderModifier;RequestMirror;RequestRedirect;ExtensionRef
|
||||
// <gateway:experimental:validation:Enum=RequestHeaderModifier;RequestMirror;RequestRedirect;URLRewrite;ExtensionRef>
|
||||
Type HTTPRouteFilterType `json:"type"`
|
||||
|
||||
// RequestHeaderModifier defines a schema for a filter that modifies request
|
||||
// headers.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
RequestHeaderModifier *HTTPRequestHeaderFilter `json:"requestHeaderModifier,omitempty"`
|
||||
|
||||
// RequestMirror defines a schema for a filter that mirrors requests.
|
||||
// Requests are sent to the specified destination, but responses from
|
||||
// that destination are ignored.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
RequestMirror *HTTPRequestMirrorFilter `json:"requestMirror,omitempty"`
|
||||
|
||||
// RequestRedirect defines a schema for a filter that responds to the
|
||||
// request with an HTTP redirection.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
RequestRedirect *HTTPRequestRedirectFilter `json:"requestRedirect,omitempty"`
|
||||
|
||||
// URLRewrite defines a schema for a filter that responds to the
|
||||
// request with an HTTP redirection.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +optional
|
||||
URLRewrite *HTTPURLRewriteFilter `json:"urlRewrite,omitempty"`
|
||||
|
||||
// ExtensionRef is an optional, implementation-specific extension to the
|
||||
// "filter" behavior. For example, resource "myroutefilter" in group
|
||||
// "networking.example.net"). ExtensionRef MUST NOT be used for core and
|
||||
// extended filters.
|
||||
//
|
||||
// Support: Implementation-specific
|
||||
//
|
||||
// +optional
|
||||
ExtensionRef *LocalObjectReference `json:"extensionRef,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteFilterType identifies a type of HTTPRoute filter.
|
||||
type HTTPRouteFilterType string
|
||||
|
||||
const (
|
||||
// HTTPRouteFilterRequestHeaderModifier can be used to add or remove an HTTP
|
||||
// header from an HTTP request before it is sent to the upstream target.
|
||||
//
|
||||
// Support in HTTPRouteRule: Core
|
||||
//
|
||||
// Support in HTTPBackendRef: Extended
|
||||
HTTPRouteFilterRequestHeaderModifier HTTPRouteFilterType = "RequestHeaderModifier"
|
||||
|
||||
// HTTPRouteFilterRequestRedirect can be used to redirect a request to
|
||||
// another location. This filter can also be used for HTTP to HTTPS
|
||||
// redirects. This may not be used on the same Route rule or BackendRef as a
|
||||
// URLRewrite filter.
|
||||
//
|
||||
// Support in HTTPRouteRule: Core
|
||||
//
|
||||
// Support in HTTPBackendRef: Extended
|
||||
HTTPRouteFilterRequestRedirect HTTPRouteFilterType = "RequestRedirect"
|
||||
|
||||
// HTTPRouteFilterURLRewrite can be used to modify a request during
|
||||
// forwarding. At most one of these filters may be used on a Route rule.
|
||||
// This may not be used on the same Route rule or BackendRef as a
|
||||
// RequestRedirect filter.
|
||||
//
|
||||
// Support in HTTPRouteRule: Extended
|
||||
//
|
||||
// Support in HTTPBackendRef: Extended
|
||||
//
|
||||
// <gateway:experimental>
|
||||
HTTPRouteFilterURLRewrite HTTPRouteFilterType = "URLRewrite"
|
||||
|
||||
// HTTPRouteFilterRequestMirror can be used to mirror HTTP requests to a
|
||||
// different backend. The responses from this backend MUST be ignored by
|
||||
// the Gateway.
|
||||
//
|
||||
// Support in HTTPRouteRule: Extended
|
||||
//
|
||||
// Support in HTTPBackendRef: Extended
|
||||
HTTPRouteFilterRequestMirror HTTPRouteFilterType = "RequestMirror"
|
||||
|
||||
// HTTPRouteFilterExtensionRef should be used for configuring custom
|
||||
// HTTP filters.
|
||||
//
|
||||
// Support in HTTPRouteRule: Custom
|
||||
//
|
||||
// Support in HTTPBackendRef: Custom
|
||||
HTTPRouteFilterExtensionRef HTTPRouteFilterType = "ExtensionRef"
|
||||
)
|
||||
|
||||
// HTTPHeader represents an HTTP Header name and value as defined by RFC 7230.
|
||||
type HTTPHeader struct {
|
||||
// Name is the name of the HTTP Header to be matched. Name matching MUST be
|
||||
// case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
//
|
||||
// If multiple entries specify equivalent header names, the first entry with
|
||||
// an equivalent name MUST be considered for a match. Subsequent entries
|
||||
// with an equivalent header name MUST be ignored. Due to the
|
||||
// case-insensitivity of header names, "foo" and "Foo" are considered
|
||||
// equivalent.
|
||||
Name HTTPHeaderName `json:"name"`
|
||||
|
||||
// Value is the value of HTTP Header to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// HTTPRequestHeaderFilter defines configuration for the RequestHeaderModifier
|
||||
// filter.
|
||||
type HTTPRequestHeaderFilter struct {
|
||||
// Set overwrites the request with the given header (name, value)
|
||||
// before the action.
|
||||
//
|
||||
// Input:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header: foo
|
||||
//
|
||||
// Config:
|
||||
// set:
|
||||
// - name: "my-header"
|
||||
// value: "bar"
|
||||
//
|
||||
// Output:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header: bar
|
||||
//
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Set []HTTPHeader `json:"set,omitempty"`
|
||||
|
||||
// Add adds the given header(s) (name, value) to the request
|
||||
// before the action. It appends to any existing values associated
|
||||
// with the header name.
|
||||
//
|
||||
// Input:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header: foo
|
||||
//
|
||||
// Config:
|
||||
// add:
|
||||
// - name: "my-header"
|
||||
// value: "bar"
|
||||
//
|
||||
// Output:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header: foo
|
||||
// my-header: bar
|
||||
//
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Add []HTTPHeader `json:"add,omitempty"`
|
||||
|
||||
// Remove the given header(s) from the HTTP request before the action. The
|
||||
// value of Remove is a list of HTTP header names. Note that the header
|
||||
// names are case-insensitive (see
|
||||
// https://datatracker.ietf.org/doc/html/rfc2616#section-4.2).
|
||||
//
|
||||
// Input:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header1: foo
|
||||
// my-header2: bar
|
||||
// my-header3: baz
|
||||
//
|
||||
// Config:
|
||||
// remove: ["my-header1", "my-header3"]
|
||||
//
|
||||
// Output:
|
||||
// GET /foo HTTP/1.1
|
||||
// my-header2: bar
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Remove []string `json:"remove,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPPathModifierType defines the type of path redirect.
|
||||
type HTTPPathModifierType string
|
||||
|
||||
const (
|
||||
// This type of modifier indicates that the complete path will be replaced
|
||||
// by the path redirect value.
|
||||
AbsoluteHTTPPathModifier HTTPPathModifierType = "Absolute"
|
||||
|
||||
// This type of modifier indicates that any prefix path matches will be
|
||||
// replaced by the substitution value. For example, a path with a prefix
|
||||
// match of "/foo" and a ReplacePrefixMatch substitution of "/bar" will have
|
||||
// the "/foo" prefix replaced with "/bar" in matching requests.
|
||||
PrefixMatchHTTPPathModifier HTTPPathModifierType = "ReplacePrefixMatch"
|
||||
)
|
||||
|
||||
// HTTPPathModifier defines configuration for path modifiers.
|
||||
// <gateway:experimental>
|
||||
type HTTPPathModifier struct {
|
||||
// Type defines the type of path modifier.
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +kubebuilder:validation:Enum=Absolute;ReplacePrefixMatch
|
||||
Type HTTPPathModifierType `json:"type"`
|
||||
|
||||
// Substitution defines the HTTP path value to substitute. An empty value
|
||||
// ("") indicates that the portion of the path to be changed should be
|
||||
// removed from the resulting path. For example, a request to "/foo/bar"
|
||||
// with a prefix match of "/foo" would be modified to "/bar".
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +kubebuilder:validation:MaxLength=1024
|
||||
Substitution string `json:"substitution"`
|
||||
}
|
||||
|
||||
// HTTPRequestRedirect defines a filter that redirects a request. This filter
|
||||
// MUST not be used on the same Route rule as a HTTPURLRewrite filter.
|
||||
type HTTPRequestRedirectFilter struct {
|
||||
// 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.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:Enum=http;https
|
||||
Scheme *string `json:"scheme,omitempty"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
Hostname *PreciseHostname `json:"hostname,omitempty"`
|
||||
|
||||
// Path defines parameters used to modify the path of the incoming request.
|
||||
// The modified path is then used to construct the `Location` header. When
|
||||
// empty, the request path is used as-is.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +optional
|
||||
Path *HTTPPathModifier `json:"path,omitempty"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
Port *PortNumber `json:"port,omitempty"`
|
||||
|
||||
// StatusCode is the HTTP status code to be used in response.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=302
|
||||
// +kubebuilder:validation:Enum=301;302
|
||||
StatusCode *int `json:"statusCode,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPURLRewriteFilter defines a filter that modifies a request during
|
||||
// forwarding. At most one of these filters may be used on a Route rule. This
|
||||
// may not be used on the same Route rule as a HTTPRequestRedirect filter.
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// Support: Extended
|
||||
type HTTPURLRewriteFilter struct {
|
||||
// Hostname is the value to be used to replace the Host header value during
|
||||
// forwarding.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +optional
|
||||
Hostname *Hostname `json:"hostname,omitempty"`
|
||||
|
||||
// Path defines a path rewrite.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// <gateway:experimental>
|
||||
// +optional
|
||||
Path *HTTPPathModifier `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRequestMirrorFilter defines configuration for the RequestMirror filter.
|
||||
type HTTPRequestMirrorFilter struct {
|
||||
// BackendRef references a resource where mirrored requests are sent.
|
||||
//
|
||||
// If the referent cannot be found, this BackendRef is invalid and must be
|
||||
// dropped from the Gateway. The controller must ensure the "ResolvedRefs"
|
||||
// condition on the Route status is set to `status: False` and not configure
|
||||
// this backend in the underlying implementation.
|
||||
//
|
||||
// If there is a cross-namespace reference to an *existing* object
|
||||
// that is not allowed by a ReferencePolicy, the controller must ensure the
|
||||
// "ResolvedRefs" condition on the Route is set to `status: False`,
|
||||
// with the "RefNotPermitted" reason and not configure this backend in the
|
||||
// underlying implementation.
|
||||
//
|
||||
// In either error case, the Message of the `ResolvedRefs` Condition
|
||||
// should be used to provide more detail about the problem.
|
||||
//
|
||||
// Support: Extended for Kubernetes Service
|
||||
// Support: Custom for any other resource
|
||||
BackendRef BackendObjectReference `json:"backendRef"`
|
||||
}
|
||||
|
||||
// HTTPBackendRef defines how a HTTPRoute should forward an HTTP request.
|
||||
type HTTPBackendRef struct {
|
||||
// BackendRef is a reference to a backend to forward matched requests to.
|
||||
//
|
||||
// If the referent cannot be found, this HTTPBackendRef is invalid and must
|
||||
// be dropped from the Gateway. The controller must ensure the
|
||||
// "ResolvedRefs" condition on the Route is set to `status: False` and not
|
||||
// configure this backend in the underlying implementation.
|
||||
//
|
||||
// If there is a cross-namespace reference to an *existing* object
|
||||
// that is not covered by a ReferencePolicy, the controller must ensure the
|
||||
// "ResolvedRefs" condition on the Route is set to `status: False`,
|
||||
// with the "RefNotPermitted" reason and not configure this backend in the
|
||||
// underlying implementation.
|
||||
//
|
||||
// In either error case, the Message of the `ResolvedRefs` Condition
|
||||
// should be used to provide more detail about the problem.
|
||||
//
|
||||
// Support: Custom
|
||||
//
|
||||
// +optional
|
||||
BackendRef `json:",inline"`
|
||||
|
||||
// Filters defined at this level should be executed if and only if the
|
||||
// request is being forwarded to the backend defined here.
|
||||
//
|
||||
// Support: Custom (For broader support of filters, use the Filters field
|
||||
// in HTTPRouteRule.)
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Filters []HTTPRouteFilter `json:"filters,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteStatus defines the observed state of HTTPRoute.
|
||||
type HTTPRouteStatus struct {
|
||||
RouteStatus `json:",inline"`
|
||||
}
|
||||
|
||||
// Hostname is the fully qualified domain name of a network host. This matches
|
||||
// the RFC 1123 definition of a hostname with 2 notable exceptions:
|
||||
//
|
||||
// 1. IPs are not allowed.
|
||||
// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard
|
||||
// label must appear by itself as the first label.
|
||||
//
|
||||
// Hostname can be "precise" which is a domain name without the terminating
|
||||
// dot of a network host (e.g. "foo.example.com") or "wildcard", which is a
|
||||
// domain name prefixed with a single wildcard label (e.g. `*.example.com`).
|
||||
//
|
||||
// Note that as per RFC1035 and RFC1123, a *label* must consist of lower case
|
||||
// alphanumeric characters or '-', and must start and end with an alphanumeric
|
||||
// character. No other punctuation is allowed.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type Hostname string
|
||||
|
||||
// PortNumber defines a network port.
|
||||
//
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
type PortNumber int32
|
||||
|
|
@ -1,427 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The API object must be valid in the cluster; the Group and Kind must
|
||||
// be registered in the cluster for this reference to be valid.
|
||||
//
|
||||
// References to objects with invalid Group and Kind are not valid, and must
|
||||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
type ParentReference struct {
|
||||
// Group is the group of the referent.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +kubebuilder:default=gateway.networking.k8s.io
|
||||
// +optional
|
||||
Group *Group `json:"group,omitempty"`
|
||||
|
||||
// Kind is kind of the referent.
|
||||
//
|
||||
// Support: Core (Gateway)
|
||||
// Support: Custom (Other Resources)
|
||||
//
|
||||
// +kubebuilder:default=Gateway
|
||||
// +optional
|
||||
Kind *Kind `json:"kind,omitempty"`
|
||||
|
||||
// Namespace is the namespace of the referent. When unspecified (or empty
|
||||
// string), this refers to the local namespace of the Route.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
Namespace *Namespace `json:"namespace,omitempty"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
//
|
||||
// Support: Core
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// SectionName is the name of a section within the target resource. In the
|
||||
// following resources, SectionName is interpreted as the following:
|
||||
//
|
||||
// * Gateway: Listener Name
|
||||
//
|
||||
// Implementations MAY choose to support attaching Routes to other resources.
|
||||
// If that is the case, they MUST clearly document how SectionName is
|
||||
// interpreted.
|
||||
//
|
||||
// When unspecified (empty string), this will reference the entire resource.
|
||||
// For the purpose of status, an attachment is considered successful if at
|
||||
// least one section in the parent resource accepts it. For example, Gateway
|
||||
// listeners can restrict which Routes can attach to them by Route kind,
|
||||
// namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from
|
||||
// the referencing Route, the Route MUST be considered successfully
|
||||
// attached. If no Gateway listeners accept attachment from this Route, the
|
||||
// Route MUST be considered detached from the Gateway.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
SectionName *SectionName `json:"sectionName,omitempty"`
|
||||
}
|
||||
|
||||
// CommonRouteSpec defines the common attributes that all Routes MUST include
|
||||
// within their spec.
|
||||
type CommonRouteSpec struct {
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
ParentRefs []ParentReference `json:"parentRefs,omitempty"`
|
||||
}
|
||||
|
||||
// BackendRef defines how a Route should forward a request to a Kubernetes
|
||||
// resource.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferencePolicy object
|
||||
// is required in the referent namespace to allow that namespace's
|
||||
// owner to accept the reference. See the ReferencePolicy documentation
|
||||
// for details.
|
||||
type BackendRef struct {
|
||||
// BackendObjectReference references a Kubernetes object.
|
||||
BackendObjectReference `json:",inline"`
|
||||
|
||||
// Weight specifies the proportion of requests forwarded to the referenced
|
||||
// backend. This is computed as weight/(sum of all weights in this
|
||||
// BackendRefs list). For non-zero values, there may be some epsilon from
|
||||
// the exact proportion defined here depending on the precision an
|
||||
// implementation supports. Weight is not a percentage and the sum of
|
||||
// weights does not need to equal 100.
|
||||
//
|
||||
// If only one backend is specified and it has a weight greater than 0, 100%
|
||||
// of the traffic is forwarded to that backend. If weight is set to 0, no
|
||||
// traffic should be forwarded for this entry. If unspecified, weight
|
||||
// defaults to 1.
|
||||
//
|
||||
// Support for this field varies based on the context where used.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=1
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
// +kubebuilder:validation:Maximum=1000000
|
||||
Weight *int32 `json:"weight,omitempty"`
|
||||
}
|
||||
|
||||
// RouteConditionType is a type of condition for a route.
|
||||
type RouteConditionType string
|
||||
|
||||
const (
|
||||
// This condition indicates whether the route has been accepted or rejected
|
||||
// by a Gateway, and why.
|
||||
ConditionRouteAccepted RouteConditionType = "Accepted"
|
||||
|
||||
// This condition indicates whether the controller was able to resolve all
|
||||
// the object references for the Route.
|
||||
ConditionRouteResolvedRefs RouteConditionType = "ResolvedRefs"
|
||||
)
|
||||
|
||||
// RouteParentStatus describes the status of a route with respect to an
|
||||
// associated Parent.
|
||||
type RouteParentStatus struct {
|
||||
// ParentRef corresponds with a ParentRef in the spec that this
|
||||
// RouteParentStatus struct describes the status of.
|
||||
ParentRef ParentReference `json:"parentRef"`
|
||||
|
||||
// ControllerName is a domain/path string that indicates the name of the
|
||||
// controller that wrote this status. This corresponds with the
|
||||
// controllerName field on GatewayClass.
|
||||
//
|
||||
// Example: "example.net/gateway-controller".
|
||||
//
|
||||
// The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are
|
||||
// valid Kubernetes names
|
||||
// (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).
|
||||
ControllerName GatewayController `json:"controllerName"`
|
||||
|
||||
// Conditions describes the status of the route with respect to the Gateway.
|
||||
// Note that the route's availability is also subject to the Gateway's own
|
||||
// status conditions and listener status.
|
||||
//
|
||||
// If the Route's ParentRef specifies an existing Gateway that supports
|
||||
// Routes of this kind AND that Gateway's controller has sufficient access,
|
||||
// then that Gateway's controller MUST set the "Accepted" condition on the
|
||||
// Route, to indicate whether the route has been accepted or rejected by the
|
||||
// Gateway, and why.
|
||||
//
|
||||
// A Route MUST be considered "Accepted" if at least one of the Route's
|
||||
// rules is implemented by the Gateway.
|
||||
//
|
||||
// There are a number of cases where the "Accepted" condition may not be set
|
||||
// due to lack of controller visibility, that includes when:
|
||||
//
|
||||
// * The Route refers to a non-existent parent.
|
||||
// * The Route is of a type that the controller does not support.
|
||||
// * The Route is in a namespace the the controller does not have access to.
|
||||
//
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
// +kubebuilder:validation:MaxItems=8
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// RouteStatus defines the common attributes that all Routes MUST include within
|
||||
// their status.
|
||||
type RouteStatus struct {
|
||||
// Parents is a list of parent resources (usually Gateways) that are
|
||||
// associated with the route, and the status of the route with respect to
|
||||
// each parent. When this route attaches to a parent, the controller that
|
||||
// manages the parent must add an entry to this list when the controller
|
||||
// first sees the route and should update the entry as appropriate when the
|
||||
// route or gateway is modified.
|
||||
//
|
||||
// Note that parent references that cannot be resolved by an implementation
|
||||
// of this API will not be added to this list. Implementations of this API
|
||||
// can only populate Route status for the Gateways/parent resources they are
|
||||
// responsible for.
|
||||
//
|
||||
// A maximum of 32 Gateways will be represented in this list. An empty list
|
||||
// means the route has not been attached to any Gateway.
|
||||
//
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
Parents []RouteParentStatus `json:"parents"`
|
||||
}
|
||||
|
||||
// PreciseHostname is the fully qualified domain name of a network host. This matches
|
||||
// the RFC 1123 definition of a hostname with 1 notable exception that
|
||||
// numeric IP addresses are not allowed.
|
||||
//
|
||||
// PreciseHostname can be "precise" which is a domain name without the terminating
|
||||
// dot of a network host (e.g. "foo.example.com").
|
||||
//
|
||||
// Note that as per RFC1035 and RFC1123, a *label* must consist of lower case
|
||||
// alphanumeric characters or '-', and must start and end with an alphanumeric
|
||||
// character. No other punctuation is allowed.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type PreciseHostname string
|
||||
|
||||
// Group refers to a Kubernetes Group. It must either be an empty string or a
|
||||
// RFC 1123 subdomain.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "" - empty string implies core Kubernetes API group
|
||||
// * "networking.k8s.io"
|
||||
// * "foo.example.com"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com/bar" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type Group string
|
||||
|
||||
// Kind refers to a Kubernetes Kind.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "Service"
|
||||
// * "HTTPRoute"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "invalid/kind" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Pattern=`^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$`
|
||||
type Kind string
|
||||
|
||||
// ObjectName refers to the name of a Kubernetes object.
|
||||
// Object names can have a variety of forms, including RFC1123 subdomains,
|
||||
// RFC 1123 labels, or RFC 1035 labels.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
type ObjectName string
|
||||
|
||||
// Namespace refers to a Kubernetes namespace. It must be a RFC 1123 label.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L187
|
||||
//
|
||||
// This is used for Namespace name validation here:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/api/validation/generic.go#L63
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com" - "." is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
type Namespace string
|
||||
|
||||
// SectionName is the name of a section in a Kubernetes resource.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example.com"
|
||||
// * "foo.example.com"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com/bar" - "/" is an invalid character
|
||||
//
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
type SectionName string
|
||||
|
||||
// GatewayController is the name of a Gateway API controller. It must be a
|
||||
// domain prefixed path.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example.com/bar"
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * "example.com" - must include path
|
||||
// * "foo.example.com" - must include path
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
|
||||
type GatewayController string
|
||||
|
||||
// AddressRouteMatches defines AddressMatch rules for inbound traffic according to
|
||||
// source and/or destination address of a packet or connection.
|
||||
type AddressRouteMatches struct {
|
||||
// SourceAddresses indicates the originating (source) network
|
||||
// addresses which are valid for routing traffic.
|
||||
//
|
||||
// Support: Extended
|
||||
SourceAddresses []AddressMatch `json:"sourceAddresses"`
|
||||
|
||||
// DestinationAddresses indicates the destination network addresses
|
||||
// which are valid for routing traffic.
|
||||
//
|
||||
// Support: Extended
|
||||
DestinationAddresses []AddressMatch `json:"destinationAddresses"`
|
||||
}
|
||||
|
||||
// AddressMatch defines matching rules for network addresses by type.
|
||||
type AddressMatch struct {
|
||||
// Type of the address, either IPAddress or NamedAddress.
|
||||
//
|
||||
// If NamedAddress is used this is a custom and specific value for each
|
||||
// implementation to handle (and add validation for) according to their
|
||||
// own needs.
|
||||
//
|
||||
// For IPAddress the implementor may expect either IPv4 or IPv6.
|
||||
//
|
||||
// Support: Core (IPAddress)
|
||||
// Support: Custom (NamedAddress)
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:Enum=IPAddress;NamedAddress
|
||||
// +kubebuilder:default=IPAddress
|
||||
Type *AddressType `json:"type,omitempty"`
|
||||
|
||||
// Value of the address. The validity of the values will depend
|
||||
// on the type and support by the controller.
|
||||
//
|
||||
// If implementations support proxy-protocol (see:
|
||||
// https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) they
|
||||
// must respect the connection metadata from proxy-protocol
|
||||
// in the match logic implemented for these address values.
|
||||
//
|
||||
// Examples: `1.2.3.4`, `128::1`, `my-named-address`.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// AddressType defines how a network address is represented as a text string.
|
||||
type AddressType string
|
||||
|
||||
const (
|
||||
// A textual representation of a numeric IP address. IPv4
|
||||
// addresses must be in dotted-decimal form. IPv6 addresses
|
||||
// must be in a standard IPv6 text representation
|
||||
// (see [RFC 5952](https://tools.ietf.org/html/rfc5952)).
|
||||
//
|
||||
// This type is intended for specific addresses. Address ranges are not
|
||||
// supported (e.g. you can not use a CIDR range like 127.0.0.0/24 as an
|
||||
// IPAddress).
|
||||
//
|
||||
// Support: Extended
|
||||
IPAddressType AddressType = "IPAddress"
|
||||
|
||||
// A Hostname represents a DNS based ingress point. This is similar to the
|
||||
// corresponding hostname field in Kubernetes load balancer status. For
|
||||
// example, this concept may be used for cloud load balancers where a DNS
|
||||
// name is used to expose a load balancer.
|
||||
//
|
||||
// Support: Extended
|
||||
HostnameAddressType AddressType = "Hostname"
|
||||
|
||||
// A NamedAddress provides a way to reference a specific IP address by name.
|
||||
// For example, this may be a name or other unique identifier that refers
|
||||
// to a resource on a cloud provider such as a static IP.
|
||||
//
|
||||
// Support: Implementation-Specific
|
||||
NamedAddressType AddressType = "NamedAddress"
|
||||
)
|
||||
|
|
@ -27,7 +27,7 @@ import (
|
|||
appmeshv1beta2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta2"
|
||||
flaggerv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/flagger/v1beta1"
|
||||
gatewayv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gateway/v1"
|
||||
gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1"
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1"
|
||||
gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
|
||||
networkingv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3"
|
||||
|
|
@ -50,8 +50,8 @@ type Interface interface {
|
|||
AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface
|
||||
FlaggerV1beta1() flaggerv1beta1.FlaggerV1beta1Interface
|
||||
GatewayV1() gatewayv1.GatewayV1Interface
|
||||
GatewayapiV1alpha2() gatewayapiv1alpha2.GatewayapiV1alpha2Interface
|
||||
GatewayapiV1beta1() gatewayapiv1beta1.GatewayapiV1beta1Interface
|
||||
GatewayapiV1() gatewayapiv1.GatewayapiV1Interface
|
||||
GlooV1() gloov1.GlooV1Interface
|
||||
NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface
|
||||
KedaV1alpha1() kedav1alpha1.KedaV1alpha1Interface
|
||||
|
|
@ -71,8 +71,8 @@ type Clientset struct {
|
|||
appmeshV1beta1 *appmeshv1beta1.AppmeshV1beta1Client
|
||||
flaggerV1beta1 *flaggerv1beta1.FlaggerV1beta1Client
|
||||
gatewayV1 *gatewayv1.GatewayV1Client
|
||||
gatewayapiV1alpha2 *gatewayapiv1alpha2.GatewayapiV1alpha2Client
|
||||
gatewayapiV1beta1 *gatewayapiv1beta1.GatewayapiV1beta1Client
|
||||
gatewayapiV1 *gatewayapiv1.GatewayapiV1Client
|
||||
glooV1 *gloov1.GlooV1Client
|
||||
networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client
|
||||
kedaV1alpha1 *kedav1alpha1.KedaV1alpha1Client
|
||||
|
|
@ -109,16 +109,16 @@ func (c *Clientset) GatewayV1() gatewayv1.GatewayV1Interface {
|
|||
return c.gatewayV1
|
||||
}
|
||||
|
||||
// GatewayapiV1alpha2 retrieves the GatewayapiV1alpha2Client
|
||||
func (c *Clientset) GatewayapiV1alpha2() gatewayapiv1alpha2.GatewayapiV1alpha2Interface {
|
||||
return c.gatewayapiV1alpha2
|
||||
}
|
||||
|
||||
// GatewayapiV1beta1 retrieves the GatewayapiV1beta1Client
|
||||
func (c *Clientset) GatewayapiV1beta1() gatewayapiv1beta1.GatewayapiV1beta1Interface {
|
||||
return c.gatewayapiV1beta1
|
||||
}
|
||||
|
||||
// GatewayapiV1 retrieves the GatewayapiV1Client
|
||||
func (c *Clientset) GatewayapiV1() gatewayapiv1.GatewayapiV1Interface {
|
||||
return c.gatewayapiV1
|
||||
}
|
||||
|
||||
// GlooV1 retrieves the GlooV1Client
|
||||
func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
|
||||
return c.glooV1
|
||||
|
|
@ -228,11 +228,11 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.gatewayapiV1alpha2, err = gatewayapiv1alpha2.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
cs.gatewayapiV1beta1, err = gatewayapiv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.gatewayapiV1beta1, err = gatewayapiv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
cs.gatewayapiV1, err = gatewayapiv1.NewForConfigAndClient(&configShallowCopy, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -298,8 +298,8 @@ func New(c rest.Interface) *Clientset {
|
|||
cs.appmeshV1beta1 = appmeshv1beta1.New(c)
|
||||
cs.flaggerV1beta1 = flaggerv1beta1.New(c)
|
||||
cs.gatewayV1 = gatewayv1.New(c)
|
||||
cs.gatewayapiV1alpha2 = gatewayapiv1alpha2.New(c)
|
||||
cs.gatewayapiV1beta1 = gatewayapiv1beta1.New(c)
|
||||
cs.gatewayapiV1 = gatewayapiv1.New(c)
|
||||
cs.glooV1 = gloov1.New(c)
|
||||
cs.networkingV1alpha3 = networkingv1alpha3.New(c)
|
||||
cs.kedaV1alpha1 = kedav1alpha1.New(c)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import (
|
|||
fakeflaggerv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/flagger/v1beta1/fake"
|
||||
gatewayv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gateway/v1"
|
||||
fakegatewayv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gateway/v1/fake"
|
||||
gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2"
|
||||
fakegatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1"
|
||||
fakegatewayapiv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1/fake"
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1"
|
||||
fakegatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake"
|
||||
gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
|
||||
|
|
@ -134,16 +134,16 @@ func (c *Clientset) GatewayV1() gatewayv1.GatewayV1Interface {
|
|||
return &fakegatewayv1.FakeGatewayV1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// GatewayapiV1alpha2 retrieves the GatewayapiV1alpha2Client
|
||||
func (c *Clientset) GatewayapiV1alpha2() gatewayapiv1alpha2.GatewayapiV1alpha2Interface {
|
||||
return &fakegatewayapiv1alpha2.FakeGatewayapiV1alpha2{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// GatewayapiV1beta1 retrieves the GatewayapiV1beta1Client
|
||||
func (c *Clientset) GatewayapiV1beta1() gatewayapiv1beta1.GatewayapiV1beta1Interface {
|
||||
return &fakegatewayapiv1beta1.FakeGatewayapiV1beta1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// GatewayapiV1 retrieves the GatewayapiV1Client
|
||||
func (c *Clientset) GatewayapiV1() gatewayapiv1.GatewayapiV1Interface {
|
||||
return &fakegatewayapiv1.FakeGatewayapiV1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// GlooV1 retrieves the GlooV1Client
|
||||
func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
|
||||
return &fakegloov1.FakeGlooV1{Fake: &c.Fake}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
appmeshv1beta1 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta1"
|
||||
appmeshv1beta2 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta2"
|
||||
flaggerv1beta1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
|
||||
gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1"
|
||||
gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1"
|
||||
|
|
@ -51,8 +51,8 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
|||
appmeshv1beta1.AddToScheme,
|
||||
flaggerv1beta1.AddToScheme,
|
||||
gatewayv1.AddToScheme,
|
||||
gatewayapiv1alpha2.AddToScheme,
|
||||
gatewayapiv1beta1.AddToScheme,
|
||||
gatewayapiv1.AddToScheme,
|
||||
gloov1.AddToScheme,
|
||||
networkingv1alpha3.AddToScheme,
|
||||
kedav1alpha1.AddToScheme,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
appmeshv1beta1 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta1"
|
||||
appmeshv1beta2 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta2"
|
||||
flaggerv1beta1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
|
||||
gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1"
|
||||
gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1"
|
||||
|
|
@ -51,8 +51,8 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
|||
appmeshv1beta1.AddToScheme,
|
||||
flaggerv1beta1.AddToScheme,
|
||||
gatewayv1.AddToScheme,
|
||||
gatewayapiv1alpha2.AddToScheme,
|
||||
gatewayapiv1beta1.AddToScheme,
|
||||
gatewayapiv1.AddToScheme,
|
||||
gloov1.AddToScheme,
|
||||
networkingv1alpha3.AddToScheme,
|
||||
kedav1alpha1.AddToScheme,
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ limitations under the License.
|
|||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// This package has the automatically generated typed clients.
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
|
@ -19,22 +19,22 @@ limitations under the License.
|
|||
package fake
|
||||
|
||||
import (
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1"
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
type FakeGatewayapiV1alpha2 struct {
|
||||
type FakeGatewayapiV1 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeGatewayapiV1alpha2) HTTPRoutes(namespace string) v1alpha2.HTTPRouteInterface {
|
||||
func (c *FakeGatewayapiV1) HTTPRoutes(namespace string) v1.HTTPRouteInterface {
|
||||
return &FakeHTTPRoutes{c, namespace}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeGatewayapiV1alpha2) RESTClient() rest.Interface {
|
||||
func (c *FakeGatewayapiV1) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
|
|
@ -21,8 +21,8 @@ package fake
|
|||
import (
|
||||
"context"
|
||||
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
|
|
@ -31,29 +31,29 @@ import (
|
|||
|
||||
// FakeHTTPRoutes implements HTTPRouteInterface
|
||||
type FakeHTTPRoutes struct {
|
||||
Fake *FakeGatewayapiV1alpha2
|
||||
Fake *FakeGatewayapiV1
|
||||
ns string
|
||||
}
|
||||
|
||||
var httproutesResource = v1alpha2.SchemeGroupVersion.WithResource("httproutes")
|
||||
var httproutesResource = v1.SchemeGroupVersion.WithResource("httproutes")
|
||||
|
||||
var httproutesKind = v1alpha2.SchemeGroupVersion.WithKind("HTTPRoute")
|
||||
var httproutesKind = v1.SchemeGroupVersion.WithKind("HTTPRoute")
|
||||
|
||||
// Get takes name of the hTTPRoute, and returns the corresponding hTTPRoute object, and an error if there is any.
|
||||
func (c *FakeHTTPRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
func (c *FakeHTTPRoutes) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.HTTPRoute, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(httproutesResource, c.ns, name), &v1alpha2.HTTPRoute{})
|
||||
Invokes(testing.NewGetAction(httproutesResource, c.ns, name), &v1.HTTPRoute{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha2.HTTPRoute), err
|
||||
return obj.(*v1.HTTPRoute), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of HTTPRoutes that match those selectors.
|
||||
func (c *FakeHTTPRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.HTTPRouteList, err error) {
|
||||
func (c *FakeHTTPRoutes) List(ctx context.Context, opts metav1.ListOptions) (result *v1.HTTPRouteList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(httproutesResource, httproutesKind, c.ns, opts), &v1alpha2.HTTPRouteList{})
|
||||
Invokes(testing.NewListAction(httproutesResource, httproutesKind, c.ns, opts), &v1.HTTPRouteList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
|
|
@ -63,8 +63,8 @@ func (c *FakeHTTPRoutes) List(ctx context.Context, opts v1.ListOptions) (result
|
|||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1alpha2.HTTPRouteList{ListMeta: obj.(*v1alpha2.HTTPRouteList).ListMeta}
|
||||
for _, item := range obj.(*v1alpha2.HTTPRouteList).Items {
|
||||
list := &v1.HTTPRouteList{ListMeta: obj.(*v1.HTTPRouteList).ListMeta}
|
||||
for _, item := range obj.(*v1.HTTPRouteList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
|
|
@ -73,57 +73,69 @@ func (c *FakeHTTPRoutes) List(ctx context.Context, opts v1.ListOptions) (result
|
|||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested hTTPRoutes.
|
||||
func (c *FakeHTTPRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
func (c *FakeHTTPRoutes) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(httproutesResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a hTTPRoute and creates it. Returns the server's representation of the hTTPRoute, and an error, if there is any.
|
||||
func (c *FakeHTTPRoutes) Create(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.CreateOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
func (c *FakeHTTPRoutes) Create(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.CreateOptions) (result *v1.HTTPRoute, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(httproutesResource, c.ns, hTTPRoute), &v1alpha2.HTTPRoute{})
|
||||
Invokes(testing.NewCreateAction(httproutesResource, c.ns, hTTPRoute), &v1.HTTPRoute{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha2.HTTPRoute), err
|
||||
return obj.(*v1.HTTPRoute), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a hTTPRoute and updates it. Returns the server's representation of the hTTPRoute, and an error, if there is any.
|
||||
func (c *FakeHTTPRoutes) Update(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.UpdateOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
func (c *FakeHTTPRoutes) Update(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (result *v1.HTTPRoute, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(httproutesResource, c.ns, hTTPRoute), &v1alpha2.HTTPRoute{})
|
||||
Invokes(testing.NewUpdateAction(httproutesResource, c.ns, hTTPRoute), &v1.HTTPRoute{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha2.HTTPRoute), err
|
||||
return obj.(*v1.HTTPRoute), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeHTTPRoutes) UpdateStatus(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (*v1.HTTPRoute, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(httproutesResource, "status", c.ns, hTTPRoute), &v1.HTTPRoute{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1.HTTPRoute), err
|
||||
}
|
||||
|
||||
// Delete takes name of the hTTPRoute and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeHTTPRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
func (c *FakeHTTPRoutes) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteActionWithOptions(httproutesResource, c.ns, name, opts), &v1alpha2.HTTPRoute{})
|
||||
Invokes(testing.NewDeleteActionWithOptions(httproutesResource, c.ns, name, opts), &v1.HTTPRoute{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeHTTPRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
func (c *FakeHTTPRoutes) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(httproutesResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1alpha2.HTTPRouteList{})
|
||||
_, err := c.Fake.Invokes(action, &v1.HTTPRouteList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched hTTPRoute.
|
||||
func (c *FakeHTTPRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.HTTPRoute, err error) {
|
||||
func (c *FakeHTTPRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.HTTPRoute, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(httproutesResource, c.ns, name, pt, data, subresources...), &v1alpha2.HTTPRoute{})
|
||||
Invokes(testing.NewPatchSubresourceAction(httproutesResource, c.ns, name, pt, data, subresources...), &v1.HTTPRoute{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha2.HTTPRoute), err
|
||||
return obj.(*v1.HTTPRoute), err
|
||||
}
|
||||
|
|
@ -16,34 +16,34 @@ limitations under the License.
|
|||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
"github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type GatewayapiV1alpha2Interface interface {
|
||||
type GatewayapiV1Interface interface {
|
||||
RESTClient() rest.Interface
|
||||
HTTPRoutesGetter
|
||||
}
|
||||
|
||||
// GatewayapiV1alpha2Client is used to interact with features provided by the gatewayapi group.
|
||||
type GatewayapiV1alpha2Client struct {
|
||||
// GatewayapiV1Client is used to interact with features provided by the gatewayapi group.
|
||||
type GatewayapiV1Client struct {
|
||||
restClient rest.Interface
|
||||
}
|
||||
|
||||
func (c *GatewayapiV1alpha2Client) HTTPRoutes(namespace string) HTTPRouteInterface {
|
||||
func (c *GatewayapiV1Client) HTTPRoutes(namespace string) HTTPRouteInterface {
|
||||
return newHTTPRoutes(c, namespace)
|
||||
}
|
||||
|
||||
// NewForConfig creates a new GatewayapiV1alpha2Client for the given config.
|
||||
// NewForConfig creates a new GatewayapiV1Client for the given config.
|
||||
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
|
||||
// where httpClient was generated with rest.HTTPClientFor(c).
|
||||
func NewForConfig(c *rest.Config) (*GatewayapiV1alpha2Client, error) {
|
||||
func NewForConfig(c *rest.Config) (*GatewayapiV1Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -55,9 +55,9 @@ func NewForConfig(c *rest.Config) (*GatewayapiV1alpha2Client, error) {
|
|||
return NewForConfigAndClient(&config, httpClient)
|
||||
}
|
||||
|
||||
// NewForConfigAndClient creates a new GatewayapiV1alpha2Client for the given config and http client.
|
||||
// NewForConfigAndClient creates a new GatewayapiV1Client for the given config and http client.
|
||||
// Note the http client provided takes precedence over the configured transport values.
|
||||
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*GatewayapiV1alpha2Client, error) {
|
||||
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*GatewayapiV1Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -66,12 +66,12 @@ func NewForConfigAndClient(c *rest.Config, h *http.Client) (*GatewayapiV1alpha2C
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GatewayapiV1alpha2Client{client}, nil
|
||||
return &GatewayapiV1Client{client}, nil
|
||||
}
|
||||
|
||||
// NewForConfigOrDie creates a new GatewayapiV1alpha2Client for the given config and
|
||||
// NewForConfigOrDie creates a new GatewayapiV1Client for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewForConfigOrDie(c *rest.Config) *GatewayapiV1alpha2Client {
|
||||
func NewForConfigOrDie(c *rest.Config) *GatewayapiV1Client {
|
||||
client, err := NewForConfig(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -79,13 +79,13 @@ func NewForConfigOrDie(c *rest.Config) *GatewayapiV1alpha2Client {
|
|||
return client
|
||||
}
|
||||
|
||||
// New creates a new GatewayapiV1alpha2Client for the given RESTClient.
|
||||
func New(c rest.Interface) *GatewayapiV1alpha2Client {
|
||||
return &GatewayapiV1alpha2Client{c}
|
||||
// New creates a new GatewayapiV1Client for the given RESTClient.
|
||||
func New(c rest.Interface) *GatewayapiV1Client {
|
||||
return &GatewayapiV1Client{c}
|
||||
}
|
||||
|
||||
func setConfigDefaults(config *rest.Config) error {
|
||||
gv := v1alpha2.SchemeGroupVersion
|
||||
gv := v1.SchemeGroupVersion
|
||||
config.GroupVersion = &gv
|
||||
config.APIPath = "/apis"
|
||||
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
|
||||
|
|
@ -99,7 +99,7 @@ func setConfigDefaults(config *rest.Config) error {
|
|||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *GatewayapiV1alpha2Client) RESTClient() rest.Interface {
|
||||
func (c *GatewayapiV1Client) RESTClient() rest.Interface {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -16,6 +16,6 @@ limitations under the License.
|
|||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
type HTTPRouteExpansion interface{}
|
||||
|
|
@ -16,15 +16,15 @@ limitations under the License.
|
|||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
rest "k8s.io/client-go/rest"
|
||||
|
|
@ -38,14 +38,15 @@ type HTTPRoutesGetter interface {
|
|||
|
||||
// HTTPRouteInterface has methods to work with HTTPRoute resources.
|
||||
type HTTPRouteInterface interface {
|
||||
Create(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.CreateOptions) (*v1alpha2.HTTPRoute, error)
|
||||
Update(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.UpdateOptions) (*v1alpha2.HTTPRoute, error)
|
||||
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
|
||||
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
|
||||
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha2.HTTPRoute, error)
|
||||
List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.HTTPRouteList, error)
|
||||
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
|
||||
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.HTTPRoute, err error)
|
||||
Create(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.CreateOptions) (*v1.HTTPRoute, error)
|
||||
Update(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (*v1.HTTPRoute, error)
|
||||
UpdateStatus(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (*v1.HTTPRoute, error)
|
||||
Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
|
||||
DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
|
||||
Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.HTTPRoute, error)
|
||||
List(ctx context.Context, opts metav1.ListOptions) (*v1.HTTPRouteList, error)
|
||||
Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
|
||||
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.HTTPRoute, err error)
|
||||
HTTPRouteExpansion
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ type hTTPRoutes struct {
|
|||
}
|
||||
|
||||
// newHTTPRoutes returns a HTTPRoutes
|
||||
func newHTTPRoutes(c *GatewayapiV1alpha2Client, namespace string) *hTTPRoutes {
|
||||
func newHTTPRoutes(c *GatewayapiV1Client, namespace string) *hTTPRoutes {
|
||||
return &hTTPRoutes{
|
||||
client: c.RESTClient(),
|
||||
ns: namespace,
|
||||
|
|
@ -64,8 +65,8 @@ func newHTTPRoutes(c *GatewayapiV1alpha2Client, namespace string) *hTTPRoutes {
|
|||
}
|
||||
|
||||
// Get takes name of the hTTPRoute, and returns the corresponding hTTPRoute object, and an error if there is any.
|
||||
func (c *hTTPRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
result = &v1alpha2.HTTPRoute{}
|
||||
func (c *hTTPRoutes) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.HTTPRoute, err error) {
|
||||
result = &v1.HTTPRoute{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -77,12 +78,12 @@ func (c *hTTPRoutes) Get(ctx context.Context, name string, options v1.GetOptions
|
|||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of HTTPRoutes that match those selectors.
|
||||
func (c *hTTPRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.HTTPRouteList, err error) {
|
||||
func (c *hTTPRoutes) List(ctx context.Context, opts metav1.ListOptions) (result *v1.HTTPRouteList, err error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
result = &v1alpha2.HTTPRouteList{}
|
||||
result = &v1.HTTPRouteList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -94,7 +95,7 @@ func (c *hTTPRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v1a
|
|||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested hTTPRoutes.
|
||||
func (c *hTTPRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
func (c *hTTPRoutes) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
|
|
@ -109,8 +110,8 @@ func (c *hTTPRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Inte
|
|||
}
|
||||
|
||||
// Create takes the representation of a hTTPRoute and creates it. Returns the server's representation of the hTTPRoute, and an error, if there is any.
|
||||
func (c *hTTPRoutes) Create(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.CreateOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
result = &v1alpha2.HTTPRoute{}
|
||||
func (c *hTTPRoutes) Create(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.CreateOptions) (result *v1.HTTPRoute, err error) {
|
||||
result = &v1.HTTPRoute{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -122,8 +123,8 @@ func (c *hTTPRoutes) Create(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute,
|
|||
}
|
||||
|
||||
// Update takes the representation of a hTTPRoute and updates it. Returns the server's representation of the hTTPRoute, and an error, if there is any.
|
||||
func (c *hTTPRoutes) Update(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute, opts v1.UpdateOptions) (result *v1alpha2.HTTPRoute, err error) {
|
||||
result = &v1alpha2.HTTPRoute{}
|
||||
func (c *hTTPRoutes) Update(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (result *v1.HTTPRoute, err error) {
|
||||
result = &v1.HTTPRoute{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -135,8 +136,24 @@ func (c *hTTPRoutes) Update(ctx context.Context, hTTPRoute *v1alpha2.HTTPRoute,
|
|||
return
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *hTTPRoutes) UpdateStatus(ctx context.Context, hTTPRoute *v1.HTTPRoute, opts metav1.UpdateOptions) (result *v1.HTTPRoute, err error) {
|
||||
result = &v1.HTTPRoute{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
Name(hTTPRoute.Name).
|
||||
SubResource("status").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Body(hTTPRoute).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the hTTPRoute and deletes it. Returns an error if one occurs.
|
||||
func (c *hTTPRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
func (c *hTTPRoutes) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -147,7 +164,7 @@ func (c *hTTPRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOpti
|
|||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *hTTPRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
func (c *hTTPRoutes) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
|
||||
var timeout time.Duration
|
||||
if listOpts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
|
||||
|
|
@ -163,8 +180,8 @@ func (c *hTTPRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions
|
|||
}
|
||||
|
||||
// Patch applies the patch and returns the patched hTTPRoute.
|
||||
func (c *hTTPRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.HTTPRoute, err error) {
|
||||
result = &v1alpha2.HTTPRoute{}
|
||||
func (c *hTTPRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.HTTPRoute, err error) {
|
||||
result = &v1.HTTPRoute{}
|
||||
err = c.client.Patch(pt).
|
||||
Namespace(c.ns).
|
||||
Resource("httproutes").
|
||||
|
|
@ -19,17 +19,17 @@ limitations under the License.
|
|||
package gatewayapi
|
||||
|
||||
import (
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi/v1"
|
||||
v1beta1 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi/v1beta1"
|
||||
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
)
|
||||
|
||||
// Interface provides access to each of this group's versions.
|
||||
type Interface interface {
|
||||
// V1alpha2 provides access to shared informers for resources in V1alpha2.
|
||||
V1alpha2() v1alpha2.Interface
|
||||
// V1beta1 provides access to shared informers for resources in V1beta1.
|
||||
V1beta1() v1beta1.Interface
|
||||
// V1 provides access to shared informers for resources in V1.
|
||||
V1() v1.Interface
|
||||
}
|
||||
|
||||
type group struct {
|
||||
|
|
@ -43,12 +43,12 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
|
|||
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||
}
|
||||
|
||||
// V1alpha2 returns a new v1alpha2.Interface.
|
||||
func (g *group) V1alpha2() v1alpha2.Interface {
|
||||
return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions)
|
||||
}
|
||||
|
||||
// V1beta1 returns a new v1beta1.Interface.
|
||||
func (g *group) V1beta1() v1beta1.Interface {
|
||||
return v1beta1.New(g.factory, g.namespace, g.tweakListOptions)
|
||||
}
|
||||
|
||||
// V1 returns a new v1.Interface.
|
||||
func (g *group) V1() v1.Interface {
|
||||
return v1.New(g.factory, g.namespace, g.tweakListOptions)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ limitations under the License.
|
|||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
time "time"
|
||||
|
||||
gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned"
|
||||
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/client/listers/gatewayapi/v1alpha2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "github.com/fluxcd/flagger/pkg/client/listers/gatewayapi/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
cache "k8s.io/client-go/tools/cache"
|
||||
|
|
@ -36,7 +36,7 @@ import (
|
|||
// HTTPRoutes.
|
||||
type HTTPRouteInformer interface {
|
||||
Informer() cache.SharedIndexInformer
|
||||
Lister() v1alpha2.HTTPRouteLister
|
||||
Lister() v1.HTTPRouteLister
|
||||
}
|
||||
|
||||
type hTTPRouteInformer struct {
|
||||
|
|
@ -58,20 +58,20 @@ func NewHTTPRouteInformer(client versioned.Interface, namespace string, resyncPe
|
|||
func NewFilteredHTTPRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||
return cache.NewSharedIndexInformer(
|
||||
&cache.ListWatch{
|
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
if tweakListOptions != nil {
|
||||
tweakListOptions(&options)
|
||||
}
|
||||
return client.GatewayapiV1alpha2().HTTPRoutes(namespace).List(context.TODO(), options)
|
||||
return client.GatewayapiV1().HTTPRoutes(namespace).List(context.TODO(), options)
|
||||
},
|
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
if tweakListOptions != nil {
|
||||
tweakListOptions(&options)
|
||||
}
|
||||
return client.GatewayapiV1alpha2().HTTPRoutes(namespace).Watch(context.TODO(), options)
|
||||
return client.GatewayapiV1().HTTPRoutes(namespace).Watch(context.TODO(), options)
|
||||
},
|
||||
},
|
||||
&gatewayapiv1alpha2.HTTPRoute{},
|
||||
&gatewayapiv1.HTTPRoute{},
|
||||
resyncPeriod,
|
||||
indexers,
|
||||
)
|
||||
|
|
@ -82,9 +82,9 @@ func (f *hTTPRouteInformer) defaultInformer(client versioned.Interface, resyncPe
|
|||
}
|
||||
|
||||
func (f *hTTPRouteInformer) Informer() cache.SharedIndexInformer {
|
||||
return f.factory.InformerFor(&gatewayapiv1alpha2.HTTPRoute{}, f.defaultInformer)
|
||||
return f.factory.InformerFor(&gatewayapiv1.HTTPRoute{}, f.defaultInformer)
|
||||
}
|
||||
|
||||
func (f *hTTPRouteInformer) Lister() v1alpha2.HTTPRouteLister {
|
||||
return v1alpha2.NewHTTPRouteLister(f.Informer().GetIndexer())
|
||||
func (f *hTTPRouteInformer) Lister() v1.HTTPRouteLister {
|
||||
return v1.NewHTTPRouteLister(f.Informer().GetIndexer())
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
|
|
@ -25,7 +25,7 @@ import (
|
|||
v1beta1 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta1"
|
||||
v1beta2 "github.com/fluxcd/flagger/pkg/apis/appmesh/v1beta2"
|
||||
flaggerv1beta1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
gatewayapiv1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1"
|
||||
gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1"
|
||||
|
|
@ -34,7 +34,7 @@ import (
|
|||
kumav1alpha1 "github.com/fluxcd/flagger/pkg/apis/kuma/v1alpha1"
|
||||
projectcontourv1 "github.com/fluxcd/flagger/pkg/apis/projectcontour/v1"
|
||||
smiv1alpha1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha1"
|
||||
smiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2"
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2"
|
||||
smiv1alpha3 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha3"
|
||||
traefikv1alpha1 "github.com/fluxcd/flagger/pkg/apis/traefik/v1alpha1"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
|
@ -99,9 +99,9 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||
case v1.SchemeGroupVersion.WithResource("routetables"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1().RouteTables().Informer()}, nil
|
||||
|
||||
// Group=gatewayapi, Version=v1alpha2
|
||||
case v1alpha2.SchemeGroupVersion.WithResource("httproutes"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1alpha2().HTTPRoutes().Informer()}, nil
|
||||
// Group=gatewayapi, Version=v1
|
||||
case gatewayapiv1.SchemeGroupVersion.WithResource("httproutes"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1().HTTPRoutes().Informer()}, nil
|
||||
|
||||
// Group=gatewayapi, Version=v1beta1
|
||||
case gatewayapiv1beta1.SchemeGroupVersion.WithResource("httproutes"):
|
||||
|
|
@ -134,7 +134,7 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||
return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha1().TrafficSplits().Informer()}, nil
|
||||
|
||||
// Group=split.smi-spec.io, Version=v1alpha2
|
||||
case smiv1alpha2.SchemeGroupVersion.WithResource("trafficsplits"):
|
||||
case v1alpha2.SchemeGroupVersion.WithResource("trafficsplits"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha2().TrafficSplits().Informer()}, nil
|
||||
|
||||
// Group=split.smi-spec.io, Version=v1alpha3
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
// HTTPRouteListerExpansion allows custom methods to be added to
|
||||
// HTTPRouteLister.
|
||||
|
|
@ -16,10 +16,10 @@ limitations under the License.
|
|||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
|
@ -30,7 +30,7 @@ import (
|
|||
type HTTPRouteLister interface {
|
||||
// List lists all HTTPRoutes in the indexer.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error)
|
||||
List(selector labels.Selector) (ret []*v1.HTTPRoute, err error)
|
||||
// HTTPRoutes returns an object that can list and get HTTPRoutes.
|
||||
HTTPRoutes(namespace string) HTTPRouteNamespaceLister
|
||||
HTTPRouteListerExpansion
|
||||
|
|
@ -47,9 +47,9 @@ func NewHTTPRouteLister(indexer cache.Indexer) HTTPRouteLister {
|
|||
}
|
||||
|
||||
// List lists all HTTPRoutes in the indexer.
|
||||
func (s *hTTPRouteLister) List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error) {
|
||||
func (s *hTTPRouteLister) List(selector labels.Selector) (ret []*v1.HTTPRoute, err error) {
|
||||
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||
ret = append(ret, m.(*v1alpha2.HTTPRoute))
|
||||
ret = append(ret, m.(*v1.HTTPRoute))
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
|
@ -64,10 +64,10 @@ func (s *hTTPRouteLister) HTTPRoutes(namespace string) HTTPRouteNamespaceLister
|
|||
type HTTPRouteNamespaceLister interface {
|
||||
// List lists all HTTPRoutes in the indexer for a given namespace.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error)
|
||||
List(selector labels.Selector) (ret []*v1.HTTPRoute, err error)
|
||||
// Get retrieves the HTTPRoute from the indexer for a given namespace and name.
|
||||
// Objects returned here must be treated as read-only.
|
||||
Get(name string) (*v1alpha2.HTTPRoute, error)
|
||||
Get(name string) (*v1.HTTPRoute, error)
|
||||
HTTPRouteNamespaceListerExpansion
|
||||
}
|
||||
|
||||
|
|
@ -79,21 +79,21 @@ type hTTPRouteNamespaceLister struct {
|
|||
}
|
||||
|
||||
// List lists all HTTPRoutes in the indexer for a given namespace.
|
||||
func (s hTTPRouteNamespaceLister) List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error) {
|
||||
func (s hTTPRouteNamespaceLister) List(selector labels.Selector) (ret []*v1.HTTPRoute, err error) {
|
||||
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
|
||||
ret = append(ret, m.(*v1alpha2.HTTPRoute))
|
||||
ret = append(ret, m.(*v1.HTTPRoute))
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Get retrieves the HTTPRoute from the indexer for a given namespace and name.
|
||||
func (s hTTPRouteNamespaceLister) Get(name string) (*v1alpha2.HTTPRoute, error) {
|
||||
func (s hTTPRouteNamespaceLister) Get(name string) (*v1.HTTPRoute, error) {
|
||||
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, errors.NewNotFound(v1alpha2.Resource("httproute"), name)
|
||||
return nil, errors.NewNotFound(v1.Resource("httproute"), name)
|
||||
}
|
||||
return obj.(*v1alpha2.HTTPRoute), nil
|
||||
return obj.(*v1.HTTPRoute), nil
|
||||
}
|
||||
|
|
@ -199,15 +199,15 @@ func (factory *Factory) MeshRouter(provider string, labelSelector string) Interf
|
|||
kubeClient: factory.kubeClient,
|
||||
kumaClient: factory.meshClient,
|
||||
}
|
||||
case strings.HasPrefix(provider, flaggerv1.GatewayAPIProvider+":v1alpha2"):
|
||||
return &GatewayAPIRouter{
|
||||
case strings.HasPrefix(provider, flaggerv1.GatewayAPIProvider+":v1beta1"):
|
||||
return &GatewayAPIV1Beta1Router{
|
||||
logger: factory.logger,
|
||||
kubeClient: factory.kubeClient,
|
||||
gatewayAPIClient: factory.meshClient,
|
||||
setOwnerRefs: factory.setOwnerRefs,
|
||||
}
|
||||
case strings.HasPrefix(provider, flaggerv1.GatewayAPIProvider+":v1beta1"):
|
||||
return &GatewayAPIV1Beta1Router{
|
||||
case strings.HasPrefix(provider, flaggerv1.GatewayAPIProvider+":v1"):
|
||||
return &GatewayAPIRouter{
|
||||
logger: factory.logger,
|
||||
kubeClient: factory.kubeClient,
|
||||
gatewayAPIClient: factory.meshClient,
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
|
||||
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
"github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3"
|
||||
clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned"
|
||||
|
|
@ -41,14 +42,14 @@ var (
|
|||
backendRefGroup = ""
|
||||
backendRefKind = "Service"
|
||||
pathMatchValue = "/"
|
||||
pathMatchType = v1alpha2.PathMatchPathPrefix
|
||||
pathMatchRegex = v1alpha2.PathMatchRegularExpression
|
||||
pathMatchExact = v1alpha2.PathMatchExact
|
||||
pathMatchPrefix = v1alpha2.PathMatchPathPrefix
|
||||
headerMatchExact = v1alpha2.HeaderMatchExact
|
||||
headerMatchRegex = v1alpha2.HeaderMatchRegularExpression
|
||||
queryMatchExact = v1alpha2.QueryParamMatchExact
|
||||
queryMatchRegex = v1alpha2.QueryParamMatchRegularExpression
|
||||
pathMatchType = v1.PathMatchPathPrefix
|
||||
pathMatchRegex = v1.PathMatchRegularExpression
|
||||
pathMatchExact = v1.PathMatchExact
|
||||
pathMatchPrefix = v1.PathMatchPathPrefix
|
||||
headerMatchExact = v1.HeaderMatchExact
|
||||
headerMatchRegex = v1.HeaderMatchRegularExpression
|
||||
queryMatchExact = v1.QueryParamMatchExact
|
||||
queryMatchRegex = v1.QueryParamMatchRegularExpression
|
||||
)
|
||||
|
||||
type GatewayAPIRouter struct {
|
||||
|
|
@ -67,32 +68,33 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
|
||||
hrNamespace := canary.Namespace
|
||||
|
||||
var hostNames []v1alpha2.Hostname
|
||||
var hostNames []v1.Hostname
|
||||
for _, host := range canary.Spec.Service.Hosts {
|
||||
hostNames = append(hostNames, v1alpha2.Hostname(host))
|
||||
hostNames = append(hostNames, v1.Hostname(host))
|
||||
}
|
||||
matches, err := gwr.mapRouteMatches(canary.Spec.Service.Match)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid request matching selectors: %w", err)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
matches = append(matches, v1alpha2.HTTPRouteMatch{
|
||||
Path: &v1alpha2.HTTPPathMatch{
|
||||
matches = append(matches, v1.HTTPRouteMatch{
|
||||
Path: &v1.HTTPPathMatch{
|
||||
Type: &pathMatchType,
|
||||
Value: &pathMatchValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
httpRouteSpec := v1alpha2.HTTPRouteSpec{
|
||||
CommonRouteSpec: v1alpha2.CommonRouteSpec{
|
||||
ParentRefs: toV1alpha2ParentRefs(canary.Spec.Service.GatewayRefs),
|
||||
httpRouteSpec := v1.HTTPRouteSpec{
|
||||
CommonRouteSpec: v1.CommonRouteSpec{
|
||||
ParentRefs: toV1ParentRefs(canary.Spec.Service.GatewayRefs),
|
||||
},
|
||||
Hostnames: hostNames,
|
||||
Rules: []v1alpha2.HTTPRouteRule{
|
||||
Rules: []v1.HTTPRouteRule{
|
||||
{
|
||||
Matches: matches,
|
||||
BackendRefs: []v1alpha2.HTTPBackendRef{
|
||||
Filters: gwr.makeFilters(canary),
|
||||
BackendRefs: []v1.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
|
|
@ -103,23 +105,36 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
},
|
||||
},
|
||||
}
|
||||
if canary.Spec.Service.Timeout != "" {
|
||||
timeout := v1.Duration(canary.Spec.Service.Timeout)
|
||||
httpRouteSpec.Rules[0].Timeouts = &v1.HTTPRouteTimeouts{
|
||||
Request: &timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// A/B testing
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
analysisMatches, _ := gwr.mapRouteMatches(canary.GetAnalysis().Match)
|
||||
// serviceMatches, _ := gwr.mapRouteMatches(canary.Spec.Service.Match)
|
||||
httpRouteSpec.Rules[0].Matches = gwr.mergeMatchConditions(analysisMatches, matches)
|
||||
httpRouteSpec.Rules = append(httpRouteSpec.Rules, v1alpha2.HTTPRouteRule{
|
||||
httpRouteSpec.Rules = append(httpRouteSpec.Rules, v1.HTTPRouteRule{
|
||||
Matches: matches,
|
||||
BackendRefs: []v1alpha2.HTTPBackendRef{
|
||||
Filters: gwr.makeFilters(canary),
|
||||
BackendRefs: []v1.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
})
|
||||
if canary.Spec.Service.Timeout != "" {
|
||||
timeout := v1.Duration(canary.Spec.Service.Timeout)
|
||||
httpRouteSpec.Rules[1].Timeouts = &v1.HTTPRouteTimeouts{
|
||||
Request: &timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Get(
|
||||
context.TODO(), apexSvcName, metav1.GetOptions{},
|
||||
)
|
||||
|
||||
|
|
@ -136,7 +151,7 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
newMetadata.Annotations = filterMetadata(newMetadata.Annotations)
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
route := &v1alpha2.HTTPRoute{
|
||||
route := &v1.HTTPRoute{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: apexSvcName,
|
||||
Namespace: hrNamespace,
|
||||
|
|
@ -156,7 +171,7 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
}
|
||||
}
|
||||
|
||||
_, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).
|
||||
_, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).
|
||||
Create(context.TODO(), route, metav1.CreateOptions{})
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -168,10 +183,42 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
|
||||
}
|
||||
|
||||
ignoreCmpOptions := []cmp.Option{
|
||||
cmpopts.IgnoreFields(v1.BackendRef{}, "Weight"),
|
||||
cmpopts.EquateEmpty(),
|
||||
}
|
||||
if canary.Spec.Analysis.SessionAffinity != nil {
|
||||
ignoreRoute := cmpopts.IgnoreSliceElements(func(r v1.HTTPRouteRule) bool {
|
||||
// Ignore the rule that does sticky routing, i.e. matches against the `Cookie` header.
|
||||
for _, match := range r.Matches {
|
||||
for _, headerMatch := range match.Headers {
|
||||
if *headerMatch.Type == headerMatchRegex && headerMatch.Name == cookieHeader &&
|
||||
strings.Contains(headerMatch.Value, canary.Spec.Analysis.SessionAffinity.CookieName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
ignoreCmpOptions = append(ignoreCmpOptions, ignoreRoute)
|
||||
// Ignore backend specific filters, since we use that to insert the `Set-Cookie` header in responses.
|
||||
ignoreCmpOptions = append(ignoreCmpOptions, cmpopts.IgnoreFields(v1.HTTPBackendRef{}, "Filters"))
|
||||
}
|
||||
|
||||
if canary.GetAnalysis().Mirror {
|
||||
// If a Canary run is in progress, the HTTPRoute rule will have an extra filter of type RequestMirror
|
||||
// which needs to be ignored so that the requests are mirrored to the canary deployment.
|
||||
inProgress := canary.Status.Phase == flaggerv1.CanaryPhaseWaiting || canary.Status.Phase == flaggerv1.CanaryPhaseProgressing ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseWaitingPromotion
|
||||
if inProgress {
|
||||
ignoreCmpOptions = append(ignoreCmpOptions, cmpopts.IgnoreFields(v1.HTTPRouteRule{}, "Filters"))
|
||||
}
|
||||
}
|
||||
|
||||
if httpRoute != nil {
|
||||
specDiff := cmp.Diff(
|
||||
httpRoute.Spec, httpRouteSpec,
|
||||
cmpopts.IgnoreFields(v1alpha2.BackendRef{}, "Weight"),
|
||||
ignoreCmpOptions...,
|
||||
)
|
||||
labelsDiff := cmp.Diff(newMetadata.Labels, httpRoute.Labels, cmpopts.EquateEmpty())
|
||||
annotationsDiff := cmp.Diff(newMetadata.Annotations, httpRoute.Annotations, cmpopts.EquateEmpty())
|
||||
|
|
@ -180,7 +227,7 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|||
hrClone.Spec = httpRouteSpec
|
||||
hrClone.ObjectMeta.Annotations = newMetadata.Annotations
|
||||
hrClone.ObjectMeta.Labels = newMetadata.Labels
|
||||
_, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).
|
||||
_, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).
|
||||
Update(context.TODO(), hrClone, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("HTTPRoute %s.%s update error: %w while reconciling", hrClone.GetName(), hrNamespace, err)
|
||||
|
|
@ -201,24 +248,52 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) (
|
|||
) {
|
||||
apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames()
|
||||
hrNamespace := canary.Namespace
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
|
||||
return
|
||||
}
|
||||
var weightedRule *v1.HTTPRouteRule
|
||||
for _, rule := range httpRoute.Spec.Rules {
|
||||
// A/B testing: Avoid reading the rule with only for backendRef.
|
||||
if len(rule.BackendRefs) == 2 {
|
||||
// If session affinity is enabled, then we are only interested in the rule
|
||||
// that has backend-specific filters, as that's the rule that does weighted
|
||||
// routing.
|
||||
if canary.Spec.Analysis.SessionAffinity != nil {
|
||||
for _, backendRef := range rule.BackendRefs {
|
||||
if backendRef.Name == v1alpha2.ObjectName(primarySvcName) {
|
||||
primaryWeight = int(*backendRef.Weight)
|
||||
}
|
||||
if backendRef.Name == v1alpha2.ObjectName(canarySvcName) {
|
||||
canaryWeight = int(*backendRef.Weight)
|
||||
if len(backendRef.Filters) > 0 {
|
||||
weightedRule = &rule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A/B testing: Avoid reading the rule with only for backendRef.
|
||||
if len(rule.BackendRefs) == 2 {
|
||||
for _, backendRef := range rule.BackendRefs {
|
||||
if backendRef.Name == v1.ObjectName(primarySvcName) {
|
||||
primaryWeight = int(*backendRef.Weight)
|
||||
}
|
||||
if backendRef.Name == v1.ObjectName(canarySvcName) {
|
||||
canaryWeight = int(*backendRef.Weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, filter := range rule.Filters {
|
||||
if filter.Type == v1.HTTPRouteFilterRequestMirror && filter.RequestMirror != nil &&
|
||||
string(filter.RequestMirror.BackendRef.Name) == canarySvcName {
|
||||
mirrored = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if weightedRule != nil {
|
||||
for _, backendRef := range weightedRule.BackendRefs {
|
||||
if backendRef.Name == v1.ObjectName(primarySvcName) {
|
||||
primaryWeight = int(*backendRef.Weight)
|
||||
}
|
||||
if backendRef.Name == v1.ObjectName(canarySvcName) {
|
||||
canaryWeight = int(*backendRef.Weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -233,63 +308,113 @@ func (gwr *GatewayAPIRouter) SetRoutes(
|
|||
cWeight := int32(canaryWeight)
|
||||
apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames()
|
||||
hrNamespace := canary.Namespace
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
|
||||
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
|
||||
}
|
||||
hrClone := httpRoute.DeepCopy()
|
||||
hostNames := []v1alpha2.Hostname{}
|
||||
hostNames := []v1.Hostname{}
|
||||
for _, host := range canary.Spec.Service.Hosts {
|
||||
hostNames = append(hostNames, v1alpha2.Hostname(host))
|
||||
hostNames = append(hostNames, v1.Hostname(host))
|
||||
}
|
||||
matches, err := gwr.mapRouteMatches(canary.Spec.Service.Match)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid request matching selectors: %w", err)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
matches = append(matches, v1alpha2.HTTPRouteMatch{
|
||||
Path: &v1alpha2.HTTPPathMatch{
|
||||
matches = append(matches, v1.HTTPRouteMatch{
|
||||
Path: &v1.HTTPPathMatch{
|
||||
Type: &pathMatchType,
|
||||
Value: &pathMatchValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
httpRouteSpec := v1alpha2.HTTPRouteSpec{
|
||||
CommonRouteSpec: v1alpha2.CommonRouteSpec{
|
||||
ParentRefs: toV1alpha2ParentRefs(canary.Spec.Service.GatewayRefs),
|
||||
},
|
||||
Hostnames: hostNames,
|
||||
Rules: []v1alpha2.HTTPRouteRule{
|
||||
var timeout v1.Duration
|
||||
if canary.Spec.Service.Timeout != "" {
|
||||
timeout = v1.Duration(canary.Spec.Service.Timeout)
|
||||
}
|
||||
|
||||
weightedRouteRule := &v1.HTTPRouteRule{
|
||||
Matches: matches,
|
||||
Filters: gwr.makeFilters(canary),
|
||||
BackendRefs: []v1.HTTPBackendRef{
|
||||
{
|
||||
Matches: matches,
|
||||
BackendRefs: []v1alpha2.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, pWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(canarySvcName, cWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, pWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(canarySvcName, cWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
}
|
||||
if canary.Spec.Service.Timeout != "" {
|
||||
timeout := v1.Duration(canary.Spec.Service.Timeout)
|
||||
weightedRouteRule.Timeouts = &v1.HTTPRouteTimeouts{
|
||||
Request: &timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// If B/G mirroring is enabled, then add a route filter which mirrors the traffic
|
||||
// to the canary service.
|
||||
if mirrored && canary.GetAnalysis().Iterations > 0 {
|
||||
weightedRouteRule.Filters = append(weightedRouteRule.Filters, v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterRequestMirror,
|
||||
RequestMirror: &v1.HTTPRequestMirrorFilter{
|
||||
BackendRef: v1.BackendObjectReference{
|
||||
Group: (*v1.Group)(&backendRefGroup),
|
||||
Kind: (*v1.Kind)(&backendRefKind),
|
||||
Name: v1.ObjectName(canarySvcName),
|
||||
Port: (*v1.PortNumber)(&canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
httpRouteSpec := v1.HTTPRouteSpec{
|
||||
CommonRouteSpec: v1.CommonRouteSpec{
|
||||
ParentRefs: toV1ParentRefs(canary.Spec.Service.GatewayRefs),
|
||||
},
|
||||
Hostnames: hostNames,
|
||||
Rules: []v1.HTTPRouteRule{
|
||||
*weightedRouteRule,
|
||||
},
|
||||
}
|
||||
|
||||
if canary.Spec.Analysis.SessionAffinity != nil {
|
||||
rules, err := gwr.getSessionAffinityRouteRules(canary, canaryWeight, weightedRouteRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpRouteSpec.Rules = rules
|
||||
}
|
||||
|
||||
hrClone.Spec = httpRouteSpec
|
||||
|
||||
// A/B testing
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
analysisMatches, _ := gwr.mapRouteMatches(canary.GetAnalysis().Match)
|
||||
hrClone.Spec.Rules[0].Matches = gwr.mergeMatchConditions(analysisMatches, matches)
|
||||
hrClone.Spec.Rules = append(hrClone.Spec.Rules, v1alpha2.HTTPRouteRule{
|
||||
hrClone.Spec.Rules = append(hrClone.Spec.Rules, v1.HTTPRouteRule{
|
||||
Matches: matches,
|
||||
BackendRefs: []v1alpha2.HTTPBackendRef{
|
||||
Filters: gwr.makeFilters(canary),
|
||||
BackendRefs: []v1.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
Timeouts: &v1.HTTPRouteTimeouts{
|
||||
Request: &timeout,
|
||||
},
|
||||
})
|
||||
|
||||
if canary.Spec.Service.Timeout != "" {
|
||||
timeout := v1.Duration(canary.Spec.Service.Timeout)
|
||||
hrClone.Spec.Rules[1].Timeouts = &v1.HTTPRouteTimeouts{
|
||||
Request: &timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Update(context.TODO(), hrClone, metav1.UpdateOptions{})
|
||||
_, err = gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Update(context.TODO(), hrClone, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("HTTPRoute %s.%s update error: %w while setting weights", hrClone.GetName(), hrNamespace, err)
|
||||
}
|
||||
|
|
@ -301,24 +426,130 @@ func (gwr *GatewayAPIRouter) Finalize(_ *flaggerv1.Canary) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatchRequest) ([]v1alpha2.HTTPRouteMatch, error) {
|
||||
matches := []v1alpha2.HTTPRouteMatch{}
|
||||
// getSessionAffinityRouteRules returns the HTTPRouteRule objects required to perform
|
||||
// session affinity based Canary releases.
|
||||
func (gwr *GatewayAPIRouter) getSessionAffinityRouteRules(canary *flaggerv1.Canary, canaryWeight int,
|
||||
weightedRouteRule *v1.HTTPRouteRule) ([]v1.HTTPRouteRule, error) {
|
||||
_, primarySvcName, canarySvcName := canary.GetServiceNames()
|
||||
stickyRouteRule := *weightedRouteRule
|
||||
|
||||
// If a canary run is active, we want all responses corresponding to requests hitting the canary deployment
|
||||
// (due to weighted routing) to include a `Set-Cookie` header. All requests that have the `Cookie` header
|
||||
// and match the value of the `Set-Cookie` header will be routed to the canary deployment.
|
||||
if canaryWeight != 0 {
|
||||
if canary.Status.SessionAffinityCookie == "" {
|
||||
canary.Status.SessionAffinityCookie = fmt.Sprintf("%s=%s", canary.Spec.Analysis.SessionAffinity.CookieName, randSeq())
|
||||
}
|
||||
|
||||
// Add `Set-Cookie` header modifier to the primary backend in the weighted routing rule.
|
||||
for i, backendRef := range weightedRouteRule.BackendRefs {
|
||||
if string(backendRef.BackendObjectReference.Name) == canarySvcName {
|
||||
backendRef.Filters = append(backendRef.Filters, v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterResponseHeaderModifier,
|
||||
ResponseHeaderModifier: &v1.HTTPHeaderFilter{
|
||||
Add: []v1.HTTPHeader{
|
||||
{
|
||||
Name: setCookieHeader,
|
||||
Value: fmt.Sprintf("%s; %s=%d", canary.Status.SessionAffinityCookie, maxAgeAttr,
|
||||
canary.Spec.Analysis.SessionAffinity.GetMaxAge(),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
weightedRouteRule.BackendRefs[i] = backendRef
|
||||
}
|
||||
|
||||
// Add `Cookie` header matcher to the sticky routing rule.
|
||||
cookieKeyAndVal := strings.Split(canary.Status.SessionAffinityCookie, "=")
|
||||
regexMatchType := v1.HeaderMatchRegularExpression
|
||||
cookieMatch := v1.HTTPRouteMatch{
|
||||
Headers: []v1.HTTPHeaderMatch{
|
||||
{
|
||||
Type: ®exMatchType,
|
||||
Name: cookieHeader,
|
||||
Value: fmt.Sprintf(".*%s.*%s.*", cookieKeyAndVal[0], cookieKeyAndVal[1]),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
svcMatches, err := gwr.mapRouteMatches(canary.Spec.Service.Match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergedMatches := gwr.mergeMatchConditions([]v1.HTTPRouteMatch{cookieMatch}, svcMatches)
|
||||
stickyRouteRule.Matches = mergedMatches
|
||||
stickyRouteRule.BackendRefs = []v1.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(primarySvcName, 0, canary.Spec.Service.Port),
|
||||
},
|
||||
{
|
||||
BackendRef: gwr.makeBackendRef(canarySvcName, 100, canary.Spec.Service.Port),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// If canary weight is 0 and SessionAffinityCookie is non-blank, then it belongs to a previous canary run.
|
||||
if canary.Status.SessionAffinityCookie != "" {
|
||||
canary.Status.PreviousSessionAffinityCookie = canary.Status.SessionAffinityCookie
|
||||
}
|
||||
previousCookie := canary.Status.PreviousSessionAffinityCookie
|
||||
|
||||
// Match against the previous session cookie and delete that cookie
|
||||
if previousCookie != "" {
|
||||
cookieKeyAndVal := strings.Split(previousCookie, "=")
|
||||
regexMatchType := v1.HeaderMatchRegularExpression
|
||||
cookieMatch := v1.HTTPRouteMatch{
|
||||
Headers: []v1.HTTPHeaderMatch{
|
||||
{
|
||||
Type: ®exMatchType,
|
||||
Name: cookieHeader,
|
||||
Value: fmt.Sprintf(".*%s.*%s.*", cookieKeyAndVal[0], cookieKeyAndVal[1]),
|
||||
},
|
||||
},
|
||||
}
|
||||
svcMatches, _ := gwr.mapRouteMatches(canary.Spec.Service.Match)
|
||||
mergedMatches := gwr.mergeMatchConditions([]v1.HTTPRouteMatch{cookieMatch}, svcMatches)
|
||||
stickyRouteRule.Matches = mergedMatches
|
||||
|
||||
stickyRouteRule.Filters = append(stickyRouteRule.Filters, v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterResponseHeaderModifier,
|
||||
ResponseHeaderModifier: &v1.HTTPHeaderFilter{
|
||||
Add: []v1.HTTPHeader{
|
||||
{
|
||||
Name: setCookieHeader,
|
||||
Value: fmt.Sprintf("%s; %s=%d", previousCookie, maxAgeAttr, -1),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
canary.Status.SessionAffinityCookie = ""
|
||||
}
|
||||
|
||||
return []v1.HTTPRouteRule{stickyRouteRule, *weightedRouteRule}, nil
|
||||
}
|
||||
|
||||
func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatchRequest) ([]v1.HTTPRouteMatch, error) {
|
||||
matches := []v1.HTTPRouteMatch{}
|
||||
|
||||
for _, requestMatch := range requestMatches {
|
||||
match := v1alpha2.HTTPRouteMatch{}
|
||||
match := v1.HTTPRouteMatch{}
|
||||
if requestMatch.Uri != nil {
|
||||
if requestMatch.Uri.Regex != "" {
|
||||
match.Path = &v1alpha2.HTTPPathMatch{
|
||||
match.Path = &v1.HTTPPathMatch{
|
||||
Type: &pathMatchRegex,
|
||||
Value: &requestMatch.Uri.Regex,
|
||||
}
|
||||
} else if requestMatch.Uri.Exact != "" {
|
||||
match.Path = &v1alpha2.HTTPPathMatch{
|
||||
match.Path = &v1.HTTPPathMatch{
|
||||
Type: &pathMatchExact,
|
||||
Value: &requestMatch.Uri.Exact,
|
||||
}
|
||||
} else if requestMatch.Uri.Prefix != "" {
|
||||
match.Path = &v1alpha2.HTTPPathMatch{
|
||||
match.Path = &v1.HTTPPathMatch{
|
||||
Type: &pathMatchPrefix,
|
||||
Value: &requestMatch.Uri.Prefix,
|
||||
}
|
||||
|
|
@ -328,50 +559,50 @@ func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatch
|
|||
}
|
||||
if requestMatch.Method != nil {
|
||||
if requestMatch.Method.Exact != "" {
|
||||
method := v1alpha2.HTTPMethod(requestMatch.Method.Exact)
|
||||
method := v1.HTTPMethod(requestMatch.Method.Exact)
|
||||
match.Method = &method
|
||||
} else {
|
||||
return nil, fmt.Errorf("Gateway API doesn't support the specified header matching selector: %+v\n", requestMatch.Headers)
|
||||
}
|
||||
}
|
||||
for key, val := range requestMatch.Headers {
|
||||
headerMatch := v1alpha2.HTTPHeaderMatch{}
|
||||
headerMatch := v1.HTTPHeaderMatch{}
|
||||
if val.Exact != "" {
|
||||
headerMatch.Name = v1alpha2.HTTPHeaderName(key)
|
||||
headerMatch.Name = v1.HTTPHeaderName(key)
|
||||
headerMatch.Type = &headerMatchExact
|
||||
headerMatch.Value = val.Exact
|
||||
} else if val.Regex != "" {
|
||||
headerMatch.Name = v1alpha2.HTTPHeaderName(key)
|
||||
headerMatch.Name = v1.HTTPHeaderName(key)
|
||||
headerMatch.Type = &headerMatchRegex
|
||||
headerMatch.Value = val.Regex
|
||||
} else {
|
||||
return nil, fmt.Errorf("Gateway API doesn't support the specified header matching selector: %+v\n", requestMatch.Headers)
|
||||
}
|
||||
if (v1alpha2.HTTPHeaderMatch{} != headerMatch) {
|
||||
if (v1.HTTPHeaderMatch{} != headerMatch) {
|
||||
match.Headers = append(match.Headers, headerMatch)
|
||||
}
|
||||
}
|
||||
|
||||
for key, val := range requestMatch.QueryParams {
|
||||
queryMatch := v1alpha2.HTTPQueryParamMatch{}
|
||||
queryMatch := v1.HTTPQueryParamMatch{}
|
||||
if val.Exact != "" {
|
||||
queryMatch.Name = key
|
||||
queryMatch.Name = v1.HTTPHeaderName(key)
|
||||
queryMatch.Type = &queryMatchExact
|
||||
queryMatch.Value = val.Exact
|
||||
} else if val.Regex != "" {
|
||||
queryMatch.Name = key
|
||||
queryMatch.Name = v1.HTTPHeaderName(key)
|
||||
queryMatch.Type = &queryMatchRegex
|
||||
queryMatch.Value = val.Regex
|
||||
} else {
|
||||
return nil, fmt.Errorf("Gateway API doesn't support the specified query matching selector: %+v\n", requestMatch.QueryParams)
|
||||
}
|
||||
|
||||
if (v1alpha2.HTTPQueryParamMatch{} != queryMatch) {
|
||||
if (v1.HTTPQueryParamMatch{} != queryMatch) {
|
||||
match.QueryParams = append(match.QueryParams, queryMatch)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(match, v1alpha2.HTTPRouteMatch{}) {
|
||||
if !reflect.DeepEqual(match, v1.HTTPRouteMatch{}) {
|
||||
matches = append(matches, match)
|
||||
}
|
||||
}
|
||||
|
|
@ -379,24 +610,27 @@ func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatch
|
|||
return matches, nil
|
||||
}
|
||||
|
||||
func (gwr *GatewayAPIRouter) makeBackendRef(svcName string, weight, port int32) v1alpha2.BackendRef {
|
||||
return v1alpha2.BackendRef{
|
||||
BackendObjectReference: v1alpha2.BackendObjectReference{
|
||||
Group: (*v1alpha2.Group)(&backendRefGroup),
|
||||
Kind: (*v1alpha2.Kind)(&backendRefKind),
|
||||
Name: v1alpha2.ObjectName(svcName),
|
||||
Port: (*v1alpha2.PortNumber)(&port),
|
||||
func (gwr *GatewayAPIRouter) makeBackendRef(svcName string, weight, port int32) v1.BackendRef {
|
||||
return v1.BackendRef{
|
||||
BackendObjectReference: v1.BackendObjectReference{
|
||||
Group: (*v1.Group)(&backendRefGroup),
|
||||
Kind: (*v1.Kind)(&backendRefKind),
|
||||
Name: v1.ObjectName(svcName),
|
||||
Port: (*v1.PortNumber)(&port),
|
||||
},
|
||||
Weight: &weight,
|
||||
}
|
||||
}
|
||||
|
||||
func (gwr *GatewayAPIRouter) mergeMatchConditions(analysis, service []v1alpha2.HTTPRouteMatch) []v1alpha2.HTTPRouteMatch {
|
||||
func (gwr *GatewayAPIRouter) mergeMatchConditions(analysis, service []v1.HTTPRouteMatch) []v1.HTTPRouteMatch {
|
||||
if len(analysis) == 0 {
|
||||
return service
|
||||
}
|
||||
if len(service) == 0 {
|
||||
return analysis
|
||||
}
|
||||
|
||||
merged := make([]v1alpha2.HTTPRouteMatch, len(service)*len(analysis))
|
||||
merged := make([]v1.HTTPRouteMatch, len(service)*len(analysis))
|
||||
num := 0
|
||||
for _, a := range analysis {
|
||||
for _, s := range service {
|
||||
|
|
@ -413,16 +647,120 @@ func (gwr *GatewayAPIRouter) mergeMatchConditions(analysis, service []v1alpha2.H
|
|||
return merged
|
||||
}
|
||||
|
||||
func toV1alpha2ParentRefs(gatewayRefs []v1beta1.ParentReference) []v1alpha2.ParentReference {
|
||||
parentRefs := make([]v1alpha2.ParentReference, 0)
|
||||
func (gwr *GatewayAPIRouter) makeFilters(canary *flaggerv1.Canary) []v1.HTTPRouteFilter {
|
||||
var filters []v1.HTTPRouteFilter
|
||||
|
||||
if canary.Spec.Service.Headers != nil {
|
||||
if canary.Spec.Service.Headers.Request != nil {
|
||||
requestHeaderFilter := v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterRequestHeaderModifier,
|
||||
RequestHeaderModifier: &v1.HTTPHeaderFilter{},
|
||||
}
|
||||
|
||||
for name, val := range canary.Spec.Service.Headers.Request.Add {
|
||||
requestHeaderFilter.RequestHeaderModifier.Add = append(requestHeaderFilter.RequestHeaderModifier.Add, v1.HTTPHeader{
|
||||
Name: v1.HTTPHeaderName(name),
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
for name, val := range canary.Spec.Service.Headers.Request.Set {
|
||||
requestHeaderFilter.RequestHeaderModifier.Set = append(requestHeaderFilter.RequestHeaderModifier.Set, v1.HTTPHeader{
|
||||
Name: v1.HTTPHeaderName(name),
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
|
||||
for _, name := range canary.Spec.Service.Headers.Request.Remove {
|
||||
requestHeaderFilter.RequestHeaderModifier.Remove = append(requestHeaderFilter.RequestHeaderModifier.Remove, name)
|
||||
}
|
||||
|
||||
filters = append(filters, requestHeaderFilter)
|
||||
}
|
||||
if canary.Spec.Service.Headers.Response != nil {
|
||||
responseHeaderFilter := v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterResponseHeaderModifier,
|
||||
ResponseHeaderModifier: &v1.HTTPHeaderFilter{},
|
||||
}
|
||||
|
||||
for name, val := range canary.Spec.Service.Headers.Response.Add {
|
||||
responseHeaderFilter.ResponseHeaderModifier.Add = append(responseHeaderFilter.ResponseHeaderModifier.Add, v1.HTTPHeader{
|
||||
Name: v1.HTTPHeaderName(name),
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
for name, val := range canary.Spec.Service.Headers.Response.Set {
|
||||
responseHeaderFilter.ResponseHeaderModifier.Set = append(responseHeaderFilter.ResponseHeaderModifier.Set, v1.HTTPHeader{
|
||||
Name: v1.HTTPHeaderName(name),
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
|
||||
for _, name := range canary.Spec.Service.Headers.Response.Remove {
|
||||
responseHeaderFilter.ResponseHeaderModifier.Remove = append(responseHeaderFilter.ResponseHeaderModifier.Remove, name)
|
||||
}
|
||||
|
||||
filters = append(filters, responseHeaderFilter)
|
||||
}
|
||||
}
|
||||
|
||||
if canary.Spec.Service.Rewrite != nil {
|
||||
rewriteFilter := v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterURLRewrite,
|
||||
URLRewrite: &v1.HTTPURLRewriteFilter{},
|
||||
}
|
||||
if canary.Spec.Service.Rewrite.Authority != "" {
|
||||
hostname := v1.PreciseHostname(canary.Spec.Service.Rewrite.Authority)
|
||||
rewriteFilter.URLRewrite.Hostname = &hostname
|
||||
}
|
||||
if canary.Spec.Service.Rewrite.Uri != "" {
|
||||
rewriteFilter.URLRewrite.Path = &v1.HTTPPathModifier{
|
||||
Type: v1.HTTPPathModifierType(canary.Spec.Service.Rewrite.GetType()),
|
||||
}
|
||||
if rewriteFilter.URLRewrite.Path.Type == v1.FullPathHTTPPathModifier {
|
||||
rewriteFilter.URLRewrite.Path.ReplaceFullPath = &canary.Spec.Service.Rewrite.Uri
|
||||
} else {
|
||||
rewriteFilter.URLRewrite.Path.ReplacePrefixMatch = &canary.Spec.Service.Rewrite.Uri
|
||||
}
|
||||
}
|
||||
|
||||
filters = append(filters, rewriteFilter)
|
||||
}
|
||||
|
||||
for _, mirror := range canary.Spec.Service.Mirror {
|
||||
mirror := mirror
|
||||
mirrorFilter := v1.HTTPRouteFilter{
|
||||
Type: v1.HTTPRouteFilterRequestMirror,
|
||||
RequestMirror: toV1RequestMirrorFilter(mirror),
|
||||
}
|
||||
filters = append(filters, mirrorFilter)
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
func toV1RequestMirrorFilter(requestMirror v1beta1.HTTPRequestMirrorFilter) *v1.HTTPRequestMirrorFilter {
|
||||
return &v1.HTTPRequestMirrorFilter{
|
||||
BackendRef: v1.BackendObjectReference{
|
||||
Group: (*v1.Group)(requestMirror.BackendRef.Group),
|
||||
Kind: (*v1.Kind)(requestMirror.BackendRef.Kind),
|
||||
Namespace: (*v1.Namespace)(requestMirror.BackendRef.Namespace),
|
||||
Name: (v1.ObjectName)(requestMirror.BackendRef.Name),
|
||||
Port: (*v1.PortNumber)(requestMirror.BackendRef.Port),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toV1ParentRefs(gatewayRefs []v1beta1.ParentReference) []v1.ParentReference {
|
||||
parentRefs := make([]v1.ParentReference, 0)
|
||||
for i := 0; i < len(gatewayRefs); i++ {
|
||||
gatewayRef := gatewayRefs[i]
|
||||
parentRefs = append(parentRefs, v1alpha2.ParentReference{
|
||||
Group: (*v1alpha2.Group)(gatewayRef.Group),
|
||||
Kind: (*v1alpha2.Kind)(gatewayRef.Kind),
|
||||
Namespace: (*v1alpha2.Namespace)(gatewayRef.Namespace),
|
||||
Name: (v1alpha2.ObjectName)(gatewayRef.Name),
|
||||
SectionName: (*v1alpha2.SectionName)(gatewayRef.SectionName),
|
||||
parentRefs = append(parentRefs, v1.ParentReference{
|
||||
Group: (*v1.Group)(gatewayRef.Group),
|
||||
Kind: (*v1.Kind)(gatewayRef.Kind),
|
||||
Namespace: (*v1.Namespace)(gatewayRef.Namespace),
|
||||
Name: (v1.ObjectName)(gatewayRef.Name),
|
||||
SectionName: (*v1.SectionName)(gatewayRef.SectionName),
|
||||
Port: (*v1.PortNumber)(gatewayRef.Port),
|
||||
})
|
||||
}
|
||||
return parentRefs
|
||||
|
|
|
|||
|
|
@ -18,8 +18,13 @@ package router
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -37,7 +42,7 @@ func TestGatewayAPIRouter_Reconcile(t *testing.T) {
|
|||
err := router.Reconcile(canary)
|
||||
require.NoError(t, err)
|
||||
|
||||
httpRoute, err := router.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
httpRoute, err := router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
routeRules := httpRoute.Spec.Rules
|
||||
|
|
@ -47,6 +52,9 @@ func TestGatewayAPIRouter_Reconcile(t *testing.T) {
|
|||
require.Equal(t, len(backendRefs), 2)
|
||||
assert.Equal(t, int32(100), *backendRefs[0].Weight)
|
||||
assert.Equal(t, int32(0), *backendRefs[1].Weight)
|
||||
|
||||
timeout := routeRules[0].Timeouts
|
||||
assert.Equal(t, string(*timeout.Request), canary.Spec.Service.Timeout)
|
||||
}
|
||||
|
||||
func TestGatewayAPIRouter_Routes(t *testing.T) {
|
||||
|
|
@ -61,12 +69,286 @@ func TestGatewayAPIRouter_Routes(t *testing.T) {
|
|||
err := router.Reconcile(canary)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = router.SetRoutes(canary, 50, 50, false)
|
||||
require.NoError(t, err)
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
err = router.SetRoutes(canary, 50, 50, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
httpRoute, err := router.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
httpRoute, err := router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
primary := httpRoute.Spec.Rules[0].BackendRefs[0]
|
||||
assert.Equal(t, int32(50), *primary.Weight)
|
||||
primary := httpRoute.Spec.Rules[0].BackendRefs[0]
|
||||
assert.Equal(t, int32(50), *primary.Weight)
|
||||
})
|
||||
|
||||
t.Run("session affinity", func(t *testing.T) {
|
||||
canary := mocks.canary.DeepCopy()
|
||||
cookieKey := "flagger-cookie"
|
||||
// enable session affinity and start canary run
|
||||
canary.Spec.Analysis.SessionAffinity = &flaggerv1.SessionAffinity{
|
||||
CookieName: cookieKey,
|
||||
MaxAge: 300,
|
||||
}
|
||||
_, pSvcName, cSvcName := canary.GetServiceNames()
|
||||
|
||||
err := router.SetRoutes(canary, 90, 10, false)
|
||||
|
||||
hr, err := mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hr.Spec.Rules, 2)
|
||||
|
||||
stickyRule := hr.Spec.Rules[0]
|
||||
weightedRule := hr.Spec.Rules[1]
|
||||
|
||||
// stickyRoute should match against a cookie and direct all traffic to the canary when a canary run is active.
|
||||
cookieMatch := stickyRule.Matches[0].Headers[0]
|
||||
assert.Equal(t, *cookieMatch.Type, v1.HeaderMatchRegularExpression)
|
||||
assert.Equal(t, string(cookieMatch.Name), cookieHeader)
|
||||
assert.Contains(t, cookieMatch.Value, cookieKey)
|
||||
|
||||
assert.Equal(t, len(stickyRule.BackendRefs), 2)
|
||||
for _, backendRef := range stickyRule.BackendRefs {
|
||||
if string(backendRef.BackendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(0))
|
||||
}
|
||||
if string(backendRef.BackendRef.Name) == cSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(100))
|
||||
}
|
||||
}
|
||||
|
||||
// weightedRoute should do regular weight based routing and inject the Set-Cookie header
|
||||
// for all responses returned from the canary deployment.
|
||||
var found bool
|
||||
for _, backendRef := range weightedRule.BackendRefs {
|
||||
if string(backendRef.Name) == cSvcName {
|
||||
found = true
|
||||
filter := backendRef.Filters[0]
|
||||
assert.Equal(t, filter.Type, v1.HTTPRouteFilterResponseHeaderModifier)
|
||||
assert.NotNil(t, filter.ResponseHeaderModifier)
|
||||
assert.Equal(t, string(filter.ResponseHeaderModifier.Add[0].Name), setCookieHeader)
|
||||
assert.Equal(t, filter.ResponseHeaderModifier.Add[0].Value, fmt.Sprintf("%s; %s=%d", canary.Status.SessionAffinityCookie, maxAgeAttr, 300))
|
||||
assert.Equal(t, *backendRef.Weight, int32(10))
|
||||
}
|
||||
if string(backendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.Weight, int32(90))
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
assert.True(t, strings.HasPrefix(canary.Status.SessionAffinityCookie, cookieKey))
|
||||
|
||||
// reconcile Canary and HTTPRoute
|
||||
err = router.Reconcile(canary)
|
||||
require.NoError(t, err)
|
||||
|
||||
// HTTPRoute should be unchanged
|
||||
hr, err = mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hr.Spec.Rules, 2)
|
||||
assert.Empty(t, cmp.Diff(hr.Spec.Rules[0], stickyRule))
|
||||
assert.Empty(t, cmp.Diff(hr.Spec.Rules[1], weightedRule))
|
||||
|
||||
// further continue the canary run
|
||||
err = router.SetRoutes(canary, 50, 50, false)
|
||||
require.NoError(t, err)
|
||||
hr, err = mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
stickyRule = hr.Spec.Rules[0]
|
||||
weightedRule = hr.Spec.Rules[1]
|
||||
|
||||
// stickyRoute should match against a cookie and direct all traffic to the canary when a canary run is active.
|
||||
cookieMatch = stickyRule.Matches[0].Headers[0]
|
||||
assert.Equal(t, *cookieMatch.Type, v1.HeaderMatchRegularExpression)
|
||||
assert.Equal(t, string(cookieMatch.Name), cookieHeader)
|
||||
assert.Contains(t, cookieMatch.Value, cookieKey)
|
||||
|
||||
assert.Equal(t, len(stickyRule.BackendRefs), 2)
|
||||
for _, backendRef := range stickyRule.BackendRefs {
|
||||
if string(backendRef.BackendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(0))
|
||||
}
|
||||
if string(backendRef.BackendRef.Name) == cSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(100))
|
||||
}
|
||||
}
|
||||
|
||||
// weightedRoute should do regular weight based routing and inject the Set-Cookie header
|
||||
// for all responses returned from the canary deployment.
|
||||
found = false
|
||||
for _, backendRef := range weightedRule.BackendRefs {
|
||||
if string(backendRef.Name) == cSvcName {
|
||||
found = true
|
||||
filter := backendRef.Filters[0]
|
||||
assert.Equal(t, filter.Type, v1.HTTPRouteFilterResponseHeaderModifier)
|
||||
assert.NotNil(t, filter.ResponseHeaderModifier)
|
||||
assert.Equal(t, string(filter.ResponseHeaderModifier.Add[0].Name), setCookieHeader)
|
||||
assert.Equal(t, filter.ResponseHeaderModifier.Add[0].Value, fmt.Sprintf("%s; %s=%d", canary.Status.SessionAffinityCookie, maxAgeAttr, 300))
|
||||
|
||||
assert.Equal(t, *backendRef.Weight, int32(50))
|
||||
}
|
||||
if string(backendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.Weight, int32(50))
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
|
||||
// promotion
|
||||
err = router.SetRoutes(canary, 100, 0, false)
|
||||
require.NoError(t, err)
|
||||
hr, err = mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, canary.Status.SessionAffinityCookie)
|
||||
assert.Contains(t, canary.Status.PreviousSessionAffinityCookie, cookieKey)
|
||||
|
||||
stickyRule = hr.Spec.Rules[0]
|
||||
weightedRule = hr.Spec.Rules[1]
|
||||
|
||||
// Assert that the stucky rule matches against the previous cookie and tells clients to delete it.
|
||||
cookieMatch = stickyRule.Matches[0].Headers[0]
|
||||
assert.Equal(t, *cookieMatch.Type, v1.HeaderMatchRegularExpression)
|
||||
assert.Equal(t, string(cookieMatch.Name), cookieHeader)
|
||||
assert.Contains(t, cookieMatch.Value, cookieKey)
|
||||
|
||||
assert.Equal(t, stickyRule.Filters[0].Type, v1.HTTPRouteFilterResponseHeaderModifier)
|
||||
headerModifier := stickyRule.Filters[0].ResponseHeaderModifier
|
||||
assert.NotNil(t, headerModifier)
|
||||
assert.Equal(t, string(headerModifier.Add[0].Name), setCookieHeader)
|
||||
assert.Equal(t, headerModifier.Add[0].Value, fmt.Sprintf("%s; %s=%d", canary.Status.PreviousSessionAffinityCookie, maxAgeAttr, -1))
|
||||
|
||||
for _, backendRef := range stickyRule.BackendRefs {
|
||||
if string(backendRef.BackendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(100))
|
||||
}
|
||||
if string(backendRef.BackendRef.Name) == cSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(0))
|
||||
}
|
||||
}
|
||||
|
||||
for _, backendRef := range weightedRule.BackendRefs {
|
||||
if string(backendRef.Name) == cSvcName {
|
||||
// Assert the weighted rule does not send Set-Cookie headers anymore
|
||||
assert.Len(t, backendRef.Filters, 0)
|
||||
assert.Equal(t, *backendRef.Weight, int32(0))
|
||||
}
|
||||
if string(backendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.Weight, int32(100))
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
})
|
||||
|
||||
t.Run("b/g mirror", func(t *testing.T) {
|
||||
canary := mocks.canary.DeepCopy()
|
||||
canary.Spec.Analysis.Mirror = true
|
||||
canary.Spec.Analysis.Iterations = 5
|
||||
_, _, cSvcName := canary.GetServiceNames()
|
||||
|
||||
err = router.SetRoutes(canary, 100, 0, true)
|
||||
hr, err := mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hr.Spec.Rules, 1)
|
||||
|
||||
rule := hr.Spec.Rules[0]
|
||||
var found bool
|
||||
for _, filter := range rule.Filters {
|
||||
if filter.Type == v1.HTTPRouteFilterRequestMirror && filter.RequestMirror != nil &&
|
||||
string(filter.RequestMirror.BackendRef.Name) == cSvcName {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "could not find request mirror filter in HTTPRoute")
|
||||
|
||||
// Mark the status as progressing to assert that request mirror filter is ignored.
|
||||
canary.Status.Phase = flaggerv1.CanaryPhaseProgressing
|
||||
err = router.Reconcile(canary)
|
||||
require.NoError(t, err)
|
||||
|
||||
hr, err = mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hr.Spec.Rules, 1)
|
||||
assert.Empty(t, cmp.Diff(hr.Spec.Rules[0], rule))
|
||||
|
||||
err = router.SetRoutes(canary, 100, 0, false)
|
||||
hr, err = mocks.meshClient.GatewayapiV1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, hr.Spec.Rules, 1)
|
||||
assert.Len(t, hr.Spec.Rules[0].Filters, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGatewayAPIRouter_getSessionAffinityRouteRules(t *testing.T) {
|
||||
canary := newTestGatewayAPICanary()
|
||||
mocks := newFixture(canary)
|
||||
cookieKey := "flagger-cookie"
|
||||
canary.Spec.Analysis.SessionAffinity = &flaggerv1.SessionAffinity{
|
||||
CookieName: cookieKey,
|
||||
MaxAge: 300,
|
||||
}
|
||||
|
||||
router := &GatewayAPIRouter{
|
||||
gatewayAPIClient: mocks.meshClient,
|
||||
kubeClient: mocks.kubeClient,
|
||||
logger: mocks.logger,
|
||||
}
|
||||
_, pSvcName, cSvcName := canary.GetServiceNames()
|
||||
weightedRouteRule := &v1.HTTPRouteRule{
|
||||
BackendRefs: []v1.HTTPBackendRef{
|
||||
{
|
||||
BackendRef: router.makeBackendRef(pSvcName, initialPrimaryWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
{
|
||||
BackendRef: router.makeBackendRef(cSvcName, initialCanaryWeight, canary.Spec.Service.Port),
|
||||
},
|
||||
},
|
||||
}
|
||||
rules, err := router.getSessionAffinityRouteRules(canary, 10, weightedRouteRule)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(rules), 2)
|
||||
assert.True(t, strings.HasPrefix(canary.Status.SessionAffinityCookie, cookieKey))
|
||||
|
||||
stickyRule := rules[0]
|
||||
cookieMatch := stickyRule.Matches[0].Headers[0]
|
||||
assert.Equal(t, *cookieMatch.Type, v1.HeaderMatchRegularExpression)
|
||||
assert.Equal(t, string(cookieMatch.Name), cookieHeader)
|
||||
assert.Contains(t, cookieMatch.Value, cookieKey)
|
||||
|
||||
assert.Equal(t, len(stickyRule.BackendRefs), 2)
|
||||
for _, backendRef := range stickyRule.BackendRefs {
|
||||
if string(backendRef.BackendRef.Name) == pSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(0))
|
||||
}
|
||||
if string(backendRef.BackendRef.Name) == cSvcName {
|
||||
assert.Equal(t, *backendRef.BackendRef.Weight, int32(100))
|
||||
}
|
||||
}
|
||||
|
||||
weightedRule := rules[1]
|
||||
var found bool
|
||||
for _, backendRef := range weightedRule.BackendRefs {
|
||||
if string(backendRef.Name) == cSvcName {
|
||||
found = true
|
||||
filter := backendRef.Filters[0]
|
||||
assert.Equal(t, filter.Type, v1.HTTPRouteFilterResponseHeaderModifier)
|
||||
assert.NotNil(t, filter.ResponseHeaderModifier)
|
||||
assert.Equal(t, string(filter.ResponseHeaderModifier.Add[0].Name), setCookieHeader)
|
||||
assert.Equal(t, filter.ResponseHeaderModifier.Add[0].Value, fmt.Sprintf("%s; %s=%d", canary.Status.SessionAffinityCookie, maxAgeAttr, 300))
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
|
||||
rules, err = router.getSessionAffinityRouteRules(canary, 0, weightedRouteRule)
|
||||
assert.Empty(t, canary.Status.SessionAffinityCookie)
|
||||
assert.Contains(t, canary.Status.PreviousSessionAffinityCookie, cookieKey)
|
||||
|
||||
stickyRule = rules[0]
|
||||
cookieMatch = stickyRule.Matches[0].Headers[0]
|
||||
assert.Equal(t, *cookieMatch.Type, v1.HeaderMatchRegularExpression)
|
||||
assert.Equal(t, string(cookieMatch.Name), cookieHeader)
|
||||
assert.Contains(t, cookieMatch.Value, cookieKey)
|
||||
|
||||
assert.Equal(t, stickyRule.Filters[0].Type, v1.HTTPRouteFilterResponseHeaderModifier)
|
||||
headerModifier := stickyRule.Filters[0].ResponseHeaderModifier
|
||||
assert.NotNil(t, headerModifier)
|
||||
assert.Equal(t, string(headerModifier.Add[0].Name), setCookieHeader)
|
||||
assert.Equal(t, headerModifier.Add[0].Value, fmt.Sprintf("%s; %s=%d", canary.Status.PreviousSessionAffinityCookie, maxAgeAttr, -1))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -554,6 +554,7 @@ func newTestGatewayAPICanary() *flaggerv1.Canary {
|
|||
Name: "podinfo",
|
||||
},
|
||||
},
|
||||
Timeout: "10s",
|
||||
},
|
||||
Analysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
|
|
|
|||
|
|
@ -2,62 +2,56 @@
|
|||
|
||||
set -o errexit
|
||||
|
||||
CONTOUR_VER="v1.26.0"
|
||||
GATEWAY_API_VER="v1beta1"
|
||||
GATEWAY_API_VER="v1.0.0"
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
KUSTOMIZE_VERSION=4.5.2
|
||||
OS=$(uname -s)
|
||||
ARCH=$(arch)
|
||||
if [[ $ARCH == "x86_64" ]]; then
|
||||
ARCH="amd64"
|
||||
fi
|
||||
ISTIO_VER="1.20.0"
|
||||
|
||||
mkdir -p ${REPO_ROOT}/bin
|
||||
|
||||
echo ">>> Installing Contour components, Gateway API CRDs"
|
||||
kubectl apply -f https://raw.githubusercontent.com/projectcontour/contour/${CONTOUR_VER}/examples/render/contour-gateway-provisioner.yaml
|
||||
echo ">>> Installing Gateway API CRDs"
|
||||
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
|
||||
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=${GATEWAY_API_VER}" | kubectl apply -f -; }
|
||||
|
||||
kubectl -n projectcontour rollout status deployment/contour-gateway-provisioner
|
||||
kubectl -n gateway-system wait --for=condition=complete job/gateway-api-admission
|
||||
kubectl -n gateway-system wait --for=condition=complete job/gateway-api-admission-patch
|
||||
kubectl -n gateway-system rollout status deployment/gateway-api-admission-server
|
||||
kubectl -n projectcontour get all
|
||||
echo ">>> Downloading Istio ${ISTIO_VER}"
|
||||
cd ${REPO_ROOT}/bin && \
|
||||
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VER} sh -
|
||||
|
||||
echo ">>> Creating GatewayClass"
|
||||
cat <<EOF | kubectl apply -f -
|
||||
kind: GatewayClass
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: contour
|
||||
spec:
|
||||
controllerName: projectcontour.io/gateway-controller
|
||||
EOF
|
||||
echo ">>> Installing Istio ${ISTIO_VER}"
|
||||
${REPO_ROOT}/bin/istio-${ISTIO_VER}/bin/istioctl install --set profile=minimal \
|
||||
--set values.pilot.resources.requests.cpu=100m \
|
||||
--set values.pilot.resources.requests.memory=100Mi -y
|
||||
|
||||
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml
|
||||
kubectl -n istio-system rollout status deployment/prometheus
|
||||
|
||||
echo ">>> Creating Gateway"
|
||||
kubectl create ns istio-ingress
|
||||
cat <<EOF | kubectl apply -f -
|
||||
kind: Gateway
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: contour
|
||||
namespace: projectcontour
|
||||
name: gateway
|
||||
namespace: istio-ingress
|
||||
spec:
|
||||
gatewayClassName: contour
|
||||
gatewayClassName: istio
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
- name: default
|
||||
hostname: "*.example.com"
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
EOF
|
||||
|
||||
echo '>>> Installing Kustomize'
|
||||
cd ${REPO_ROOT}/bin && \
|
||||
curl -sL https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_${OS}_${ARCH}.tar.gz | \
|
||||
tar xz
|
||||
|
||||
echo '>>> Installing Flagger'
|
||||
${REPO_ROOT}/bin/kustomize build ${REPO_ROOT}/kustomize/gatewayapi | kubectl apply -f -
|
||||
kubectl -n flagger-system set image deployment/flagger flagger=test/flagger:latest
|
||||
kubectl create ns flagger-system
|
||||
helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \
|
||||
--set crd.create=false \
|
||||
--namespace flagger-system \
|
||||
--set prometheus.install=false \
|
||||
--set meshProvider=gatewayapi:v1 \
|
||||
--set metricsServer=http://prometheus.istio-system:9090
|
||||
|
||||
kubectl -n flagger-system set image deployment/flagger flagger=test/flagger:latest
|
||||
kubectl -n flagger-system rollout status deployment/flagger
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script runs e2e tests for A/B traffic shifting, Canary analysis and promotion
|
||||
# Prerequisites: Kubernetes Kind and Contour with GatewayAPI
|
||||
# Prerequisites: Kubernetes Kind and Istio with GatewayAPI
|
||||
|
||||
set -o errexit
|
||||
|
||||
|
|
@ -33,10 +33,10 @@ spec:
|
|||
port: 9898
|
||||
portName: http
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
|
|
@ -46,14 +46,6 @@ spec:
|
|||
x-canary:
|
||||
exact: "insider"
|
||||
metrics:
|
||||
# - name: request-success-rate
|
||||
# thresholdRange:
|
||||
# min: 99
|
||||
# interval: 1m
|
||||
# - name: request-duration
|
||||
# threshold: 500
|
||||
# interval: 30s
|
||||
|
||||
- name: error-rate
|
||||
templateRef:
|
||||
name: error-rate
|
||||
|
|
@ -74,7 +66,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io -H 'X-Canary: insider' http://envoy-contour.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com -H 'X-Canary: insider' http://gateway-istio.istio-ingress"
|
||||
logCmdOutput: "true"
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script runs e2e tests for Blue/Green traffic shifting, Canary analysis and promotion
|
||||
# Prerequisites: Kubernetes Kind and Contour with GatewayAPI
|
||||
# Prerequisites: Kubernetes Kind and Istio with GatewayAPI
|
||||
|
||||
set -o errexit
|
||||
|
||||
|
|
@ -9,6 +9,8 @@ REPO_ROOT=$(git rev-parse --show-toplevel)
|
|||
|
||||
source ${REPO_ROOT}/test/gatewayapi/test-utils.sh
|
||||
|
||||
create_request_duration_metric_template
|
||||
|
||||
echo '>>> Deploy podinfo in bg-test namespace'
|
||||
kubectl create ns bg-test
|
||||
kubectl apply -f ${REPO_ROOT}/test/workloads/secret.yaml -n bg-test
|
||||
|
|
@ -30,20 +32,19 @@ spec:
|
|||
port: 9898
|
||||
portName: http
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
interval: 10s
|
||||
threshold: 5
|
||||
iterations: 5
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
- name: custom-requst-duration
|
||||
templateRef:
|
||||
name: request-duration
|
||||
namespace: flagger-system
|
||||
threshold: 500
|
||||
interval: 30s
|
||||
webhooks:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script runs e2e tests for progressive traffic shifting, Canary analysis and promotion
|
||||
# Prerequisites: Kubernetes Kind and Contour with GatewayAPI
|
||||
# Prerequisites: Kubernetes Kind and Istio with GatewayAPI
|
||||
|
||||
set -o errexit
|
||||
|
||||
|
|
@ -29,10 +29,10 @@ spec:
|
|||
port: 9898
|
||||
portName: http
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
|
|
@ -59,7 +59,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy-contour.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress"
|
||||
logCmdOutput: "true"
|
||||
EOF
|
||||
|
||||
|
|
@ -122,13 +122,13 @@ spec:
|
|||
targetPort: 9898
|
||||
portName: http
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
threshold: 6
|
||||
maxWeight: 50
|
||||
stepWeight: 10
|
||||
metrics:
|
||||
|
|
@ -152,7 +152,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy-contour.projectcontour/status/500"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/status/500"
|
||||
logCmdOutput: "true"
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script runs e2e tests for progressive traffic shifting with session affinity, Canary analysis and promotion
|
||||
# Prerequisites: Kubernetes Kind and Contour with GatewayAPI
|
||||
# Prerequisites: Kubernetes Kind and Istio with GatewayAPI
|
||||
|
||||
set -o errexit
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ spec:
|
|||
port: 9898
|
||||
portName: http
|
||||
hosts:
|
||||
- localproject.contour.io
|
||||
- www.example.com
|
||||
gatewayRefs:
|
||||
- name: contour
|
||||
namespace: projectcontour
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
|
|
@ -66,7 +66,7 @@ spec:
|
|||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host localproject.contour.io http://envoy-contour.projectcontour/"
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress"
|
||||
logCmdOutput: "true"
|
||||
EOF
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ check_primary "sa-test"
|
|||
display_httproute "sa-test"
|
||||
|
||||
echo '>>> Port forwarding load balancer'
|
||||
kubectl port-forward -n projectcontour svc/envoy-contour 8888:80 2>&1 > /dev/null &
|
||||
kubectl port-forward -n istio-ingress svc/gateway-istio 8888:80 2>&1 > /dev/null &
|
||||
pf_pid=$!
|
||||
|
||||
cleanup() {
|
||||
|
|
@ -104,7 +104,7 @@ until ${ok}; do
|
|||
done
|
||||
|
||||
echo '>>> Verifying session affinity'
|
||||
if ! URL=http://localhost:8888 HOST=localproject.contour.io VERSION=6.1.0 COOKIE_NAME=flagger-cookie \
|
||||
if ! URL=http://localhost:8888 HOST=www.example.com VERSION=6.1.0 COOKIE_NAME=flagger-cookie \
|
||||
go run ${REPO_ROOT}/test/gatewayapi/verify_session_affinity.go; then
|
||||
echo "failed to verify session affinity"
|
||||
exit $?
|
||||
|
|
@ -145,7 +145,7 @@ done
|
|||
|
||||
echo '>>> Verifying cookie cleanup'
|
||||
canary_cookie=$(kubectl -n sa-test get canary podinfo -o=jsonpath='{.status.previousSessionAffinityCookie}' | xargs)
|
||||
response=$(curl -H "Host: localproject.contour.io" -H "Cookie: $canary_cookie" -D - http://localhost:8888)
|
||||
response=$(curl -H "Host: www.example.com" -H "Cookie: $canary_cookie" -D - http://localhost:8888)
|
||||
|
||||
if [[ $response == *"$canary_cookie"* ]]; then
|
||||
echo "✔ Found previous cookie in response"
|
||||
|
|
|
|||
|
|
@ -36,6 +36,34 @@ display_httproute() {
|
|||
fi
|
||||
}
|
||||
|
||||
create_request_duration_metric_template() {
|
||||
if ! kubectl -n flagger-system get metrictemplates request-duration ; then
|
||||
echo '>>> Create request-duration metric template'
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: MetricTemplate
|
||||
metadata:
|
||||
name: request-duration
|
||||
namespace: flagger-system
|
||||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://prometheus.istio-system:9090
|
||||
query: |
|
||||
histogram_quantile(0.99,
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_bucket{
|
||||
namespace=~"{{ namespace }}",
|
||||
app="{{ target }}",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
create_latency_metric_template() {
|
||||
if ! kubectl -n flagger-system get metrictemplates latency; then
|
||||
echo '>>> Create latency metric template'
|
||||
|
|
@ -48,13 +76,15 @@ create_latency_metric_template() {
|
|||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://flagger-prometheus:9090
|
||||
address: http://prometheus.istio-system:9090
|
||||
query: |
|
||||
histogram_quantile(0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
istio_request_duration_milliseconds_bucket{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
|
|
@ -75,21 +105,25 @@ create_error_rate_metric_template() {
|
|||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://flagger-prometheus:9090
|
||||
address: http://prometheus.istio-system:9090
|
||||
query: |
|
||||
100 - sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
envoy_response_code!~"5.*"
|
||||
istio_requests_total{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
istio_requests_total{
|
||||
reporter="source",
|
||||
destination_workload_namespace=~"{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue