|
|
@ -18,6 +18,7 @@ package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
@ -35,6 +36,9 @@ import (
|
|
|
|
|
|
|
|
|
|
|
|
var _ Driver = (*Secrets)(nil)
|
|
|
|
var _ Driver = (*Secrets)(nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SizeCutoff = 1048576 // https://kubernetes.io/docs/concepts/configuration/secret/#restriction-data-size
|
|
|
|
|
|
|
|
const HelmPartialStorageType = "sh.helm.partial.v1"
|
|
|
|
|
|
|
|
|
|
|
|
// SecretsDriverName is the string name of the driver.
|
|
|
|
// SecretsDriverName is the string name of the driver.
|
|
|
|
const SecretsDriverName = "Secret"
|
|
|
|
const SecretsDriverName = "Secret"
|
|
|
|
|
|
|
|
|
|
|
@ -45,6 +49,11 @@ type Secrets struct {
|
|
|
|
Log func(string, ...interface{})
|
|
|
|
Log func(string, ...interface{})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The "partial" pendant to pkg.storage.driver.storage:makeKey
|
|
|
|
|
|
|
|
func makePartialKey(rlsname string, version int, chunkIndex int) string {
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s.%s.v%d-%d", HelmPartialStorageType, rlsname, version, chunkIndex)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewSecrets initializes a new Secrets wrapping an implementation of
|
|
|
|
// NewSecrets initializes a new Secrets wrapping an implementation of
|
|
|
|
// the kubernetes SecretsInterface.
|
|
|
|
// the kubernetes SecretsInterface.
|
|
|
|
func NewSecrets(impl corev1.SecretInterface) *Secrets {
|
|
|
|
func NewSecrets(impl corev1.SecretInterface) *Secrets {
|
|
|
@ -59,6 +68,25 @@ func (secrets *Secrets) Name() string {
|
|
|
|
return SecretsDriverName
|
|
|
|
return SecretsDriverName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// _FetchReleaseData is an internal function to fetch the release data
|
|
|
|
|
|
|
|
// from the release secret and subsequent partial secrets.
|
|
|
|
|
|
|
|
func (secrets *Secrets) _FetchReleaseData(first *v1.Secret) (string, error) {
|
|
|
|
|
|
|
|
data := string(first.Data["release"])
|
|
|
|
|
|
|
|
nextKey, ok := first.Labels["continuedIn"]
|
|
|
|
|
|
|
|
for ok {
|
|
|
|
|
|
|
|
obj, err := secrets.impl.Get(context.Background(), nextKey, metav1.GetOptions{})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
|
|
|
|
|
|
return "", errors.Wrapf(ErrReleaseNotFound, "partial release not found %q", nextKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.Wrapf(err, "failed to get partial %q", nextKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
data = data + string(obj.Data["release"])
|
|
|
|
|
|
|
|
nextKey, ok = obj.Labels["continuedIn"]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get fetches the release named by key. The corresponding release is returned
|
|
|
|
// Get fetches the release named by key. The corresponding release is returned
|
|
|
|
// or error if not found.
|
|
|
|
// or error if not found.
|
|
|
|
func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
|
|
|
|
func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
|
|
|
@ -70,8 +98,12 @@ func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, errors.Wrapf(err, "get: failed to get %q", key)
|
|
|
|
return nil, errors.Wrapf(err, "get: failed to get %q", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// found the secret, decode the base64 data string
|
|
|
|
data, err := secrets._FetchReleaseData(obj)
|
|
|
|
r, err := decodeRelease(string(obj.Data["release"]))
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "get: failed to fetch release data %q", key)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode the base64 data string
|
|
|
|
|
|
|
|
r, err := decodeRelease(data)
|
|
|
|
return r, errors.Wrapf(err, "get: failed to decode data %q", key)
|
|
|
|
return r, errors.Wrapf(err, "get: failed to decode data %q", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -92,7 +124,13 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release,
|
|
|
|
// iterate over the secrets object list
|
|
|
|
// iterate over the secrets object list
|
|
|
|
// and decode each release
|
|
|
|
// and decode each release
|
|
|
|
for _, item := range list.Items {
|
|
|
|
for _, item := range list.Items {
|
|
|
|
rls, err := decodeRelease(string(item.Data["release"]))
|
|
|
|
data, err := secrets._FetchReleaseData(&item)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
secrets.Log("list: failed to fetch release data: %v: %s", item, err)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode the base64 data string
|
|
|
|
|
|
|
|
rls, err := decodeRelease(data)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
secrets.Log("list: failed to decode release: %v: %s", item, err)
|
|
|
|
secrets.Log("list: failed to decode release: %v: %s", item, err)
|
|
|
|
continue
|
|
|
|
continue
|
|
|
@ -131,7 +169,12 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error)
|
|
|
|
|
|
|
|
|
|
|
|
var results []*rspb.Release
|
|
|
|
var results []*rspb.Release
|
|
|
|
for _, item := range list.Items {
|
|
|
|
for _, item := range list.Items {
|
|
|
|
rls, err := decodeRelease(string(item.Data["release"]))
|
|
|
|
data, err := secrets._FetchReleaseData(&item)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
secrets.Log("query: failed to fetch release data: %s", err)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
rls, err := decodeRelease(data)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
secrets.Log("query: failed to decode release: %s", err)
|
|
|
|
secrets.Log("query: failed to decode release: %s", err)
|
|
|
|
continue
|
|
|
|
continue
|
|
|
@ -151,17 +194,18 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
|
|
|
|
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
|
|
|
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
|
|
|
|
|
|
|
|
|
|
|
// create a new secret to hold the release
|
|
|
|
// create a new secret to hold the release
|
|
|
|
obj, err := newSecretsObject(key, rls, lbs)
|
|
|
|
secretsList, err := newSecretObjects(key, rls, lbs)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "create: failed to encode release %q", rls.Name)
|
|
|
|
return errors.Wrapf(err, "create: failed to encode release %q", rls.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// push the secret object out into the kubiverse
|
|
|
|
// push the secret objects out into the kubiverse
|
|
|
|
if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
|
|
|
|
for _, obj := range secretsList {
|
|
|
|
if apierrors.IsAlreadyExists(err) {
|
|
|
|
if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
|
|
|
|
return ErrReleaseExists
|
|
|
|
if apierrors.IsAlreadyExists(err) {
|
|
|
|
|
|
|
|
return errors.Wrapf(ErrReleaseExists, "create: key %s already exists", obj.ObjectMeta.Name)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrap(err, "create: failed to create")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return errors.Wrap(err, "create: failed to create")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -169,20 +213,68 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
|
|
|
|
// Update updates the Secret holding the release. If not found
|
|
|
|
// Update updates the Secret holding the release. If not found
|
|
|
|
// the Secret is created to hold the release.
|
|
|
|
// the Secret is created to hold the release.
|
|
|
|
func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
|
|
|
|
func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
|
|
|
|
|
|
|
|
// get current release secret
|
|
|
|
|
|
|
|
obj, err := secrets.impl.Get(context.Background(), key, metav1.GetOptions{})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
|
|
|
|
|
|
return ErrReleaseNotFound
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrapf(err, "update: failed to get %q", key)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
partialKeys := map[string]bool{} // store if this partial should be deleted
|
|
|
|
|
|
|
|
partialKeys[key] = false // add the first release key, never delete it
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get keys for existing partial items if there's any
|
|
|
|
|
|
|
|
// don't use _FetchReleaseData as we only need the keys, not the data
|
|
|
|
|
|
|
|
nextKey, ok := obj.Labels["continuedIn"]
|
|
|
|
|
|
|
|
for ok {
|
|
|
|
|
|
|
|
partialKeys[nextKey] = true
|
|
|
|
|
|
|
|
obj, err := secrets.impl.Get(context.Background(), nextKey, metav1.GetOptions{})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
|
|
|
|
|
|
return errors.Wrapf(ErrReleaseNotFound, "update: partial release not found %q", nextKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrapf(err, "update: failed to get %q", key)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nextKey, ok = obj.Labels["continuedIn"]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// set labels for secrets object meta data
|
|
|
|
// set labels for secrets object meta data
|
|
|
|
var lbs labels
|
|
|
|
var lbs labels
|
|
|
|
|
|
|
|
|
|
|
|
lbs.init()
|
|
|
|
lbs.init()
|
|
|
|
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
|
|
|
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
|
|
|
|
|
|
|
|
|
|
|
// create a new secret object to hold the release
|
|
|
|
// create new secret objects to hold the updated release
|
|
|
|
obj, err := newSecretsObject(key, rls, lbs)
|
|
|
|
secretsList, err := newSecretObjects(key, rls, lbs)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "update: failed to encode release %q", rls.Name)
|
|
|
|
return errors.Wrapf(err, "update: failed to encode release %q", rls.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// push the secret object out into the kubiverse
|
|
|
|
|
|
|
|
_, err = secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
|
|
|
|
// update secrets as needed
|
|
|
|
return errors.Wrap(err, "update: failed to update")
|
|
|
|
for _, obj := range secretsList {
|
|
|
|
|
|
|
|
_, ok = partialKeys[obj.ObjectMeta.Name]
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
|
|
partialKeys[obj.ObjectMeta.Name] = false
|
|
|
|
|
|
|
|
if _, err := secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{}); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "update: failed to update")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "update: failed to create new partial")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete any extra partials
|
|
|
|
|
|
|
|
for key, shouldRemove := range partialKeys {
|
|
|
|
|
|
|
|
if shouldRemove {
|
|
|
|
|
|
|
|
if err := secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "update: failed to delete extra partial")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Delete deletes the Secret holding the release named by key.
|
|
|
|
// Delete deletes the Secret holding the release named by key.
|
|
|
@ -191,25 +283,58 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
|
|
|
|
if rls, err = secrets.Get(key); err != nil {
|
|
|
|
if rls, err = secrets.Get(key); err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// delete the release
|
|
|
|
|
|
|
|
err = secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{})
|
|
|
|
// fetch main release object
|
|
|
|
return rls, err
|
|
|
|
obj, err := secrets.impl.Get(context.Background(), key, metav1.GetOptions{})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// don't use _FetchReleaseData as we only need the keys, not the data
|
|
|
|
|
|
|
|
// fetch all keys that need to be deleted
|
|
|
|
|
|
|
|
var keys []string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextKey, ok := obj.Labels["continuedIn"]
|
|
|
|
|
|
|
|
for ok {
|
|
|
|
|
|
|
|
obj, err := secrets.impl.Get(context.Background(), nextKey, metav1.GetOptions{})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(ErrReleaseNotFound, "delete: partial release not found %q", nextKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "delete: failed to get partial %q", nextKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the partial key to the list of partial keys to delete
|
|
|
|
|
|
|
|
keys = append(keys, nextKey)
|
|
|
|
|
|
|
|
// Prepare next iteration
|
|
|
|
|
|
|
|
nextKey, ok = obj.Labels["continuedIn"]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete all objects
|
|
|
|
|
|
|
|
if err := secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil {
|
|
|
|
|
|
|
|
return rls, errors.Wrapf(err, "delete: failed to delete %q", key)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, deleteKey := range keys {
|
|
|
|
|
|
|
|
if err := secrets.impl.Delete(context.Background(), deleteKey, metav1.DeleteOptions{}); err != nil {
|
|
|
|
|
|
|
|
return rls, errors.Wrapf(err, "delete: failed to delete partial %q", deleteKey)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return rls, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// newSecretsObject constructs a kubernetes Secret object
|
|
|
|
// newSecretObjects constructs an array of kubernetes Secret objects
|
|
|
|
// to store a release. Each secret data entry is the base64
|
|
|
|
// to store a release.
|
|
|
|
// encoded gzipped string of a release.
|
|
|
|
// The data stored within these secrets is the base64 encoded, gzipped string
|
|
|
|
|
|
|
|
// of a release, split across multiple secrets if needed.
|
|
|
|
|
|
|
|
// The maximum size of a secret, when this code was written, is 1Mib, as defined here
|
|
|
|
|
|
|
|
// https://kubernetes.io/docs/concepts/configuration/secret/#restriction-data-size
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// The following labels are used within each secret:
|
|
|
|
// The following labels are used within each secret:
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update)
|
|
|
|
// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update)
|
|
|
|
// "createdAt" - timestamp indicating when this secret was created. (set in Create)
|
|
|
|
// "createdAt" - timestamp indicating when this secret was created. (set in Create)
|
|
|
|
// "version" - version of the release.
|
|
|
|
// "version" - version of the release.
|
|
|
|
// "status" - status of the release (see pkg/release/status.go for variants)
|
|
|
|
// "status" - status of the release (see pkg/release/status.go for variants)
|
|
|
|
// "owner" - owner of the secret, currently "helm".
|
|
|
|
// "owner" - owner of the secret, currently "helm".
|
|
|
|
// "name" - name of the release.
|
|
|
|
// "name" - name of the release.
|
|
|
|
|
|
|
|
// "continuedIn" - if set, the encoded contents of the release continue in the secret this references.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) {
|
|
|
|
func newSecretObjects(key string, rls *rspb.Release, lbs labels) ([]*v1.Secret, error) {
|
|
|
|
const owner = "helm"
|
|
|
|
const owner = "helm"
|
|
|
|
|
|
|
|
|
|
|
|
// encode the release
|
|
|
|
// encode the release
|
|
|
@ -222,13 +347,14 @@ func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, er
|
|
|
|
lbs.init()
|
|
|
|
lbs.init()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
releaseBytes := []byte(s)
|
|
|
|
|
|
|
|
|
|
|
|
// apply labels
|
|
|
|
// apply labels
|
|
|
|
lbs.set("name", rls.Name)
|
|
|
|
lbs.set("name", rls.Name)
|
|
|
|
lbs.set("owner", owner)
|
|
|
|
lbs.set("owner", owner)
|
|
|
|
lbs.set("status", rls.Info.Status.String())
|
|
|
|
lbs.set("status", rls.Info.Status.String())
|
|
|
|
lbs.set("version", strconv.Itoa(rls.Version))
|
|
|
|
lbs.set("version", strconv.Itoa(rls.Version))
|
|
|
|
|
|
|
|
|
|
|
|
// create and return secret object.
|
|
|
|
|
|
|
|
// Helm 3 introduced setting the 'Type' field
|
|
|
|
// Helm 3 introduced setting the 'Type' field
|
|
|
|
// in the Kubernetes storage object.
|
|
|
|
// in the Kubernetes storage object.
|
|
|
|
// Helm defines the field content as follows:
|
|
|
|
// Helm defines the field content as follows:
|
|
|
@ -239,12 +365,78 @@ func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, er
|
|
|
|
// metadata is modified.
|
|
|
|
// metadata is modified.
|
|
|
|
// This would potentially be a breaking change
|
|
|
|
// This would potentially be a breaking change
|
|
|
|
// and should only happen between major versions.
|
|
|
|
// and should only happen between major versions.
|
|
|
|
return &v1.Secret{
|
|
|
|
|
|
|
|
|
|
|
|
var secrets []*v1.Secret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(releaseBytes) <= SizeCutoff {
|
|
|
|
|
|
|
|
// early return to only create the first object
|
|
|
|
|
|
|
|
return append(secrets, &v1.Secret{
|
|
|
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
|
|
|
Name: key,
|
|
|
|
|
|
|
|
Labels: lbs.toMap(),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Type: "helm.sh/release.v1",
|
|
|
|
|
|
|
|
Data: map[string][]byte{"release": releaseBytes},
|
|
|
|
|
|
|
|
}), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create copy of the labels
|
|
|
|
|
|
|
|
var currentLabels labels
|
|
|
|
|
|
|
|
currentLabels.init()
|
|
|
|
|
|
|
|
currentLabels.fromMap(lbs.toMap())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create a secret with the first chunk of data
|
|
|
|
|
|
|
|
firstSecret := &v1.Secret{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: key,
|
|
|
|
Name: key,
|
|
|
|
Labels: lbs.toMap(),
|
|
|
|
Labels: currentLabels.toMap(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Type: "helm.sh/release.v1",
|
|
|
|
Type: "helm.sh/release.v1",
|
|
|
|
Data: map[string][]byte{"release": []byte(s)},
|
|
|
|
Data: map[string][]byte{"release": releaseBytes[0:SizeCutoff]},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// build the reference to the next chunk
|
|
|
|
|
|
|
|
var currentChunkIndex int = 1
|
|
|
|
|
|
|
|
currentChunkKey := makePartialKey(rls.Name, rls.Version, currentChunkIndex)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add the continuedIn field
|
|
|
|
|
|
|
|
firstSecret.ObjectMeta.Labels["continuedIn"] = currentChunkKey
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// append to the list of secrets to create
|
|
|
|
|
|
|
|
secrets = append(secrets, firstSecret)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// prepare to split
|
|
|
|
|
|
|
|
// use a window defined by idxStart:idxStop
|
|
|
|
|
|
|
|
var idxStart int = 0
|
|
|
|
|
|
|
|
var idxStop int = SizeCutoff
|
|
|
|
|
|
|
|
for idxStop != len(releaseBytes) {
|
|
|
|
|
|
|
|
// shift window
|
|
|
|
|
|
|
|
idxStart += SizeCutoff
|
|
|
|
|
|
|
|
idxStop += SizeCutoff
|
|
|
|
|
|
|
|
// don't overread - cap idxStop
|
|
|
|
|
|
|
|
if idxStop > len(releaseBytes) {
|
|
|
|
|
|
|
|
idxStop = len(releaseBytes)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
currentLabels.init()
|
|
|
|
|
|
|
|
currentLabels.fromMap(lbs.toMap())
|
|
|
|
|
|
|
|
// create secret to store partial data
|
|
|
|
|
|
|
|
currentSecret := &v1.Secret{
|
|
|
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
|
|
|
Name: currentChunkKey, // this is the key the previous chunk will point to
|
|
|
|
|
|
|
|
Labels: currentLabels.toMap(),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Type: "helm.sh/partial.v1", // custom type to indicate this isn't a release definition in itself
|
|
|
|
|
|
|
|
Data: map[string][]byte{"release": releaseBytes[idxStart:idxStop]},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we'll need another partial chunk
|
|
|
|
|
|
|
|
if idxStop != len(releaseBytes) {
|
|
|
|
|
|
|
|
currentChunkIndex += 1 // increment current chunk
|
|
|
|
|
|
|
|
currentChunkKey = makePartialKey(rls.Name, rls.Version, currentChunkIndex) // make key for the next chunk
|
|
|
|
|
|
|
|
currentSecret.ObjectMeta.Labels["continuedIn"] = currentChunkKey // store reference to it
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
secrets = append(secrets, currentSecret)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return secrets, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|