API Products with Custom Route Tables

Custom Route Tables are a beta feature.

Use a Custom Route Table to supply non-standard routing config for a version of a Portal API Product.

When you use Gloo Portal with Gloo Edge Enterprise, Gloo Portal generates Route Tables deterministically for each API Product version, as described in the Routing overview. However, you cannot natively configure routing behavior via the API Product resource.

In order to configure routing for API Product versions in non-standard ways, create a Custom Route Table that handles all of the routes required by the API Product for each version. For example, you might want to create routing rules based on matching headers for blue-green testing of different versions of your API Product.

Pre-requisites

  1. Install Gloo Edge Enterprise version 1.8.0 or later in a Kubernetes cluster.
  2. Install Gloo Portal version 1.4.0-beta1 or later in the same cluster.
  3. Follow the Create an API Product to set up an Environment with an API Product.
  4. Optional: In order to demo this feature in the Portal UI, also follow the Create a Portal guide to set up a Portal, and the Users and Groups guide to set up a user with access to the Environment.

Create a RouteTable

You will need to define your custom routing configuration in a RouteTable custom resource. See the “Delegating with Route Tables” guide in Gloo Edge's docs for more information about Route Tables and their use.

The Route Table will need to have routes to handle all operations in the API Product Version, so it will be useful to start by copying the generated Route Table.

When you configure an API Product to refer to a CustomRouteTable, Portal validates that the specified CustomRouteTable exists. However, Portal does not validate that the CustomRouteTable's configuration correctly handles routing traffic for all of the operations within the API Product version.
  1. Write the configuration of the generated Route Table to a new file.

    kubectl get routetables.gateway.solo.io dev.petstore-product.v1 -oyaml > custom-route-table.yaml
    
  2. Open the file, remove all metadata other than name and namespace, and give the Route Table a different name.

  3. To demonstrate the ability to add custom routing, add a route that includes query param matching. We'll add a route that adds the Content-Language: English header response header when there is a request that includes english as a value for the tags query param. We will leave all existing routes to ensure that all operations are still handled.

    The file should look like the following:

    apiVersion: gateway.solo.io/v1
    kind: RouteTable
    metadata:
      name: custom-route-table
      namespace: default
    spec:
      routes:
      - name: findPetsWithParamMatch
        matchers:
        - exact: /api/pets
          methods:
          - GET
          - OPTIONS
          queryParameters:
          - name: tags
            value: .*[Ee]nglish.*
            regex: true
        routeAction:
          multi:
            destinations:
            - destination:
                upstream:
                  name: default-petstore-8080
                  namespace: gloo-portal
              weight: 1
              options:
                headerManipulation:
                  responseHeadersToAdd:
                  - header:
                      key: Content-Language
                      value: English
        - exact: /api/pets
          methods:
          - GET
          - OPTIONS
        name: petstore-product.default.petstore-api-spec.default.findPets
    ...

    The RouteTable generated by Portal contains a transformation for each Route which adds API Product and Environment names to dynamic metadata. If you are relying on these fields to be in dynamic metadata, ensure that such a transformation is included on relevant routes in your custom RouteTable.
    Using DirectResponseActions in CustomRouteTables is not recommended. The VirtualServices generated by Portal for Environments contain CORS settings, to support Portal UI functionality. However Gloo Edge does not support DirectResponseActions with CORS. If a DirectResponseAction is used, the API will function as expected, but the Portal UI might not function correctly.
  4. Create the custom Route Table in your cluster.

    kubectl apply -f custom-route-table.yaml
    

Add the Route Table to the API Product

  1. In the v1 version of the petstore-product API Product, add the Route Table in a customRouteTable section.

     cat << EOF | kubectl apply -f -
     apiVersion: portal.gloo.solo.io/v1beta1
     kind: APIProduct
     metadata:
       name: petstore-product
       namespace: default
       labels:
         app: petstore
     spec:
       displayInfo:
         description: Petstore Product
         title: Petstore Product
       versions:
         - name: v1
           apis:
             - apiDoc:
                 name: petstore-schema
                 namespace: default
           customRouteTable:
             name: custom-route-table
             namespace: default
           gatewayConfig:
             route:
               inlineRoute:
                 backends:
                   - upstream:
                       name: default-petstore-8080
                       namespace: gloo-system
           tags:
             stable: {}
    EOF

  2. Verify that the API Product's Status is Succeeded and contains the customRouteTable reference:

    kubectl get -n default apiproducts petstore-product -oyaml
    

    Example output:

    apiVersion: portal.gloo.solo.io/v1beta1
    kind: APIProduct
    metadata:
    ...
      name: petstore-product
      namespace: default
    ...
    spec:
    ...
    status:
    ...
      state: Succeeded
      versionInfo:
        test-version:
          apiType: OPEN_API
          customRouteTable:
            name: custom-route-table
            namespace: default

Try it out

  1. We can call the /api/pets endpoint as we did in the Getting Started guide, but this time in verbose mode. We should get the following response:

    curl "http://${INGRESS_HOST}:${INGRESS_PORT}/api/pets" -v -H "Host: api.example.com"
    

    Output:

    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to 127.0.0.1 (127.0.0.1) port 32000 (#0)
    > GET /api/pets HTTP/1.1
    > Host: api.example.com
    > User-Agent: curl/7.64.1
    > Accept: */*
    >
    < HTTP/1.1 200 OK
    < content-type: text/html
    < date: Wed, 10 May 2023 20:31:41 GMT
    < content-length: 86
    < x-envoy-upstream-service-time: 1
    < server: envoy
    <
    [{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]
    * Connection #0 to host 127.0.0.1 left intact
    * Closing connection 0
    
  2. Now we can call the /api/pets endpoint, using query parameter matching on the route we added to the Route Table. We should get the Content-Language header on the response:

    curl "http://${INGRESS_HOST}:${INGRESS_PORT}/api/pets?tags=english" -v -H "Host: api.example.com"
    

    Output:

    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to 127.0.0.1 (127.0.0.1) port 32000 (#0)
    > GET /api/pets HTTP/1.1
    > Host: api.example.com
    > User-Agent: curl/7.64.1
    > Accept: */*
    > x-custom-header: foo
    >
    < HTTP/1.1 200 OK
    < content-type: text/html
    < date: Wed, 10 May 2023 20:31:51 GMT
    < content-length: 86
    < x-envoy-upstream-service-time: 0
    < content-language: English
    < server: envoy
    <
    [{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]
    * Connection #0 to host 127.0.0.1 left intact
    * Closing connection 0

Try it out in Portal UI

Before you begin, follow the Create a Portal guide to set up a Portal, and the Users and Groups guide to set up a user with access to the Environment. Both guides are linked in the Pre-requisites section of this guide.
  1. Navigate to the Petstore Product page and authenticate using an API Key. If you have just completed the Users and Groups guide you should already be there.

  2. Expand the GET /api/pets command, click Try it out, and then click Execute to see the response we'd expect from the generated Route Table:

    Standard try it out result

  3. Click Add string item for the tags query param, and type “english”. Then click Execute to see the response, including the Content-Language response header, as specified in our CustomRouteTable:

Standard try it out result