Skip to content

Commit cceec2e

Browse files
committed
use a release wrapper to serialize custom labels into the release blob
Signed-off-by: Joe Lanford <joe.lanford@gmail.com>
1 parent 123a4c4 commit cceec2e

File tree

3 files changed

+53
-58
lines changed

3 files changed

+53
-58
lines changed

pkg/storage/chunked.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"hash"
1111
"hash/fnv"
1212
"io"
13-
"maps"
1413
"strconv"
1514
"sync"
1615
"time"
@@ -95,6 +94,18 @@ type chunk struct {
9594
data []byte
9695
}
9796

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+
98109
// encodeRelease encodes a release returning a base64 encoded
99110
// gzipped string representation, or error.
100111
func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release) ([]chunk, error) {
@@ -106,7 +117,7 @@ func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release)
106117
return err
107118
}
108119
defer gzw.Close()
109-
return json.NewEncoder(gzw).Encode(rls)
120+
return json.NewEncoder(gzw).Encode(wrapRelease(rls))
110121
}(); err != nil {
111122
return nil, err
112123
}
@@ -148,13 +159,12 @@ func (c *chunkedSecrets) indexSecretFromChunks(key string, rls *release.Release,
148159
panic(err)
149160
}
150161

151-
indexLabels, indexAnnotations := newIndexLabelsAndAnnotations(c.owner, key, rls)
162+
indexLabels := newIndexLabels(c.owner, key, rls)
152163
indexSecret := &corev1.Secret{
153164
Type: SecretTypeChunkedIndex,
154165
ObjectMeta: metav1.ObjectMeta{
155-
Name: key,
156-
Labels: indexLabels,
157-
Annotations: indexAnnotations,
166+
Name: key,
167+
Labels: indexLabels,
158168
},
159169
Immutable: ptr.To(false),
160170
Data: map[string][]byte{
@@ -320,29 +330,51 @@ func (c *chunkedSecrets) Query(queryLabels map[string]string) ([]*release.Releas
320330
c.Log("query: labels=%v", queryLabels)
321331
defer c.Log("queried: labels=%v", queryLabels)
322332

323-
selector := newListIndicesLabelSelector(c.owner)
324-
if queryRequirements, selectable := labels.Set(queryLabels).AsSelector().Requirements(); selectable {
325-
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+
}
326345
}
327346

328-
indexSecrets, err := c.client.List(context.Background(), metav1.ListOptions{LabelSelector: selector.String()})
329-
if err != nil {
330-
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...)
331351
}
332352

333-
if len(indexSecrets.Items) == 0 {
334-
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)
335356
}
336357

358+
// Pass 2: decode the releases that matched the server selector and filter based on the client selector
337359
results := make([]*release.Release, 0, len(indexSecrets.Items))
360+
clientSelector := clientSelectorSet.AsSelector()
338361
for _, indexSecret := range indexSecrets.Items {
339362
indexSecret := indexSecret
340363
rls, err := c.decodeRelease(context.Background(), &indexSecret)
341364
if err != nil {
342365
return nil, fmt.Errorf("query: failed to decode release: %w", err)
343366
}
367+
368+
if !clientSelector.Matches(labels.Set(rls.Labels)) {
369+
continue
370+
}
344371
results = append(results, rls)
345372
}
373+
374+
if len(results) == 0 {
375+
return nil, driver.ErrReleaseNotFound
376+
}
377+
346378
return results, nil
347379
}
348380

@@ -400,12 +432,13 @@ func (c *chunkedSecrets) decodeRelease(ctx context.Context, indexSecret *corev1.
400432
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
401433
}
402434
releaseDecoder := json.NewDecoder(gzr)
403-
var r release.Release
404-
if err := releaseDecoder.Decode(&r); err != nil {
435+
var wrappedRelease releaseWrapper
436+
if err := releaseDecoder.Decode(&wrappedRelease); err != nil {
405437
return nil, fmt.Errorf("failed to decode release: %w", err)
406438
}
407-
r.Labels = filterSystemLabels(indexSecret.Labels)
408-
maps.Copy(r.Labels, indexSecret.Annotations)
439+
440+
r := wrappedRelease.Release
441+
r.Labels = filterSystemLabels(wrappedRelease.Labels)
409442
return &r, nil
410443
}
411444

