mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Provisioning: Rename k8s origin metadata to repo (#96524)
This commit is contained in:
parent
2e62f75166
commit
cc6d057a18
@ -540,8 +540,8 @@ func TestHTTPServer_FolderMetadataK8s(t *testing.T) {
|
||||
"creationTimestamp": "2024-09-17T04:16:35Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:fdxsqt7t5ryf4a",
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originPath": "3"
|
||||
"grafana.app/repoName": "SQL",
|
||||
"grafana.app/repoPath": "3"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -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 {
|
||||
|
@ -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"))
|
||||
|
@ -10,10 +10,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
@ -286,14 +287,14 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: origin_name.String,
|
||||
Path: originPath,
|
||||
Hash: origin_hash.String,
|
||||
Timestamp: &ts,
|
||||
})
|
||||
} else if plugin_id != "" {
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "plugin",
|
||||
Path: plugin_id,
|
||||
})
|
||||
@ -516,7 +517,7 @@ func (a *dashboardSqlAccess) GetLibraryPanels(ctx context.Context, query Library
|
||||
meta.SetCreatedBy(p.CreatedBy)
|
||||
meta.SetUpdatedBy(p.UpdatedBy)
|
||||
meta.SetUpdatedTimestamp(&p.Updated)
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: strconv.FormatInt(p.ID, 10),
|
||||
})
|
||||
|
@ -108,12 +108,12 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob
|
||||
UID: name,
|
||||
OrgID: info.OrgID,
|
||||
}
|
||||
origin, err := obj.GetOriginInfo()
|
||||
repo, err := obj.GetRepositoryInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if origin != nil && origin.Name == "SQL" {
|
||||
dto.ID, err = strconv.ParseInt(origin.Path, 10, 64)
|
||||
if repo != nil && repo.Name == "SQL" {
|
||||
dto.ID, err = strconv.ParseInt(repo.Path, 10, 64)
|
||||
if err == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
||||
|
||||
meta.SetUpdatedTimestamp(&v.Updated)
|
||||
if v.ID > 0 { // nolint:staticcheck
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: fmt.Sprintf("%d", v.ID), // nolint:staticcheck
|
||||
Timestamp: &v.Created,
|
||||
@ -187,13 +187,13 @@ func setParentUID(u *unstructured.Unstructured, parentUid string) error {
|
||||
func getLegacyID(meta utils.GrafanaMetaAccessor) (int64, error) {
|
||||
var i int64
|
||||
|
||||
info, err := meta.GetOriginInfo()
|
||||
repo, err := meta.GetRepositoryInfo()
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
if info != nil && info.Name == "SQL" {
|
||||
i, err = strconv.ParseInt(info.Path, 10, 64)
|
||||
if repo != nil && repo.Name == "SQL" {
|
||||
i, err = strconv.ParseInt(repo.Path, 10, 64)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
@ -208,7 +208,7 @@ func getURL(meta utils.GrafanaMetaAccessor, title string) string {
|
||||
}
|
||||
|
||||
func getCreated(meta utils.GrafanaMetaAccessor) (*time.Time, error) {
|
||||
created, err := meta.GetOriginTimestamp()
|
||||
created, err := meta.GetRepositoryTimestamp()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func toSAItem(sa legacy.ServiceAccount, ns string) iamv0.ServiceAccount {
|
||||
}
|
||||
obj, _ := utils.MetaAccessor(&item)
|
||||
obj.SetUpdatedTimestamp(&sa.Updated)
|
||||
obj.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
obj.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: strconv.FormatInt(sa.ID, 10),
|
||||
})
|
||||
|
@ -136,7 +136,7 @@ func toTeamObject(t team.Team, ns claims.NamespaceInfo) iamv0.Team {
|
||||
}
|
||||
meta, _ := utils.MetaAccessor(&obj)
|
||||
meta.SetUpdatedTimestamp(&t.Updated)
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: strconv.FormatInt(t.ID, 10),
|
||||
})
|
||||
|
@ -137,7 +137,7 @@ func toUserItem(u *user.User, ns string) iamv0.User {
|
||||
}
|
||||
obj, _ := utils.MetaAccessor(item)
|
||||
obj.SetUpdatedTimestamp(&u.Updated)
|
||||
obj.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
obj.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: strconv.FormatInt(u.ID, 10),
|
||||
})
|
||||
|
@ -97,7 +97,7 @@ func convertToK8sResource(v *playlistsvc.PlaylistDTO, namespacer request.Namespa
|
||||
meta.SetUpdatedTimestampMillis(v.UpdatedAt)
|
||||
if v.Id > 0 {
|
||||
createdAt := time.UnixMilli(v.CreatedAt)
|
||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
||||
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||
Name: "SQL",
|
||||
Path: fmt.Sprintf("%d", v.Id),
|
||||
Timestamp: &createdAt,
|
||||
@ -135,7 +135,7 @@ func getLegacyID(item *unstructured.Unstructured) int64 {
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
info, _ := meta.GetOriginInfo()
|
||||
info, _ := meta.GetRepositoryInfo()
|
||||
if info != nil && info.Name == "SQL" {
|
||||
i, err := strconv.ParseInt(info.Path, 10, 64)
|
||||
if err == nil {
|
||||
|
@ -42,9 +42,9 @@ func TestPlaylistConversion(t *testing.T) {
|
||||
"resourceVersion": "54321",
|
||||
"creationTimestamp": "1970-01-01T00:00:12Z",
|
||||
"annotations": {
|
||||
"grafana.app/originPath": "123",
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originTimestamp":"1970-01-01T00:00:12Z",
|
||||
"grafana.app/repoPath": "123",
|
||||
"grafana.app/repoName": "SQL",
|
||||
"grafana.app/repoTimestamp":"1970-01-01T00:00:12Z",
|
||||
"grafana.app/updatedTimestamp": "1970-01-01T00:00:54Z"
|
||||
}
|
||||
},
|
||||
|
@ -60,12 +60,12 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime
|
||||
obj.SetResourceVersion("")
|
||||
obj.SetSelfLink("")
|
||||
|
||||
// Read+write will verify that origin format is accurate
|
||||
origin, err := obj.GetOriginInfo()
|
||||
// Read+write will verify that repository format is accurate
|
||||
repo, err := obj.GetRepositoryInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj.SetOriginInfo(origin)
|
||||
obj.SetRepositoryInfo(repo)
|
||||
obj.SetUpdatedBy("")
|
||||
obj.SetUpdatedTimestamp(nil)
|
||||
obj.SetCreatedBy(user.GetUID())
|
||||
@ -115,11 +115,11 @@ func (s *Storage) prepareObjectForUpdate(ctx context.Context, updateObject runti
|
||||
obj.SetResourceVersion("") // removed from saved JSON because the RV is not yet calculated
|
||||
|
||||
// Read+write will verify that origin format is accurate
|
||||
origin, err := obj.GetOriginInfo()
|
||||
repo, err := obj.GetRepositoryInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj.SetOriginInfo(origin)
|
||||
obj.SetRepositoryInfo(repo)
|
||||
obj.SetUpdatedBy(user.GetUID())
|
||||
obj.SetUpdatedTimestampMillis(time.Now().UnixMilli())
|
||||
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
type WriteAccessHooks struct {
|
||||
// When configured, this will make sure a user is allowed to save to a given origin
|
||||
Origin func(ctx context.Context, user claims.AuthInfo, origin string) bool
|
||||
CanWriteValueFromRepoCheck func(ctx context.Context, user claims.AuthInfo, origin string) bool
|
||||
}
|
||||
|
||||
type LifecycleHooks interface {
|
||||
@ -20,11 +20,11 @@ type LifecycleHooks interface {
|
||||
Stop(context.Context) error
|
||||
}
|
||||
|
||||
func (a *WriteAccessHooks) CanWriteOrigin(ctx context.Context, user claims.AuthInfo, uid string) error {
|
||||
if a.Origin == nil || uid == "UI" {
|
||||
func (a *WriteAccessHooks) CanWriteValueFromRepository(ctx context.Context, user claims.AuthInfo, uid string) error {
|
||||
if a.CanWriteValueFromRepoCheck == nil || uid == "UI" {
|
||||
return nil // default to OK
|
||||
}
|
||||
if !a.Origin(ctx, user, uid) {
|
||||
if !a.CanWriteValueFromRepoCheck(ctx, user, uid) {
|
||||
return fmt.Errorf("not allowed to write resource at origin")
|
||||
}
|
||||
return nil
|
||||
|
@ -394,12 +394,12 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour
|
||||
}
|
||||
}
|
||||
|
||||
origin, err := obj.GetOriginInfo()
|
||||
repo, err := obj.GetRepositoryInfo()
|
||||
if err != nil {
|
||||
return nil, NewBadRequestError("invalid origin info")
|
||||
return nil, NewBadRequestError("invalid repository info")
|
||||
}
|
||||
if origin != nil {
|
||||
err = s.writeHooks.CanWriteOrigin(ctx, user, origin.Name)
|
||||
if repo != nil {
|
||||
err = s.writeHooks.CanWriteValueFromRepository(ctx, user, repo.Name)
|
||||
if err != nil {
|
||||
return nil, AsErrorResult(err)
|
||||
}
|
||||
|
@ -60,9 +60,9 @@ func TestSimpleServer(t *testing.T) {
|
||||
"uid": "xyz",
|
||||
"namespace": "default",
|
||||
"annotations": {
|
||||
"grafana.app/originName": "elsewhere",
|
||||
"grafana.app/originPath": "path/to/item",
|
||||
"grafana.app/originTimestamp": "2024-02-02T00:00:00Z"
|
||||
"grafana.app/repoName": "elsewhere",
|
||||
"grafana.app/repoPath": "path/to/item",
|
||||
"grafana.app/repoTimestamp": "2024-02-02T00:00:00Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
@ -176,9 +176,9 @@ func TestSimpleServer(t *testing.T) {
|
||||
"namespace": "default",
|
||||
"uid": "xyz",
|
||||
"annotations": {
|
||||
"grafana.app/originName": "elsewhere",
|
||||
"grafana.app/originPath": "path/to/item",
|
||||
"grafana.app/originTimestamp": "2024-02-02T00:00:00Z"
|
||||
"grafana.app/repoName": "elsewhere",
|
||||
"grafana.app/repoPath": "path/to/item",
|
||||
"grafana.app/repoTimestamp": "2024-02-02T00:00:00Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -8,9 +8,9 @@
|
||||
"creationTimestamp": "2024-10-30T18:30:54Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
||||
"grafana.app/originName": "UI",
|
||||
"grafana.app/originPath": "/dashboard/new"
|
||||
"grafana.app/repoHash": "Grafana v9.2.0 (NA)",
|
||||
"grafana.app/repoName": "UI",
|
||||
"grafana.app/repoPath": "/dashboard/new"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
@ -23,9 +23,9 @@
|
||||
"f:metadata": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:grafana.app/originHash": {},
|
||||
"f:grafana.app/originName": {},
|
||||
"f:grafana.app/originPath": {}
|
||||
"f:grafana.app/repoHash": {},
|
||||
"f:grafana.app/repoName": {},
|
||||
"f:grafana.app/repoPath": {}
|
||||
},
|
||||
"f:generateName": {}
|
||||
},
|
||||
|
@ -7,10 +7,7 @@
|
||||
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956",
|
||||
"creationTimestamp": "2024-10-30T20:24:07Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
||||
"grafana.app/originName": "UI",
|
||||
"grafana.app/originPath": "/dashboard/new"
|
||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
|
@ -7,10 +7,7 @@
|
||||
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956-2",
|
||||
"creationTimestamp": "2024-10-30T20:24:07Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
||||
"grafana.app/originName": "UI",
|
||||
"grafana.app/originPath": "/dashboard/new"
|
||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
|
@ -7,10 +7,7 @@
|
||||
"uid": "aaaa-bbbb",
|
||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:1",
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originPath": "15",
|
||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
||||
"grafana.app/createdBy": "user:1"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -8,9 +8,9 @@
|
||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:1",
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originPath": "15",
|
||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
||||
"grafana.app/repoName": "SQL",
|
||||
"grafana.app/repoPath": "15",
|
||||
"grafana.app/repoTimestamp": "2024-11-01T19:42:22Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -8,9 +8,9 @@
|
||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:1",
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originPath": "15",
|
||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
||||
"grafana.app/repoName": "SQL",
|
||||
"grafana.app/repoPath": "15",
|
||||
"grafana.app/repoTimestamp": "2024-11-01T19:42:22Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -11,9 +11,6 @@ import {
|
||||
ResourceList,
|
||||
ResourceClient,
|
||||
ObjectMeta,
|
||||
AnnoKeyOriginPath,
|
||||
AnnoKeyOriginHash,
|
||||
AnnoKeyOriginName,
|
||||
K8sAPIGroupList,
|
||||
} from './types';
|
||||
|
||||
@ -55,12 +52,12 @@ export class ScopedResourceClient<T = object, K = string> implements ResourceCli
|
||||
// THe passed in value is the suggested prefix
|
||||
obj.metadata.generateName = login ? login.slice(0, 2) : 'g';
|
||||
}
|
||||
setOriginAsUI(obj.metadata);
|
||||
setSavedFromUIAnnotation(obj.metadata);
|
||||
return getBackendSrv().post(this.url, obj);
|
||||
}
|
||||
|
||||
public async update(obj: Resource<T, K>): Promise<Resource<T, K>> {
|
||||
setOriginAsUI(obj.metadata);
|
||||
setSavedFromUIAnnotation(obj.metadata);
|
||||
return getBackendSrv().put<Resource<T, K>>(`${this.url}/${obj.metadata.name}`, obj);
|
||||
}
|
||||
|
||||
@ -103,13 +100,11 @@ export class ScopedResourceClient<T = object, K = string> implements ResourceCli
|
||||
}
|
||||
|
||||
// add the origin annotations so we know what was set from the UI
|
||||
function setOriginAsUI(meta: Partial<ObjectMeta>) {
|
||||
function setSavedFromUIAnnotation(meta: Partial<ObjectMeta>) {
|
||||
if (!meta.annotations) {
|
||||
meta.annotations = {};
|
||||
}
|
||||
meta.annotations[AnnoKeyOriginName] = 'UI';
|
||||
meta.annotations[AnnoKeyOriginPath] = window.location.pathname;
|
||||
meta.annotations[AnnoKeyOriginHash] = config.buildInfo.versionString;
|
||||
meta.annotations['grafana.app/saved-from-ui'] = config.buildInfo.versionString;
|
||||
}
|
||||
|
||||
export class DatasourceAPIVersions {
|
||||
|
@ -37,10 +37,10 @@ export const AnnoKeyMessage = 'grafana.app/message';
|
||||
export const AnnoKeySlug = 'grafana.app/slug';
|
||||
|
||||
// Identify where values came from
|
||||
export const AnnoKeyOriginName = 'grafana.app/originName';
|
||||
export const AnnoKeyOriginPath = 'grafana.app/originPath';
|
||||
export const AnnoKeyOriginHash = 'grafana.app/originHash';
|
||||
const AnnoKeyOriginTimestamp = 'grafana.app/originTimestamp';
|
||||
export const AnnoKeyRepoName = 'grafana.app/repoName';
|
||||
export const AnnoKeyRepoPath = 'grafana.app/repoPath';
|
||||
export const AnnoKeyRepoHash = 'grafana.app/repoHash';
|
||||
const AnnoKeyRepoTimestamp = 'grafana.app/repoTimestamp';
|
||||
|
||||
type GrafanaAnnotations = {
|
||||
[AnnoKeyCreatedBy]?: string;
|
||||
@ -49,10 +49,10 @@ type GrafanaAnnotations = {
|
||||
[AnnoKeyFolder]?: string;
|
||||
[AnnoKeySlug]?: string;
|
||||
|
||||
[AnnoKeyOriginName]?: string;
|
||||
[AnnoKeyOriginPath]?: string;
|
||||
[AnnoKeyOriginHash]?: string;
|
||||
[AnnoKeyOriginTimestamp]?: string;
|
||||
[AnnoKeyRepoName]?: string;
|
||||
[AnnoKeyRepoPath]?: string;
|
||||
[AnnoKeyRepoHash]?: string;
|
||||
[AnnoKeyRepoTimestamp]?: string;
|
||||
|
||||
// Any key value
|
||||
[key: string]: string | undefined;
|
||||
|
Loading…
Reference in New Issue
Block a user