mirror of https://github.com/grafana/grafana.git
122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/grafana/grafana-app-sdk/app"
|
|
"github.com/grafana/grafana-app-sdk/logging"
|
|
"github.com/grafana/grafana-app-sdk/operator"
|
|
"github.com/grafana/grafana-app-sdk/resource"
|
|
"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"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
var appManifestData = app.ManifestData{
|
|
AppName: "iam-folder-reconciler",
|
|
Group: "iam.grafana.app",
|
|
}
|
|
|
|
type InformerConfig struct {
|
|
MaxConcurrentWorkers uint64
|
|
}
|
|
|
|
type AppConfig struct {
|
|
ZanzanaClientCfg authz.ZanzanaClientConfig
|
|
InformerConfig InformerConfig
|
|
Namespace string
|
|
MetricsRegisterer prometheus.Registerer
|
|
}
|
|
|
|
func Provider(appCfg app.SpecificConfig) app.Provider {
|
|
return simple.NewAppProvider(app.NewEmbeddedManifest(appManifestData), appCfg, New)
|
|
}
|
|
|
|
func generateInformerSupplier(informerConfig InformerConfig, metrics *reconcilers.ReconcilerMetrics) simple.InformerSupplier {
|
|
return func(kind resource.Kind, clients resource.ClientGenerator, options operator.InformerOptions) (operator.Informer, error) {
|
|
client, err := clients.ClientFor(kind)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
informer, err := operator.NewKubernetesBasedInformer(
|
|
kind, client,
|
|
options,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return operator.NewConcurrentInformer(
|
|
informer,
|
|
operator.ConcurrentInformerOptions{
|
|
MaxConcurrentWorkers: informerConfig.MaxConcurrentWorkers,
|
|
ErrorHandler: func(ctx context.Context, err error) {
|
|
logging.FromContext(ctx).With("error", err).Error("ConcurrentInformer processing error")
|
|
if metrics != nil {
|
|
// Use "unknown" for action since informer errors don't have specific actions
|
|
metrics.RecordReconcileFailure("unknown", "informer")
|
|
}
|
|
},
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func New(cfg app.Config) (app.App, error) {
|
|
appSpecificConfig, ok := cfg.SpecificConfig.(AppConfig)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid config type: expected AppConfig, got %T", cfg.SpecificConfig)
|
|
}
|
|
|
|
// Initialize metrics first so they can be shared across components
|
|
metrics := reconcilers.NewReconcilerMetrics(appSpecificConfig.MetricsRegisterer, appSpecificConfig.Namespace)
|
|
|
|
folderReconciler, err := reconcilers.NewFolderReconciler(reconcilers.ReconcilerConfig{
|
|
ZanzanaCfg: appSpecificConfig.ZanzanaClientCfg,
|
|
Metrics: metrics,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create FolderReconciler: %w", err)
|
|
}
|
|
|
|
logging.DefaultLogger.Info("FolderReconciler created")
|
|
|
|
config := simple.AppConfig{
|
|
Name: cfg.ManifestData.AppName,
|
|
KubeConfig: cfg.KubeConfig,
|
|
InformerConfig: simple.AppInformerConfig{
|
|
InformerSupplier: generateInformerSupplier(appSpecificConfig.InformerConfig, metrics),
|
|
InformerOptions: operator.InformerOptions{
|
|
ErrorHandler: func(ctx context.Context, err error) {
|
|
logging.FromContext(ctx).With("error", err).Error("Informer processing error")
|
|
if metrics != nil {
|
|
// Use "unknown" for action since top-level informer errors don't have specific actions
|
|
metrics.RecordReconcileFailure("unknown", "informer")
|
|
}
|
|
},
|
|
},
|
|
},
|
|
UnmanagedKinds: []simple.AppUnmanagedKind{
|
|
{
|
|
Kind: foldersKind.FolderKind(),
|
|
Reconciler: folderReconciler,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Create the App
|
|
a, err := simple.NewApp(config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Validate the capabilities against the provided manifest to make sure there isn't a mismatch
|
|
err = a.ValidateManifest(cfg.ManifestData)
|
|
|
|
return a, err
|
|
}
|