feat(storage): mark configmap release schema with a label

ConfigMaps have no Type field to record it in, unlike Secrets. Absence
of the label means v1. Also documents why the sh.helm.release.v1 name
prefix is decoupled from Type.

Signed-off-by: Evans Mungai <mbuevans@gmail.com>
pull/32667/head
Evans Mungai 2 weeks ago
parent 1c5e0bacfd
commit c37b306f64
No known key found for this signature in database
GPG Key ID: BBEB812143DD14E1

@ -255,6 +255,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls release.Releaser, err error)
// "status" - status of the release (see pkg/release/status.go for variants)
// "owner" - owner of the configmap, currently "helm".
// "name" - name of the release.
// "helm.sh/release-version" - release object schema version, see releaseVersionLabel.
func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) {
const owner = "helm"
@ -276,6 +277,7 @@ func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigM
lbs.set("owner", owner)
lbs.set("status", rls.Info.Status.String())
lbs.set("version", strconv.Itoa(rls.Version))
lbs.set(releaseVersionLabel, releaseVersion)
// create and return configmap object
return &v1.ConfigMap{

@ -72,6 +72,24 @@ func TestUncompressedConfigMapGet(t *testing.T) {
assert.Equalf(t, rel, got, "Expected {%v}, got {%v}", rel, got)
}
func TestConfigMapReleaseVersionLabel(t *testing.T) {
vers := 1
name := "smug-pigeon"
namespace := "default"
key := testKey(name, vers)
rel := releaseStub(name, vers, namespace, common.StatusDeployed)
cfgmap, err := newConfigMapsObject(key, rel, nil)
require.NoError(t, err, "Failed to create configmap")
// ConfigMaps have no Type field, so the release schema version is a label.
assert.Equal(t, releaseVersion, cfgmap.Labels[releaseVersionLabel])
// The label is a system label and must not leak into the release's own labels.
assert.True(t, isSystemLabel(releaseVersionLabel))
assert.NotContains(t, filterSystemLabels(cfgmap.Labels), releaseVersionLabel)
}
func convertReleaserToV1(t *testing.T, rel release.Releaser) *rspb.Release {
t.Helper()
switch r := rel.(type) {

@ -31,7 +31,17 @@ var b64 = base64.StdEncoding
var magicGzip = []byte{0x1f, 0x8b, 0x08}
var systemLabels = []string{"name", "owner", "status", "version", "createdAt", "modifiedAt"}
// releaseVersionLabel carries the release object schema version on storage
// objects that have no native field for it. Secrets record it in their Type
// field, but ConfigMaps have no equivalent, so the marker is a label instead.
// Absence of the label means release v1, so records written before this label
// existed continue to read correctly.
const releaseVersionLabel = "helm.sh/release-version"
// releaseVersion is the schema version written by this package.
const releaseVersion = "v2"
var systemLabels = []string{"name", "owner", "status", "version", "createdAt", "modifiedAt", releaseVersionLabel}
// encodeRelease encodes a release returning a base64 encoded
// gzipped string representation, or error.

@ -30,11 +30,23 @@ import (
"helm.sh/helm/v4/pkg/storage/driver"
)
// HelmStorageType is the type field of the Kubernetes storage object which stores the Helm release
// version. It is modified slightly replacing the '/': sh.helm/release.v1
// Note: The version 'v1' is incremented if the release object metadata is
// modified between major releases.
// This constant is used as a prefix for the Kubernetes storage object name.
// HelmStorageType is the prefix used for the name of the Kubernetes storage object
// that holds a release. It is derived from that object's 'Type' field
// (helm.sh/release.v1) by reversing the domain and replacing the '/' with a '.'.
//
// The prefix is deliberately decoupled from the 'Type' field
// (helm.sh/release.v1, helm.sh/release.v2). Type records the schema
// of the encoded release body, so it tracks the release object version. This
// prefix only keeps release names unique, and was added to stop Helm 3 records
// colliding with Helm 2 ones (https://github.com/helm/helm/issues/6435).
//
// The prefix is therefore not incremented with the release object version.
// Kubernetes objects cannot be renamed in place, so doing so would mean
// recreating every record and deleting the old one, per revision, per namespace.
// Until that completed, Get, Update and Delete would all miss, since they address
// objects by exact key, while List, Query and History would keep returning the old
// records because they select on labels. The only thing gained is seeing the
// release object version in the object name, which Type already records.
const HelmStorageType = "sh.helm.release.v1"
// Storage represents a storage engine for a Release.

Loading…
Cancel
Save