Provisioning: Rename k8s origin metadata to repo (#96524)

This commit is contained in:
Ryan McKinley
2024-11-15 17:26:14 +03:00
committed by GitHub
parent 2e62f75166
commit cc6d057a18
23 changed files with 198 additions and 154 deletions
+79 -45
View File
@@ -29,23 +29,35 @@ const AnnoKeyMessage = "grafana.app/message"
// Identify where values came from
const AnnoKeyOriginName = "grafana.app/originName"
const AnnoKeyOriginPath = "grafana.app/originPath"
const AnnoKeyOriginHash = "grafana.app/originHash"
const AnnoKeyOriginTimestamp = "grafana.app/originTimestamp"
const AnnoKeyRepoName = "grafana.app/repoName"
const AnnoKeyRepoPath = "grafana.app/repoPath"
const AnnoKeyRepoHash = "grafana.app/repoHash"
const AnnoKeyRepoTimestamp = "grafana.app/repoTimestamp"
// #TODO revisit keeping these folder-specific annotations once we have complete support for mode 1
// These can be removed once we verify that non of the dual-write sources
// (for dashboards/playlists/etc) depend on the saved internal ID in SQL
const oldAnnoKeyOriginName = "grafana.app/originName"
const oldAnnoKeyOriginPath = "grafana.app/originPath"
const oldAnnoKeyOriginHash = "grafana.app/originHash"
const oldAnnoKeyOriginTimestamp = "grafana.app/originTimestamp"
const AnnoKeyFullPath = "grafana.app/fullPath"
const AnnoKeyFullPathUIDs = "grafana.app/fullPathUIDs"
// annoKeyFullPath encodes the full path in folder resources
// revisit keeping these folder-specific annotations once we have complete support for mode 1
// Deprecated: this goes away when folders have a better solution
const annoKeyFullPath = "grafana.app/fullPath"
// ResourceOriginInfo is saved in annotations. This is used to identify where the resource came from
// This object can model the same data as our existing provisioning table or a more general git sync
type ResourceOriginInfo struct {
// Name of the origin/provisioning source
// annoKeyFullPathUIDs encodes the full path in folder resources
// Deprecated: this goes away when folders have a better solution
const annoKeyFullPathUIDs = "grafana.app/fullPathUIDs"
// ResourceRepositoryInfo is encoded into kubernetes metadata annotations.
// This value identifies indicates the state of the resource in its provisioning source when
// the spec was last saved. Currently this is derived from the dashboards provisioning table.
type ResourceRepositoryInfo struct {
// Name of the repository/provisioning source
Name string `json:"name,omitempty"`
// The path within the named origin above (external_id in the existing dashboard provisioing)
// The path within the named repository above (external_id in the existing dashboard provisioning)
Path string `json:"path,omitempty"`
// Verification/identification hash (check_sum in existing dashboard provisioning)
@@ -90,12 +102,12 @@ type GrafanaMetaAccessor interface {
SetBlob(v *BlobInfo)
GetBlob() *BlobInfo
GetOriginInfo() (*ResourceOriginInfo, error)
SetOriginInfo(info *ResourceOriginInfo)
GetOriginName() string
GetOriginPath() string
GetOriginHash() string
GetOriginTimestamp() (*time.Time, error)
GetRepositoryInfo() (*ResourceRepositoryInfo, error)
SetRepositoryInfo(info *ResourceRepositoryInfo)
GetRepositoryName() string
GetRepositoryPath() string
GetRepositoryHash() string
GetRepositoryTimestamp() (*time.Time, error)
GetSpec() (any, error)
SetSpec(any) error
@@ -271,7 +283,16 @@ func (m *grafanaMetaAccessor) SetSlug(v string) {
m.SetAnnotation(AnnoKeySlug, v)
}
func (m *grafanaMetaAccessor) SetOriginInfo(info *ResourceOriginInfo) {
// This allows looking up a primary and secondary key -- if either exist the value will be returned
func (m *grafanaMetaAccessor) getAnnoValue(primary, secondary string) (string, bool) {
v, ok := m.obj.GetAnnotations()[primary]
if !ok {
v, ok = m.obj.GetAnnotations()[secondary]
}
return v, ok
}
func (m *grafanaMetaAccessor) SetRepositoryInfo(info *ResourceRepositoryInfo) {
anno := m.obj.GetAnnotations()
if anno == nil {
if info == nil {
@@ -280,53 +301,62 @@ func (m *grafanaMetaAccessor) SetOriginInfo(info *ResourceOriginInfo) {
anno = make(map[string]string, 0)
}
delete(anno, AnnoKeyOriginName)
delete(anno, AnnoKeyOriginPath)
delete(anno, AnnoKeyOriginHash)
delete(anno, AnnoKeyOriginTimestamp)
// remove legacy values
delete(anno, oldAnnoKeyOriginHash)
delete(anno, oldAnnoKeyOriginPath)
delete(anno, oldAnnoKeyOriginHash)
delete(anno, oldAnnoKeyOriginTimestamp)
delete(anno, AnnoKeyRepoName)
delete(anno, AnnoKeyRepoPath)
delete(anno, AnnoKeyRepoHash)
delete(anno, AnnoKeyRepoTimestamp)
if info != nil && info.Name != "" {
anno[AnnoKeyOriginName] = info.Name
anno[AnnoKeyRepoName] = info.Name
if info.Path != "" {
anno[AnnoKeyOriginPath] = info.Path
anno[AnnoKeyRepoPath] = info.Path
}
if info.Hash != "" {
anno[AnnoKeyOriginHash] = info.Hash
anno[AnnoKeyRepoHash] = info.Hash
}
if info.Timestamp != nil {
anno[AnnoKeyOriginTimestamp] = info.Timestamp.UTC().Format(time.RFC3339)
anno[AnnoKeyRepoTimestamp] = info.Timestamp.UTC().Format(time.RFC3339)
}
}
m.obj.SetAnnotations(anno)
}
func (m *grafanaMetaAccessor) GetOriginInfo() (*ResourceOriginInfo, error) {
v, ok := m.obj.GetAnnotations()[AnnoKeyOriginName]
func (m *grafanaMetaAccessor) GetRepositoryInfo() (*ResourceRepositoryInfo, error) {
v, ok := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName)
if !ok {
return nil, nil
}
t, err := m.GetOriginTimestamp()
return &ResourceOriginInfo{
t, err := m.GetRepositoryTimestamp()
return &ResourceRepositoryInfo{
Name: v,
Path: m.GetOriginPath(),
Hash: m.GetOriginHash(),
Path: m.GetRepositoryPath(),
Hash: m.GetRepositoryHash(),
Timestamp: t,
}, err
}
func (m *grafanaMetaAccessor) GetOriginName() string {
return m.get(AnnoKeyOriginName)
func (m *grafanaMetaAccessor) GetRepositoryName() string {
v, _ := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName)
return v // will be empty string
}
func (m *grafanaMetaAccessor) GetOriginPath() string {
return m.get(AnnoKeyOriginPath)
func (m *grafanaMetaAccessor) GetRepositoryPath() string {
v, _ := m.getAnnoValue(AnnoKeyRepoPath, oldAnnoKeyOriginPath)
return v // will be empty string
}
func (m *grafanaMetaAccessor) GetOriginHash() string {
return m.get(AnnoKeyOriginHash)
func (m *grafanaMetaAccessor) GetRepositoryHash() string {
v, _ := m.getAnnoValue(AnnoKeyRepoHash, oldAnnoKeyOriginHash)
return v // will be empty string
}
func (m *grafanaMetaAccessor) GetOriginTimestamp() (*time.Time, error) {
v, ok := m.obj.GetAnnotations()[AnnoKeyOriginTimestamp]
func (m *grafanaMetaAccessor) GetRepositoryTimestamp() (*time.Time, error) {
v, ok := m.getAnnoValue(AnnoKeyRepoTimestamp, oldAnnoKeyOriginTimestamp)
if !ok || v == "" {
return nil, nil
}
@@ -617,19 +647,23 @@ func (m *grafanaMetaAccessor) SetStatus(s any) (err error) {
}
func (m *grafanaMetaAccessor) GetFullPath() string {
return m.get(AnnoKeyFullPath)
// nolint:staticcheck
return m.get(annoKeyFullPath)
}
func (m *grafanaMetaAccessor) SetFullPath(path string) {
m.SetAnnotation(AnnoKeyFullPath, path)
// nolint:staticcheck
m.SetAnnotation(annoKeyFullPath, path)
}
func (m *grafanaMetaAccessor) GetFullPathUIDs() string {
return m.get(AnnoKeyFullPathUIDs)
// nolint:staticcheck
return m.get(annoKeyFullPathUIDs)
}
func (m *grafanaMetaAccessor) SetFullPathUIDs(path string) {
m.SetAnnotation(AnnoKeyFullPathUIDs, path)
// nolint:staticcheck
m.SetAnnotation(annoKeyFullPathUIDs, path)
}
func (m *grafanaMetaAccessor) FindTitle(defaultTitle string) string {
+50 -27
View File
@@ -4,11 +4,12 @@ import (
"encoding/json"
"testing"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
type TestResource struct {
@@ -129,7 +130,7 @@ func (in *Spec2) DeepCopy() *Spec2 {
}
func TestMetaAccessor(t *testing.T) {
originInfo := &utils.ResourceOriginInfo{
repoInfo := &utils.ResourceRepositoryInfo{
Name: "test",
Path: "a/b/c",
Hash: "kkk",
@@ -177,14 +178,14 @@ func TestMetaAccessor(t *testing.T) {
},
}
meta.SetOriginInfo(originInfo)
meta.SetRepositoryInfo(repoInfo)
meta.SetFolder("folderUID")
require.Equal(t, map[string]string{
"grafana.app/originName": "test",
"grafana.app/originPath": "a/b/c",
"grafana.app/originHash": "kkk",
"grafana.app/folder": "folderUID",
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "kkk",
"grafana.app/folder": "folderUID",
}, res.GetAnnotations())
meta.SetNamespace("aaa")
@@ -229,14 +230,14 @@ func TestMetaAccessor(t *testing.T) {
meta, err := utils.MetaAccessor(res)
require.NoError(t, err)
meta.SetOriginInfo(originInfo)
meta.SetRepositoryInfo(repoInfo)
meta.SetFolder("folderUID")
require.Equal(t, map[string]string{
"grafana.app/originName": "test",
"grafana.app/originPath": "a/b/c",
"grafana.app/originHash": "kkk",
"grafana.app/folder": "folderUID",
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "kkk",
"grafana.app/folder": "folderUID",
}, res.GetAnnotations())
meta.SetNamespace("aaa")
@@ -280,14 +281,14 @@ func TestMetaAccessor(t *testing.T) {
meta, err := utils.MetaAccessor(res)
require.NoError(t, err)
meta.SetOriginInfo(originInfo)
meta.SetRepositoryInfo(repoInfo)
meta.SetFolder("folderUID")
require.Equal(t, map[string]string{
"grafana.app/originName": "test",
"grafana.app/originPath": "a/b/c",
"grafana.app/originHash": "kkk",
"grafana.app/folder": "folderUID",
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "kkk",
"grafana.app/folder": "folderUID",
}, res.GetAnnotations())
meta.SetNamespace("aaa")
@@ -321,6 +322,28 @@ func TestMetaAccessor(t *testing.T) {
require.Equal(t, "ZZ", res.Status.Title)
})
t.Run("test reading old originInfo (now repository)", func(t *testing.T) {
res := &TestResource2{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "zzz",
"grafana.app/folder": "folderUID",
},
},
Spec: Spec2{},
}
meta, err := utils.MetaAccessor(res)
require.NoError(t, err)
info, err := meta.GetRepositoryInfo()
require.NoError(t, err)
require.Equal(t, "test", info.Name)
require.Equal(t, "a/b/c", info.Path)
require.Equal(t, "zzz", info.Hash)
})
t.Run("blob info", func(t *testing.T) {
info := &utils.BlobInfo{UID: "AAA", Size: 123, Hash: "xyz", MimeType: "application/json", Charset: "utf-8"}
anno := info.String()
@@ -343,14 +366,14 @@ func TestMetaAccessor(t *testing.T) {
meta, err := utils.MetaAccessor(obj)
require.NoError(t, err)
meta.SetOriginInfo(originInfo)
meta.SetRepositoryInfo(repoInfo)
meta.SetFolder("folderUID")
require.Equal(t, map[string]string{
"grafana.app/originName": "test",
"grafana.app/originPath": "a/b/c",
"grafana.app/originHash": "kkk",
"grafana.app/folder": "folderUID",
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "kkk",
"grafana.app/folder": "folderUID",
}, obj.GetAnnotations())
require.Equal(t, "HELLO", obj.Spec.Title)
@@ -366,14 +389,14 @@ func TestMetaAccessor(t *testing.T) {
meta, err = utils.MetaAccessor(obj2)
require.NoError(t, err)
meta.SetOriginInfo(originInfo)
meta.SetRepositoryInfo(repoInfo)
meta.SetFolder("folderUID")
require.Equal(t, map[string]string{
"grafana.app/originName": "test",
"grafana.app/originPath": "a/b/c",
"grafana.app/originHash": "kkk",
"grafana.app/folder": "folderUID",
"grafana.app/repoName": "test",
"grafana.app/repoPath": "a/b/c",
"grafana.app/repoHash": "kkk",
"grafana.app/folder": "folderUID",
}, obj2.GetAnnotations())
require.Equal(t, "xxx", meta.FindTitle("xxx"))