Skip to content

Commit 123a4c4

Browse files
committed
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]>
1 parent 6bad50e commit 123a4c4

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

pkg/storage/chunked.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"hash"
1111
"hash/fnv"
1212
"io"
13+
"maps"
1314
"strconv"
1415
"sync"
1516
"time"
@@ -147,12 +148,13 @@ func (c *chunkedSecrets) indexSecretFromChunks(key string, rls *release.Release,
147148
panic(err)
148149
}
149150

150-
indexLabels := newIndexLabels(c.owner, key, rls)
151+
indexLabels, indexAnnotations := newIndexLabelsAndAnnotations(c.owner, key, rls)
151152
indexSecret := &corev1.Secret{
152153
Type: SecretTypeChunkedIndex,
153154
ObjectMeta: metav1.ObjectMeta{
154-
Name: key,
155-
Labels: indexLabels,
155+
Name: key,
156+
Labels: indexLabels,
157+
Annotations: indexAnnotations,
156158
},
157159
Immutable: ptr.To(false),
158160
Data: map[string][]byte{
@@ -403,6 +405,7 @@ func (c *chunkedSecrets) decodeRelease(ctx context.Context, indexSecret *corev1.
403405
return nil, fmt.Errorf("failed to decode release: %w", err)
404406
}
405407
r.Labels = filterSystemLabels(indexSecret.Labels)
408+
maps.Copy(r.Labels, indexSecret.Annotations)
406409
return &r, nil
407410
}
408411

pkg/storage/chunked_test.go

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

910
. "github.com/onsi/ginkgo/v2"
1011
. "github.com/onsi/gomega"
@@ -95,6 +96,28 @@ var _ = Describe("chunkedSecrets", func() {
9596
_, err := maxReadDriver.Get(releaseKey(rel))
9697
Expect(err).To(MatchError(ContainSubstring("release too large")))
9798
})
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+
})
98121
})
99122

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

pkg/storage/labels.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
package storage
22

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

76
"helm.sh/helm/v3/pkg/release"
87
"k8s.io/apimachinery/pkg/labels"
98
"k8s.io/apimachinery/pkg/util/sets"
9+
"k8s.io/apimachinery/pkg/util/validation"
1010
)
1111

12-
func newIndexLabels(owner, key string, rls *release.Release) map[string]string {
12+
func newIndexLabelsAndAnnotations(owner, key string, rls *release.Release) (map[string]string, map[string]string) {
1313
labels := map[string]string{}
14-
maps.Copy(labels, rls.Labels)
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+
1523
labels["name"] = rls.Name
1624
labels["owner"] = owner
1725
labels["status"] = rls.Info.Status.String()
1826
labels["version"] = strconv.Itoa(rls.Version)
1927
labels["key"] = key
2028
labels["type"] = "index"
21-
return labels
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
2235
}
2336

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

0 commit comments

Comments
 (0)