Spring Cloud Gateway includes an OpenAPI route conversion tool to help you generate a RouteConfig for a given OpenAPI spec. This feature is bundled with the Spring Cloud Gateway Operator and is exposed as an API. It can accept both OpenAPI 2.0 and OpenAPI 3.0 specs.

Conversion endpoint

An OpenAPI Conversion service can be found in an SCG operator instance. If you have the service exposed in your kubernetes cluster via port-forward or Ingress, you only need to send a POST request with path /api/convert/openapi to the reachable SCG-operator instance.

The next attributes are supported by the OpenAPI Conversion service

Field Description
service Kubernetes Service to route traffic to spec.routes spec which doesn't contain any service configuration.
  • .namespace: (Optional) If not set will use the RouteConfig's namespace.
  • .name: Name of a service to route to. Takes lower precedence than uri. Either name or uri are required unless all routes define their own uri.
  • .port: (Optional) If not set will use one of the available service ports.
  • .ssoEnabled: (Optional) Define SSO validation for all routes.
  • .tokenRelay: (Optional) Define Token Relay for all routes.
  • .filters: (Optional) Predicates to be prepended to all routes.
  • .uri: A URI to apply to all routes. Takes precedence over name. Either `name` or `uri` are required unless all routes define their own uri. Routes can override this value.
openapi
  • .location: URL of the OpenAPI Spec to use.
routes
  • .filters: Route filters to allow the modification of the incoming HTTP request or outgoing HTTP response in some manner.
  • .predicates: Predicates to match on different attributes of the HTTP request.
  • .ssoEnabled: Activate SSO validation.
  • .tokenRelay: Pass currently-authenticated user's identity token to application service.
  • .order: Route processing order, same as Spring Cloud Gateway.
  • .tags: Classification tags, will be applied to methods in the generated OpenAPI documentation.

For more details about the JSON schema, please, check the section JSON schema to validate requests.

Conversion request

You can generate a RouteConfig for your service calling the operator endpoint. For example, given you have the operator exposed at http://operator.scg and your Kubernetes service my-service. For your OpenAPI specification "https://petstore3.swagger.io/api/v3/openapi.json", you only need to make a call to the endpoint at api/convert/openapi:

curl --request POST 'http://operator.scg/api/convert/openapi' \
--header 'Content-Type: application/json' \
--data-raw '{
    "service": {
        "name": "my-service"
    },
    "openapi": {
        "location": "https://petstore3.swagger.io/api/v3/openapi.json"
    }
}'

This endpoint will return a JSON response, e.g:

{
  "apiVersion": "tanzu.vmware.com/v1",
  "kind": "SpringCloudGatewayRouteConfig",
  "metadata": {
    "name": "my-service"
  },
  "spec": {
    "openapi": {
      "components": {
        "schemas": {...},
        "requestBodies": {...},
        "securitySchemes": {...}
      },
      "ref": "https://petstore3.swagger.io/api/v3/openapi.json"
    },
    "routes": [
      {
        "description": "Update an existing pet by Id",
        "model": {
          "requestBody": {...},
          "responses": {...}
        },
        "predicates": [
          "Path=/pet",
          "Method=PUT"
        ],
        "tags": [
          "pet"
        ],
        "title": "Update an existing pet"
      },
      ...
    ],
    "service": {
      "name": "my-service"
    }
  }
}

Referencing an OpenAPI endpoint in your cluster

For example, the request body below will pull the OpenAPI spec exposed via the openapi service in the development namespace, effectively making a call the uri openapi.development.svc.cluster.local:8080/openapi:

{
    "service": {
        "name": "openapi",
        "namespace": "development",
        "port": "8080"
    },
    "openapi": {
        "location": "/openapi"
    }
}

Providing service-level filters

To provide service level filters, you can specify a filters array inside service of the request body:

{
    "openapi": {
        "location": "https://petstore3.swagger.io/api/v3/openapi.json"
    },
    "service": {
        "name": "my-service",
        "filters": ["StripPrefix=1"]
    }
}

Example result JSON:

{
  "apiVersion": "tanzu.vmware.com/v1",
  "kind": "SpringCloudGatewayRouteConfig",
  "metadata": {
    "name": "my-service"
  },
  "spec": {
    "openapi": {...},
    "routes": [...],
    "service": {
      "filters": [
        "StripPrefix=1"
      ],
      "name": "my-service"
    }
  }
}

Providing route-level filters

Route level filters can be applied with a wildcard or using exact path. First route whose predicate matches exactly with the OpenAPI spec path is taken first. Otherwise, the first most specific matching a wildcard path will be taken.

Example:

  "routes": [
    { # Route 1
      "predicates": ["Method=GET", "Path=/pet/findByStatus"],
      "filters": ["RateLimit=1,10s", "CustomFilter1"]
    },
    { # Route 2
      "predicates": ["Method=GET", "Path=/pet**"],
      "filters": ["RateLimit=2,10s", "CustomFilter2"]
    },
    { # Route 3
      "predicates": ["Method=PUT", "Path=/store**"],
      "filters": ["RateLimit=3,10s", "CustomFilter3"]
    },
    { # Route 4
      "predicates": ["Method=PUT", "Path=/store/order**"],
      "filters": ["RateLimit=4,10s", "CustomFilter4"]
    },
    { # Route 5
      "predicates": ["Method=PUT", "Path=/store/order**"],
      "filters": ["RateLimit=5,10s", "CustomFilter5"]
    }
  ]
  • The path GET /pet/findByStatus will use filters ["RateLimit=1,10s", "CustomFilter1"] and attributes coming from Route 1
  • The path GET /pet will use filters ["RateLimit=2,10s", "CustomFilter2"] and attributes coming from Route 2
  • The path PUT /store will use filters ["RateLimit=3,10s", "CustomFilter3"] and attributes coming from Route 3
  • The path PUT /store/order will use filters ["RateLimit=4,10s", "CustomFilter4"] and attributes coming from Route 4
  • Route 5 is ignored because Route 4 is the first route defining path /store/order**

