Files

130 lines
4.4 KiB
Go

package export
import (
"context"
"errors"
"fmt"
"time"
"github.com/grafana/grafana-app-sdk/logging"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/apps/provisioning/pkg/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/utils"
)
//go:generate mockery --name ExportFn --structname MockExportFn --inpackage --filename mock_export_fn.go --with-expecter
type ExportFn func(ctx context.Context, repoName string, options provisioning.ExportJobOptions, clients resources.ResourceClients, repositoryResources resources.RepositoryResources, progress jobs.JobProgressRecorder) error
//go:generate mockery --name WrapWithStageFn --structname MockWrapWithStageFn --inpackage --filename mock_wrap_with_stage_fn.go --with-expecter
type WrapWithStageFn func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repo repository.Repository, staged bool) error) error
type ExportWorker struct {
clientFactory resources.ClientFactory
repositoryResources resources.RepositoryResourcesFactory
exportFn ExportFn
wrapWithStageFn WrapWithStageFn
metrics jobs.JobMetrics
}
func NewExportWorker(
clientFactory resources.ClientFactory,
repositoryResources resources.RepositoryResourcesFactory,
exportFn ExportFn,
wrapWithStageFn WrapWithStageFn,
metrics jobs.JobMetrics,
) *ExportWorker {
return &ExportWorker{
clientFactory: clientFactory,
repositoryResources: repositoryResources,
exportFn: exportFn,
wrapWithStageFn: wrapWithStageFn,
metrics: metrics,
}
}
func (r *ExportWorker) IsSupported(ctx context.Context, job provisioning.Job) bool {
return job.Spec.Action == provisioning.JobActionPush
}
// Process will start a job
func (r *ExportWorker) Process(ctx context.Context, repo repository.Repository, job provisioning.Job, progress jobs.JobProgressRecorder) error {
options := job.Spec.Push
if options == nil {
return errors.New("missing export settings")
}
logger := logging.FromContext(ctx).With("job", job.GetName(), "namespace", job.GetNamespace())
start := time.Now()
outcome := utils.ErrorOutcome
resourcesExported := 0
defer func() {
r.metrics.RecordJob(string(provisioning.JobActionPush), outcome, resourcesExported, time.Since(start).Seconds())
}()
cfg := repo.Config()
// Can write to external branch
if err := repository.IsWriteAllowed(cfg, options.Branch); err != nil {
return err
}
msg := options.Message
if msg == "" {
msg = fmt.Sprintf("Export from Grafana %s", job.Name)
}
cloneOptions := repository.StageOptions{
Ref: options.Branch,
Timeout: 10 * time.Minute,
PushOnWrites: false,
Mode: repository.StageModeCommitOnlyOnce,
CommitOnlyOnceMessage: msg,
}
fn := func(repo repository.Repository, _ bool) error {
clients, err := r.clientFactory.Clients(ctx, cfg.Namespace)
if err != nil {
logger.Error("failed to create clients", "error", err)
return fmt.Errorf("create clients: %w", err)
}
rw, ok := repo.(repository.ReaderWriter)
if !ok {
logger.Error("export job submitted targeting repository that is not a ReaderWriter")
return errors.New("export job submitted targeting repository that is not a ReaderWriter")
}
repositoryResources, err := r.repositoryResources.Client(ctx, rw)
if err != nil {
logger.Error("failed to create repository resource client", "error", err)
return fmt.Errorf("create repository resource client: %w", err)
}
return r.exportFn(ctx, cfg.Name, *options, clients, repositoryResources, progress)
}
err := r.wrapWithStageFn(ctx, repo, cloneOptions, fn)
// Set RefURLs if the repository supports it and we have a target branch
if options.Branch != "" {
if repoWithURLs, ok := repo.(repository.RepositoryWithURLs); ok {
if refURLs, urlErr := repoWithURLs.RefURLs(ctx, options.Branch); urlErr == nil && refURLs != nil {
progress.SetRefURLs(ctx, refURLs)
}
}
}
if err != nil {
logger.Error("failed to export", "error", err)
return err
}
outcome = utils.SuccessOutcome
jobStatus := progress.Complete(ctx, nil)
for _, summary := range jobStatus.Summary {
resourcesExported += int(summary.Write)
}
return nil
}