Delegating with Route Tables

The Gloo Virtual Service makes it possible to define all routes for a domain on a single configuration resource.

However, condensing all routing config onto a single object can be cumbersome when dealing with a large number of routes.

Gloo provides a feature referred to as delegation. Delegation allows a complete routing configuration to be assembled from separate config objects. The root config object delegates responsibility to other objects, forming a tree of config objects. The tree always has a Virtual Service as its root, which delgates to any number of Route Tables. Route Tables can further delegate to other Route Tables.

Motivation

Use cases for delegation include:

Using delegation, organizations can delegate ownership of routing config to individuals or teams. Those individuals or teams can then further delegate routes to others.

Config Model

A configuration using Delegation can be understood as a tree.

The root node in the tree is a Virtual Service while every child node is a RouteTable:

graph LR; vs[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1(Route Table

/api/pets
/api/vets) vs -->|delegate /site prefix | rt2(Route Table

/site/login
/site/logout) style vs fill:#0DFF00,stroke:#233,stroke-width:4px

Route Tables can be nested for any level of granularity:

graph LR; vs[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1(Route Table

/api/pets
/api/vets) rt1 -->|delegate /api/pets/.* prefix | rt3(Route Table

/api/pets/.*/records
/api/pets/.*/billing) vs -->|delegate /site prefix | rt2(Route Table

/site/login
/site/logout) rt1 -->|delegate /api/vets prefix | rt4(Route Table

GET /api/vets
POST /api/vets) style vs fill:#0DFF00,stroke:#233,stroke-width:4px

Non-delegating routes can be defined at every level of the config tree:

graph LR; vs[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1(Route Table

/api/pets
/api/vets) vs -->|delegate /site prefix | rt2(Route Table

/site/login
/site/logout) vs -->|route /pharmacy | us1(Upstream

pharmacy-svc.petstore.cluster.svc.local:80) rt1 -->|route /api/pets | us2(Upstream

pet-svc.petstore.cluster.svc.local:80) style vs fill:#0DFF00,stroke:#233,stroke-width:4px style us1 fill:#f9f,stroke:#333,stroke-width:4px style us2 fill:#f9f,stroke:#333,stroke-width:4px

Routes defined at any level must inherit the prefix delegated to them, else Gloo will not consider the config tree valid:

graph LR; subgraph invalid vs[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1(Route Table

/api/v1
/v2) style vs fill:#f54,stroke:#233,stroke-width:4px end subgraph valid vsValid[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1Valid(Route Table

/api/v1
/api/v2) style vsValid fill:#0DFF00,stroke:#233,stroke-width:4px end

Gloo will flatten the non-delegated routes defined in config tree down to a single Proxy object, such that:

graph LR; vs[Virtual Service

*.petclinic.com] -->|delegate /api prefix | rt1(Route Table

/api/pets
/api/vets) vs -->|route /pharmacy | us1(Upstream

pharmacy-svc.petstore.cluster.svc.local:80) rt1 -->|route /api/pets | us2(Upstream

pet-svc.petstore.cluster.svc.local:80) rt1 -->|route /api/vets | us3(Upstream

vet-svc.petstore.cluster.svc.local:80) style vs fill:#0DFF00,stroke:#233,stroke-width:4px style us1 fill:#f9f,stroke:#333,stroke-width:4px style us2 fill:#f9f,stroke:#333,stroke-width:4px style us3 fill:#f9f,stroke:#333,stroke-width:4px

Would become:

graph LR; px{Proxy

*.petclinic.com} --> |route /api/pets | us2(Upstream

pet-svc.petstore.cluster.svc.local:80) px -->|route /api/vets | us3(Upstream

vet-svc.petstore.cluster.svc.local:80) px --> |route /pharmacy | us1(Upstream

pharmacy-svc.petstore.cluster.svc.local:80) style px fill:#0DFFDD,stroke:#333,stroke-width:4px style us1 fill:#f9f,stroke:#333,stroke-width:4px style us2 fill:#f9f,stroke:#333,stroke-width:4px style us3 fill:#f9f,stroke:#333,stroke-width:4px

Example Configuration

A complete configuration might look as follows:

apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: 'any'
  namespace: 'any'
spec:
  virtualHost:
    domains:
    - 'any.com'
    routes:
    - matcher:
        prefix: '/a' # delegate ownership of routes for `any.com/a`
      delegateAction:
        name: 'a-routes'
        namespace: 'a'
    - matcher:
        prefix: '/b' # delegate ownership of routes for `any.com/b`
      delegateAction:
        name: 'b-routes'
        namespace: 'b'

The above configuration can be visualized as:

graph LR; vs[Virtual Service

any.com] vs -->|delegate /a prefix | rt1(Route Table

/a/1
/a/2) vs -->|delegate /b prefix | rt2(Route Table

/b/1
/b/2) rt1 -->|route /a/1 | us1(Upstream

foo-upstream) rt1 -->|route /a/2 | us2(Upstream

bar-upstream) rt2 -->|route /b/3 | us3(Upstream

baz-upstream) rt2 -->|route /b/c | rt3(Route Table

/b/c/4) rt3 -->|route /b/c/4 | us4(Upstream

qux-upstream) style vs fill:#0DFF00,stroke:#233,stroke-width:4px style us1 fill:#f9f,stroke:#333,stroke-width:4px style us2 fill:#f9f,stroke:#333,stroke-width:4px style us3 fill:#f9f,stroke:#333,stroke-width:4px style us4 fill:#f9f,stroke:#333,stroke-width:4px

And would result in the following Proxy:

graph LR; px{Proxy

any.com} style px fill:#0DFFDD,stroke:#333,stroke-width:4px px -->|route /a/1 | us1(Upstream

foo-upstream) px -->|route /a/2 | us2(Upstream

bar-upstream) px -->|route /b/3 | us3(Upstream

baz-upstream) px -->|route /b/c/4 | us4(Upstream

qux-upstream) style us1 fill:#f9f,stroke:#333,stroke-width:4px style us2 fill:#f9f,stroke:#333,stroke-width:4px style us3 fill:#f9f,stroke:#333,stroke-width:4px style us4 fill:#f9f,stroke:#333,stroke-width:4px

Learn more

Explore Gloo’s Routing API in the API documentation:

Please submit questions and feedback to the solo.io slack channel, or open an issue on GitHub.