diff --git a/pkg/registry/apis/provisioning/controller/finalizers.go b/pkg/registry/apis/provisioning/controller/finalizers.go index 13727cfd59c..c2875530d5e 100644 --- a/pkg/registry/apis/provisioning/controller/finalizers.go +++ b/pkg/registry/apis/provisioning/controller/finalizers.go @@ -54,9 +54,9 @@ func (f *finalizer) process(ctx context.Context, err := f.processExistingItems(ctx, repo.Config(), func(client dynamic.ResourceInterface, item *provisioning.ResourceListItem) error { _, err := client.Patch(ctx, item.Name, types.JSONPatchType, []byte(`[ - {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeyRepoName+`" }, - {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeyRepoPath+`" }, - {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeyRepoHash+`" } + {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeyManagerKind+`" }, + {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeyManagerIdentity+`" }, + {"op": "remove", "path": "/metadata/annotations/`+utils.AnnoKeySourcePath+`" } ]`), v1.PatchOptions{}) return err }) diff --git a/pkg/registry/apis/provisioning/jobs/export/resources.go b/pkg/registry/apis/provisioning/jobs/export/resources.go index a703bbd774a..cbbdcbc6dfa 100644 --- a/pkg/registry/apis/provisioning/jobs/export/resources.go +++ b/pkg/registry/apis/provisioning/jobs/export/resources.go @@ -92,8 +92,8 @@ func (r *exportJob) write(ctx context.Context, obj *unstructured.Unstructured) j } name := meta.GetName() - repoName := meta.GetRepositoryName() - if repoName == r.target.Config().GetName() { + manager, _ := meta.GetManagerProperties() + if manager.Identity == r.target.Config().GetName() { result.Action = repository.FileActionIgnored return result } diff --git a/pkg/registry/apis/provisioning/jobs/migrate/resources.go b/pkg/registry/apis/provisioning/jobs/migrate/resources.go index 927852dd452..d5c7b5b696e 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/resources.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/resources.go @@ -48,8 +48,9 @@ func (r *resourceReader) Write(ctx context.Context, key *resource.ResourceKey, v return fmt.Errorf("failed to unmarshal unstructured: %w", err) } - // clear anything so it will get written - parsed.Meta.SetRepositoryInfo(nil) + // clear all manager fields so they are not exported + parsed.Meta.SetManagerProperties(utils.ManagerProperties{}) + parsed.Meta.SetSourceProperties(utils.SourceProperties{}) if result := r.job.write(ctx, parsed.Obj); result.Error != nil { r.job.progress.Record(ctx, result) @@ -134,8 +135,8 @@ func (j *migrationJob) write(ctx context.Context, obj *unstructured.Unstructured } name := meta.GetName() - repoName := meta.GetRepositoryName() - if repoName == j.target.Config().GetName() { + manager, _ := meta.GetManagerProperties() + if manager.Identity == j.target.Config().GetName() { result.Action = repository.FileActionIgnored return result } diff --git a/pkg/registry/apis/provisioning/jobs/sync/worker.go b/pkg/registry/apis/provisioning/jobs/sync/worker.go index 53cdabcf182..29cf2dd3ea8 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/worker.go +++ b/pkg/registry/apis/provisioning/jobs/sync/worker.go @@ -538,7 +538,7 @@ func (r *syncJob) ensureFolderExists(ctx context.Context, folder resources.Folde cfg := r.repository.Config() obj, err := r.folders.Get(ctx, folder.ID, metav1.GetOptions{}) if err == nil { - current, ok := obj.GetAnnotations()[utils.AnnoKeyRepoName] + current, ok := obj.GetAnnotations()[utils.AnnoKeyManagerIdentity] if !ok { return fmt.Errorf("target folder is not managed by a repository") } @@ -568,11 +568,12 @@ func (r *syncJob) ensureFolderExists(ctx context.Context, folder resources.Folde if parent != "" { meta.SetFolder(parent) } - meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{ - Name: cfg.GetName(), - Path: folder.Path, - Hash: "", // FIXME: which hash? - Timestamp: nil, // ???&info.Modified.Time, + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: cfg.GetName(), + }) + meta.SetSourceProperties(utils.SourceProperties{ + Path: folder.Path, }) result := jobs.JobResourceResult{ diff --git a/pkg/registry/apis/provisioning/resources/parser.go b/pkg/registry/apis/provisioning/resources/parser.go index e9a492a383e..db26c697ad8 100644 --- a/pkg/registry/apis/provisioning/resources/parser.go +++ b/pkg/registry/apis/provisioning/resources/parser.go @@ -130,11 +130,14 @@ func (r *Parser) Parse(ctx context.Context, info *repository.FileInfo, validate } obj.SetNamespace(cfg.GetNamespace()) - parsed.Meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{ - Name: cfg.Name, - Path: info.Path, // joinPathWithRef(info.Path, info.Ref), - Hash: info.Hash, - Timestamp: nil, // ???&info.Modified.Time, + parsed.Meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: cfg.Name, + }) + parsed.Meta.SetSourceProperties(utils.SourceProperties{ + Path: info.Path, // joinPathWithRef(info.Path, info.Ref), + Checksum: info.Hash, + TimestampMillis: asMillis(info.Modified), }) // Calculate name+folder from the file path @@ -211,6 +214,13 @@ func (f *ParsedResource) ToSaveBytes() ([]byte, error) { } } +func asMillis(t *metav1.Time) int64 { + if t == nil || t.IsZero() { + return 0 + } + return t.UnixMilli() +} + func (f *ParsedResource) AsResourceWrapper() *provisioning.ResourceWrapper { info := f.Info res := provisioning.ResourceObjects{ diff --git a/pkg/registry/apis/provisioning/resources/tree.go b/pkg/registry/apis/provisioning/resources/tree.go index b76c0886c77..89f0663bca0 100644 --- a/pkg/registry/apis/provisioning/resources/tree.go +++ b/pkg/registry/apis/provisioning/resources/tree.go @@ -104,7 +104,8 @@ func (t *FolderTree) AddUnstructured(item *unstructured.Unstructured, skipRepo s if err != nil { return fmt.Errorf("extract meta accessor: %w", err) } - if meta.GetRepositoryName() == skipRepo { + manager, _ := meta.GetManagerProperties() + if manager.Identity == skipRepo { return nil // skip it... already in tree? } folder := Folder{ diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index 89f15d33a80..0905eade7b3 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -44,10 +44,10 @@ export const AnnoKeyMessage = 'grafana.app/message'; export const AnnoKeySlug = 'grafana.app/slug'; // Identify where values came from -export const AnnoKeyRepoName = 'grafana.app/repoName'; -export const AnnoKeyRepoPath = 'grafana.app/repoPath'; -export const AnnoKeyRepoHash = 'grafana.app/repoHash'; -export const AnnoKeyRepoTimestamp = 'grafana.app/repoTimestamp'; +export const AnnoKeyManagerKind = 'grafana.app/managerKind'; +export const AnnoKeyManagerIdentity = 'grafana.app/managerIdentity'; +export const AnnoKeySourcePath = 'grafana.app/sourcePath'; +export const AnnoKeySourceChecksum = 'grafana.app/sourceChecksum'; export const AnnoKeySavedFromUI = 'grafana.app/saved-from-ui'; export const AnnoKeyDashboardNotFound = 'grafana.app/dashboard-not-found'; @@ -66,10 +66,10 @@ type GrafanaAnnotations = { [AnnoKeyFolder]?: string; [AnnoKeySlug]?: string; - [AnnoKeyRepoName]?: string; - [AnnoKeyRepoPath]?: string; - [AnnoKeyRepoHash]?: string; - [AnnoKeyRepoTimestamp]?: string; + [AnnoKeyManagerKind]?: string; + [AnnoKeyManagerIdentity]?: string; + [AnnoKeySourcePath]?: string; + [AnnoKeySourceChecksum]?: string; }; // Annotations provided by the front-end client diff --git a/public/app/features/dashboard-scene/saving/provisioned/hooks.ts b/public/app/features/dashboard-scene/saving/provisioned/hooks.ts index f9991c18e4d..65ecc44f82f 100644 --- a/public/app/features/dashboard-scene/saving/provisioned/hooks.ts +++ b/public/app/features/dashboard-scene/saving/provisioned/hooks.ts @@ -1,7 +1,7 @@ import { Chance } from 'chance'; import { dateTime } from '@grafana/data'; -import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types'; +import { AnnoKeyManagerKind, AnnoKeyManagerIdentity, AnnoKeySourcePath } from 'app/features/apiserver/types'; import { useGetResourceRepository } from 'app/features/provisioning/hooks'; import { DashboardMeta } from 'app/types'; @@ -25,10 +25,12 @@ function generatePath(timestamp: string, pathFromAnnotation?: string, slug?: str export function useDefaultValues({ meta, defaultTitle, defaultDescription }: UseDefaultValuesParams) { const annotations = meta.k8s?.annotations; - const annoName = annotations?.[AnnoKeyRepoName]; - const annoPath = annotations?.[AnnoKeyRepoPath]; + const managerKind = annotations?.[AnnoKeyManagerKind]; + const managerId = annotations?.[AnnoKeyManagerIdentity]; + const sourcePath = annotations?.[AnnoKeySourcePath]; // Get config by resource name or folder UID for new resources - const repositoryConfig = useGetResourceRepository({ name: annoName, folderUid: meta.folderUid }); + const repositoryConfig = + managerKind === 'repo' ? useGetResourceRepository({ name: managerId, folderUid: meta.folderUid }) : undefined; const repository = repositoryConfig?.spec; const random = Chance(1); @@ -37,8 +39,8 @@ export function useDefaultValues({ meta, defaultTitle, defaultDescription }: Use return { values: { ref: `dashboard/${timestamp}`, - path: generatePath(timestamp, annoPath, meta.slug), - repo: annoName || repositoryConfig?.metadata?.name || '', + path: generatePath(timestamp, sourcePath, meta.slug), + repo: managerId || repositoryConfig?.metadata?.name || '', comment: '', folder: { uid: meta.folderUid, @@ -48,7 +50,7 @@ export function useDefaultValues({ meta, defaultTitle, defaultDescription }: Use description: defaultDescription ?? '', workflow: getDefaultWorkflow(repository), }, - isNew: !annoName, + isNew: !managerId, repositoryConfig: repository, isGitHub: repository?.type === 'github', }; diff --git a/public/app/features/provisioning/dashboard.ts b/public/app/features/provisioning/dashboard.ts index 532ee35dbec..1d7ecd5809b 100644 --- a/public/app/features/provisioning/dashboard.ts +++ b/public/app/features/provisioning/dashboard.ts @@ -1,7 +1,7 @@ import { getBackendSrv } from '@grafana/runtime'; import { DashboardDTO } from 'app/types'; -import { AnnoKeyRepoName, AnnoKeyRepoPath } from '../apiserver/types'; +import { AnnoKeyManagerIdentity, AnnoKeySourcePath } from '../apiserver/types'; import { BASE_URL } from './api/baseAPI'; @@ -32,10 +32,10 @@ export async function loadDashboardFromProvisioning(repo: string, path: string): if (!anno) { dryRun.metadata.annotations = anno = {}; } - anno[AnnoKeyRepoName] = repo; - anno[AnnoKeyRepoPath] = path; + anno[AnnoKeyManagerIdentity] = repo; + anno[AnnoKeySourcePath] = path; if (ref) { - anno[AnnoKeyRepoPath] = path + '#' + ref; + anno[AnnoKeySourcePath] = path + '#' + ref; } return {