Skip to content

Commit

Permalink
OSSM-1423 Preliminary support for Gateway API
Browse files Browse the repository at this point in the history
This adds support for Gateway API in a multi-tenant OSSM install, plus
integration tests. It also improves upon our current test suite a bit.

Caveats:
- we don't support namespace selectors in the AllowedRoutes field
- GatewayClasses are ignored (but specifying "istio" always works)
  • Loading branch information
dgn authored and jewertow committed Aug 29, 2022
1 parent e73da2a commit 262fd34
Show file tree
Hide file tree
Showing 9 changed files with 4,224 additions and 71 deletions.
5 changes: 5 additions & 0 deletions pilot/pkg/config/kube/crdclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,11 @@ func handleCRDAdd(cl *Client, name string, stop <-chan struct{}) {
resourceGVK := s.Resource().GroupVersionKind()
gvr := s.Resource().GroupVersionResource()

if cl.client.GetMemberRoll() != nil && resourceGVK == gvk.GatewayClass {
scope.Infof("Skipping CRD %v as it is not compatible with maistra multi-tenancy", s.Resource().GroupVersionKind())
return
}

cl.kindsMu.Lock()
defer cl.kindsMu.Unlock()
if _, f := cl.kinds[resourceGVK]; f {
Expand Down
57 changes: 41 additions & 16 deletions pilot/pkg/config/kube/gateway/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,26 +87,33 @@ var _ model.GatewayController = &Controller{}
func NewController(client kube.Client, c model.ConfigStoreController, options controller.Options) *Controller {
var ctl *status.Controller

nsInformer := client.KubeInformer().Core().V1().Namespaces().Informer()
var nsLister listerv1.NamespaceLister
var nsInformer cache.SharedIndexInformer
if client.GetMemberRoll() == nil {
nsInformer = client.KubeInformer().Core().V1().Namespaces().Informer()
nsLister = client.KubeInformer().Core().V1().Namespaces().Lister()
}
gatewayController := &Controller{
client: client,
cache: c,
namespaceLister: client.KubeInformer().Core().V1().Namespaces().Lister(),
namespaceLister: nsLister,
namespaceInformer: nsInformer,
domain: options.DomainSuffix,
statusController: ctl,
// Disabled by default, we will enable only if we win the leader election
statusEnabled: atomic.NewBool(false),
}

nsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
gatewayController.namespaceEvent(nil, obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
gatewayController.namespaceEvent(oldObj, newObj)
},
})
if nsInformer != nil {
nsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
gatewayController.namespaceEvent(nil, obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
gatewayController.namespaceEvent(oldObj, newObj)
},
})
}

return gatewayController
}
Expand Down Expand Up @@ -202,13 +209,28 @@ func (c *Controller) Recompute(context model.GatewayContext) error {
return nil
}

nsl, err := c.namespaceLister.List(klabels.Everything())
if err != nil {
return fmt.Errorf("failed to list type Namespaces: %v", err)
}
namespaces := map[string]*corev1.Namespace{}
for _, ns := range nsl {
namespaces[ns.Name] = ns
if c.namespaceLister != nil {
nsl, err := c.namespaceLister.List(klabels.Everything())
if err != nil {
return fmt.Errorf("failed to list type Namespaces: %v", err)
}
for _, ns := range nsl {
namespaces[ns.Name] = ns
}
} else {
// we don't support namespace selectors in multi-tenant Istio right now,
// so we remove them, inducing default behavior (namespace-local)
for _, obj := range gateway {
gw := obj.Spec.(*k8s.GatewaySpec)
for _, listener := range gw.Listeners {
if listener.AllowedRoutes != nil &&
listener.AllowedRoutes.Namespaces != nil &&
listener.AllowedRoutes.Namespaces.Selector != nil {
listener.AllowedRoutes.Namespaces.Selector = nil
}
}
}
}
input.Namespaces = namespaces
output := convertResources(input)
Expand Down Expand Up @@ -272,6 +294,9 @@ func (c *Controller) RegisterEventHandler(typ config.GroupVersionKind, handler m
}

func (c *Controller) Run(stop <-chan struct{}) {
if c.namespaceInformer == nil {
return
}
go func() {
if crdclient.WaitForCRD(gvk.GatewayClass, stop) {
gcc := NewClassController(c.client)
Expand Down
33 changes: 22 additions & 11 deletions pilot/pkg/config/kube/gateway/deploymentcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"
"text/template"

maistrav1alpha2 "github.com/maistra/xns-informer/pkg/generated/gatewayapi/apis/v1alpha2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -77,8 +78,13 @@ type patcher func(gvr schema.GroupVersionResource, name string, namespace string
// NewDeploymentController constructs a DeploymentController and registers required informers.
// The controller will not start until Run() is called.
func NewDeploymentController(client kube.Client) *DeploymentController {
var gwcInformer maistrav1alpha2.GatewayClassInformer
var gwcLister v1alpha2.GatewayClassLister
if client.GetMemberRoll() == nil {
gwcInformer = client.GatewayAPIInformer().Gateway().V1alpha2().GatewayClasses()
gwcLister = gwcInformer.Lister()
}
gw := client.GatewayAPIInformer().Gateway().V1alpha2().Gateways()
gwc := client.GatewayAPIInformer().Gateway().V1alpha2().GatewayClasses()
dc := &DeploymentController{
client: client,
templates: processTemplates(),
Expand All @@ -92,7 +98,7 @@ func NewDeploymentController(client kube.Client) *DeploymentController {
return err
},
gatewayLister: gw.Lister(),
gatewayClassLister: gwc.Lister(),
gatewayClassLister: gwcLister,
}
dc.queue = controllers.NewQueue("gateway deployment",
controllers.WithReconciler(dc.Reconcile),
Expand All @@ -114,15 +120,17 @@ func NewDeploymentController(client kube.Client) *DeploymentController {

// Use the full informer; we are already watching all Gateways for the core Istiod logic
gw.Informer().AddEventHandler(controllers.ObjectHandler(dc.queue.AddObject))
gwc.Informer().AddEventHandler(controllers.ObjectHandler(func(o controllers.Object) {
o.GetName()
gws, _ := dc.gatewayLister.List(klabels.Everything())
for _, g := range gws {
if string(g.Spec.GatewayClassName) == o.GetName() {
dc.queue.AddObject(g)
if gwcInformer != nil {
gwcInformer.Informer().AddEventHandler(controllers.ObjectHandler(func(o controllers.Object) {
o.GetName()
gws, _ := dc.gatewayLister.List(klabels.Everything())
for _, g := range gws {
if string(g.Spec.GatewayClassName) == o.GetName() {
dc.queue.AddObject(g)
}
}
}
}))
}))
}

return dc
}
Expand All @@ -147,7 +155,10 @@ func (d *DeploymentController) Reconcile(req types.NamespacedName) error {
return nil
}

gc, _ := d.gatewayClassLister.Get(string(gw.Spec.GatewayClassName))
var gc *gateway.GatewayClass
if d.gatewayClassLister != nil {
gc, _ = d.gatewayClassLister.Get(string(gw.Spec.GatewayClassName))
}
if gc != nil {
// We found the gateway class, but we do not implement it. Skip
if gc.Spec.ControllerName != ControllerName {
Expand Down

0 comments on commit 262fd34

Please sign in to comment.