Skip to content

Commit

Permalink
pkg/storage: handle more possible release label k/v pairs (#399)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
joelanford authored Oct 17, 2024
1 parent 6bad50e commit a3b49f3
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
60 changes: 48 additions & 12 deletions pkg/storage/chunked.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ type chunk struct {
data []byte
}

type releaseWrapper struct {
release.Release
Labels map[string]string `json:"labels"`
}

func wrapRelease(rls *release.Release) *releaseWrapper {
return &releaseWrapper{
Release: *rls,
Labels: rls.Labels,
}
}

// encodeRelease encodes a release returning a base64 encoded
// gzipped string representation, or error.
func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release) ([]chunk, error) {
Expand All @@ -105,7 +117,7 @@ func (c *chunkedSecrets) encodeReleaseAsChunks(key string, rls *release.Release)
return err
}
defer gzw.Close()
return json.NewEncoder(gzw).Encode(rls)
return json.NewEncoder(gzw).Encode(wrapRelease(rls))
}(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -318,29 +330,51 @@ func (c *chunkedSecrets) Query(queryLabels map[string]string) ([]*release.Releas
c.Log("query: labels=%v", queryLabels)
defer c.Log("queried: labels=%v", queryLabels)

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

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

if len(indexSecrets.Items) == 0 {
return nil, driver.ErrReleaseNotFound
indexSecrets, err := c.client.List(context.Background(), metav1.ListOptions{LabelSelector: serverSelector.String()})
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}

// Pass 2: decode the releases that matched the server selector and filter based on the client selector
results := make([]*release.Release, 0, len(indexSecrets.Items))
clientSelector := clientSelectorSet.AsSelector()
for _, indexSecret := range indexSecrets.Items {
indexSecret := indexSecret
rls, err := c.decodeRelease(context.Background(), &indexSecret)
if err != nil {
return nil, fmt.Errorf("query: failed to decode release: %w", err)
}

if !clientSelector.Matches(labels.Set(rls.Labels)) {
continue
}
results = append(results, rls)
}

if len(results) == 0 {
return nil, driver.ErrReleaseNotFound
}

return results, nil
}

Expand Down Expand Up @@ -398,11 +432,13 @@ func (c *chunkedSecrets) decodeRelease(ctx context.Context, indexSecret *corev1.
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
}
releaseDecoder := json.NewDecoder(gzr)
var r release.Release
if err := releaseDecoder.Decode(&r); err != nil {
var wrappedRelease releaseWrapper
if err := releaseDecoder.Decode(&wrappedRelease); err != nil {
return nil, fmt.Errorf("failed to decode release: %w", err)
}
r.Labels = filterSystemLabels(indexSecret.Labels)

r := wrappedRelease.Release
r.Labels = filterSystemLabels(wrappedRelease.Labels)
return &r, nil
}

Expand Down
2 changes: 0 additions & 2 deletions pkg/storage/labels.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package storage

import (
"maps"
"strconv"

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

func newIndexLabels(owner, key string, rls *release.Release) map[string]string {
labels := map[string]string{}
maps.Copy(labels, rls.Labels)
labels["name"] = rls.Name
labels["owner"] = owner
labels["status"] = rls.Info.Status.String()
Expand Down

0 comments on commit a3b49f3

Please sign in to comment.