Skip to content

Commit

Permalink
Rewrite Listeners in terms of an endpoint concept.
Browse files Browse the repository at this point in the history
Rewrite Listeners to model the concept of a logical network endpoint.
This change defines a Gateway more strongly as the network element to
which addresses can be attached, and Listeners as logical endpoints
within the network element.

The main goal of this change is to consolidate the name of an application
(i.e. the endpoint) into a single part of the API that is controlled
by one user persona. In this change, the application name is strictly
part of the Gateway spec and controlled by the cluster operator role,
while routes are controlled by the application owner.

The Listener protocol is now strongly defined to a core set of common
protocol stacks. This loss of flexibility improves clarity because it
makes it possible to specify how Listeners should be collapsed into
virtual hosts in an underlying proxy implementation.

This change doesn't update ListenerStatus, though clearly it no longer
makes complete sense. The most likely change needed for GatewayStatus
is to drop the Listeners field and fold listener errors directly into
the Conditions field.

Signed-off-by: James Peach <jpeach@vmware.com>
  • Loading branch information
jpeach committed Jun 4, 2020
1 parent 7b07da9 commit a0723b3
Show file tree
Hide file tree
Showing 14 changed files with 1,587 additions and 696 deletions.
241 changes: 192 additions & 49 deletions api/v1alpha1/gateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import (
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// Gateway represents an instantiation of a service-traffic handling infrastructure.
// Gateway represents an instantiation of a service-traffic handling
// infrastructure by binding Listeners to a set of IP addresses.
type Gateway struct {
metav1.TypeMeta `json:",inline" protobuf:"bytes,1,opt,name=typeMeta"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,2,opt,name=metadata"`
Expand Down Expand Up @@ -54,78 +55,212 @@ type GatewayList struct {
type GatewaySpec struct {
// Class used for this Gateway. This is the name of a GatewayClass resource.
Class string `json:"class" protobuf:"bytes,1,opt,name=class"`
// Listeners associated with this Gateway. Listeners define what addresses,
// ports, protocols are bound on this Gateway.

// 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 this array must have a unique Port field,
// however a GatewayClass may collapse compatible Listener
// definitions into single implementation-defined acceptor
// configuration even if their Port fields would otherwise conflict.
//
// Listeners are compatible if all of the following conditions are true:
//
// 1. all their Protocol fields are "HTTP", or all their Protocol fields are "HTTPS" or TLS"
// 2. their Hostname fields are specified with a match type other than "Any"
// 3. their Hostname fields are not an exact match for any other Listener
//
// As a special case, each group of compatible listeners
// may contain exactly one Listener with a match type of "Any".
//
// If the GatewayClass collapses compatible Listeners, the
// host name provided in the incoming client request MUST be
// matched to a Listener to find the correct set of Routes.
// The incoming host name 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 "Domain"
// matches, which must be processed before "Any" matches.
//
// If this field specifies multiple Listeners that have the same
// Port value but are not compatible, the GatewayClass must raise
// a "PortConflict" condition on the Gateway.
//
// Support: Core
//
// +required
// +kubebuilder:validation:MinItems=1
Listeners []Listener `json:"listeners" protobuf:"bytes,2,rep,name=listeners"`
// Routes specifies a schema for associating routes with the Gateway using
// selectors. A route is a resource capable of servicing a request and allows
// a cluster operator to expose a cluster resource (i.e. Service) by
// externally-reachable URL, load-balance traffic and terminate SSL/TLS.
// Typically, a route is a "httproute" or "tcproute" in group
// "networking.x-k8s.io". However, an implementation may support other resources.

// Addresses requested for this gateway. This is optional and
// behavior can depend on the GatewayClass. If a value is set
// in the spec and the requested address is invalid, the
// GatewayClass MUST indicate this in the associated entry in
// GatewayStatus.Listeners.
//
// If no ListenerAddresses are specified, the GatewayClass may
// schedule the Gateway in an implementation-defined manner,
// assigning an appropriate set of ListenerAddresses.
//
// The GatewayClass MUST bind all Listeners to every
// ListenerAddress that it assigns to the Gateway.
//
// Support: Core
//
Routes RouteBindingSelector `json:"routes" protobuf:"bytes,3,opt,name=routes"`
// +optional
Addresses []ListenerAddress `json:"addresses" protobuf:"bytes,3,rep,name=addresses"`
}

// ProtocolType defines the application protocol accepted by a Listener.
type ProtocolType string

const (
// HTTPProtocolType accepts cleartext HTTP/1.1 sessions over TCP.
HTTPProtocolType ProtocolType = "HTTP"

// HTTPSProtocolType accepts HTTP/1.1 or HTTP/2 sessions over TLS.
HTTPSProtocolType ProtocolType = "HTTPS"

// TLSProtocolType accepts TLS sessions over TCP.
TLSProtocolType ProtocolType = "TLS"

// TCPProtocolType accepts TCP sessions.
TCPProtocolType ProtocolType = "TCP"
)

// HostnameMatchType specifies the types of matches that are valid
// for host names.
type HostnameMatchType string

const (
// HTTPProtocol constant.
HTTPProtocol = "HTTP"
// HTTPSProtocol constant.
HTTPSProtocol = "HTTPS"
// HostnameMatchDomain specifies that the host name provided
// by the client should be matched against a DNS domain value.
// The domain match removes the leftmost DNS label from the
// host name provided by the client and compares the resulting
// value.
//
// For example, "example.com" is a "Domain" match for the host
// name "foo.example.com", but not for "foo.bar.example.com"
// or for "example.foo.com".
//
// This match type MUST be case-insensitive.
HostnameMatchDomain HostnameMatchType = "Domain"

// HostnameMatchExact specifies that the host name provided
// by the client must exactly match the specified value.
//
// This match type MUST be case-insensitive.
HostnameMatchExact HostnameMatchType = "Exact"

// HostnameMatchAny specifies that this Listener accepts
// all client traffic regardless of the presence or value of
// any host name supplied by the client.
HostnameMatchAny HostnameMatchType = "Any"
)

// Listener defines a
// HostnameMatch specifies how a Listener should match the incoming
// host name from a client request. Depending on the incoming protocol,
// the match must apply to names provided by the client at both the
// TLS and the HTTP protocol layers.
type HostnameMatch struct {
// Match specifies how the host name provided by the client should be
// matched against the given value.
//
// +optional
// +kubebuilder:validation:Enum=Domain;Exact;Any
// +kubebuilder:default=Exact
Match HostnameMatchType `json:"match" protobuf:"bytes,1,name=match"`

// Name contains the name to match against. This value must
// be a fully qualified host or domain name conforming to the
// preferred name syntax defined in
// [RFC 1034](https://tools.ietf.org/html/rfc1034#section-3.5)
//
// In addition to any RFC rules, this field MUST NOT contain
//
// 1. IP address literals
// 2. Colon-delimited port numbers
// 3. Percent-encoded octets
//
// This field is required for the "Domain" and "Exact" match types.
//
// +optional
Name string `json:"name" protobuf:"bytes,2,name=name"`
}

// Listener embodies the concept of a logical endpoint where a
// Gateway can accept network connections.
type Listener struct {
// Name is the listener's name and should be specified as an
// RFC 1035 DNS_LABEL [1]:
// Hostname specifies to match the virtual host name for
// protocol types that define this concept.
//
// [1] https://tools.ietf.org/html/rfc1035
// Incoming requests that include a host name are matched
// according to the given HostnameMatchType to select
// the Routes from this Listener.
//
// Each listener of a Gateway must have a unique name. Name is used
// for associating a listener in Gateway status.
// If a match type other than "Any" is supplied, it MUST
// be compatible with the specified Protocol field.
//
// Support: Core
//
// +required
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Address requested for this listener. This is optional and behavior
// can depend on GatewayClass. If a value is set in the spec and
// the request address is invalid, the GatewayClass MUST indicate
// this in the associated entry in GatewayStatus.Listeners.
// +kubebuilder:default={match: "Any"}
Hostname HostnameMatch `json:"hostname,omitempty" protobuf:"bytes,1,opt,name=hostname"`

// Port is the network port. Multiple listeners may use the
// same port, subject to the Listener compatibility rules.
//
// Support:
// Support: Core
//
// +optional
Address *ListenerAddress `json:"address,omitempty" protobuf:"bytes,2,opt,name=address"`
// Port is a list of ports associated with the Address.
// +required
Port int32 `json:"port,omitempty" protobuf:"varint,2,opt,name=port"`

// Protocol specifies the network protocol this listener
// expects to receive. The GatewayClass MUST validate that
// match type specified in the Hostname field is appropriate
// for the protocol.
//
// Support:
// +optional
Port *int32 `json:"port,omitempty" protobuf:"varint,3,opt,name=port"`
// Protocol to use.
// * For the "TLS" protocol, the Hostname match MUST be
// applied to the [SNI](https://tools.ietf.org/html/rfc6066#section-3)
// server name offered by the client.
// * For the "HTTP" protocol, the Hostname match MUST be
// applied to the host portion of the
// [effective request URI](https://tools.ietf.org/html/rfc7230#section-5.5)
// or the [:authority pseudo-header](https://tools.ietf.org/html/rfc7540#section-8.1.2.3)
// * For the "HTTPS" protocol, the Hostname match MUST be
// applied at both the TLS and HTTP protocol layers.
//
// Support:
// +optional
Protocol *string `json:"protocol,omitempty" protobuf:"bytes,4,opt,name=protocol"`
// TLS is the TLS configuration for the Listener. If unspecified,
// the listener will not support TLS connections.
// Support: Core
//
// +required
// +kubebuilder:validation:Enum=HTTP;HTTPS;TLS;TCP
Protocol ProtocolType `json:"protocol,omitempty" protobuf:"bytes,3,opt,name=protocol"`

// TLS is the TLS configuration for the Listener. This field
// is required if the Protocol field is "HTTPS" or "TLS".
//
// Support: Core
//
// +optional
TLS *TLSConfig `json:"tls,omitempty" protobuf:"bytes,5,opt,name=tls"`
// ExtensionRef for this Listener. The resource may be "configmaps" or
// an implementation-defined resource (for example, resource
// "mylisteners" in group "networking.acme.io"). Omitting or specifying
// the empty string for both the resource and group indicates that the
// resource is "configmaps". If the referent cannot be found, the
// listener's "InvalidListener" status condition will be true.
//
// Support: custom.
// +optional
ExtensionRef *ListenerExtensionObjectReference `json:"extensionRef,omitempty" protobuf:"bytes,6,opt,name=extensionRef"`
TLS *TLSConfig `json:"tls,omitempty" protobuf:"bytes,4,opt,name=tls"`

// Routes specifies a schema for associating routes with the
// Listener using selectors. A Route is a resource capable of
// servicing a request and allows a cluster operator to expose
// a cluster resource (i.e. Service) by externally-reachable
// URL, load-balance traffic and terminate SSL/TLS. Typically,
// a route is a "HTTPRoute" or "TCPRoute" in group
// "networking.x-k8s.io", however, an implementation may support
// other types of resources.
//
// The Routes selector MUST select a set of objects that
// are compatible with the application protocol specified in
// the Protocol field.
//
// Support: Core
//
// +required
Routes RouteBindingSelector `json:"routes" protobuf:"bytes,5,opt,name=routes"`
}

// AddressType defines how a network address is represented as a text string.
Expand Down Expand Up @@ -261,8 +396,12 @@ type GatewayCondition struct {
// ListenerStatus is the status associated with each listener block.
type ListenerStatus struct {
// Name is the name of the listener this status refers to.
// TODO(jpeach) Listeners are not indexed by a unique name any more,
// so this field probably doesn't make sense.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Address bound on this listener.
// TODO(jpeach) Listeners don't have addresses anymore so this field
// should move to the GatewayStatus.
Address *ListenerAddress `json:"address" protobuf:"bytes,2,opt,name=address"`
// Conditions describe the current condition of this listener.
Conditions []ListenerCondition `json:"conditions" protobuf:"bytes,3,rep,name=conditions"`
Expand All @@ -287,6 +426,10 @@ const (
// ConditionNameConflict indicates that two or more Listeners with
// the same name were bound to this gateway.
ConditionNameConflict ListenerConditionType = "NameConflict"
// ConditionPortConflict indicates that two or more Listeners with
// the same port were bound to this gateway and they could not be
// collapsed into a single configuration.
ConditionPortConflict ListenerConditionType = "PortConflict"
// ConditionInvalidCertificateRef indicates the certificate reference of the
// listener's TLS configuration is invalid.
ConditionInvalidCertificateRef ListenerConditionType = "InvalidCertificateRef"
Expand Down
Loading

0 comments on commit a0723b3

Please sign in to comment.