Adding Swagger Support
- Introduction
- Goals
- Prerequisites
- Configuring the Project
- Building the Component
- Deploying the Component
- Finding the Swagger Documentation
- The Missing Pieces
Introduction
Swagger is an important tool that allows users to explore an API (help me). In this guide, we will update Hello World Service so that we can browse its Swagger documentation in the Cisco MSX Portal.
Goals
- browse Hello World Service Swagger documentation
Prerequisites
Configuring the Project
A number of changes and new files are required to add Swagger support to Hello World Service. The screenshot below shows what we are aiming for once the configuration is done.
go.mod
Update the module path in go.mod
from “go-hello-world-service-5” to “go-hello-world-service-6” like we have in previous guides. An MSX library is required to support Swagger, you can add the dependency with this terminal command:
$ go get github.com/CiscoDevNet/go-msx-swagger
internal/config/config.go
Update internal/config/config.go
to include a structure to store the Swagger values. Note that they will be populated from Consul, Vault, and helloworld.yml
, depending on whether your service is running on local infrastructure or in an MSX environment.
.
.
.
// HelloWorld config options.
type Config struct {
Consul Consul
Vault Vault
Cockroach Cockroach
Swagger Swagger
}
// Swagger config options.
type Swagger struct {
Secure bool
SsoURL string
SwaggerJsonPath string
RootPath string
ClientID string
}
.
.
.
internal/swagger/swagger.go
The module internal/swagger/swagger.go
file defines an instance of the Swagger configuration that we will populate from a data file.
package swagger
import (
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/config"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/consul"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/vault"
"github.com/CiscoDevNet/go-msx-swagger"
)
// Override configuration with values from Consul and Vault.
func UpdateConfig(c *config.Config, consul *consul.HelloWorldConsul, vault *vault.HelloWorldVault) error {
c.Swagger.SsoURL, _ = consul.GetString(c.Consul.Prefix + "/defaultapplication/swagger.security.sso.baseUrl", c.Swagger.SsoURL)
c.Swagger.ClientID, _ = consul.GetString(c.Consul.Prefix + "/helloworldservice/public.security.clientId", c.Swagger.ClientID)
return nil
}
func NewSwagger(cfg *config.Config) (*msxswagger.MsxSwagger,error) {
c := msxswagger.NewDefaultMsxSwaggerConfig()
c.SwaggerJsonPath = cfg.Swagger.SwaggerJsonPath
c.DocumentationConfig.RootPath = cfg.Swagger.RootPath
c.DocumentationConfig.Security.Enabled = cfg.Swagger.Secure
c.DocumentationConfig.Security.Sso.BaseUrl = cfg.Swagger.SsoURL
c.DocumentationConfig.Security.Sso.ClientId = cfg.Swagger.ClientID
c.DocumentationConfig.RootPath = "/helloworld"
return msxswagger.NewMsxSwagger(c)
}
main.go
Update the “main()” function in main.go
to set up Swagger, and add the route for Swagger.
package main
import (
openapi "github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/go"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/config"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/consul"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/datastore"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/swagger"
"github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal/vault"
"log"
"net/http"
)
func main() {
.
.
.
// Setup CockroachDB
datastore.UpdateConfig(config, &consul, &vault)
db, err := datastore.NewCockroachDB(config)
if err != nil {
log.Fatal("FATAL: Could not connect to DB: %s", err.Error())
}
err = db.BuildSchema()
if err != nil {
log.Fatal("FATAL: Could not build DB schema: %s", err.Error())
}
// Setup Swagger.
swagger.UpdateConfig(config, &consul, &vault)
swagger, err := swagger.NewSwagger(config)
if err != nil {
log.Fatalf("Could not setup Swagger: %s", err.Error())
}
// Setup Controllers
ItemsApiController := openapi.NewItemsApiController(db)
LanguagesApiController := openapi.NewLanguagesApiController(db)
// Setup Router
router := openapi.NewRouter(ItemsApiController, LanguagesApiController)
// Add route for Swagger.
router.PathPrefix("/helloworld/swagger").HandlerFunc(swagger.SwaggerRoutes)
log.Fatal(http.ListenAndServe(":8080", router))
}
.
.
.
helloworld.yml
Update helloworld.yml
to include the Swagger configuration. You can download the complete file here.
.
.
.
swagger:
secure: true # Required by MSX.
ssourl: "http://localhost:9515/idm" # CONSUL {prefix}/defaultapplication/swagger.security.sso.baseUrl
clientid: # CONSUL {prefix}/helloworldservice/public.security.clientId
swaggerjsonpath: "HelloWorldService-1.json" # Required by MSX.
.
.
.
manifest.yml
For MSX <= 4.2 update manifest.yml
to include configuration for the public security client identifier required by Swagger (help me).
For MSX >= 4.3 the security client will be created for you automatically.
---
Name: "helloworldservice"
Description: "Hello World service with support for multiple languages."
Version: "1.0.0"
Type: Internal
Infrastructure:
Database:
Type: Cockroach
Name: "helloworld"
ConfigFiles:
- Name: "helloworld.yml"
MountTo:
Container: "helloworldservice"
Path: "/helloworld.yml"
ConsulKeys:
- Name: "favourite.color"
Value: "Green"
- Name: "favourite.food"
Value: "Pizza"
- Name: "favourite.dinosaur"
Value: "Moros Intrepidus"
# NOT NEEDED FOR MSX >= 4.3
# - Name: "public.security.clientId"
# Value: "hello-world-service-public-client"
Containers:
- Name: "helloworldservice"
Version: "1.0.0"
Artifact: "helloworldservice-1.0.0.tar.gz"
Port: 8080
ContextPath: "/helloworld"
Tags:
- "3.10.0"
- "4.0.0"
- "4.1.0"
- "4.2.0"
- "4.3.0"
- "5.0.0"
- "managedMicroservice"
- "name=Hello World Service"
- "componentAttributes=serviceName:helloworldservice~serviceName:helloworldservice~context:/helloworld~name:Hello World Service~description:Hello World service with support for multiple languages."
Check:
Http:
Scheme: "http"
Host: "127.0.0.1"
Path: "/helloworld/api/v1/items"
IntervalSec: 60
InitialDelaySec: 30
TimeoutSec: 30
Limits:
Memory: "256Mi"
CPU: "1"
Command:
- "/helloworld"
Dockerfile
Update Dockerfile
to copy HelloWorldService-1.json
into the container.
FROM --platform=linux/amd64 golang:alpine as builder
RUN apk update && apk add ca-certificates upx git
COPY go/ /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/go
COPY go.mod go.sum main.go /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/
COPY internal/ /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/internal
COPY HelloWorldService-1.json /
WORKDIR /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6
RUN go mod vendor \
&& go build -ldflags="-s -w" -o helloworld main.go \
&& upx helloworld
# Create appuser.
ENV USER=helloworld
ENV UID=10001
# See https://stackoverflow.com/a/55757473/12429735RUN
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"
RUN chown helloworld:helloworld /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/helloworld
RUN chown helloworld:helloworld /HelloWorldService-1.json
FROM --platform=linux/amd64 scratch
COPY --from=builder /go/src/github.com/CiscoDevNet/msx-examples/go-hello-world-service-6/helloworld /
COPY --from=builder /HelloWorldService-1.json /
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1
USER helloworld:helloworld
ENTRYPOINT ["/helloworld"]
Building the Component
Like we did in earlier guides, build the component helloworldservice-1.0.0-component.tar.gz
by calling make with component “NAME” and “VERSION” parameters.
$ make NAME=helloworldservice VERSION=1.0.0
.
.
.
Successfully built a4621de07764
Successfully tagged helloworldservice:1.0.0
docker save helloworldservice:1.0.0 | gzip > helloworldservice-1.0.0.tar.gz
tar -czvf helloworldservice-1.0.0-component.tar.gz manifest.yml helloworld.yml helloworldservice-1.0.0.tar.gz
a manifest.yml
a helloworld.yml
a helloworldservice-1.0.0.tar.gz
rm -f helloworldservice-1.0.0.tar.gz
Deploying the Component
Log in to your MSX environment and deploy helloworldservice-1.0.0-component.tar.gz
using MSX UI->Settings->Components (help me). If the helloworldservice is already deployed, delete it before uploading it again.
Finding the Swagger Documentation
There are two ways to find the Swagger documentation for Hello World Service in the Cisco MSX Portal. The first is to browse to this URL once you have made replaced the hostname.
https://dev-plt-aio1.lab.ciscomsx.com/helloworld/swagger/
The second is to use the Cisco MSX Portal to navigate to the Swagger documentation for all services (help me). Whichever path you take once you get there it will look like this.
This ability to try the API is a powerful tool than can help you refine your service before you ship it. If you have not used Swagger before, take this opportunity to explore.
The Missing Pieces
Users can now try out Hello World Service using Swagger, the last pieces of the puzzle are:
- add role based access control
PREVIOUS | NEXT | HOME |