Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 13 additions & 74 deletions docs/03-add-cli-command.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,23 @@
# Generate App Platform API Client Library
# Add a CLI Command

The App Platform is API driven. It adheres 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](https://github.com/oapi-codegen/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.
APL CLI becomes much more useful if we can also use it for post-installation CRUD operations. Fortunately the App Platform is API driven, which enables us to do just that. A client library is already provided and available for importing into CLI commands―no further action is required it start using it. This [doc](api/client-codegen.md) outlines the process used to generate it.

Let's get started by adding this tool to our arsenal. For systems running Go 1.24+, the official 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/).

```bash
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

# alternatively use `go install`
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
```

> [!IMPORTANT]
> These lab instructions currently support APL `v4.12.1` (the default version deployed by APL CLI). Updates are still pending for `v4.14.1`, which does not expose the API JSON schema at `/api/api-docs/`.

Next, if you haven't already, login to your APL console dashboard at `https://console.${DOMAIN}`. Be sure to replace `${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.

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`.

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.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: client
output: client.go
generate:
models: true
client: true
```

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.

```bash
oapi-codegen -config cfg.yaml api.yaml
```

For reference, the `aplcli` directory tree looks like this after a fresh `git clone` and `cd` into it.

```bash
.
├── cmd
│ └── templates
│ ├── apl
│ ├── ci
│ ├── infra
│ ├── init
│ ├── pulumi
│ ├── utils
│ └── values
├── config
├── docs
│ └── solutions
└── media
├── images
└── screencasts
```

Create a new `client` subdirectory under `cmd` and then move or copy the newly 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:

```bash
mkdir cmd/client
cp ../client.go cmd/client/
```

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.

```bash
go get github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider
```

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.
APL CLI was built using the popular `cobra-cli` [generator tool](https://github.com/spf13/cobra/), and `viper` [configuration solution](https://github.com/spf13/viper). Ensure that both of these are installed on your system before proceeding further.

```bash
go install github.com/spf13/cobra-cli@latest
go get -u github.com/spf13/viper
go mod tidy
```

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.
Let's add a `team` command that utilizes this client library to manage teams on an APL instance. From the repository root, run the below `cobra-cli` command to generate `team.go` with some boilerplate code.

```bash
cobra-cli add team
```

You should see it appear under the `cmd` directory.

```bash
tree cmd/ -L 1
cmd/
Expand All @@ -101,13 +37,13 @@ cmd/
└── templates
```

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.
Then to make this lab a little easier, use the kubeconfig file from one of your APL Kubernetes clusters and set some environment variables in your shell. If the name of your cluster/APL instance is `my-platform` for example, set that corresponding kubeconfig file, and then use it with `kubectl` to query `keycloak` for the realm username and password.

```bash
export KUBECONFIG=$HOME/.kube/my-platform-kubeconfig.yaml

# keycloak credentials for obtaining api tokens
export CLIENT_SECRET=$(kubectl get -n apl-keycloak-operator secrets apl-keycloak-operator-secret -ojson | jq -r '.data.KEYCLOAK_CL IENT_SECRET' | base64 -d)
export CLIENT_SECRET=$(kubectl get -n apl-keycloak-operator secrets apl-keycloak-operator-secret -ojson | jq -r '.data.KEYCLOAK_CLIENT_SECRET' | base64 -d)
export USER_NAME=$(kubectl get secret keycloak-initial-admin -n keycloak -o json | jq -r '.data.username' | base64 -d)
export USER_PASSWORD=$(kubectl get secret keycloak-initial-admin -n keycloak -o json | jq -r '.data.password' | base64 -d)
```
Expand All @@ -133,4 +69,7 @@ You can also look at the [example solution](./solutions/03-add-cli-command.md)if

### Bonus

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 instance, and adds, removes, and lists users on it.
In the above lab instructions, you manually set the `$KUBECONFIG`, `$CLIENT_SECRET`, `$USER_NAME`, and `$USER_PASSWORD` environment variables in your shell. Update the `team` command to do this programmatically.

> [!TIP]
> The correct file for `$KUBECONFIG` can be matched by the value provided to the `--name/-n` flag.
70 changes: 70 additions & 0 deletions docs/api/client-codegen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Generating API Client

The App Platform is API driven. It adheres 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](https://github.com/oapi-codegen/oapi-codegen). In addition to generating HTTP models and server-side code, it can also generate clients.

Start by adding `oapi-codegen` to the arsenal. For systems running Go 1.24+, the official project recommends installing with `go get -tool`, but the `go install` method 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 tool explains it pretty well in this [post](https://www.jvt.me/posts/2025/01/27/go-tools-124/).

```bash
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

# alternatively use `go install`
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
```

> [!IMPORTANT]
> These lab instructions currently support APL `v4.12.1` (the default version deployed by APL CLI). Updates are still pending for `v4.14.1`, which does not expose the API JSON schema at `/api/api-docs/`.

Next, if you haven't already, login to your APL console dashboard at `https://console.${DOMAIN}`. Be sure to replace `${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.

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`.

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 simplicity let's just stick with the convention in the example below.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: client
output: client.go
generate:
models: true
client: true
```

Now provide these two `yaml` files you just created to the `oapi-codegen` command, and watch your new `client.go` file come to life. The part we are most interested in starts at around line `4442` with the `Client` struct.

```bash
oapi-codegen -config cfg.yaml api.yaml
```

For reference, the `aplcli` directory tree looks like this after a fresh `git clone` and `cd` into it.

```bash
.
├── cmd
│ └── templates
│ ├── apl
│ ├── ci
│ ├── infra
│ ├── init
│ ├── pulumi
│ ├── utils
│ └── values
├── config
├── docs
│ └── solutions
└── media
├── images
└── screencasts
```

Lastly, move or copy the newly generated `client.go` to `internal/client`, where it can be imported as a private library APL CLI commands. 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:

```bash
mkdir cmd/client
cp ../client.go cmd/client/
```

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.

```bash
go get github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider
```
Loading