Files
grafana/apps/example
owensmallwood a3daf0e39d Unified storage: Add quotas app to apiserver (#114425)
* initial generation

* went through doc to add new resource

* added dummy kind so grafana will run

* added dummy handler and custom route

* fix app name

* gets custom route working - still a dummy route

* adds groupOverride to manifest

* adds quotas to grpc client and server

* WIP - trying to get api recognized - not working

* Gets route working

* fixes group and resource vars

* expects group and resource as separate params

* set content-type header on response

* removes Quotas kind and regens

* Update grafana-app-sdk to v0.48.5

* Update codegen

* updates manifest

* formatting

* updates grafana-app-sdk version to 0.48.5

* regen ResourceClient mocks

* adds tests

* remove commented code

* uncomment go mod tidy

* fix tests and make update workspace

* adds quotas app to codeowners

* formatting

* make gen-apps

* deletes temp file

* fix generated folder code

* make gofmt

* make gen-go

* make update-workspace

* add COPY apps/quotas to Dockerfile

* fix test mock

* fixes undefined NewFolderStatus()

* make gen-apps, and add func for NewFolderStatus

* make gen-apps again

* make update-workspace

* regen folder_object_gen.go

* gofmt

* fix linting

* apps/folder make update-workspace

* make gen-apps

* make gen-apps

* fixes enterprise_imports.go

* go get testcontainers

* adds feature toggle

* make update-workspace

* fix go mod

* fix another client mock

---------

Co-authored-by: Steve Simpson <steve@grafana.com>
2025-12-09 09:40:34 -06:00
..

Example App

This App is an example of general app capabilities when developing on the grafana app platform.

Enabling the App

By default, the example app is disabled. To enable this App, add the following to your conf/custom.ini:

[grafana-apiserver]
runtime_config = example.grafana.app/v0alpha1=true,example.grafana.app/v1alpha1=true

Manifest

The source of the app's schemas and list of capabilities is the manifest, which is generated from kinds/manifest.cue. The Example kind is defined for v0alpha1 here and v1alpha1 (default) here. The root definition of the Example kind that both versions share is defined here.

The CUE is used to generate code (and the AppManifest) when make generate is run.

Code

All of the app's code is located in pkg/app. The New() function in pkg/app/app.go is the entry point of the app, and everything should be discoverable from there.

The code to register the app with the grafana API server (including inserting the app-specific config ExampleConfig) is located in /pkg/registry/apps/example/register.go.

Any app must also have its installer listed in WireSet and added to installers in ProvideAppInstallers. When building a new app, make to to regerate wire (make build in the root of the repo does this).

Generated Code

The pkg/apis package, and all its subdirectories, contain code generated by make generate. This code should not be edited, but it can be useful to look at when working through the flow of the app.

Sample Swagger Payloads

Navigate to localhost:3000/swagger?api=example.grafana.app-v1alpha1 to view the swagger for the app's v1alpha1 version (this version has the most capabilities/endpoints). You can use the Execute button to make requests via the swagger UI.

Create a new Example resource with via swagger with:

{
  "apiVersion": "example.grafana.app/v1alpha1",
  "kind": "Example",
  "metadata": {
    "name": "test",
    "namespace": "default"
  },
  "spec": {
    "firstField": "test",
    "secondField": 0,
    "list": {
      "info": "foo",
      "next": {
        "info": "bar"
      }
    }
  }
}

Create an invalid object which will be rejected by validation:

{
  "apiVersion": "example.grafana.app/v1alpha1",
  "kind": "Example",
  "metadata": {
    "name": "invalid",
    "namespace": "default"
  },
  "spec": {
    "firstField": "test",
    "secondField": 0,
    "list": {
      "info": "foo",
      "next": {
        "info": "bar"
      }
    }
  }
}

Update custom subresource:

{
  "apiVersion": "example.grafana.app/v1alpha1",
  "kind": "Example",
  "metadata": {
    "namespace": "default",
    "name": "test",
    "resourceVersion": "<REPLACEME>"
  },
  "custom": {
    "myField": "foo",
    "otherField": "bar"
  }
}

(metadata.resourceVersion is required for an update, use the value you get from a GET request)

cURL

You can also interact with the grafana API server via a kubeconfig set up for it, or via curl using the -u <username>:<password> flag. Currently, cluster-scoped custom routes are erased from the swagger as part of grafana's APIServer code, but can still be called via curl, like so:

curl -u admin:admin http://localhost:3000/apis/example.grafana.app/v1alpha1/other
% curl -u admin:admin http://localhost:3000/apis/example.grafana.app/v1alpha1/other
{"message":"This is a cluster route"}