From 7002ab90ae00631de5bb623b6f3c66fa12f5de84 Mon Sep 17 00:00:00 2001 From: Georges Chaudy Date: Fri, 11 Jul 2025 18:25:48 +0200 Subject: [PATCH 1/8] unistore: save returns a writecloser (#107955) * unistore: save returns a writecloser * go-lint * address comments --- pkg/storage/unified/resource/datastore.go | 12 +++- pkg/storage/unified/resource/eventstore.go | 14 +++-- pkg/storage/unified/resource/kv.go | 66 ++++++++++++++----- pkg/storage/unified/resource/kv_test.go | 35 ++++++----- pkg/storage/unified/resource/metadata.go | 15 +++-- pkg/storage/unified/testing/kv.go | 73 +++++++++++++++------- 6 files changed, 149 insertions(+), 66 deletions(-) diff --git a/pkg/storage/unified/resource/datastore.go b/pkg/storage/unified/resource/datastore.go index 5838b585b8f..a3301062048 100644 --- a/pkg/storage/unified/resource/datastore.go +++ b/pkg/storage/unified/resource/datastore.go @@ -247,7 +247,17 @@ func (d *dataStore) Save(ctx context.Context, key DataKey, value io.Reader) erro return fmt.Errorf("invalid data key: %w", err) } - return d.kv.Save(ctx, dataSection, key.String(), value) + writer, err := d.kv.Save(ctx, dataSection, key.String()) + if err != nil { + return err + } + _, err = io.Copy(writer, value) + if err != nil { + _ = writer.Close() + return err + } + + return writer.Close() } func (d *dataStore) Delete(ctx context.Context, key DataKey) error { diff --git a/pkg/storage/unified/resource/eventstore.go b/pkg/storage/unified/resource/eventstore.go index e1f4b565952..93f1922c5cc 100644 --- a/pkg/storage/unified/resource/eventstore.go +++ b/pkg/storage/unified/resource/eventstore.go @@ -1,7 +1,6 @@ package resource import ( - "bytes" "context" "encoding/json" "fmt" @@ -134,12 +133,17 @@ func (n *eventStore) Save(ctx context.Context, event Event) error { return fmt.Errorf("invalid event key: %w", err) } - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - if err := encoder.Encode(event); err != nil { + writer, err := n.kv.Save(ctx, eventsSection, eventKey.String()) + if err != nil { return err } - return n.kv.Save(ctx, eventsSection, eventKey.String(), &buf) + encoder := json.NewEncoder(writer) + if err := encoder.Encode(event); err != nil { + _ = writer.Close() + return err + } + + return writer.Close() } func (n *eventStore) Get(ctx context.Context, key EventKey) (Event, error) { diff --git a/pkg/storage/unified/resource/kv.go b/pkg/storage/unified/resource/kv.go index 0e9c7a13d0e..613841c5994 100644 --- a/pkg/storage/unified/resource/kv.go +++ b/pkg/storage/unified/resource/kv.go @@ -36,8 +36,8 @@ type KV interface { // Get retrieves the value for a key from the store Get(ctx context.Context, section string, key string) (io.ReadCloser, error) - // Save a new value - Save(ctx context.Context, section string, key string, value io.Reader) error + // Save a new value - returns a WriteCloser to write the value to + Save(ctx context.Context, section string, key string) (io.WriteCloser, error) // Delete a value Delete(ctx context.Context, section string, key string) error @@ -92,32 +92,66 @@ func (k *badgerKV) Get(ctx context.Context, section string, key string) (io.Read return io.NopCloser(bytes.NewReader(value)), nil } -func (k *badgerKV) Save(ctx context.Context, section string, key string, value io.Reader) error { - if k.db.IsClosed() { +// badgerWriteCloser implements io.WriteCloser for badgerKV +type badgerWriteCloser struct { + db *badger.DB + keyWithSection string + buf *bytes.Buffer + closed bool +} + +// Write implements io.Writer +func (w *badgerWriteCloser) Write(p []byte) (int, error) { + if w.closed { + return 0, fmt.Errorf("write to closed writer") + } + return w.buf.Write(p) +} + +// Close implements io.Closer - stores the buffered data in BadgerDB +func (w *badgerWriteCloser) Close() error { + if w.closed { + return nil + } + w.closed = true + + if w.db.IsClosed() { return fmt.Errorf("database is closed") } - if section == "" { - return fmt.Errorf("section is required") - } + data := w.buf.Bytes() - key = section + "/" + key - - data, err := io.ReadAll(value) - if err != nil { - return fmt.Errorf("failed to read value: %w", err) - } - - txn := k.db.NewTransaction(true) + txn := w.db.NewTransaction(true) defer txn.Discard() - err = txn.Set([]byte(key), data) + err := txn.Set([]byte(w.keyWithSection), data) if err != nil { return err } return txn.Commit() } +func (k *badgerKV) Save(ctx context.Context, section string, key string) (io.WriteCloser, error) { + if k.db.IsClosed() { + return nil, fmt.Errorf("database is closed") + } + + if section == "" { + return nil, fmt.Errorf("section is required") + } + + if key == "" { + return nil, fmt.Errorf("key is required") + } + + return &badgerWriteCloser{ + db: k.db, + keyWithSection: section + "/" + key, + buf: &bytes.Buffer{}, + closed: false, + }, nil +} + func (k *badgerKV) Delete(ctx context.Context, section string, key string) error { if k.db.IsClosed() { return fmt.Errorf("database is closed") diff --git a/pkg/storage/unified/resource/kv_test.go b/pkg/storage/unified/resource/kv_test.go index 6b7ead40879..27f24e19122 100644 --- a/pkg/storage/unified/resource/kv_test.go +++ b/pkg/storage/unified/resource/kv_test.go @@ -64,11 +64,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) { expectedInternalKey := section + "/" + key // Save through KV interface - err := kv.Save(ctx, section, key, strings.NewReader(value)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value)) // Verify the raw key exists in badger with correct format - err = db.View(func(txn *badger.Txn) error { + err := db.View(func(txn *badger.Txn) error { item, err := txn.Get([]byte(expectedInternalKey)) require.NoError(t, err) @@ -90,13 +89,11 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) { value2 := "value-from-section2" // Save same key in different sections - err := kv.Save(ctx, section1, key, strings.NewReader(value1)) - require.NoError(t, err) - err = kv.Save(ctx, section2, key, strings.NewReader(value2)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section1, key, strings.NewReader(value1)) + saveKVHelper(t, kv, ctx, section2, key, strings.NewReader(value2)) // Verify both keys exist in badger with different internal keys - err = db.View(func(txn *badger.Txn) error { + err := db.View(func(txn *badger.Txn) error { // Check section1 key item1, err := txn.Get([]byte(section1 + "/" + key)) require.NoError(t, err) @@ -140,11 +137,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) { internalKey := section + "/" + key // Save and verify it exists - err := kv.Save(ctx, section, key, strings.NewReader(value)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value)) // Verify it exists in badger - err = db.View(func(txn *badger.Txn) error { + err := db.View(func(txn *badger.Txn) error { _, err := txn.Get([]byte(internalKey)) return err }) @@ -172,12 +168,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) { keys2 := []string{"b1", "b2", "b3"} for _, k := range keys1 { - err := kv.Save(ctx, section1, k, strings.NewReader("value"+k)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section1, k, strings.NewReader("value"+k)) } for _, k := range keys2 { - err := kv.Save(ctx, section2, k, strings.NewReader("value"+k)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section2, k, strings.NewReader("value"+k)) } // List keys from section1 only @@ -262,3 +256,14 @@ func TestIsValidKey(t *testing.T) { }) } } + +// saveKVHelper is a helper function to save data to KV store using the new WriteCloser interface +func saveKVHelper(t *testing.T, kv KV, ctx context.Context, section, key string, value io.Reader) { + t.Helper() + writer, err := kv.Save(ctx, section, key) + require.NoError(t, err) + _, err = io.Copy(writer, value) + require.NoError(t, err) + err = writer.Close() + require.NoError(t, err) +} diff --git a/pkg/storage/unified/resource/metadata.go b/pkg/storage/unified/resource/metadata.go index ba0ea429faf..83240f97727 100644 --- a/pkg/storage/unified/resource/metadata.go +++ b/pkg/storage/unified/resource/metadata.go @@ -1,7 +1,6 @@ package resource import ( - "bytes" "context" "encoding/json" "fmt" @@ -340,12 +339,18 @@ func (d *metadataStore) Save(ctx context.Context, obj MetaDataObj) error { if err := obj.Key.Validate(); err != nil { return fmt.Errorf("invalid metadata key: %w", err) } - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - if err := encoder.Encode(obj.Value); err != nil { + + writer, err := d.kv.Save(ctx, metaSection, obj.Key.String()) + if err != nil { return err } - return d.kv.Save(ctx, metaSection, obj.Key.String(), &buf) + encoder := json.NewEncoder(writer) + if err := encoder.Encode(obj.Value); err != nil { + _ = writer.Close() + return err + } + + return writer.Close() } // parseMetaDataKey parses a string key into a MetaDataKey struct diff --git a/pkg/storage/unified/testing/kv.go b/pkg/storage/unified/testing/kv.go index 60b69d38497..79704b48b55 100644 --- a/pkg/storage/unified/testing/kv.go +++ b/pkg/storage/unified/testing/kv.go @@ -81,8 +81,7 @@ func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) { t.Run("get existing key", func(t *testing.T) { // First save a key testValue := "test value for get" - err := kv.Save(ctx, section, "existing-key", strings.NewReader(testValue)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "existing-key", strings.NewReader(testValue)) // Now get it reader, err := kv.Get(ctx, section, "existing-key") @@ -117,8 +116,7 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) { t.Run("save new key", func(t *testing.T) { testValue := "new test value" - err := kv.Save(ctx, section, "new-key", strings.NewReader(testValue)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "new-key", strings.NewReader(testValue)) // Verify it was saved reader, err := kv.Get(ctx, section, "new-key") @@ -133,13 +131,11 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) { t.Run("save overwrite existing key", func(t *testing.T) { // First save - err := kv.Save(ctx, section, "overwrite-key", strings.NewReader("old value")) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader("old value")) // Overwrite newValue := "new value" - err = kv.Save(ctx, section, "overwrite-key", strings.NewReader(newValue)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader(newValue)) // Verify it was updated reader, err := kv.Get(ctx, section, "overwrite-key") @@ -153,15 +149,14 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) { }) t.Run("save with empty section", func(t *testing.T) { - err := kv.Save(ctx, "", "some-key", strings.NewReader("some value")) + _, err := kv.Save(ctx, "", "some-key") assert.Error(t, err) assert.Contains(t, err.Error(), "section is required") }) t.Run("save binary data", func(t *testing.T) { binaryData := []byte{0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD} - err := kv.Save(ctx, section, "binary-key", bytes.NewReader(binaryData)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "binary-key", bytes.NewReader(binaryData)) // Verify binary data reader, err := kv.Get(ctx, section, "binary-key") @@ -176,8 +171,7 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) { t.Run("save key with no data", func(t *testing.T) { // Save a key with empty data - err := kv.Save(ctx, section, "empty-key", strings.NewReader("")) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "empty-key", strings.NewReader("")) // Verify it was saved with empty data reader, err := kv.Get(ctx, section, "empty-key") @@ -198,11 +192,10 @@ func runTestKVDelete(t *testing.T, kv resource.KV, nsPrefix string) { t.Run("delete existing key", func(t *testing.T) { // First create a key - err := kv.Save(ctx, section, "delete-key", strings.NewReader("delete me")) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, "delete-key", strings.NewReader("delete me")) // Verify it exists - _, err = kv.Get(ctx, section, "delete-key") + _, err := kv.Get(ctx, section, "delete-key") require.NoError(t, err) // Delete it @@ -235,8 +228,7 @@ func runTestKVKeys(t *testing.T, kv resource.KV, nsPrefix string) { // Setup test data testKeys := []string{"a1", "a2", "b1", "b2", "c1"} for _, key := range testKeys { - err := kv.Save(ctx, section, key, strings.NewReader("value"+key)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key)) } t.Run("list all keys", func(t *testing.T) { @@ -284,8 +276,7 @@ func runTestKVKeysWithLimits(t *testing.T, kv resource.KV, nsPrefix string) { // Setup test data testKeys := []string{"a1", "a2", "b1", "b2", "c1", "c2", "d1", "d2"} for _, key := range testKeys { - err := kv.Save(ctx, section, key, strings.NewReader("value"+key)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key)) } t.Run("keys with limit", func(t *testing.T) { @@ -339,8 +330,7 @@ func runTestKVKeysWithSort(t *testing.T, kv resource.KV, nsPrefix string) { // Setup test data testKeys := []string{"a1", "a2", "b1", "b2", "c1"} for _, key := range testKeys { - err := kv.Save(ctx, section, key, strings.NewReader("value"+key)) - require.NoError(t, err) + saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key)) } t.Run("keys in ascending order (default)", func(t *testing.T) { @@ -407,7 +397,19 @@ func runTestKVConcurrent(t *testing.T, kv resource.KV, nsPrefix string) { value := fmt.Sprintf("concurrent-value-%d-%d", goroutineID, j) // Save - err = kv.Save(ctx, section, key, strings.NewReader(value)) + writer, err := kv.Save(ctx, section, key) + if err != nil { + return + } + defer func() { + err := writer.Close() + require.NoError(t, err) + }() + _, err = io.Copy(writer, strings.NewReader(value)) + if err != nil { + return + } + err = writer.Close() if err != nil { return } @@ -447,7 +449,19 @@ func runTestKVConcurrent(t *testing.T, kv resource.KV, nsPrefix string) { value := fmt.Sprintf("concurrent-ops-value-%d", goroutineID) // Save - err = kv.Save(ctx, section, key, strings.NewReader(value)) + writer, err := kv.Save(ctx, section, key) + if err != nil { + return + } + defer func() { + err := writer.Close() + require.NoError(t, err) + }() + _, err = io.Copy(writer, strings.NewReader(value)) + if err != nil { + return + } + err = writer.Close() if err != nil { return } @@ -512,3 +526,14 @@ func runTestKVUnixTimestamp(t *testing.T, kv resource.KV, nsPrefix string) { require.InDelta(t, timestamp1, timestamp2, 1) }) } + +// saveKVHelper is a helper function to save data to KV store using the new WriteCloser interface +func saveKVHelper(t *testing.T, kv resource.KV, ctx context.Context, section, key string, value io.Reader) { + t.Helper() + writer, err := kv.Save(ctx, section, key) + require.NoError(t, err) + _, err = io.Copy(writer, value) + require.NoError(t, err) + err = writer.Close() + require.NoError(t, err) +} From 180a901c7de31aa13b8271da94d0bbd7fd2f9df6 Mon Sep 17 00:00:00 2001 From: Kevin Minehart <5140827+kminehart@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:38:43 -0500 Subject: [PATCH 2/8] CI: Change prerelease bucket path from {VERSION} to {VERSION}_{BUILD_ID} (#108031) * CI: Change prerelease bucket path from {VERSION} to {VERSION}_{BUILD_ID} * use run_id --- .github/workflows/release-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 54c81f1091f..35b8a219d62 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -186,5 +186,5 @@ jobs: bucket: grafana-prerelease pattern: artifacts-* run-id: ${{ github.run_id }} - bucket-path: ${{ needs.setup.outputs.version }} + bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }} environment: prod From 9786389ae8378bf3a675e64cb859c6cea3128ad7 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 11 Jul 2025 09:47:54 -0700 Subject: [PATCH 3/8] SecureValues: Support inline secure values in GrafanaMetaAccessor (#107996) --- .../apis/common/v0alpha1/secure_values.go | 94 +++++++++++ .../common/v0alpha1/secure_values_test.go | 47 ++++++ .../common/v0alpha1/zz_generated.deepcopy.go | 16 ++ .../common/v0alpha1/zz_generated.openapi.go | 154 +++++++++++------- pkg/apimachinery/go.mod | 4 +- pkg/apimachinery/utils/meta.go | 103 ++++++++++++ pkg/apimachinery/utils/meta_mock.go | 107 +++++++++++- pkg/apimachinery/utils/meta_test.go | 40 +++++ pkg/storage/unified/resource/server.go | 12 ++ 9 files changed, 516 insertions(+), 61 deletions(-) create mode 100644 pkg/apimachinery/apis/common/v0alpha1/secure_values.go create mode 100644 pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go diff --git a/pkg/apimachinery/apis/common/v0alpha1/secure_values.go b/pkg/apimachinery/apis/common/v0alpha1/secure_values.go new file mode 100644 index 00000000000..3413753cee4 --- /dev/null +++ b/pkg/apimachinery/apis/common/v0alpha1/secure_values.go @@ -0,0 +1,94 @@ +package v0alpha1 + +import ( + "encoding/json" + "fmt" + "strconv" + + "gopkg.in/yaml.v3" +) + +const redacted = "[REDACTED]" + +// RawSecureValue contains the raw decrypted secure value. +type RawSecureValue string + +var ( + _ fmt.Stringer = (*RawSecureValue)(nil) + _ fmt.Formatter = (*RawSecureValue)(nil) + _ fmt.GoStringer = (*RawSecureValue)(nil) + _ json.Marshaler = (*RawSecureValue)(nil) + _ yaml.Marshaler = (*RawSecureValue)(nil) +) + +// Allow access to a secure value inside +// +k8s:openapi-gen=true +type InlineSecureValue struct { + // Create a secure value -- this is only used for POST/PUT + // +k8s:validation:minLength=1 + // +k8s:validation:maxLength=24576 + Create RawSecureValue `json:"create,omitempty"` + + // Name in the secret service (reference) + Name string `json:"name,omitempty"` + + // Remove this value from the secure value map + // Values owned by this resource will be deleted if necessary + Remove bool `json:"remove,omitempty,omitzero"` +} + +func (v InlineSecureValue) IsZero() bool { + return v.Create.IsZero() && v.Name == "" && !v.Remove +} + +// Collection of secure values +// +k8s:openapi-gen=true +type InlineSecureValues = map[string]InlineSecureValue + +// NewSecretValue creates a new exposed secure value wrapper. +func NewSecretValue(v string) RawSecureValue { + return RawSecureValue(v) +} + +// DangerouslyExposeAndConsumeValue will move the decrypted secure value out of the wrapper and return it. +// Further attempts to call this method will panic. +// The function name is intentionally kept long and weird because this is a dangerous operation and should be used carefully! +func (s *RawSecureValue) DangerouslyExposeAndConsumeValue() string { + if *s == "" { + panic("underlying value is empty or was consumed") + } + + tmp := *s + *s = "" + + return string(tmp) +} + +func (s RawSecureValue) IsZero() bool { + return s == "" // exclude from JSON +} + +// String must not return the exposed secure value. +func (s RawSecureValue) String() string { + return redacted +} + +// Format must not return the exposed secure value. +func (s RawSecureValue) Format(f fmt.State, _verb rune) { + _, _ = fmt.Fprint(f, redacted) +} + +// GoString must not return the exposed secure value. +func (s RawSecureValue) GoString() string { + return redacted +} + +// MarshalJSON must not return the exposed secure value. +func (s RawSecureValue) MarshalJSON() ([]byte, error) { + return []byte(strconv.Quote(redacted)), nil +} + +// MarshalYAML must not return the exposed secure value. +func (s RawSecureValue) MarshalYAML() (any, error) { + return redacted, nil +} diff --git a/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go b/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go new file mode 100644 index 00000000000..53f180753a9 --- /dev/null +++ b/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go @@ -0,0 +1,47 @@ +package v0alpha1 + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestSecureValues(t *testing.T) { + expected := "[REDACTED]" + + rawValue := "a-password" + esv := NewSecretValue(rawValue) + + // String must not return the exposed secure value. + require.Equal(t, expected, esv.String()) + + // Format/GoString must not return the exposed secure value. + require.Equal(t, expected, fmt.Sprintf("%+#v", esv)) + require.Equal(t, expected, fmt.Sprintf("%v", esv)) + require.Equal(t, expected, fmt.Sprintf("%s", esv)) + + buf := new(bytes.Buffer) + _, err := fmt.Fprintf(buf, "%#v", esv) + require.NoError(t, err) + require.Equal(t, expected, buf.String()) + + // MarshalJSON must not return the exposed secure value. + bytes, err := json.Marshal(esv) + require.NoError(t, err) + require.Equal(t, `"`+expected+`"`, string(bytes)) + + // MarshalYAML must not return the exposed secure value. + bytes, err = yaml.Marshal(esv) + require.NoError(t, err) + require.Equal(t, "'"+expected+"'\n", string(bytes)) + + // DangerouslyExposeAndConsumeValue returns the raw value. + require.Equal(t, rawValue, esv.DangerouslyExposeAndConsumeValue()) + + // Further calls to DangerouslyExposeAndConsumeValue will panic. + require.Panics(t, func() { esv.DangerouslyExposeAndConsumeValue() }) +} diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go index 8a3334e3634..d3f8e64dbff 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go @@ -11,6 +11,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InlineSecureValue) DeepCopyInto(out *InlineSecureValue) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InlineSecureValue. +func (in *InlineSecureValue) DeepCopy() *InlineSecureValue { + if in == nil { + return nil + } + out := new(InlineSecureValue) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { *out = *in diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go index f4465722266..2f92840b5e6 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go @@ -11,68 +11,106 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "k8s.io/kube-openapi/pkg/common" spec "k8s.io/kube-openapi/pkg/validation/spec" + ptr "k8s.io/utils/ptr" ) func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ApplyOptions": schema_pkg_apis_meta_v1_ApplyOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Condition": schema_pkg_apis_meta_v1_Condition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldSelectorRequirement": schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), - "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), - "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), - "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue": schema_apimachinery_apis_common_v0alpha1_InlineSecureValue(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ApplyOptions": schema_pkg_apis_meta_v1_ApplyOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Condition": schema_pkg_apis_meta_v1_Condition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldSelectorRequirement": schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), + } +} + +func schema_apimachinery_apis_common_v0alpha1_InlineSecureValue(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Allow access to a secure value inside", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "create": { + SchemaProps: spec.SchemaProps{ + Description: "Create a secure value -- this is only used for POST/PUT", + MinLength: ptr.To[int64](1), + MaxLength: ptr.To[int64](24576), + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name in the secret service (reference)", + Type: []string{"string"}, + Format: "", + }, + }, + "remove": { + SchemaProps: spec.SchemaProps{ + Description: "Remove this value from the secure value map Values owned by this resource will be deleted if necessary", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, } } diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index a69b61d33ff..3d4120daa43 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -7,9 +7,11 @@ require ( github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team github.com/stretchr/testify v1.10.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.33.2 k8s.io/apiserver v0.33.2 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 ) require ( @@ -48,9 +50,7 @@ require ( google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/pkg/apimachinery/utils/meta.go b/pkg/apimachinery/utils/meta.go index 0d19536138d..508263b87f4 100644 --- a/pkg/apimachinery/utils/meta.go +++ b/pkg/apimachinery/utils/meta.go @@ -12,6 +12,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) // LabelKeyGetHistory is used to select object history for an given resource @@ -146,6 +148,13 @@ type GrafanaMetaAccessor interface { // SetSourceProperties sets the source properties of the resource. SetSourceProperties(SourceProperties) + + // GetSecureValues reads the "secure" property on a resource + GetSecureValues() (common.InlineSecureValues, error) + + // SetSourceProperties sets the source properties of the resource. + // For write commands, this may include inline secrets; read will only have references + SetSecureValues(common.InlineSecureValues) error } var _ GrafanaMetaAccessor = (*grafanaMetaAccessor)(nil) @@ -821,3 +830,97 @@ func (m *grafanaMetaAccessor) SetSourceProperties(v SourceProperties) { m.obj.SetAnnotations(annot) } + +// GetSecureValues implements GrafanaMetaAccessor. +func (m *grafanaMetaAccessor) GetSecureValues() (vals common.InlineSecureValues, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error reading spec") + } + }() + + var property any // may be map or struct + + f := m.r.FieldByName("Secure") + if f.IsValid() { + property = f.Interface() + } else { + // Unstructured + u, ok := m.raw.(*unstructured.Unstructured) + if ok { + property = u.Object["secure"] + } + } + + // Not found (and no error) + if property == nil { + return nil, nil + } + + // Try directly casting the property + vals, ok := property.(common.InlineSecureValues) + if ok { + return vals, nil + } + + // Generic map + u, ok := property.(map[string]any) + if ok { + vals = make(common.InlineSecureValues, len(u)) + for k, v := range u { + sv, ok := v.(map[string]any) + if !ok { + return nil, fmt.Errorf("unsupported nested secure value: %t", v) + } + inline := common.InlineSecureValue{} + inline.Name, _, _ = unstructured.NestedString(sv, "name") + inline.Remove, _, _ = unstructured.NestedBool(sv, "remove") + create, _, _ := unstructured.NestedString(sv, "create") + if create != "" { + inline.Create = common.NewSecretValue(create) + } + vals[k] = inline + } + return vals, nil + } + + fmt.Printf("TODO PROPERTY: (%t) %+v\n", property, property) + + return nil, fmt.Errorf("support: %t", property) +} + +// SetSecureValues implements GrafanaMetaAccessor. +func (m *grafanaMetaAccessor) SetSecureValues(vals common.InlineSecureValues) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error setting spec") + } + }() + + f := m.r.FieldByName("Secure") + if f.IsValid() && f.CanSet() { + f.Set(reflect.ValueOf(vals)) + return + } + + // Unstructured object + u, ok := m.raw.(*unstructured.Unstructured) + if ok { + u.Object["secure"] = vals + return + } + + return fmt.Errorf("unable to set secure values on (%T)", m.raw) +} + +func ToObjectReference(obj GrafanaMetaAccessor) common.ObjectReference { + gvk := obj.GetGroupVersionKind() + return common.ObjectReference{ + APIGroup: gvk.Group, + APIVersion: gvk.Version, + Kind: gvk.Kind, + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + UID: obj.GetUID(), + } +} diff --git a/pkg/apimachinery/utils/meta_mock.go b/pkg/apimachinery/utils/meta_mock.go index d73107b1bd6..4a0815a1b08 100644 --- a/pkg/apimachinery/utils/meta_mock.go +++ b/pkg/apimachinery/utils/meta_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.52.4. DO NOT EDIT. +// Code generated by mockery v2.53.4. DO NOT EDIT. package utils @@ -12,6 +12,8 @@ import ( types "k8s.io/apimachinery/pkg/types" + v0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -1248,6 +1250,63 @@ func (_c *MockGrafanaMetaAccessor_GetRuntimeObject_Call) RunAndReturn(run func() return _c } +// GetSecureValues provides a mock function with no fields +func (_m *MockGrafanaMetaAccessor) GetSecureValues() (map[string]v0alpha1.InlineSecureValue, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetSecureValues") + } + + var r0 map[string]v0alpha1.InlineSecureValue + var r1 error + if rf, ok := ret.Get(0).(func() (map[string]v0alpha1.InlineSecureValue, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() map[string]v0alpha1.InlineSecureValue); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]v0alpha1.InlineSecureValue) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockGrafanaMetaAccessor_GetSecureValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSecureValues' +type MockGrafanaMetaAccessor_GetSecureValues_Call struct { + *mock.Call +} + +// GetSecureValues is a helper method to define mock.On call +func (_e *MockGrafanaMetaAccessor_Expecter) GetSecureValues() *MockGrafanaMetaAccessor_GetSecureValues_Call { + return &MockGrafanaMetaAccessor_GetSecureValues_Call{Call: _e.mock.On("GetSecureValues")} +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) Run(run func()) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) Return(_a0 map[string]v0alpha1.InlineSecureValue, _a1 error) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) RunAndReturn(run func() (map[string]v0alpha1.InlineSecureValue, error)) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Return(run) + return _c +} + // GetSelfLink provides a mock function with no fields func (_m *MockGrafanaMetaAccessor) GetSelfLink() string { ret := _m.Called() @@ -2369,6 +2428,52 @@ func (_c *MockGrafanaMetaAccessor_SetResourceVersionInt64_Call) RunAndReturn(run return _c } +// SetSecureValues provides a mock function with given fields: _a0 +func (_m *MockGrafanaMetaAccessor) SetSecureValues(_a0 map[string]v0alpha1.InlineSecureValue) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for SetSecureValues") + } + + var r0 error + if rf, ok := ret.Get(0).(func(map[string]v0alpha1.InlineSecureValue) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockGrafanaMetaAccessor_SetSecureValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetSecureValues' +type MockGrafanaMetaAccessor_SetSecureValues_Call struct { + *mock.Call +} + +// SetSecureValues is a helper method to define mock.On call +// - _a0 map[string]v0alpha1.InlineSecureValue +func (_e *MockGrafanaMetaAccessor_Expecter) SetSecureValues(_a0 interface{}) *MockGrafanaMetaAccessor_SetSecureValues_Call { + return &MockGrafanaMetaAccessor_SetSecureValues_Call{Call: _e.mock.On("SetSecureValues", _a0)} +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) Run(run func(_a0 map[string]v0alpha1.InlineSecureValue)) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(map[string]v0alpha1.InlineSecureValue)) + }) + return _c +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) Return(_a0 error) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) RunAndReturn(run func(map[string]v0alpha1.InlineSecureValue) error) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Return(run) + return _c +} + // SetSelfLink provides a mock function with given fields: selfLink func (_m *MockGrafanaMetaAccessor) SetSelfLink(selfLink string) { _m.Called(selfLink) diff --git a/pkg/apimachinery/utils/meta_test.go b/pkg/apimachinery/utils/meta_test.go index 0cc7d423d8f..fa58489b768 100644 --- a/pkg/apimachinery/utils/meta_test.go +++ b/pkg/apimachinery/utils/meta_test.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" ) @@ -24,6 +25,9 @@ type TestResource struct { // Read/write raw status Status Spec `json:"status,omitempty"` + + // Secure values as map + Secure common.InlineSecureValues `json:"secure,omitempty"` } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -84,6 +88,9 @@ type TestResource2 struct { // Exercise read/write pointer status Status *Spec `json:"status,omitempty"` + + // This time defined with a strict struct + SecureValues ExplictSecureValues `json:"secure,omitempty"` } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -112,6 +119,12 @@ func (in *TestResource2) DeepCopyObject() runtime.Object { return nil } +// Spec defines model for Spec. +type ExplictSecureValues struct { + // Sample token value + Prop common.InlineSecureValue `json:"token,omitempty"` +} + // Spec defines model for Spec. type Spec2 struct{} @@ -247,6 +260,15 @@ func TestMetaAccessor(t *testing.T) { status, err = meta.GetStatus() require.NoError(t, err) require.Equal(t, res.Object["status"], status) + + // Check write/read on unstructured object + err = meta.SetSecureValues(common.InlineSecureValues{ + "a": {Name: "bbbb"}, + }) + require.NoError(t, err) + secure, err := meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"a": {"name": "bbbb"}}`, asJSON(secure, true)) }) t.Run("get and set grafana metadata (TestResource)", func(t *testing.T) { @@ -254,6 +276,11 @@ func TestMetaAccessor(t *testing.T) { Spec: Spec{ Title: "test", }, + Secure: common.InlineSecureValues{ + "x": common.InlineSecureValue{ + Create: "hello", + }, + }, // Status is empty, but not nil! } meta, err := utils.MetaAccessor(res) @@ -302,6 +329,19 @@ func TestMetaAccessor(t *testing.T) { require.Equal(t, res.Status, status) require.Equal(t, "111", res.Status.Title) require.Equal(t, `{"title":"111"}`, asJSON(status, false)) + + // Check read/write secure values + secure, err := meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"x": {"create": "[REDACTED]"}}`, asJSON(secure, true)) + + err = meta.SetSecureValues(common.InlineSecureValues{ + "a": {Name: "bbbb"}, + }) + require.NoError(t, err) + secure, err = meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"a": {"name": "bbbb"}}`, asJSON(secure, true)) }) t.Run("get and set grafana metadata (TestResource2)", func(t *testing.T) { diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index 00567a3774c..b06ee3e7240 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -404,6 +404,8 @@ func (s *server) Stop(ctx context.Context) error { } // Old value indicates an update -- otherwise a create +// +//nolint:gocyclo func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resourcepb.ResourceKey, value, oldValue []byte) (*WriteEvent, *resourcepb.ErrorResult) { tmp := &unstructured.Unstructured{} err := tmp.UnmarshalJSON(value) @@ -436,6 +438,16 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resour return nil, NewBadRequestError("can not save annotation: " + utils.AnnoKeyGrantPermissions) } + // Verify that this resource can reference secure values + secure, err := obj.GetSecureValues() + if err != nil { + return nil, AsErrorResult(err) + } + if len(secure) > 0 { + // See: https://github.com/grafana/grafana/pull/107803 + return nil, NewBadRequestError("Saving secure values is not yet supported") + } + event := &WriteEvent{ Value: value, Key: key, From 9c1b2fb79234ae32408ec083882313734930c1e4 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Fri, 11 Jul 2025 19:14:05 +0200 Subject: [PATCH 4/8] Secrets: Bump API version to v1beta1 (#108026) --- go.mod | 5 +- go.sum | 6 +- go.work.sum | 3 +- pkg/registry/apis/secret/contracts/decrypt.go | 4 +- pkg/registry/apis/secret/contracts/keeper.go | 22 ++-- .../apis/secret/contracts/secure_value.go | 8 +- .../apis/secret/decrypt/authorizer.go | 6 +- .../apis/secret/decrypt/service_test.go | 16 +-- .../secret/secretkeeper/fakes/fake_keeper.go | 12 +- .../apis/secret/secretkeeper/secretkeeper.go | 4 +- .../secret/secretkeeper/sqlkeeper/keeper.go | 12 +- .../secretkeeper/sqlkeeper/keeper_test.go | 4 +- pkg/registry/apis/secret/service/decrypt.go | 8 +- .../apis/secret/service/secure_value.go | 28 ++--- .../apis/secret/service/secure_value_test.go | 6 +- .../apis/secret/testutils/testutils.go | 23 ++-- pkg/storage/secret/metadata/decrypt_store.go | 4 +- .../secret/metadata/decrypt_store_test.go | 45 ++++---- pkg/storage/secret/metadata/keeper_model.go | 90 +++++++-------- pkg/storage/secret/metadata/keeper_store.go | 20 ++-- .../secret/metadata/keeper_store_test.go | 105 +++++++++--------- .../secret/metadata/secure_value_model.go | 14 +-- .../secret/metadata/secure_value_store.go | 10 +- .../metadata/secure_value_store_test.go | 21 ++-- .../secret/metadata/secure_value_test.go | 90 ++++++++------- 25 files changed, 290 insertions(+), 276 deletions(-) diff --git a/go.mod b/go.mod index dfb884a040c..5de283a74d2 100644 --- a/go.mod +++ b/go.mod @@ -136,6 +136,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // @grafana/alerting-backend github.com/microsoft/go-mssqldb v1.8.0 // @grafana/partner-datasources github.com/migueleliasweb/go-github-mock v1.1.0 // @grafana/grafana-app-platform-squad + github.com/mitchellh/copystructure v1.2.0 // @grafana/grafana-operator-experience-squad github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c //@grafana/identity-access-team github.com/mocktools/go-smtp-mock/v2 v2.3.1 // @grafana/grafana-backend-group github.com/modern-go/reflect2 v1.0.2 // @grafana/alerting-backend @@ -233,8 +234,9 @@ require ( github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b // @grafana/identity-access-team github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b // @fcjack @matryer github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-operator-experience-squad github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad @@ -455,7 +457,6 @@ require ( github.com/miekg/dns v1.1.63 // indirect github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index 0a022fc6562..367b077d77a 100644 --- a/go.sum +++ b/go.sum @@ -1621,10 +1621,12 @@ github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712 github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:8RlQ4U9lccPEBD/QxV4zyIMh9+lzjS/7xGpiqn3cHLY= github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b h1:elfpvk06igCjE0yL+/urc69UDOt1B/sPfdNg9X9kUMc= github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:fPtx6dwGm0PweQRVbgtthMapJMvXobBcORbndb7Dgd4= +github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5 h1:+fMhUoqwGdY8ntH0GL2icJa3uk+bTiIMicawDG2r9Uc= +github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:TIrKvhgo2j6lvVeOZ3TUmXbI4I48d6v7QcadL/f6SKQ= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b h1:ei01IFqmnXkOrrVvsT3CYe+i5xYra3SCX7Wsu3PMsDU= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:+H4Va9jDJlGQJjAN+OFD/hLx2I/yEzDRMQLaKecvgAc= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b h1:e0dG1tPpuv4NHCAPP235Gip/MBQB8fQ2XW2bwHqoWh4= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:u0+k7KLCvGi6zHWsc2B7r+tmGcYjN/qR+gn51pl104E= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 h1:f4fopIH6eQRoZ/E7bstn69UtDAHleIdQ6DrdzEs++Ug= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8= github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b h1:rkQO7exsDLdr4KGA7kgEnkQnbJGePbDIP1SUQptLRs8= github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:9YjiHZzii2DZfocRDJbqSeC8M3GWenU5yexeHHxsZ4Y= github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b h1:QyJLJn3xwFTIXu9KPZujsrIUN0X8DdiR9b2h75L0AfI= diff --git a/go.work.sum b/go.work.sum index 754974ef77b..e2494dfa54a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -718,7 +718,7 @@ github.com/grafana/grafana/apps/dashboard v0.0.0-20250616145019-8d27f12428cb/go. github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI= github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8= github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c= github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg= github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:xrKQcxQxz+IUF90ybtfENFeEXtlj9nAsX/3Fw0KEIeQ= @@ -1424,7 +1424,6 @@ k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1 k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kms v0.33.2/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= diff --git a/pkg/registry/apis/secret/contracts/decrypt.go b/pkg/registry/apis/secret/contracts/decrypt.go index 73ff2248e7a..5bc3ac1631a 100644 --- a/pkg/registry/apis/secret/contracts/decrypt.go +++ b/pkg/registry/apis/secret/contracts/decrypt.go @@ -4,7 +4,7 @@ import ( "context" "errors" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" ) @@ -16,7 +16,7 @@ var ( // DecryptStorage is the interface for wiring and dependency injection. type DecryptStorage interface { - Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv0alpha1.ExposedSecureValue, error) + Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv1beta1.ExposedSecureValue, error) } // DecryptAuthorizer is the interface for authorizing decryption requests. diff --git a/pkg/registry/apis/secret/contracts/keeper.go b/pkg/registry/apis/secret/contracts/keeper.go index c7f0482e54d..b8d25cf651a 100644 --- a/pkg/registry/apis/secret/contracts/keeper.go +++ b/pkg/registry/apis/secret/contracts/keeper.go @@ -4,7 +4,7 @@ import ( "context" "errors" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -15,12 +15,12 @@ var ( // KeeperMetadataStorage is the interface for wiring and dependency injection. type KeeperMetadataStorage interface { - Create(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) - Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv0alpha1.Keeper, error) - Update(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) + Create(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) + Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv1beta1.Keeper, error) + Update(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) Delete(ctx context.Context, namespace xkube.Namespace, name string) error - List(ctx context.Context, namespace xkube.Namespace) ([]secretv0alpha1.Keeper, error) - GetKeeperConfig(ctx context.Context, namespace string, name *string, opts ReadOpts) (secretv0alpha1.KeeperConfig, error) + List(ctx context.Context, namespace xkube.Namespace) ([]secretv1beta1.Keeper, error) + GetKeeperConfig(ctx context.Context, namespace string, name *string, opts ReadOpts) (secretv1beta1.KeeperConfig, error) } // ErrKeeperInvalidSecureValues is returned when a Keeper references SecureValues that do not exist. @@ -95,14 +95,14 @@ func (s ExternalID) String() string { // Keeper is the interface for secret keepers. type Keeper interface { - Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (ExternalID, error) - Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID, exposedValueOrRef string) error - Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID) (secretv0alpha1.ExposedSecureValue, error) - Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID) error + Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (ExternalID, error) + Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID, exposedValueOrRef string) error + Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID) (secretv1beta1.ExposedSecureValue, error) + Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID) error } // Service is the interface for secret keeper services. // This exists because OSS and Enterprise have different amounts of keepers available. type KeeperService interface { - KeeperForConfig(secretv0alpha1.KeeperConfig) (Keeper, error) + KeeperForConfig(secretv1beta1.KeeperConfig) (Keeper, error) } diff --git a/pkg/registry/apis/secret/contracts/secure_value.go b/pkg/registry/apis/secret/contracts/secure_value.go index 94bde58c68e..f3bc1d3c45c 100644 --- a/pkg/registry/apis/secret/contracts/secure_value.go +++ b/pkg/registry/apis/secret/contracts/secure_value.go @@ -4,7 +4,7 @@ import ( "context" "errors" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" ) @@ -30,9 +30,9 @@ type ReadOpts struct { // SecureValueMetadataStorage is the interface for wiring and dependency injection. type SecureValueMetadataStorage interface { - Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) - Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv0alpha1.SecureValue, error) - List(ctx context.Context, namespace xkube.Namespace) ([]secretv0alpha1.SecureValue, error) + Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) + Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv1beta1.SecureValue, error) + List(ctx context.Context, namespace xkube.Namespace) ([]secretv1beta1.SecureValue, error) SetVersionToActive(ctx context.Context, namespace xkube.Namespace, name string, version int64) error SetVersionToInactive(ctx context.Context, namespace xkube.Namespace, name string, version int64) error SetExternalID(ctx context.Context, namespace xkube.Namespace, name string, version int64, externalID ExternalID) error diff --git a/pkg/registry/apis/secret/decrypt/authorizer.go b/pkg/registry/apis/secret/decrypt/authorizer.go index 9f2915d708b..0ae6f5c9796 100644 --- a/pkg/registry/apis/secret/decrypt/authorizer.go +++ b/pkg/registry/apis/secret/decrypt/authorizer.go @@ -9,7 +9,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" ) @@ -88,8 +88,8 @@ func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName strin // Changes: 1) we don't support `*` for verbs; 2) we support specific names in the permission. func hasPermissionInToken(tokenPermissions []string, name string) bool { var ( - group = secretv0alpha1.GROUP - resource = secretv0alpha1.SecureValuesResourceInfo.GetName() + group = secretv1beta1.APIGroup + resource = secretv1beta1.SecureValuesResourceInfo.GetName() verb = "decrypt" ) diff --git a/pkg/registry/apis/secret/decrypt/service_test.go b/pkg/registry/apis/secret/decrypt/service_test.go index 665a6730527..0aaac869a67 100644 --- a/pkg/registry/apis/secret/decrypt/service_test.go +++ b/pkg/registry/apis/secret/decrypt/service_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/service" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/stretchr/testify/mock" @@ -22,7 +22,7 @@ func TestDecryptService(t *testing.T) { mockErr := errors.New("mock error") mockStorage := &MockDecryptStorage{} - mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv0alpha1.ExposedSecureValue(""), mockErr) + mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv1beta1.ExposedSecureValue(""), mockErr) decryptedValuesResp := map[string]service.DecryptResult{ "secure-value-1": service.NewDecryptResultErr(mockErr), } @@ -42,8 +42,8 @@ func TestDecryptService(t *testing.T) { mockStorage := &MockDecryptStorage{} // Set up the mock to return a different value for each name in the test - exposedSecureValue1 := secretv0alpha1.NewExposedSecureValue("value1") - exposedSecureValue2 := secretv0alpha1.NewExposedSecureValue("value2") + exposedSecureValue1 := secretv1beta1.NewExposedSecureValue("value1") + exposedSecureValue2 := secretv1beta1.NewExposedSecureValue("value2") mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1"). Return(exposedSecureValue1, nil) mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2"). @@ -69,11 +69,11 @@ func TestDecryptService(t *testing.T) { mockErr := errors.New("mock error") mockStorage := &MockDecryptStorage{} - exposedSecureValue := secretv0alpha1.NewExposedSecureValue("value") + exposedSecureValue := secretv1beta1.NewExposedSecureValue("value") mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1"). Return(exposedSecureValue, nil) mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2"). - Return(secretv0alpha1.ExposedSecureValue(""), mockErr) + Return(secretv1beta1.ExposedSecureValue(""), mockErr) decryptedValuesResp := map[string]service.DecryptResult{ "secure-value-1": service.NewDecryptResultValue(&exposedSecureValue), @@ -95,7 +95,7 @@ type MockDecryptStorage struct { mock.Mock } -func (m *MockDecryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv0alpha1.ExposedSecureValue, error) { +func (m *MockDecryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv1beta1.ExposedSecureValue, error) { args := m.Called(ctx, namespace, name) - return args.Get(0).(secretv0alpha1.ExposedSecureValue), args.Error(1) + return args.Get(0).(secretv1beta1.ExposedSecureValue), args.Error(1) } diff --git a/pkg/registry/apis/secret/secretkeeper/fakes/fake_keeper.go b/pkg/registry/apis/secret/secretkeeper/fakes/fake_keeper.go index 0c8408a04bd..d24f9c0221c 100644 --- a/pkg/registry/apis/secret/secretkeeper/fakes/fake_keeper.go +++ b/pkg/registry/apis/secret/secretkeeper/fakes/fake_keeper.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" ) @@ -24,7 +24,7 @@ func NewFakeKeeper() *FakeKeeper { } } -func (s *FakeKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) { +func (s *FakeKeeper) Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) { ns, ok := s.values[namespace] if !ok { ns = make(map[string]string) @@ -36,7 +36,7 @@ func (s *FakeKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, return contracts.ExternalID(uid), nil } -func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv0alpha1.ExposedSecureValue, error) { +func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv1beta1.ExposedSecureValue, error) { ns, ok := s.values[namespace] if !ok { return "", ErrSecretNotFound @@ -46,14 +46,14 @@ func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig return "", ErrSecretNotFound } - return secretv0alpha1.NewExposedSecureValue(exposedVal), nil + return secretv1beta1.NewExposedSecureValue(exposedVal), nil } -func (s *FakeKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) error { +func (s *FakeKeeper) Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) error { return nil } -func (s *FakeKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error { +func (s *FakeKeeper) Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error { ns, ok := s.values[namespace] if !ok { return ErrSecretNotFound diff --git a/pkg/registry/apis/secret/secretkeeper/secretkeeper.go b/pkg/registry/apis/secret/secretkeeper/secretkeeper.go index 9cb1a4ec495..fc65573ac61 100644 --- a/pkg/registry/apis/secret/secretkeeper/secretkeeper.go +++ b/pkg/registry/apis/secret/secretkeeper/secretkeeper.go @@ -3,7 +3,7 @@ package secretkeeper import ( "go.opentelemetry.io/otel/trace" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/sqlkeeper" "github.com/prometheus/client_golang/prometheus" @@ -30,6 +30,6 @@ func ProvideService( // Ignore the config, but we could use it to get the keeper type and then return the correct keeper. // Instantiation only happens on ProvideService ONCE. -func (k *OSSKeeperService) KeeperForConfig(secretv0alpha1.KeeperConfig) (contracts.Keeper, error) { +func (k *OSSKeeperService) KeeperForConfig(secretv1beta1.KeeperConfig) (contracts.Keeper, error) { return k.systemKeeper, nil } diff --git a/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper.go b/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper.go index 30656bb8e65..a65052c40f0 100644 --- a/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper.go +++ b/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/metrics" "github.com/prometheus/client_golang/prometheus" @@ -36,7 +36,7 @@ func NewSQLKeeper( } } -func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) { +func (s *SQLKeeper) Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) { ctx, span := s.tracer.Start(ctx, "SQLKeeper.Store", trace.WithAttributes(attribute.String("namespace", namespace))) defer span.End() @@ -58,7 +58,7 @@ func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, return externalID, nil } -func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv0alpha1.ExposedSecureValue, error) { +func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv1beta1.ExposedSecureValue, error) { ctx, span := s.tracer.Start(ctx, "SQLKeeper.Expose", trace.WithAttributes( attribute.String("namespace", namespace), attribute.String("externalID", externalID.String()), @@ -76,13 +76,13 @@ func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, return "", fmt.Errorf("unable to decrypt value: %w", err) } - exposedValue := secretv0alpha1.NewExposedSecureValue(string(exposedBytes)) + exposedValue := secretv1beta1.NewExposedSecureValue(string(exposedBytes)) s.metrics.ExposeDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds()) return exposedValue, nil } -func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) error { +func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) error { ctx, span := s.tracer.Start(ctx, "SQLKeeper.Delete", trace.WithAttributes( attribute.String("namespace", namespace), attribute.String("externalID", externalID.String()), @@ -100,7 +100,7 @@ func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, return nil } -func (s *SQLKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error { +func (s *SQLKeeper) Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error { ctx, span := s.tracer.Start(ctx, "SQLKeeper.Update", trace.WithAttributes( attribute.String("namespace", namespace), attribute.String("externalID", externalID.String()), diff --git a/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper_test.go b/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper_test.go index fa9aa7a6ba5..902c1f64b63 100644 --- a/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper_test.go +++ b/pkg/registry/apis/secret/secretkeeper/sqlkeeper/keeper_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" encryptionmanager "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/manager" @@ -44,7 +44,7 @@ func Test_SQLKeeperSetup(t *testing.T) { require.NoError(t, err) require.NotNil(t, sqlKeeper) - keeperCfg := &secretv0alpha1.SystemKeeperConfig{} + keeperCfg := &secretv1beta1.SystemKeeperConfig{} t.Run("storing an encrypted value returns no error", func(t *testing.T) { externalId1, err := sqlKeeper.Store(ctx, keeperCfg, namespace1, plaintext1) diff --git a/pkg/registry/apis/secret/service/decrypt.go b/pkg/registry/apis/secret/service/decrypt.go index d9b19ed2e76..f8ff83d95c9 100644 --- a/pkg/registry/apis/secret/service/decrypt.go +++ b/pkg/registry/apis/secret/service/decrypt.go @@ -3,14 +3,14 @@ package service import ( "context" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" ) // DecryptResult is the (union) result of a decryption operation. // It contains the decrypted `value` when the decryption succeeds, and the `err` when it fails. // It is not possible to construct a `DecryptResult` where both `value` and `err` are set from another package. type DecryptResult struct { - value *secretv0alpha1.ExposedSecureValue + value *secretv1beta1.ExposedSecureValue err error } @@ -18,7 +18,7 @@ func (d DecryptResult) Error() error { return d.err } -func (d DecryptResult) Value() *secretv0alpha1.ExposedSecureValue { +func (d DecryptResult) Value() *secretv1beta1.ExposedSecureValue { return d.value } @@ -26,7 +26,7 @@ func NewDecryptResultErr(err error) DecryptResult { return DecryptResult{err: err} } -func NewDecryptResultValue(value *secretv0alpha1.ExposedSecureValue) DecryptResult { +func NewDecryptResultValue(value *secretv1beta1.ExposedSecureValue) DecryptResult { return DecryptResult{value: value} } diff --git a/pkg/registry/apis/secret/service/secure_value.go b/pkg/registry/apis/secret/service/secure_value.go index 2611eb5a41b..6170bca37b6 100644 --- a/pkg/registry/apis/secret/service/secure_value.go +++ b/pkg/registry/apis/secret/service/secure_value.go @@ -6,8 +6,8 @@ import ( claims "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/utils" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "go.opentelemetry.io/otel/attribute" @@ -41,7 +41,7 @@ func ProvideSecureValueService( } } -func (s *SecureValueService) Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) { +func (s *SecureValueService) Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) { ctx, span := s.tracer.Start(ctx, "SecureValueService.Create", trace.WithAttributes( attribute.String("name", sv.GetName()), attribute.String("namespace", sv.GetNamespace()), @@ -51,7 +51,7 @@ func (s *SecureValueService) Create(ctx context.Context, sv *secretv0alpha1.Secu return s.createNewVersion(ctx, sv, actorUID) } -func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, bool, error) { +func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) { ctx, span := s.tracer.Start(ctx, "SecureValueService.Update", trace.WithAttributes( attribute.String("name", newSecureValue.GetName()), attribute.String("namespace", newSecureValue.GetNamespace()), @@ -59,7 +59,7 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv )) defer span.End() - if newSecureValue.Spec.Value == "" { + if newSecureValue.Spec.Value == nil { decrypted, err := s.secureValueMetadataStorage.ReadForDecrypt(ctx, xkube.Namespace(newSecureValue.Namespace), newSecureValue.Name) if err != nil { return nil, false, fmt.Errorf("reading secure value secret: %+w", err) @@ -82,7 +82,7 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv return nil, false, fmt.Errorf("reading secret value from keeper: %w", err) } - newSecureValue.Spec.Value = secret + newSecureValue.Spec.Value = &secret } const updateIsSync = true @@ -90,12 +90,12 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv return createdSv, updateIsSync, err } -func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) { +func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) { createdSv, err := s.secureValueMetadataStorage.Create(ctx, sv, actorUID) if err != nil { return nil, fmt.Errorf("creating secure value: %w", err) } - createdSv.Status = secretv0alpha1.SecureValueStatus{ + createdSv.Status = secretv1beta1.SecureValueStatus{ Version: createdSv.Status.Version, } @@ -135,7 +135,7 @@ func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv0a return createdSv, nil } -func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.SecureValue, error) { +func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error) { ctx, span := s.tracer.Start(ctx, "SecureValueService.Read", trace.WithAttributes( attribute.String("name", name), attribute.String("namespace", namespace.String()), @@ -145,7 +145,7 @@ func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace return s.secureValueMetadataStorage.Read(ctx, namespace, name, contracts.ReadOpts{ForUpdate: false}) } -func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace) (*secretv0alpha1.SecureValueList, error) { +func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace) (*secretv1beta1.SecureValueList, error) { ctx, span := s.tracer.Start(ctx, "SecureValueService.List", trace.WithAttributes( attribute.String("namespace", namespace.String()), )) @@ -157,8 +157,8 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace } hasPermissionFor, err := s.accessClient.Compile(ctx, user, claims.ListRequest{ - Group: secretv0alpha1.GROUP, - Resource: secretv0alpha1.SecureValuesResourceInfo.GetName(), + Group: secretv1beta1.APIGroup, + Resource: secretv1beta1.SecureValuesResourceInfo.GetName(), Namespace: namespace.String(), Verb: utils.VerbGet, // Why not VerbList? }) @@ -171,7 +171,7 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace return nil, fmt.Errorf("fetching secure values from storage: %+w", err) } - out := make([]secretv0alpha1.SecureValue, 0) + out := make([]secretv1beta1.SecureValue, 0) for _, metadata := range secureValuesMetadata { // Check whether the user has permission to access this specific SecureValue in the namespace. @@ -182,12 +182,12 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace out = append(out, metadata) } - return &secretv0alpha1.SecureValueList{ + return &secretv1beta1.SecureValueList{ Items: out, }, nil } -func (s *SecureValueService) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.SecureValue, error) { +func (s *SecureValueService) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error) { ctx, span := s.tracer.Start(ctx, "SecureValueService.Delete", trace.WithAttributes( attribute.String("name", name), attribute.String("namespace", namespace.String()), diff --git a/pkg/registry/apis/secret/service/secure_value_test.go b/pkg/registry/apis/secret/service/secure_value_test.go index 865138d097b..1304e5b0ce1 100644 --- a/pkg/registry/apis/secret/service/secure_value_test.go +++ b/pkg/registry/apis/secret/service/secure_value_test.go @@ -3,11 +3,12 @@ package service_test import ( "testing" - "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/testutils" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" ) func TestCrud(t *testing.T) { @@ -23,7 +24,7 @@ func TestCrud(t *testing.T) { // Create the same secure value twice input := sv1.DeepCopy() input.Spec.Description = "d2" - input.Spec.Value = v0alpha1.NewExposedSecureValue("v2") + input.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("v2")) sv2, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(input)) require.NoError(t, err) @@ -55,6 +56,7 @@ func TestCrud(t *testing.T) { // Update the secure value input := sv1.DeepCopy() input.Spec.Description = "d2" + input.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("v3")) sv2, err := sut.UpdateSv(t.Context(), input) require.NoError(t, err) diff --git a/pkg/registry/apis/secret/testutils/testutils.go b/pkg/registry/apis/secret/testutils/testutils.go index b85b098d834..0485b44bfba 100644 --- a/pkg/registry/apis/secret/testutils/testutils.go +++ b/pkg/registry/apis/secret/testutils/testutils.go @@ -6,11 +6,12 @@ import ( "github.com/grafana/authlib/authn" "github.com/grafana/authlib/types" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/identity" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" encryptionstorage "github.com/grafana/grafana/pkg/storage/secret/encryption" "go.opentelemetry.io/otel/trace/noop" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" @@ -138,28 +139,28 @@ type Sut struct { } type CreateSvConfig struct { - Sv *secretv0alpha1.SecureValue + Sv *secretv1beta1.SecureValue } -func CreateSvWithSv(sv *secretv0alpha1.SecureValue) func(*CreateSvConfig) { +func CreateSvWithSv(sv *secretv1beta1.SecureValue) func(*CreateSvConfig) { return func(cfg *CreateSvConfig) { cfg.Sv = sv } } -func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*secretv0alpha1.SecureValue, error) { +func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*secretv1beta1.SecureValue, error) { cfg := CreateSvConfig{ - Sv: &secretv0alpha1.SecureValue{ + Sv: &secretv1beta1.SecureValue{ ObjectMeta: metav1.ObjectMeta{ Name: "sv1", Namespace: "ns1", }, - Spec: secretv0alpha1.SecureValueSpec{ + Spec: secretv1beta1.SecureValueSpec{ Description: "desc1", - Value: secretv0alpha1.NewExposedSecureValue("v1"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("v1")), Decrypters: []string{"decrypter1"}, }, - Status: secretv0alpha1.SecureValueStatus{}, + Status: secretv1beta1.SecureValueStatus{}, }, } for _, opt := range opts { @@ -173,12 +174,12 @@ func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*sec return createdSv, nil } -func (s *Sut) UpdateSv(ctx context.Context, sv *secretv0alpha1.SecureValue) (*secretv0alpha1.SecureValue, error) { +func (s *Sut) UpdateSv(ctx context.Context, sv *secretv1beta1.SecureValue) (*secretv1beta1.SecureValue, error) { newSv, _, err := s.SecureValueService.Update(ctx, sv, "actor-uid") return newSv, err } -func (s *Sut) DeleteSv(ctx context.Context, namespace, name string) (*secretv0alpha1.SecureValue, error) { +func (s *Sut) DeleteSv(ctx context.Context, namespace, name string) (*secretv1beta1.SecureValue, error) { sv, err := s.SecureValueService.Delete(ctx, xkube.Namespace(namespace), name) return sv, err } @@ -191,7 +192,7 @@ func newKeeperServiceWrapper(keeper contracts.Keeper) *keeperServiceWrapper { return &keeperServiceWrapper{keeper: keeper} } -func (wrapper *keeperServiceWrapper) KeeperForConfig(cfg secretv0alpha1.KeeperConfig) (contracts.Keeper, error) { +func (wrapper *keeperServiceWrapper) KeeperForConfig(cfg secretv1beta1.KeeperConfig) (contracts.Keeper, error) { return wrapper.keeper, nil } diff --git a/pkg/storage/secret/metadata/decrypt_store.go b/pkg/storage/secret/metadata/decrypt_store.go index e907374748a..93d212299cb 100644 --- a/pkg/storage/secret/metadata/decrypt_store.go +++ b/pkg/storage/secret/metadata/decrypt_store.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/trace" "github.com/grafana/grafana-app-sdk/logging" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -60,7 +60,7 @@ type decryptStorage struct { } // Decrypt decrypts a secure value from the keeper. -func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (_ secretv0alpha1.ExposedSecureValue, decryptErr error) { +func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (_ secretv1beta1.ExposedSecureValue, decryptErr error) { ctx, span := s.tracer.Start(ctx, "DecryptStorage.Decrypt", trace.WithAttributes( attribute.String("namespace", namespace.String()), attribute.String("name", name), diff --git a/pkg/storage/secret/metadata/decrypt_store_test.go b/pkg/storage/secret/metadata/decrypt_store_test.go index 5ebbb2f9466..b3ad4643b8a 100644 --- a/pkg/storage/secret/metadata/decrypt_store_test.go +++ b/pkg/storage/secret/metadata/decrypt_store_test.go @@ -7,9 +7,10 @@ import ( "github.com/grafana/authlib/authn" "github.com/grafana/authlib/types" "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/identity" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/testutils" ) @@ -73,12 +74,12 @@ func TestIntegrationDecrypt(t *testing.T) { })) // Create a secure value that is not in the allowlist - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = svName sv.Namespace = "default" @@ -110,12 +111,12 @@ func TestIntegrationDecrypt(t *testing.T) { })) // Create a secure value that is in the allowlist - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = "sv-test" sv.Namespace = "default" @@ -149,12 +150,12 @@ func TestIntegrationDecrypt(t *testing.T) { })) // Create a secure value that is in the allowlist - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = svName sv.Namespace = "default" @@ -181,12 +182,12 @@ func TestIntegrationDecrypt(t *testing.T) { sut := testutils.Setup(t) // Create a secure value - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = "sv-test" sv.Namespace = "default" @@ -214,12 +215,12 @@ func TestIntegrationDecrypt(t *testing.T) { sut := testutils.Setup(t) // Create a secure value - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = svName sv.Namespace = "default" @@ -246,12 +247,12 @@ func TestIntegrationDecrypt(t *testing.T) { sut := testutils.Setup(t) // Create a secure value - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = "sv-test" sv.Namespace = "default" @@ -279,12 +280,12 @@ func TestIntegrationDecrypt(t *testing.T) { sut := testutils.Setup(t) // Create a secure value - spec := secretv0alpha1.SecureValueSpec{ + spec := secretv1beta1.SecureValueSpec{ Description: "description", Decrypters: []string{svcIdentity}, - Value: secretv0alpha1.NewExposedSecureValue("value"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")), } - sv := &secretv0alpha1.SecureValue{Spec: spec} + sv := &secretv1beta1.SecureValue{Spec: spec} sv.Name = svName sv.Namespace = "default" diff --git a/pkg/storage/secret/metadata/keeper_model.go b/pkg/storage/secret/metadata/keeper_model.go index ae8ef7bb908..8fd43dc35e0 100644 --- a/pkg/storage/secret/metadata/keeper_model.go +++ b/pkg/storage/secret/metadata/keeper_model.go @@ -6,8 +6,8 @@ import ( "time" "github.com/google/uuid" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/utils" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/storage/secret/migrator" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,7 +37,7 @@ func (*keeperDB) TableName() string { } // toKubernetes maps a DB row into a Kubernetes resource (metadata + spec). -func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) { +func (kp *keeperDB) toKubernetes() (*secretv1beta1.Keeper, error) { annotations := make(map[string]string, 0) if kp.Annotations != "" { if err := json.Unmarshal([]byte(kp.Annotations), &annotations); err != nil { @@ -52,23 +52,23 @@ func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) { } } - resource := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + resource := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: kp.Description, }, } // Obtain provider configs - provider := toProvider(secretv0alpha1.KeeperType(kp.Type), kp.Payload) + provider := toProvider(secretv1beta1.KeeperType(kp.Type), kp.Payload) switch v := provider.(type) { - case *secretv0alpha1.AWSKeeperConfig: - resource.Spec.AWS = v - case *secretv0alpha1.AzureKeeperConfig: + case *secretv1beta1.KeeperAWSConfig: + resource.Spec.Aws = v + case *secretv1beta1.KeeperAzureConfig: resource.Spec.Azure = v - case *secretv0alpha1.GCPKeeperConfig: - resource.Spec.GCP = v - case *secretv0alpha1.HashiCorpKeeperConfig: - resource.Spec.HashiCorp = v + case *secretv1beta1.KeeperGCPConfig: + resource.Spec.Gcp = v + case *secretv1beta1.KeeperHashiCorpConfig: + resource.Spec.HashiCorpVault = v } // Set all meta fields here for consistency. @@ -94,7 +94,7 @@ func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) { } // toKeeperCreateRow maps a Kubernetes resource into a DB row for new resources being created/inserted. -func toKeeperCreateRow(kp *secretv0alpha1.Keeper, actorUID string) (*keeperDB, error) { +func toKeeperCreateRow(kp *secretv1beta1.Keeper, actorUID string) (*keeperDB, error) { row, err := toKeeperRow(kp) if err != nil { return nil, fmt.Errorf("failed to map to row: %w", err) @@ -112,7 +112,7 @@ func toKeeperCreateRow(kp *secretv0alpha1.Keeper, actorUID string) (*keeperDB, e } // toKeeperUpdateRow maps a Kubernetes resource into a DB row for existing resources being updated. -func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv0alpha1.Keeper, actorUID string) (*keeperDB, error) { +func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv1beta1.Keeper, actorUID string) (*keeperDB, error) { row, err := toKeeperRow(newKeeper) if err != nil { return nil, fmt.Errorf("failed to map to row: %w", err) @@ -130,7 +130,7 @@ func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv0alpha1.Keeper, a } // toKeeperRow maps a Kubernetes Keeper resource into a Keeper DB row. -func toKeeperRow(kp *secretv0alpha1.Keeper) (*keeperDB, error) { +func toKeeperRow(kp *secretv1beta1.Keeper) (*keeperDB, error) { var annotations string if len(kp.Annotations) > 0 { cleanedAnnotations := xkube.CleanAnnotations(kp.Annotations) @@ -196,19 +196,19 @@ func toKeeperRow(kp *secretv0alpha1.Keeper) (*keeperDB, error) { // toTypeAndPayload obtain keeper type and payload from a Kubernetes Keeper resource. // TODO: Move as method of KeeperSpec -func toTypeAndPayload(kp *secretv0alpha1.Keeper) (secretv0alpha1.KeeperType, string, error) { - if kp.Spec.AWS != nil { - payload, err := json.Marshal(kp.Spec.AWS.AWSCredentials) - return secretv0alpha1.AWSKeeperType, string(payload), err +func toTypeAndPayload(kp *secretv1beta1.Keeper) (secretv1beta1.KeeperType, string, error) { + if kp.Spec.Aws != nil { + payload, err := json.Marshal(kp.Spec.Aws) + return secretv1beta1.AWSKeeperType, string(payload), err } else if kp.Spec.Azure != nil { payload, err := json.Marshal(kp.Spec.Azure) - return secretv0alpha1.AzureKeeperType, string(payload), err - } else if kp.Spec.GCP != nil { - payload, err := json.Marshal(kp.Spec.GCP) - return secretv0alpha1.GCPKeeperType, string(payload), err - } else if kp.Spec.HashiCorp != nil { - payload, err := json.Marshal(kp.Spec.HashiCorp) - return secretv0alpha1.HashiCorpKeeperType, string(payload), err + return secretv1beta1.AzureKeeperType, string(payload), err + } else if kp.Spec.Gcp != nil { + payload, err := json.Marshal(kp.Spec.Gcp) + return secretv1beta1.GCPKeeperType, string(payload), err + } else if kp.Spec.HashiCorpVault != nil { + payload, err := json.Marshal(kp.Spec.HashiCorpVault) + return secretv1beta1.HashiCorpKeeperType, string(payload), err } return "", "", fmt.Errorf("no keeper type found") @@ -216,28 +216,28 @@ func toTypeAndPayload(kp *secretv0alpha1.Keeper) (secretv0alpha1.KeeperType, str // toProvider maps a KeeperType and payload into a provider config struct. // TODO: Move as method of KeeperType -func toProvider(keeperType secretv0alpha1.KeeperType, payload string) secretv0alpha1.KeeperConfig { +func toProvider(keeperType secretv1beta1.KeeperType, payload string) secretv1beta1.KeeperConfig { switch keeperType { - case secretv0alpha1.AWSKeeperType: - aws := &secretv0alpha1.AWSKeeperConfig{} + case secretv1beta1.AWSKeeperType: + aws := &secretv1beta1.KeeperAWSConfig{} if err := json.Unmarshal([]byte(payload), aws); err != nil { return nil } return aws - case secretv0alpha1.AzureKeeperType: - azure := &secretv0alpha1.AzureKeeperConfig{} + case secretv1beta1.AzureKeeperType: + azure := &secretv1beta1.KeeperAzureConfig{} if err := json.Unmarshal([]byte(payload), azure); err != nil { return nil } return azure - case secretv0alpha1.GCPKeeperType: - gcp := &secretv0alpha1.GCPKeeperConfig{} + case secretv1beta1.GCPKeeperType: + gcp := &secretv1beta1.KeeperGCPConfig{} if err := json.Unmarshal([]byte(payload), gcp); err != nil { return nil } return gcp - case secretv0alpha1.HashiCorpKeeperType: - hashicorp := &secretv0alpha1.HashiCorpKeeperConfig{} + case secretv1beta1.HashiCorpKeeperType: + hashicorp := &secretv1beta1.KeeperHashiCorpConfig{} if err := json.Unmarshal([]byte(payload), hashicorp); err != nil { return nil } @@ -248,17 +248,17 @@ func toProvider(keeperType secretv0alpha1.KeeperType, payload string) secretv0al } // extractSecureValues extracts unique securevalues referenced by the keeper, if any. -func extractSecureValues(kp *secretv0alpha1.Keeper) map[string]struct{} { +func extractSecureValues(kp *secretv1beta1.Keeper) map[string]struct{} { switch { - case kp.Spec.AWS != nil: + case kp.Spec.Aws != nil: secureValues := make(map[string]struct{}, 0) - if kp.Spec.AWS.AccessKeyID.SecureValueName != "" { - secureValues[kp.Spec.AWS.AccessKeyID.SecureValueName] = struct{}{} + if kp.Spec.Aws.AccessKeyID.SecureValueName != "" { + secureValues[kp.Spec.Aws.AccessKeyID.SecureValueName] = struct{}{} } - if kp.Spec.AWS.SecretAccessKey.SecureValueName != "" { - secureValues[kp.Spec.AWS.SecretAccessKey.SecureValueName] = struct{}{} + if kp.Spec.Aws.SecretAccessKey.SecureValueName != "" { + secureValues[kp.Spec.Aws.SecretAccessKey.SecureValueName] = struct{}{} } return secureValues @@ -269,12 +269,12 @@ func extractSecureValues(kp *secretv0alpha1.Keeper) map[string]struct{} { } // GCP does not reference secureValues. - case kp.Spec.GCP != nil: + case kp.Spec.Gcp != nil: return nil - case kp.Spec.HashiCorp != nil: - if kp.Spec.HashiCorp.Token.SecureValueName != "" { - return map[string]struct{}{kp.Spec.HashiCorp.Token.SecureValueName: {}} + case kp.Spec.HashiCorpVault != nil: + if kp.Spec.HashiCorpVault.Token.SecureValueName != "" { + return map[string]struct{}{kp.Spec.HashiCorpVault.Token.SecureValueName: {}} } } diff --git a/pkg/storage/secret/metadata/keeper_store.go b/pkg/storage/secret/metadata/keeper_store.go index defc9974125..6b4e73fc2bd 100644 --- a/pkg/storage/secret/metadata/keeper_store.go +++ b/pkg/storage/secret/metadata/keeper_store.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -46,7 +46,7 @@ func ProvideKeeperMetadataStorage( }, nil } -func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) { +func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Create", trace.WithAttributes( attribute.String("name", keeper.GetName()), @@ -111,7 +111,7 @@ func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv0alph return createdKeeper, nil } -func (s *keeperMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv0alpha1.Keeper, error) { +func (s *keeperMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv1beta1.Keeper, error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Read", trace.WithAttributes( attribute.String("name", name), @@ -174,7 +174,7 @@ func (s *keeperMetadataStorage) read(ctx context.Context, namespace, name string return &keeper, nil } -func (s *keeperMetadataStorage) Update(ctx context.Context, newKeeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) { +func (s *keeperMetadataStorage) Update(ctx context.Context, newKeeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Update", trace.WithAttributes( attribute.String("name", newKeeper.GetName()), @@ -291,7 +291,7 @@ func (s *keeperMetadataStorage) Delete(ctx context.Context, namespace xkube.Name return nil } -func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (keeperList []secretv0alpha1.Keeper, err error) { +func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (keeperList []secretv1beta1.Keeper, err error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.List", trace.WithAttributes( attribute.String("namespace", namespace.String()), @@ -318,7 +318,7 @@ func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namesp } defer func() { _ = rows.Close() }() - keepers := make([]secretv0alpha1.Keeper, 0) + keepers := make([]secretv1beta1.Keeper, 0) for rows.Next() { var row keeperDB @@ -350,7 +350,7 @@ func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namesp // validateSecureValueReferences checks that all secure values referenced by the keeper exist and are not referenced by other third-party keepers. // It is used by other methods inside a transaction. -func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Context, keeper *secretv0alpha1.Keeper) (err error) { +func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Context, keeper *secretv1beta1.Keeper) (err error) { ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.ValidateSecureValueReferences", trace.WithAttributes( attribute.String("name", keeper.GetName()), attribute.String("namespace", keeper.GetNamespace()), @@ -497,7 +497,7 @@ func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Contex return nil } -func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace string, name *string, opts contracts.ReadOpts) (secretv0alpha1.KeeperConfig, error) { +func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace string, name *string, opts contracts.ReadOpts) (secretv1beta1.KeeperConfig, error) { ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.GetKeeperConfig", trace.WithAttributes( attribute.String("namespace", namespace), attribute.Bool("isForUpdate", opts.ForUpdate), @@ -506,7 +506,7 @@ func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace s // Check if keeper is the systemwide one. if name == nil { - return &secretv0alpha1.SystemKeeperConfig{}, nil + return &secretv1beta1.SystemKeeperConfig{}, nil } start := time.Now() @@ -518,7 +518,7 @@ func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace s return nil, err } - keeperConfig := toProvider(secretv0alpha1.KeeperType(kp.Type), kp.Payload) + keeperConfig := toProvider(secretv1beta1.KeeperType(kp.Type), kp.Payload) s.metrics.KeeperMetadataGetKeeperConfigDuration.Observe(time.Since(start).Seconds()) diff --git a/pkg/storage/secret/metadata/keeper_store_test.go b/pkg/storage/secret/metadata/keeper_store_test.go index c5da14eaf2d..02f2207b61f 100644 --- a/pkg/storage/secret/metadata/keeper_store_test.go +++ b/pkg/storage/secret/metadata/keeper_store_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/storage/secret/migrator" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" + "k8s.io/utils/ptr" ) func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { @@ -22,10 +23,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { defaultKeeperName := "kp-test" defaultKeeperNS := "default" - testKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + testKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } @@ -41,7 +42,7 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { // get system keeper config keeperConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, defaultKeeperNS, nil, contracts.ReadOpts{}) require.NoError(t, err) - require.IsType(t, &secretv0alpha1.SystemKeeperConfig{}, keeperConfig) + require.IsType(t, &secretv1beta1.SystemKeeperConfig{}, keeperConfig) }) t.Run("get test keeper config", func(t *testing.T) { @@ -90,10 +91,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { keeperTest := "kp-test2" keeperNamespaceTest := "ns" - testKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + testKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "another description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } testKeeper.Name = keeperTest @@ -128,10 +129,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { keeperNamespaceTest := "ns" // Create initial keeper - initialKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + initialKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "initial description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } initialKeeper.Name = keeperTest @@ -147,10 +148,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { require.Equal(t, "initial description", keeper.Spec.Description) // Update the keeper with new values - updatedKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + updatedKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "updated description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } updatedKeeper.Name = keeperTest @@ -182,19 +183,17 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { keeperNamespaceTest := "ns" // Create initial keeper with first AWS config - initialKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + initialKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "initial description", - AWS: &secretv0alpha1.AWSKeeperConfig{ - AWSCredentials: secretv0alpha1.AWSCredentials{ - AccessKeyID: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_ACCESS_KEY_ID_1", - }, - SecretAccessKey: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_SECRET_ACCESS_KEY_1", - }, - KMSKeyID: "kms-key-id-1", + Aws: &secretv1beta1.KeeperAWSConfig{ + AccessKeyID: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_ACCESS_KEY_ID_1", }, + SecretAccessKey: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_SECRET_ACCESS_KEY_1", + }, + KmsKeyID: ptr.To("kms-key-id-1"), }, }, } @@ -208,24 +207,22 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { // Verify initial AWS config keeper, err := keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{}) require.NoError(t, err) - require.Equal(t, "AWS_ACCESS_KEY_ID_1", keeper.Spec.AWS.AccessKeyID.ValueFromEnv) - require.Equal(t, "AWS_SECRET_ACCESS_KEY_1", keeper.Spec.AWS.SecretAccessKey.ValueFromEnv) - require.Equal(t, "kms-key-id-1", keeper.Spec.AWS.KMSKeyID) + require.Equal(t, "AWS_ACCESS_KEY_ID_1", keeper.Spec.Aws.AccessKeyID.ValueFromEnv) + require.Equal(t, "AWS_SECRET_ACCESS_KEY_1", keeper.Spec.Aws.SecretAccessKey.ValueFromEnv) + require.Equal(t, "kms-key-id-1", *keeper.Spec.Aws.KmsKeyID) // Update with new AWS config - updatedKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + updatedKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "updated description", - AWS: &secretv0alpha1.AWSKeeperConfig{ - AWSCredentials: secretv0alpha1.AWSCredentials{ - AccessKeyID: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_ACCESS_KEY_ID_2", - }, - SecretAccessKey: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_SECRET_ACCESS_KEY_2", - }, - KMSKeyID: "kms-key-id-2", + Aws: &secretv1beta1.KeeperAWSConfig{ + AccessKeyID: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_ACCESS_KEY_ID_2", }, + SecretAccessKey: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_SECRET_ACCESS_KEY_2", + }, + KmsKeyID: ptr.To("kms-key-id-2"), }, }, } @@ -239,9 +236,9 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { // Verify updated AWS config updatedKeeper, err = keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{}) require.NoError(t, err) - require.Equal(t, "AWS_ACCESS_KEY_ID_2", updatedKeeper.Spec.AWS.AccessKeyID.ValueFromEnv) - require.Equal(t, "AWS_SECRET_ACCESS_KEY_2", updatedKeeper.Spec.AWS.SecretAccessKey.ValueFromEnv) - require.Equal(t, "kms-key-id-2", updatedKeeper.Spec.AWS.KMSKeyID) + require.Equal(t, "AWS_ACCESS_KEY_ID_2", updatedKeeper.Spec.Aws.AccessKeyID.ValueFromEnv) + require.Equal(t, "AWS_SECRET_ACCESS_KEY_2", updatedKeeper.Spec.Aws.SecretAccessKey.ValueFromEnv) + require.Equal(t, "kms-key-id-2", *updatedKeeper.Spec.Aws.KmsKeyID) }) t.Run("list keepers in empty namespace", func(t *testing.T) { @@ -276,17 +273,15 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { keeperNamespaceTest := "ns1" // Create initial keeper - initialKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + initialKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "initial description", - AWS: &secretv0alpha1.AWSKeeperConfig{ - AWSCredentials: secretv0alpha1.AWSCredentials{ - AccessKeyID: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_ACCESS_KEY_ID", - }, - SecretAccessKey: secretv0alpha1.CredentialValue{ - ValueFromEnv: "AWS_SECRET_ACCESS_KEY", - }, + Aws: &secretv1beta1.KeeperAWSConfig{ + AccessKeyID: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_ACCESS_KEY_ID", + }, + SecretAccessKey: secretv1beta1.KeeperCredentialValue{ + ValueFromEnv: "AWS_SECRET_ACCESS_KEY", }, }, }, @@ -320,10 +315,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) { ctx := context.Background() keeperMetadataStorage := initStorage(t) - nonExistentKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + nonExistentKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "some description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } nonExistentKeeper.Name = "non-existent" diff --git a/pkg/storage/secret/metadata/secure_value_model.go b/pkg/storage/secret/metadata/secure_value_model.go index 134128f8a25..c2f59a6e4c1 100644 --- a/pkg/storage/secret/metadata/secure_value_model.go +++ b/pkg/storage/secret/metadata/secure_value_model.go @@ -7,8 +7,8 @@ import ( "time" "github.com/google/uuid" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/utils" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/storage/secret/migrator" @@ -45,7 +45,7 @@ func (*secureValueDB) TableName() string { } // toKubernetes maps a DB row into a Kubernetes resource (metadata + spec). -func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) { +func (sv *secureValueDB) toKubernetes() (*secretv1beta1.SecureValue, error) { annotations := make(map[string]string, 0) if sv.Annotations != "" { if err := json.Unmarshal([]byte(sv.Annotations), &annotations); err != nil { @@ -68,12 +68,12 @@ func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) { } } - resource := &secretv0alpha1.SecureValue{ - Spec: secretv0alpha1.SecureValueSpec{ + resource := &secretv1beta1.SecureValue{ + Spec: secretv1beta1.SecureValueSpec{ Description: sv.Description, Decrypters: decrypters, }, - Status: secretv0alpha1.SecureValueStatus{ + Status: secretv1beta1.SecureValueStatus{ ExternalID: sv.ExternalID, Version: sv.Version, }, @@ -111,7 +111,7 @@ func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) { } // toCreateRow maps a Kubernetes resource into a DB row for new resources being created/inserted. -func toCreateRow(sv *secretv0alpha1.SecureValue, actorUID string) (*secureValueDB, error) { +func toCreateRow(sv *secretv1beta1.SecureValue, actorUID string) (*secureValueDB, error) { row, err := toRow(sv, "") if err != nil { return nil, fmt.Errorf("failed to convert SecureValue to secureValueDB: %w", err) @@ -129,7 +129,7 @@ func toCreateRow(sv *secretv0alpha1.SecureValue, actorUID string) (*secureValueD } // toRow maps a Kubernetes resource into a DB row. -func toRow(sv *secretv0alpha1.SecureValue, externalID string) (*secureValueDB, error) { +func toRow(sv *secretv1beta1.SecureValue, externalID string) (*secureValueDB, error) { var annotations string if len(sv.Annotations) > 0 { cleanedAnnotations := xkube.CleanAnnotations(sv.Annotations) diff --git a/pkg/storage/secret/metadata/secure_value_store.go b/pkg/storage/secret/metadata/secure_value_store.go index ecc22df28bd..3653c6006df 100644 --- a/pkg/storage/secret/metadata/secure_value_store.go +++ b/pkg/storage/secret/metadata/secure_value_store.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -46,7 +46,7 @@ type secureValueMetadataStorage struct { tracer trace.Tracer } -func (s *secureValueMetadataStorage) Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) { +func (s *secureValueMetadataStorage) Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.Create", trace.WithAttributes( attribute.String("name", sv.GetName()), @@ -289,7 +289,7 @@ func (s *secureValueMetadataStorage) readActiveVersion(ctx context.Context, name return secureValue, nil } -func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv0alpha1.SecureValue, error) { +func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv1beta1.SecureValue, error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.Read", trace.WithAttributes( attribute.String("name", name), @@ -314,7 +314,7 @@ func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.N return secureValueKub, nil } -func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (svList []secretv0alpha1.SecureValue, error error) { +func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (svList []secretv1beta1.SecureValue, error error) { start := time.Now() ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.List", trace.WithAttributes( attribute.String("namespace", namespace.String()), @@ -341,7 +341,7 @@ func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.N } defer func() { _ = rows.Close() }() - secureValues := make([]secretv0alpha1.SecureValue, 0) + secureValues := make([]secretv1beta1.SecureValue, 0) for rows.Next() { row := secureValueDB{} diff --git a/pkg/storage/secret/metadata/secure_value_store_test.go b/pkg/storage/secret/metadata/secure_value_store_test.go index 1de2dbf73f1..4014d497264 100644 --- a/pkg/storage/secret/metadata/secure_value_store_test.go +++ b/pkg/storage/secret/metadata/secure_value_store_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -14,15 +14,16 @@ import ( "github.com/grafana/grafana/pkg/storage/secret/migrator" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" + "k8s.io/utils/ptr" ) func createTestKeeper(t *testing.T, ctx context.Context, keeperStorage contracts.KeeperMetadataStorage, name, namespace string) string { t.Helper() - testKeeper := &secretv0alpha1.Keeper{ - Spec: secretv0alpha1.KeeperSpec{ + testKeeper := &secretv1beta1.Keeper{ + Spec: secretv1beta1.KeeperSpec{ Description: "test keeper description", - AWS: &secretv0alpha1.AWSKeeperConfig{}, + Aws: &secretv1beta1.KeeperAWSConfig{}, }, } testKeeper.Name = name @@ -56,10 +57,10 @@ func Test_SecureValueMetadataStorage_CreateAndRead(t *testing.T) { keeperName := createTestKeeper(t, ctx, keeperStorage, "test-keeper", "default") // Create a test secure value - testSecureValue := &secretv0alpha1.SecureValue{ - Spec: secretv0alpha1.SecureValueSpec{ + testSecureValue := &secretv1beta1.SecureValue{ + Spec: secretv1beta1.SecureValueSpec{ Description: "test description", - Value: "test-value", + Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value")), Keeper: &keeperName, }, } @@ -110,10 +111,10 @@ func Test_SecureValueMetadataStorage_CreateAndRead(t *testing.T) { keeperName := createTestKeeper(t, ctx, keeperStorage, "test-keeper-2", "default") // Create a test secure value - testSecureValue := &secretv0alpha1.SecureValue{ - Spec: secretv0alpha1.SecureValueSpec{ + testSecureValue := &secretv1beta1.SecureValue{ + Spec: secretv1beta1.SecureValueSpec{ Description: "test description 2", - Value: "test-value-2", + Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value-2")), Keeper: &keeperName, }, } diff --git a/pkg/storage/secret/metadata/secure_value_test.go b/pkg/storage/secret/metadata/secure_value_test.go index 459db29f74a..d35213ecd77 100644 --- a/pkg/storage/secret/metadata/secure_value_test.go +++ b/pkg/storage/secret/metadata/secure_value_test.go @@ -5,18 +5,20 @@ import ( "slices" "testing" - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/service" "github.com/grafana/grafana/pkg/registry/apis/secret/testutils" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" + "github.com/mitchellh/copystructure" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "pgregory.net/rapid" ) type modelSecureValue struct { - *secretv0alpha1.SecureValue + *secretv1beta1.SecureValue active bool } @@ -66,7 +68,7 @@ func (m *model) readActiveVersion(namespace, name string) *modelSecureValue { return nil } -func (m *model) create(sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) { +func (m *model) create(sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) { modelSv := &modelSecureValue{sv, false} modelSv.Status.Version = m.getNewVersionNumber(modelSv.Namespace, modelSv.Name) modelSv.Status.ExternalID = fmt.Sprintf("%d", modelSv.Status.Version) @@ -75,9 +77,9 @@ func (m *model) create(sv *secretv0alpha1.SecureValue, actorUID string) (*secret return modelSv.SecureValue, nil } -func (m *model) update(newSecureValue *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, bool, error) { +func (m *model) update(newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) { // If the payload doesn't contain a value, get the value from current version - if newSecureValue.Spec.Value == "" { + if newSecureValue.Spec.Value == nil { sv := m.readActiveVersion(newSecureValue.Namespace, newSecureValue.Name) if sv == nil { return nil, false, contracts.ErrSecureValueNotFound @@ -88,7 +90,7 @@ func (m *model) update(newSecureValue *secretv0alpha1.SecureValue, actorUID stri return createdSv, true, err } -func (m *model) delete(namespace, name string) (*secretv0alpha1.SecureValue, error) { +func (m *model) delete(namespace, name string) (*secretv1beta1.SecureValue, error) { modelSv := m.readActiveVersion(namespace, name) if modelSv == nil { return nil, contracts.ErrSecureValueNotFound @@ -97,8 +99,8 @@ func (m *model) delete(namespace, name string) (*secretv0alpha1.SecureValue, err return modelSv.SecureValue, nil } -func (m *model) list(namespace string) (*secretv0alpha1.SecureValueList, error) { - out := make([]secretv0alpha1.SecureValue, 0) +func (m *model) list(namespace string) (*secretv1beta1.SecureValueList, error) { + out := make([]secretv1beta1.SecureValue, 0) for _, v := range m.secureValues { if v.Namespace == namespace && v.active { @@ -106,7 +108,7 @@ func (m *model) list(namespace string) (*secretv0alpha1.SecureValueList, error) } } - return &secretv0alpha1.SecureValueList{Items: out}, nil + return &secretv1beta1.SecureValueList{Items: out}, nil } func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.DecryptResult, error) { @@ -116,7 +118,7 @@ func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.D v.active { if slices.ContainsFunc(v.Spec.Decrypters, func(d string) bool { return d == decrypter }) { return map[string]service.DecryptResult{ - name: service.NewDecryptResultValue(&v.DeepCopy().Spec.Value), + name: service.NewDecryptResultValue(deepCopy(v).Spec.Value), }, nil } @@ -130,7 +132,7 @@ func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.D }, nil } -func (m *model) read(namespace, name string) (*secretv0alpha1.SecureValue, error) { +func (m *model) read(namespace, name string) (*secretv1beta1.SecureValue, error) { modelSv := m.readActiveVersion(namespace, name) if modelSv == nil { return nil, contracts.ErrSecureValueNotFound @@ -142,25 +144,25 @@ var ( decryptersGen = rapid.SampledFrom([]string{"svc1", "svc2", "svc3", "svc4", "svc5"}) nameGen = rapid.SampledFrom([]string{"n1", "n2", "n3", "n4", "n5"}) namespaceGen = rapid.SampledFrom([]string{"ns1", "ns2", "ns3", "ns4", "ns5"}) - anySecureValueGen = rapid.Custom(func(t *rapid.T) *secretv0alpha1.SecureValue { - return &secretv0alpha1.SecureValue{ + anySecureValueGen = rapid.Custom(func(t *rapid.T) *secretv1beta1.SecureValue { + return &secretv1beta1.SecureValue{ ObjectMeta: metav1.ObjectMeta{ Name: nameGen.Draw(t, "name"), Namespace: namespaceGen.Draw(t, "ns"), }, - Spec: secretv0alpha1.SecureValueSpec{ + Spec: secretv1beta1.SecureValueSpec{ Description: rapid.SampledFrom([]string{"d1", "d2", "d3", "d4", "d5"}).Draw(t, "description"), - Value: secretv0alpha1.NewExposedSecureValue(rapid.SampledFrom([]string{"v1", "v2", "v3", "v4", "v5"}).Draw(t, "value")), + Value: ptr.To(secretv1beta1.NewExposedSecureValue(rapid.SampledFrom([]string{"v1", "v2", "v3", "v4", "v5"}).Draw(t, "value"))), Decrypters: rapid.SliceOfDistinct(decryptersGen, func(v string) string { return v }).Draw(t, "decrypters"), }, - Status: secretv0alpha1.SecureValueStatus{}, + Status: secretv1beta1.SecureValueStatus{}, } }) - updateSecureValueGen = rapid.Custom(func(t *rapid.T) *secretv0alpha1.SecureValue { + updateSecureValueGen = rapid.Custom(func(t *rapid.T) *secretv1beta1.SecureValue { sv := anySecureValueGen.Draw(t, "sv") // Maybe update the secret value, maybe not if !rapid.Bool().Draw(t, "should_update_value") { - sv.Spec.Value = "" + sv.Spec.Value = nil } return sv }) @@ -184,17 +186,17 @@ type decryptInput struct { func TestModel(t *testing.T) { t.Parallel() - sv := &secretv0alpha1.SecureValue{ + sv := &secretv1beta1.SecureValue{ ObjectMeta: metav1.ObjectMeta{ Name: "sv1", Namespace: "ns1", }, - Spec: secretv0alpha1.SecureValueSpec{ + Spec: secretv1beta1.SecureValueSpec{ Description: "desc1", - Value: secretv0alpha1.NewExposedSecureValue("v1"), + Value: ptr.To(secretv1beta1.NewExposedSecureValue("v1")), Decrypters: []string{"decrypter1"}, }, - Status: secretv0alpha1.SecureValueStatus{}, + Status: secretv1beta1.SecureValueStatus{}, } t.Run("creating secure values", func(t *testing.T) { @@ -203,14 +205,14 @@ func TestModel(t *testing.T) { m := newModel() // Create a secure value - sv1, err := m.create(sv.DeepCopy(), "actor-uid") + sv1, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) require.Equal(t, sv.Namespace, sv1.Namespace) require.Equal(t, sv.Name, sv1.Name) require.EqualValues(t, 1, sv1.Status.Version) // Create a new version of a secure value - sv2, err := m.create(sv.DeepCopy(), "actor-uid") + sv2, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) require.Equal(t, sv.Namespace, sv2.Namespace) require.Equal(t, sv.Name, sv2.Name) @@ -222,27 +224,27 @@ func TestModel(t *testing.T) { m := newModel() - sv1, err := m.create(sv.DeepCopy(), "actor-uid") + sv1, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) // Create a new version of a secure value by updating it - sv2, _, err := m.update(sv1.DeepCopy(), "actor-uid") + sv2, _, err := m.update(deepCopy(sv1), "actor-uid") require.NoError(t, err) require.Equal(t, sv.Namespace, sv2.Namespace) require.Equal(t, sv.Name, sv2.Name) require.EqualValues(t, 2, sv2.Status.Version) // Try updating a secure value that doesn't exist without specifying a value for it - sv3 := sv2.DeepCopy() + sv3 := deepCopy(sv2) sv3.Name = "i_dont_exist" - sv3.Spec.Value = "" + sv3.Spec.Value = nil _, _, err = m.update(sv3, "actor-uid") require.ErrorIs(t, err, contracts.ErrSecureValueNotFound) // Updating a value that doesn't exist creates a new version - sv4 := sv3.DeepCopy() + sv4 := deepCopy(sv3) sv4.Name = "i_dont_exist" - sv4.Spec.Value = secretv0alpha1.NewExposedSecureValue("sv4") + sv4.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("sv4")) sv4, _, err = m.update(sv4, "actor-uid") require.NoError(t, err) require.EqualValues(t, 1, sv4.Status.Version) @@ -253,7 +255,7 @@ func TestModel(t *testing.T) { m := newModel() - sv1, err := m.create(sv.DeepCopy(), "actor-uid") + sv1, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) // Deleting a secure value @@ -279,7 +281,7 @@ func TestModel(t *testing.T) { require.Equal(t, 0, len(list.Items)) // Create a secure value - sv1, err := m.create(sv.DeepCopy(), "actor-uid") + sv1, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) // 1 secure value exists and it should be returned @@ -304,7 +306,8 @@ func TestModel(t *testing.T) { require.ErrorIs(t, result["name"].Error(), contracts.ErrDecryptNotFound) // Create a secure value - sv1, err := m.create(sv.DeepCopy(), "actor-uid") + secret := "v1" + sv1, err := m.create(deepCopy(sv), "actor-uid") require.NoError(t, err) // Decrypt the just created secure value @@ -312,7 +315,7 @@ func TestModel(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(result)) require.Nil(t, result[sv1.Name].Error()) - require.Equal(t, result[sv1.Name].Value().DangerouslyExposeAndConsumeValue(), sv.DeepCopy().Spec.Value.DangerouslyExposeAndConsumeValue()) + require.Equal(t, secret, result[sv1.Name].Value().DangerouslyExposeAndConsumeValue()) }) } @@ -328,9 +331,10 @@ func TestStateMachine(t *testing.T) { t.Repeat(map[string]func(*rapid.T){ "create": func(t *rapid.T) { sv := anySecureValueGen.Draw(t, "sv") - modelCreatedSv, modelErr := model.create(sv.DeepCopy(), "actor-uid") - createdSv, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(sv.DeepCopy())) + modelCreatedSv, modelErr := model.create(deepCopy(sv), "actor-uid") + + createdSv, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(deepCopy(sv))) if err != nil || modelErr != nil { require.ErrorIs(t, err, modelErr) return @@ -341,8 +345,8 @@ func TestStateMachine(t *testing.T) { }, "update": func(t *rapid.T) { sv := updateSecureValueGen.Draw(t, "sv") - modelCreatedSv, _, modelErr := model.update(sv.DeepCopy(), "actor-uid") - createdSv, err := sut.UpdateSv(t.Context(), sv.DeepCopy()) + modelCreatedSv, _, modelErr := model.update(deepCopy(sv), "actor-uid") + createdSv, err := sut.UpdateSv(t.Context(), deepCopy(sv)) if err != nil || modelErr != nil { require.ErrorIs(t, err, modelErr) return @@ -379,7 +383,7 @@ func TestStateMachine(t *testing.T) { // PERFORMANCE: The lists are always small for _, v1 := range modelList.Items { - if !slices.ContainsFunc(list.Items, func(v2 secretv0alpha1.SecureValue) bool { + if !slices.ContainsFunc(list.Items, func(v2 secretv1beta1.SecureValue) bool { return v2.Namespace == v1.Namespace && v2.Name == v1.Name && v2.Status.Version == v1.Status.Version }) { t.Fatalf("expected sut to return secure value ns=%+v name=%+v version=%+v in the result", v1.Namespace, v1.Name, v1.Status.Version) @@ -417,3 +421,11 @@ func TestStateMachine(t *testing.T) { }) }) } + +func deepCopy[T any](sv T) T { + copied, err := copystructure.Copy(sv) + if err != nil { + panic(fmt.Sprintf("failed to copy secure value: %v", err)) + } + return copied.(T) +} From fd269ce0413e32f774f1c861cf840926335f1e16 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:43:03 -0600 Subject: [PATCH 5/8] Chore: Fix releaseFinder script perf (#108035) * baldm0mma/ speed up branch loop * baldm0mma/ rem unneeded comment * baldm0mma/ update release output format * baldm0mma/ refactor --- scripts/releasefinder.sh | 91 ++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 60 deletions(-) diff --git a/scripts/releasefinder.sh b/scripts/releasefinder.sh index fb5bb23a386..68b85ca4b8a 100755 --- a/scripts/releasefinder.sh +++ b/scripts/releasefinder.sh @@ -46,93 +46,64 @@ fi echo "Fetching latest remote information..." git fetch --all --tags --prune 2>/dev/null -echo "Finding release branches containing the commit..." -echo "Finding tags associated with the commit..." -echo - -echo "Results for commit: $COMMIT_HASH" +echo "Finding releases containing commit: $COMMIT_HASH" echo "=============================================" echo -# Get commit details -echo "Commit details:" -echo " Author: $(git log -1 --format="%an <%ae>" "$COMMIT_HASH")" -echo " Date: $(git log -1 --format="%ad" --date=iso "$COMMIT_HASH")" +# Get all commit details in one call for better performance +commit_info=$(git log -1 --format="%an <%ae>%n%ad%n%B" --date=iso "$COMMIT_HASH") +author=$(echo "$commit_info" | sed -n '1p') +date=$(echo "$commit_info" | sed -n '2p') +commit_message=$(echo "$commit_info" | sed -n '3,$p') -# Extract original PR number and create link -PR_NUMBER=$(git log -1 --pretty=format:"%B" "$COMMIT_HASH" | grep -o '#[0-9]\+' | head -n1 | tr -d '#') +echo "Commit details:" +echo " Author: $author" +echo " Date: $date" + +# Extract PR number and title +PR_NUMBER=$(echo "$commit_message" | grep -o '#[0-9]\+' | head -n1 | tr -d '#') if [ -n "$PR_NUMBER" ]; then - # Extract PR title (first line of commit message) - PR_TITLE=$(git log -1 --pretty=format:"%s" "$COMMIT_HASH") + PR_TITLE=$(echo "$commit_message" | head -n1) echo " PR: #$PR_NUMBER - $PR_TITLE" echo " Link: https://github.com/grafana/grafana/pull/$PR_NUMBER" fi echo -# Arrays to store results -declare -a release_branches=() -declare -a direct_tags=() -declare -a included_tags=() +# Find release branches and tags containing the commit +release_branches=$(git branch -r --contains "$COMMIT_HASH" 2>/dev/null | grep -E 'origin/release-[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sed 's/.*origin\///') +release_tags=$(git tag --contains "$COMMIT_HASH" 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sort -V) -# First check all release branches (including security releases) -for branch in $(git branch -r | grep -E 'origin/release-[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sed 's/origin\///'); do - # Check if the commit is in this branch's history - if git merge-base --is-ancestor "$COMMIT_HASH" "origin/$branch" 2>/dev/null; then - release_branches+=("$branch") - fi -done +# Get all existing tags for upcoming release filtering +all_tags=$(git tag 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$') -# Then check all version tags (including security releases) -for tag in $(git tag | sort -V); do - # Skip non-version tags - if ! [[ $tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$ ]]; then - continue - fi - - # Check if the commit is in this tag - if git merge-base --is-ancestor "$COMMIT_HASH" "$tag" 2>/dev/null; then - # If this is the first tag containing the commit, it's the initial release tag - if [ ${#direct_tags[@]} -eq 0 ]; then - direct_tags+=("$tag") - else - included_tags+=("$tag") - fi - fi -done - -# Print previous releases if they exist -if [ ${#direct_tags[@]} -gt 0 ] || [ ${#included_tags[@]} -gt 0 ]; then +# Display previous releases +if [ -n "$release_tags" ]; then echo "This commit has been included in these PREVIOUS on-prem releases:" - # Get all tags sorted - readarray -t all_tags < <(printf "%s\n" "${direct_tags[@]}" "${included_tags[@]}" | sort -V) - # Get the first release - first_release="${all_tags[0]}" - # Print all tags with annotation for the first release - for tag in "${all_tags[@]}"; do + first_release=$(echo "$release_tags" | head -1) + while read -r tag; do if [ "$tag" = "$first_release" ]; then echo " - $tag (first release)" else echo " - $tag" fi - done + done <<< "$release_tags" echo echo "Note: This code may have been backported to previous release branches. Please check the original PR for backport information." echo fi -# Print upcoming releases -if [ ${#release_branches[@]} -eq 0 ]; then - echo " This commit is not yet included in any release branches." - echo " The corresponding release branch has likely not been created yet." -else +# Display upcoming releases +if [ -n "$release_branches" ]; then echo "This commit will be included in these UPCOMING on-prem releases:" - for branch in "${release_branches[@]}"; do - # Convert branch name to tag format (e.g., release-11.5.0 -> v11.5.0) + while read -r branch; do tag_version="v${branch#release-}" # Only show branches that don't have a corresponding tag yet - if ! git tag | grep -q "^$tag_version$"; then + if ! echo "$all_tags" | grep -q "^$tag_version$"; then echo " - $tag_version" fi - done | sort -V + done <<< "$release_branches" | sort -V +else + echo "This commit is not yet included in any release branches." + echo "The corresponding release branch has likely not been created yet." fi echo From c20067d70a066e87fa7559c569c9c3215aa4cbb8 Mon Sep 17 00:00:00 2001 From: Jacob Valdez Date: Fri, 11 Jul 2025 12:53:34 -0500 Subject: [PATCH 6/8] docs: Clarifying the support level of SCIM (#108034) --- .../configure-security/configure-scim-provisioning/_index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md index 4d9a5dab53c..75729ee8b7d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md @@ -20,7 +20,8 @@ weight: 300 System for Cross-domain Identity Management (SCIM) is an open standard that allows automated user provisioning and management. With SCIM, you can automate the provisioning of users and groups from your identity provider to Grafana. {{< admonition type="note" >}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/) in [public preview](https://grafana.com/docs/release-life-cycle/). +Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. {{< /admonition >}} {{< admonition type="note" >}} From db3ab6a0a7bfc5f68c5406fb59daca4bc8e3e281 Mon Sep 17 00:00:00 2001 From: Sarah Zinger Date: Fri, 11 Jul 2025 15:46:31 -0300 Subject: [PATCH 7/8] Fix typo in docs (#107976) --- docs/sources/setup-grafana/configure-grafana/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index ddc65c17d7a..c1b834b6678 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -2866,7 +2866,7 @@ Set this to `false` to disable expressions and hide them in the Grafana UI. Defa Set the maximum number of cells that can be passed to a SQL expression. Default is `100000`. -#### `sql_expression_cell_output_limit` +#### `sql_expression_output_cell_limit` Set the maximum number of cells that can be returned from a SQL expression. Default is `100000`. From 2cd0be3cbddead3de419a9f64a3b036a4d8b3599 Mon Sep 17 00:00:00 2001 From: mohammad-hamid Date: Fri, 11 Jul 2025 14:55:52 -0400 Subject: [PATCH 8/8] Update authlib version (#107939) * update authlib version * add latest versions * make update-workspace * typo * Trigger Build * Trigger Build --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/apimachinery/go.mod | 4 ++-- pkg/apimachinery/go.sum | 8 ++++---- pkg/apiserver/go.mod | 4 ++-- pkg/apiserver/go.sum | 8 ++++---- pkg/storage/unified/resource/access.go | 8 ++++---- pkg/storage/unified/resource/access_test.go | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 5de283a74d2..7f960bd15eb 100644 --- a/go.mod +++ b/go.mod @@ -86,8 +86,8 @@ require ( github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team - github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team + github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // @grafana/grafana-backend-group diff --git a/go.sum b/go.sum index 367b077d77a..415efc1a272 100644 --- a/go.sum +++ b/go.sum @@ -1573,10 +1573,10 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d h1:rtlYpwsE3KDWWCg2kytDw3s5qgpDjG87qh1IixAyNz4= github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d/go.mod h1:gtR7agmxVfJOmNKV/n2ZULgOYTYNL+PDKYB5N48tQ7Q= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA= github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg= github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index 3d4120daa43..ffcbdb28510 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -4,8 +4,8 @@ go 1.24.4 require ( github.com/go-jose/go-jose/v3 v3.0.4 // @grafana/identity-access-team - github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team - github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team + github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team github.com/stretchr/testify v1.10.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.33.2 diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index e7cb5aa2cd0..9d6bb141c1e 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -31,10 +31,10 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 43b95bee29c..cc6a7c3e8d7 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -4,7 +4,7 @@ go 1.24.4 require ( github.com/google/go-cmp v0.7.0 - github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d + github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 github.com/grafana/grafana-app-sdk/logging v0.39.1 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e github.com/prometheus/client_golang v1.22.0 @@ -43,7 +43,7 @@ require ( github.com/google/gnostic-models v0.6.9 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // indirect + github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 0b7f20d4879..0d2c6f06dc3 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -78,10 +78,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM= -github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM= -github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= +github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw= +github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE= github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E= github.com/grafana/grafana-app-sdk/logging v0.39.1 h1:lI5rbrheuwVPuyIM6LIuEYOCSpgmXahfKtqeMyhbGPU= diff --git a/pkg/storage/unified/resource/access.go b/pkg/storage/unified/resource/access.go index 4a2ea89d7a0..8a71049a741 100644 --- a/pkg/storage/unified/resource/access.go +++ b/pkg/storage/unified/resource/access.go @@ -137,8 +137,8 @@ func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req c if !claims.NamespaceMatches(id.GetNamespace(), req.Namespace) { span.SetAttributes(attribute.Bool("allowed", false)) span.SetStatus(codes.Error, "Namespace mismatch") - span.RecordError(claims.ErrNamespaceMissmatch) - return claims.CheckResponse{Allowed: false}, claims.ErrNamespaceMissmatch + span.RecordError(claims.ErrNamespaceMismatch) + return claims.CheckResponse{Allowed: false}, claims.ErrNamespaceMismatch } if !c.IsCompatibleWithRBAC(req.Group, req.Resource) { @@ -186,8 +186,8 @@ func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req if !claims.NamespaceMatches(id.GetNamespace(), req.Namespace) { span.SetAttributes(attribute.Bool("allowed", false)) span.SetStatus(codes.Error, "Namespace mismatch") - span.RecordError(claims.ErrNamespaceMissmatch) - return nil, claims.ErrNamespaceMissmatch + span.RecordError(claims.ErrNamespaceMismatch) + return nil, claims.ErrNamespaceMismatch } if !c.IsCompatibleWithRBAC(req.Group, req.Resource) { diff --git a/pkg/storage/unified/resource/access_test.go b/pkg/storage/unified/resource/access_test.go index 5477c438f6e..2ff885deab2 100644 --- a/pkg/storage/unified/resource/access_test.go +++ b/pkg/storage/unified/resource/access_test.go @@ -148,8 +148,8 @@ func TestNamespaceMatching(t *testing.T) { if tt.expectError { require.Error(t, checkErr, "Check should return error") require.Error(t, compileErr, "Compile should return error") - assert.ErrorIs(t, checkErr, authlib.ErrNamespaceMissmatch, "Check should return namespace mismatch error") - assert.ErrorIs(t, compileErr, authlib.ErrNamespaceMissmatch, "Compile should return namespace mismatch error") + assert.ErrorIs(t, checkErr, authlib.ErrNamespaceMismatch, "Check should return namespace mismatch error") + assert.ErrorIs(t, compileErr, authlib.ErrNamespaceMismatch, "Compile should return namespace mismatch error") } else { assert.NoError(t, checkErr, "Check should not return error when namespaces match") assert.NoError(t, compileErr, "Compile should not return error when namespaces match")