Apps: Add Example App to ./apps (#112069)
* [API Server] Add Example App for reference use. * Remove Printlns. * Upgrade app-sdk to v0.46.0, update apps to handle breaking changes. * Only start the reconciler for the example app if the v1alpha1 API version is enabled. * Some comment doc updates. * Run make update-workspace * Set codeowner for /apps/example * Run make gofmt and make update-workspace * Run prettier on apps/example/README.md * Add COPY apps/example to Dockerfile * Add an authorizer to the example app. * Fix import ordering. * Update apps/example/kinds/manifest.cue Co-authored-by: Owen Diehl <ow.diehl@gmail.com> * Run make update-workspace * Re-run make gen-go for enterprise import updates * Run make update-workspace --------- Co-authored-by: Owen Diehl <ow.diehl@gmail.com>
This commit is contained in:
co-authored by
Owen Diehl
parent
d25f5199c7
commit
bf65c43783
@@ -57,5 +57,4 @@ import (
|
||||
_ "github.com/grafana/tempo/pkg/traceql"
|
||||
|
||||
_ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1"
|
||||
_ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/rules"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
@@ -38,10 +39,12 @@ func ProvideAppInstallers(
|
||||
correlationsAppInstaller *correlations.AppInstaller,
|
||||
alertingNotificationAppInstaller *notifications.AlertingNotificationsAppInstaller,
|
||||
logsdrilldownAppInstaller *logsdrilldown.LogsDrilldownAppInstaller,
|
||||
exampleAppInstaller *example.ExampleAppInstaller,
|
||||
) []appsdkapiserver.AppInstaller {
|
||||
installers := []appsdkapiserver.AppInstaller{
|
||||
playlistAppInstaller,
|
||||
pluginsApplInstaller,
|
||||
exampleAppInstaller,
|
||||
}
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesShortURLs) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/rules"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@@ -19,6 +20,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
|
||||
rulesInstaller := &rules.AlertingRulesAppInstaller{}
|
||||
correlationsAppInstaller := &correlations.AppInstaller{}
|
||||
notificationsAppInstaller := ¬ifications.AlertingNotificationsAppInstaller{}
|
||||
exampleAppInstaller := &example.ExampleAppInstaller{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -35,7 +37,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)
|
||||
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, exampleAppInstaller)
|
||||
if tt.expectRulesApp {
|
||||
require.Contains(t, got, tt.rulesInst)
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
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"
|
||||
"github.com/grafana/grafana/apps/example/pkg/apis"
|
||||
"github.com/grafana/grafana/apps/example/pkg/apis/example/v1alpha1"
|
||||
exampleapp "github.com/grafana/grafana/apps/example/pkg/app"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ appsdkapiserver.AppInstaller = (*ExampleAppInstaller)(nil)
|
||||
)
|
||||
|
||||
type ExampleAppInstaller struct {
|
||||
appsdkapiserver.AppInstaller
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func (e ExampleAppInstaller) GetAuthorizer() authorizer.Authorizer {
|
||||
return exampleapp.GetAuthorizer()
|
||||
}
|
||||
|
||||
func RegisterAppInstaller(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
) (*ExampleAppInstaller, error) {
|
||||
installer := &ExampleAppInstaller{
|
||||
cfg: cfg,
|
||||
}
|
||||
// Config specific to the app. This can pull from feature flags or setting.Cfg.
|
||||
specificConfig := &exampleapp.ExampleConfig{
|
||||
EnableSomeFeature: true,
|
||||
}
|
||||
|
||||
// Set specificConfig.EnableReconciler to true IFF the v1alpha1 API is enabled in the runtime config.
|
||||
// This is example-app-specific, as the version the reconciler uses is not served by default and must be enabled via an override.
|
||||
apiserverRuntimeCfg := cfg.SectionWithEnvOverrides("grafana-apiserver").Key("runtime_config").String()
|
||||
for _, s := range strings.Split(apiserverRuntimeCfg, ",") {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
arr := strings.SplitN(s, "=", 2)
|
||||
if len(arr) == 2 {
|
||||
if arr[0] == fmt.Sprintf("%s/%s", v1alpha1.APIGroup, v1alpha1.APIVersion) {
|
||||
specificConfig.EnableReconciler = strings.EqualFold("true", arr[1])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Provider is the app provider, which contains the AppManifest, app-specific-config, and the New function for the app
|
||||
provider := simple.NewAppProvider(apis.LocalManifest(), specificConfig, exampleapp.New)
|
||||
|
||||
// appConfig is used alongside the provider for registrion.
|
||||
// Most of the data is redunant, this may be more optimized in the future.
|
||||
appConfig := app.Config{
|
||||
KubeConfig: restclient.Config{}, // this will be overridden by the installer's InitializeApp method
|
||||
ManifestData: *apis.LocalManifest().ManifestData,
|
||||
SpecificConfig: specificConfig,
|
||||
}
|
||||
// NewDefaultInstaller gets us the installer we need to underly the ExampleAppInstaller type.
|
||||
// It does all the hard work of installing our app to the grafana API server
|
||||
i, err := appsdkapiserver.NewDefaultAppInstaller(provider, appConfig, apis.NewGoTypeAssociator())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installer.AppInstaller = i
|
||||
|
||||
return installer, nil
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/rules"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
@@ -26,4 +27,5 @@ var WireSet = wire.NewSet(
|
||||
rules.RegisterAppInstaller,
|
||||
notifications.RegisterAppInstaller,
|
||||
logsdrilldown.RegisterAppInstaller,
|
||||
example.RegisterAppInstaller,
|
||||
)
|
||||
|
||||
+11
-2
@@ -81,6 +81,7 @@ import (
|
||||
notifications2 "github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/rules"
|
||||
correlations2 "github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
@@ -783,7 +784,11 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller)
|
||||
exampleAppInstaller, err := example.RegisterAppInstaller(cfg, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, exampleAppInstaller)
|
||||
builderMetrics := builder.ProvideBuilderMetrics(registerer)
|
||||
apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, inlineSecureValueSupport, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics)
|
||||
if err != nil {
|
||||
@@ -1400,7 +1405,11 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller)
|
||||
exampleAppInstaller, err := example.RegisterAppInstaller(cfg, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, exampleAppInstaller)
|
||||
builderMetrics := builder.ProvideBuilderMetrics(registerer)
|
||||
apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, inlineSecureValueSupport, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics)
|
||||
if err != nil {
|
||||
|
||||
@@ -116,6 +116,13 @@ func (s *serverWrapper) configureStorage(gr schema.GroupResource, dualWriteSuppo
|
||||
return statusStore
|
||||
}
|
||||
|
||||
// if the storage is a subresource store, we need to extract the underlying generic registry store
|
||||
if subresourceStore, ok := storage.(*appsdkapiserver.SubresourceREST); ok {
|
||||
subresourceStore.Store.KeyFunc = grafanaregistry.NamespaceKeyFunc(gr)
|
||||
subresourceStore.Store.KeyRootFunc = grafanaregistry.KeyRootFunc(gr)
|
||||
return subresourceStore
|
||||
}
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user