In addition, you can specify multiple methods to match with.

For example, given a request body of:

{
  "service": {
    "name": "test-service"
  },
  "openapi": {
    "location": "https://petstore3.swagger.io/api/v3/openapi.json"
  },
  "routes": [
    {
      "predicates": ["Method=PUT,DELETE", "Path=/user**"],
      "filters": ["RateLimit=4,15s", "StripPrefix=2"]
    }
  ]
}
  • The paths PUT /user/{username} and DELETE /user/{username} will have the filters RateLimit=4,15s and StripPrefix=2 applied

See available filters here.

Route-level attributes

Apart of predicates and filters, you can activate some features in a route adding the next optional attributes:

  • ssoEnabled (true|false): Activate SSO validation. See "Using Single Sign-On"
  • tokenRelay (true|false): Pass currently-authenticated user's identity token to application service
  • order (number): Route processing order, same as Spring Cloud Gateway
  • tags (array of strings): Classification tags, will override tags to methods in the generated OpenAPI documentation

The next route will activate SSO, pass authentication token to the upstream service, override tags by the tag "status" and the order of the route will be 1000

"routes" : [
  {
  "predicates": [ "Method=GET", "Path=/order/findByStatus" ],
  "filters": [ "RateLimit=2,10s" ],
  "ssoEnabled": true,
  "tokenRelay": true,
  "order": 1000,
  "tags": ["status"]
  }
]

JSON schema to validate requests

You can fetch the JSON schema to validate requests by calling /json/schema on the Spring Cloud Gateway operator.

For example, given you have the Spring Cloud Gateway operator exposed at http://operator.scg, you can run:

curl --request GET 'http://operator.scg/json/schema' --header 'Accept: application/json'

TLS

To retrieve OpenAPI spec hosted with private CA, you need to provide a secret containing the custom CA cert and bind it to the scg-operator deployment. First we need to create a kubernetes secret with the CA certificate. This can be done either

  • with the cert-manager ClusterIssuer
  • with a CA certificate generated outside of the cluster

With the cert-manager ClusterIssuer

Use cert-manager to create a TLS secret from the ClusterIssuer on your cluster.

Create a certificate from your ClusterIssuer

Create a certificate from your ClusterIssuer to generate a secret, e.g. scg-tls-cert.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: scg-tls-cert
  namespace: spring-cloud-gateway
spec:
  secretName: scg-tls-cert
  dnsNames:
    - "your.domain.here"
  issuerRef:
    name: "your-cluster-issuer-here"
    kind: ClusterIssuer

This secret will contain 3 keys: ca.crt, tls.key, and tls.crt. We will need to construct a secret with only the ca.crt. If you are on a TAP cluster, the next section will make use of the SecretTemplate to achieve this.

If not, inspect the secret and extract the CA crt to a file, ca.crt:

kubectl get secret scg-tls-cert -o yaml | yq -r '.data["ca.crt"]' | base64 -d > ca.crt

Then follow the steps on With CA certificate generated outside of the cluster

Create a SecretTemplate for the CA Cert

Using a SecretTemplate from the secrettemplates.secretgen.carvel.dev CRD, we can create a secret scg-ca-cert with only the ca.crt from the original scg-tls-cert secret:

apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretTemplate
metadata:
  name: scg-ca-cert
  namespace: spring-cloud-gateway
spec:
  inputResources:
  - name: scg-tls-cert
    ref:
      apiVersion: v1
      kind: Secret
      name: scg-tls-cert
  template:
    data:
      ca.crt: $(.scg-tls-cert.data.ca\.crt)

Finally, see Create a ServiceBinding to the SCG Operator to bind the secret to the scg-operator deployment.

With a CA certificate generated outside of the cluster

If you have a CA certificate file, e.g. ca.crt, you can create the secret as follows:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: scg-ca-cert
type: Opaque
data:
  ca.crt: $(cat path/to/ca.crt | base64 | tr -d '\n')
EOF

Bind the secret to the SCG Operator with a ServiceBinding

Now that we have a secret with only the ca.crt, we create a ServiceBinding of type ca-certificates and apply it to the scg-operator deployment. This requires the Service Bindings operator, which is installed in TAP, and uses the servicebindings.servicebinding.io CRD.

apiVersion: servicebinding.io/v1beta1
kind: ServiceBinding
metadata:
  name: tls-cert
  namespace: spring-cloud-gateway
spec:
  name: tls-cert
  service:
    apiVersion: v1
    kind: Secret
    name: scg-ca-cert
  workload:
    apiVersion: apps/v1
    kind: Deployment
    name: scg-operator
  type: ca-certificates

The scg-operator pod will restart with a volume mount /bindings/tls-cert containing the CA cert. You can list the files with the following command:

kubectl exec -it deploy/scg-operator -- ls /bindings/tls-cert

which should return something similar to:

ca.crt  type
  • ca.crt being the CA certificate
  • type as ca-certificates to let container know this is a servicebinding for CA certificates
check-circle-line exclamation-circle-line close-line
Scroll to top icon