diff --git a/pkg/registry/apis/provisioning/files.go b/pkg/registry/apis/provisioning/files.go index 9be666e10da..cf9b49ba0fd 100644 --- a/pkg/registry/apis/provisioning/files.go +++ b/pkg/registry/apis/provisioning/files.go @@ -16,15 +16,14 @@ import ( "k8s.io/apiserver/pkg/registry/rest" provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - "github.com/grafana/grafana/pkg/registry/apis/provisioning/lint" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) type filesConnector struct { - getter RepoGetter - client *resources.ClientFactory - logger *slog.Logger + getter RepoGetter + parsers *resources.ParserFactory + logger *slog.Logger } func (*filesConnector) New() runtime.Object { @@ -142,46 +141,13 @@ func (s *filesConnector) Connect(ctx context.Context, name string, opts runtime. }), nil } -func (s *filesConnector) getParser(ctx context.Context, logger *slog.Logger, repo repository.Repository) (*resources.Parser, error) { - ns := repo.Config().Namespace - client, kinds, err := s.client.New(ns) // As system user - if err != nil { - return nil, err - } - - parser := resources.NewParser(repo.Config(), client, kinds) - if repo.Config().Spec.Linting { - linterFactory := lint.NewDashboardLinterFactory() - cfg, err := repo.Read(ctx, logger, linterFactory.ConfigPath(), repo.Config().Spec.GitHub.Branch) - - var linter lint.Linter - switch { - case err == nil: - logger.InfoContext(ctx, "linter config found", "config", string(cfg.Data)) - linter, err = linterFactory.NewFromConfig(cfg.Data) - if err != nil { - return nil, fmt.Errorf("failed to create linter: %w", err) - } - case apierrors.IsNotFound(err): - logger.InfoContext(ctx, "no linter config found, using default") - linter = linterFactory.New() - default: - return nil, fmt.Errorf("failed to read linter config: %w", err) - } - - parser.SetLinter(linter) - } - - return parser, nil -} - func (s *filesConnector) doRead(ctx context.Context, logger *slog.Logger, repo repository.Repository, path string, ref string) (int, *provisioning.ResourceWrapper, error) { info, err := repo.Read(ctx, logger, path, ref) if err != nil { return 0, nil, err } - parser, err := s.getParser(ctx, logger, repo) + parser, err := s.parsers.GetParser(repo) if err != nil { return 0, nil, err } @@ -228,7 +194,7 @@ func (s *filesConnector) doWrite(ctx context.Context, logger *slog.Logger, updat Ref: ref, } - parser, err := s.getParser(ctx, logger, repo) + parser, err := s.parsers.GetParser(repo) if err != nil { return nil, err } diff --git a/pkg/registry/apis/provisioning/jobs/worker.go b/pkg/registry/apis/provisioning/jobs/worker.go index 5d01abef695..03b19a36c4b 100644 --- a/pkg/registry/apis/provisioning/jobs/worker.go +++ b/pkg/registry/apis/provisioning/jobs/worker.go @@ -21,20 +21,20 @@ type RepoGetter interface { var _ Worker = (*JobWorker)(nil) type JobWorker struct { - getter RepoGetter - resourceClient *resources.ClientFactory - identities auth.BackgroundIdentityService - logger *slog.Logger - ignore provisioning.IgnoreFile + getter RepoGetter + parsers *resources.ParserFactory + identities auth.BackgroundIdentityService + logger *slog.Logger + ignore provisioning.IgnoreFile } -func NewJobWorker(getter RepoGetter, resourceClient *resources.ClientFactory, identities auth.BackgroundIdentityService, logger *slog.Logger, ignore provisioning.IgnoreFile) *JobWorker { +func NewJobWorker(getter RepoGetter, parsers *resources.ParserFactory, identities auth.BackgroundIdentityService, logger *slog.Logger, ignore provisioning.IgnoreFile) *JobWorker { return &JobWorker{ - getter: getter, - resourceClient: resourceClient, - identities: identities, - logger: logger, - ignore: ignore, + getter: getter, + parsers: parsers, + identities: identities, + logger: logger, + ignore: ignore, } } @@ -60,7 +60,7 @@ func (g *JobWorker) Process(ctx context.Context, job provisioning.Job) (*provisi return nil, fmt.Errorf("unknown repository") } - factory := resources.NewReplicatorFactory(g.resourceClient, job.Namespace, repo, g.ignore, logger) + factory := resources.NewReplicatorFactory(repo, g.parsers, g.ignore, logger) replicator, err := factory.New() if err != nil { return nil, fmt.Errorf("error creating replicator") diff --git a/pkg/registry/apis/provisioning/register.go b/pkg/registry/apis/provisioning/register.go index 8cceff9bf84..39071f4b302 100644 --- a/pkg/registry/apis/provisioning/register.go +++ b/pkg/registry/apis/provisioning/register.go @@ -67,6 +67,7 @@ type ProvisioningAPIBuilder struct { logger *slog.Logger renderer *renderer client *resources.ClientFactory + parsers *resources.ParserFactory ghFactory github.ClientFactory identities auth.BackgroundIdentityService jobs jobs.JobQueue @@ -86,15 +87,20 @@ func NewProvisioningAPIBuilder( configProvider apiserver.RestConfigProvider, ghFactory github.ClientFactory, ) *ProvisioningAPIBuilder { + clientFactory := resources.NewFactory(identities) builder := &ProvisioningAPIBuilder{ urlProvider: urlProvider, localFileResolver: local, logger: slog.Default().With("logger", "provisioning-api-builder"), webhookSecretKey: webhookSecretKey, - client: resources.NewFactory(identities), features: features, ghFactory: ghFactory, identities: identities, + client: clientFactory, + parsers: &resources.ParserFactory{ + Client: clientFactory, + Logger: slog.Default().With("logger", "provisioning-parser-factory"), + }, renderer: &renderer{ render: render, blobstore: blobstore, @@ -181,7 +187,7 @@ func (b *ProvisioningAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserv b.jobs.Register(jobs.NewJobWorker( b, - b.client, + b.parsers, b.identities, b.logger.With("worker", "github"), provisioning.IncludeYamlOrJSON, @@ -211,9 +217,9 @@ func (b *ProvisioningAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserv logger: b.logger.With("connector", "test"), } storage[provisioning.RepositoryResourceInfo.StoragePath("files")] = &filesConnector{ - getter: b, - client: b.client, - logger: b.logger.With("connector", "files"), + getter: b, + parsers: b.parsers, + logger: b.logger.With("connector", "files"), } storage[provisioning.RepositoryResourceInfo.StoragePath("history")] = &historySubresource{ repoGetter: b, diff --git a/pkg/registry/apis/provisioning/resources/fileformat_test.go b/pkg/registry/apis/provisioning/resources/fileformat_test.go index 4c7f8a78ca9..ca52a9cd747 100644 --- a/pkg/registry/apis/provisioning/resources/fileformat_test.go +++ b/pkg/registry/apis/provisioning/resources/fileformat_test.go @@ -107,7 +107,7 @@ spec: // try to validate (and lint) validate := true - parser.SetLinter(lint.NewDashboardLinter()) + parser.linter = lint.NewDashboardLinter() // Support dashboard conversion parsed, err := parser.Parse(context.Background(), slog.Default(), info, validate) diff --git a/pkg/registry/apis/provisioning/resources/parser.go b/pkg/registry/apis/provisioning/resources/parser.go index 6a98c5c90bc..b0f400606ca 100644 --- a/pkg/registry/apis/provisioning/resources/parser.go +++ b/pkg/registry/apis/provisioning/resources/parser.go @@ -11,6 +11,7 @@ import ( "strings" "gopkg.in/yaml.v3" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -25,6 +26,51 @@ import ( var ErrNamespaceMismatch = errors.New("the file namespace does not match target namespace") +type ParserFactory struct { + Client *ClientFactory + Logger *slog.Logger +} + +func (f *ParserFactory) GetParser(repo repository.Repository) (*Parser, error) { + config := repo.Config() + client, kinds, err := f.Client.New(config.Namespace) // As system user + if err != nil { + return nil, err + } + logger := f.Logger.With("parser", config.Name) + + parser := &Parser{ + repo: config, + client: client, + kinds: kinds, + } + if repo.Config().Spec.Linting { + ctx := context.Background() + + linterFactory := lint.NewDashboardLinterFactory() + cfg, err := repo.Read(ctx, logger, linterFactory.ConfigPath(), "") + + var linter lint.Linter + switch { + case err == nil: + logger.Info("linter config found", "config", string(cfg.Data)) + linter, err = linterFactory.NewFromConfig(cfg.Data) + if err != nil { + return nil, fmt.Errorf("failed to create linter: %w", err) + } + case apierrors.IsNotFound(err): + logger.Info("no linter config found, using default") + linter = linterFactory.New() + default: + return nil, fmt.Errorf("failed to read linter config: %w", err) + } + + parser.linter = linter + } + + return parser, nil +} + type Parser struct { // The target repository repo *provisioning.Repository @@ -43,10 +89,6 @@ func NewParser(repo *provisioning.Repository, client *DynamicClient, kinds Kinds } } -func (r *Parser) SetLinter(l lint.Linter) { - r.linter = l -} - type ParsedResource struct { // Original file Info Info *repository.FileInfo @@ -83,6 +125,10 @@ type ParsedResource struct { Errors []error } +func (r *Parser) Client() *DynamicClient { + return r.client +} + func (r *Parser) Parse(ctx context.Context, logger *slog.Logger, info *repository.FileInfo, validate bool) (parsed *ParsedResource, err error) { parsed = &ParsedResource{ Info: info, diff --git a/pkg/registry/apis/provisioning/resources/replicator.go b/pkg/registry/apis/provisioning/resources/replicator.go index fab553e81c7..3696cb04775 100644 --- a/pkg/registry/apis/provisioning/resources/replicator.go +++ b/pkg/registry/apis/provisioning/resources/replicator.go @@ -10,43 +10,41 @@ import ( "path/filepath" "strings" - apiutils "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "gopkg.in/yaml.v3" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" + + apiutils "github.com/grafana/grafana/pkg/apimachinery/utils" + provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) type ReplicatorFactory struct { - repo repository.Repository - client *ClientFactory - namespace string - ignore provisioning.IgnoreFile - logger *slog.Logger + repo repository.Repository + parsers *ParserFactory + ignore provisioning.IgnoreFile + logger *slog.Logger } -func NewReplicatorFactory(client *ClientFactory, namespace string, repo repository.Repository, ignore provisioning.IgnoreFile, logger *slog.Logger) *ReplicatorFactory { +func NewReplicatorFactory(repo repository.Repository, parsers *ParserFactory, ignore provisioning.IgnoreFile, logger *slog.Logger) *ReplicatorFactory { return &ReplicatorFactory{ - client: client, - namespace: namespace, - repo: repo, - ignore: ignore, - logger: logger, + parsers: parsers, + repo: repo, + ignore: ignore, + logger: logger, } } func (f *ReplicatorFactory) New() (repository.FileReplicator, error) { - dynamicClient, kinds, err := f.client.New(f.namespace) - if err != nil { - return nil, fmt.Errorf("failed to get client for namespace %s: %w", f.namespace, err) - } - // The replicator does not need a linter - parser := NewParser(f.repo.Config(), dynamicClient, kinds) + parser, err := f.parsers.GetParser(f.repo) + if err != nil { + return nil, fmt.Errorf("failed to get parser for %s: %w", f.repo.Config().Name, err) + } + dynamicClient := parser.Client() folders := dynamicClient.Resource(schema.GroupVersionResource{ Group: "folder.grafana.app", Version: "v0alpha1",