POC/Provisioning: Create parser in one place (#97929)

This commit is contained in:
Ryan McKinley authored and GitHub committed 2024-12-13 17:00:58 +03:00
1 parent 1689c51b0d
commit b454092cd8
6 files changed
+97 -81

No files matched your search

+5 -39
View File
@@ -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
}
+12 -12
View File
@@ -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")
+11 -5
View File
@@ -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,
@@ -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)
@@ -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,
@@ -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",