Skip to content

Commit

Permalink
[docs] App Manifest Doc & Writing Kinds Doc Update (#532)
Browse files Browse the repository at this point in the history
Update the writing kinds doc to remove the `group` and `apiResource`
fields from the CUE kinds, and explain adding a kind to the app manifest
for proper code generation. Added a new doc on the app manifest itself.
  • Loading branch information
IfSentient authored Dec 10, 2024
1 parent 6cabe27 commit fbb7327
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 23 deletions.
100 changes: 100 additions & 0 deletions docs/app-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# App Manifest

Every app platform app needs a manifest, which describes all the information about your app needed for the platform to run it.
This data is primarily split into three groups:
* Your app's [kinds](./custom-kinds/README.md)
* Your app's capabilities
* The extra permissions your app requires to run

The manifest is a kubernetes object, and a complete one looks something like this:
```yaml
apiVersion: apps.grafana.com/v1
kind: AppManifest
metadata:
name: example
spec:
appName: example
group: example.ext.grafana.com
kinds:
- kind: Foo
scope: Namespaced
versions:
- name: v1
admission:
validation:
operations:
- CREATE
- UPDATE
mutation:
operations:
- CREATE
- UPDATE
schema:
spec:
properties:
firstField:
type: string
required:
- firstField
type: object
conversion: false
extraPermissions:
accessKinds:
- group: playlist.grafana.app
resource: playlists
actions:
- get
- list
- watch
```
This manifest describes an app which is called `example` (each appName must be unique), with the API group `example.ext.grafana.com`.
This app has a kind it manages called `Foo`, which is has the capability to **validate** and **mutate**.
Finally, it requires permissions to get, list, and watch `playlists` from the `playlist.grafana.app` group.

Typically, you do not need to write your app's manifest yourself, it can instead be generated using the `grafana-app-sdk` CLI.

## Generating a Manifest

By default, your manifest is generated alongside code from CUE. In your project, you'll need a `manifest` CUE object.
If you use `grafana-app-sdk project init` to set up your project, this is automatically generated for you in `kinds/manifest.cue`.
A standard `manifest` CUE object looks like:
```cue
package kinds
manifest: {
// appName is the unique name of your app. It is used to reference the app from other config objects,
// and to generate the group used by your app in the app platform API.
appName: "example-app"
// kinds is the list of kinds that your app defines and manages. If your app deals with kinds defined/managed
// by another app, use permissions.accessKinds to allow your app access
kinds: [myKind1, myKind2]
// extraPermissions contains any additional permissions your app may require to function.
// Your app will always have all permissions for each kind it manages (the items defined in 'kinds').
extraPermissions: {
// If your app needs access to additional kinds supplied by other apps, you can list them here
accessKinds: [
// Here is an example for your app accessing the playlist kind for reads and watch
{
group: "playlist.grafana.app"
resource: "playlists"
actions: ["get","list","watch"]
}
]
}
}
```
This manifest has two kinds (the kinds defined by the CUE selectors `myKind1` and `myKind2`, see [Writing Kinds](./custom-kinds/writing-kinds.md)),
and uses the API group `exampleapp.ext.grafana.com`. The group is automatically determined from the app name.
If you are working from an app written in a much older version of the app-sdk, or otherwise need to change the group,
you can add a `groupOverride` field with a fully-qualified group name to keep your current group.
This manifest also requires the same extra permissions for playlists as the YAML example manifest above.

To generate a manifest JSON, simply run:
```shell
grafana-app-sdk generate
```
For yaml, you can use the `--crdencoding` flag:
```shell
grafana-app-sdk generate --crdencoding=yaml
```
A manifest isn't all that useful in most scenarios without at least one kind that your app exposes, so be sure you're familiar with [custom kinds](./custom-kinds/README.md) and [writing custom kinds](./custom-kinds/writing-kinds.md).
41 changes: 18 additions & 23 deletions docs/custom-kinds/writing-kinds.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ Defining a kind can be thought of as being split into two parts: the kind metada
foo: {
// kind is the kind name. It must be capitalized by convention
kind: "Foo"
// group is the group the kind belongs to, typically the name of the app.
// When generating the CRD and resource.Kind, the group will be appended with '.ext.grafana.com'
group: "myapp"
// apiResource tells the codegen that this is an API resource.
// Optionally, one can specify additional options in this object.
// For non-API resource kinds, see later section.
apiResource: {}
// Collection of all versions for the kind, as a map of version string => version details
versions: {}
}
```

> [!NOTE]
> When using `grafana-app-sdk project kind add`, `group` is automatically set to the plugin ID.
> For a kind to be expressed by your app and work with the codegen, it must be a part of your app's [**Manifest**](../app-manifest.md).
> When using `grafana-app-sdk project kind add`, the newly-created kind is automatically added to your manifest.
## Schemas

Expand Down Expand Up @@ -68,8 +64,6 @@ With all that, let's complete our simple kind:
```cue
foo: {
kind: "Foo"
group: "myapp"
apiResource: {}
currentVersion: "v1"
versions: {
"v1": {
Expand All @@ -92,8 +86,6 @@ package kinds
foo: {
kind: "Foo"
group: "myapp"
apiResource: {}
currentVersion: "v1"
versions: {
"v1": {
Expand All @@ -110,7 +102,18 @@ foo: {

## Generating Code

We now have a valid kind! If you save this as a CUE file (`.cue`) in your project (the default directory for parsing kinds is `./kinds`), you can now generate code and a CRD file for your kind. To do so, make sure you have the `grafana-app-sdk` CLI installed (you can download a binary for your distribution on the [releases](https://github.com/grafana/grafana-app-sdk/releases) page, build the binary from the repo with `make build`, or use `go install` with the cloned repo (there is a known issue with `replace` in the `go.mod` that prevents `go install` working from a remote source)). Now you can run
We now have a valid kind! If you save this as a CUE file (`.cue`) in your project (the default directory for parsing kinds is `./kinds`), you can now generate code and a CRD file for your kind.
To do so, make sure you have the `grafana-app-sdk` CLI installed (you can download a binary for your distribution on the [releases](https://github.com/grafana/grafana-app-sdk/releases) page, build the binary from the repo with `make build`, or use `go install` with the cloned repo (there is a known issue with `replace` in the `go.mod` that prevents `go install` working from a remote source)).
Make sure your kind is added to your [**manifest**](../app-manifest.md). If you set up your project with `grafana-app-sdk project init`, you'll already have a `kind/manifest.cue` file, but if you don't, a simple manifest looks like this:
```cue
package kinds // Or the package you're using for your CUE
manifest: {
appName: "my-app"
kinds: [foo] // This points to the kind `foo` we defined in our file
}
```
Now you can run
```shell
grafana-app-sdk generate
```
Expand Down Expand Up @@ -146,7 +149,8 @@ Additional `types.x.gen.ts` files will be generated for each subresource in your

### `definitions`

The `definitions` directory holds a JSON (or YAML, depending on CLI flags) Custom Resource Definition (CRD) file for each of your kinds. These files can be applied to a kubernetes API server to generate CRDs for your kinds, which you can then use the other generated code to interface with. For more about CRDs see [Kubernetes Concepts](../kubernetes.md).
The `definitions` directory holds a JSON (or YAML, depending on CLI flags) Custom Resource Definition (CRD) file for each of your kinds. These files can be applied to a kubernetes API server to generate CRDs for your kinds, which you can then use the other generated code to interface with. For more about CRDs see [Kubernetes Concepts](../kubernetes.md).
This directory also holds a generated JSON (or YAML) **manifest** for your app. This is a file which will be used in the future to register your app with the grafana API server, without needing to work with CRD's and RBAC.

### Toggling Frontend/Backend Codegen

Expand All @@ -161,9 +165,7 @@ And can be overwritten at either the kind level, or the version level (version l
```cue
myKind: {
kind: "MyKind"
group: "mygroup"
current: "v2"
apiResource: {}
codegen: {
frontend: false // Turn off front-end codegen for this kind
}
Expand Down Expand Up @@ -267,8 +269,6 @@ import "time"
foo: {
kind: "Foo"
group: "myapp"
apiResource: {}
currentVersion: "v1"
versions: {
"v1": {
Expand All @@ -295,7 +295,6 @@ The `kind` format allows for configuring the `additionalPrinterColumns` paramete
```cue
myKind: {
kind: "MyKind"
group: "mygroup"
current: "v1"
[...]
versions: {
Expand All @@ -322,10 +321,6 @@ myKind: {

Example complex schemas used for codegen testing can be found in the [cuekind codegen testing directory](../../codegen/cuekind/testing/).

## Non-API Resource Kinds

If you wish to use the kind codegen tooling for non-API Server resources, you can do so, with fewer restrictions on your schema. **Non-API Resource Kinds cannot be used with an API Server or with an operator**, but can sometimes be useful as contract guarantees between front-end and back-end, as the go and TypeScript are generated from the same source. For nearly all purposes where you would write a kind, you would want to use an API Resource Kind as described in previous sections. However, to create a non-API Resource Kind, you simply need to omit the `apiResource` field in your kind. By doing so, the `schema` for each of your versions no longer has the restriciton of needing a `spec` and other subresources. The generated go code will not include anything aside from the kind (no `resource.Object` implementation, `resource.Kind`, or `Codec`).

## Recommended Reading

* [Managing Multiple Kind Versions](./managing-multiple-versions.md)
Expand Down

0 comments on commit fbb7327

Please sign in to comment.