mirror of https://github.com/helm/helm
Signed-off-by: Julien Breux <julienbreux@google.com>pull/10922/head
parent
49819b4ef7
commit
5900066f7b
@ -0,0 +1,409 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/storage"
|
||||||
|
"google.golang.org/api/googleapi"
|
||||||
|
"google.golang.org/api/iterator"
|
||||||
|
rspb "helm.sh/helm/v3/pkg/release"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GCSDriverName is the string name of this driver.
|
||||||
|
GCSDriverName = "GCS"
|
||||||
|
|
||||||
|
gcsReleaseNameMetadata = "name"
|
||||||
|
gcsReleaseNamespaceMetadata = "namespace"
|
||||||
|
gcsReleaseVersionMetadata = "version"
|
||||||
|
gcsReleaseStatusMetadata = "status"
|
||||||
|
gcsReleaseOwnerColumn = "owner"
|
||||||
|
gcsReleaseCreatedAtMetadata = "createdAt"
|
||||||
|
gcsReleaseModifiedAtMetadata = "modifiedAt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GCS is the GCS storage driver implementation.
|
||||||
|
type GCS struct {
|
||||||
|
client *storage.Client
|
||||||
|
|
||||||
|
bucket string
|
||||||
|
pathPrefix string
|
||||||
|
|
||||||
|
namespace string
|
||||||
|
|
||||||
|
Log func(string, ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGCS initializes a new GCS driver.
|
||||||
|
func NewGCS(bucket, pathPrefix, namespace string, logger func(string, ...interface{})) (*GCS, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client, err := storage.NewClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
driver := &GCS{
|
||||||
|
client: client,
|
||||||
|
|
||||||
|
bucket: bucket,
|
||||||
|
pathPrefix: pathPrefix,
|
||||||
|
namespace: namespace,
|
||||||
|
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the driver.
|
||||||
|
func (s *GCS) Name() string {
|
||||||
|
return GCSDriverName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the release named by key or returns ErrReleaseNotFound.
|
||||||
|
func (s *GCS) Get(key string) (*rspb.Release, error) {
|
||||||
|
rel, _, err := s.readRelease(s.fullPathName(key, s.namespace), false)
|
||||||
|
return rel, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns the list of all releases such that filter(release) == true
|
||||||
|
func (s *GCS) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||||
|
namespaces, err := s.listNamespaces()
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to list: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []*rspb.Release
|
||||||
|
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
releases, err := s.listReleases(namespace)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to list releases in namespace: %v", namespace, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range releases {
|
||||||
|
release, _, err := s.readRelease(key, false)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to read release: %v: %v", release, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filter(release) {
|
||||||
|
list = append(list, release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the set of releases that match the provided set of labels
|
||||||
|
func (s *GCS) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||||
|
// List namespaces
|
||||||
|
namespaces, err := s.listNamespaces()
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to list: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create filter to compare labels and metadata
|
||||||
|
filter := func(metadata map[string]string) bool {
|
||||||
|
for key, val := range labels {
|
||||||
|
if metadataVal, ok := metadata[key]; !ok || metadataVal != val {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the releases
|
||||||
|
var list []*rspb.Release
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
releases, err := s.listReleases(namespace)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to list releases in namespace: %v", namespace, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range releases {
|
||||||
|
release, metadata, err := s.readRelease(key, true)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("list: failed to read release: %v: %v", release, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filter(metadata) {
|
||||||
|
list = append(list, release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
return nil, ErrReleaseNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new release.
|
||||||
|
func (s *GCS) Create(key string, rls *rspb.Release) error {
|
||||||
|
release, err := encodeRelease(rls)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("failed to encode release: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
obj := s.client.Bucket(s.bucket).
|
||||||
|
Object(s.fullPathName(key, rls.Namespace)).
|
||||||
|
If(storage.Conditions{DoesNotExist: true}).
|
||||||
|
NewWriter(ctx)
|
||||||
|
obj.Metadata = s.metadata(rls, true)
|
||||||
|
|
||||||
|
if _, err := obj.Write([]byte(release)); err != nil {
|
||||||
|
s.Log("failed to write object: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obj.Close(); err != nil {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case *googleapi.Error:
|
||||||
|
if e.Code == http.StatusPreconditionFailed {
|
||||||
|
s.Log("release %s already exists", key)
|
||||||
|
return ErrReleaseExists
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s.Log("failed to close bucket: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a release.
|
||||||
|
func (s *GCS) Update(key string, rls *rspb.Release) error {
|
||||||
|
release, err := encodeRelease(rls)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("failed to encode release: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
obj := s.client.Bucket(s.bucket).Object(s.fullPathName(key, rls.Namespace)).NewWriter(ctx)
|
||||||
|
obj.Metadata = s.metadata(rls, false)
|
||||||
|
if _, err := obj.Write([]byte(release)); err != nil {
|
||||||
|
s.Log("failed to write object: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obj.Close(); err != nil {
|
||||||
|
s.Log("failed to close object: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a release or returns ErrReleaseNotFound.
|
||||||
|
func (s *GCS) Delete(key string) (*rspb.Release, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
obj := s.client.Bucket(s.bucket).Object(s.fullPathName(key, s.namespace))
|
||||||
|
objRdr, err := obj.NewReader(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, storage.ErrObjectNotExist) {
|
||||||
|
err = ErrReleaseNotFound
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := ioutil.ReadAll(objRdr)
|
||||||
|
objRdr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := decodeRelease(string(record))
|
||||||
|
if err != nil {
|
||||||
|
s.Log("get: failed to decode data %q: %v", key, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.Delete(ctx); err != nil {
|
||||||
|
s.Log("failed to delete object: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return release, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePrefixedReleases deletes all prefixed releases
|
||||||
|
func (s *GCS) DeletePrefixedReleases() error {
|
||||||
|
query := storage.Query{
|
||||||
|
Prefix: fmt.Sprintf("%s/", s.pathPrefix),
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
objs := s.client.Bucket(s.bucket).Objects(ctx, &query)
|
||||||
|
for {
|
||||||
|
objAttrs, err := objs.Next()
|
||||||
|
if err == iterator.Done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.Log("unable to delete objects", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
obj := s.client.Bucket(s.bucket).Object(objAttrs.Name)
|
||||||
|
if obj.Delete(ctx); err != nil {
|
||||||
|
s.Log("failed to delete object: (%s) %v", objAttrs.Name, err)
|
||||||
|
}
|
||||||
|
s.Log("object deleted successfully: (%s)", objAttrs.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GCS) fullPathName(name, namespace string) string {
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = defaultNamespace
|
||||||
|
}
|
||||||
|
return strings.TrimLeft(
|
||||||
|
fmt.Sprintf("%s/%s/%s", s.pathPrefix, namespace, name),
|
||||||
|
"/",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GCS) metadata(rls *rspb.Release, isCreation bool) map[string]string {
|
||||||
|
ts := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
|
||||||
|
md := map[string]string{
|
||||||
|
gcsReleaseNameMetadata: rls.Name,
|
||||||
|
gcsReleaseNamespaceMetadata: rls.Namespace,
|
||||||
|
gcsReleaseStatusMetadata: rls.Info.Status.String(),
|
||||||
|
gcsReleaseVersionMetadata: strconv.Itoa(rls.Version),
|
||||||
|
gcsReleaseOwnerColumn: "helm",
|
||||||
|
gcsReleaseModifiedAtMetadata: ts,
|
||||||
|
}
|
||||||
|
if isCreation {
|
||||||
|
md[gcsReleaseCreatedAtMetadata] = ts
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GCS) readRelease(key string, withMetadata bool) (*rspb.Release, map[string]string, error) {
|
||||||
|
metadata := make(map[string]string)
|
||||||
|
ctx := context.Background()
|
||||||
|
objHandle := s.client.Bucket(s.bucket).Object(key)
|
||||||
|
obj, err := objHandle.NewReader(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, storage.ErrObjectNotExist) {
|
||||||
|
err = ErrReleaseNotFound
|
||||||
|
}
|
||||||
|
return nil, metadata, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := ioutil.ReadAll(obj)
|
||||||
|
obj.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, err
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := decodeRelease(string(record))
|
||||||
|
if err != nil {
|
||||||
|
s.Log("read release: failed to decode data %q: %v", key, err)
|
||||||
|
return nil, metadata, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if withMetadata {
|
||||||
|
objAttrs, err := objHandle.Attrs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
s.Log("read release: failed to read metadata %q: %v", key, err)
|
||||||
|
return nil, metadata, err
|
||||||
|
}
|
||||||
|
metadata = objAttrs.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
return release, metadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GCS) listNamespaces() ([]string, error) {
|
||||||
|
if s.namespace != "" {
|
||||||
|
return []string{s.namespace}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
objectsName := make(map[string]string)
|
||||||
|
|
||||||
|
prefix := fmt.Sprintf("%s/", strings.TrimRight(s.pathPrefix, "/"))
|
||||||
|
query := storage.Query{
|
||||||
|
StartOffset: prefix,
|
||||||
|
Prefix: prefix,
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
objs := s.client.Bucket(s.bucket).Objects(ctx, &query)
|
||||||
|
for {
|
||||||
|
objAttrs, err := objs.Next()
|
||||||
|
if err == iterator.Done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.Log("unable to list objects", err)
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
objectName, _, _ := strings.Cut(strings.TrimPrefix(objAttrs.Name, prefix), "/")
|
||||||
|
objectsName[objectName] = objectName
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces := []string{}
|
||||||
|
for objectName := range objectsName {
|
||||||
|
namespaces = append(namespaces, objectName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GCS) listReleases(namespace string) ([]string, error) {
|
||||||
|
releases := []string{}
|
||||||
|
|
||||||
|
query := storage.Query{
|
||||||
|
Prefix: strings.TrimRight(fmt.Sprintf("%s/%s", s.pathPrefix, namespace), "/"),
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
objs := s.client.Bucket(s.bucket).Objects(ctx, &query)
|
||||||
|
for {
|
||||||
|
objAttrs, err := objs.Next()
|
||||||
|
if err == iterator.Done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.Log("unable to list objects", err)
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
releases = append(releases, objAttrs.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return releases, nil
|
||||||
|
}
|
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
rspb "helm.sh/helm/v3/pkg/release"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prefixes = []string{"helm-releases", "helm-releases-list"}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
removeReleases()
|
||||||
|
|
||||||
|
retCode := m.Run()
|
||||||
|
|
||||||
|
// removeReleases()
|
||||||
|
|
||||||
|
os.Exit(retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeReleases() {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
gcsDriver := newTestFixtureGCS(prefix, "default")
|
||||||
|
if err := gcsDriver.DeletePrefixedReleases(); err != nil {
|
||||||
|
fmt.Printf("Expected error on setup tests: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSName(t *testing.T) {
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
if gcsDriver.Name() != GCSDriverName {
|
||||||
|
t.Errorf("Expected name to be %s, got %s", GCSDriverName, gcsDriver.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSGet(t *testing.T) {
|
||||||
|
vers := int(1)
|
||||||
|
name := "gcs-test-get"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
rel := releaseStub(name, vers, "default", rspb.StatusDeployed)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
// create stub
|
||||||
|
if err := gcsDriver.Create(key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test get release
|
||||||
|
got, err := gcsDriver.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get release: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(rel, got) {
|
||||||
|
t.Errorf("Expected release {%v}, got {%v}", rel, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSGetNotExist(t *testing.T) {
|
||||||
|
vers := int(1)
|
||||||
|
name := "gcs-test-get-not-exists"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
got, err := gcsDriver.Get(key)
|
||||||
|
if err == nil || got != nil {
|
||||||
|
t.Fatal("Release must be not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSCreate(t *testing.T) {
|
||||||
|
vers := 1
|
||||||
|
name := "gcs-test"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
rel := releaseStub(name, vers, "default", rspb.StatusDeployed)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
if err := gcsDriver.Create(key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSUpdate(t *testing.T) {
|
||||||
|
vers := 1
|
||||||
|
name := "gcs-test-update"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
rel := releaseStub(name, vers, "default", rspb.StatusDeployed)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
// create stub
|
||||||
|
if err := gcsDriver.Create(key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test update release
|
||||||
|
if err := gcsDriver.Update(key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to update release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSDelete(t *testing.T) {
|
||||||
|
vers := 1
|
||||||
|
name := "gcs-test-delete"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
rel := releaseStub(name, vers, "default", rspb.StatusDeployed)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
// create stub
|
||||||
|
if err := gcsDriver.Create(key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test delete release
|
||||||
|
deletedRelease, err := gcsDriver.Delete(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to delete release with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(rel, deletedRelease) {
|
||||||
|
t.Errorf("Expected release {%v}, got {%v}", rel, deletedRelease)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSDeleteNotFound(t *testing.T) {
|
||||||
|
vers := 1
|
||||||
|
name := "gcs-test-delete-not-found"
|
||||||
|
key := testKey(name, vers)
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases", "default")
|
||||||
|
|
||||||
|
if _, err := gcsDriver.Delete(key); err == nil {
|
||||||
|
t.Fatalf("release found with key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSList(t *testing.T) {
|
||||||
|
namespaceA := "list-a"
|
||||||
|
namespaceB := "list-b"
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
key string
|
||||||
|
namespace string
|
||||||
|
status rspb.Status
|
||||||
|
}{
|
||||||
|
{"gcs-test-list-key-1", namespaceA, rspb.StatusUninstalled},
|
||||||
|
{"gcs-test-list-key-2", namespaceA, rspb.StatusUninstalled},
|
||||||
|
{"gcs-test-list-key-3", namespaceA, rspb.StatusDeployed},
|
||||||
|
{"gcs-test-list-key-4", namespaceB, rspb.StatusDeployed},
|
||||||
|
{"gcs-test-list-key-5", namespaceB, rspb.StatusSuperseded},
|
||||||
|
{"gcs-test-list-key-6", namespaceB, rspb.StatusSuperseded},
|
||||||
|
}
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases-list", "")
|
||||||
|
|
||||||
|
// create stubs
|
||||||
|
for _, tt := range tests {
|
||||||
|
rel := releaseStub(tt.key, 1, tt.namespace, tt.status)
|
||||||
|
if err := gcsDriver.Create(tt.key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", tt.key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all deleted releases
|
||||||
|
del, err := gcsDriver.List(func(rel *rspb.Release) bool {
|
||||||
|
return rel.Info.Status == rspb.StatusUninstalled
|
||||||
|
})
|
||||||
|
// check
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to list deleted: %v", err)
|
||||||
|
}
|
||||||
|
if len(del) != 2 {
|
||||||
|
t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all deployed releases
|
||||||
|
dpl, err := gcsDriver.List(func(rel *rspb.Release) bool {
|
||||||
|
return rel.Info.Status == rspb.StatusDeployed
|
||||||
|
})
|
||||||
|
// check
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to list deployed: %v", err)
|
||||||
|
}
|
||||||
|
if len(dpl) != 2 {
|
||||||
|
t.Errorf("Expected 2 deployed, got %d:\n%+v\n", len(dpl), dpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list all superseded releases
|
||||||
|
ssd, err := gcsDriver.List(func(rel *rspb.Release) bool {
|
||||||
|
return rel.Info.Status == rspb.StatusSuperseded
|
||||||
|
})
|
||||||
|
// check
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to list superseded: %v", err)
|
||||||
|
}
|
||||||
|
if len(ssd) != 2 {
|
||||||
|
t.Errorf("Expected 2 superseded, got %d:\n%v\n", len(ssd), ssd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGCSQuery(t *testing.T) {
|
||||||
|
namespace := "default"
|
||||||
|
tests := []struct {
|
||||||
|
key string
|
||||||
|
namespace string
|
||||||
|
status rspb.Status
|
||||||
|
}{
|
||||||
|
{"gcs-test-list-key-1", namespace, rspb.StatusUninstalled},
|
||||||
|
{"gcs-test-list-key-2", namespace, rspb.StatusUninstalled},
|
||||||
|
{"gcs-test-list-key-3", namespace, rspb.StatusDeployed},
|
||||||
|
{"gcs-test-list-key-4", namespace, rspb.StatusDeployed},
|
||||||
|
{"gcs-test-list-key-5", namespace, rspb.StatusSuperseded},
|
||||||
|
{"gcs-test-list-key-6", namespace, rspb.StatusSuperseded},
|
||||||
|
}
|
||||||
|
|
||||||
|
gcsDriver := newTestFixtureGCS("helm-releases-query", "")
|
||||||
|
|
||||||
|
// create stubs
|
||||||
|
for _, tt := range tests {
|
||||||
|
rel := releaseStub(tt.key, 1, tt.namespace, tt.status)
|
||||||
|
if err := gcsDriver.Create(tt.key, rel); err != nil {
|
||||||
|
t.Fatalf("failed to create release with key %s: %v", tt.key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rls, err := gcsDriver.Query(map[string]string{"status": "deployed"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to query: %s", err)
|
||||||
|
}
|
||||||
|
if len(rls) != 2 {
|
||||||
|
t.Fatalf("Expected 2 results, actual %d", len(rls))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = gcsDriver.Query(map[string]string{"name": "notExist"})
|
||||||
|
if err != ErrReleaseNotFound {
|
||||||
|
t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue