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>
This commit is contained in:
owensmallwood
2025-12-09 09:40:34 -06:00
committed by GitHub
co-authored by Steve Simpson
parent 297e886e1b
commit a3daf0e39d
47 changed files with 1758 additions and 149 deletions
+7
View File
@@ -3,6 +3,8 @@ package appregistry
import (
"context"
"github.com/grafana/grafana/pkg/registry/apps/quotas"
"github.com/open-feature/go-sdk/openfeature"
"k8s.io/client-go/rest"
"github.com/grafana/grafana-app-sdk/app"
@@ -44,12 +46,17 @@ func ProvideAppInstallers(
exampleAppInstaller *example.ExampleAppInstaller,
advisorAppInstaller *advisor.AdvisorAppInstaller,
alertingHistorianAppInstaller *historian.AlertingHistorianAppInstaller,
quotasAppInstaller *quotas.QuotasAppInstaller,
) []appsdkapiserver.AppInstaller {
featureClient := openfeature.NewDefaultClient()
installers := []appsdkapiserver.AppInstaller{
playlistAppInstaller,
pluginsApplInstaller,
exampleAppInstaller,
}
if featureClient.Boolean(context.Background(), featuremgmt.FlagKubernetesUnifiedStorageQuotas, false, openfeature.TransactionContext(context.Background())) {
installers = append(installers, quotasAppInstaller)
}
//nolint:staticcheck // not yet migrated to OpenFeature
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesShortURLs) {
installers = append(installers, shorturlAppInstaller)
+3 -1
View File
@@ -3,6 +3,7 @@ package appregistry
import (
"testing"
"github.com/grafana/grafana/pkg/registry/apps/quotas"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/registry/apps/advisor"
@@ -27,6 +28,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
exampleAppInstaller := &example.ExampleAppInstaller{}
advisorAppInstaller := &advisor.AdvisorAppInstaller{}
historianAppInstaller := &historian.AlertingHistorianAppInstaller{}
quotasAppInstaller := &quotas.QuotasAppInstaller{}
tests := []struct {
name string
@@ -43,7 +45,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
features := featuremgmt.WithFeatures(tt.flags...)
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, historianAppInstaller)
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, annotationAppInstaller, exampleAppInstaller, advisorAppInstaller, historianAppInstaller, quotasAppInstaller)
if tt.expectRulesApp {
require.Contains(t, got, tt.rulesInst)
} else {
+50
View File
@@ -0,0 +1,50 @@
package quotas
import (
"github.com/grafana/grafana/apps/quotas/pkg/apis"
"github.com/grafana/grafana/pkg/storage/unified/resource"
restclient "k8s.io/client-go/rest"
"github.com/grafana/grafana-app-sdk/app"
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
"github.com/grafana/grafana-app-sdk/simple"
quotasapp "github.com/grafana/grafana/apps/quotas/pkg/app"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
)
var (
_ appsdkapiserver.AppInstaller = (*QuotasAppInstaller)(nil)
)
type QuotasAppInstaller struct {
appsdkapiserver.AppInstaller
cfg *setting.Cfg
}
func RegisterAppInstaller(
cfg *setting.Cfg,
features featuremgmt.FeatureToggles,
resourceClient resource.ResourceClient,
) (*QuotasAppInstaller, error) {
installer := &QuotasAppInstaller{
cfg: cfg,
}
specificConfig := &quotasapp.QuotasAppConfig{
ResourceClient: resourceClient,
}
provider := simple.NewAppProvider(apis.LocalManifest(), specificConfig, quotasapp.New)
appConfig := app.Config{
KubeConfig: restclient.Config{}, // this will be overridden by the installer's InitializeApp method
ManifestData: *apis.LocalManifest().ManifestData,
SpecificConfig: specificConfig,
}
i, err := appsdkapiserver.NewDefaultAppInstaller(provider, appConfig, apis.NewGoTypeAssociator())
if err != nil {
return nil, err
}
installer.AppInstaller = i
return installer, nil
}
+2
View File
@@ -2,6 +2,7 @@ package appregistry
import (
"github.com/google/wire"
"github.com/grafana/grafana/pkg/registry/apps/quotas"
"github.com/grafana/grafana/pkg/registry/apps/alerting/historian"
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
@@ -29,5 +30,6 @@ var WireSet = wire.NewSet(
historian.RegisterAppInstaller,
logsdrilldown.RegisterAppInstaller,
annotation.RegisterAppInstaller,
quotas.RegisterAppInstaller,
example.RegisterAppInstaller,
)