POC/Provisioning: Create parser in one place (#97929)
This commit is contained in:
1 parent
1689c51b0d
commit
b454092cd8
6 files changed
+97
-81
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user