mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Zanzana: Fix reconciliation of fixed roles (#98696)
Remove "globalReconciler" and reuse the same one but only run them for cluster namespace
This commit is contained in:
parent
4ae627e420
commit
fb5783691d
@ -414,8 +414,8 @@ func rolePermissionsCollector(store db.DB) legacyTupleCollector {
|
||||
}
|
||||
}
|
||||
|
||||
func fixedRolePermissionsCollector(store db.DB) globalTupleCollector {
|
||||
return func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error) {
|
||||
func fixedRolePermissionsCollector(store db.DB) legacyTupleCollector {
|
||||
return func(ctx context.Context, _ int64) (map[string]map[string]*openfgav1.TupleKey, error) {
|
||||
var query = `
|
||||
SELECT r.uid as role_uid, p.action, p.kind, p.identifier
|
||||
FROM permission p
|
||||
@ -457,6 +457,18 @@ func fixedRolePermissionsCollector(store db.DB) globalTupleCollector {
|
||||
tuples[tuple.Object] = make(map[string]*openfgav1.TupleKey)
|
||||
}
|
||||
|
||||
// For resource actions on folders we need to merge the tuples into one with combined
|
||||
// group_resources.
|
||||
if zanzana.IsFolderResourceTuple(tuple) {
|
||||
key := tupleStringWithoutCondition(tuple)
|
||||
if t, ok := tuples[tuple.Object][key]; ok {
|
||||
zanzana.MergeFolderResourceTuples(t, tuple)
|
||||
} else {
|
||||
tuples[tuple.Object][key] = tuple
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
tuples[tuple.Object][tuple.String()] = tuple
|
||||
}
|
||||
|
||||
|
@ -1,112 +0,0 @@
|
||||
package dualwrite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
)
|
||||
|
||||
type globalTupleCollector func(ctx context.Context) (map[string]map[string]*openfgav1.TupleKey, error)
|
||||
|
||||
type globalReconciler struct {
|
||||
name string
|
||||
globalCollector globalTupleCollector
|
||||
zanzana zanzanaTupleCollector
|
||||
client zanzana.Client
|
||||
}
|
||||
|
||||
func newGlobalReconciler(name string, globalCollector globalTupleCollector, zanzana zanzanaTupleCollector, client zanzana.Client) globalReconciler {
|
||||
return globalReconciler{name, globalCollector, zanzana, client}
|
||||
}
|
||||
|
||||
func (r globalReconciler) reconcile(ctx context.Context) error {
|
||||
namespace := zanzana.ClusterNamespace
|
||||
|
||||
// 1. Fetch grafana resources stored in grafana db.
|
||||
res, err := r.globalCollector(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect legacy tuples for %s: %w", r.name, err)
|
||||
}
|
||||
|
||||
var (
|
||||
writes = []*openfgav1.TupleKey{}
|
||||
deletes = []*openfgav1.TupleKeyWithoutCondition{}
|
||||
)
|
||||
|
||||
for object, tuples := range res {
|
||||
// 2. Fetch all tuples for given object.
|
||||
// Due to limitations in open fga api we need to collect tuples per object
|
||||
zanzanaTuples, err := r.zanzana(ctx, r.client, object, namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect zanzanaa tuples for %s: %w", r.name, err)
|
||||
}
|
||||
|
||||
// 3. Check if tuples from grafana db exists in zanzana and if not add them to writes
|
||||
for key, t := range tuples {
|
||||
stored, ok := zanzanaTuples[key]
|
||||
if !ok {
|
||||
writes = append(writes, t)
|
||||
continue
|
||||
}
|
||||
|
||||
// 4. For folder resource tuples we also need to compare the stored group_resources
|
||||
if zanzana.IsFolderResourceTuple(t) && t.String() != stored.String() {
|
||||
deletes = append(deletes, &openfgav1.TupleKeyWithoutCondition{
|
||||
User: t.User,
|
||||
Relation: t.Relation,
|
||||
Object: t.Object,
|
||||
})
|
||||
|
||||
writes = append(writes, t)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Check if tuple from zanzana don't exists in grafana db, if not add them to deletes.
|
||||
for key, tuple := range zanzanaTuples {
|
||||
_, ok := tuples[key]
|
||||
if !ok {
|
||||
deletes = append(deletes, &openfgav1.TupleKeyWithoutCondition{
|
||||
User: tuple.User,
|
||||
Relation: tuple.Relation,
|
||||
Object: tuple.Object,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(writes) == 0 && len(deletes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(deletes) > 0 {
|
||||
err := batch(deletes, 100, func(items []*openfgav1.TupleKeyWithoutCondition) error {
|
||||
return r.client.Write(ctx, &authzextv1.WriteRequest{
|
||||
Namespace: namespace,
|
||||
Deletes: &authzextv1.WriteRequestDeletes{TupleKeys: zanzana.ToAuthzExtTupleKeysWithoutCondition(items)},
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(writes) > 0 {
|
||||
err := batch(writes, 100, func(items []*openfgav1.TupleKey) error {
|
||||
return r.client.Write(ctx, &authzextv1.WriteRequest{
|
||||
Namespace: namespace,
|
||||
Writes: &authzextv1.WriteRequestWrites{TupleKeys: zanzana.ToAuthzExtTupleKeys(items)},
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -32,8 +32,9 @@ type ZanzanaReconciler struct {
|
||||
lock *serverlock.ServerLockService
|
||||
// reconcilers are migrations that tries to reconcile the state of grafana db to zanzana store.
|
||||
// These are run periodically to try to maintain a consistent state.
|
||||
reconcilers []resourceReconciler
|
||||
globalReconcilers []globalReconciler
|
||||
reconcilers []resourceReconciler
|
||||
// globalReconcilers are reconcilers that should only run for cluster namespace
|
||||
globalReconcilers []resourceReconciler
|
||||
}
|
||||
|
||||
func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB, lock *serverlock.ServerLockService, folderService folder.Service) *ZanzanaReconciler {
|
||||
@ -93,11 +94,11 @@ func NewZanzanaReconciler(cfg *setting.Cfg, client zanzana.Client, store db.DB,
|
||||
client,
|
||||
),
|
||||
},
|
||||
globalReconcilers: []globalReconciler{
|
||||
newGlobalReconciler(
|
||||
globalReconcilers: []resourceReconciler{
|
||||
newResourceReconciler(
|
||||
"fixed role pemissions",
|
||||
fixedRolePermissionsCollector(store),
|
||||
zanzanaCollector([]string{zanzana.RelationAssignee}),
|
||||
zanzanaCollector(zanzana.RelationsFolder),
|
||||
client,
|
||||
),
|
||||
},
|
||||
@ -146,7 +147,7 @@ func (r *ZanzanaReconciler) reconcile(ctx context.Context) {
|
||||
runGlobal := func(ctx context.Context) {
|
||||
for _, reconciler := range r.globalReconcilers {
|
||||
r.log.Debug("Performing zanzana reconciliation", "reconciler", reconciler.name)
|
||||
if err := reconciler.reconcile(ctx); err != nil {
|
||||
if err := reconciler.reconcile(ctx, zanzana.ClusterNamespace); err != nil {
|
||||
r.log.Warn("Failed to perform reconciliation for resource", "err", err)
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ const (
|
||||
|
||||
var (
|
||||
RelationsFolder = common.RelationsFolder
|
||||
RelationsFolderResource = common.RelationsFolder
|
||||
RelationsResouce = common.RelationsResource
|
||||
RelationsFolderResource = common.RelationsFolderResource
|
||||
)
|
||||
|
||||
const (
|
||||
|
Loading…
Reference in New Issue
Block a user