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 May 22, 2020
1 parent 2b66cd7 commit 1f21b8c
Show file tree
Hide file tree
Showing 10 changed files with 1,097 additions and 536 deletions.
218 changes: 168 additions & 50 deletions api/v1alpha1/gateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ 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. The distinguishing characteristics of a gateway are
// that it has at least one IP address, and that it behaves as a network
// endpoint from the perspective of a client application.
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 +57,185 @@ 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. If no Listeners are
// provided, the GatewayClass may associate the Gateway with
// an address but the Gateway will not be able to accept any
// connections.
//
// +optional
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.

// Address 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
// addresses are specified, the GatewayClass may schedule the Gateway
// in an implementation-defined manner, providing it with an appropriate
// set of addresses.
//
// Support: Core
// Support:
//
Routes RouteBindingSelector `json:"routes" protobuf:"bytes,3,opt,name=routes"`
// +optional
Addresses []ListenerAddress `json:"endpoints" protobuf:"bytes,3,rep,name=endpoints"`
}

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

const (
// HTTPProtocol constant.
HTTPProtocol = "HTTP"
// HTTPSProtocol constant.
HTTPSProtocol = "HTTPS"
// 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"
)

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

const (
// HostnameMatchDomain specifies that the host name provided by the
// client should be matched against a DNS domain value. The domain
// match should follow the wildcard certificate matching rules in
// [RFC 6125](https://tools.ietf.org/html/rfc6125#section-6.4.3).
HostnameMatchDomain HostnameMatchType = "Domain"

// HostnameMatchExact specifies that the host name provided by the
// client must exactly match the specified value.
HostnameMatchExact HostnameMatchType = "Exact"
)

// HostnameMatch specifies how a Listener should match the host name from a
// client request. Depending on the incoming protocol, the match may apply to
// the TLS and the HTTP protocol.
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
// +kubebuilder:default=Exact
Match HostnameMatchType `json:"match" protobuf:"bytes,1,name=match"`

// Value contains the value to match against. This value must be a
// fully qualified host or domain name as defined by
// [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2).
// Note the following deviations from the "host" part of the URI as defined in the RFC:
//
// 1. IPs are not allowed.
// 2. The `:` delimiter is not respected because ports are not allowed.
//
// +required
// +kubebuilder:validation:MinLength=1
Value string `json:"value" protobuf:"bytes,2,name=value"`
}

// Listener embodies the concept of a logical endpoint where a
// Gateway can accept network connections.
//
// The GatewayClass may collapse Listener definitions into single
// implementation-defined acceptor configuration. This allows Gateways to
// be implemented in terms of virtual services configured within the same
// proxy server process.
//
// Listeners may be considered collapsible if the Protocol field is
// "HTTP", "HTTPS" or "TLS", the Hostname field is specified, and the Port
// field matches. As a special case, each group of collapsed Listeners may
// contain up to one Listener that omits the Hostname field. Listeners that
// specify identical Hostname fields must not be treated as collapsible.
//
// If the GatewayClass collapses Listeners, the Hostname field must be matched
// in order of most to least specific. "Exact" matches must be processed before
// "Domain" matches, which must be processed before Listeners with no Hostname
// field.
//
// If the Gateway specifies multiple Listeners that have the same Port value
// but are not collapsible, the GatewayClass must raise a "PortConflict"
// condition on the Gateway.
type Listener struct {
// Name is the listener's name and should be specified as an
// RFC 1035 DNS_LABEL [1]:
// Hostname specifies the virtual host name for protocol types
// that define this concept.
//
// [1] https://tools.ietf.org/html/rfc1035
// Incoming requests are matched against Hostname before processing
// HTTPRoute rules. For example, if the HTTP request header
// contains "Host: foo.example.com", an Listener with Hostname value
// "foo.example.com" will be an "Exact" match, but a Listener with
// Hostname values "example.com" or "bar.example.com" will not
// match. However these host names would match against a "Domain"
// match type with a value of "example.com".
//
// Each listener of a Gateway must have a unique name. Name is used
// for associating a listener in Gateway status.
// If Hostname is unspecified, the Gateway routes all traffic based on
// the specified rules.
//
// Support: Core
// A Hostname match should be supplied for "HTTP" and "HTTPS"
// protocols, and may be supplied for "TLS" protocol. If no
// match is supplied, then this Listener must accept all client
// connections on the specified Port.
//
// +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.
// As Listener that omits this field may be collapsed with other
// Listeners on the same port and protocol. There may be only
// one such Listener per Port. If more that one is configured,
// the GatewayClass must raise a "NameConflict" condition.
//
// 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.
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 collapsing rules.
//
// Support:
// +optional
Port *int32 `json:"port,omitempty" protobuf:"varint,3,opt,name=port"`
// Protocol to use.
// Support: Core
//
// Support:
// +optional
Protocol *string `json:"protocol,omitempty" protobuf:"bytes,4,opt,name=protocol"`
// +required
Port int32 `json:"port,omitempty" protobuf:"varint,2,opt,name=port"`

// Protocol specifies the application protocol this listener expects to receive.
//
// 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. If unspecified,
// the listener will not support TLS connections.
// the Listener will not support TLS connections. If the Hostname
// field is also specified, this endpoint must only accept TLS
// sessions where the client sends a TLS Server Name Indication (SNI)
// extension, and that value matches against the Listener's Hostname
// field.
//
// If the Protocol field is "HTTPS" or "TLS", this field is required.
//
// 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 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.
//
// 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 @@ -259,8 +369,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 indexexd 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 @@ -285,6 +399,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 1f21b8c

Please sign in to comment.