Publishing an API from the OAS definition
Tetrate Service Express (TSE) can function as an API gateway, exposing APIs defined using OpenAPI (OAS) definitions
Consider the following scenario:
"I have an API that I wish to expose. The API is described using an OAS definition, and is served from a workload in my K8s cluster. I wish to publish the API, and add runtime capabilities, such as Observability, High-Availability, Authentication, Rate Limiting and CORS control."
In this exercise, we will see how to do exactly that. We will publish an API using its OpenAPI Spec (OAS) definition. We will see how to decorate the OAS definition to add additional features to control how the API is exposed and consumed. This exercise builds on the knowledge gained in the previous Publish a Service exercise.
Prerequisites
- You have completed the Publish a Service exercise
- You have performed the cleanup steps to remove the Ingress Gateway and related services
Publish an API from its OAS Definition
Deploy the Ingress Gateway
Deploy an Ingress Gateway in the bookinfo namespace
Create a Gateway Group
The Gateway Group is used to collect the gateway-related configuration for workloads in the Bookinfo workspace
Define an Application and API
Define an Application, and expose an API from a service within that Application. Test the resulting configuration
Deploy the Ingress Gateway
Exactly as we did in the Publish-a-Service exercise, we begin by provisioning an Ingress Gateway in the bookinfo namespace. The Ingress Gateway deploys an Envoy proxy and creates a service (bookinfo-ingress-gw
) that makes this proxy accessible from outside the Kubernetes cluster:
cat <<EOF > ingress-gw-install.yaml
apiVersion: install.tetrate.io/v1alpha1
kind: IngressGateway
metadata:
name: bookinfo-ingress-gw
namespace: bookinfo
spec:
kubeSpec:
service:
type: LoadBalancer
EOF
kubectl apply -f ingress-gw-install.yaml
Create a Gateway Group
Create a TSE Gateway Group within the bookinfo-ws workspace. A Gateway Group is used to collect the gateway-related configuration for workloads in the workspace:
cat <<EOF > bookinfo-gwgroup.yaml
apiVersion: gateway.tsb.tetrate.io/v2
kind: Group
metadata:
displayName: bookinfo
name: bookinfo-gwgroup
organization: tse
tenant: tse
workspace: bookinfo-ws
spec:
displayName: bookinfo-gwgroup
namespaceSelector:
names:
- "*/bookinfo"
EOF
tctl apply -f bookinfo-gwgroup.yaml
Define an Application and API
Previously when exposing a service, we created an Gateway resource that matched the FQDN, port and other parameters (e.g. HTTP path) in a request. Matching requests were then routed (proxied) to the selected Kubernetes service by the Ingress Gateway.
Now, when exposing an API, we want to identify requests based on the API specification. Matching requests should then be routed to the Kubernetes service that implements this API.
- We first define an Application, a TSE resource that identifies a collection of services that should be exposed through an Ingress Gateway
- We then attach one or more API resources with that Application. Each API resource wraps an OAS definition, decorated with the Tetrate-specific annotations if needed.
TSE then generates the appropriate Gateway configuration to expose these APIs through the Ingress Gateway.
Define the Application
cat <<EOF > bookinfo-app.yaml
apiversion: application.tsb.tetrate.io/v2
kind: Application
metadata:
organization: tse
tenant: tse
name: bookinfo-app
spec:
displayName: Bookinfo-App
description: Bookinfo application
workspace: organizations/tse/tenants/tse/workspaces/bookinfo-ws
gatewayGroup: organizations/tse/tenants/tse/workspaces/bookinfo-ws/gatewaygroups/bookinfo-gwgroup
EOF
tctl apply -f bookinfo-app.yaml
You can monitor the state of the Application resource using tctl
or the TSE UI:
tctl x status application bookinfo-app --org tse --tenant tse -o yaml
Define the API
The TSE API resource contains the following information:
- The Application resource (
application
) that contains the API - The Ingress Gateway (
workLoadSelector
) that exposes the API - The OAS definition, as a scalar value in the
spec > openapi
field - The Kubernetes service (
x-tsb-service
) that implements the API
Note that the x-tsb-service
parameter is added to the OAS definition.
You may also want to edit the servers
parameter in the OAS definition to match the FQDN you want to use to publish the API.
cat <<'EOF' > bookinfo-api.yaml
apiversion: application.tsb.tetrate.io/v2
kind: API
metadata:
organization: tse
tenant: tse
application: bookinfo-app
name: bookinfo-api
spec:
workloadSelector:
namespace: bookinfo
labels:
app: bookinfo-ingress-gw
openapi: |
openapi: 3.0.0
info:
description: This is the API of the Istio BookInfo sample application.
version: 1.0.0
title: BookInfo API
termsOfService: https://istio.io/
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
x-tsb-service: productpage.bookinfo
servers:
- url: http://api.tse.tetratelabs.io/api/v1
tags:
- name: product
description: Information about a product (in this case a book)
- name: review
description: Review information for a product
- name: rating
description: Rating information for a product
externalDocs:
description: Learn more about the Istio BookInfo application
url: https://istio.io/docs/samples/bookinfo.html
paths:
/products:
get:
tags:
- product
summary: List all products
description: List all products available in the application with a minimum amount of
information.
operationId: getProducts
responses:
"200":
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Product"
"/products/{id}":
get:
tags:
- product
summary: Get individual product
description: Get detailed information about an individual product with the given id.
operationId: getProduct
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int32
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ProductDetails"
"400":
description: Invalid product id
"/products/{id}/reviews":
get:
tags:
- review
summary: Get reviews for a product
description: Get reviews for a product, including review text and possibly ratings
information.
operationId: getProductReviews
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int32
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ProductReviews"
"400":
description: Invalid product id
"/products/{id}/ratings":
get:
tags:
- rating
summary: Get ratings for a product
description: Get ratings for a product, including stars and their color.
operationId: getProductRatings
parameters:
- name: id
in: path
description: Product id
required: true
schema:
type: integer
format: int32
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ProductRatings"
"400":
description: Invalid product id
components:
schemas:
Product:
type: object
description: Basic information about a product
properties:
id:
type: integer
format: int32
description: Product id
title:
type: string
description: Title of the book
descriptionHtml:
type: string
description: Description of the book - may contain HTML tags
required:
- id
- title
- descriptionHtml
ProductDetails:
type: object
description: Detailed information about a product
properties:
id:
type: integer
format: int32
description: Product id
publisher:
type: string
description: Publisher of the book
language:
type: string
description: Language of the book
author:
type: string
description: Author of the book
ISBN-10:
type: string
description: ISBN-10 of the book
ISBN-13:
type: string
description: ISBN-13 of the book
year:
type: integer
format: int32
description: Year the book was first published in
type:
type: string
enum:
- paperback
- hardcover
description: Type of the book
pages:
type: integer
format: int32
description: Number of pages of the book
required:
- id
- publisher
- language
- author
- ISBN-10
- ISBN-13
- year
- type
- pages
ProductReviews:
type: object
description: Object containing reviews for a product
properties:
id:
type: integer
format: int32
description: Product id
reviews:
type: array
description: List of reviews
items:
$ref: "#/components/schemas/Review"
required:
- id
- reviews
Review:
type: object
description: Review of a product
properties:
reviewer:
type: string
description: Name of the reviewer
text:
type: string
description: Review text
rating:
$ref: "#/components/schemas/Rating"
required:
- reviewer
- text
Rating:
type: object
description: Rating of a product
properties:
stars:
type: integer
format: int32
minimum: 1
maximum: 5
description: Number of stars
color:
type: string
enum:
- red
- black
description: Color in which stars should be displayed
required:
- stars
- color
ProductRatings:
type: object
description: Object containing ratings of a product
properties:
id:
type: integer
format: int32
description: Product id
ratings:
type: object
description: A hashmap where keys are reviewer names, values are number of stars
additionalProperties:
type: string
required:
- id
- ratings
EOF
tctl apply -f bookinfo-api.yaml
Once again, you can monitor the state of the Application resource using tctl
or the TSE UI:
tctl x status api bookinfo-api --application bookinfo-app --tenant tse -o yaml
Test the API
If you have enabled TSE's DNS Integration, then TSE will provision the DNS entry for you, and you can access the API directly:
curl -v http://api.tse.tetratelabs.io/api/v1/products
If DNS is not available, you can access the API as follows:
export GATEWAY_IP=$(kubectl -n bookinfo get service bookinfo-ingress-gw -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
curl -v http://api.tse.tetratelabs.io/api/v1/products --connect-to "api.tse.tetratelabs.io:80:$GATEWAY_IP"
In each case, once the AWS load balancers are fully provisioned and the DNS entry is ready (if applicable), you can expect to see output resembling:
[{"id": 0, "title": "The Comedy of Errors", "descriptionHtml": "<a href=\"https://en.wikipedia.or
g/wiki/The_Comedy_of_Errors\">Wikipedia Summary</a>: The Comedy of Errors is one of <b>William Sh
akespeare's</b> early plays. It is his shortest and one of his most farcical comedies, with a maj
or part of the humour coming from slapstick and mistaken identity, in addition to puns and word p
lay."}]
Fine-tune how the API is published and accessed
You can further extend the OAS definition to define rate-limiting, authentication and CORS policy.
Tetrate has a set of internal extensions to the OAS specification (beginning x-tsb-
). These can be added to the OAS spec to declare additional behaviors, such as CORS control, Authentication and Rate Limiting. The available extensions are:
x-tsb-service
. Short name of the service in the TSB service registry where the path is exposed.x-tsb-redirect
. Configures a redirection for the given path.x-tsb-tls
. Configures the TLS settings for a given server.x-tsb-cors
. Configures CORS policy settings for the given server.x-tsb-authentication
. Configures JWT Authentication rules for the given server.x-tsb-jwt-authorization
. Configures Authorization based on JWT tokens.x-tsb-external-authorization
. Configures an external authorization server to handle all authorization requests for the configured server.x-tsb-ratelimiting
. Configures settings for rate limiting requests.x-tsb-external-ratelimiting
. Configures settings for rate limiting requests using an external ratelimit server.
Please refer to the API resource documentation for more information.
Cleaning Up
You can remove the Ingress Gateway and related services as follows:
tctl delete api --org tse --tenant tse --application bookinfo-app bookinfo-api
tctl delete app --org tse --tenant tse bookinfo-app
tctl delete gg --org tse --tenant tse --workspace bookinfo-ws bookinfo-gwgroup
kubectl delete ingressgateway -n bookinfo bookinfo-ingress-gw