Slight refactor of Zanzana GRPC Client to use it in the IAM Folder Operator (#110120)

This commit is contained in:
Mihai Turdean
2025-08-26 08:34:15 -06:00
committed by GitHub
parent 60f31342c0
commit 3eebe43c6d
7 changed files with 313 additions and 91 deletions
+17 -4
View File
@@ -9,21 +9,34 @@ import (
"github.com/grafana/grafana-app-sdk/simple"
foldersKind "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
"github.com/grafana/grafana/apps/iam/pkg/reconcilers"
"github.com/grafana/grafana/pkg/services/authz"
)
type AppConfig = reconcilers.AppConfig
var appManifestData = app.ManifestData{
AppName: "iam-folder-reconciler",
Group: "iam.grafana.app",
}
func Provider(appCfg AppConfig) app.Provider {
type AppConfig struct {
ZanzanaCfg authz.ZanzanaClientConfig
FolderReconcilerNamespace string
}
func Provider(appCfg app.SpecificConfig) app.Provider {
return simple.NewAppProvider(app.NewEmbeddedManifest(appManifestData), appCfg, New)
}
func New(cfg app.Config) (app.App, error) {
folderReconciler, err := reconcilers.NewFolderReconciler(cfg)
appSpecificConfig, ok := cfg.SpecificConfig.(AppConfig)
if !ok {
return nil, fmt.Errorf("invalid config type: expected AppConfig, got %T", cfg.SpecificConfig)
}
folderReconciler, err := reconcilers.NewFolderReconciler(reconcilers.ReconcilerConfig{
ZanzanaCfg: appSpecificConfig.ZanzanaCfg,
KubeConfig: &cfg.KubeConfig,
FolderReconcilerNamespace: appSpecificConfig.FolderReconcilerNamespace,
})
if err != nil {
return nil, fmt.Errorf("unable to create FolderReconciler: %w", err)
}
+13 -39
View File
@@ -5,13 +5,11 @@ import (
"fmt"
"time"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/operator"
foldersKind "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/grafana/grafana/pkg/services/authz"
"k8s.io/client-go/rest"
)
// FolderStore interface for retrieving folder information
@@ -27,8 +25,9 @@ type PermissionStore interface {
}
// AppConfig represents the app-specific configuration
type AppConfig struct {
ZanzanaAddr string
type ReconcilerConfig struct {
ZanzanaCfg authz.ZanzanaClientConfig
KubeConfig *rest.Config
FolderReconcilerNamespace string
}
@@ -37,21 +36,16 @@ type FolderReconciler struct {
folderStore FolderStore
}
func NewFolderReconciler(cfg app.Config) (operator.Reconciler, error) {
// Extract Zanzana address from config
appCfg, ok := cfg.SpecificConfig.(AppConfig)
if !ok {
return nil, fmt.Errorf("invalid config type: expected AppConfig, got %T", cfg.SpecificConfig)
}
func NewFolderReconciler(cfg ReconcilerConfig) (operator.Reconciler, error) {
// Create Zanzana client
zanzanaClient, err := getZanzanaClient(appCfg.ZanzanaAddr)
zanzanaClient, err := authz.NewZanzanaClient("*", cfg.ZanzanaCfg)
if err != nil {
return nil, fmt.Errorf("unable to create zanzana client: %w", err)
}
// Create dependencies
folderStore := NewAPIFolderStore(&cfg.KubeConfig)
folderStore := NewAPIFolderStore(cfg.KubeConfig)
permissionStore := NewZanzanaPermissionStore(zanzanaClient)
folderReconciler := &FolderReconciler{
@@ -66,34 +60,11 @@ func NewFolderReconciler(cfg app.Config) (operator.Reconciler, error) {
return reconciler, nil
}
func getZanzanaClient(addr string) (zanzana.Client, error) {
transportCredentials := insecure.NewCredentials()
dialOptions := []grpc.DialOption{
grpc.WithTransportCredentials(transportCredentials),
}
conn, err := grpc.NewClient(addr, dialOptions...)
if err != nil {
return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
}
client, err := zanzana.NewClient(conn)
if err != nil {
return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
}
return client, nil
}
func (r *FolderReconciler) reconcile(ctx context.Context, req operator.TypedReconcileRequest[*foldersKind.Folder]) (operator.ReconcileResult, error) {
// Add timeout to prevent hanging operations
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
logger := logging.FromContext(ctx)
logger.Info("Reconciling request", "req", req)
err := validateFolder(req.Object)
if err != nil {
return operator.ReconcileResult{}, err
@@ -119,22 +90,24 @@ func (r *FolderReconciler) handleUpdateFolder(ctx context.Context, folder *folde
parentUID, err := r.folderStore.GetFolderParent(ctx, namespace, folderUID)
if err != nil {
logger.Error("Error getting folder parent", "error", err)
return operator.ReconcileResult{}, err
}
parents, err := r.permissionStore.GetFolderParents(ctx, namespace, folderUID)
if err != nil {
logger.Error("Error getting folder parents", "error", err)
return operator.ReconcileResult{}, err
}
if (len(parents) == 0 && parentUID == "") || (len(parents) == 1 && parents[0] == parentUID) {
// Folder is already reconciled
logger.Info("Folder is already reconciled", "folder", folderUID, "parent", parentUID, "namespace", namespace)
return operator.ReconcileResult{}, nil
}
err = r.permissionStore.SetFolderParent(ctx, namespace, folderUID, parentUID)
if err != nil {
logger.Error("Error setting folder parent", "error", err)
return operator.ReconcileResult{}, err
}
@@ -151,6 +124,7 @@ func (r *FolderReconciler) handleDeleteFolder(ctx context.Context, folder *folde
err := r.permissionStore.DeleteFolderParents(ctx, namespace, folderUID)
if err != nil {
logger.Error("Error deleting folder parents", "error", err)
return operator.ReconcileResult{}, err
}