From 2289d68e4cf1a21cba537c97040e928722717488 Mon Sep 17 00:00:00 2001 From: Louis Cabrol Date: Tue, 29 Apr 2025 17:43:20 +0200 Subject: [PATCH 1/4] fix: use byte functions instead of string to avoid encoding errors --- pkg/storage/driver/secrets.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 048d4987d55..821bb5cd034 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -71,7 +71,8 @@ func (secrets *Secrets) Name() string { // _FetchReleaseData is an internal function to fetch the release data // from the release secret and subsequent partial secrets. func (secrets *Secrets) _FetchReleaseData(first *v1.Secret) (string, error) { - data := string(first.Data["release"]) + var allData []byte + allData = append(allData, first.Data["release"]...) nextKey, ok := first.Labels["continuedIn"] for ok { obj, err := secrets.impl.Get(context.Background(), nextKey, metav1.GetOptions{}) @@ -81,10 +82,10 @@ func (secrets *Secrets) _FetchReleaseData(first *v1.Secret) (string, error) { } return "", errors.Wrapf(err, "failed to get partial %q", nextKey) } - data = data + string(obj.Data["release"]) + allData = append(allData, obj.Data["release"]...) nextKey, ok = obj.Labels["continuedIn"] } - return data, nil + return string(allData), nil } // Get fetches the release named by key. The corresponding release is returned From db21b0ffe6f600e8c629705c112bbddf68f83a8a Mon Sep 17 00:00:00 2001 From: Louis Cabrol Date: Tue, 29 Apr 2025 18:24:37 +0200 Subject: [PATCH 2/4] fix: support partial releases when listing all namespaces --- pkg/storage/driver/secrets.go | 42 +++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 821bb5cd034..dfe21382248 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -88,6 +88,24 @@ func (secrets *Secrets) _FetchReleaseData(first *v1.Secret) (string, error) { return string(allData), nil } +// _FetchReleaseDataFromMap is an internal function to fetch the release data +// from the release secret and subsequent partial secrets, from an already-fetched List call. +// This works around the fact that List calls can accept `namespace=""`, but Get calls can't. +func (secrets *Secrets) _FetchReleaseDataFromMap(first *v1.Secret, secretMap map[string]*v1.Secret) (string, error) { + var allData []byte + allData = append(allData, first.Data["release"]...) + nextKey, ok := first.Labels["continuedIn"] + for ok { + obj, found := secretMap[nextKey] + if !found { + return "", errors.Wrapf(ErrReleaseNotFound, "partial release not found %q", nextKey) + } + allData = append(allData, obj.Data["release"]...) + nextKey, ok = obj.Labels["continuedIn"] + } + return string(allData), nil +} + // Get fetches the release named by key. The corresponding release is returned // or error if not found. func (secrets *Secrets) Get(key string) (*rspb.Release, error) { @@ -122,11 +140,21 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, } var results []*rspb.Release + + // Build in-memory map of all fetched objects + secretMap := make(map[string]*v1.Secret) + for i, item := range list.Items { + secretMap[item.Name] = &list.Items[i] // Important: use pointer to the slice element + } // iterate over the secrets object list // and decode each release for _, item := range list.Items { - data, err := secrets._FetchReleaseData(&item) + if item.Type != "helm.sh/release.v1" { + // skip partials or anything not a full release + continue + } + data, err := secrets._FetchReleaseDataFromMap(&item, secretMap) if err != nil { secrets.Log("list: failed to fetch release data: %v: %s", item, err) continue @@ -169,9 +197,19 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) return nil, ErrReleaseNotFound } + // Build in-memory map of all fetched objects + secretMap := make(map[string]*v1.Secret) + for i, item := range list.Items { + secretMap[item.Name] = &list.Items[i] // Important: use pointer to the slice element + } + var results []*rspb.Release for _, item := range list.Items { - data, err := secrets._FetchReleaseData(&item) + if item.Type != "helm.sh/release.v1" { + // skip partials or anything not a full release + continue + } + data, err := secrets._FetchReleaseDataFromMap(&item, secretMap) if err != nil { secrets.Log("query: failed to fetch release data: %s", err) continue From 474c78c5e3e415323ba69ee0173eba930d722451 Mon Sep 17 00:00:00 2001 From: Louis Cabrol Date: Tue, 29 Apr 2025 18:24:59 +0200 Subject: [PATCH 3/4] fix: relax logging --- pkg/storage/driver/secrets.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index dfe21382248..ce051535b32 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -156,13 +156,13 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, } data, err := secrets._FetchReleaseDataFromMap(&item, secretMap) if err != nil { - secrets.Log("list: failed to fetch release data: %v: %s", item, err) + secrets.Log("list: failed to fetch release data: %s: %s", item.ObjectMeta.Name, err) continue } // decode the base64 data string rls, err := decodeRelease(data) if err != nil { - secrets.Log("list: failed to decode release: %v: %s", item, err) + secrets.Log("list: failed to decode release: %s: %s", item.ObjectMeta.Name, err) continue } From 1984f9d2c5c784b8e6f6321f0b4dfbea2f8f0ce7 Mon Sep 17 00:00:00 2001 From: Louis Cabrol Date: Tue, 29 Apr 2025 18:25:24 +0200 Subject: [PATCH 4/4] fix: always re-create labels to avoid persisting old continuedIn --- pkg/storage/driver/secrets.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index ce051535b32..dbaaab1c86d 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -296,19 +296,30 @@ func (secrets *Secrets) Update(key string, rls *rspb.Release) error { } // update secrets as needed - for _, obj := range secretsList { - _, ok = partialKeys[obj.ObjectMeta.Name] - if ok { - partialKeys[obj.ObjectMeta.Name] = false - if _, err := secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{}); err != nil { + for _, newObj := range secretsList { + _, exists := partialKeys[newObj.ObjectMeta.Name] + if exists { + partialKeys[newObj.ObjectMeta.Name] = false + // Load current object + current, err := secrets.impl.Get(context.Background(), newObj.ObjectMeta.Name, metav1.GetOptions{}) + if err != nil { + return errors.Wrap(err, "update: failed to re-get existing secret") + } + // Fully replace fields + current.ObjectMeta.Labels = newObj.ObjectMeta.Labels + current.Data = newObj.Data + current.Type = newObj.Type + + if _, err := secrets.impl.Update(context.Background(), current, metav1.UpdateOptions{}); err != nil { return errors.Wrap(err, "update: failed to update") } } else { - if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil { + if _, err := secrets.impl.Create(context.Background(), newObj, metav1.CreateOptions{}); err != nil { return errors.Wrap(err, "update: failed to create new partial") } } } + // delete any extra partials for key, shouldRemove := range partialKeys { if shouldRemove {