Chore: Start annotations app (#113018)
* annotation legacy store with api server, read only * Add a feature flag for annotations app * implement list filters * annotations are not addressable by ID for read operations * fix registry apps test * add ownership for an app * disable linter * typo, of course * fix go workspace * update workspace * copy annotation app in dockerfile * update workspace --------- Co-authored-by: Tania B. <10127682+undef1nd@users.noreply.github.com>
This commit is contained in:
co-authored by
Tania B.
parent
0931423259
commit
95ea758475
@@ -0,0 +1,344 @@
|
||||
package annotation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
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/annotation/pkg/apis"
|
||||
annotationV0 "github.com/grafana/grafana/apps/annotation/pkg/apis/annotation/v0alpha1"
|
||||
annotationapp "github.com/grafana/grafana/apps/annotation/pkg/app"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
apiserverrest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/appinstaller"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ appsdkapiserver.AppInstaller = (*AnnotationAppInstaller)(nil)
|
||||
_ appinstaller.LegacyStorageProvider = (*AnnotationAppInstaller)(nil)
|
||||
)
|
||||
|
||||
type AnnotationAppInstaller struct {
|
||||
appsdkapiserver.AppInstaller
|
||||
cfg *setting.Cfg
|
||||
legacy *legacyStorage
|
||||
}
|
||||
|
||||
func RegisterAppInstaller(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
service annotations.Repository,
|
||||
) (*AnnotationAppInstaller, error) {
|
||||
installer := &AnnotationAppInstaller{
|
||||
cfg: cfg,
|
||||
}
|
||||
provider := simple.NewAppProvider(apis.LocalManifest(), nil, annotationapp.New)
|
||||
|
||||
appConfig := app.Config{
|
||||
KubeConfig: restclient.Config{}, // this will be overridden by the installer's InitializeApp method
|
||||
ManifestData: *apis.LocalManifest().ManifestData,
|
||||
}
|
||||
i, err := appsdkapiserver.NewDefaultAppInstaller(provider, appConfig, apis.NewGoTypeAssociator())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installer.AppInstaller = i
|
||||
|
||||
if service != nil {
|
||||
installer.legacy = &legacyStorage{
|
||||
service: service,
|
||||
namespacer: request.GetNamespaceMapper(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
return installer, nil
|
||||
}
|
||||
|
||||
func (a *AnnotationAppInstaller) GetLegacyStorage(requested schema.GroupVersionResource) apiserverrest.Storage {
|
||||
kind := annotationV0.AnnotationKind()
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: kind.Group(),
|
||||
Version: kind.Version(),
|
||||
Resource: kind.Plural(),
|
||||
}
|
||||
if requested.String() != gvr.String() {
|
||||
return nil
|
||||
}
|
||||
a.legacy.tableConverter = utils.NewTableConverter(
|
||||
gvr.GroupResource(),
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Text", Type: "string", Format: "name"},
|
||||
},
|
||||
Reader: func(obj any) ([]any, error) {
|
||||
m, ok := obj.(*annotationV0.Annotation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected Annotation")
|
||||
}
|
||||
return []any{
|
||||
m.Spec.Text,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return a.legacy
|
||||
}
|
||||
|
||||
var (
|
||||
_ rest.Scoper = (*legacyStorage)(nil)
|
||||
_ rest.SingularNameProvider = (*legacyStorage)(nil)
|
||||
_ rest.Getter = (*legacyStorage)(nil)
|
||||
_ rest.Storage = (*legacyStorage)(nil)
|
||||
_ rest.Creater = (*legacyStorage)(nil)
|
||||
_ rest.Updater = (*legacyStorage)(nil)
|
||||
_ rest.GracefulDeleter = (*legacyStorage)(nil)
|
||||
)
|
||||
|
||||
type legacyStorage struct {
|
||||
service annotations.Repository
|
||||
namespacer request.NamespaceMapper
|
||||
tableConverter rest.TableConvertor
|
||||
}
|
||||
|
||||
func (s *legacyStorage) New() runtime.Object {
|
||||
return annotationV0.AnnotationKind().ZeroValue()
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Destroy() {}
|
||||
|
||||
func (s *legacyStorage) NamespaceScoped() bool {
|
||||
return true // namespace == org
|
||||
}
|
||||
|
||||
func (s *legacyStorage) GetSingularName() string {
|
||||
return strings.ToLower(annotationV0.AnnotationKind().Kind())
|
||||
}
|
||||
|
||||
func (s *legacyStorage) NewList() runtime.Object {
|
||||
return annotationV0.AnnotationKind().ZeroListValue()
|
||||
}
|
||||
|
||||
func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
orgID, err := request.OrgIDForList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := &annotations.ItemQuery{OrgID: orgID, SignedInUser: user, AlertID: -1}
|
||||
if options.FieldSelector != nil {
|
||||
for _, r := range options.FieldSelector.Requirements() {
|
||||
switch r.Field {
|
||||
case "spec.dashboardUID":
|
||||
if r.Operator == selection.Equals || r.Operator == selection.DoubleEquals {
|
||||
query.DashboardUID = r.Value
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported operator %s for spec.dashboardUID (only = supported)", r.Operator)
|
||||
}
|
||||
|
||||
case "spec.panelID":
|
||||
if r.Operator == selection.Equals || r.Operator == selection.DoubleEquals {
|
||||
panelID, err := strconv.ParseInt(r.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid panelID value %q: %w", r.Value, err)
|
||||
}
|
||||
query.PanelID = panelID
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported operator %s for spec.panelID (only = supported)", r.Operator)
|
||||
}
|
||||
case "spec.time":
|
||||
switch r.Operator {
|
||||
case selection.GreaterThan:
|
||||
from, err := strconv.ParseInt(r.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid time value %q: %w", r.Value, err)
|
||||
}
|
||||
query.From = from
|
||||
case selection.LessThan:
|
||||
to, err := strconv.ParseInt(r.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid time value %q: %w", r.Value, err)
|
||||
}
|
||||
query.To = to
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported operator %s for spec.time (only >, < supported for ranges)", r.Operator)
|
||||
}
|
||||
|
||||
case "spec.timeEnd":
|
||||
switch r.Operator {
|
||||
case selection.GreaterThan:
|
||||
from, err := strconv.ParseInt(r.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid timeEnd value %q: %w", r.Value, err)
|
||||
}
|
||||
query.From = from
|
||||
case selection.LessThan:
|
||||
to, err := strconv.ParseInt(r.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid timeEnd value %q: %w", r.Value, err)
|
||||
}
|
||||
query.To = to
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported operator %s for spec.timeEnd (only >, < supported for ranges)", r.Operator)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported field selector: %s", r.Field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query.Limit = 100
|
||||
if options.Limit > 0 {
|
||||
query.Limit = options.Limit
|
||||
}
|
||||
items, err := s.service.Find(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &annotationV0.AnnotationList{
|
||||
Items: make([]annotationV0.Annotation, len(items)),
|
||||
}
|
||||
for i, item := range items {
|
||||
c, err := toK8sResource(orgID, item, s.namespacer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list.Items[i] = *c
|
||||
}
|
||||
|
||||
// TODO: pagination?
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return nil, errors.New("fetching single annotations not supported by legacy storage")
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Create(ctx context.Context,
|
||||
obj runtime.Object,
|
||||
createValidation rest.ValidateObjectFunc,
|
||||
options *metav1.CreateOptions,
|
||||
) (runtime.Object, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
// resource, ok := obj.(*correlationsV0.Correlation)
|
||||
// if !ok {
|
||||
// return nil, fmt.Errorf("expected correlation")
|
||||
// }
|
||||
//
|
||||
// cmd, err := correlations.ToCreateCorrelationCommand(resource)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// out, err := s.service.CreateCorrelation(ctx, *cmd)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return s.Get(ctx, out.UID, &metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Update(ctx context.Context,
|
||||
name string,
|
||||
objInfo rest.UpdatedObjectInfo,
|
||||
createValidation rest.ValidateObjectFunc,
|
||||
updateValidation rest.ValidateObjectUpdateFunc,
|
||||
forceAllowCreate bool,
|
||||
options *metav1.UpdateOptions,
|
||||
) (runtime.Object, bool, error) {
|
||||
return nil, false, errors.New("not implemented")
|
||||
// before, err := s.Get(ctx, name, &metav1.GetOptions{})
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// }
|
||||
// obj, err := objInfo.UpdatedObject(ctx, before)
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// }
|
||||
//
|
||||
// resource, ok := obj.(*correlationsV0.Correlation)
|
||||
// if !ok {
|
||||
// return nil, false, fmt.Errorf("expected correlation")
|
||||
// }
|
||||
//
|
||||
// cmd, err := correlations.ToUpdateCorrelationCommand(resource)
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// }
|
||||
//
|
||||
// out, err := s.service.UpdateCorrelation(ctx, *cmd)
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// }
|
||||
// obj, err = s.Get(ctx, out.UID, &metav1.GetOptions{})
|
||||
// return obj, false, err
|
||||
}
|
||||
|
||||
// GracefulDeleter
|
||||
func (s *legacyStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, errors.New("not implemented")
|
||||
// orgID, err := request.OrgIDForList(ctx)
|
||||
// if err != nil {
|
||||
// return nil, false, err
|
||||
// }
|
||||
// err = s.service.DeleteCorrelation(ctx, correlations.DeleteCorrelationCommand{
|
||||
// OrgId: orgID,
|
||||
// UID: name,
|
||||
// })
|
||||
// return nil, (err == nil), err
|
||||
}
|
||||
|
||||
// CollectionDeleter
|
||||
func (s *legacyStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
|
||||
return nil, fmt.Errorf("DeleteCollection for annotation not implemented")
|
||||
}
|
||||
|
||||
func toK8sResource(orgID int64, item *annotations.ItemDTO, namespacer request.NamespaceMapper) (*annotationV0.Annotation, error) {
|
||||
annotation := &annotationV0.Annotation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("a-%d", item.ID), // FIXME
|
||||
Namespace: namespacer(orgID),
|
||||
},
|
||||
Spec: annotationV0.AnnotationSpec{
|
||||
Text: item.Text,
|
||||
Time: item.Time,
|
||||
Tags: item.Tags,
|
||||
},
|
||||
}
|
||||
|
||||
if item.DashboardUID != nil && *item.DashboardUID != "" {
|
||||
annotation.Spec.DashboardUID = item.DashboardUID
|
||||
}
|
||||
if item.PanelID != 0 {
|
||||
annotation.Spec.PanelID = &item.PanelID
|
||||
}
|
||||
if item.TimeEnd != 0 {
|
||||
annotation.Spec.TimeEnd = &item.TimeEnd
|
||||
}
|
||||
return annotation, nil
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"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/annotation"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
@@ -39,6 +40,7 @@ func ProvideAppInstallers(
|
||||
correlationsAppInstaller *correlations.AppInstaller,
|
||||
alertingNotificationAppInstaller *notifications.AlertingNotificationsAppInstaller,
|
||||
logsdrilldownAppInstaller *logsdrilldown.LogsDrilldownAppInstaller,
|
||||
annotationAppInstaller *annotation.AnnotationAppInstaller,
|
||||
exampleAppInstaller *example.ExampleAppInstaller,
|
||||
) []appsdkapiserver.AppInstaller {
|
||||
installers := []appsdkapiserver.AppInstaller{
|
||||
@@ -65,6 +67,11 @@ func ProvideAppInstallers(
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesLogsDrilldown) {
|
||||
installers = append(installers, logsdrilldownAppInstaller)
|
||||
}
|
||||
//nolint:staticcheck
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesAnnotations) {
|
||||
installers = append(installers, annotationAppInstaller)
|
||||
}
|
||||
|
||||
return installers
|
||||
}
|
||||
|
||||
|
||||
@@ -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/annotation"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
@@ -20,6 +21,7 @@ func TestProvideAppInstallers_Table(t *testing.T) {
|
||||
rulesInstaller := &rules.AlertingRulesAppInstaller{}
|
||||
correlationsAppInstaller := &correlations.AppInstaller{}
|
||||
notificationsAppInstaller := ¬ifications.AlertingNotificationsAppInstaller{}
|
||||
annotationAppInstaller := &annotation.AnnotationAppInstaller{}
|
||||
exampleAppInstaller := &example.ExampleAppInstaller{}
|
||||
|
||||
tests := []struct {
|
||||
@@ -37,7 +39,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, exampleAppInstaller)
|
||||
got := ProvideAppInstallers(features, playlistInstaller, pluginsInstaller, nil, tt.rulesInst, correlationsAppInstaller, notificationsAppInstaller, nil, annotationAppInstaller, exampleAppInstaller)
|
||||
if tt.expectRulesApp {
|
||||
require.Contains(t, got, tt.rulesInst)
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"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/annotation"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/correlations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/example"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
@@ -27,5 +28,6 @@ var WireSet = wire.NewSet(
|
||||
rules.RegisterAppInstaller,
|
||||
notifications.RegisterAppInstaller,
|
||||
logsdrilldown.RegisterAppInstaller,
|
||||
annotation.RegisterAppInstaller,
|
||||
example.RegisterAppInstaller,
|
||||
)
|
||||
|
||||
Generated
+11
-2
@@ -80,6 +80,7 @@ import (
|
||||
advisor2 "github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
notifications2 "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/annotation"
|
||||
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"
|
||||
@@ -798,11 +799,15 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
annotationAppInstaller, err := annotation.RegisterAppInstaller(cfg, featureToggles, repositoryImpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exampleAppInstaller, err := example.RegisterAppInstaller(cfg, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, exampleAppInstaller)
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, 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 {
|
||||
@@ -1432,11 +1437,15 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
annotationAppInstaller, err := annotation.RegisterAppInstaller(cfg, featureToggles, repositoryImpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exampleAppInstaller, err := example.RegisterAppInstaller(cfg, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, exampleAppInstaller)
|
||||
v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, pluginsAppInstaller, shortURLAppInstaller, alertingRulesAppInstaller, appInstaller, alertingNotificationsAppInstaller, logsDrilldownAppInstaller, annotationAppInstaller, 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 {
|
||||
|
||||
@@ -330,7 +330,9 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
|
||||
params = append(params, query.AnnotationID)
|
||||
}
|
||||
|
||||
if query.AlertID != 0 {
|
||||
if query.AlertID < 0 {
|
||||
sql.WriteString(` AND a.alert_id = 0`)
|
||||
} else if query.AlertID > 0 {
|
||||
sql.WriteString(` AND a.alert_id = ?`)
|
||||
params = append(params, query.AlertID)
|
||||
} else if query.AlertUID != "" {
|
||||
|
||||
@@ -2159,6 +2159,13 @@ var (
|
||||
Owner: grafanaSharingSquad,
|
||||
FrontendOnly: false,
|
||||
},
|
||||
{
|
||||
Name: "kubernetesAnnotations",
|
||||
Description: "Enables app platform API for annotations",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaBackendServicesSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -277,3 +277,4 @@ pluginStoreServiceLoading,experimental,@grafana/plugins-platform-backend,false,f
|
||||
onlyStoreActionSets,GA,@grafana/identity-access-team,false,false,false
|
||||
panelTimeSettings,experimental,@grafana/dashboards-squad,false,false,false
|
||||
dashboardTemplates,experimental,@grafana/sharing-squad,false,false,false
|
||||
kubernetesAnnotations,experimental,@grafana/grafana-backend-services-squad,false,false,false
|
||||
|
||||
|
Generated
+4
@@ -1117,4 +1117,8 @@ const (
|
||||
// FlagDashboardTemplates
|
||||
// Enable template dashboards
|
||||
FlagDashboardTemplates = "dashboardTemplates"
|
||||
|
||||
// FlagKubernetesAnnotations
|
||||
// Enables app platform API for annotations
|
||||
FlagKubernetesAnnotations = "kubernetesAnnotations"
|
||||
)
|
||||
|
||||
@@ -2153,6 +2153,19 @@
|
||||
"requiresRestart": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kubernetesAnnotations",
|
||||
"resourceVersion": "1761142826172",
|
||||
"creationTimestamp": "2025-10-22T14:20:26Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables app platform API for annotations",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/grafana-backend-services-squad",
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kubernetesAuthZHandlerRedirect",
|
||||
|
||||
Reference in New Issue
Block a user