Discovered Upstreams

Let’s configure Gloo to route to a single upstream that was automatically detected by Gloo’s built in discovery system. In this case, we’ll deploy an application to Kubernetes and discovery will create a new Upstream CRD for the service that was created. We’ll then configure a virtual service to route to that upstream.

This guide assumes that you have deployed Gloo to the gloo-system namespace and that the glooctl command line utility is installed on your machine. glooctl provides several convenient functions to view, manipulate, and debug Gloo resources; in particular, it is worth mentioning the following command, which we will use each time we need to retrieve the URL of the Gloo Gateway that is running inside your cluster:

glooctl proxy url

Deploy Petstore

Let’s deploy a simple example application called petstore:


kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo/v1.2.9/example/petstore/petstore.yaml

This should deploy the petstore service to the default namespace. Gloo’s discovery system is watching that namespace and should immediately create an upstream for the petstore service.

Look at discovered Upstream

Discovery automatically creates an upstream in the discovery namespace (usually gloo-system, determined by the discoveryNamespace setting). The name of a discovered upstream is based on the name, namespace, and port of the service it references, in the form: NAMESPACE-NAME-PORT. For instance, in this example, we are deploying the petstore service in the default namespace and it listens on port 8080. So we’d expect the discovered upstream to have the name default-petstore-8080.

Let’s verify the service was discovered and an upstream was written. We can do this using glooctl:

glooctl get upstream -n gloo-system default-petstore-8080
+-----------------------+------------+----------+-------------------------+
|       UPSTREAM        |    TYPE    |  STATUS  |         DETAILS         |
+-----------------------+------------+----------+-------------------------+
| default-petstore-8080 | Kubernetes | Accepted | svc name:      petstore |
|                       |            |          | svc namespace: default  |
|                       |            |          | port:          8080     |
|                       |            |          | REST service:           |
|                       |            |          | functions:              |
|                       |            |          | - addPet                |
|                       |            |          | - deletePet             |
|                       |            |          | - findPetById           |
|                       |            |          | - findPets              |
|                       |            |          |                         |
+-----------------------+------------+----------+-------------------------+

Here, we can see an upstream was created and accepted. The upstream points to the petstore service on port 8080 in the default namespace. In fact, Gloo discovered that it was a REST service and, using it’s function discovery system, added the functions it found in the Swagger definition to the upstream.

The default endpoints evaluated for swagger or OpenAPISepc docs are:

"/swagger.json"
"/swagger/docs/v1"
"/swagger/docs/v2"
"/v1/swagger"
"/v2/swagger"

If you have a Swagger definition in a different location that the default conventions listed above, you can customize the location by configuring it in the serviceSpec.rest.swaggerInfo.url field. See Configuring Function Discovery for more information.

Let’s look at the yaml output for this upstream from Kubernetes:

kubectl get upstream -n gloo-system default-petstore-8080 -oyaml
apiVersion: gloo.solo.io/v1
kind: Upstream
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"service":"petstore"},"name":"petstore","namespace":"default"},"spec":{"ports":[{"port":8080,"protocol":"TCP"}],"selector":{"app":"petstore"}}}
  creationTimestamp: 2019-07-30T20:04:14Z
  generation: 4
  labels:
    discovered_by: kubernetesplugin
    service: petstore
  name: default-petstore-8080
  namespace: gloo-system
  resourceVersion: "360256"
  selfLink: /apis/gloo.solo.io/v1/namespaces/gloo-system/upstreams/default-petstore-8080
  uid: 344a9166-b305-11e9-bbf8-42010a800130
spec:
  discoveryMetadata: {}
  kube:
    selector:
      app: petstore
    serviceName: petstore
    serviceNamespace: default
    servicePort: 8080
    serviceSpec:
      rest:
        swaggerInfo:
          url: http://petstore.default.svc.cluster.local:8080/swagger.json
        transformations:
          addPet:
            body:
              text: '{"id": {{ default(id, "") }},"name": "{{ default(name, "")}}","tag":
                "{{ default(tag, "")}}"}'
            headers:
              :method:
                text: POST
              :path:
                text: /api/pets
              content-type:
                text: application/json
          deletePet:
            headers:
              :method:
                text: DELETE
              :path:
                text: /api/pets/{{ default(id, "") }}
              content-type:
                text: application/json
          findPetById:
            body: {}
            headers:
              :method:
                text: GET
              :path:
                text: /api/pets/{{ default(id, "") }}
              content-length:
                text: "0"
              content-type: {}
              transfer-encoding: {}
          findPets:
            body: {}
            headers:
              :method:
                text: GET
              :path:
                text: /api/pets?tags={{default(tags, "")}}&limit={{default(limit,
                  "")}}
              content-length:
                text: "0"
              content-type: {}
              transfer-encoding: {}
status:
  reported_by: gloo
  state: 1

Note that the discovered upstream also has this label: discovered_by: kubernetesplugin. This is an easy way to determine that the upstream was created by Gloo discovery.

Create a route to this service

Let’s create a virtual service, and add a route that directs requests to a function on the petstore service. Specifically, we’ll add a route for the path /api/pets.


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: test-petstore
  namespace: gloo-system
spec:
  virtualHost:
    domains:
      - 'foo'
    routes:
      - matchers:
         - prefix: /api/pets
        routeAction:
          single:
            upstream:
              name: default-petstore-8080
              namespace: gloo-system

glooctl create vs --name test-petstore --namespace gloo-system --domains foo 
glooctl add route --name test-petstore --path-prefix /api/pets --dest-name default-petstore-8080

Test the route

We can now query this route using curl.

curl -H "Host: foo" $(glooctl proxy url)/api/pets

This should return:

[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]

Summary

We deployed an application to Kubernetes and Gloo automatically discovered upstreams from it, including specific functions off of a swagger definition. We created a virtual service and routed requests to one of those endpoints.

Let’s clean up the virtual service we created:


kubectl delete vs -n gloo-system test-petstore

glooctl delete vs test-petstore