pkg/storage/chunked_test.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/json"
66
"fmt"
77
"maps"
8-
"strings"
98

109
. "github.com/onsi/ginkgo/v2"
1110
. "github.com/onsi/gomega"
@@ -96,28 +95,6 @@ var _ = Describe("chunkedSecrets", func() {
9695
_, err := maxReadDriver.Get(releaseKey(rel))
9796
Expect(err).To(MatchError(ContainSubstring("release too large")))
9897
})
99-
It("should handle release labels that are invalid metadata labels", func() {
100-
invalidMetadataLabelKey := "stored-as-annotation"
101-
invalidMetadataLabelValue := strings.Repeat("a", 64)
102-
103-
expected := genRelease("test-release", 1, release.StatusPendingInstall, map[string]string{invalidMetadataLabelKey: invalidMetadataLabelValue}, chunkSize/2)
104-
Expect(expected.Labels).To(HaveKey(invalidMetadataLabelKey))
105-
Expect(chunkedDriver.Create(releaseKey(expected), expected)).To(Succeed())
106-
107-
// Make sure the release-only label is in the release metadata
108-
actual, err := chunkedDriver.Get(releaseKey(expected))
109-
Expect(err).ToNot(HaveOccurred())
110-
Expect(actual).To(Equal(expected))
111-
Expect(actual.Labels).To(HaveKey(invalidMetadataLabelKey))
112-
Expect(actual.Labels[invalidMetadataLabelKey]).To(Equal(invalidMetadataLabelValue))
113-
114-
// Make sure the invalid label is stored in the secret annotations, not labels
115-
items, err := secretInterface.List(context.Background(), metav1.ListOptions{})
116-
Expect(err).ToNot(HaveOccurred())
117-
Expect(items.Items).To(HaveLen(1))
118-
Expect(items.Items[0].Labels).ToNot(HaveKey(invalidMetadataLabelKey))
119-
Expect(items.Items[0].Annotations).To(HaveKey(invalidMetadataLabelKey))
120-
})
12198
})
12299

123100
var _ = Describe("Update", func() {

pkg/storage/labels.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,17 @@ import (
66
"helm.sh/helm/v3/pkg/release"
77
"k8s.io/apimachinery/pkg/labels"
88
"k8s.io/apimachinery/pkg/util/sets"
9-
"k8s.io/apimachinery/pkg/util/validation"
109
)
1110

12-
func newIndexLabelsAndAnnotations(owner, key string, rls *release.Release) (map[string]string, map[string]string) {
11+
func newIndexLabels(owner, key string, rls *release.Release) map[string]string {
1312
labels := map[string]string{}
14-
annotations := map[string]string{}
15-
for k, v := range rls.Labels {
16-
if promoteToAnnotation(k, v) {
17-
annotations[k] = v
18-
} else {
19-
labels[k] = v
20-
}
21-
}
22-
2313
labels["name"] = rls.Name
2414
labels["owner"] = owner
2515
labels["status"] = rls.Info.Status.String()
2616
labels["version"] = strconv.Itoa(rls.Version)
2717
labels["key"] = key
2818
labels["type"] = "index"
29-
return labels, annotations
30-
}
31-
32-
func promoteToAnnotation(k, v string) bool {
33-
isValidLabel := len(validation.IsQualifiedName(k)) == 0 && len(validation.IsValidLabelValue(v)) == 0
34-
return !isValidLabel
19+
return labels
3520
}
3621

3722
func newChunkLabels(owner, key string) map[string]string {

0 commit comments

Comments
 (0)