Skip to content

Commit

Permalink
add starting implementation of gateway api v1alpha2
Browse files Browse the repository at this point in the history
  • Loading branch information
jcmoraisjr committed Feb 9, 2022
1 parent 3940408 commit a718769
Show file tree
Hide file tree
Showing 6 changed files with 1,694 additions and 27 deletions.
43 changes: 23 additions & 20 deletions docs/content/en/docs/configuration/gateway-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,44 @@ description: >

The following steps configure the Kubernetes cluster and HAProxy Ingress to read and parse Gateway API resources:

* Manually install the Gateway API CRDs, see the Gateway API [documentation](https://gateway-api.sigs.k8s.io/v1alpha1/guides/getting-started/#installing-gateway-api-crds-manually)
* ... or simply `kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.3.0" | kubectl apply -f -`
* Manually install the Gateway API CRDs, see the Gateway API [documentation](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/#installing-gateway-api-crds-manually)
* ... or simply `kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.4.1" | kubectl apply -f -`
* Start (or restart) the controller

See below the [getting started steps](#getting-started).

## Conformance

Gateway API v1alpha1 spec is partially implemented in the v0.13 release. The following list describes what is (or is not) supported:
Gateway API v1alpha2 spec is partially implemented in the v0.14 release. The following list describes what is (or is not) supported:

* Target Services can be annotated with [Backend or Path scoped]({{% relref "keys#scope" %}}) configuration keys, this will continue to be supported.
* Gateway API resources doesn't support annotations, this will continue to be unsupported, extensions to the Gateway API spec will be added in the extension points of the API.
* Only the `GatewayClass`, `Gateway` and `HTTPRoute` resource definitions were implemented.
* The controller doesn't implement partial parsing yet for Gateway API resources, changes should be a bit slow on clusters with thousands of Ingress, Gateway API resources or Services.
* Gateway's Listener Port and Protocol wasn't implemented - Port uses the global [bind-port]({{% relref "keys#bind-port" %}}) configuration and Protocol is based on the presence or absence of the TLS attribute.
* Gateway's Route Namespace selector only supports `Same` or `All` namespaces.
* Gateway's Addresses wasn't implemented - binding addresses use the global [bind-ip-addr]({{% relref "keys#bind-ip-addr" %}}) configuration.
* Gateway's Hostname only supports empty/absence of Hostname or a single `*`, any other string will override the HTTPRoute Hostnames configuration without any merging.
* HTTPRoute's Matches doesn't support Headers.
* HTTPRoute's Rules and ForwardTo doesn't support Filters.
* HTTPRoute's Rules and BackendRefs don't support Filters.
* Resources status aren't updated.

Version v1alpha2 should be partially implemented in v0.14 (beta version starting jan/22) and fully implemented in v0.15 (Q2'22).
Version v1alpha2 should be fully implemented in v0.15 (Q2'22).

Version v1alpha1 support will be dropped in v0.16.

## Ingress

A single HAProxy Ingress deployment can manage Ingress and Gateway API resources in the same Kubernetes cluster. If the same hostname and path is declared in the Gateway API and Ingress, the Gateway API wins and a warning is logged. Ingress resources will continue to be supported in future controller versions, without side effects, and without the need to install the Gateway API CRDs.
A single HAProxy Ingress deployment can manage Ingress, and both v1alpha1 and v1alpha2 Gateway API resources in the same Kubernetes cluster. If the same hostname and path is declared in the Gateway API and Ingress, the Gateway API wins and a warning is logged. Ingress resources will continue to be supported in future controller versions, without side effects, and without the need to install the Gateway API CRDs.

## Getting started

Add the following steps to the [Getting Started guide]({{% relref "/docs/getting-started" %}}) in order to expose the echoserver service along with the Gateway API:

[Manually install](https://gateway-api.sigs.k8s.io/guides/getting-started/#installing-gateway-api-crds-manually) the Gateway API CRDs:
[Manually install](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/#installing-gateway-api-crds-manually) the Gateway API CRDs:

```
kubectl kustomize\
"github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.3.0" |\
"github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.4.1" |\
kubectl apply -f -
```

Expand All @@ -61,20 +63,20 @@ kubectl --namespace default expose deployment echoserver --port=8080
A GatewayClass enables Gateways to be read and parsed by HAProxy Ingress. Create a GatewayClass with the following content:

```yaml
apiVersion: networking.x-k8s.io/v1alpha1
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: GatewayClass
metadata:
name: haproxy
spec:
controller: haproxy-ingress.github.io/controller
controllerName: haproxy-ingress.github.io/controller
```
Gateways create listeners and allow to configure hostnames. Create a Gateway with the following content:
Note: port and protocol attributes [have some limitations](#conformance).
```yaml
apiVersion: networking.x-k8s.io/v1alpha1
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
name: echoserver
Expand All @@ -84,29 +86,30 @@ spec:
listeners:
- protocol: HTTP
port: 80
routes:
kind: HTTPRoute
selector:
matchLabels:
gateway: echo
name: echoserver-gw
allowedRoutes:
namespaces:
from: Same
```
HTTPRoutes configure the hostnames and target services. Create a HTTPRoute with the following content, changing `echoserver-from-gateway.local` to a hostname that resolves to a HAProxy Ingress node:

```yaml
apiVersion: networking.x-k8s.io/v1alpha1
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
labels:
gateway: echo
name: echoserver
namespace: default
spec:
parentRefs:
- name: echoserver-gw
hostnames:
- echoserver-from-gateway.local
rules:
- forwardTo:
- serviceName: echoserver
- backendRefs:
- name: echoserver
port: 8080
```

Expand Down
41 changes: 35 additions & 6 deletions pkg/controller/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,12 @@ func (c *k8scache) hasGateway() bool {
return c.listers.gatewayClassLister != nil
}

var errGatewayDisabled = fmt.Errorf("Gateway API wasn't initialized")
var errGatewayA1Disabled = fmt.Errorf("Gateway API v1alpha1 wasn't initialized")
var errGatewayA2Disabled = fmt.Errorf("Gateway API v1alpha2 wasn't initialized")

func (c *k8scache) GetGatewayA1(gatewayName string) (*gatewayv1alpha1.Gateway, error) {
if !c.hasGatewayA1() {
return nil, errGatewayDisabled
return nil, errGatewayA1Disabled
}
namespace, name, err := cache.SplitMetaNamespaceKey(gatewayName)
if err != nil {
Expand All @@ -230,7 +231,7 @@ func (c *k8scache) GetGatewayA1(gatewayName string) (*gatewayv1alpha1.Gateway, e

func (c *k8scache) GetGatewayA1List() ([]*gatewayv1alpha1.Gateway, error) {
if !c.hasGatewayA1() {
return nil, errGatewayDisabled
return nil, errGatewayA1Disabled
}
gwList, err := c.listers.gatewayA1Lister.List(labels.Everything())
if err != nil {
Expand All @@ -247,16 +248,33 @@ func (c *k8scache) GetGatewayA1List() ([]*gatewayv1alpha1.Gateway, error) {
return validGwList[:i], nil
}

func (c *k8scache) GetGatewayMap() (map[string]*gatewayv1alpha2.Gateway, error) {
if !c.hasGateway() {
return nil, errGatewayA2Disabled
}
gwList, err := c.listers.gatewayLister.List(labels.Everything())
if err != nil {
return nil, err
}
validGwList := make(map[string]*gatewayv1alpha2.Gateway, len(gwList))
for _, gw := range gwList {
if c.IsValidGateway(gw) {
validGwList[gw.Namespace+"/"+gw.Name] = gw
}
}
return validGwList, nil
}

func (c *k8scache) GetGatewayClassA1(className string) (*gatewayv1alpha1.GatewayClass, error) {
if !c.hasGatewayA1() {
return nil, errGatewayDisabled
return nil, errGatewayA1Disabled
}
return c.listers.gatewayClassA1Lister.Get(className)
}

func (c *k8scache) GetGatewayClass(className string) (*gatewayv1alpha2.GatewayClass, error) {
if !c.hasGateway() {
return nil, errGatewayDisabled
return nil, errGatewayA2Disabled
}
return c.listers.gatewayClassLister.Get(className)
}
Expand All @@ -271,7 +289,7 @@ func buildLabelSelector(match map[string]string) (labels.Selector, error) {

func (c *k8scache) GetHTTPRouteA1List(namespace string, match map[string]string) ([]*gatewayv1alpha1.HTTPRoute, error) {
if !c.hasGatewayA1() {
return nil, errGatewayDisabled
return nil, errGatewayA1Disabled
}
selector, err := buildLabelSelector(match)
if err != nil {
Expand All @@ -283,6 +301,13 @@ func (c *k8scache) GetHTTPRouteA1List(namespace string, match map[string]string)
return c.listers.httpRouteA1Lister.List(selector)
}

func (c *k8scache) GetHTTPRouteList() ([]*gatewayv1alpha2.HTTPRoute, error) {
if !c.hasGateway() {
return nil, errGatewayA2Disabled
}
return c.listers.httpRouteLister.List(labels.Everything())
}

func (c *k8scache) GetService(defaultNamespace, serviceName string) (*api.Service, error) {
namespace, name, err := c.buildResourceName(defaultNamespace, "service", serviceName, c.dynamicConfig.CrossNamespaceServices)
if err != nil {
Expand Down Expand Up @@ -310,6 +335,10 @@ func (c *k8scache) GetConfigMap(configMapName string) (*api.ConfigMap, error) {
return c.listers.configMapLister.ConfigMaps(namespace).Get(name)
}

func (c *k8scache) GetNamespace(name string) (*api.Namespace, error) {
return c.client.CoreV1().Namespaces().Get(c.ctx, name, metav1.GetOptions{})
}

func (c *k8scache) GetEndpoints(service *api.Service) (*api.Endpoints, error) {
return c.listers.endpointLister.Endpoints(service.Namespace).Get(service.Name)
}
Expand Down
Loading

0 comments on commit a718769

Please sign in to comment.