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",
|
"creationTimestamp": "2024-09-17T04:16:35Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:fdxsqt7t5ryf4a",
|
"grafana.app/createdBy": "user:fdxsqt7t5ryf4a",
|
||||||
"grafana.app/originName": "SQL",
|
"grafana.app/repoName": "SQL",
|
||||||
"grafana.app/originPath": "3"
|
"grafana.app/repoPath": "3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -29,23 +29,35 @@ const AnnoKeyMessage = "grafana.app/message"
|
|||||||
|
|
||||||
// Identify where values came from
|
// Identify where values came from
|
||||||
|
|
||||||
const AnnoKeyOriginName = "grafana.app/originName"
|
const AnnoKeyRepoName = "grafana.app/repoName"
|
||||||
const AnnoKeyOriginPath = "grafana.app/originPath"
|
const AnnoKeyRepoPath = "grafana.app/repoPath"
|
||||||
const AnnoKeyOriginHash = "grafana.app/originHash"
|
const AnnoKeyRepoHash = "grafana.app/repoHash"
|
||||||
const AnnoKeyOriginTimestamp = "grafana.app/originTimestamp"
|
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"
|
// annoKeyFullPath encodes the full path in folder resources
|
||||||
const AnnoKeyFullPathUIDs = "grafana.app/fullPathUIDs"
|
// 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
|
// annoKeyFullPathUIDs encodes the full path in folder resources
|
||||||
// This object can model the same data as our existing provisioning table or a more general git sync
|
// Deprecated: this goes away when folders have a better solution
|
||||||
type ResourceOriginInfo struct {
|
const annoKeyFullPathUIDs = "grafana.app/fullPathUIDs"
|
||||||
// Name of the origin/provisioning source
|
|
||||||
|
// 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"`
|
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"`
|
Path string `json:"path,omitempty"`
|
||||||
|
|
||||||
// Verification/identification hash (check_sum in existing dashboard provisioning)
|
// Verification/identification hash (check_sum in existing dashboard provisioning)
|
||||||
@ -90,12 +102,12 @@ type GrafanaMetaAccessor interface {
|
|||||||
SetBlob(v *BlobInfo)
|
SetBlob(v *BlobInfo)
|
||||||
GetBlob() *BlobInfo
|
GetBlob() *BlobInfo
|
||||||
|
|
||||||
GetOriginInfo() (*ResourceOriginInfo, error)
|
GetRepositoryInfo() (*ResourceRepositoryInfo, error)
|
||||||
SetOriginInfo(info *ResourceOriginInfo)
|
SetRepositoryInfo(info *ResourceRepositoryInfo)
|
||||||
GetOriginName() string
|
GetRepositoryName() string
|
||||||
GetOriginPath() string
|
GetRepositoryPath() string
|
||||||
GetOriginHash() string
|
GetRepositoryHash() string
|
||||||
GetOriginTimestamp() (*time.Time, error)
|
GetRepositoryTimestamp() (*time.Time, error)
|
||||||
|
|
||||||
GetSpec() (any, error)
|
GetSpec() (any, error)
|
||||||
SetSpec(any) error
|
SetSpec(any) error
|
||||||
@ -271,7 +283,16 @@ func (m *grafanaMetaAccessor) SetSlug(v string) {
|
|||||||
m.SetAnnotation(AnnoKeySlug, v)
|
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()
|
anno := m.obj.GetAnnotations()
|
||||||
if anno == nil {
|
if anno == nil {
|
||||||
if info == nil {
|
if info == nil {
|
||||||
@ -280,53 +301,62 @@ func (m *grafanaMetaAccessor) SetOriginInfo(info *ResourceOriginInfo) {
|
|||||||
anno = make(map[string]string, 0)
|
anno = make(map[string]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(anno, AnnoKeyOriginName)
|
// remove legacy values
|
||||||
delete(anno, AnnoKeyOriginPath)
|
delete(anno, oldAnnoKeyOriginHash)
|
||||||
delete(anno, AnnoKeyOriginHash)
|
delete(anno, oldAnnoKeyOriginPath)
|
||||||
delete(anno, AnnoKeyOriginTimestamp)
|
delete(anno, oldAnnoKeyOriginHash)
|
||||||
|
delete(anno, oldAnnoKeyOriginTimestamp)
|
||||||
|
|
||||||
|
delete(anno, AnnoKeyRepoName)
|
||||||
|
delete(anno, AnnoKeyRepoPath)
|
||||||
|
delete(anno, AnnoKeyRepoHash)
|
||||||
|
delete(anno, AnnoKeyRepoTimestamp)
|
||||||
if info != nil && info.Name != "" {
|
if info != nil && info.Name != "" {
|
||||||
anno[AnnoKeyOriginName] = info.Name
|
anno[AnnoKeyRepoName] = info.Name
|
||||||
if info.Path != "" {
|
if info.Path != "" {
|
||||||
anno[AnnoKeyOriginPath] = info.Path
|
anno[AnnoKeyRepoPath] = info.Path
|
||||||
}
|
}
|
||||||
if info.Hash != "" {
|
if info.Hash != "" {
|
||||||
anno[AnnoKeyOriginHash] = info.Hash
|
anno[AnnoKeyRepoHash] = info.Hash
|
||||||
}
|
}
|
||||||
if info.Timestamp != nil {
|
if info.Timestamp != nil {
|
||||||
anno[AnnoKeyOriginTimestamp] = info.Timestamp.UTC().Format(time.RFC3339)
|
anno[AnnoKeyRepoTimestamp] = info.Timestamp.UTC().Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.obj.SetAnnotations(anno)
|
m.obj.SetAnnotations(anno)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetOriginInfo() (*ResourceOriginInfo, error) {
|
func (m *grafanaMetaAccessor) GetRepositoryInfo() (*ResourceRepositoryInfo, error) {
|
||||||
v, ok := m.obj.GetAnnotations()[AnnoKeyOriginName]
|
v, ok := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
t, err := m.GetOriginTimestamp()
|
t, err := m.GetRepositoryTimestamp()
|
||||||
return &ResourceOriginInfo{
|
return &ResourceRepositoryInfo{
|
||||||
Name: v,
|
Name: v,
|
||||||
Path: m.GetOriginPath(),
|
Path: m.GetRepositoryPath(),
|
||||||
Hash: m.GetOriginHash(),
|
Hash: m.GetRepositoryHash(),
|
||||||
Timestamp: t,
|
Timestamp: t,
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetOriginName() string {
|
func (m *grafanaMetaAccessor) GetRepositoryName() string {
|
||||||
return m.get(AnnoKeyOriginName)
|
v, _ := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName)
|
||||||
|
return v // will be empty string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetOriginPath() string {
|
func (m *grafanaMetaAccessor) GetRepositoryPath() string {
|
||||||
return m.get(AnnoKeyOriginPath)
|
v, _ := m.getAnnoValue(AnnoKeyRepoPath, oldAnnoKeyOriginPath)
|
||||||
|
return v // will be empty string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetOriginHash() string {
|
func (m *grafanaMetaAccessor) GetRepositoryHash() string {
|
||||||
return m.get(AnnoKeyOriginHash)
|
v, _ := m.getAnnoValue(AnnoKeyRepoHash, oldAnnoKeyOriginHash)
|
||||||
|
return v // will be empty string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetOriginTimestamp() (*time.Time, error) {
|
func (m *grafanaMetaAccessor) GetRepositoryTimestamp() (*time.Time, error) {
|
||||||
v, ok := m.obj.GetAnnotations()[AnnoKeyOriginTimestamp]
|
v, ok := m.getAnnoValue(AnnoKeyRepoTimestamp, oldAnnoKeyOriginTimestamp)
|
||||||
if !ok || v == "" {
|
if !ok || v == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -617,19 +647,23 @@ func (m *grafanaMetaAccessor) SetStatus(s any) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetFullPath() string {
|
func (m *grafanaMetaAccessor) GetFullPath() string {
|
||||||
return m.get(AnnoKeyFullPath)
|
// nolint:staticcheck
|
||||||
|
return m.get(annoKeyFullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) SetFullPath(path string) {
|
func (m *grafanaMetaAccessor) SetFullPath(path string) {
|
||||||
m.SetAnnotation(AnnoKeyFullPath, path)
|
// nolint:staticcheck
|
||||||
|
m.SetAnnotation(annoKeyFullPath, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) GetFullPathUIDs() string {
|
func (m *grafanaMetaAccessor) GetFullPathUIDs() string {
|
||||||
return m.get(AnnoKeyFullPathUIDs)
|
// nolint:staticcheck
|
||||||
|
return m.get(annoKeyFullPathUIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) SetFullPathUIDs(path string) {
|
func (m *grafanaMetaAccessor) SetFullPathUIDs(path string) {
|
||||||
m.SetAnnotation(AnnoKeyFullPathUIDs, path)
|
// nolint:staticcheck
|
||||||
|
m.SetAnnotation(annoKeyFullPathUIDs, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *grafanaMetaAccessor) FindTitle(defaultTitle string) string {
|
func (m *grafanaMetaAccessor) FindTitle(defaultTitle string) string {
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestResource struct {
|
type TestResource struct {
|
||||||
@ -129,7 +130,7 @@ func (in *Spec2) DeepCopy() *Spec2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMetaAccessor(t *testing.T) {
|
func TestMetaAccessor(t *testing.T) {
|
||||||
originInfo := &utils.ResourceOriginInfo{
|
repoInfo := &utils.ResourceRepositoryInfo{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Path: "a/b/c",
|
Path: "a/b/c",
|
||||||
Hash: "kkk",
|
Hash: "kkk",
|
||||||
@ -177,13 +178,13 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.SetOriginInfo(originInfo)
|
meta.SetRepositoryInfo(repoInfo)
|
||||||
meta.SetFolder("folderUID")
|
meta.SetFolder("folderUID")
|
||||||
|
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"grafana.app/originName": "test",
|
"grafana.app/repoName": "test",
|
||||||
"grafana.app/originPath": "a/b/c",
|
"grafana.app/repoPath": "a/b/c",
|
||||||
"grafana.app/originHash": "kkk",
|
"grafana.app/repoHash": "kkk",
|
||||||
"grafana.app/folder": "folderUID",
|
"grafana.app/folder": "folderUID",
|
||||||
}, res.GetAnnotations())
|
}, res.GetAnnotations())
|
||||||
|
|
||||||
@ -229,13 +230,13 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
meta, err := utils.MetaAccessor(res)
|
meta, err := utils.MetaAccessor(res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
meta.SetOriginInfo(originInfo)
|
meta.SetRepositoryInfo(repoInfo)
|
||||||
meta.SetFolder("folderUID")
|
meta.SetFolder("folderUID")
|
||||||
|
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"grafana.app/originName": "test",
|
"grafana.app/repoName": "test",
|
||||||
"grafana.app/originPath": "a/b/c",
|
"grafana.app/repoPath": "a/b/c",
|
||||||
"grafana.app/originHash": "kkk",
|
"grafana.app/repoHash": "kkk",
|
||||||
"grafana.app/folder": "folderUID",
|
"grafana.app/folder": "folderUID",
|
||||||
}, res.GetAnnotations())
|
}, res.GetAnnotations())
|
||||||
|
|
||||||
@ -280,13 +281,13 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
meta, err := utils.MetaAccessor(res)
|
meta, err := utils.MetaAccessor(res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
meta.SetOriginInfo(originInfo)
|
meta.SetRepositoryInfo(repoInfo)
|
||||||
meta.SetFolder("folderUID")
|
meta.SetFolder("folderUID")
|
||||||
|
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"grafana.app/originName": "test",
|
"grafana.app/repoName": "test",
|
||||||
"grafana.app/originPath": "a/b/c",
|
"grafana.app/repoPath": "a/b/c",
|
||||||
"grafana.app/originHash": "kkk",
|
"grafana.app/repoHash": "kkk",
|
||||||
"grafana.app/folder": "folderUID",
|
"grafana.app/folder": "folderUID",
|
||||||
}, res.GetAnnotations())
|
}, res.GetAnnotations())
|
||||||
|
|
||||||
@ -321,6 +322,28 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
require.Equal(t, "ZZ", res.Status.Title)
|
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) {
|
t.Run("blob info", func(t *testing.T) {
|
||||||
info := &utils.BlobInfo{UID: "AAA", Size: 123, Hash: "xyz", MimeType: "application/json", Charset: "utf-8"}
|
info := &utils.BlobInfo{UID: "AAA", Size: 123, Hash: "xyz", MimeType: "application/json", Charset: "utf-8"}
|
||||||
anno := info.String()
|
anno := info.String()
|
||||||
@ -343,13 +366,13 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
|
|
||||||
meta, err := utils.MetaAccessor(obj)
|
meta, err := utils.MetaAccessor(obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
meta.SetOriginInfo(originInfo)
|
meta.SetRepositoryInfo(repoInfo)
|
||||||
meta.SetFolder("folderUID")
|
meta.SetFolder("folderUID")
|
||||||
|
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"grafana.app/originName": "test",
|
"grafana.app/repoName": "test",
|
||||||
"grafana.app/originPath": "a/b/c",
|
"grafana.app/repoPath": "a/b/c",
|
||||||
"grafana.app/originHash": "kkk",
|
"grafana.app/repoHash": "kkk",
|
||||||
"grafana.app/folder": "folderUID",
|
"grafana.app/folder": "folderUID",
|
||||||
}, obj.GetAnnotations())
|
}, obj.GetAnnotations())
|
||||||
|
|
||||||
@ -366,13 +389,13 @@ func TestMetaAccessor(t *testing.T) {
|
|||||||
|
|
||||||
meta, err = utils.MetaAccessor(obj2)
|
meta, err = utils.MetaAccessor(obj2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
meta.SetOriginInfo(originInfo)
|
meta.SetRepositoryInfo(repoInfo)
|
||||||
meta.SetFolder("folderUID")
|
meta.SetFolder("folderUID")
|
||||||
|
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"grafana.app/originName": "test",
|
"grafana.app/repoName": "test",
|
||||||
"grafana.app/originPath": "a/b/c",
|
"grafana.app/repoPath": "a/b/c",
|
||||||
"grafana.app/originHash": "kkk",
|
"grafana.app/repoHash": "kkk",
|
||||||
"grafana.app/folder": "folderUID",
|
"grafana.app/folder": "folderUID",
|
||||||
}, obj2.GetAnnotations())
|
}, obj2.GetAnnotations())
|
||||||
|
|
||||||
|
@ -10,10 +10,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/authlib/claims"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
|
"github.com/grafana/authlib/claims"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
@ -286,14 +287,14 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: origin_name.String,
|
Name: origin_name.String,
|
||||||
Path: originPath,
|
Path: originPath,
|
||||||
Hash: origin_hash.String,
|
Hash: origin_hash.String,
|
||||||
Timestamp: &ts,
|
Timestamp: &ts,
|
||||||
})
|
})
|
||||||
} else if plugin_id != "" {
|
} else if plugin_id != "" {
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "plugin",
|
Name: "plugin",
|
||||||
Path: plugin_id,
|
Path: plugin_id,
|
||||||
})
|
})
|
||||||
@ -516,7 +517,7 @@ func (a *dashboardSqlAccess) GetLibraryPanels(ctx context.Context, query Library
|
|||||||
meta.SetCreatedBy(p.CreatedBy)
|
meta.SetCreatedBy(p.CreatedBy)
|
||||||
meta.SetUpdatedBy(p.UpdatedBy)
|
meta.SetUpdatedBy(p.UpdatedBy)
|
||||||
meta.SetUpdatedTimestamp(&p.Updated)
|
meta.SetUpdatedTimestamp(&p.Updated)
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: strconv.FormatInt(p.ID, 10),
|
Path: strconv.FormatInt(p.ID, 10),
|
||||||
})
|
})
|
||||||
|
@ -108,12 +108,12 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob
|
|||||||
UID: name,
|
UID: name,
|
||||||
OrgID: info.OrgID,
|
OrgID: info.OrgID,
|
||||||
}
|
}
|
||||||
origin, err := obj.GetOriginInfo()
|
repo, err := obj.GetRepositoryInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if origin != nil && origin.Name == "SQL" {
|
if repo != nil && repo.Name == "SQL" {
|
||||||
dto.ID, err = strconv.ParseInt(origin.Path, 10, 64)
|
dto.ID, err = strconv.ParseInt(repo.Path, 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
|||||||
|
|
||||||
meta.SetUpdatedTimestamp(&v.Updated)
|
meta.SetUpdatedTimestamp(&v.Updated)
|
||||||
if v.ID > 0 { // nolint:staticcheck
|
if v.ID > 0 { // nolint:staticcheck
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: fmt.Sprintf("%d", v.ID), // nolint:staticcheck
|
Path: fmt.Sprintf("%d", v.ID), // nolint:staticcheck
|
||||||
Timestamp: &v.Created,
|
Timestamp: &v.Created,
|
||||||
@ -187,13 +187,13 @@ func setParentUID(u *unstructured.Unstructured, parentUid string) error {
|
|||||||
func getLegacyID(meta utils.GrafanaMetaAccessor) (int64, error) {
|
func getLegacyID(meta utils.GrafanaMetaAccessor) (int64, error) {
|
||||||
var i int64
|
var i int64
|
||||||
|
|
||||||
info, err := meta.GetOriginInfo()
|
repo, err := meta.GetRepositoryInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if info != nil && info.Name == "SQL" {
|
if repo != nil && repo.Name == "SQL" {
|
||||||
i, err = strconv.ParseInt(info.Path, 10, 64)
|
i, err = strconv.ParseInt(repo.Path, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ func getURL(meta utils.GrafanaMetaAccessor, title string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCreated(meta utils.GrafanaMetaAccessor) (*time.Time, error) {
|
func getCreated(meta utils.GrafanaMetaAccessor) (*time.Time, error) {
|
||||||
created, err := meta.GetOriginTimestamp()
|
created, err := meta.GetRepositoryTimestamp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ func toSAItem(sa legacy.ServiceAccount, ns string) iamv0.ServiceAccount {
|
|||||||
}
|
}
|
||||||
obj, _ := utils.MetaAccessor(&item)
|
obj, _ := utils.MetaAccessor(&item)
|
||||||
obj.SetUpdatedTimestamp(&sa.Updated)
|
obj.SetUpdatedTimestamp(&sa.Updated)
|
||||||
obj.SetOriginInfo(&utils.ResourceOriginInfo{
|
obj.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: strconv.FormatInt(sa.ID, 10),
|
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, _ := utils.MetaAccessor(&obj)
|
||||||
meta.SetUpdatedTimestamp(&t.Updated)
|
meta.SetUpdatedTimestamp(&t.Updated)
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: strconv.FormatInt(t.ID, 10),
|
Path: strconv.FormatInt(t.ID, 10),
|
||||||
})
|
})
|
||||||
|
@ -137,7 +137,7 @@ func toUserItem(u *user.User, ns string) iamv0.User {
|
|||||||
}
|
}
|
||||||
obj, _ := utils.MetaAccessor(item)
|
obj, _ := utils.MetaAccessor(item)
|
||||||
obj.SetUpdatedTimestamp(&u.Updated)
|
obj.SetUpdatedTimestamp(&u.Updated)
|
||||||
obj.SetOriginInfo(&utils.ResourceOriginInfo{
|
obj.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: strconv.FormatInt(u.ID, 10),
|
Path: strconv.FormatInt(u.ID, 10),
|
||||||
})
|
})
|
||||||
|
@ -97,7 +97,7 @@ func convertToK8sResource(v *playlistsvc.PlaylistDTO, namespacer request.Namespa
|
|||||||
meta.SetUpdatedTimestampMillis(v.UpdatedAt)
|
meta.SetUpdatedTimestampMillis(v.UpdatedAt)
|
||||||
if v.Id > 0 {
|
if v.Id > 0 {
|
||||||
createdAt := time.UnixMilli(v.CreatedAt)
|
createdAt := time.UnixMilli(v.CreatedAt)
|
||||||
meta.SetOriginInfo(&utils.ResourceOriginInfo{
|
meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{
|
||||||
Name: "SQL",
|
Name: "SQL",
|
||||||
Path: fmt.Sprintf("%d", v.Id),
|
Path: fmt.Sprintf("%d", v.Id),
|
||||||
Timestamp: &createdAt,
|
Timestamp: &createdAt,
|
||||||
@ -135,7 +135,7 @@ func getLegacyID(item *unstructured.Unstructured) int64 {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
info, _ := meta.GetOriginInfo()
|
info, _ := meta.GetRepositoryInfo()
|
||||||
if info != nil && info.Name == "SQL" {
|
if info != nil && info.Name == "SQL" {
|
||||||
i, err := strconv.ParseInt(info.Path, 10, 64)
|
i, err := strconv.ParseInt(info.Path, 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -42,9 +42,9 @@ func TestPlaylistConversion(t *testing.T) {
|
|||||||
"resourceVersion": "54321",
|
"resourceVersion": "54321",
|
||||||
"creationTimestamp": "1970-01-01T00:00:12Z",
|
"creationTimestamp": "1970-01-01T00:00:12Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/originPath": "123",
|
"grafana.app/repoPath": "123",
|
||||||
"grafana.app/originName": "SQL",
|
"grafana.app/repoName": "SQL",
|
||||||
"grafana.app/originTimestamp":"1970-01-01T00:00:12Z",
|
"grafana.app/repoTimestamp":"1970-01-01T00:00:12Z",
|
||||||
"grafana.app/updatedTimestamp": "1970-01-01T00:00:54Z"
|
"grafana.app/updatedTimestamp": "1970-01-01T00:00:54Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -60,12 +60,12 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime
|
|||||||
obj.SetResourceVersion("")
|
obj.SetResourceVersion("")
|
||||||
obj.SetSelfLink("")
|
obj.SetSelfLink("")
|
||||||
|
|
||||||
// Read+write will verify that origin format is accurate
|
// Read+write will verify that repository format is accurate
|
||||||
origin, err := obj.GetOriginInfo()
|
repo, err := obj.GetRepositoryInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
obj.SetOriginInfo(origin)
|
obj.SetRepositoryInfo(repo)
|
||||||
obj.SetUpdatedBy("")
|
obj.SetUpdatedBy("")
|
||||||
obj.SetUpdatedTimestamp(nil)
|
obj.SetUpdatedTimestamp(nil)
|
||||||
obj.SetCreatedBy(user.GetUID())
|
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
|
obj.SetResourceVersion("") // removed from saved JSON because the RV is not yet calculated
|
||||||
|
|
||||||
// Read+write will verify that origin format is accurate
|
// Read+write will verify that origin format is accurate
|
||||||
origin, err := obj.GetOriginInfo()
|
repo, err := obj.GetRepositoryInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
obj.SetOriginInfo(origin)
|
obj.SetRepositoryInfo(repo)
|
||||||
obj.SetUpdatedBy(user.GetUID())
|
obj.SetUpdatedBy(user.GetUID())
|
||||||
obj.SetUpdatedTimestampMillis(time.Now().UnixMilli())
|
obj.SetUpdatedTimestampMillis(time.Now().UnixMilli())
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
type WriteAccessHooks struct {
|
type WriteAccessHooks struct {
|
||||||
// When configured, this will make sure a user is allowed to save to a given origin
|
// 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 {
|
type LifecycleHooks interface {
|
||||||
@ -20,11 +20,11 @@ type LifecycleHooks interface {
|
|||||||
Stop(context.Context) error
|
Stop(context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *WriteAccessHooks) CanWriteOrigin(ctx context.Context, user claims.AuthInfo, uid string) error {
|
func (a *WriteAccessHooks) CanWriteValueFromRepository(ctx context.Context, user claims.AuthInfo, uid string) error {
|
||||||
if a.Origin == nil || uid == "UI" {
|
if a.CanWriteValueFromRepoCheck == nil || uid == "UI" {
|
||||||
return nil // default to OK
|
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 fmt.Errorf("not allowed to write resource at origin")
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
if err != nil {
|
||||||
return nil, NewBadRequestError("invalid origin info")
|
return nil, NewBadRequestError("invalid repository info")
|
||||||
}
|
}
|
||||||
if origin != nil {
|
if repo != nil {
|
||||||
err = s.writeHooks.CanWriteOrigin(ctx, user, origin.Name)
|
err = s.writeHooks.CanWriteValueFromRepository(ctx, user, repo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, AsErrorResult(err)
|
return nil, AsErrorResult(err)
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ func TestSimpleServer(t *testing.T) {
|
|||||||
"uid": "xyz",
|
"uid": "xyz",
|
||||||
"namespace": "default",
|
"namespace": "default",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/originName": "elsewhere",
|
"grafana.app/repoName": "elsewhere",
|
||||||
"grafana.app/originPath": "path/to/item",
|
"grafana.app/repoPath": "path/to/item",
|
||||||
"grafana.app/originTimestamp": "2024-02-02T00:00:00Z"
|
"grafana.app/repoTimestamp": "2024-02-02T00:00:00Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
@ -176,9 +176,9 @@ func TestSimpleServer(t *testing.T) {
|
|||||||
"namespace": "default",
|
"namespace": "default",
|
||||||
"uid": "xyz",
|
"uid": "xyz",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/originName": "elsewhere",
|
"grafana.app/repoName": "elsewhere",
|
||||||
"grafana.app/originPath": "path/to/item",
|
"grafana.app/repoPath": "path/to/item",
|
||||||
"grafana.app/originTimestamp": "2024-02-02T00:00:00Z"
|
"grafana.app/repoTimestamp": "2024-02-02T00:00:00Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"creationTimestamp": "2024-10-30T18:30:54Z",
|
"creationTimestamp": "2024-10-30T18:30:54Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
||||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
"grafana.app/repoHash": "Grafana v9.2.0 (NA)",
|
||||||
"grafana.app/originName": "UI",
|
"grafana.app/repoName": "UI",
|
||||||
"grafana.app/originPath": "/dashboard/new"
|
"grafana.app/repoPath": "/dashboard/new"
|
||||||
},
|
},
|
||||||
"managedFields": [
|
"managedFields": [
|
||||||
{
|
{
|
||||||
@ -23,9 +23,9 @@
|
|||||||
"f:metadata": {
|
"f:metadata": {
|
||||||
"f:annotations": {
|
"f:annotations": {
|
||||||
".": {},
|
".": {},
|
||||||
"f:grafana.app/originHash": {},
|
"f:grafana.app/repoHash": {},
|
||||||
"f:grafana.app/originName": {},
|
"f:grafana.app/repoName": {},
|
||||||
"f:grafana.app/originPath": {}
|
"f:grafana.app/repoPath": {}
|
||||||
},
|
},
|
||||||
"f:generateName": {}
|
"f:generateName": {}
|
||||||
},
|
},
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956",
|
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956",
|
||||||
"creationTimestamp": "2024-10-30T20:24:07Z",
|
"creationTimestamp": "2024-10-30T20:24:07Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
"grafana.app/createdBy": "user:be2g71ke8yoe8b"
|
||||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
|
||||||
"grafana.app/originName": "UI",
|
|
||||||
"grafana.app/originPath": "/dashboard/new"
|
|
||||||
},
|
},
|
||||||
"managedFields": [
|
"managedFields": [
|
||||||
{
|
{
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956-2",
|
"uid": "86ab200a-e8b0-47ce-bbc1-8c2e078b0956-2",
|
||||||
"creationTimestamp": "2024-10-30T20:24:07Z",
|
"creationTimestamp": "2024-10-30T20:24:07Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:be2g71ke8yoe8b",
|
"grafana.app/createdBy": "user:be2g71ke8yoe8b"
|
||||||
"grafana.app/originHash": "Grafana v9.2.0 (NA)",
|
|
||||||
"grafana.app/originName": "UI",
|
|
||||||
"grafana.app/originPath": "/dashboard/new"
|
|
||||||
},
|
},
|
||||||
"managedFields": [
|
"managedFields": [
|
||||||
{
|
{
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
"uid": "aaaa-bbbb",
|
"uid": "aaaa-bbbb",
|
||||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:1",
|
"grafana.app/createdBy": "user:1"
|
||||||
"grafana.app/originName": "SQL",
|
|
||||||
"grafana.app/originPath": "15",
|
|
||||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:1",
|
"grafana.app/createdBy": "user:1",
|
||||||
"grafana.app/originName": "SQL",
|
"grafana.app/repoName": "SQL",
|
||||||
"grafana.app/originPath": "15",
|
"grafana.app/repoPath": "15",
|
||||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
"grafana.app/repoTimestamp": "2024-11-01T19:42:22Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"creationTimestamp": "2024-11-01T19:42:22Z",
|
"creationTimestamp": "2024-11-01T19:42:22Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/createdBy": "user:1",
|
"grafana.app/createdBy": "user:1",
|
||||||
"grafana.app/originName": "SQL",
|
"grafana.app/repoName": "SQL",
|
||||||
"grafana.app/originPath": "15",
|
"grafana.app/repoPath": "15",
|
||||||
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
|
"grafana.app/repoTimestamp": "2024-11-01T19:42:22Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -11,9 +11,6 @@ import {
|
|||||||
ResourceList,
|
ResourceList,
|
||||||
ResourceClient,
|
ResourceClient,
|
||||||
ObjectMeta,
|
ObjectMeta,
|
||||||
AnnoKeyOriginPath,
|
|
||||||
AnnoKeyOriginHash,
|
|
||||||
AnnoKeyOriginName,
|
|
||||||
K8sAPIGroupList,
|
K8sAPIGroupList,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
@ -55,12 +52,12 @@ export class ScopedResourceClient<T = object, K = string> implements ResourceCli
|
|||||||
// THe passed in value is the suggested prefix
|
// THe passed in value is the suggested prefix
|
||||||
obj.metadata.generateName = login ? login.slice(0, 2) : 'g';
|
obj.metadata.generateName = login ? login.slice(0, 2) : 'g';
|
||||||
}
|
}
|
||||||
setOriginAsUI(obj.metadata);
|
setSavedFromUIAnnotation(obj.metadata);
|
||||||
return getBackendSrv().post(this.url, obj);
|
return getBackendSrv().post(this.url, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async update(obj: Resource<T, K>): Promise<Resource<T, K>> {
|
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);
|
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
|
// 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) {
|
if (!meta.annotations) {
|
||||||
meta.annotations = {};
|
meta.annotations = {};
|
||||||
}
|
}
|
||||||
meta.annotations[AnnoKeyOriginName] = 'UI';
|
meta.annotations['grafana.app/saved-from-ui'] = config.buildInfo.versionString;
|
||||||
meta.annotations[AnnoKeyOriginPath] = window.location.pathname;
|
|
||||||
meta.annotations[AnnoKeyOriginHash] = config.buildInfo.versionString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DatasourceAPIVersions {
|
export class DatasourceAPIVersions {
|
||||||
|
@ -37,10 +37,10 @@ export const AnnoKeyMessage = 'grafana.app/message';
|
|||||||
export const AnnoKeySlug = 'grafana.app/slug';
|
export const AnnoKeySlug = 'grafana.app/slug';
|
||||||
|
|
||||||
// Identify where values came from
|
// Identify where values came from
|
||||||
export const AnnoKeyOriginName = 'grafana.app/originName';
|
export const AnnoKeyRepoName = 'grafana.app/repoName';
|
||||||
export const AnnoKeyOriginPath = 'grafana.app/originPath';
|
export const AnnoKeyRepoPath = 'grafana.app/repoPath';
|
||||||
export const AnnoKeyOriginHash = 'grafana.app/originHash';
|
export const AnnoKeyRepoHash = 'grafana.app/repoHash';
|
||||||
const AnnoKeyOriginTimestamp = 'grafana.app/originTimestamp';
|
const AnnoKeyRepoTimestamp = 'grafana.app/repoTimestamp';
|
||||||
|
|
||||||
type GrafanaAnnotations = {
|
type GrafanaAnnotations = {
|
||||||
[AnnoKeyCreatedBy]?: string;
|
[AnnoKeyCreatedBy]?: string;
|
||||||
@ -49,10 +49,10 @@ type GrafanaAnnotations = {
|
|||||||
[AnnoKeyFolder]?: string;
|
[AnnoKeyFolder]?: string;
|
||||||
[AnnoKeySlug]?: string;
|
[AnnoKeySlug]?: string;
|
||||||
|
|
||||||
[AnnoKeyOriginName]?: string;
|
[AnnoKeyRepoName]?: string;
|
||||||
[AnnoKeyOriginPath]?: string;
|
[AnnoKeyRepoPath]?: string;
|
||||||
[AnnoKeyOriginHash]?: string;
|
[AnnoKeyRepoHash]?: string;
|
||||||
[AnnoKeyOriginTimestamp]?: string;
|
[AnnoKeyRepoTimestamp]?: string;
|
||||||
|
|
||||||
// Any key value
|
// Any key value
|
||||||
[key: string]: string | undefined;
|
[key: string]: string | undefined;
|
||||||
|
Loading…
Reference in New Issue
Block a user