diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 9b1d6fcc..6286fb85 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -31,6 +31,7 @@ jobs: - gloo - skipper - kubernetes + - gatewayapi steps: - name: Checkout uses: actions/checkout@v2 diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index e4f5c3eb..361dd2cb 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -495,6 +495,40 @@ spec: type: array items: type: string + gatewayRefs: + description: The list of parent Gateways for a HTTPRoute + maxItems: 32 + type: array + items: + required: + - name + type: object + properties: + group: + default: gateway.networking.k8s.io + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + maxLength: 253 + minLength: 1 + type: string + namespace: + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string corsPolicy: description: Istio Cross-Origin Resource Sharing policy (CORS) type: object diff --git a/charts/flagger/crds/crd.yaml b/charts/flagger/crds/crd.yaml index e4f5c3eb..361dd2cb 100644 --- a/charts/flagger/crds/crd.yaml +++ b/charts/flagger/crds/crd.yaml @@ -495,6 +495,40 @@ spec: type: array items: type: string + gatewayRefs: + description: The list of parent Gateways for a HTTPRoute + maxItems: 32 + type: array + items: + required: + - name + type: object + properties: + group: + default: gateway.networking.k8s.io + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + maxLength: 253 + minLength: 1 + type: string + namespace: + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string corsPolicy: description: Istio Cross-Origin Resource Sharing policy (CORS) type: object diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index ecac5a46..519615a4 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -30,7 +30,7 @@ chmod +x ${CODEGEN_PKG}/generate-groups.sh ${CODEGEN_PKG}/generate-groups.sh all \ 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" \ + "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" \ --output-base "${TEMP_DIR}" \ --go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt diff --git a/kustomize/base/flagger/crd.yaml b/kustomize/base/flagger/crd.yaml index e4f5c3eb..361dd2cb 100644 --- a/kustomize/base/flagger/crd.yaml +++ b/kustomize/base/flagger/crd.yaml @@ -495,6 +495,40 @@ spec: type: array items: type: string + gatewayRefs: + description: The list of parent Gateways for a HTTPRoute + maxItems: 32 + type: array + items: + required: + - name + type: object + properties: + group: + default: gateway.networking.k8s.io + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + maxLength: 253 + minLength: 1 + type: string + namespace: + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + sectionName: + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string corsPolicy: description: Istio Cross-Origin Resource Sharing policy (CORS) type: object diff --git a/kustomize/base/flagger/rbac.yaml b/kustomize/base/flagger/rbac.yaml index bb1134a4..8349a6b3 100644 --- a/kustomize/base/flagger/rbac.yaml +++ b/kustomize/base/flagger/rbac.yaml @@ -194,6 +194,20 @@ rules: - /version verbs: - get + - apiGroups: + - gateway.networking.k8s.io + resources: + - gateways + - httproutes + - httproutes/finalizers + verbs: + - get + - list + - watch + - create + - update + - patch + - delete --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/kustomize/gatewayapi/kustomization.yaml b/kustomize/gatewayapi/kustomization.yaml new file mode 100644 index 00000000..b3e58e59 --- /dev/null +++ b/kustomize/gatewayapi/kustomization.yaml @@ -0,0 +1,5 @@ +bases: + - ../base/flagger/ + - ../base/prometheus/ +patchesStrategicMerge: + - patch.yaml diff --git a/kustomize/gatewayapi/patch.yaml b/kustomize/gatewayapi/patch.yaml new file mode 100644 index 00000000..b17f1f60 --- /dev/null +++ b/kustomize/gatewayapi/patch.yaml @@ -0,0 +1,15 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flagger +spec: + template: + spec: + containers: + - name: flagger + image: aryan9600/flagger + args: + - -log-level=debug + - -include-label-prefix=app.kubernetes.io + - -mesh-provider=gatewayapi + - -metrics-server=http://flagger-prometheus:9090 diff --git a/pkg/apis/flagger/v1beta1/canary.go b/pkg/apis/flagger/v1beta1/canary.go index a6dbcee4..59b5956f 100644 --- a/pkg/apis/flagger/v1beta1/canary.go +++ b/pkg/apis/flagger/v1beta1/canary.go @@ -20,6 +20,7 @@ import ( "fmt" "time" + "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" istiov1alpha3 "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -139,6 +140,11 @@ type CanaryService struct { // +optional Gateways []string `json:"gateways,omitempty"` + // Gateways that the HTTPRoute needs to attach itself to. + // Must be specified while using the Gateway API as a provider. + // +optional + GatewayRefs []v1alpha2.ParentReference `json:"gatewayRefs,omitempty"` + // Hosts attached to the generated Istio virtual service // Defaults to the service name // +optional diff --git a/pkg/apis/flagger/v1beta1/provider.go b/pkg/apis/flagger/v1beta1/provider.go index dcfa8d72..64f7a1c4 100644 --- a/pkg/apis/flagger/v1beta1/provider.go +++ b/pkg/apis/flagger/v1beta1/provider.go @@ -13,4 +13,5 @@ const ( TraefikProvider string = "traefik" OsmProvider string = "osm" KumaProvider string = "kuma" + GatewayAPIProvider string = "gatewayapi" ) diff --git a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go index 371e887e..b1183b12 100644 --- a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ limitations under the License. package v1beta1 import ( + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" v1alpha3 "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -338,6 +339,13 @@ func (in *CanaryService) DeepCopyInto(out *CanaryService) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.GatewayRefs != nil { + in, out := &in.GatewayRefs, &out.GatewayRefs + *out = make([]v1alpha2.ParentReference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Hosts != nil { in, out := &in.Hosts, &out.Hosts *out = make([]string, len(*in)) diff --git a/pkg/apis/gatewayapi/register.go b/pkg/apis/gatewayapi/register.go new file mode 100644 index 00000000..5e07d073 --- /dev/null +++ b/pkg/apis/gatewayapi/register.go @@ -0,0 +1,5 @@ +package gatewayapi + +const ( + GroupName = "gateway.networking.k8s.io" +) diff --git a/pkg/apis/gatewayapi/v1alpha2/doc.go b/pkg/apis/gatewayapi/v1alpha2/doc.go new file mode 100644 index 00000000..01910cd9 --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/doc.go @@ -0,0 +1,6 @@ +// +k8s:deepcopy-gen=package + +// Package v1alpha2 contains API Schema definitions for the +// gateway.networking.k8s.io API group. + +package v1alpha2 diff --git a/pkg/apis/gatewayapi/v1alpha2/gateway_types.go b/pkg/apis/gatewayapi/v1alpha2/gateway_types.go new file mode 100644 index 00000000..8074e66f --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/gateway_types.go @@ -0,0 +1,513 @@ +/* +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:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Class",type=string,JSONPath=`.spec.gatewayClassName` +// +kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.status.addresses[*].value` +// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` + +// Gateway represents an instance of a service-traffic handling infrastructure +// by binding Listeners to a set of IP addresses. +type Gateway struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of Gateway. + Spec GatewaySpec `json:"spec"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GatewayList contains a list of Gateways. +type GatewayList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Gateway `json:"items"` +} + +// GatewaySpec defines the desired state of Gateway. +// +// Not all possible combinations of options specified in the Spec are +// valid. Some invalid configurations can be caught synchronously via a +// webhook, but there are many cases that will require asynchronous +// signaling via the GatewayStatus block. +type GatewaySpec struct { + // GatewayClassName used for this Gateway. This is the name of a + // GatewayClass resource. + GatewayClassName ObjectName `json:"gatewayClassName"` + + // Listeners associated with this Gateway. Listeners define + // logical endpoints that are bound on this Gateway's addresses. + // At least one Listener MUST be specified. + // + // Each listener in a Gateway must have a unique combination of Hostname, + // Port, and Protocol. + // + // An implementation MAY group Listeners by Port and then collapse each + // group of Listeners into a single Listener if the implementation + // determines that the Listeners in the group are "compatible". An + // implementation MAY also group together and collapse compatible + // Listeners belonging to different Gateways. + // + // For example, an implementation might consider Listeners to be + // compatible with each other if all of the following conditions are + // met: + // + // 1. Either each Listener within the group specifies the "HTTP" + // Protocol or each Listener within the group specifies either + // the "HTTPS" or "TLS" Protocol. + // + // 2. Each Listener within the group specifies a Hostname that is unique + // within the group. + // + // 3. As a special case, one Listener within a group may omit Hostname, + // in which case this Listener matches when no other Listener + // matches. + // + // If the implementation does collapse compatible Listeners, the + // hostname provided in the incoming client request MUST be + // matched to a Listener to find the correct set of Routes. + // The incoming hostname MUST be matched using the Hostname + // field for each Listener in order of most to least specific. + // That is, exact matches must be processed before wildcard + // matches. + // + // If this field specifies multiple Listeners that have the same + // Port value but are not compatible, the implementation must raise + // a "Conflicted" condition in the Listener status. + // + // Support: Core + // + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=64 + Listeners []Listener `json:"listeners"` + + // Addresses requested for this Gateway. This is optional and behavior can + // depend on the implementation. If a value is set in the spec and the + // requested address is invalid or unavailable, the implementation MUST + // indicate this in the associated entry in GatewayStatus.Addresses. + // + // The Addresses field represents a request for the address(es) on the + // "outside of the Gateway", that traffic bound for this Gateway will use. + // This could be the IP address or hostname of an external load balancer or + // other networking infrastructure, or some other address that traffic will + // be sent to. + // + // The .listener.hostname field is used to route traffic that has already + // arrived at the Gateway to the correct in-cluster destination. + // + // If no Addresses are specified, the implementation MAY schedule the + // Gateway in an implementation-specific manner, assigning an appropriate + // set of Addresses. + // + // The implementation MUST bind all Listeners to every GatewayAddress that + // it assigns to the Gateway and add a corresponding entry in + // GatewayStatus.Addresses. + // + // Support: Core + // + // +optional + // +kubebuilder:validation:MaxItems=16 + Addresses []GatewayAddress `json:"addresses,omitempty"` +} + +// Listener embodies the concept of a logical endpoint where a Gateway accepts +// network connections. +type Listener struct { + // Name is the name of the Listener. This name MUST be unique within a + // Gateway. + // + // Support: Core + Name SectionName `json:"name"` + + // Hostname specifies the virtual hostname to match for protocol types that + // define this concept. When unspecified, all hostnames are matched. This + // field is ignored for protocols that don't require hostname based + // matching. + // + // Implementations MUST apply Hostname matching appropriately for each of + // the following protocols: + // + // * TLS: The Listener Hostname MUST match the SNI. + // * HTTP: The Listener Hostname MUST match the Host header of the request. + // * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + // protocol layers as described above. If an implementation does not + // ensure that both the SNI and Host header match the Listener hostname, + // it MUST clearly document that. + // + // For HTTPRoute and TLSRoute resources, there is an interaction with the + // `spec.hostnames` array. When both listener and route specify hostnames, + // there MUST be an intersection between the values for a Route to be + // accepted. For more information, refer to the Route specific Hostnames + // documentation. + // + // Support: Core + // + // +optional + Hostname *Hostname `json:"hostname,omitempty"` + + // Port is the network port. Multiple listeners may use the + // same port, subject to the Listener compatibility rules. + // + // Support: Core + Port PortNumber `json:"port"` + + // Protocol specifies the network protocol this listener expects to receive. + // + // Support: Core + Protocol ProtocolType `json:"protocol"` + + // TLS is the TLS configuration for the Listener. This field is required if + // the Protocol field is "HTTPS" or "TLS". It is invalid to set this field + // if the Protocol field is "HTTP", "TCP", or "UDP". + // + // The association of SNIs to Certificate defined in GatewayTLSConfig is + // defined based on the Hostname field for this listener. + // + // The GatewayClass MUST use the longest matching SNI out of all + // available certificates for any TLS handshake. + // + // Support: Core + // + // +optional + TLS *GatewayTLSConfig `json:"tls,omitempty"` + + // AllowedRoutes defines the types of routes that MAY be attached to a + // Listener and the trusted namespaces where those Route resources MAY be + // present. + // + // Although a client request may match multiple route rules, only one rule + // may ultimately receive the request. Matching precedence MUST be + // determined in order of the following criteria: + // + // * The most specific match as defined by the Route type. + // * The oldest Route based on creation timestamp. For example, a Route with + // a creation timestamp of "2020-09-08 01:02:03" is given precedence over + // a Route with a creation timestamp of "2020-09-08 01:02:04". + // * If everything else is equivalent, the Route appearing first in + // alphabetical order (namespace/name) should be given precedence. For + // example, foo/bar is given precedence over foo/baz. + // + // All valid rules within a Route attached to this Listener should be + // implemented. Invalid Route rules can be ignored (sometimes that will mean + // the full Route). If a Route rule transitions from valid to invalid, + // support for that Route rule should be dropped to ensure consistency. For + // example, even if a filter specified by a Route rule is invalid, the rest + // of the rules within that Route should still be supported. + // + // Support: Core + // +kubebuilder:default={namespaces:{from: Same}} + // +optional + AllowedRoutes *AllowedRoutes `json:"allowedRoutes,omitempty"` +} + +// ProtocolType defines the application protocol accepted by a Listener. +// Implementations are not required to accept all the defined protocols. +// If an implementation does not support a specified protocol, it +// should raise a "Detached" condition for the affected Listener with +// a reason of "UnsupportedProtocol". +// +// Core ProtocolType values are listed in the table below. +// +// Implementations can define their own protocols if a core ProtocolType does not +// exist. Such definitions must use prefixed name, such as +// `mycompany.com/my-custom-protocol`. Un-prefixed names are reserved for core +// protocols. Any protocol defined by implementations will fall under custom +// conformance. +// +// Valid values include: +// +// * "HTTP" - Core support +// * "example.com/bar" - Implementation-specific support +// +// Invalid values include: +// +// * "example.com" - must include path if domain is used +// * "foo.example.com" - must include path if domain is used +// +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=255 +// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$` +type ProtocolType string + +const ( + // Accepts cleartext HTTP/1.1 sessions over TCP. Implementations MAY also + // support HTTP/2 over cleartext. If implementations support HTTP/2 over + // cleartext on "HTTP" listeners, that MUST be clearly documented by the + // implementation. + HTTPProtocolType ProtocolType = "HTTP" + + // Accepts HTTP/1.1 or HTTP/2 sessions over TLS. + HTTPSProtocolType ProtocolType = "HTTPS" + + // Accepts TLS sessions over TCP. + TLSProtocolType ProtocolType = "TLS" + + // Accepts TCP sessions. + TCPProtocolType ProtocolType = "TCP" + + // Accepts UDP packets. + UDPProtocolType ProtocolType = "UDP" +) + +// GatewayTLSConfig describes a TLS configuration. +type GatewayTLSConfig struct { + // Mode defines the TLS behavior for the TLS session initiated by the client. + // There are two possible modes: + // + // - Terminate: The TLS session between the downstream client + // and the Gateway is terminated at the Gateway. This mode requires + // certificateRefs to be set and contain at least one element. + // - Passthrough: The TLS session is NOT terminated by the Gateway. This + // implies that the Gateway can't decipher the TLS stream except for + // the ClientHello message of the TLS protocol. + // CertificateRefs field is ignored in this mode. + // + // Support: Core + // + // +optional + // +kubebuilder:default=Terminate + Mode *TLSModeType `json:"mode,omitempty"` + + // CertificateRefs contains a series of references to Kubernetes objects that + // contains TLS certificates and private keys. These certificates are used to + // establish a TLS handshake for requests that match the hostname of the + // associated listener. + // + // A single CertificateRef to a Kubernetes Secret has "Core" support. + // Implementations MAY choose to support attaching multiple certificates to + // a Listener, but this behavior is implementation-specific. + // + // References to a resource in different namespace are invalid UNLESS there + // is a ReferencePolicy in the target namespace that allows the certificate + // to be attached. If a ReferencePolicy does not allow this reference, the + // "ResolvedRefs" condition MUST be set to False for this listener with the + // "InvalidCertificateRef" reason. + // + // This field is required to have at least one element when the mode is set + // to "Terminate" (default) and is optional otherwise. + // + // CertificateRefs can reference to standard Kubernetes resources, i.e. + // Secret, or implementation-specific custom resources. + // + // Support: Core - A single reference to a Kubernetes Secret + // + // Support: Implementation-specific (More than one reference or other resource types) + // + // +optional + // +kubebuilder:validation:MaxItems=64 + CertificateRefs []*SecretObjectReference `json:"certificateRefs,omitempty"` + + // Options are a list of key/value pairs to enable extended TLS + // configuration for each implementation. For example, configuring the + // minimum TLS version or supported cipher suites. + // + // A set of common keys MAY be defined by the API in the future. To avoid + // any ambiguity, implementation-specific definitions MUST use + // domain-prefixed names, such as `example.com/my-custom-option`. + // Un-prefixed names are reserved for key names defined by Gateway API. + // + // Support: Implementation-specific + // + // +optional + // +kubebuilder:validation:MaxProperties=16 + Options map[AnnotationKey]AnnotationValue `json:"options,omitempty"` +} + +// TLSModeType type defines how a Gateway handles TLS sessions. +// +// +kubebuilder:validation:Enum=Terminate;Passthrough +type TLSModeType string + +const ( + // In this mode, TLS session between the downstream client + // and the Gateway is terminated at the Gateway. + TLSModeTerminate TLSModeType = "Terminate" + + // In this mode, the TLS session is NOT terminated by the Gateway. This + // implies that the Gateway can't decipher the TLS stream except for + // the ClientHello message of the TLS protocol. + // + // Note that SSL passthrough is only supported by TLSRoute. + TLSModePassthrough TLSModeType = "Passthrough" +) + +// AllowedRoutes defines which Routes may be attached to this Listener. +type AllowedRoutes struct { + // Namespaces indicates namespaces from which Routes may be attached to this + // Listener. This is restricted to the namespace of this Gateway by default. + // + // Support: Core + // + // +optional + // +kubebuilder:default={from: Same} + Namespaces *RouteNamespaces `json:"namespaces,omitempty"` + + // Kinds specifies the groups and kinds of Routes that are allowed to bind + // to this Gateway Listener. When unspecified or empty, the kinds of Routes + // selected are determined using the Listener protocol. + // + // A RouteGroupKind MUST correspond to kinds of Routes that are compatible + // with the application protocol specified in the Listener's Protocol field. + // If an implementation does not support or recognize this resource type, it + // MUST set the "ResolvedRefs" condition to False for this Listener with the + // "InvalidRouteKinds" reason. + // + // Support: Core + // + // +optional + // +kubebuilder:validation:MaxItems=8 + Kinds []RouteGroupKind `json:"kinds,omitempty"` +} + +// FromNamespaces specifies namespace from which Routes may be attached to a +// Gateway. +// +// +kubebuilder:validation:Enum=All;Selector;Same +type FromNamespaces string + +const ( + // Routes in all namespaces may be attached to this Gateway. + NamespacesFromAll FromNamespaces = "All" + // Only Routes in namespaces selected by the selector may be attached to + // this Gateway. + NamespacesFromSelector FromNamespaces = "Selector" + // Only Routes in the same namespace as the Gateway may be attached to this + // Gateway. + NamespacesFromSame FromNamespaces = "Same" +) + +// RouteNamespaces indicate which namespaces Routes should be selected from. +type RouteNamespaces struct { + // From indicates where Routes will be selected for this Gateway. Possible + // values are: + // * All: Routes in all namespaces may be used by this Gateway. + // * Selector: Routes in namespaces selected by the selector may be used by + // this Gateway. + // * Same: Only Routes in the same namespace may be used by this Gateway. + // + // Support: Core + // + // +optional + // +kubebuilder:default=Same + From *FromNamespaces `json:"from,omitempty"` + + // Selector must be specified when From is set to "Selector". In that case, + // only Routes in Namespaces matching this Selector will be selected by this + // Gateway. This field is ignored for other values of "From". + // + // Support: Core + // + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` +} + +// RouteGroupKind indicates the group and kind of a Route resource. +type RouteGroupKind struct { + // Group is the group of the Route. + // + // +optional + // +kubebuilder:default=gateway.networking.k8s.io + Group *Group `json:"group,omitempty"` + + // Kind is the kind of the Route. + Kind Kind `json:"kind"` +} + +// GatewayAddress describes an address that can be bound to a Gateway. +type GatewayAddress struct { + // Type of the address. + // + // +optional + // +kubebuilder:validation:Enum=IPAddress;Hostname;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. + // + // Examples: `1.2.3.4`, `128::1`, `my-ip-address`. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Value string `json:"value"` +} + +// 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 + +// 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 diff --git a/pkg/apis/gatewayapi/v1alpha2/httproute_types.go b/pkg/apis/gatewayapi/v1alpha2/httproute_types.go new file mode 100644 index 00000000..e0e2306c --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/httproute_types.go @@ -0,0 +1,853 @@ +/* +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 + // + 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 + // + // + // +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 + // + // + 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. +// +type HTTPPathModifier struct { + // Type defines the type of path modifier. + // + // + // +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". + // + // + // +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 + // + // + // +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. +// +// +// Support: Extended +type HTTPURLRewriteFilter struct { + // Hostname is the value to be used to replace the Host header value during + // forwarding. + // + // Support: Extended + // + // + // +optional + Hostname *Hostname `json:"hostname,omitempty"` + + // Path defines a path rewrite. + // + // Support: Extended + // + // + // +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"` +} diff --git a/pkg/apis/gatewayapi/v1alpha2/object_reference_types.go b/pkg/apis/gatewayapi/v1alpha2/object_reference_types.go new file mode 100644 index 00000000..12730b9e --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/object_reference_types.go @@ -0,0 +1,131 @@ +/* +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 + +// LocalObjectReference identifies an API object within the namespace of the +// referrer. +// 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 LocalObjectReference struct { + // Group is the group of the referent. For example, "networking.k8s.io". + // When unspecified (empty string), core API group is inferred. + Group Group `json:"group"` + + // Kind is kind of the referent. For example "HTTPRoute" or "Service". + Kind Kind `json:"kind"` + + // Name is the name of the referent. + Name ObjectName `json:"name"` +} + +// SecretObjectReference identifies an API object including its namespace, +// defaulting to Secret. +// +// 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 SecretObjectReference struct { + // Group is the group of the referent. For example, "networking.k8s.io". + // When unspecified (empty string), core API group is inferred. + // + // +optional + // +kubebuilder:default="" + Group *Group `json:"group"` + + // Kind is kind of the referent. For example "HTTPRoute" or "Service". + // + // +optional + // +kubebuilder:default=Secret + Kind *Kind `json:"kind"` + + // Name is the name of the referent. + Name ObjectName `json:"name"` + + // Namespace is the namespace of the backend. When unspecified, the local + // namespace is inferred. + // + // 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. + // + // Support: Core + // + // +optional + Namespace *Namespace `json:"namespace,omitempty"` +} + +// BackendObjectReference defines how an ObjectReference that is +// 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 +// is required in the referent namespace to allow that namespace's +// owner to accept the reference. See the ReferencePolicy documentation +// for details. +// +// 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 BackendObjectReference struct { + // Group is the group of the referent. For example, "networking.k8s.io". + // When unspecified (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". + // + // +optional + // +kubebuilder:default=Service + Kind *Kind `json:"kind,omitempty"` + + // Name is the name of the referent. + Name ObjectName `json:"name"` + + // Namespace is the namespace of the backend. When unspecified, the local + // namespace is inferred. + // + // 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. + // + // Support: Core + // + // +optional + 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. + // For other resources, destination port might be derived from the referent + // resource or this field. + // + // +optional + Port *PortNumber `json:"port,omitempty"` +} diff --git a/pkg/apis/gatewayapi/v1alpha2/register.go b/pkg/apis/gatewayapi/v1alpha2/register.go new file mode 100644 index 00000000..15a84494 --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/register.go @@ -0,0 +1,33 @@ +package v1alpha2 + +import ( + "github.com/fluxcd/flagger/pkg/apis/gatewayapi" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is the GroupVersion for the Kuma API +var SchemeGroupVersion = schema.GroupVersion{Group: gatewayapi.GroupName, Version: "v1alpha2"} + +// Resource gets a Kuma GroupResource for a specified resource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Gateway{}, + &GatewayList{}, + &HTTPRoute{}, + &HTTPRouteList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/gatewayapi/v1alpha2/shared_types.go b/pkg/apis/gatewayapi/v1alpha2/shared_types.go new file mode 100644 index 00000000..074969f6 --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/shared_types.go @@ -0,0 +1,427 @@ +/* +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" +) diff --git a/pkg/apis/gatewayapi/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/gatewayapi/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 00000000..cb1d842e --- /dev/null +++ b/pkg/apis/gatewayapi/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,1008 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + v1 "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 *AllowedRoutes) DeepCopyInto(out *AllowedRoutes) { + *out = *in + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(RouteNamespaces) + (*in).DeepCopyInto(*out) + } + if in.Kinds != nil { + in, out := &in.Kinds, &out.Kinds + *out = make([]RouteGroupKind, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedRoutes. +func (in *AllowedRoutes) DeepCopy() *AllowedRoutes { + if in == nil { + return nil + } + out := new(AllowedRoutes) + 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 + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(Group) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(Kind) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(Namespace) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(PortNumber) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendObjectReference. +func (in *BackendObjectReference) DeepCopy() *BackendObjectReference { + if in == nil { + return nil + } + out := new(BackendObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendRef) DeepCopyInto(out *BackendRef) { + *out = *in + in.BackendObjectReference.DeepCopyInto(&out.BackendObjectReference) + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRef. +func (in *BackendRef) DeepCopy() *BackendRef { + if in == nil { + return nil + } + out := new(BackendRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommonRouteSpec) DeepCopyInto(out *CommonRouteSpec) { + *out = *in + if in.ParentRefs != nil { + in, out := &in.ParentRefs, &out.ParentRefs + *out = make([]ParentReference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonRouteSpec. +func (in *CommonRouteSpec) DeepCopy() *CommonRouteSpec { + if in == nil { + return nil + } + out := new(CommonRouteSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Gateway) DeepCopyInto(out *Gateway) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. +func (in *Gateway) DeepCopy() *Gateway { + if in == nil { + return nil + } + out := new(Gateway) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Gateway) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayAddress) DeepCopyInto(out *GatewayAddress) { + *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 GatewayAddress. +func (in *GatewayAddress) DeepCopy() *GatewayAddress { + if in == nil { + return nil + } + out := new(GatewayAddress) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayList) DeepCopyInto(out *GatewayList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Gateway, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayList. +func (in *GatewayList) DeepCopy() *GatewayList { + if in == nil { + return nil + } + out := new(GatewayList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GatewayList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec) { + *out = *in + if in.Listeners != nil { + in, out := &in.Listeners, &out.Listeners + *out = make([]Listener, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]GatewayAddress, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewaySpec. +func (in *GatewaySpec) DeepCopy() *GatewaySpec { + if in == nil { + return nil + } + out := new(GatewaySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GatewayTLSConfig) DeepCopyInto(out *GatewayTLSConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(TLSModeType) + **out = **in + } + if in.CertificateRefs != nil { + in, out := &in.CertificateRefs, &out.CertificateRefs + *out = make([]*SecretObjectReference, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(SecretObjectReference) + (*in).DeepCopyInto(*out) + } + } + } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = make(map[AnnotationKey]AnnotationValue, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayTLSConfig. +func (in *GatewayTLSConfig) DeepCopy() *GatewayTLSConfig { + if in == nil { + return nil + } + out := new(GatewayTLSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPBackendRef) DeepCopyInto(out *HTTPBackendRef) { + *out = *in + in.BackendRef.DeepCopyInto(&out.BackendRef) + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = make([]HTTPRouteFilter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPBackendRef. +func (in *HTTPBackendRef) DeepCopy() *HTTPBackendRef { + if in == nil { + return nil + } + out := new(HTTPBackendRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPHeader) DeepCopyInto(out *HTTPHeader) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeader. +func (in *HTTPHeader) DeepCopy() *HTTPHeader { + if in == nil { + return nil + } + out := new(HTTPHeader) + 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 + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(HeaderMatchType) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeaderMatch. +func (in *HTTPHeaderMatch) DeepCopy() *HTTPHeaderMatch { + if in == nil { + return nil + } + out := new(HTTPHeaderMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPPathMatch) DeepCopyInto(out *HTTPPathMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(PathMatchType) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPathMatch. +func (in *HTTPPathMatch) DeepCopy() *HTTPPathMatch { + if in == nil { + return nil + } + out := new(HTTPPathMatch) + in.DeepCopyInto(out) + return out +} + +// 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 + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPathModifier. +func (in *HTTPPathModifier) DeepCopy() *HTTPPathModifier { + if in == nil { + return nil + } + out := new(HTTPPathModifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPQueryParamMatch) DeepCopyInto(out *HTTPQueryParamMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(QueryParamMatchType) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPQueryParamMatch. +func (in *HTTPQueryParamMatch) DeepCopy() *HTTPQueryParamMatch { + if in == nil { + return nil + } + out := new(HTTPQueryParamMatch) + in.DeepCopyInto(out) + 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 + in.BackendRef.DeepCopyInto(&out.BackendRef) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestMirrorFilter. +func (in *HTTPRequestMirrorFilter) DeepCopy() *HTTPRequestMirrorFilter { + if in == nil { + return nil + } + out := new(HTTPRequestMirrorFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRequestRedirectFilter) DeepCopyInto(out *HTTPRequestRedirectFilter) { + *out = *in + if in.Scheme != nil { + in, out := &in.Scheme, &out.Scheme + *out = new(string) + **out = **in + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(PreciseHostname) + **out = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(HTTPPathModifier) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(PortNumber) + **out = **in + } + if in.StatusCode != nil { + in, out := &in.StatusCode, &out.StatusCode + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestRedirectFilter. +func (in *HTTPRequestRedirectFilter) DeepCopy() *HTTPRequestRedirectFilter { + if in == nil { + return nil + } + out := new(HTTPRequestRedirectFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. +func (in *HTTPRoute) DeepCopy() *HTTPRoute { + if in == nil { + return nil + } + out := new(HTTPRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPRoute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteFilter) DeepCopyInto(out *HTTPRouteFilter) { + *out = *in + if in.RequestHeaderModifier != nil { + in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier + *out = new(HTTPRequestHeaderFilter) + (*in).DeepCopyInto(*out) + } + if in.RequestMirror != nil { + in, out := &in.RequestMirror, &out.RequestMirror + *out = new(HTTPRequestMirrorFilter) + (*in).DeepCopyInto(*out) + } + if in.RequestRedirect != nil { + in, out := &in.RequestRedirect, &out.RequestRedirect + *out = new(HTTPRequestRedirectFilter) + (*in).DeepCopyInto(*out) + } + if in.URLRewrite != nil { + in, out := &in.URLRewrite, &out.URLRewrite + *out = new(HTTPURLRewriteFilter) + (*in).DeepCopyInto(*out) + } + if in.ExtensionRef != nil { + in, out := &in.ExtensionRef, &out.ExtensionRef + *out = new(LocalObjectReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteFilter. +func (in *HTTPRouteFilter) DeepCopy() *HTTPRouteFilter { + if in == nil { + return nil + } + out := new(HTTPRouteFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteList) DeepCopyInto(out *HTTPRouteList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HTTPRoute, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteList. +func (in *HTTPRouteList) DeepCopy() *HTTPRouteList { + if in == nil { + return nil + } + out := new(HTTPRouteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPRouteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteMatch) DeepCopyInto(out *HTTPRouteMatch) { + *out = *in + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(HTTPPathMatch) + (*in).DeepCopyInto(*out) + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make([]HTTPHeaderMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.QueryParams != nil { + in, out := &in.QueryParams, &out.QueryParams + *out = make([]HTTPQueryParamMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Method != nil { + in, out := &in.Method, &out.Method + *out = new(HTTPMethod) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteMatch. +func (in *HTTPRouteMatch) DeepCopy() *HTTPRouteMatch { + if in == nil { + return nil + } + out := new(HTTPRouteMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) { + *out = *in + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]HTTPRouteMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = make([]HTTPRouteFilter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.BackendRefs != nil { + in, out := &in.BackendRefs, &out.BackendRefs + *out = make([]HTTPBackendRef, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteRule. +func (in *HTTPRouteRule) DeepCopy() *HTTPRouteRule { + if in == nil { + return nil + } + out := new(HTTPRouteRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteSpec) DeepCopyInto(out *HTTPRouteSpec) { + *out = *in + in.CommonRouteSpec.DeepCopyInto(&out.CommonRouteSpec) + if in.Hostnames != nil { + in, out := &in.Hostnames, &out.Hostnames + *out = make([]Hostname, len(*in)) + copy(*out, *in) + } + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]HTTPRouteRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteSpec. +func (in *HTTPRouteSpec) DeepCopy() *HTTPRouteSpec { + if in == nil { + return nil + } + out := new(HTTPRouteSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteStatus) DeepCopyInto(out *HTTPRouteStatus) { + *out = *in + in.RouteStatus.DeepCopyInto(&out.RouteStatus) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteStatus. +func (in *HTTPRouteStatus) DeepCopy() *HTTPRouteStatus { + if in == nil { + return nil + } + out := new(HTTPRouteStatus) + 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 = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(HTTPPathModifier) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPURLRewriteFilter. +func (in *HTTPURLRewriteFilter) DeepCopy() *HTTPURLRewriteFilter { + if in == nil { + return nil + } + out := new(HTTPURLRewriteFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Listener) DeepCopyInto(out *Listener) { + *out = *in + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(Hostname) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(GatewayTLSConfig) + (*in).DeepCopyInto(*out) + } + if in.AllowedRoutes != nil { + in, out := &in.AllowedRoutes, &out.AllowedRoutes + *out = new(AllowedRoutes) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Listener. +func (in *Listener) DeepCopy() *Listener { + if in == nil { + return nil + } + out := new(Listener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference. +func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { + if in == nil { + return nil + } + out := new(LocalObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ParentReference) DeepCopyInto(out *ParentReference) { + *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(Group) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(Kind) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(Namespace) + **out = **in + } + if in.SectionName != nil { + in, out := &in.SectionName, &out.SectionName + *out = new(SectionName) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParentReference. +func (in *ParentReference) DeepCopy() *ParentReference { + if in == nil { + return nil + } + out := new(ParentReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteGroupKind) DeepCopyInto(out *RouteGroupKind) { + *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(Group) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteGroupKind. +func (in *RouteGroupKind) DeepCopy() *RouteGroupKind { + if in == nil { + return nil + } + out := new(RouteGroupKind) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteNamespaces) DeepCopyInto(out *RouteNamespaces) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = new(FromNamespaces) + **out = **in + } + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteNamespaces. +func (in *RouteNamespaces) DeepCopy() *RouteNamespaces { + if in == nil { + return nil + } + out := new(RouteNamespaces) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteParentStatus) DeepCopyInto(out *RouteParentStatus) { + *out = *in + in.ParentRef.DeepCopyInto(&out.ParentRef) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteParentStatus. +func (in *RouteParentStatus) DeepCopy() *RouteParentStatus { + if in == nil { + return nil + } + out := new(RouteParentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteStatus) DeepCopyInto(out *RouteStatus) { + *out = *in + if in.Parents != nil { + in, out := &in.Parents, &out.Parents + *out = make([]RouteParentStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteStatus. +func (in *RouteStatus) DeepCopy() *RouteStatus { + if in == nil { + return nil + } + out := new(RouteStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretObjectReference) DeepCopyInto(out *SecretObjectReference) { + *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(Group) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(Kind) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(Namespace) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretObjectReference. +func (in *SecretObjectReference) DeepCopy() *SecretObjectReference { + if in == nil { + return nil + } + out := new(SecretObjectReference) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index ecc007f5..7880d0c1 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -26,6 +26,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" gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1" networkingv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3" kumav1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/kuma/v1alpha1" @@ -45,6 +46,7 @@ type Interface interface { AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface FlaggerV1beta1() flaggerv1beta1.FlaggerV1beta1Interface GatewayV1() gatewayv1.GatewayV1Interface + GatewayapiV1alpha2() gatewayapiv1alpha2.GatewayapiV1alpha2Interface GlooV1() gloov1.GlooV1Interface NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface KumaV1alpha1() kumav1alpha1.KumaV1alpha1Interface @@ -63,6 +65,7 @@ type Clientset struct { appmeshV1beta1 *appmeshv1beta1.AppmeshV1beta1Client flaggerV1beta1 *flaggerv1beta1.FlaggerV1beta1Client gatewayV1 *gatewayv1.GatewayV1Client + gatewayapiV1alpha2 *gatewayapiv1alpha2.GatewayapiV1alpha2Client glooV1 *gloov1.GlooV1Client networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client kumaV1alpha1 *kumav1alpha1.KumaV1alpha1Client @@ -93,6 +96,11 @@ func (c *Clientset) GatewayV1() gatewayv1.GatewayV1Interface { return c.gatewayV1 } +// GatewayapiV1alpha2 retrieves the GatewayapiV1alpha2Client +func (c *Clientset) GatewayapiV1alpha2() gatewayapiv1alpha2.GatewayapiV1alpha2Interface { + return c.gatewayapiV1alpha2 +} + // GlooV1 retrieves the GlooV1Client func (c *Clientset) GlooV1() gloov1.GlooV1Interface { return c.glooV1 @@ -189,6 +197,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, if err != nil { return nil, err } + cs.gatewayapiV1alpha2, err = gatewayapiv1alpha2.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } cs.glooV1, err = gloov1.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err @@ -246,6 +258,7 @@ 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.glooV1 = gloov1.New(c) cs.networkingV1alpha3 = networkingv1alpha3.New(c) cs.kumaV1alpha1 = kumav1alpha1.New(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 0c8b64c8..a0fbb222 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -28,6 +28,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" gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1" fakegloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1/fake" networkingv1alpha3 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3" @@ -121,6 +123,11 @@ 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} +} + // GlooV1 retrieves the GlooV1Client func (c *Clientset) GlooV1() gloov1.GlooV1Interface { return &fakegloov1.FakeGlooV1{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index f4f594b0..874deb25 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -22,6 +22,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" gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" networkingv1alpha3 "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" @@ -46,6 +47,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ appmeshv1beta1.AddToScheme, flaggerv1beta1.AddToScheme, gatewayv1.AddToScheme, + gatewayapiv1alpha2.AddToScheme, gloov1.AddToScheme, networkingv1alpha3.AddToScheme, kumav1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index a5082453..f5ed457f 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -22,6 +22,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" gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" networkingv1alpha3 "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" @@ -46,6 +47,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ appmeshv1beta1.AddToScheme, flaggerv1beta1.AddToScheme, gatewayv1.AddToScheme, + gatewayapiv1alpha2.AddToScheme, gloov1.AddToScheme, networkingv1alpha3.AddToScheme, kumav1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/doc.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/doc.go new file mode 100644 index 00000000..c0e4eb6f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha2 diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/doc.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/doc.go new file mode 100644 index 00000000..1ccd9119 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gateway.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gateway.go new file mode 100644 index 00000000..b2ce02a4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gateway.go @@ -0,0 +1,130 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeGateways implements GatewayInterface +type FakeGateways struct { + Fake *FakeGatewayapiV1alpha2 + ns string +} + +var gatewaysResource = schema.GroupVersionResource{Group: "gatewayapi", Version: "v1alpha2", Resource: "gateways"} + +var gatewaysKind = schema.GroupVersionKind{Group: "gatewayapi", Version: "v1alpha2", Kind: "Gateway"} + +// Get takes name of the gateway, and returns the corresponding gateway object, and an error if there is any. +func (c *FakeGateways) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.Gateway, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(gatewaysResource, c.ns, name), &v1alpha2.Gateway{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.Gateway), err +} + +// List takes label and field selectors, and returns the list of Gateways that match those selectors. +func (c *FakeGateways) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.GatewayList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(gatewaysResource, gatewaysKind, c.ns, opts), &v1alpha2.GatewayList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GatewayList{ListMeta: obj.(*v1alpha2.GatewayList).ListMeta} + for _, item := range obj.(*v1alpha2.GatewayList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested gateways. +func (c *FakeGateways) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(gatewaysResource, c.ns, opts)) + +} + +// Create takes the representation of a gateway and creates it. Returns the server's representation of the gateway, and an error, if there is any. +func (c *FakeGateways) Create(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.CreateOptions) (result *v1alpha2.Gateway, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(gatewaysResource, c.ns, gateway), &v1alpha2.Gateway{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.Gateway), err +} + +// Update takes the representation of a gateway and updates it. Returns the server's representation of the gateway, and an error, if there is any. +func (c *FakeGateways) Update(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.UpdateOptions) (result *v1alpha2.Gateway, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(gatewaysResource, c.ns, gateway), &v1alpha2.Gateway{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.Gateway), err +} + +// Delete takes name of the gateway and deletes it. Returns an error if one occurs. +func (c *FakeGateways) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(gatewaysResource, c.ns, name, opts), &v1alpha2.Gateway{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGateways) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(gatewaysResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha2.GatewayList{}) + return err +} + +// Patch applies the patch and returns the patched gateway. +func (c *FakeGateways) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.Gateway, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(gatewaysResource, c.ns, name, pt, data, subresources...), &v1alpha2.Gateway{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.Gateway), err +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gatewayapi_client.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gatewayapi_client.go new file mode 100644 index 00000000..c9540750 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_gatewayapi_client.go @@ -0,0 +1,44 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha2 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeGatewayapiV1alpha2 struct { + *testing.Fake +} + +func (c *FakeGatewayapiV1alpha2) Gateways(namespace string) v1alpha2.GatewayInterface { + return &FakeGateways{c, namespace} +} + +func (c *FakeGatewayapiV1alpha2) HTTPRoutes(namespace string) v1alpha2.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 { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_httproute.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_httproute.go new file mode 100644 index 00000000..32a89c9a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/fake/fake_httproute.go @@ -0,0 +1,130 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHTTPRoutes implements HTTPRouteInterface +type FakeHTTPRoutes struct { + Fake *FakeGatewayapiV1alpha2 + ns string +} + +var httproutesResource = schema.GroupVersionResource{Group: "gatewayapi", Version: "v1alpha2", Resource: "httproutes"} + +var httproutesKind = schema.GroupVersionKind{Group: "gatewayapi", Version: "v1alpha2", Kind: "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) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(httproutesResource, c.ns, name), &v1alpha2.HTTPRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.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) { + obj, err := c.Fake. + Invokes(testing.NewListAction(httproutesResource, httproutesKind, c.ns, opts), &v1alpha2.HTTPRouteList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.HTTPRouteList{ListMeta: obj.(*v1alpha2.HTTPRouteList).ListMeta} + for _, item := range obj.(*v1alpha2.HTTPRouteList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hTTPRoutes. +func (c *FakeHTTPRoutes) Watch(ctx context.Context, opts v1.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) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(httproutesResource, c.ns, hTTPRoute), &v1alpha2.HTTPRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.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) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(httproutesResource, c.ns, hTTPRoute), &v1alpha2.HTTPRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.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 { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(httproutesResource, c.ns, name, opts), &v1alpha2.HTTPRoute{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHTTPRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(httproutesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha2.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) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(httproutesResource, c.ns, name, pt, data, subresources...), &v1alpha2.HTTPRoute{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.HTTPRoute), err +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gateway.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gateway.go new file mode 100644 index 00000000..553596f5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gateway.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + "time" + + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "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" +) + +// GatewaysGetter has a method to return a GatewayInterface. +// A group's client should implement this interface. +type GatewaysGetter interface { + Gateways(namespace string) GatewayInterface +} + +// GatewayInterface has methods to work with Gateway resources. +type GatewayInterface interface { + Create(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.CreateOptions) (*v1alpha2.Gateway, error) + Update(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.UpdateOptions) (*v1alpha2.Gateway, 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.Gateway, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.GatewayList, 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.Gateway, err error) + GatewayExpansion +} + +// gateways implements GatewayInterface +type gateways struct { + client rest.Interface + ns string +} + +// newGateways returns a Gateways +func newGateways(c *GatewayapiV1alpha2Client, namespace string) *gateways { + return &gateways{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the gateway, and returns the corresponding gateway object, and an error if there is any. +func (c *gateways) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.Gateway, err error) { + result = &v1alpha2.Gateway{} + err = c.client.Get(). + Namespace(c.ns). + Resource("gateways"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Gateways that match those selectors. +func (c *gateways) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.GatewayList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.GatewayList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("gateways"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested gateways. +func (c *gateways) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("gateways"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a gateway and creates it. Returns the server's representation of the gateway, and an error, if there is any. +func (c *gateways) Create(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.CreateOptions) (result *v1alpha2.Gateway, err error) { + result = &v1alpha2.Gateway{} + err = c.client.Post(). + Namespace(c.ns). + Resource("gateways"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(gateway). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a gateway and updates it. Returns the server's representation of the gateway, and an error, if there is any. +func (c *gateways) Update(ctx context.Context, gateway *v1alpha2.Gateway, opts v1.UpdateOptions) (result *v1alpha2.Gateway, err error) { + result = &v1alpha2.Gateway{} + err = c.client.Put(). + Namespace(c.ns). + Resource("gateways"). + Name(gateway.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(gateway). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the gateway and deletes it. Returns an error if one occurs. +func (c *gateways) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("gateways"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *gateways) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("gateways"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched gateway. +func (c *gateways) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.Gateway, err error) { + result = &v1alpha2.Gateway{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("gateways"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gatewayapi_client.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gatewayapi_client.go new file mode 100644 index 00000000..933434dc --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/gatewayapi_client.go @@ -0,0 +1,112 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "net/http" + + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type GatewayapiV1alpha2Interface interface { + RESTClient() rest.Interface + GatewaysGetter + HTTPRoutesGetter +} + +// GatewayapiV1alpha2Client is used to interact with features provided by the gatewayapi group. +type GatewayapiV1alpha2Client struct { + restClient rest.Interface +} + +func (c *GatewayapiV1alpha2Client) Gateways(namespace string) GatewayInterface { + return newGateways(c, namespace) +} + +func (c *GatewayapiV1alpha2Client) HTTPRoutes(namespace string) HTTPRouteInterface { + return newHTTPRoutes(c, namespace) +} + +// NewForConfig creates a new GatewayapiV1alpha2Client 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) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new GatewayapiV1alpha2Client 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) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &GatewayapiV1alpha2Client{client}, nil +} + +// NewForConfigOrDie creates a new GatewayapiV1alpha2Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *GatewayapiV1alpha2Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new GatewayapiV1alpha2Client for the given RESTClient. +func New(c rest.Interface) *GatewayapiV1alpha2Client { + return &GatewayapiV1alpha2Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha2.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *GatewayapiV1alpha2Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/generated_expansion.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/generated_expansion.go new file mode 100644 index 00000000..e1072b4d --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +type GatewayExpansion interface{} + +type HTTPRouteExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/httproute.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/httproute.go new file mode 100644 index 00000000..ac36f1b2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1alpha2/httproute.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + "time" + + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "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" +) + +// HTTPRoutesGetter has a method to return a HTTPRouteInterface. +// A group's client should implement this interface. +type HTTPRoutesGetter interface { + HTTPRoutes(namespace string) HTTPRouteInterface +} + +// 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) + HTTPRouteExpansion +} + +// hTTPRoutes implements HTTPRouteInterface +type hTTPRoutes struct { + client rest.Interface + ns string +} + +// newHTTPRoutes returns a HTTPRoutes +func newHTTPRoutes(c *GatewayapiV1alpha2Client, namespace string) *hTTPRoutes { + return &hTTPRoutes{ + client: c.RESTClient(), + ns: namespace, + } +} + +// 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{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httproutes"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// 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) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.HTTPRouteList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httproutes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hTTPRoutes. +func (c *hTTPRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("httproutes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// 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{} + err = c.client.Post(). + Namespace(c.ns). + Resource("httproutes"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPRoute). + Do(ctx). + Into(result) + return +} + +// 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{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httproutes"). + Name(hTTPRoute.Name). + 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 { + return c.client.Delete(). + Namespace(c.ns). + Resource("httproutes"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hTTPRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("httproutes"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// 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{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("httproutes"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index a4bd8fc0..f20ae611 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -27,6 +27,7 @@ import ( appmesh "github.com/fluxcd/flagger/pkg/client/informers/externalversions/appmesh" flagger "github.com/fluxcd/flagger/pkg/client/informers/externalversions/flagger" gateway "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gateway" + gatewayapi "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi" gloo "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gloo" internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" istio "github.com/fluxcd/flagger/pkg/client/informers/externalversions/istio" @@ -183,6 +184,7 @@ type SharedInformerFactory interface { Appmesh() appmesh.Interface Flagger() flagger.Interface Gateway() gateway.Interface + Gatewayapi() gatewayapi.Interface Gloo() gloo.Interface Networking() istio.Interface Kuma() kuma.Interface @@ -203,6 +205,10 @@ func (f *sharedInformerFactory) Gateway() gateway.Interface { return gateway.New(f, f.namespace, f.tweakListOptions) } +func (f *sharedInformerFactory) Gatewayapi() gatewayapi.Interface { + return gatewayapi.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Gloo() gloo.Interface { return gloo.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/gatewayapi/interface.go b/pkg/client/informers/externalversions/gatewayapi/interface.go new file mode 100644 index 00000000..f8721206 --- /dev/null +++ b/pkg/client/informers/externalversions/gatewayapi/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package gatewayapi + +import ( + v1alpha2 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi/v1alpha2" + 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 +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + 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) +} diff --git a/pkg/client/informers/externalversions/gatewayapi/v1alpha2/gateway.go b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/gateway.go new file mode 100644 index 00000000..5d7601cb --- /dev/null +++ b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/gateway.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + time "time" + + gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + 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" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// GatewayInformer provides access to a shared informer and lister for +// Gateways. +type GatewayInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.GatewayLister +} + +type gatewayInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewGatewayInformer constructs a new informer for Gateway type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewGatewayInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGatewayInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredGatewayInformer constructs a new informer for Gateway type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredGatewayInformer(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) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1alpha2().Gateways(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1alpha2().Gateways(namespace).Watch(context.TODO(), options) + }, + }, + &gatewayapiv1alpha2.Gateway{}, + resyncPeriod, + indexers, + ) +} + +func (f *gatewayInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGatewayInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *gatewayInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&gatewayapiv1alpha2.Gateway{}, f.defaultInformer) +} + +func (f *gatewayInformer) Lister() v1alpha2.GatewayLister { + return v1alpha2.NewGatewayLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/gatewayapi/v1alpha2/httproute.go b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/httproute.go new file mode 100644 index 00000000..fdd1a4ee --- /dev/null +++ b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/httproute.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + time "time" + + gatewayapiv1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + 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" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HTTPRouteInformer provides access to a shared informer and lister for +// HTTPRoutes. +type HTTPRouteInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.HTTPRouteLister +} + +type hTTPRouteInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHTTPRouteInformer constructs a new informer for HTTPRoute type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHTTPRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHTTPRouteInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHTTPRouteInformer constructs a new informer for HTTPRoute type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +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) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1alpha2().HTTPRoutes(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1alpha2().HTTPRoutes(namespace).Watch(context.TODO(), options) + }, + }, + &gatewayapiv1alpha2.HTTPRoute{}, + resyncPeriod, + indexers, + ) +} + +func (f *hTTPRouteInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHTTPRouteInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *hTTPRouteInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&gatewayapiv1alpha2.HTTPRoute{}, f.defaultInformer) +} + +func (f *hTTPRouteInformer) Lister() v1alpha2.HTTPRouteLister { + return v1alpha2.NewHTTPRouteLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/gatewayapi/v1alpha2/interface.go b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/interface.go new file mode 100644 index 00000000..e5729201 --- /dev/null +++ b/pkg/client/informers/externalversions/gatewayapi/v1alpha2/interface.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Gateways returns a GatewayInformer. + Gateways() GatewayInformer + // HTTPRoutes returns a HTTPRouteInformer. + HTTPRoutes() HTTPRouteInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Gateways returns a GatewayInformer. +func (v *version) Gateways() GatewayInformer { + return &gatewayInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// HTTPRoutes returns a HTTPRouteInformer. +func (v *version) HTTPRoutes() HTTPRouteInformer { + return &hTTPRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 13bb2c33..876de50e 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -24,13 +24,14 @@ 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" v1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" v1alpha3 "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" v1alpha1 "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" - v1alpha2 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha2" + smiv1alpha2 "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" @@ -91,6 +92,12 @@ 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("gateways"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1alpha2().Gateways().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("httproutes"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1alpha2().HTTPRoutes().Informer()}, nil + // Group=gloo.solo.io, Version=v1 case gloov1.SchemeGroupVersion.WithResource("upstreams"): return &genericInformer{resource: resource.GroupResource(), informer: f.Gloo().V1().Upstreams().Informer()}, nil @@ -114,7 +121,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 v1alpha2.SchemeGroupVersion.WithResource("trafficsplits"): + case smiv1alpha2.SchemeGroupVersion.WithResource("trafficsplits"): return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha2().TrafficSplits().Informer()}, nil // Group=split.smi-spec.io, Version=v1alpha3 diff --git a/pkg/client/listers/gatewayapi/v1alpha2/expansion_generated.go b/pkg/client/listers/gatewayapi/v1alpha2/expansion_generated.go new file mode 100644 index 00000000..75c1e459 --- /dev/null +++ b/pkg/client/listers/gatewayapi/v1alpha2/expansion_generated.go @@ -0,0 +1,35 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +// GatewayListerExpansion allows custom methods to be added to +// GatewayLister. +type GatewayListerExpansion interface{} + +// GatewayNamespaceListerExpansion allows custom methods to be added to +// GatewayNamespaceLister. +type GatewayNamespaceListerExpansion interface{} + +// HTTPRouteListerExpansion allows custom methods to be added to +// HTTPRouteLister. +type HTTPRouteListerExpansion interface{} + +// HTTPRouteNamespaceListerExpansion allows custom methods to be added to +// HTTPRouteNamespaceLister. +type HTTPRouteNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/gatewayapi/v1alpha2/gateway.go b/pkg/client/listers/gatewayapi/v1alpha2/gateway.go new file mode 100644 index 00000000..854c038e --- /dev/null +++ b/pkg/client/listers/gatewayapi/v1alpha2/gateway.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// GatewayLister helps list Gateways. +// All objects returned here must be treated as read-only. +type GatewayLister interface { + // List lists all Gateways in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha2.Gateway, err error) + // Gateways returns an object that can list and get Gateways. + Gateways(namespace string) GatewayNamespaceLister + GatewayListerExpansion +} + +// gatewayLister implements the GatewayLister interface. +type gatewayLister struct { + indexer cache.Indexer +} + +// NewGatewayLister returns a new GatewayLister. +func NewGatewayLister(indexer cache.Indexer) GatewayLister { + return &gatewayLister{indexer: indexer} +} + +// List lists all Gateways in the indexer. +func (s *gatewayLister) List(selector labels.Selector) (ret []*v1alpha2.Gateway, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.Gateway)) + }) + return ret, err +} + +// Gateways returns an object that can list and get Gateways. +func (s *gatewayLister) Gateways(namespace string) GatewayNamespaceLister { + return gatewayNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// GatewayNamespaceLister helps list and get Gateways. +// All objects returned here must be treated as read-only. +type GatewayNamespaceLister interface { + // List lists all Gateways in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha2.Gateway, err error) + // Get retrieves the Gateway from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha2.Gateway, error) + GatewayNamespaceListerExpansion +} + +// gatewayNamespaceLister implements the GatewayNamespaceLister +// interface. +type gatewayNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Gateways in the indexer for a given namespace. +func (s gatewayNamespaceLister) List(selector labels.Selector) (ret []*v1alpha2.Gateway, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.Gateway)) + }) + return ret, err +} + +// Get retrieves the Gateway from the indexer for a given namespace and name. +func (s gatewayNamespaceLister) Get(name string) (*v1alpha2.Gateway, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("gateway"), name) + } + return obj.(*v1alpha2.Gateway), nil +} diff --git a/pkg/client/listers/gatewayapi/v1alpha2/httproute.go b/pkg/client/listers/gatewayapi/v1alpha2/httproute.go new file mode 100644 index 00000000..002ffe9a --- /dev/null +++ b/pkg/client/listers/gatewayapi/v1alpha2/httproute.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Flux 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + v1alpha2 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// HTTPRouteLister helps list HTTPRoutes. +// All objects returned here must be treated as read-only. +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) + // HTTPRoutes returns an object that can list and get HTTPRoutes. + HTTPRoutes(namespace string) HTTPRouteNamespaceLister + HTTPRouteListerExpansion +} + +// hTTPRouteLister implements the HTTPRouteLister interface. +type hTTPRouteLister struct { + indexer cache.Indexer +} + +// NewHTTPRouteLister returns a new HTTPRouteLister. +func NewHTTPRouteLister(indexer cache.Indexer) HTTPRouteLister { + return &hTTPRouteLister{indexer: indexer} +} + +// List lists all HTTPRoutes in the indexer. +func (s *hTTPRouteLister) List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.HTTPRoute)) + }) + return ret, err +} + +// HTTPRoutes returns an object that can list and get HTTPRoutes. +func (s *hTTPRouteLister) HTTPRoutes(namespace string) HTTPRouteNamespaceLister { + return hTTPRouteNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// HTTPRouteNamespaceLister helps list and get HTTPRoutes. +// All objects returned here must be treated as read-only. +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) + // 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) + HTTPRouteNamespaceListerExpansion +} + +// hTTPRouteNamespaceLister implements the HTTPRouteNamespaceLister +// interface. +type hTTPRouteNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all HTTPRoutes in the indexer for a given namespace. +func (s hTTPRouteNamespaceLister) List(selector labels.Selector) (ret []*v1alpha2.HTTPRoute, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.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) { + 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 obj.(*v1alpha2.HTTPRoute), nil +} diff --git a/pkg/metrics/observers/factory.go b/pkg/metrics/observers/factory.go index 23e4bd2f..af699919 100644 --- a/pkg/metrics/observers/factory.go +++ b/pkg/metrics/observers/factory.go @@ -68,7 +68,7 @@ func (factory Factory) Observer(provider string) Interface { return &NginxObserver{ client: factory.Client, } - case provider == flaggerv1.KubernetesProvider: + case provider == flaggerv1.KubernetesProvider || provider == flaggerv1.GatewayAPIProvider: return &HttpObserver{ client: factory.Client, } diff --git a/pkg/router/factory.go b/pkg/router/factory.go index e5a864e7..812e9126 100644 --- a/pkg/router/factory.go +++ b/pkg/router/factory.go @@ -177,6 +177,12 @@ func (factory *Factory) MeshRouter(provider string, labelSelector string) Interf kubeClient: factory.kubeClient, kumaClient: factory.meshClient, } + case provider == flaggerv1.GatewayAPIProvider: + return &GatewayAPIRouter{ + logger: factory.logger, + kubeClient: factory.kubeClient, + gatewayAPIClient: factory.meshClient, + } case provider == flaggerv1.KubernetesProvider: return &NopRouter{} default: diff --git a/pkg/router/gateway_api.go b/pkg/router/gateway_api.go new file mode 100644 index 00000000..a3b22b9e --- /dev/null +++ b/pkg/router/gateway_api.go @@ -0,0 +1,385 @@ +/* +Copyright 2022 The Flux 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 router + +import ( + "context" + "fmt" + "reflect" + + flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1" + "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1alpha2" + "github.com/fluxcd/flagger/pkg/apis/istio/v1alpha3" + clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" +) + +var ( + initialPrimaryWeight = int32(100) + initialCanaryWeight = int32(0) + 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 +) + +type GatewayAPIRouter struct { + gatewayAPIClient clientset.Interface + kubeClient kubernetes.Interface + logger *zap.SugaredLogger +} + +func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error { + if len(canary.Spec.Service.GatewayRefs) == 0 { + return fmt.Errorf("GatewayRefs must be specified when using Gateway API as a provider.") + } + + apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() + + hrNamespace := canary.Namespace + if canary.Spec.TargetRef.Namespace != "" { + hrNamespace = canary.Spec.TargetRef.Namespace + } + + hostNames := []v1alpha2.Hostname{} + for _, host := range canary.Spec.Service.Hosts { + hostNames = append(hostNames, v1alpha2.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{ + Type: &pathMatchType, + Value: &pathMatchValue, + }, + }) + } + + httpRouteSpec := v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: canary.Spec.Service.GatewayRefs, + }, + Hostnames: hostNames, + Rules: []v1alpha2.HTTPRouteRule{ + { + Matches: matches, + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + }, + { + BackendRef: gwr.makeBackendRef(canarySvcName, initialCanaryWeight, canary.Spec.Service.Port), + }, + }, + }, + }, + } + + // 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 = analysisMatches + httpRouteSpec.Rules = append(httpRouteSpec.Rules, v1alpha2.HTTPRouteRule{ + Matches: matches, + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + }, + }, + }) + } + + httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get( + context.TODO(), apexSvcName, metav1.GetOptions{}, + ) + + if errors.IsNotFound(err) { + metadata := canary.Spec.Service.Apex + if metadata == nil { + metadata = &flaggerv1.CustomMetadata{} + } + if metadata.Labels == nil { + metadata.Labels = make(map[string]string) + } + if metadata.Annotations == nil { + metadata.Annotations = make(map[string]string) + } + route := &v1alpha2.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: apexSvcName, + Namespace: hrNamespace, + Labels: metadata.Labels, + Annotations: filterMetadata(metadata.Annotations), + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(canary, schema.GroupVersionKind{ + Group: flaggerv1.SchemeGroupVersion.Group, + Version: flaggerv1.SchemeGroupVersion.Version, + Kind: flaggerv1.CanaryKind, + }), + }, + }, + Spec: httpRouteSpec, + } + + _, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace). + Create(context.TODO(), route, metav1.CreateOptions{}) + + if err != nil { + return fmt.Errorf("HTTPRoute %s.%s create error: %w", apexSvcName, hrNamespace, err) + } + gwr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). + Infof("HTTPRoute %s.%s created", route.GetName(), hrNamespace) + } else if err != nil { + return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err) + } + + if diff := cmp.Diff( + httpRoute.Spec, httpRouteSpec, + cmpopts.IgnoreFields(v1alpha2.BackendRef{}, "Weight"), + ); diff != "" { + hrClone := httpRoute.DeepCopy() + hrClone.Spec = httpRouteSpec + _, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace). + Update(context.TODO(), hrClone, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("HTTPRoute %s.%s update error: %w", hrClone.GetName(), hrNamespace, err) + } + gwr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). + Infof("HTTPProxy %s.%s updated", hrClone.GetName(), hrNamespace) + } + return nil +} + +func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) ( + primaryWeight int, + canaryWeight int, + mirrored bool, + err error, +) { + apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() + hrNamespace := canary.Namespace + if canary.Spec.TargetRef.Namespace != "" { + hrNamespace = canary.Spec.TargetRef.Namespace + } + httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{}) + if err != nil { + err = fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err) + return + } + for _, rule := range httpRoute.Spec.Rules { + // A/B testing: Avoid reading the rule with only for backendRef. + if len(rule.BackendRefs) == 2 { + 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) + } + } + } + + } + return +} + +func (gwr *GatewayAPIRouter) SetRoutes( + canary *flaggerv1.Canary, + primaryWeight int, + canaryWeight int, + mirrored bool, +) error { + pWeight := int32(primaryWeight) + cWeight := int32(canaryWeight) + apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() + hrNamespace := canary.Namespace + if canary.Spec.TargetRef.Namespace != "" { + hrNamespace = canary.Spec.TargetRef.Namespace + } + httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().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{} + for _, host := range canary.Spec.Service.Hosts { + hostNames = append(hostNames, v1alpha2.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{ + Type: &pathMatchType, + Value: &pathMatchValue, + }, + }) + } + httpRouteSpec := v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: canary.Spec.Service.GatewayRefs, + }, + Hostnames: hostNames, + Rules: []v1alpha2.HTTPRouteRule{ + { + Matches: matches, + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: gwr.makeBackendRef(primarySvcName, pWeight, canary.Spec.Service.Port), + }, + { + BackendRef: gwr.makeBackendRef(canarySvcName, cWeight, canary.Spec.Service.Port), + }, + }, + }, + }, + } + hrClone.Spec = httpRouteSpec + + // A/B testing + if len(canary.GetAnalysis().Match) > 0 { + analysisMatches, _ := gwr.mapRouteMatches(canary.GetAnalysis().Match) + hrClone.Spec.Rules[0].Matches = analysisMatches + hrClone.Spec.Rules = append(hrClone.Spec.Rules, v1alpha2.HTTPRouteRule{ + Matches: matches, + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + }, + }, + }) + } + + _, err = gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Update(context.TODO(), hrClone, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("HTTPRoute %s.%s update error: %w", hrClone.GetName(), hrNamespace, err) + } + return nil +} + +func (gwr *GatewayAPIRouter) Finalize(_ *flaggerv1.Canary) error { + return nil +} + +func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatchRequest) ([]v1alpha2.HTTPRouteMatch, error) { + matches := []v1alpha2.HTTPRouteMatch{} + + for _, requestMatch := range requestMatches { + match := v1alpha2.HTTPRouteMatch{} + if requestMatch.Uri != nil { + if requestMatch.Uri.Regex != "" { + match.Path = &v1alpha2.HTTPPathMatch{ + Type: &pathMatchRegex, + Value: &requestMatch.Uri.Regex, + } + } else if requestMatch.Uri.Exact != "" { + match.Path = &v1alpha2.HTTPPathMatch{ + Type: &pathMatchExact, + Value: &requestMatch.Uri.Exact, + } + } else if requestMatch.Uri.Prefix != "" { + match.Path = &v1alpha2.HTTPPathMatch{ + Type: &pathMatchPrefix, + Value: &requestMatch.Uri.Prefix, + } + } else { + return nil, fmt.Errorf("Gateway API doesn't support the specified path matching selector: %+v\n", requestMatch.Uri) + } + } + if requestMatch.Method != nil { + if requestMatch.Method.Exact != "" { + method := v1alpha2.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{} + if val.Exact != "" { + headerMatch.Name = v1alpha2.HTTPHeaderName(key) + headerMatch.Type = &headerMatchExact + headerMatch.Value = val.Exact + } else if val.Regex != "" { + headerMatch.Name = v1alpha2.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) { + match.Headers = append(match.Headers, headerMatch) + } + } + + for key, val := range requestMatch.QueryParams { + queryMatch := v1alpha2.HTTPQueryParamMatch{} + if val.Exact != "" { + queryMatch.Name = key + queryMatch.Type = &queryMatchExact + queryMatch.Value = val.Exact + } else if val.Regex != "" { + queryMatch.Name = 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) { + match.QueryParams = append(match.QueryParams, queryMatch) + } + } + + if !reflect.DeepEqual(match, v1alpha2.HTTPRouteMatch{}) { + matches = append(matches, match) + } + } + + 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), + }, + Weight: &weight, + } +} diff --git a/test/gatewayapi/install.sh b/test/gatewayapi/install.sh new file mode 100755 index 00000000..e7f54e29 --- /dev/null +++ b/test/gatewayapi/install.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -o errexit + +CONTOUR_VER="release-1.20" +GATEWAY_API_VER="v1alpha2" +REPO_ROOT=$(git rev-parse --show-toplevel) +KUSTOMIZE_VERSION=4.5.2 +OS=$(uname -s) +ARCH=$(arch) + +mkdir -p ${REPO_ROOT}/bin + +echo ">>> Installing Contour ${CONTOUR_VER}, Gateway API components ${GATEWAY_API_VER}" +cd ${REPO_ROOT}/bin && \ +kubectl apply -f https://raw.githubusercontent.com/projectcontour/contour/${CONTOUR_VER}/examples/render/contour-gateway.yaml + +kubectl -n projectcontour rollout status deployment/contour +kubectl -n projectcontour get all + +echo '>>> Installing Kustomize' +cd ${REPO_ROOT}/bin && kustomize_url=https://github.com/kubernetes-sigs/kustomize/releases/download && \ +curl -sL ${kustomize_url}/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_${OS}_${ARCH}.tar.gz | \ +tar xz + +echo '>>> Installing Flagger' +${REPO_ROOT}/bin/kustomize build ${REPO_ROOT}/test/gatewayapi | kubectl apply -f - + +kubectl -n projectcontour set image deployment/flagger flagger=test/flagger:latest +kubectl -n projectcontour rollout status deployment/flagger diff --git a/test/gatewayapi/kustomization.yaml b/test/gatewayapi/kustomization.yaml new file mode 100644 index 00000000..993f5271 --- /dev/null +++ b/test/gatewayapi/kustomization.yaml @@ -0,0 +1,6 @@ +namespace: projectcontour +bases: + - ../../kustomize/base/flagger/ + - ../../kustomize/base/prometheus/ +patchesStrategicMerge: + - patch.yaml diff --git a/test/gatewayapi/patch.yaml b/test/gatewayapi/patch.yaml new file mode 100644 index 00000000..b17f1f60 --- /dev/null +++ b/test/gatewayapi/patch.yaml @@ -0,0 +1,15 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flagger +spec: + template: + spec: + containers: + - name: flagger + image: aryan9600/flagger + args: + - -log-level=debug + - -include-label-prefix=app.kubernetes.io + - -mesh-provider=gatewayapi + - -metrics-server=http://flagger-prometheus:9090 diff --git a/test/gatewayapi/run.sh b/test/gatewayapi/run.sh new file mode 100755 index 00000000..519424be --- /dev/null +++ b/test/gatewayapi/run.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -o errexit + +REPO_ROOT=$(git rev-parse --show-toplevel) +DIR="$(cd "$(dirname "$0")" && pwd)" + +"$DIR"/install.sh + +"$REPO_ROOT"/test/workloads/init.sh +"$DIR"/run-canary.sh diff --git a/test/gatewayapi/test-canary.sh b/test/gatewayapi/test-canary.sh new file mode 100755 index 00000000..06fba8b3 --- /dev/null +++ b/test/gatewayapi/test-canary.sh @@ -0,0 +1,331 @@ +#!/usr/bin/env bash + +# This script runs e2e tests for Canary, A/B initialization, analysis and promotion +# Prerequisites: Kubernetes Kind and Contour with GatewayAPI + +set -o errexit + +echo '>>> Installing Canary' +cat <>> Waiting for primary to be ready' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false + sleep 5 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary initialization test passed' + +passed=$(kubectl -n test get svc/podinfo -o jsonpath='{.spec.selector.app}' 2>&1 | { grep podinfo-primary || true; }) +if [ -z "$passed" ]; then + echo -e '\u2716 podinfo selector test failed' + exit 1 +fi + +echo '✔ Canary service custom metadata test passed' + +echo '>>> Triggering canary deployment' +kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.1 + +echo '>>> Waiting for canary promotion' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test describe deployment/podinfo-primary | grep '3.1.1' && ok=true || ok=false + sleep 10 + kubectl -n projectcontour logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '>>> Waiting for canary finalization' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Succeeded' && ok=true || ok=false + sleep 5 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n istio-system logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary promotion test passed' + +if [[ "$1" = "canary" ]]; then + exit 0 +fi + +cat <>> Triggering B/G deployment' +kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.2 + +echo '>>> Waiting for B/G promotion' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test describe deployment/podinfo-primary | grep '3.1.2' && ok=true || ok=false + sleep 10 + kubectl -n projectcontour logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n test describe deployment/podinfo + kubectl -n test describe deployment/podinfo-primary + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '>>> Waiting for B/G finalization' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Succeeded' && ok=true || ok=false + sleep 5 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ B/G promotion test passed' + +cat <>> Triggering A/B testing' +kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.3 + +echo '>>> Waiting for A/B testing promotion' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test describe deployment/podinfo-primary | grep '3.1.3' && ok=true || ok=false + sleep 10 + kubectl -n projectcontour logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n test describe deployment/podinfo + kubectl -n test describe deployment/podinfo-primary + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ A/B testing promotion test passed' + +cat <>> Triggering canary deployment rollback test' +kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.2 + +echo '>>> Waiting for canary rollback' +retries=50 +count=0 +ok=false +until ${ok}; do + kubectl -n test get canary/podinfo | grep 'Failed' && ok=true || ok=false + sleep 10 + kubectl -n projectcontour logs deployment/flagger --tail 1 + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n projectcontour logs deployment/flagger + echo "No more retries left" + exit 1 + fi +done + +echo '✔ Canary rollback test passed'