257
vendor/helm.sh/helm/v3/pkg/storage/driver/cfgmaps.go
vendored
Normal file
257
vendor/helm.sh/helm/v3/pkg/storage/driver/cfgmaps.go
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kblabels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var _ Driver = (*ConfigMaps)(nil)
|
||||
|
||||
// ConfigMapsDriverName is the string name of the driver.
|
||||
const ConfigMapsDriverName = "ConfigMap"
|
||||
|
||||
// ConfigMaps is a wrapper around an implementation of a kubernetes
|
||||
// ConfigMapsInterface.
|
||||
type ConfigMaps struct {
|
||||
impl corev1.ConfigMapInterface
|
||||
Log func(string, ...interface{})
|
||||
}
|
||||
|
||||
// NewConfigMaps initializes a new ConfigMaps wrapping an implementation of
|
||||
// the kubernetes ConfigMapsInterface.
|
||||
func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps {
|
||||
return &ConfigMaps{
|
||||
impl: impl,
|
||||
Log: func(_ string, _ ...interface{}) {},
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the driver.
|
||||
func (cfgmaps *ConfigMaps) Name() string {
|
||||
return ConfigMapsDriverName
|
||||
}
|
||||
|
||||
// Get fetches the release named by key. The corresponding release is returned
|
||||
// or error if not found.
|
||||
func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
|
||||
// fetch the configmap holding the release named by key
|
||||
obj, err := cfgmaps.impl.Get(context.Background(), key, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
cfgmaps.Log("get: failed to get %q: %s", key, err)
|
||||
return nil, err
|
||||
}
|
||||
// found the configmap, decode the base64 data string
|
||||
r, err := decodeRelease(obj.Data["release"])
|
||||
if err != nil {
|
||||
cfgmaps.Log("get: failed to decode data %q: %s", key, err)
|
||||
return nil, err
|
||||
}
|
||||
// return the release object
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// List fetches all releases and returns the list releases such
|
||||
// that filter(release) == true. An error is returned if the
|
||||
// configmap fails to retrieve the releases.
|
||||
func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
lsel := kblabels.Set{"owner": "helm"}.AsSelector()
|
||||
opts := metav1.ListOptions{LabelSelector: lsel.String()}
|
||||
|
||||
list, err := cfgmaps.impl.List(context.Background(), opts)
|
||||
if err != nil {
|
||||
cfgmaps.Log("list: failed to list: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []*rspb.Release
|
||||
|
||||
// iterate over the configmaps object list
|
||||
// and decode each release
|
||||
for _, item := range list.Items {
|
||||
rls, err := decodeRelease(item.Data["release"])
|
||||
if err != nil {
|
||||
cfgmaps.Log("list: failed to decode release: %v: %s", item, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
|
||||
if filter(rls) {
|
||||
results = append(results, rls)
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Query fetches all releases that match the provided map of labels.
|
||||
// An error is returned if the configmap fails to retrieve the releases.
|
||||
func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||
ls := kblabels.Set{}
|
||||
for k, v := range labels {
|
||||
if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
|
||||
return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
|
||||
}
|
||||
ls[k] = v
|
||||
}
|
||||
|
||||
opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
|
||||
|
||||
list, err := cfgmaps.impl.List(context.Background(), opts)
|
||||
if err != nil {
|
||||
cfgmaps.Log("query: failed to query with labels: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list.Items) == 0 {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
var results []*rspb.Release
|
||||
for _, item := range list.Items {
|
||||
rls, err := decodeRelease(item.Data["release"])
|
||||
if err != nil {
|
||||
cfgmaps.Log("query: failed to decode release: %s", err)
|
||||
continue
|
||||
}
|
||||
results = append(results, rls)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Create creates a new ConfigMap holding the release. If the
|
||||
// ConfigMap already exists, ErrReleaseExists is returned.
|
||||
func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
|
||||
// set labels for configmaps object meta data
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new configmap to hold the release
|
||||
obj, err := newConfigMapsObject(key, rls, lbs)
|
||||
if err != nil {
|
||||
cfgmaps.Log("create: failed to encode release %q: %s", rls.Name, err)
|
||||
return err
|
||||
}
|
||||
// push the configmap object out into the kubiverse
|
||||
if _, err := cfgmaps.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
return ErrReleaseExists
|
||||
}
|
||||
|
||||
cfgmaps.Log("create: failed to create: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates the ConfigMap holding the release. If not found
|
||||
// the ConfigMap is created to hold the release.
|
||||
func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
|
||||
// set labels for configmaps object meta data
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new configmap object to hold the release
|
||||
obj, err := newConfigMapsObject(key, rls, lbs)
|
||||
if err != nil {
|
||||
cfgmaps.Log("update: failed to encode release %q: %s", rls.Name, err)
|
||||
return err
|
||||
}
|
||||
// push the configmap object out into the kubiverse
|
||||
_, err = cfgmaps.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
cfgmaps.Log("update: failed to update: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes the ConfigMap holding the release named by key.
|
||||
func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
|
||||
// fetch the release to check existence
|
||||
if rls, err = cfgmaps.Get(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// delete the release
|
||||
if err = cfgmaps.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil {
|
||||
return rls, err
|
||||
}
|
||||
return rls, nil
|
||||
}
|
||||
|
||||
// newConfigMapsObject constructs a kubernetes ConfigMap object
|
||||
// to store a release. Each configmap data entry is the base64
|
||||
// encoded gzipped string of a release.
|
||||
//
|
||||
// The following labels are used within each configmap:
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this configmap was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this configmap was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the configmap, currently "helm".
|
||||
// "name" - name of the release.
|
||||
//
|
||||
func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) {
|
||||
const owner = "helm"
|
||||
|
||||
// encode the release
|
||||
s, err := encodeRelease(rls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lbs == nil {
|
||||
lbs.init()
|
||||
}
|
||||
|
||||
// apply labels
|
||||
lbs.set("name", rls.Name)
|
||||
lbs.set("owner", owner)
|
||||
lbs.set("status", rls.Info.Status.String())
|
||||
lbs.set("version", strconv.Itoa(rls.Version))
|
||||
|
||||
// create and return configmap object
|
||||
return &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: key,
|
||||
Labels: lbs.toMap(),
|
||||
},
|
||||
Data: map[string]string{"release": s},
|
||||
}, nil
|
||||
}
|
||||
105
vendor/helm.sh/helm/v3/pkg/storage/driver/driver.go
vendored
Normal file
105
vendor/helm.sh/helm/v3/pkg/storage/driver/driver.go
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrReleaseNotFound indicates that a release is not found.
|
||||
ErrReleaseNotFound = errors.New("release: not found")
|
||||
// ErrReleaseExists indicates that a release already exists.
|
||||
ErrReleaseExists = errors.New("release: already exists")
|
||||
// ErrInvalidKey indicates that a release key could not be parsed.
|
||||
ErrInvalidKey = errors.New("release: invalid key")
|
||||
// ErrNoDeployedReleases indicates that there are no releases with the given key in the deployed state
|
||||
ErrNoDeployedReleases = errors.New("has no deployed releases")
|
||||
)
|
||||
|
||||
// StorageDriverError records an error and the release name that caused it
|
||||
type StorageDriverError struct {
|
||||
ReleaseName string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *StorageDriverError) Error() string {
|
||||
return fmt.Sprintf("%q %s", e.ReleaseName, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *StorageDriverError) Unwrap() error { return e.Err }
|
||||
|
||||
func NewErrNoDeployedReleases(releaseName string) error {
|
||||
return &StorageDriverError{
|
||||
ReleaseName: releaseName,
|
||||
Err: ErrNoDeployedReleases,
|
||||
}
|
||||
}
|
||||
|
||||
// Creator is the interface that wraps the Create method.
|
||||
//
|
||||
// Create stores the release or returns ErrReleaseExists
|
||||
// if an identical release already exists.
|
||||
type Creator interface {
|
||||
Create(key string, rls *rspb.Release) error
|
||||
}
|
||||
|
||||
// Updator is the interface that wraps the Update method.
|
||||
//
|
||||
// Update updates an existing release or returns
|
||||
// ErrReleaseNotFound if the release does not exist.
|
||||
type Updator interface {
|
||||
Update(key string, rls *rspb.Release) error
|
||||
}
|
||||
|
||||
// Deletor is the interface that wraps the Delete method.
|
||||
//
|
||||
// Delete deletes the release named by key or returns
|
||||
// ErrReleaseNotFound if the release does not exist.
|
||||
type Deletor interface {
|
||||
Delete(key string) (*rspb.Release, error)
|
||||
}
|
||||
|
||||
// Queryor is the interface that wraps the Get and List methods.
|
||||
//
|
||||
// Get returns the release named by key or returns ErrReleaseNotFound
|
||||
// if the release does not exist.
|
||||
//
|
||||
// List returns the set of all releases that satisfy the filter predicate.
|
||||
//
|
||||
// Query returns the set of all releases that match the provided label set.
|
||||
type Queryor interface {
|
||||
Get(key string) (*rspb.Release, error)
|
||||
List(filter func(*rspb.Release) bool) ([]*rspb.Release, error)
|
||||
Query(labels map[string]string) ([]*rspb.Release, error)
|
||||
}
|
||||
|
||||
// Driver is the interface composed of Creator, Updator, Deletor, and Queryor
|
||||
// interfaces. It defines the behavior for storing, updating, deleted,
|
||||
// and retrieving Helm releases from some underlying storage mechanism,
|
||||
// e.g. memory, configmaps.
|
||||
type Driver interface {
|
||||
Creator
|
||||
Updator
|
||||
Deletor
|
||||
Queryor
|
||||
Name() string
|
||||
}
|
||||
48
vendor/helm.sh/helm/v3/pkg/storage/driver/labels.go
vendored
Normal file
48
vendor/helm.sh/helm/v3/pkg/storage/driver/labels.go
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver
|
||||
|
||||
// labels is a map of key value pairs to be included as metadata in a configmap object.
|
||||
type labels map[string]string
|
||||
|
||||
func (lbs *labels) init() { *lbs = labels(make(map[string]string)) }
|
||||
func (lbs labels) get(key string) string { return lbs[key] }
|
||||
func (lbs labels) set(key, val string) { lbs[key] = val }
|
||||
|
||||
func (lbs labels) keys() (ls []string) {
|
||||
for key := range lbs {
|
||||
ls = append(ls, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (lbs labels) match(set labels) bool {
|
||||
for _, key := range set.keys() {
|
||||
if lbs.get(key) != set.get(key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (lbs labels) toMap() map[string]string { return lbs }
|
||||
|
||||
func (lbs *labels) fromMap(kvs map[string]string) {
|
||||
for k, v := range kvs {
|
||||
lbs.set(k, v)
|
||||
}
|
||||
}
|
||||
240
vendor/helm.sh/helm/v3/pkg/storage/driver/memory.go
vendored
Normal file
240
vendor/helm.sh/helm/v3/pkg/storage/driver/memory.go
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var _ Driver = (*Memory)(nil)
|
||||
|
||||
const (
|
||||
// MemoryDriverName is the string name of this driver.
|
||||
MemoryDriverName = "Memory"
|
||||
|
||||
defaultNamespace = "default"
|
||||
)
|
||||
|
||||
// A map of release names to list of release records
|
||||
type memReleases map[string]records
|
||||
|
||||
// Memory is the in-memory storage driver implementation.
|
||||
type Memory struct {
|
||||
sync.RWMutex
|
||||
namespace string
|
||||
// A map of namespaces to releases
|
||||
cache map[string]memReleases
|
||||
}
|
||||
|
||||
// NewMemory initializes a new memory driver.
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{cache: map[string]memReleases{}, namespace: "default"}
|
||||
}
|
||||
|
||||
// SetNamespace sets a specific namespace in which releases will be accessed.
|
||||
// An empty string indicates all namespaces (for the list operation)
|
||||
func (mem *Memory) SetNamespace(ns string) {
|
||||
mem.namespace = ns
|
||||
}
|
||||
|
||||
// Name returns the name of the driver.
|
||||
func (mem *Memory) Name() string {
|
||||
return MemoryDriverName
|
||||
}
|
||||
|
||||
// Get returns the release named by key or returns ErrReleaseNotFound.
|
||||
func (mem *Memory) Get(key string) (*rspb.Release, error) {
|
||||
defer unlock(mem.rlock())
|
||||
|
||||
keyWithoutPrefix := strings.TrimPrefix(key, "sh.helm.release.v1.")
|
||||
switch elems := strings.Split(keyWithoutPrefix, ".v"); len(elems) {
|
||||
case 2:
|
||||
name, ver := elems[0], elems[1]
|
||||
if _, err := strconv.Atoi(ver); err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
if recs, ok := mem.cache[mem.namespace][name]; ok {
|
||||
if r := recs.Get(key); r != nil {
|
||||
return r.rls, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrReleaseNotFound
|
||||
default:
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
}
|
||||
|
||||
// List returns the list of all releases such that filter(release) == true
|
||||
func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
defer unlock(mem.rlock())
|
||||
|
||||
var ls []*rspb.Release
|
||||
for namespace := range mem.cache {
|
||||
if mem.namespace != "" {
|
||||
// Should only list releases of this namespace
|
||||
namespace = mem.namespace
|
||||
}
|
||||
for _, recs := range mem.cache[namespace] {
|
||||
recs.Iter(func(_ int, rec *record) bool {
|
||||
if filter(rec.rls) {
|
||||
ls = append(ls, rec.rls)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
if mem.namespace != "" {
|
||||
// Should only list releases of this namespace
|
||||
break
|
||||
}
|
||||
}
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// Query returns the set of releases that match the provided set of labels
|
||||
func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
|
||||
defer unlock(mem.rlock())
|
||||
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.fromMap(keyvals)
|
||||
|
||||
var ls []*rspb.Release
|
||||
for namespace := range mem.cache {
|
||||
if mem.namespace != "" {
|
||||
// Should only query releases of this namespace
|
||||
namespace = mem.namespace
|
||||
}
|
||||
for _, recs := range mem.cache[namespace] {
|
||||
recs.Iter(func(_ int, rec *record) bool {
|
||||
// A query for a release name that doesn't exist (has been deleted)
|
||||
// can cause rec to be nil.
|
||||
if rec == nil {
|
||||
return false
|
||||
}
|
||||
if rec.lbs.match(lbs) {
|
||||
ls = append(ls, rec.rls)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
if mem.namespace != "" {
|
||||
// Should only query releases of this namespace
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(ls) == 0 {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// Create creates a new release or returns ErrReleaseExists.
|
||||
func (mem *Memory) Create(key string, rls *rspb.Release) error {
|
||||
defer unlock(mem.wlock())
|
||||
|
||||
// For backwards compatibility, we protect against an unset namespace
|
||||
namespace := rls.Namespace
|
||||
if namespace == "" {
|
||||
namespace = defaultNamespace
|
||||
}
|
||||
mem.SetNamespace(namespace)
|
||||
|
||||
if _, ok := mem.cache[namespace]; !ok {
|
||||
mem.cache[namespace] = memReleases{}
|
||||
}
|
||||
|
||||
if recs, ok := mem.cache[namespace][rls.Name]; ok {
|
||||
if err := recs.Add(newRecord(key, rls)); err != nil {
|
||||
return err
|
||||
}
|
||||
mem.cache[namespace][rls.Name] = recs
|
||||
return nil
|
||||
}
|
||||
mem.cache[namespace][rls.Name] = records{newRecord(key, rls)}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates a release or returns ErrReleaseNotFound.
|
||||
func (mem *Memory) Update(key string, rls *rspb.Release) error {
|
||||
defer unlock(mem.wlock())
|
||||
|
||||
// For backwards compatibility, we protect against an unset namespace
|
||||
namespace := rls.Namespace
|
||||
if namespace == "" {
|
||||
namespace = defaultNamespace
|
||||
}
|
||||
mem.SetNamespace(namespace)
|
||||
|
||||
if _, ok := mem.cache[namespace]; ok {
|
||||
if rs, ok := mem.cache[namespace][rls.Name]; ok && rs.Exists(key) {
|
||||
rs.Replace(key, newRecord(key, rls))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrReleaseNotFound
|
||||
}
|
||||
|
||||
// Delete deletes a release or returns ErrReleaseNotFound.
|
||||
func (mem *Memory) Delete(key string) (*rspb.Release, error) {
|
||||
defer unlock(mem.wlock())
|
||||
|
||||
keyWithoutPrefix := strings.TrimPrefix(key, "sh.helm.release.v1.")
|
||||
elems := strings.Split(keyWithoutPrefix, ".v")
|
||||
|
||||
if len(elems) != 2 {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
name, ver := elems[0], elems[1]
|
||||
if _, err := strconv.Atoi(ver); err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
if _, ok := mem.cache[mem.namespace]; ok {
|
||||
if recs, ok := mem.cache[mem.namespace][name]; ok {
|
||||
if r := recs.Remove(key); r != nil {
|
||||
// recs.Remove changes the slice reference, so we have to re-assign it.
|
||||
mem.cache[mem.namespace][name] = recs
|
||||
return r.rls, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
// wlock locks mem for writing
|
||||
func (mem *Memory) wlock() func() {
|
||||
mem.Lock()
|
||||
return func() { mem.Unlock() }
|
||||
}
|
||||
|
||||
// rlock locks mem for reading
|
||||
func (mem *Memory) rlock() func() {
|
||||
mem.RLock()
|
||||
return func() { mem.RUnlock() }
|
||||
}
|
||||
|
||||
// unlock calls fn which reverses a mem.rlock or mem.wlock. e.g:
|
||||
// ```defer unlock(mem.rlock())```, locks mem for reading at the
|
||||
// call point of defer and unlocks upon exiting the block.
|
||||
func unlock(fn func()) { fn() }
|
||||
124
vendor/helm.sh/helm/v3/pkg/storage/driver/records.go
vendored
Normal file
124
vendor/helm.sh/helm/v3/pkg/storage/driver/records.go
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// records holds a list of in-memory release records
|
||||
type records []*record
|
||||
|
||||
func (rs records) Len() int { return len(rs) }
|
||||
func (rs records) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
|
||||
func (rs records) Less(i, j int) bool { return rs[i].rls.Version < rs[j].rls.Version }
|
||||
|
||||
func (rs *records) Add(r *record) error {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rs.Exists(r.key) {
|
||||
return ErrReleaseExists
|
||||
}
|
||||
|
||||
*rs = append(*rs, r)
|
||||
sort.Sort(*rs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs records) Get(key string) *record {
|
||||
if i, ok := rs.Index(key); ok {
|
||||
return rs[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *records) Iter(fn func(int, *record) bool) {
|
||||
cp := make([]*record, len(*rs))
|
||||
copy(cp, *rs)
|
||||
|
||||
for i, r := range cp {
|
||||
if !fn(i, r) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *records) Index(key string) (int, bool) {
|
||||
for i, r := range *rs {
|
||||
if r.key == key {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func (rs records) Exists(key string) bool {
|
||||
_, ok := rs.Index(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (rs *records) Remove(key string) (r *record) {
|
||||
if i, ok := rs.Index(key); ok {
|
||||
return rs.removeAt(i)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *records) Replace(key string, rec *record) *record {
|
||||
if i, ok := rs.Index(key); ok {
|
||||
old := (*rs)[i]
|
||||
(*rs)[i] = rec
|
||||
return old
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *records) removeAt(index int) *record {
|
||||
r := (*rs)[index]
|
||||
(*rs)[index] = nil
|
||||
copy((*rs)[index:], (*rs)[index+1:])
|
||||
*rs = (*rs)[:len(*rs)-1]
|
||||
return r
|
||||
}
|
||||
|
||||
// record is the data structure used to cache releases
|
||||
// for the in-memory storage driver
|
||||
type record struct {
|
||||
key string
|
||||
lbs labels
|
||||
rls *rspb.Release
|
||||
}
|
||||
|
||||
// newRecord creates a new in-memory release record
|
||||
func newRecord(key string, rls *rspb.Release) *record {
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.set("name", rls.Name)
|
||||
lbs.set("owner", "helm")
|
||||
lbs.set("status", rls.Info.Status.String())
|
||||
lbs.set("version", strconv.Itoa(rls.Version))
|
||||
|
||||
// return &record{key: key, lbs: lbs, rls: proto.Clone(rls).(*rspb.Release)}
|
||||
return &record{key: key, lbs: lbs, rls: rls}
|
||||
}
|
||||
250
vendor/helm.sh/helm/v3/pkg/storage/driver/secrets.go
vendored
Normal file
250
vendor/helm.sh/helm/v3/pkg/storage/driver/secrets.go
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kblabels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var _ Driver = (*Secrets)(nil)
|
||||
|
||||
// SecretsDriverName is the string name of the driver.
|
||||
const SecretsDriverName = "Secret"
|
||||
|
||||
// Secrets is a wrapper around an implementation of a kubernetes
|
||||
// SecretsInterface.
|
||||
type Secrets struct {
|
||||
impl corev1.SecretInterface
|
||||
Log func(string, ...interface{})
|
||||
}
|
||||
|
||||
// NewSecrets initializes a new Secrets wrapping an implementation of
|
||||
// the kubernetes SecretsInterface.
|
||||
func NewSecrets(impl corev1.SecretInterface) *Secrets {
|
||||
return &Secrets{
|
||||
impl: impl,
|
||||
Log: func(_ string, _ ...interface{}) {},
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the driver.
|
||||
func (secrets *Secrets) Name() string {
|
||||
return SecretsDriverName
|
||||
}
|
||||
|
||||
// Get fetches the release named by key. The corresponding release is returned
|
||||
// or error if not found.
|
||||
func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
|
||||
// fetch the secret holding the release named by key
|
||||
obj, err := secrets.impl.Get(context.Background(), key, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
return nil, errors.Wrapf(err, "get: failed to get %q", key)
|
||||
}
|
||||
// found the secret, decode the base64 data string
|
||||
r, err := decodeRelease(string(obj.Data["release"]))
|
||||
return r, errors.Wrapf(err, "get: failed to decode data %q", key)
|
||||
}
|
||||
|
||||
// List fetches all releases and returns the list releases such
|
||||
// that filter(release) == true. An error is returned if the
|
||||
// secret fails to retrieve the releases.
|
||||
func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
lsel := kblabels.Set{"owner": "helm"}.AsSelector()
|
||||
opts := metav1.ListOptions{LabelSelector: lsel.String()}
|
||||
|
||||
list, err := secrets.impl.List(context.Background(), opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "list: failed to list")
|
||||
}
|
||||
|
||||
var results []*rspb.Release
|
||||
|
||||
// iterate over the secrets object list
|
||||
// and decode each release
|
||||
for _, item := range list.Items {
|
||||
rls, err := decodeRelease(string(item.Data["release"]))
|
||||
if err != nil {
|
||||
secrets.Log("list: failed to decode release: %v: %s", item, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
|
||||
if filter(rls) {
|
||||
results = append(results, rls)
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Query fetches all releases that match the provided map of labels.
|
||||
// An error is returned if the secret fails to retrieve the releases.
|
||||
func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||
ls := kblabels.Set{}
|
||||
for k, v := range labels {
|
||||
if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
|
||||
return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
|
||||
}
|
||||
ls[k] = v
|
||||
}
|
||||
|
||||
opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
|
||||
|
||||
list, err := secrets.impl.List(context.Background(), opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "query: failed to query with labels")
|
||||
}
|
||||
|
||||
if len(list.Items) == 0 {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
var results []*rspb.Release
|
||||
for _, item := range list.Items {
|
||||
rls, err := decodeRelease(string(item.Data["release"]))
|
||||
if err != nil {
|
||||
secrets.Log("query: failed to decode release: %s", err)
|
||||
continue
|
||||
}
|
||||
results = append(results, rls)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Create creates a new Secret holding the release. If the
|
||||
// Secret already exists, ErrReleaseExists is returned.
|
||||
func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
|
||||
// set labels for secrets object meta data
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new secret to hold the release
|
||||
obj, err := newSecretsObject(key, rls, lbs)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create: failed to encode release %q", rls.Name)
|
||||
}
|
||||
// push the secret object out into the kubiverse
|
||||
if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
return ErrReleaseExists
|
||||
}
|
||||
|
||||
return errors.Wrap(err, "create: failed to create")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates the Secret holding the release. If not found
|
||||
// the Secret is created to hold the release.
|
||||
func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
|
||||
// set labels for secrets object meta data
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new secret object to hold the release
|
||||
obj, err := newSecretsObject(key, rls, lbs)
|
||||
if err != nil {
|
||||
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{})
|
||||
return errors.Wrap(err, "update: failed to update")
|
||||
}
|
||||
|
||||
// Delete deletes the Secret holding the release named by key.
|
||||
func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
|
||||
// fetch the release to check existence
|
||||
if rls, err = secrets.Get(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// delete the release
|
||||
err = secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{})
|
||||
return rls, err
|
||||
}
|
||||
|
||||
// newSecretsObject constructs a kubernetes Secret object
|
||||
// to store a release. Each secret data entry is the base64
|
||||
// encoded gzipped string of a release.
|
||||
//
|
||||
// The following labels are used within each secret:
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this secret was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the secret, currently "helm".
|
||||
// "name" - name of the release.
|
||||
//
|
||||
func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) {
|
||||
const owner = "helm"
|
||||
|
||||
// encode the release
|
||||
s, err := encodeRelease(rls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lbs == nil {
|
||||
lbs.init()
|
||||
}
|
||||
|
||||
// apply labels
|
||||
lbs.set("name", rls.Name)
|
||||
lbs.set("owner", owner)
|
||||
lbs.set("status", rls.Info.Status.String())
|
||||
lbs.set("version", strconv.Itoa(rls.Version))
|
||||
|
||||
// create and return secret object.
|
||||
// Helm 3 introduced setting the 'Type' field
|
||||
// in the Kubernetes storage object.
|
||||
// Helm defines the field content as follows:
|
||||
// <helm_domain>/<helm_object>.v<helm_object_version>
|
||||
// Type field for Helm 3: helm.sh/release.v1
|
||||
// Note: Version starts at 'v1' for Helm 3 and
|
||||
// should be incremented if the release object
|
||||
// metadata is modified.
|
||||
// This would potentially be a breaking change
|
||||
// and should only happen between major versions.
|
||||
return &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: key,
|
||||
Labels: lbs.toMap(),
|
||||
},
|
||||
Type: "helm.sh/release.v1",
|
||||
Data: map[string][]byte{"release": []byte(s)},
|
||||
}, nil
|
||||
}
|
||||
496
vendor/helm.sh/helm/v3/pkg/storage/driver/sql.go
vendored
Normal file
496
vendor/helm.sh/helm/v3/pkg/storage/driver/sql.go
vendored
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
// Import pq for postgres dialect
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var _ Driver = (*SQL)(nil)
|
||||
|
||||
var labelMap = map[string]struct{}{
|
||||
"modifiedAt": {},
|
||||
"createdAt": {},
|
||||
"version": {},
|
||||
"status": {},
|
||||
"owner": {},
|
||||
"name": {},
|
||||
}
|
||||
|
||||
const postgreSQLDialect = "postgres"
|
||||
|
||||
// SQLDriverName is the string name of this driver.
|
||||
const SQLDriverName = "SQL"
|
||||
|
||||
const sqlReleaseTableName = "releases_v1"
|
||||
|
||||
const (
|
||||
sqlReleaseTableKeyColumn = "key"
|
||||
sqlReleaseTableTypeColumn = "type"
|
||||
sqlReleaseTableBodyColumn = "body"
|
||||
sqlReleaseTableNameColumn = "name"
|
||||
sqlReleaseTableNamespaceColumn = "namespace"
|
||||
sqlReleaseTableVersionColumn = "version"
|
||||
sqlReleaseTableStatusColumn = "status"
|
||||
sqlReleaseTableOwnerColumn = "owner"
|
||||
sqlReleaseTableCreatedAtColumn = "createdAt"
|
||||
sqlReleaseTableModifiedAtColumn = "modifiedAt"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlReleaseDefaultOwner = "helm"
|
||||
sqlReleaseDefaultType = "helm.sh/release.v1"
|
||||
)
|
||||
|
||||
// SQL is the sql storage driver implementation.
|
||||
type SQL struct {
|
||||
db *sqlx.DB
|
||||
namespace string
|
||||
statementBuilder sq.StatementBuilderType
|
||||
|
||||
Log func(string, ...interface{})
|
||||
}
|
||||
|
||||
// Name returns the name of the driver.
|
||||
func (s *SQL) Name() string {
|
||||
return SQLDriverName
|
||||
}
|
||||
|
||||
func (s *SQL) ensureDBSetup() error {
|
||||
// Populate the database with the relations we need if they don't exist yet
|
||||
migrations := &migrate.MemoryMigrationSource{
|
||||
Migrations: []*migrate.Migration{
|
||||
{
|
||||
Id: "init",
|
||||
Up: []string{
|
||||
fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
%s VARCHAR(67),
|
||||
%s VARCHAR(64) NOT NULL,
|
||||
%s TEXT NOT NULL,
|
||||
%s VARCHAR(64) NOT NULL,
|
||||
%s VARCHAR(64) NOT NULL,
|
||||
%s INTEGER NOT NULL,
|
||||
%s TEXT NOT NULL,
|
||||
%s TEXT NOT NULL,
|
||||
%s INTEGER NOT NULL,
|
||||
%s INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(%s, %s)
|
||||
);
|
||||
CREATE INDEX ON %s (%s, %s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
|
||||
GRANT ALL ON %s TO PUBLIC;
|
||||
|
||||
ALTER TABLE %s ENABLE ROW LEVEL SECURITY;
|
||||
`,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableKeyColumn,
|
||||
sqlReleaseTableTypeColumn,
|
||||
sqlReleaseTableBodyColumn,
|
||||
sqlReleaseTableNameColumn,
|
||||
sqlReleaseTableNamespaceColumn,
|
||||
sqlReleaseTableVersionColumn,
|
||||
sqlReleaseTableStatusColumn,
|
||||
sqlReleaseTableOwnerColumn,
|
||||
sqlReleaseTableCreatedAtColumn,
|
||||
sqlReleaseTableModifiedAtColumn,
|
||||
sqlReleaseTableKeyColumn,
|
||||
sqlReleaseTableNamespaceColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableKeyColumn,
|
||||
sqlReleaseTableNamespaceColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableVersionColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableStatusColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableOwnerColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableCreatedAtColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableModifiedAtColumn,
|
||||
sqlReleaseTableName,
|
||||
sqlReleaseTableName,
|
||||
),
|
||||
},
|
||||
Down: []string{
|
||||
fmt.Sprintf(`
|
||||
DROP TABLE %s;
|
||||
`, sqlReleaseTableName),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := migrate.Exec(s.db.DB, postgreSQLDialect, migrations, migrate.Up)
|
||||
return err
|
||||
}
|
||||
|
||||
// SQLReleaseWrapper describes how Helm releases are stored in an SQL database
|
||||
type SQLReleaseWrapper struct {
|
||||
// The primary key, made of {release-name}.{release-version}
|
||||
Key string `db:"key"`
|
||||
|
||||
// See https://github.com/helm/helm/blob/c9fe3d118caec699eb2565df9838673af379ce12/pkg/storage/driver/secrets.go#L231
|
||||
Type string `db:"type"`
|
||||
|
||||
// The rspb.Release body, as a base64-encoded string
|
||||
Body string `db:"body"`
|
||||
|
||||
// Release "labels" that can be used as filters in the storage.Query(labels map[string]string)
|
||||
// we implemented. Note that allowing Helm users to filter against new dimensions will require a
|
||||
// new migration to be added, and the Create and/or update functions to be updated accordingly.
|
||||
Name string `db:"name"`
|
||||
Namespace string `db:"namespace"`
|
||||
Version int `db:"version"`
|
||||
Status string `db:"status"`
|
||||
Owner string `db:"owner"`
|
||||
CreatedAt int `db:"createdAt"`
|
||||
ModifiedAt int `db:"modifiedAt"`
|
||||
}
|
||||
|
||||
// NewSQL initializes a new sql driver.
|
||||
func NewSQL(connectionString string, logger func(string, ...interface{}), namespace string) (*SQL, error) {
|
||||
db, err := sqlx.Connect(postgreSQLDialect, connectionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver := &SQL{
|
||||
db: db,
|
||||
Log: logger,
|
||||
statementBuilder: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
|
||||
}
|
||||
|
||||
if err := driver.ensureDBSetup(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver.namespace = namespace
|
||||
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
// Get returns the release named by key.
|
||||
func (s *SQL) Get(key string) (*rspb.Release, error) {
|
||||
var record SQLReleaseWrapper
|
||||
|
||||
qb := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
||||
|
||||
query, args, err := qb.ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get will return an error if the result is empty
|
||||
if err := s.db.Get(&record, query, args...); err != nil {
|
||||
s.Log("got SQL error when getting release %s: %v", key, err)
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
release, err := decodeRelease(record.Body)
|
||||
if err != nil {
|
||||
s.Log("get: failed to decode data %q: %v", key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
// List returns the list of all releases such that filter(release) == true
|
||||
func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
sb := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableOwnerColumn: sqlReleaseDefaultOwner})
|
||||
|
||||
// If a namespace was specified, we only list releases from that namespace
|
||||
if s.namespace != "" {
|
||||
sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
||||
}
|
||||
|
||||
query, args, err := sb.ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records = []SQLReleaseWrapper{}
|
||||
if err := s.db.Select(&records, query, args...); err != nil {
|
||||
s.Log("list: failed to list: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var releases []*rspb.Release
|
||||
for _, record := range records {
|
||||
release, err := decodeRelease(record.Body)
|
||||
if err != nil {
|
||||
s.Log("list: failed to decode release: %v: %v", record, err)
|
||||
continue
|
||||
}
|
||||
if filter(release) {
|
||||
releases = append(releases, release)
|
||||
}
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
// Query returns the set of releases that match the provided set of labels.
|
||||
func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||
sb := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName)
|
||||
|
||||
keys := make([]string, 0, len(labels))
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
if _, ok := labelMap[key]; ok {
|
||||
sb = sb.Where(sq.Eq{key: labels[key]})
|
||||
} else {
|
||||
s.Log("unknown label %s", key)
|
||||
return nil, fmt.Errorf("unknown label %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
// If a namespace was specified, we only list releases from that namespace
|
||||
if s.namespace != "" {
|
||||
sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
|
||||
}
|
||||
|
||||
// Build our query
|
||||
query, args, err := sb.ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records = []SQLReleaseWrapper{}
|
||||
if err := s.db.Select(&records, query, args...); err != nil {
|
||||
s.Log("list: failed to query with labels: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
var releases []*rspb.Release
|
||||
for _, record := range records {
|
||||
release, err := decodeRelease(record.Body)
|
||||
if err != nil {
|
||||
s.Log("list: failed to decode release: %v: %v", record, err)
|
||||
continue
|
||||
}
|
||||
releases = append(releases, release)
|
||||
}
|
||||
|
||||
if len(releases) == 0 {
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
// Create creates a new release.
|
||||
func (s *SQL) Create(key string, rls *rspb.Release) error {
|
||||
namespace := rls.Namespace
|
||||
if namespace == "" {
|
||||
namespace = defaultNamespace
|
||||
}
|
||||
s.namespace = namespace
|
||||
|
||||
body, err := encodeRelease(rls)
|
||||
if err != nil {
|
||||
s.Log("failed to encode release: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
transaction, err := s.db.Beginx()
|
||||
if err != nil {
|
||||
s.Log("failed to start SQL transaction: %v", err)
|
||||
return fmt.Errorf("error beginning transaction: %v", err)
|
||||
}
|
||||
|
||||
insertQuery, args, err := s.statementBuilder.
|
||||
Insert(sqlReleaseTableName).
|
||||
Columns(
|
||||
sqlReleaseTableKeyColumn,
|
||||
sqlReleaseTableTypeColumn,
|
||||
sqlReleaseTableBodyColumn,
|
||||
sqlReleaseTableNameColumn,
|
||||
sqlReleaseTableNamespaceColumn,
|
||||
sqlReleaseTableVersionColumn,
|
||||
sqlReleaseTableStatusColumn,
|
||||
sqlReleaseTableOwnerColumn,
|
||||
sqlReleaseTableCreatedAtColumn,
|
||||
).
|
||||
Values(
|
||||
key,
|
||||
sqlReleaseDefaultType,
|
||||
body,
|
||||
rls.Name,
|
||||
namespace,
|
||||
int(rls.Version),
|
||||
rls.Info.Status.String(),
|
||||
sqlReleaseDefaultOwner,
|
||||
int(time.Now().Unix()),
|
||||
).ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build insert query: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := transaction.Exec(insertQuery, args...); err != nil {
|
||||
defer transaction.Rollback()
|
||||
|
||||
selectQuery, args, buildErr := s.statementBuilder.
|
||||
Select(sqlReleaseTableKeyColumn).
|
||||
From(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
if buildErr != nil {
|
||||
s.Log("failed to build select query: %v", buildErr)
|
||||
return err
|
||||
}
|
||||
|
||||
var record SQLReleaseWrapper
|
||||
if err := transaction.Get(&record, selectQuery, args...); err == nil {
|
||||
s.Log("release %s already exists", key)
|
||||
return ErrReleaseExists
|
||||
}
|
||||
|
||||
s.Log("failed to store release %s in SQL database: %v", key, err)
|
||||
return err
|
||||
}
|
||||
defer transaction.Commit()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates a release.
|
||||
func (s *SQL) Update(key string, rls *rspb.Release) error {
|
||||
namespace := rls.Namespace
|
||||
if namespace == "" {
|
||||
namespace = defaultNamespace
|
||||
}
|
||||
s.namespace = namespace
|
||||
|
||||
body, err := encodeRelease(rls)
|
||||
if err != nil {
|
||||
s.Log("failed to encode release: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
query, args, err := s.statementBuilder.
|
||||
Update(sqlReleaseTableName).
|
||||
Set(sqlReleaseTableBodyColumn, body).
|
||||
Set(sqlReleaseTableNameColumn, rls.Name).
|
||||
Set(sqlReleaseTableVersionColumn, int(rls.Version)).
|
||||
Set(sqlReleaseTableStatusColumn, rls.Info.Status.String()).
|
||||
Set(sqlReleaseTableOwnerColumn, sqlReleaseDefaultOwner).
|
||||
Set(sqlReleaseTableModifiedAtColumn, int(time.Now().Unix())).
|
||||
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: namespace}).
|
||||
ToSql()
|
||||
|
||||
if err != nil {
|
||||
s.Log("failed to build update query: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := s.db.Exec(query, args...); err != nil {
|
||||
s.Log("failed to update release %s in SQL database: %v", key, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a release or returns ErrReleaseNotFound.
|
||||
func (s *SQL) Delete(key string) (*rspb.Release, error) {
|
||||
transaction, err := s.db.Beginx()
|
||||
if err != nil {
|
||||
s.Log("failed to start SQL transaction: %v", err)
|
||||
return nil, fmt.Errorf("error beginning transaction: %v", err)
|
||||
}
|
||||
|
||||
selectQuery, args, err := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build select query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var record SQLReleaseWrapper
|
||||
err = transaction.Get(&record, selectQuery, args...)
|
||||
if err != nil {
|
||||
s.Log("release %s not found: %v", key, err)
|
||||
return nil, ErrReleaseNotFound
|
||||
}
|
||||
|
||||
release, err := decodeRelease(record.Body)
|
||||
if err != nil {
|
||||
s.Log("failed to decode release %s: %v", key, err)
|
||||
transaction.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
defer transaction.Commit()
|
||||
|
||||
deleteQuery, args, err := s.statementBuilder.
|
||||
Delete(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableKeyColumn: key}).
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build select query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = transaction.Exec(deleteQuery, args...)
|
||||
return release, err
|
||||
}
|
||||
85
vendor/helm.sh/helm/v3/pkg/storage/driver/util.go
vendored
Normal file
85
vendor/helm.sh/helm/v3/pkg/storage/driver/util.go
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
var magicGzip = []byte{0x1f, 0x8b, 0x08}
|
||||
|
||||
// encodeRelease encodes a release returning a base64 encoded
|
||||
// gzipped string representation, or error.
|
||||
func encodeRelease(rls *rspb.Release) (string, error) {
|
||||
b, err := json.Marshal(rls)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err = w.Write(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
w.Close()
|
||||
|
||||
return b64.EncodeToString(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
// decodeRelease decodes the bytes of data into a release
|
||||
// type. Data must contain a base64 encoded gzipped string of a
|
||||
// valid release, otherwise an error is returned.
|
||||
func decodeRelease(data string) (*rspb.Release, error) {
|
||||
// base64 decode string
|
||||
b, err := b64.DecodeString(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For backwards compatibility with releases that were stored before
|
||||
// compression was introduced we skip decompression if the
|
||||
// gzip magic header is not found
|
||||
if bytes.Equal(b[0:3], magicGzip) {
|
||||
r, err := gzip.NewReader(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
b2, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = b2
|
||||
}
|
||||
|
||||
var rls rspb.Release
|
||||
// unmarshal release object bytes
|
||||
if err := json.Unmarshal(b, &rls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rls, nil
|
||||
}
|
||||
Reference in New Issue
Block a user