[Provisioning] Add repository info to folders (#99273)

* WIP: Add repository info to folders

* Fix issues
This commit is contained in:
Roberto Jiménez Sánchez authored and GitHub committed 2025-01-22 14:04:08 +01:00
1 parent 3254a8d1bd
commit 8fd6f2de4a
3 files changed
+123 -22

No files matched your search

@@ -476,6 +476,7 @@ func (b *ProvisioningAPIBuilder) GetPostStartHooks() (map[string]genericapiserve
c.ProvisioningV0alpha1(),
repoInformer,
b, // repoGetter
b.parsers,
b.identities,
b.tester,
b.jobs,
@@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/provisioning/auth"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
)
const loggerName = "provisioning-repository-controller"
@@ -48,6 +49,7 @@ type RepositoryController struct {
client client.ProvisioningV0alpha1Interface
repoLister listers.RepositoryLister
repoSynced cache.InformerSynced
parsers *resources.ParserFactory
jobs jobs.JobQueue
@@ -69,6 +71,7 @@ func NewRepositoryController(
provisioningClient client.ProvisioningV0alpha1Interface,
repoInformer informer.RepositoryInformer,
repoGetter RepoGetter,
parsers *resources.ParserFactory,
identities auth.BackgroundIdentityService,
tester *RepositoryTester,
jobs jobs.JobQueue,
@@ -84,6 +87,7 @@ func NewRepositoryController(
},
),
repoGetter: repoGetter,
parsers: parsers,
identities: identities,
tester: tester,
jobs: jobs,
@@ -230,10 +234,27 @@ func (rc *RepositoryController) process(item *queueItem) error {
return fmt.Errorf("unable to create repository from object: %w", err)
}
hooks, ok := repo.(repository.RepositoryHooks)
if ok {
return hooks.OnDelete(ctx)
if hooks, ok := repo.(repository.RepositoryHooks); !ok {
err := hooks.OnDelete(ctx)
if err != nil {
return fmt.Errorf("handle repository delete: %w", err)
}
}
parser, err := rc.parsers.GetParser(ctx, repo)
if err != nil {
return fmt.Errorf("failed to get parser for %s: %w", repo.Config().Name, err)
}
replicator, err := resources.NewReplicator(repo, parser)
if err != nil {
return fmt.Errorf("error creating replicator")
}
if err := replicator.Unsync(ctx, resources.UnsyncOptions{}); err != nil {
return fmt.Errorf("unsync repository: %w", err)
}
return nil
}
@@ -18,11 +18,16 @@ import (
"k8s.io/client-go/dynamic"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana/pkg/apimachinery/utils"
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 UnsyncOptions struct {
KeepDashboards bool
}
type Replicator struct {
client *DynamicClient
parser *Parser
@@ -99,6 +104,39 @@ func (r *Replicator) Sync(ctx context.Context) (string, error) {
return latest, nil
}
func (r *Replicator) Unsync(ctx context.Context, opts UnsyncOptions) error {
cfg := r.repository.Config()
logger := logging.FromContext(ctx)
logger = logger.With("repository", cfg.Name, "namespace", cfg.GetNamespace(), "folder", cfg.Spec.Folder)
logger.Info("start repository unsync")
defer logger.Info("end repository unsync")
if r.repository.Config().Spec.Folder != "" {
obj, err := r.folders.Get(ctx, r.repository.Config().Spec.Folder, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("get folder to unsync: %w", err)
}
meta, err := apiutils.MetaAccessor(obj)
if err != nil {
return fmt.Errorf("create meta accessor from folder object: %w", err)
}
meta.SetRepositoryInfo(nil)
obj, err = r.folders.Update(ctx, obj, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("remove repo info from folder: %w", err)
}
logger.Info("removed repo info from folder", "object", obj)
} else {
logger.Info("skip repo info removal as it's root folder")
}
return nil
}
// replicateTree replicates all files in the repository.
func (r *Replicator) replicateTree(ctx context.Context, ref string) error {
tree, err := r.repository.ReadTree(ctx, ref)
@@ -211,12 +249,16 @@ func (r *Replicator) createFolderPath(ctx context.Context, filePath string) (str
}
logger := logging.FromContext(ctx).With("file", filePath)
var currentPath string
for _, folder := range strings.Split(dir, "/") {
if folder == "" {
// Trailing / leading slash?
continue
}
currentPath = path.Join(currentPath, folder)
logger := logger.With("folder", folder)
obj, err := r.folders.Get(ctx, folder, metav1.GetOptions{})
// FIXME: Check for IsNotFound properly
@@ -226,21 +268,31 @@ func (r *Replicator) createFolderPath(ctx context.Context, filePath string) (str
continue
}
_, err = r.folders.Create(ctx, &unstructured.Unstructured{
obj = &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]any{
"name": folder,
"namespace": r.client.GetNamespace(),
"annotations": map[string]any{
apiutils.AnnoKeyFolder: parent,
},
},
"spec": map[string]any{
"title": folder, // TODO: how do we want to get this?
"description": "Repository-managed folder.",
},
},
}, metav1.CreateOptions{})
}
meta, err := apiutils.MetaAccessor(obj)
if err != nil {
return "", fmt.Errorf("create meta accessor for the object: %w", err)
}
obj.SetNamespace(r.client.GetNamespace())
obj.SetName(folder)
meta.SetFolder(parent)
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
Name: r.repository.Config().Name,
Path: currentPath,
Hash: "", // FIXME: which hash?
Timestamp: nil, // ???&info.Modified.Time,
})
_, err = r.folders.Create(ctx, obj, metav1.CreateOptions{})
if err != nil {
return parent, fmt.Errorf("failed to create folder '%s': %w", folder, err)
}
@@ -494,8 +546,24 @@ func (r *Replicator) ensureRepositoryFolderExists(ctx context.Context) error {
return nil
}
_, err := r.folders.Get(ctx, r.repository.Config().Spec.Folder, metav1.GetOptions{})
obj, err := r.folders.Get(ctx, r.repository.Config().Spec.Folder, metav1.GetOptions{})
if err == nil {
meta, err := utils.MetaAccessor(obj)
if err != nil {
return fmt.Errorf("create meta accessor for the object: %w", err)
}
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
Name: r.repository.Config().Name,
Path: "",
Hash: "", // FIXME: which hash?
Timestamp: nil, // ???&info.Modified.Time,
})
if _, err := r.folders.Update(ctx, obj, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("failed to add repo info to configured folder: %w", err)
}
return nil
} else if !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to check if folder exists: %w", err)
@@ -507,18 +575,29 @@ func (r *Replicator) ensureRepositoryFolderExists(ctx context.Context) error {
title = cfg.Spec.Folder
}
if _, err := r.folders.Create(ctx, &unstructured.Unstructured{
Object: map[string]any{
"metadata": map[string]any{
"name": cfg.Spec.Folder,
"namespace": cfg.GetNamespace(),
},
obj = &unstructured.Unstructured{
Object: map[string]interface{}{
"spec": map[string]any{
"title": title,
"description": "Repository-managed folder",
"title": title, // TODO: how do we want to get this?
},
},
}, metav1.CreateOptions{}); err != nil {
}
meta, err := utils.MetaAccessor(obj)
if err != nil {
return fmt.Errorf("create meta accessor for the object: %w", err)
}
obj.SetNamespace(cfg.GetNamespace())
obj.SetName(cfg.Spec.Folder)
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
Name: r.repository.Config().Name,
Path: "",
Hash: "", // FIXME: which hash?
Timestamp: nil, // ???&info.Modified.Time,
})
if _, err := r.folders.Create(ctx, obj, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("failed to create folder: %w", err)
}