diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index bff3d37ca..d2ffc46c1 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -17,9 +17,11 @@ limitations under the License. package driver // import "helm.sh/helm/v3/pkg/storage/driver" import ( + "bytes" "context" "fmt" "testing" + "text/template" sqlmock "github.com/DATA-DOG/go-sqlmock" sq "github.com/Masterminds/squirrel" @@ -34,6 +36,35 @@ import ( rspb "helm.sh/helm/v3/pkg/release" ) +var multiReleaseTestTmpl = ` +{{.}}: + {{.}}Sub1: + {{.}}Sub1Sub1: true + {{.}}Sub2: + {{.}}Sub2Sub1: "{{.}}Sub2Sub1Value" + {{.}}Sub2Sub2: false + {{.}}Sub2Sub3: false + {{.}}Sub2Sub4: false + {{.}}Sub2Sub5: "{{.}}Sub2Sub5Value" + {{.}}Sub2Sub6: "{{.}}Sub2Sub6Value" +` + +func multiReleaseStub(name string, vers int, namespace string, status rspb.Status) *rspb.Release { + var buf bytes.Buffer + tmpl, _ := template.New("").Parse(multiReleaseTestTmpl) + for i := 0; i < 100; i++ { + tmpl.Execute(&buf, fmt.Sprintf("field%d", i)) + } + + return &rspb.Release{ + Name: name, + Version: vers, + Namespace: namespace, + Info: &rspb.Info{Status: status}, + Manifest: buf.String(), + } +} + func releaseStub(name string, vers int, namespace string, status rspb.Status) *rspb.Release { return &rspb.Release{ Name: name, @@ -275,11 +306,14 @@ func (mock *MockMultiSecretsInterface) Init(t *testing.T, releases ...*rspb.Rele for _, rls := range releases { objkey := testKey(rls.Name, rls.Version) - secrets, err := newMultiSecretsObject(objkey, rls, nil, -1) + secrets, err := newMultiSecretsObject(objkey, rls, nil, 1024) if err != nil { t.Fatalf("Failed to create secrets: %s", err) } - mock.objects[objkey] = &(*secrets)[0] + for _, se := range *secrets { + v := se + mock.objects[se.Name] = &v + } } } @@ -302,8 +336,9 @@ func (mock *MockMultiSecretsInterface) List(_ context.Context, opts metav1.ListO } for _, secret := range mock.objects { - if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) { - list.Items = append(list.Items, *secret) + temp := *secret + if labelSelector.Matches(kblabels.Set(temp.ObjectMeta.Labels)) { + list.Items = append(list.Items, temp) } } return &list, nil @@ -322,10 +357,11 @@ func (mock *MockMultiSecretsInterface) Create(_ context.Context, secret *v1.Secr // Update updates a Secret. func (mock *MockMultiSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) { name := secret.ObjectMeta.Name + temp := *secret if _, ok := mock.objects[name]; !ok { return nil, apierrors.NewNotFound(v1.Resource("tests"), name) } - mock.objects[name] = secret + mock.objects[name] = &temp return secret, nil } diff --git a/pkg/storage/driver/multisecrets.go b/pkg/storage/driver/multisecrets.go index 834ac2359..2723075ae 100644 --- a/pkg/storage/driver/multisecrets.go +++ b/pkg/storage/driver/multisecrets.go @@ -383,6 +383,8 @@ func newMultiSecretsObject(key string, rls *rspb.Release, lbs labels, chunkSizeE // Load remaining chunks given a key and 1st release Secret func loadRemainingChunks(obj *v1.Secret, multiSecretImpl *MultiSecrets) ([]byte, error) { chunks, _ := strconv.Atoi(string(obj.Data["chunks"])) + releaseData := make([]byte, 0, len(obj.Data["release"])*chunks) + releaseData = append(releaseData, obj.Data["release"]...) for chunk := 2; chunk <= chunks; chunk++ { key := fmt.Sprintf("%s.%d", obj.ObjectMeta.Name, chunk) ctx := context.WithValue(context.Background(), multiSecretContextNamespace, obj.Namespace) @@ -393,9 +395,9 @@ func loadRemainingChunks(obj *v1.Secret, multiSecretImpl *MultiSecrets) ([]byte, } return nil, errors.Wrapf(err, "get: failed to get %q", key) } - obj.Data["release"] = append(obj.Data["release"], chunkobj.Data["release"]...) + releaseData = append(releaseData, chunkobj.Data["release"]...) } - return obj.Data["release"], nil + return releaseData, nil } // Determine the chunk size to use diff --git a/pkg/storage/driver/multisecrets_test.go b/pkg/storage/driver/multisecrets_test.go index bdf7a4b18..f9b6d174f 100644 --- a/pkg/storage/driver/multisecrets_test.go +++ b/pkg/storage/driver/multisecrets_test.go @@ -17,6 +17,7 @@ package driver import ( "encoding/base64" "encoding/json" + "fmt" "reflect" "testing" @@ -37,7 +38,7 @@ func TestMultiSecretsGet(t *testing.T) { name := "smug-pigeon" namespace := "default" key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + rel := multiReleaseStub(name, vers, namespace, rspb.StatusDeployed) multisecrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) @@ -57,7 +58,7 @@ func TestUNcompressedMultiSecretsGet(t *testing.T) { name := "smug-pigeon" namespace := "default" key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + rel := multiReleaseStub(name, vers, namespace, rspb.StatusDeployed) // Create a test fixture which contains an uncompressed release secret, err := newSecretsObject(key, rel, nil) @@ -85,14 +86,7 @@ func TestUNcompressedMultiSecretsGet(t *testing.T) { } func TestMultiSecretsList(t *testing.T) { - multisecrets := newTestFixtureMultiSecrets(t, []*rspb.Release{ - releaseStub("multi-secrets-key-1", 1, "default", rspb.StatusUninstalled), - releaseStub("multi-secrets-key-2", 1, "default", rspb.StatusUninstalled), - releaseStub("multi-secrets-key-3", 1, "default", rspb.StatusDeployed), - releaseStub("multi-secrets-key-4", 1, "default", rspb.StatusDeployed), - releaseStub("multi-secrets-key-5", 1, "default", rspb.StatusSuperseded), - releaseStub("multi-secrets-key-6", 1, "default", rspb.StatusSuperseded), - }...) + multisecrets := fakeMultiSecretsWithRels(t) // list all deleted releases del, err := multisecrets.List(func(rel *rspb.Release) bool { @@ -106,6 +100,7 @@ func TestMultiSecretsList(t *testing.T) { t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) } + multisecrets = fakeMultiSecretsWithRels(t) // list all deployed releases dpl, err := multisecrets.List(func(rel *rspb.Release) bool { return rel.Info.Status == rspb.StatusDeployed @@ -118,6 +113,7 @@ func TestMultiSecretsList(t *testing.T) { t.Errorf("Expected 2 deployed, got %d", len(dpl)) } + multisecrets = fakeMultiSecretsWithRels(t) // list all superseded releases ssd, err := multisecrets.List(func(rel *rspb.Release) bool { return rel.Info.Status == rspb.StatusSuperseded @@ -132,14 +128,7 @@ func TestMultiSecretsList(t *testing.T) { } func TestMultiSecretsQuery(t *testing.T) { - multisecrets := newTestFixtureMultiSecrets(t, []*rspb.Release{ - releaseStub("multi-secrets-key-1", 1, "default", rspb.StatusUninstalled), - releaseStub("multi-secrets-key-2", 1, "default", rspb.StatusUninstalled), - releaseStub("multi-secrets-key-3", 1, "default", rspb.StatusDeployed), - releaseStub("multi-secrets-key-4", 1, "default", rspb.StatusDeployed), - releaseStub("multi-secrets-key-5", 1, "default", rspb.StatusSuperseded), - releaseStub("multi-secrets-key-6", 1, "default", rspb.StatusSuperseded), - }...) + multisecrets := fakeMultiSecretsWithRels(t) rls, err := multisecrets.Query(map[string]string{"status": "deployed"}) if err != nil { @@ -162,13 +151,12 @@ func TestMultiSecretsCreate(t *testing.T) { name := "smug-pigeon" namespace := "default" key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + rel := multiReleaseStub(name, vers, namespace, rspb.StatusDeployed) // store the release in a secret if err := multisecrets.Create(key, rel); err != nil { t.Fatalf("Failed to create release with key %q: %s", key, err) } - // get the release back got, err := multisecrets.Get(key) if err != nil { @@ -186,7 +174,7 @@ func TestMultiSecretsUpdate(t *testing.T) { name := "smug-pigeon" namespace := "default" key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + rel := multiReleaseStub(name, vers, namespace, rspb.StatusDeployed) multisecrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) @@ -215,7 +203,7 @@ func TestMultiSecretsDelete(t *testing.T) { name := "smug-pigeon" namespace := "default" key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + rel := multiReleaseStub(name, vers, namespace, rspb.StatusDeployed) multisecrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) @@ -240,3 +228,71 @@ func TestMultiSecretsDelete(t *testing.T) { t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err) } } + +func TestMultiSecretsSplitChunks(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + tests := []struct { + rls *rspb.Release + size int + chunks int + }{ + { + multiReleaseStub(name, vers, namespace, rspb.StatusDeployed), + 10240, + 1, + }, + { + multiReleaseStub(name, vers, namespace, rspb.StatusDeployed), + 4096, + 2, + }, + { + multiReleaseStub(name, vers, namespace, rspb.StatusDeployed), + 2048, + 3, + }, + { + multiReleaseStub(name, vers, namespace, rspb.StatusDeployed), + 1024, + 5, + }, + { + multiReleaseStub(name, vers, namespace, rspb.StatusDeployed), + 512, + 10, + }, + } + + for _, item := range tests { + secrets, err := newMultiSecretsObject(key, item.rls, nil, item.size) + if err != nil { + t.Fatalf("Failed to create secrets: %s", err) + } + if !reflect.DeepEqual(item.chunks, len(*secrets)) { + t.Errorf("Expected {%v}, got {%v}", 5, len(*secrets)) + } + for k, se := range *secrets { + if k == 0 && se.Name != "smug-pigeon.v1" { + t.Errorf("Expected {%s}, got {%s}", "smug-pigeon.v1", se.Name) + } + if k > 0 && se.Name != fmt.Sprintf("smug-pigeon.v1.%d", k+1) { + t.Errorf("Expected {%s}, got {%s}", "smug-pigeon.v1", se.Name) + } + } + } +} + +func fakeMultiSecretsWithRels(t *testing.T) *MultiSecrets { + t.Helper() + return newTestFixtureMultiSecrets(t, []*rspb.Release{ + multiReleaseStub("multi-secrets-key-1", 1, "default", rspb.StatusUninstalled), + multiReleaseStub("multi-secrets-key-2", 1, "default", rspb.StatusUninstalled), + multiReleaseStub("multi-secrets-key-3", 1, "default", rspb.StatusDeployed), + multiReleaseStub("multi-secrets-key-4", 1, "default", rspb.StatusDeployed), + multiReleaseStub("multi-secrets-key-5", 1, "default", rspb.StatusSuperseded), + multiReleaseStub("multi-secrets-key-6", 1, "default", rspb.StatusSuperseded), + }...) +}