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