Skip to content

Commit a3b49f3

Browse files
authored
pkg/storage: handle more possible release label k/v pairs (#399)
* pkg/storage: handle more possible release label k/v pairs Release labels are stored only in Kubernetes object metadata. In order to support a wider variety of release labels, check release label validity. If a release label is invalid for a kubernetes metadata.label store it as an annotation instead. Signed-off-by: Joe Lanford <[email protected]> * use a release wrapper to serialize custom labels into the release blob Signed-off-by: Joe Lanford <[email protected]> --------- Signed-off-by: Joe Lanford <[email protected]>
1 parent 6bad50e commit a3b49f3

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

pkg/storage/chunked.go

+48-12
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ type chunk struct {
9494
data []byte
9595
}
9696

97+
type releaseWrapper struct {
98+
release.Release
99+
Labels map[string]string `json:"labels"`
100+
}
101+
102+
func wrapRelease(rls *release.Release) *releaseWrapper {
103+
return &releaseWrapper{
104+
Release: *rls,
105+
Labels: rls.Labels,
106+
}
107+
}
108+
97109
// encodeRelease encodes a release returning a base64 encoded
98110
// gzipped string representation, or error.
99111
func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release) ([]chunk, error) {
@@ -105,7 +117,7 @@ func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release)
105117
return err
106118
}
107119
defer gzw.Close()
108-
return json.NewEncoder(gzw).Encode(rls)
120+
return json.NewEncoder(gzw).Encode(wrapRelease(rls))
109121
}(); err != nil {
110122
return nil, err
111123
}
@@ -318,29 +330,51 @@ func (c *chunkedSecrets) Query(queryLabels map[string]string) ([]*release.Releas
318330
c.Log("query: labels=%v", queryLabels)
319331
defer c.Log("queried: labels=%v", queryLabels)
320332

321-
selector := newListIndicesLabelSelector(c.owner)
322-
if queryRequirements, selectable := labels.Set(queryLabels).AsSelector().Requirements(); selectable {
323-
selector = selector.Add(queryRequirements...)
333+
// The only labels that get stored on the index secret are system labels, so we'll do a two-pass
334+
// query. First, we'll request index secrets from the API server that match the query labels that
335+
// are system labels. From there, we decode the releases that match, and then further filter those
336+
// based on the rest of the query labels that are not system labels.
337+
serverSelectorSet := labels.Set{}
338+
clientSelectorSet := labels.Set{}
339+
for k, v := range queryLabels {
340+
if isSystemLabel(k) {
341+
serverSelectorSet[k] = v
342+
} else {
343+
clientSelectorSet[k] = v
344+
}
324345
}
325346

326-
indexSecrets, err := c.client.List(context.Background(), metav1.ListOptions{LabelSelector: selector.String()})
327-
if err != nil {
328-
return nil, fmt.Errorf("query: %w", err)
347+
// Pass 1: build the server selector and query for index secrets
348+
serverSelector := newListIndicesLabelSelector(c.owner)
349+
if queryRequirements, selectable := serverSelectorSet.AsSelector().Requirements(); selectable {
350+
serverSelector = serverSelector.Add(queryRequirements...)
329351
}
330352

331-
if len(indexSecrets.Items) == 0 {
332-
return nil, driver.ErrReleaseNotFound
353+
indexSecrets, err := c.client.List(context.Background(), metav1.ListOptions{LabelSelector: serverSelector.String()})
354+
if err != nil {
355+
return nil, fmt.Errorf("query: %w", err)
333356
}
334357

358+
// Pass 2: decode the releases that matched the server selector and filter based on the client selector
335359
results := make([]*release.Release, 0, len(indexSecrets.Items))
360+
clientSelector := clientSelectorSet.AsSelector()
336361
for _, indexSecret := range indexSecrets.Items {
337362
indexSecret := indexSecret
338363
rls, err := c.decodeRelease(context.Background(), &indexSecret)
339364
if err != nil {
340365
return nil, fmt.Errorf("query: failed to decode release: %w", err)
341366
}
367+
368+
if !clientSelector.Matches(labels.Set(rls.Labels)) {
369+
continue
370+
}
342371
results = append(results, rls)
343372
}
373+
374+
if len(results) == 0 {
375+
return nil, driver.ErrReleaseNotFound
376+
}
377+
344378
return results, nil
345379
}
346380

@@ -398,11 +432,13 @@ func (c *chunkedSecrets) decodeRelease(ctx context.Context, indexSecret *corev1.
398432
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
399433
}
400434
releaseDecoder := json.NewDecoder(gzr)
401-
var r release.Release
402-
if err := releaseDecoder.Decode(&r); err != nil {
435+
var wrappedRelease releaseWrapper
436+
if err := releaseDecoder.Decode(&wrappedRelease); err != nil {
403437
return nil, fmt.Errorf("failed to decode release: %w", err)
404438
}
405-
r.Labels = filterSystemLabels(indexSecret.Labels)
439+
440+
r := wrappedRelease.Release
441+
r.Labels = filterSystemLabels(wrappedRelease.Labels)
406442
return &r, nil
407443
}
408444

pkg/storage/labels.go

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package storage
22

33
import (
4-
"maps"
54
"strconv"
65

76
"helm.sh/helm/v3/pkg/release"
@@ -11,7 +10,6 @@ import (
1110

1211
func newIndexLabels(owner, key string, rls *release.Release) map[string]string {
1312
labels := map[string]string{}
14-
maps.Copy(labels, rls.Labels)
1513
labels["name"] = rls.Name
1614
labels["owner"] = owner
1715
labels["status"] = rls.Info.Status.String()

0 commit comments

Comments
 (0)