Secrets: garbage collection (#110247)
* clean up older secret versions * start gargbage collection worker as background service * make gen-go * fix typo * make update-workspace * undo go mod changes * undo go work sum changes * Update pkg/registry/apis/secret/garbagecollectionworker/worker.go Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> * Update pkg/registry/apis/secret/garbagecollectionworker/worker.go Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> * default gc_worker_batch_size to 1 minute * fix typo * fix typo * add test to ensure cleaning up secure values is idempotent * make gen-go * make update-workspace * undo go.mod and .sum changes * undo enterprise imports --------- Co-authored-by: Matheus Macabu <macabu.matheus@gmail.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com>
This commit is contained in:
co-authored by
Matheus Macabu
Matheus Macabu
parent
d5eb3e291a
commit
f8cd7049e8
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -20,7 +21,9 @@ import (
|
||||
|
||||
type modelSecureValue struct {
|
||||
*secretv1beta1.SecureValue
|
||||
active bool
|
||||
active bool
|
||||
created time.Time
|
||||
leaseCreated time.Time
|
||||
}
|
||||
|
||||
// A simplified model of the grafana secrets manager
|
||||
@@ -69,8 +72,8 @@ func (m *model) readActiveVersion(namespace, name string) *modelSecureValue {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) create(sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) {
|
||||
modelSv := &modelSecureValue{sv, false}
|
||||
func (m *model) create(now time.Time, sv *secretv1beta1.SecureValue) (*secretv1beta1.SecureValue, error) {
|
||||
modelSv := &modelSecureValue{SecureValue: sv, active: false, created: now}
|
||||
modelSv.Status.Version = m.getNewVersionNumber(modelSv.Namespace, modelSv.Name)
|
||||
modelSv.Status.ExternalID = fmt.Sprintf("%d", modelSv.Status.Version)
|
||||
m.secureValues = append(m.secureValues, modelSv)
|
||||
@@ -78,7 +81,7 @@ func (m *model) create(sv *secretv1beta1.SecureValue, actorUID string) (*secretv
|
||||
return modelSv.SecureValue, nil
|
||||
}
|
||||
|
||||
func (m *model) update(newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) {
|
||||
func (m *model) update(now time.Time, newSecureValue *secretv1beta1.SecureValue) (*secretv1beta1.SecureValue, bool, error) {
|
||||
// If the payload doesn't contain a value, get the value from current version
|
||||
if newSecureValue.Spec.Value == nil {
|
||||
sv := m.readActiveVersion(newSecureValue.Namespace, newSecureValue.Name)
|
||||
@@ -87,7 +90,7 @@ func (m *model) update(newSecureValue *secretv1beta1.SecureValue, actorUID strin
|
||||
}
|
||||
newSecureValue.Spec.Value = sv.Spec.Value
|
||||
}
|
||||
createdSv, err := m.create(newSecureValue, actorUID)
|
||||
createdSv, err := m.create(now, newSecureValue)
|
||||
return createdSv, true, err
|
||||
}
|
||||
|
||||
@@ -141,6 +144,22 @@ func (m *model) read(namespace, name string) (*secretv1beta1.SecureValue, error)
|
||||
return modelSv.SecureValue, nil
|
||||
}
|
||||
|
||||
func (m *model) leaseInactiveSecureValues(now time.Time, minAge, leaseTTL time.Duration, maxBatchSize uint16) ([]*modelSecureValue, error) {
|
||||
out := make([]*modelSecureValue, 0)
|
||||
|
||||
for _, sv := range m.secureValues {
|
||||
if len(out) >= int(maxBatchSize) {
|
||||
break
|
||||
}
|
||||
if !sv.active && now.Sub(sv.created) > minAge && now.Sub(sv.leaseCreated) > leaseTTL {
|
||||
sv.leaseCreated = now
|
||||
out = append(out, sv)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var (
|
||||
decryptersGen = rapid.SampledFrom([]string{"svc1", "svc2", "svc3", "svc4", "svc5"})
|
||||
nameGen = rapid.SampledFrom([]string{"n1", "n2", "n3", "n4", "n5"})
|
||||
@@ -204,16 +223,17 @@ func TestModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := newModel()
|
||||
now := time.Now()
|
||||
|
||||
// Create a secure value
|
||||
sv1, err := m.create(deepCopy(sv), "actor-uid")
|
||||
sv1, err := m.create(now, deepCopy(sv))
|
||||
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(deepCopy(sv), "actor-uid")
|
||||
sv2, err := m.create(now, deepCopy(sv))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, sv.Namespace, sv2.Namespace)
|
||||
require.Equal(t, sv.Name, sv2.Name)
|
||||
@@ -225,11 +245,13 @@ func TestModel(t *testing.T) {
|
||||
|
||||
m := newModel()
|
||||
|
||||
sv1, err := m.create(deepCopy(sv), "actor-uid")
|
||||
now := time.Now()
|
||||
|
||||
sv1, err := m.create(now, deepCopy(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a new version of a secure value by updating it
|
||||
sv2, _, err := m.update(deepCopy(sv1), "actor-uid")
|
||||
sv2, _, err := m.update(now, deepCopy(sv1))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, sv.Namespace, sv2.Namespace)
|
||||
require.Equal(t, sv.Name, sv2.Name)
|
||||
@@ -239,14 +261,14 @@ func TestModel(t *testing.T) {
|
||||
sv3 := deepCopy(sv2)
|
||||
sv3.Name = "i_dont_exist"
|
||||
sv3.Spec.Value = nil
|
||||
_, _, err = m.update(sv3, "actor-uid")
|
||||
_, _, err = m.update(now, sv3)
|
||||
require.ErrorIs(t, err, contracts.ErrSecureValueNotFound)
|
||||
|
||||
// Updating a value that doesn't exist creates a new version
|
||||
sv4 := deepCopy(sv3)
|
||||
sv4.Name = "i_dont_exist"
|
||||
sv4.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("sv4"))
|
||||
sv4, _, err = m.update(sv4, "actor-uid")
|
||||
sv4, _, err = m.update(now, sv4)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, sv4.Status.Version)
|
||||
})
|
||||
@@ -255,8 +277,9 @@ func TestModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := newModel()
|
||||
now := time.Now()
|
||||
|
||||
sv1, err := m.create(deepCopy(sv), "actor-uid")
|
||||
sv1, err := m.create(now, deepCopy(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deleting a secure value
|
||||
@@ -275,6 +298,7 @@ func TestModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := newModel()
|
||||
now := time.Now()
|
||||
|
||||
// No secure values exist yet
|
||||
list, err := m.list(sv.Namespace)
|
||||
@@ -282,7 +306,7 @@ func TestModel(t *testing.T) {
|
||||
require.Equal(t, 0, len(list.Items))
|
||||
|
||||
// Create a secure value
|
||||
sv1, err := m.create(deepCopy(sv), "actor-uid")
|
||||
sv1, err := m.create(now, deepCopy(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
// 1 secure value exists and it should be returned
|
||||
@@ -298,6 +322,7 @@ func TestModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := newModel()
|
||||
now := time.Now()
|
||||
|
||||
// Decrypting a secure value that does not exist
|
||||
result, err := m.decrypt("decrypter", "namespace", "name")
|
||||
@@ -308,7 +333,7 @@ func TestModel(t *testing.T) {
|
||||
|
||||
// Create a secure value
|
||||
secret := "v1"
|
||||
sv1, err := m.create(deepCopy(sv), "actor-uid")
|
||||
sv1, err := m.create(now, deepCopy(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Decrypt the just created secure value
|
||||
@@ -333,7 +358,7 @@ func TestStateMachine(t *testing.T) {
|
||||
"create": func(t *rapid.T) {
|
||||
sv := anySecureValueGen.Draw(t, "sv")
|
||||
|
||||
modelCreatedSv, modelErr := model.create(deepCopy(sv), "actor-uid")
|
||||
modelCreatedSv, modelErr := model.create(sut.Clock.Now(), deepCopy(sv))
|
||||
|
||||
createdSv, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(deepCopy(sv)))
|
||||
if err != nil || modelErr != nil {
|
||||
@@ -346,7 +371,7 @@ func TestStateMachine(t *testing.T) {
|
||||
},
|
||||
"update": func(t *rapid.T) {
|
||||
sv := updateSecureValueGen.Draw(t, "sv")
|
||||
modelCreatedSv, _, modelErr := model.update(deepCopy(sv), "actor-uid")
|
||||
modelCreatedSv, _, modelErr := model.update(sut.Clock.Now(), deepCopy(sv))
|
||||
createdSv, err := sut.UpdateSv(t.Context(), deepCopy(sv))
|
||||
if err != nil || modelErr != nil {
|
||||
require.ErrorIs(t, err, modelErr)
|
||||
|
||||
Reference in New Issue
Block a user