From de9aec8e56927b6c0f449c5e47df6c9046926bc8 Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:38:24 +0100 Subject: [PATCH 1/3] Alerting: change Settings page subtitle (#98368) * Alerting: change Settings page subtitle * update pseudo-locale * fix pseudo-locale translation using `make i18n-extract` --- public/app/core/utils/navBarItem-translations.ts | 5 ++++- public/locales/en-US/grafana.json | 2 +- public/locales/pseudo-LOCALE/grafana.json | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/public/app/core/utils/navBarItem-translations.ts b/public/app/core/utils/navBarItem-translations.ts index 653cacaa3fe..993a914d2c7 100644 --- a/public/app/core/utils/navBarItem-translations.ts +++ b/public/app/core/utils/navBarItem-translations.ts @@ -234,7 +234,10 @@ export function getNavSubTitle(navId: string | undefined) { 'Upgrade your existing legacy alerts and notification channels to the new Grafana Alerting' ); case 'alerting-admin': - return t('nav.alerting-admin.subtitle', 'Manage Alertmanager configurations'); + return t( + 'nav.alerting-admin.subtitle', + 'Manage Alertmanager configurations and enable receiving Grafana-managed alerts' + ); case 'alert-list': return t('nav.alerting-list.subtitle', 'Rules that determine whether an alert will fire'); case 'receivers': diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index a1d1c9684ce..75c470cc7fa 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2031,7 +2031,7 @@ "title": "Alerting" }, "alerting-admin": { - "subtitle": "Manage Alertmanager configurations", + "subtitle": "Manage Alertmanager configurations and enable receiving Grafana-managed alerts", "title": "Settings" }, "alerting-am-routes": { diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 2c7eadc99e0..9eecbf9d185 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -2031,7 +2031,7 @@ "title": "Åľęřŧįʼnģ" }, "alerting-admin": { - "subtitle": "Mäʼnäģę Åľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼnş", + "subtitle": "Mäʼnäģę Åľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼnş äʼnđ ęʼnäþľę řęčęįvįʼnģ Ğřäƒäʼnä-mäʼnäģęđ äľęřŧş", "title": "Ŝęŧŧįʼnģş" }, "alerting-am-routes": { From 6957e1f7b7bcb4a54fbf1c8317467ea68e09fcd9 Mon Sep 17 00:00:00 2001 From: "Arati R." <33031346+suntala@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:53:09 +0100 Subject: [PATCH 2/3] Folders: Replace sql query with folder service call when collecting folder tree (#98443) * Replace sql query with folder service call when collecting folder tree * Update provider for folder service implementation for wire * Refactor provisioning of oss service in folder permissions test util --- pkg/api/folder_bench_test.go | 8 ++--- pkg/server/wire.go | 1 + pkg/services/accesscontrol/acimpl/service.go | 8 +++-- .../accesscontrol/acimpl/service_test.go | 1 + .../accesscontrol/dualwrite/collectors.go | 29 +++++++++++-------- .../accesscontrol/dualwrite/reconciler.go | 5 ++-- .../ossaccesscontrol/testutil/testutil.go | 10 ++++--- .../service/zanzana_integration_test.go | 18 +++++++++++- pkg/services/folder/folderimpl/folder.go | 2 +- .../extsvcaccounts/service_test.go | 2 +- 10 files changed, 56 insertions(+), 28 deletions(-) diff --git a/pkg/api/folder_bench_test.go b/pkg/api/folder_bench_test.go index 7901d005151..db2067c7202 100644 --- a/pkg/api/folder_bench_test.go +++ b/pkg/api/folder_bench_test.go @@ -459,13 +459,13 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()) cfg := setting.NewCfg() actionSets := resourcepermissions.NewActionSetService(features) - acSvc := acimpl.ProvideOSSService( - sc.cfg, acdb.ProvideService(sc.db), actionSets, localcache.ProvideService(), - features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db, permreg.ProvidePermissionRegistry(), nil, - ) fStore := folderimpl.ProvideStore(sc.db) folderServiceWithFlagOn := folderimpl.ProvideService(fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, sc.db, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) + acSvc := acimpl.ProvideOSSService( + sc.cfg, acdb.ProvideService(sc.db), actionSets, localcache.ProvideService(), + features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db, permreg.ProvidePermissionRegistry(), nil, folderServiceWithFlagOn, + ) folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets) require.NoError(b, err) diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 7a749adf4ae..95bd6b81415 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -300,6 +300,7 @@ var wireBasicSet = wire.NewSet( dashboardservice.ProvideDashboardPluginService, dashboardstore.ProvideDashboardStore, folderimpl.ProvideService, + wire.Bind(new(folder.Service), new(*folderimpl.Service)), folderimpl.ProvideStore, wire.Bind(new(folder.Store), new(*folderimpl.FolderStoreImpl)), folderimpl.ProvideDashboardFolderStore, diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index d4fe1735f05..3a13f94af69 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -55,7 +55,7 @@ func ProvideService( cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegister, cache *localcache.CacheService, accessControl accesscontrol.AccessControl, userService user.Service, actionResolver accesscontrol.ActionResolver, features featuremgmt.FeatureToggles, tracer tracing.Tracer, zclient zanzana.Client, permRegistry permreg.PermissionRegistry, - lock *serverlock.ServerLockService, + lock *serverlock.ServerLockService, folderService folder.Service, ) (*Service, error) { service := ProvideOSSService( cfg, @@ -68,6 +68,7 @@ func ProvideService( db, permRegistry, lock, + folderService, ) api.NewAccessControlAPI(routeRegister, accessControl, service, userService, features).RegisterAPIEndpoints() @@ -89,7 +90,8 @@ func ProvideService( func ProvideOSSService( cfg *setting.Cfg, store accesscontrol.Store, actionResolver accesscontrol.ActionResolver, cache *localcache.CacheService, features featuremgmt.FeatureToggles, tracer tracing.Tracer, - zclient zanzana.Client, db db.DB, permRegistry permreg.PermissionRegistry, lock *serverlock.ServerLockService, + zclient zanzana.Client, db db.DB, permRegistry permreg.PermissionRegistry, + lock *serverlock.ServerLockService, folderService folder.Service, ) *Service { s := &Service{ actionResolver: actionResolver, @@ -99,7 +101,7 @@ func ProvideOSSService( log: log.New("accesscontrol.service"), roles: accesscontrol.BuildBasicRoleDefinitions(), store: store, - reconciler: dualwrite.NewZanzanaReconciler(cfg, zclient, db, lock), + reconciler: dualwrite.NewZanzanaReconciler(cfg, zclient, db, lock, folderService), permRegistry: permRegistry, } diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index 2dc2303443c..674fb544775 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -74,6 +74,7 @@ func TestUsageMetrics(t *testing.T) { nil, permreg.ProvidePermissionRegistry(), nil, + nil, ) assert.Equal(t, tt.expectedValue, s.GetUsageStats(context.Background())["stats.oss.accesscontrol.enabled.count"]) }) diff --git a/pkg/services/accesscontrol/dualwrite/collectors.go b/pkg/services/accesscontrol/dualwrite/collectors.go index c38803eebfd..d7ea5a1343c 100644 --- a/pkg/services/accesscontrol/dualwrite/collectors.go +++ b/pkg/services/accesscontrol/dualwrite/collectors.go @@ -8,6 +8,9 @@ import ( "github.com/grafana/grafana/pkg/infra/db" authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" "github.com/grafana/grafana/pkg/services/authz/zanzana" + "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/folder" + "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" ) @@ -63,24 +66,26 @@ func teamMembershipCollector(store db.DB) legacyTupleCollector { } // folderTreeCollector collects folder tree structure and writes it as relation tuples -func folderTreeCollector(store db.DB) legacyTupleCollector { +func folderTreeCollector(folderService folder.Service) legacyTupleCollector { return func(ctx context.Context, orgID int64) (map[string]map[string]*openfgav1.TupleKey, error) { ctx, span := tracer.Start(ctx, "accesscontrol.migrator.folderTreeCollector") defer span.End() - const query = ` - SELECT uid, parent_uid, org_id FROM folder WHERE org_id = ? - ` - type folder struct { - FolderUID string `xorm:"uid"` - ParentUID string `xorm:"parent_uid"` + user := &user.SignedInUser{ + Login: "folder-tree-collector", + OrgRole: "Admin", + IsGrafanaAdmin: true, + IsServiceAccount: true, + Permissions: map[int64]map[string][]string{orgID: {dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll}}}, + OrgID: orgID, } - var folders []folder - err := store.WithDbSession(ctx, func(sess *db.Session) error { - return sess.SQL(query, orgID).Find(&folders) - }) + q := folder.GetFoldersQuery{ + OrgID: orgID, + SignedInUser: user, + } + folders, err := folderService.GetFolders(ctx, q) if err != nil { return nil, err } @@ -94,7 +99,7 @@ func folderTreeCollector(store db.DB) legacyTupleCollector { } tuple = &openfgav1.TupleKey{ - Object: zanzana.NewTupleEntry(zanzana.TypeFolder, f.FolderUID, ""), + Object: zanzana.NewTupleEntry(zanzana.TypeFolder, f.UID, ""), Relation: zanzana.RelationParent, User: zanzana.NewTupleEntry(zanzana.TypeFolder, f.ParentUID, ""), } diff --git a/pkg/services/accesscontrol/dualwrite/reconciler.go b/pkg/services/accesscontrol/dualwrite/reconciler.go index e5cbecfa1fc..0aa51d95592 100644 --- a/pkg/services/accesscontrol/dualwrite/reconciler.go +++ b/pkg/services/accesscontrol/dualwrite/reconciler.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/services/authz/zanzana" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/setting" ) @@ -34,7 +35,7 @@ type ZanzanaReconciler struct { reconcilers []resourceReconciler } -func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, lock *serverlock.ServerLockService) *ZanzanaReconciler { +func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, lock *serverlock.ServerLockService, folderService folder.Service) *ZanzanaReconciler { zanzanaReconciler := &ZanzanaReconciler{ cfg: cfg, log: log.New("zanzana.reconciler"), @@ -50,7 +51,7 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, ), newResourceReconciler( "folder tree", - folderTreeCollector(store), + folderTreeCollector(folderService), zanzanaCollector([]string{zanzana.RelationParent}), client, ), diff --git a/pkg/services/accesscontrol/ossaccesscontrol/testutil/testutil.go b/pkg/services/accesscontrol/ossaccesscontrol/testutil/testutil.go index 3af775f8600..01b6aaa1db9 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/testutil/testutil.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/testutil/testutil.go @@ -33,10 +33,6 @@ func ProvideFolderPermissions( sqlStore *sqlstore.SQLStore, ) (*ossaccesscontrol.FolderPermissionsService, error) { actionSets := resourcepermissions.NewActionSetService(features) - acSvc := acimpl.ProvideOSSService( - cfg, acdb.ProvideService(sqlStore), actionSets, localcache.ProvideService(), - features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sqlStore, permreg.ProvidePermissionRegistry(), nil, - ) license := licensingtest.NewFakeLicensing() license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() @@ -55,6 +51,12 @@ func ProvideFolderPermissions( dashboardStore, folderStore, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, tracing.InitializeTracerForTest()) + acSvc := acimpl.ProvideOSSService( + cfg, acdb.ProvideService(sqlStore), actionSets, localcache.ProvideService(), + features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sqlStore, permreg.ProvidePermissionRegistry(), + nil, fService, + ) + orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService) if err != nil { return nil, err diff --git a/pkg/services/dashboards/service/zanzana_integration_test.go b/pkg/services/dashboards/service/zanzana_integration_test.go index fe7d9971bca..178979c2aca 100644 --- a/pkg/services/dashboards/service/zanzana_integration_test.go +++ b/pkg/services/dashboards/service/zanzana_integration_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/serverlock" "github.com/grafana/grafana/pkg/infra/tracing" @@ -23,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" + "github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest" "github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -87,10 +89,24 @@ func TestIntegrationDashboardServiceZanzana(t *testing.T) { createDashboards(t, service, 100, "test-a") createDashboards(t, service, 100, "test-b") + folderImplStore := folderimpl.ProvideStore(db) + folderService := folderimpl.ProvideService( + folderImplStore, + ac, + bus.ProvideBus(tracing.InitializeTracerForTest()), + dashboardStore, + folderStore, + db, + featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), + supportbundlestest.NewFakeBundleService(), + nil, + tracing.InitializeTracerForTest(), + ) + // Sync Grafana DB with zanzana (migrate data) tracer := tracing.InitializeTracerForTest() lock := serverlock.ProvideService(db, tracer) - zanzanaSyncronizer := dualwrite.NewZanzanaReconciler(cfg, zclient, db, lock) + zanzanaSyncronizer := dualwrite.NewZanzanaReconciler(cfg, zclient, db, lock, folderService) err = zanzanaSyncronizer.ReconcileSync(context.Background()) require.NoError(t, err) diff --git a/pkg/services/folder/folderimpl/folder.go b/pkg/services/folder/folderimpl/folder.go index 880d964b0b2..4e7037a8074 100644 --- a/pkg/services/folder/folderimpl/folder.go +++ b/pkg/services/folder/folderimpl/folder.go @@ -68,7 +68,7 @@ func ProvideService( supportBundles supportbundles.Service, r prometheus.Registerer, tracer tracing.Tracer, -) folder.Service { +) *Service { srv := &Service{ log: slog.Default().With("logger", "folder-service"), dashboardStore: dashboardStore, diff --git a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go index 55ed6867c2d..71e45e37376 100644 --- a/pkg/services/serviceaccounts/extsvcaccounts/service_test.go +++ b/pkg/services/serviceaccounts/extsvcaccounts/service_test.go @@ -55,7 +55,7 @@ func setupTestEnv(t *testing.T) *TestEnv { acSvc: acimpl.ProvideOSSService( cfg, env.AcStore, &resourcepermissions.FakeActionSetSvc{}, localcache.New(0, 0), fmgt, tracing.InitializeTracerForTest(), nil, nil, - permreg.ProvidePermissionRegistry(), nil, + permreg.ProvidePermissionRegistry(), nil, nil, ), defaultOrgID: autoAssignOrgID, logger: logger, From d96f378562398a8fb4e04ba6bcf995731aca3848 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 7 Jan 2025 12:13:08 +0300 Subject: [PATCH 3/3] K8s/OpenAPI: Update the openapi operation ids (#98515) --- pkg/services/apiserver/builder/helper.go | 98 +++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/pkg/services/apiserver/builder/helper.go b/pkg/services/apiserver/builder/helper.go index 93d7df85415..c209903b76e 100644 --- a/pkg/services/apiserver/builder/helper.go +++ b/pkg/services/apiserver/builder/helper.go @@ -2,9 +2,14 @@ package builder import ( "context" + "encoding/csv" + "encoding/json" + "errors" "fmt" "net/http" + "os" "regexp" + "strings" "time" "github.com/prometheus/client_golang/prometheus" @@ -116,15 +121,104 @@ func SetupConfig( // Add the custom routes to service discovery serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(buildVersion, builders) serverConfig.OpenAPIV3Config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) { + meta := r.Metadata() + kind := "" + action := "" + sub := "" + tags := []string{} - prop, ok := r.Metadata()["x-kubernetes-group-version-kind"] + prop, ok := meta["x-kubernetes-group-version-kind"] if ok { gvk, ok := prop.(metav1.GroupVersionKind) if ok && gvk.Kind != "" { + kind = gvk.Kind tags = append(tags, gvk.Kind) } } - return r.OperationName(), tags, nil + prop, ok = meta["x-kubernetes-action"] + if ok { + action = fmt.Sprintf("%v", prop) + } + + isNew := false + if _, err := os.Stat("test.csv"); errors.Is(err, os.ErrNotExist) { + isNew = true + } + + if action == "connect" { + idx := strings.LastIndex(r.Path(), "/{name}/") + if idx > 0 { + sub = r.Path()[(idx + len("/{name}/")):] + } + } + + operationAlt := r.OperationName() + if action != "" { + if action == "connect" { + idx := strings.Index(r.OperationName(), "Namespaced") + if idx > 0 { + operationAlt = strings.ToLower(r.Method()) + + r.OperationName()[idx:] + } + } + } + + operationAlt = strings.ReplaceAll(operationAlt, "Namespaced", "") + if strings.HasPrefix(operationAlt, "post") { + operationAlt = "create" + operationAlt[len("post"):] + } else if strings.HasPrefix(operationAlt, "read") { + operationAlt = "get" + operationAlt[len("read"):] + } else if strings.HasPrefix(operationAlt, "patch") { + operationAlt = "update" + operationAlt[len("patch"):] + } + + // Audit our options here + if false { + // Safe to ignore G304 -- this will be removed before merging to main, and just helps audit the conversion + // nolint:gosec + f, err := os.OpenFile("test.csv", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + } else { + metastr, _ := json.Marshal(meta) + + prop, ok = meta["x-kubernetes-group-version-kind"] + if ok { + gvk, ok := prop.(metav1.GroupVersionKind) + if ok { + kind = gvk.Kind + } + } + + w := csv.NewWriter(f) + if isNew { + _ = w.Write([]string{ + "#Path", + "Method", + "action", + "kind", + "sub", + "OperationName", + "OperationNameAlt", + "Description", + "metadata", + }) + } + _ = w.Write([]string{ + r.Path(), + r.Method(), + action, + kind, + sub, + r.OperationName(), + operationAlt, + r.Description(), + string(metastr), + }) + w.Flush() + } + } + return operationAlt, tags, nil } // Set the swagger build versions