[Provisioning] Add repository info to folders (#99273)
* WIP: Add repository info to folders * Fix issues
This commit is contained in:
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)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user