Skip to content

Commit 0cd173c

Browse files
authored
Merge pull request #18 from ruckus-voxi/docs/add-client-library
docs: combined labs 3 and 4, along with example solution
2 parents bfb36a1 + 58f505b commit 0cd173c

7 files changed

Lines changed: 461 additions & 19 deletions

File tree

docs/03-add-cli-command.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Generate App Platform API Client Library
2+
3+
The App Platform is API driven. It adhears to the [OpenAPI 3.0 specification](https://swagger.io/specification/v3/) and exposes an endpoint for users to access the Swagger web UI for documentation and testing―you've probably seen [these](https://petstore.swagger.io/?_gl=1*87igsc*_gcl_au*MTQzMDc3NTkwNS4xNzcxMDMxMDQ1) before. This standardization enables the use of tooling which can consume the JSON schema and auto-generate boilerplate code for us. In the Golang arena, One such tool is the remarkable [oapi-codegen]. We can use it to generate HTTP models, server-side code, as well as clients. The latter is what we'll focus on for this exercise.
4+
5+
Let's get started by adding this tool to our arsenal. For systems running Go 1.24+, the offical project recommends installing it `go get -tool`, but the `go install` works as well. We won't dig into the reasons for picking one or the other, but if you're interested, a co-maintainer of the `oapi-codegen` project outlines it pretty well this [post](https://www.jvt.me/posts/2025/01/27/go-tools-124/).
6+
7+
```bash
8+
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
9+
10+
# alternatively use `go install`
11+
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
12+
```
13+
14+
Next, if you haven't already, login to your APL console dashboard at `https://console.${DOMAIN}`. Be sure to repalce `${DOMAIN}` with the actual domain/subdomain of your APL installation. Once logged in you can visit the Swagger UI at `https://console.${DOMAIN}/api/api-docs/swagger/`. Look around for bit and get familiar, you'll notice there are both `/v1` and `/v2` endpoints. The differences are not immediately clear, but for a short example, you'll find API definitions for _team-specific_ configuration from underneath of `/v1/teams/{teamid}` (images, workloads, etc) or _platform specific_ settings from `/v2` or `/v1`. With some rare exceptions (when the API is not finalized or there are new apps under testing), the entire values structure is represented in these API definitions.
15+
16+
Remove `/swagger/` from the URL path and reload the page, so that you are visiting `https://console.${DOMAIN}/api/api-docs/`. You should see the massive blob of JSON representing every possible API endpoint. Highlight all of it and copy/paste to a file called `api.yaml`.
17+
18+
Next you'll create an `oapi-codegen` config file called `cfg.yaml`. The values for `package` and `output` can be customized if you'd like, but for the purpose of following along to setup for this exercise, you may find it easier to just copy the example below.
19+
20+
```yaml
21+
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
22+
package: client
23+
output: client.go
24+
generate:
25+
models: true
26+
client: true
27+
```
28+
29+
Now the start of the fun part―run the below command and watch your new client library appear in a file called `client.go`. The part we are most interested in starts at around line `4442` with the `Client` struct.
30+
31+
```bash
32+
oapi-codegen -config cfg.yaml api.yaml
33+
```
34+
35+
For reference, the `aplcli` directory tree looks like this after a fresh `git clone` and `cd` into it.
36+
37+
```bash
38+
.
39+
├── cmd
40+
│ └── templates
41+
│ ├── apl
42+
│ ├── ci
43+
│ ├── infra
44+
│ ├── init
45+
│ ├── pulumi
46+
│ ├── utils
47+
│ └── values
48+
├── config
49+
├── docs
50+
│ └── solutions
51+
└── media
52+
├── images
53+
└── screencasts
54+
```
55+
56+
Create a new `client` subdirectory under `cmd` and then move or copy the newley generated `client.go` file to it. Assuming you currently in the repo root, and you ran the previous `oapi-codgen` command from one directory outside of it, the two commands you need to run are:
57+
58+
```bash
59+
mkdir cmd/client
60+
cp ../client.go cmd/client/
61+
```
62+
63+
Now before we start importing this module into our CLI code, we need to quickly install a few more dependencies, the first being another package by the `oapi-codegen` project called `securityprovider`. This provides a `RequestEditorFn` type our client uses to intercept and mutate HTTP requests―we use it for [Bearer Authentication](https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/) in this case. See the [Authenticated API Example](https://github.com/oapi-codegen/oapi-codegen/blob/main/examples/authenticated-api/README.md) for additional info.
64+
65+
```bash
66+
go get github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider
67+
```
68+
69+
Then be sure you have the `cobra-cli` [generator tool](https://github.com/spf13/cobra/) installed on your system, and the `viper` [configuration solution](https://github.com/spf13/viper) installed as a module.
70+
71+
```bash
72+
go install github.com/spf13/cobra-cli@latest
73+
go get -u github.com/spf13/viper
74+
go mod tidy
75+
```
76+
77+
We are finally at the fun part...let's write some code! Let's use `cobra-cli` to get us started with adding a `team` command to our own CLI, which enables adding, removing, and listing of developer teams of an App Platform instance. From the root of repository, run the following to generate `team.go`. You should see it appear under the `cmd` directory.
78+
79+
```bash
80+
cobra-cli add team
81+
```
82+
```bash
83+
tree cmd/ -L 1
84+
cmd/
85+
├── automation.go
86+
├── cleanup.go
87+
├── create.go
88+
├── deploy.go
89+
├── destroy.go
90+
├── esc.go
91+
├── gen.go
92+
├── help.go
93+
├── logger.go
94+
├── rclone.go
95+
├── root.go
96+
├── setup.go
97+
├── team.go <--
98+
└── templates
99+
```
100+
101+
Then to make this lab a little easier, use the kubeconfig file from one of your APL Kubernetes clusters and set some environment variables. If the name of your cluster/APL instance is for example, `my-platform`, set that corresponding kubeconfig file, and then use it with `kubectl` to query `keycloak` for the realm username and password.
102+
103+
```bash
104+
export KUBECONFIG=$HOME/.kube/my-platform-kubeconfig.yaml
105+
export USER_NAME=$(kubectl get secret keycloak-initial-admin -n keycloak -o json | jq -r '.data.username' | base64 -d)
106+
export USER_PASSWORD=$(kubectl get secret keycloak-initial-admin -n keycloak -o json | jq -r '.data.password' | base64 -d)
107+
```
108+
____
109+
## Lab Challenge
110+
111+
Begin writing the `team` command logic in this `team.go` source file. Also open up the `client.go` file we generated earlier, to see what you need to import. Ensure that it can minimally satisfy the following requirements, for any existing APL instance named from your `aplcli` configuration file:
112+
113+
- Add new teams
114+
- Remove existing teams
115+
- List existing teams
116+
117+
> [!TIP]
118+
> Use the same format of a `--name/-n` flag that ohter commands use, in order to specify the APL instance to act on. For that matter, you'll likely save a ton of time by reviewing how those other commands work.
119+
120+
You can also look at the [example solution](./solutions/03-add-cli-command.md)if you're feeling stuck somewhere, but try your best to finish the lab without taking a peek. Also consider that the example solution is not perfect, nor is it the only way to factor your code for achieving this task. Good luck!
121+
122+
> [!TIP]
123+
> By default the API token generated via the `keycloak` client secret has a short expiry. Your code needs to account for this. To get an idea for the process involved in getting an access token, run these commands separately in your terminal.
124+
> ```bash
125+
> export TOKEN=$(curl -s -X POST "https://keycloak.ams.arch-linux.io/realms/otomi/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=otomi" -d "client_secret=${CLIENT_SECRET}" -d "grant_type=password" -d "username=${USER_NAME}" -d "password=${USER_PASSWORD}" | jq -r .access_token)
126+
> ```
127+
128+
### Bonus
129+
130+
If you nailed the challenge of adding the `team` command, and thirsty for more, keep going! Add another command called something like `user` that just like the `team` command, imports generated code from `client.go`. Also ensure it can target a specific APL istance, and adds, removes, and lists users on it.

docs/03-api-client-library.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/04-add-cobra-commands.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/labs.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ APL CLI is cool and all, but what's even cooler, is your ability to customize an
66

77
1. [Build your template library](01-build-tempates.md)
88
2. [Automatically obtain default login creds](02-initial-login-creds.md)
9-
3. [Create an API client library](03-api-client-library.md)
10-
4. [Add new commands](04-add-cobra-commands.md)
9+
3. [Add new CLI command](03-add-cli-command.md)

0 commit comments

Comments
 (0)