feat(rollback-storage): gofmt, added missing license headers, and canconical import paths

reviewable/pr1155/r1
fibonacci1729 9 years ago
parent de5365ec5c
commit e25732284b

@ -17,20 +17,20 @@ limitations under the License.
package driver // import "k8s.io/helm/pkg/storage/driver" package driver // import "k8s.io/helm/pkg/storage/driver"
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"log" "log"
"strconv" "strconv"
"time" "time"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
kberrs "k8s.io/kubernetes/pkg/api/errors" kberrs "k8s.io/kubernetes/pkg/api/errors"
client "k8s.io/kubernetes/pkg/client/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned"
kblabels "k8s.io/kubernetes/pkg/labels" kblabels "k8s.io/kubernetes/pkg/labels"
) )
var _ Driver = (*ConfigMaps)(nil) var _ Driver = (*ConfigMaps)(nil)
@ -43,169 +43,169 @@ var b64 = base64.StdEncoding
// ConfigMaps is a wrapper around an implementation of a kubernetes // ConfigMaps is a wrapper around an implementation of a kubernetes
// ConfigMapsInterface. // ConfigMapsInterface.
type ConfigMaps struct { type ConfigMaps struct {
impl client.ConfigMapsInterface impl client.ConfigMapsInterface
} }
// NewConfigMaps initializes a new ConfigMaps wrapping an implmenetation of // NewConfigMaps initializes a new ConfigMaps wrapping an implmenetation of
// the kubernetes ConfigMapsInterface. // the kubernetes ConfigMapsInterface.
func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps { func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps {
return &ConfigMaps{impl: impl} return &ConfigMaps{impl: impl}
} }
// Name returns the name of the driver. // Name returns the name of the driver.
func (cfgmaps *ConfigMaps) Name() string { func (cfgmaps *ConfigMaps) Name() string {
return ConfigMapsDriverName return ConfigMapsDriverName
} }
// 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 (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
// fetch the configmap holding the release named by key // fetch the configmap holding the release named by key
obj, err := cfgmaps.impl.Get(key) obj, err := cfgmaps.impl.Get(key)
if err != nil { if err != nil {
if kberrs.IsNotFound(err) { if kberrs.IsNotFound(err) {
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
} }
logerrf(err, "get: failed to get %q", key) logerrf(err, "get: failed to get %q", key)
return nil, err return nil, err
} }
// found the configmap, decode the base64 data string // found the configmap, decode the base64 data string
r, err := decodeRelease(obj.Data["release"]) r, err := decodeRelease(obj.Data["release"])
if err != nil { if err != nil {
logerrf(err, "get: failed to decode data %q", key) logerrf(err, "get: failed to decode data %q", key)
return nil, err return nil, err
} }
// return the release object // return the release object
return r, nil return r, nil
} }
// List fetches all releases and returns the list releases such // List fetches all releases and returns the list releases such
// that filter(release) == true. An error is returned if the // that filter(release) == true. An error is returned if the
// configmap fails to retrieve the releases. // configmap fails to retrieve the releases.
func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
list, err := cfgmaps.impl.List(api.ListOptions{}) list, err := cfgmaps.impl.List(api.ListOptions{})
if err != nil { if err != nil {
logerrf(err, "list: failed to list") logerrf(err, "list: failed to list")
return nil, err return nil, err
} }
var results []*rspb.Release var results []*rspb.Release
// iterate over the configmaps object list // iterate over the configmaps object list
// and decode each release // and decode each release
for _, item := range list.Items { for _, item := range list.Items {
rls, err := decodeRelease(item.Data["release"]) rls, err := decodeRelease(item.Data["release"])
if err != nil { if err != nil {
logerrf(err, "list: failed to decode release: %s", rls) logerrf(err, "list: failed to decode release: %s", rls)
continue continue
} }
if filter(rls) { if filter(rls) {
results = append(results, rls) results = append(results, rls)
} }
} }
return results, nil return results, nil
} }
// Query fetches all releases that match the provided map of labels. // Query fetches all releases that match the provided map of labels.
// An error is returned if the configmap fails to retrieve the releases. // An error is returned if the configmap fails to retrieve the releases.
func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) { func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) {
ls := kblabels.Set{} ls := kblabels.Set{}
for k, v := range labels { for k, v := range labels {
ls[k] = v ls[k] = v
} }
opts := api.ListOptions{LabelSelector: ls.AsSelector()} opts := api.ListOptions{LabelSelector: ls.AsSelector()}
list, err := cfgmaps.impl.List(opts) list, err := cfgmaps.impl.List(opts)
if err != nil { if err != nil {
logerrf(err, "query: failed to query with labels") logerrf(err, "query: failed to query with labels")
return nil, err return nil, err
} }
if len(list.Items) == 0 { if len(list.Items) == 0 {
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
} }
var results []*rspb.Release var results []*rspb.Release
for _, item := range list.Items { for _, item := range list.Items {
rls, err := decodeRelease(item.Data["release"]) rls, err := decodeRelease(item.Data["release"])
if err != nil { if err != nil {
logerrf(err, "query: failed to decode release: %s", err) logerrf(err, "query: failed to decode release: %s", err)
continue continue
} }
results = append(results, rls) results = append(results, rls)
} }
return results, nil return results, nil
} }
// Create creates a new ConfigMap holding the release. If the // Create creates a new ConfigMap holding the release. If the
// ConfigMap already exists, ErrReleaseExists is returned. // ConfigMap already exists, ErrReleaseExists is returned.
func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
// set labels for configmaps object meta data // set labels for configmaps object meta data
var lbs labels var lbs labels
lbs.init() lbs.init()
lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix()))) lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new configmap to hold the release // create a new configmap to hold the release
obj, err := newConfigMapsObject(key, rls, lbs) obj, err := newConfigMapsObject(key, rls, lbs)
if err != nil { if err != nil {
logerrf(err, "create: failed to encode release %q", rls.Name) logerrf(err, "create: failed to encode release %q", rls.Name)
return err return err
} }
// push the configmap object out into the kubiverse // push the configmap object out into the kubiverse
if _, err := cfgmaps.impl.Create(obj); err != nil { if _, err := cfgmaps.impl.Create(obj); err != nil {
if kberrs.IsAlreadyExists(err) { if kberrs.IsAlreadyExists(err) {
return ErrReleaseExists return ErrReleaseExists
} }
logerrf(err, "create: failed to create") logerrf(err, "create: failed to create")
return err return err
} }
return nil return nil
} }
// Update updates the ConfigMap holding the release. If not found // Update updates the ConfigMap holding the release. If not found
// the ConfigMap is created to hold the release. // the ConfigMap is created to hold the release.
func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error { func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
// set labels for configmaps object meta data // set labels for configmaps object meta data
var lbs labels var lbs labels
lbs.init() lbs.init()
lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix()))) lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new configmap object to hold the release // create a new configmap object to hold the release
obj, err := newConfigMapsObject(key, rls, lbs) obj, err := newConfigMapsObject(key, rls, lbs)
if err != nil { if err != nil {
logerrf(err, "update: failed to encode release %q", rls.Name) logerrf(err, "update: failed to encode release %q", rls.Name)
return err return err
} }
// push the configmap object out into the kubiverse // push the configmap object out into the kubiverse
_, err = cfgmaps.impl.Update(obj) _, err = cfgmaps.impl.Update(obj)
if err != nil { if err != nil {
logerrf(err, "update: failed to update") logerrf(err, "update: failed to update")
return err return err
} }
return nil return nil
} }
// Delete deletes the ConfigMap holding the release named by key. // Delete deletes the ConfigMap holding the release named by key.
func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
// fetch the release to check existence // fetch the release to check existence
if rls, err = cfgmaps.Get(key); err != nil { if rls, err = cfgmaps.Get(key); err != nil {
if kberrs.IsNotFound(err) { if kberrs.IsNotFound(err) {
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
} }
logerrf(err, "delete: failed to get release %q", rls.Name) logerrf(err, "delete: failed to get release %q", rls.Name)
return nil, err return nil, err
} }
// delete the release // delete the release
if err = cfgmaps.impl.Delete(key); err != nil { if err = cfgmaps.impl.Delete(key); err != nil {
return rls, err return rls, err
} }
return rls, nil return rls, nil
} }
// newConfigMapsObject constructs a kubernetes ConfigMap object // newConfigMapsObject constructs a kubernetes ConfigMap object
@ -222,42 +222,42 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
// "NAME" - name of the release. // "NAME" - name of the release.
// //
func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*api.ConfigMap, error) { func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*api.ConfigMap, error) {
const owner = "TILLER" const owner = "TILLER"
// encode the release // encode the release
s, err := encodeRelease(rls) s, err := encodeRelease(rls)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if lbs == nil { if lbs == nil {
lbs.init() lbs.init()
} }
// 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", rspb.Status_Code_name[int32(rls.Info.Status.Code)]) lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)])
lbs.set("VERSION", strconv.Itoa(int(rls.Version))) lbs.set("VERSION", strconv.Itoa(int(rls.Version)))
// create and return configmap object // create and return configmap object
return &api.ConfigMap{ return &api.ConfigMap{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: key, Name: key,
Labels: lbs.toMap(), Labels: lbs.toMap(),
}, },
Data: map[string]string{"release": s}, Data: map[string]string{"release": s},
}, nil }, nil
} }
// encodeRelease encodes a release returning a base64 encoded // encodeRelease encodes a release returning a base64 encoded
// binary protobuf encoding representation, or error. // binary protobuf encoding representation, or error.
func encodeRelease(rls *rspb.Release) (string, error) { func encodeRelease(rls *rspb.Release) (string, error) {
b, err := proto.Marshal(rls) b, err := proto.Marshal(rls)
if err != nil { if err != nil {
return "", err return "", err
} }
return b64.EncodeToString(b), nil return b64.EncodeToString(b), nil
} }
// decodeRelease decodes the bytes in data into a release // decodeRelease decodes the bytes in data into a release
@ -265,21 +265,21 @@ func encodeRelease(rls *rspb.Release) (string, error) {
// valid protobuf encoding of a release, otherwise // valid protobuf encoding of a release, otherwise
// an error is returned. // an error is returned.
func decodeRelease(data string) (*rspb.Release, error) { func decodeRelease(data string) (*rspb.Release, error) {
// base64 decode string // base64 decode string
b, err := b64.DecodeString(data) b, err := b64.DecodeString(data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var rls rspb.Release var rls rspb.Release
// unmarshal protobuf bytes // unmarshal protobuf bytes
if err := proto.Unmarshal(b, &rls); err != nil { if err := proto.Unmarshal(b, &rls); err != nil {
return nil, err return nil, err
} }
return &rls, nil return &rls, nil
} }
// logerrf wraps an error with the a formatted string (used for debugging) // logerrf wraps an error with the a formatted string (used for debugging)
func logerrf(err error, format string, args ...interface{}) { func logerrf(err error, format string, args ...interface{}) {
log.Printf("configmaps: %s: %s\n", fmt.Sprintf(format, args...), err) log.Printf("configmaps: %s: %s\n", fmt.Sprintf(format, args...), err)
} }

@ -14,134 +14,134 @@ limitations under the License.
package driver package driver
import ( import (
"reflect" "reflect"
"testing" "testing"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
func TestConfigMapName(t *testing.T) { func TestConfigMapName(t *testing.T) {
c := newTestFixtureCfgMaps(t) c := newTestFixtureCfgMaps(t)
if c.Name() != ConfigMapsDriverName { if c.Name() != ConfigMapsDriverName {
t.Errorf("Expected name to be %q, got %q", ConfigMapsDriverName, c.Name()) t.Errorf("Expected name to be %q, got %q", ConfigMapsDriverName, c.Name())
} }
} }
func TestConfigMapGet(t *testing.T) { func TestConfigMapGet(t *testing.T) {
vers := int32(1) vers := int32(1)
name := "smug-pigeon" name := "smug-pigeon"
key := testKey(name, vers) key := testKey(name, vers)
rel := releaseStub(name, vers, rspb.Status_DEPLOYED) rel := releaseStub(name, vers, rspb.Status_DEPLOYED)
cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...) cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...)
// get release with key // get release with key
got, err := cfgmaps.Get(key) got, err := cfgmaps.Get(key)
if err != nil { if err != nil {
t.Fatalf("Failed to get release: %s", err) t.Fatalf("Failed to get release: %s", err)
} }
// compare fetched release with original // compare fetched release with original
if !reflect.DeepEqual(rel, got) { if !reflect.DeepEqual(rel, got) {
t.Errorf("Expected {%q}, got {%q}", rel, got) t.Errorf("Expected {%q}, got {%q}", rel, got)
} }
} }
func TestConfigMapList(t *testing.T) { func TestConfigMapList(t *testing.T) {
cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{ cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{
releaseStub("key-1", 1, rspb.Status_DELETED), releaseStub("key-1", 1, rspb.Status_DELETED),
releaseStub("key-2", 1, rspb.Status_DELETED), releaseStub("key-2", 1, rspb.Status_DELETED),
releaseStub("key-3", 1, rspb.Status_DEPLOYED), releaseStub("key-3", 1, rspb.Status_DEPLOYED),
releaseStub("key-4", 1, rspb.Status_DEPLOYED), releaseStub("key-4", 1, rspb.Status_DEPLOYED),
releaseStub("key-5", 1, rspb.Status_SUPERSEDED), releaseStub("key-5", 1, rspb.Status_SUPERSEDED),
releaseStub("key-6", 1, rspb.Status_SUPERSEDED), releaseStub("key-6", 1, rspb.Status_SUPERSEDED),
}...) }...)
// list all deleted releases // list all deleted releases
del, err := cfgmaps.List(func(rel *rspb.Release) bool { del, err := cfgmaps.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_DELETED return rel.Info.Status.Code == rspb.Status_DELETED
}) })
// check // check
if err != nil { if err != nil {
t.Errorf("Failed to list deleted: %s", err) t.Errorf("Failed to list deleted: %s", err)
} }
if len(del) != 2 { if len(del) != 2 {
t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del)
} }
// list all deployed releases // list all deployed releases
dpl, err := cfgmaps.List(func(rel *rspb.Release) bool { dpl, err := cfgmaps.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_DEPLOYED return rel.Info.Status.Code == rspb.Status_DEPLOYED
}) })
// check // check
if err != nil { if err != nil {
t.Errorf("Failed to list deployed: %s", err) t.Errorf("Failed to list deployed: %s", err)
} }
if len(dpl) != 2 { if len(dpl) != 2 {
t.Errorf("Expected 2 deployed, got %d", len(dpl)) t.Errorf("Expected 2 deployed, got %d", len(dpl))
} }
// list all superseded releases // list all superseded releases
ssd, err := cfgmaps.List(func(rel *rspb.Release) bool { ssd, err := cfgmaps.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_SUPERSEDED return rel.Info.Status.Code == rspb.Status_SUPERSEDED
}) })
// check // check
if err != nil { if err != nil {
t.Errorf("Failed to list superseded: %s", err) t.Errorf("Failed to list superseded: %s", err)
} }
if len(ssd) != 2 { if len(ssd) != 2 {
t.Errorf("Expected 2 superseded, got %d", len(ssd)) t.Errorf("Expected 2 superseded, got %d", len(ssd))
} }
} }
func TestConfigMapCreate(t *testing.T) { func TestConfigMapCreate(t *testing.T) {
cfgmaps := newTestFixtureCfgMaps(t) cfgmaps := newTestFixtureCfgMaps(t)
vers := int32(1) vers := int32(1)
name := "smug-pigeon" name := "smug-pigeon"
key := testKey(name, vers) key := testKey(name, vers)
rel := releaseStub(name, vers, rspb.Status_DEPLOYED) rel := releaseStub(name, vers, rspb.Status_DEPLOYED)
// store the release in a configmap // store the release in a configmap
if err := cfgmaps.Create(key, rel); err != nil { if err := cfgmaps.Create(key, rel); err != nil {
t.Fatalf("Failed to create release with key %q: %s", key, err) t.Fatalf("Failed to create release with key %q: %s", key, err)
} }
// get the release back // get the release back
got, err := cfgmaps.Get(key) got, err := cfgmaps.Get(key)
if err != nil { if err != nil {
t.Fatalf("Failed to get release with key %q: %s", key, err) t.Fatalf("Failed to get release with key %q: %s", key, err)
} }
// compare created release with original // compare created release with original
if !reflect.DeepEqual(rel, got) { if !reflect.DeepEqual(rel, got) {
t.Errorf("Expected {%q}, got {%q}", rel, got) t.Errorf("Expected {%q}, got {%q}", rel, got)
} }
} }
func TestConfigMapUpdate(t *testing.T) { func TestConfigMapUpdate(t *testing.T) {
vers := int32(1) vers := int32(1)
name := "smug-pigeon" name := "smug-pigeon"
key := testKey(name, vers) key := testKey(name, vers)
rel := releaseStub(name, vers, rspb.Status_DEPLOYED) rel := releaseStub(name, vers, rspb.Status_DEPLOYED)
cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...) cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...)
// modify release status code // modify release status code
rel.Info.Status.Code = rspb.Status_SUPERSEDED rel.Info.Status.Code = rspb.Status_SUPERSEDED
// perform the update // perform the update
if err := cfgmaps.Update(key, rel); err != nil { if err := cfgmaps.Update(key, rel); err != nil {
t.Fatalf("Failed to update release: %s", err) t.Fatalf("Failed to update release: %s", err)
} }
// fetch the updated release // fetch the updated release
got, err := cfgmaps.Get(key) got, err := cfgmaps.Get(key)
if err != nil { if err != nil {
t.Fatalf("Failed to get release with key %q: %s", key, err) t.Fatalf("Failed to get release with key %q: %s", key, err)
} }
// check release has actually been updated by comparing modified fields // check release has actually been updated by comparing modified fields
if rel.Info.Status.Code != got.Info.Status.Code { if rel.Info.Status.Code != got.Info.Status.Code {
t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code) t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code)
} }
} }

@ -17,18 +17,18 @@ limitations under the License.
package driver // import "k8s.io/helm/pkg/storage/driver" package driver // import "k8s.io/helm/pkg/storage/driver"
import ( import (
"errors" "errors"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
var ( var (
// ErrReleaseNotFound indicates that a release is not found. // ErrReleaseNotFound indicates that a release is not found.
ErrReleaseNotFound = errors.New("release: not found") ErrReleaseNotFound = errors.New("release: not found")
// ErrReleaseExists indicates that a release already exists. // ErrReleaseExists indicates that a release already exists.
ErrReleaseExists = errors.New("release: already exists") ErrReleaseExists = errors.New("release: already exists")
// ErrInvalidKey indicates that a release key could not be parsed. // ErrInvalidKey indicates that a release key could not be parsed.
ErrInvalidKey = errors.New("release: invalid key") ErrInvalidKey = errors.New("release: invalid key")
) )
// Creator is the interface that wraps the Create method. // Creator is the interface that wraps the Create method.
@ -36,7 +36,7 @@ var (
// Create stores the release or returns ErrReleaseExists // Create stores the release or returns ErrReleaseExists
// if an identical release already exists. // if an identical release already exists.
type Creator interface { type Creator interface {
Create(key string, rls *rspb.Release) error Create(key string, rls *rspb.Release) error
} }
// Updator is the interface that wraps the Update method. // Updator is the interface that wraps the Update method.
@ -44,7 +44,7 @@ type Creator interface {
// Update updates an existing release or returns // Update updates an existing release or returns
// ErrReleaseNotFound if the release does not exist. // ErrReleaseNotFound if the release does not exist.
type Updator interface { type Updator interface {
Update(key string, rls *rspb.Release) error Update(key string, rls *rspb.Release) error
} }
// Deletor is the interface that wraps the Delete method. // Deletor is the interface that wraps the Delete method.
@ -52,7 +52,7 @@ type Updator interface {
// Delete deletes the release named by key or returns // Delete deletes the release named by key or returns
// ErrReleaseNotFound if the release does not exist. // ErrReleaseNotFound if the release does not exist.
type Deletor interface { type Deletor interface {
Delete(key string) (*rspb.Release, error) Delete(key string) (*rspb.Release, error)
} }
// Queryor is the interface that wraps the Get and List methods. // Queryor is the interface that wraps the Get and List methods.
@ -64,9 +64,9 @@ type Deletor interface {
// //
// Query returns the set of all releases that match the provided label set. // Query returns the set of all releases that match the provided label set.
type Queryor interface { type Queryor interface {
Get(key string) (*rspb.Release, error) Get(key string) (*rspb.Release, error)
List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error)
Query(labels map[string]string) ([]*rspb.Release, error) Query(labels map[string]string) ([]*rspb.Release, error)
} }
// Driver is the interface composed of Creator, Updator, Deletor, Queryor // Driver is the interface composed of Creator, Updator, Deletor, Queryor
@ -74,9 +74,9 @@ type Queryor interface {
// and retrieving tiller releases from some underlying storage mechanism, // and retrieving tiller releases from some underlying storage mechanism,
// e.g. memory, configmaps. // e.g. memory, configmaps.
type Driver interface { type Driver interface {
Creator Creator
Updator Updator
Deletor Deletor
Queryor Queryor
Name() string Name() string
} }

@ -1,9 +1,25 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 package driver
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
) )
// labels is a map of key value pairs to be included as metadata in a configmap object. // labels is a map of key value pairs to be included as metadata in a configmap object.
@ -14,37 +30,37 @@ func (lbs labels) get(key string) string { return lbs[key] }
func (lbs labels) set(key, val string) { lbs[key] = val } func (lbs labels) set(key, val string) { lbs[key] = val }
func (lbs labels) keys() (ls []string) { func (lbs labels) keys() (ls []string) {
for key := range lbs { for key := range lbs {
ls = append(ls, key) ls = append(ls, key)
} }
return return
} }
func (lbs labels) match(set labels) bool { func (lbs labels) match(set labels) bool {
for _, key := range set.keys() { for _, key := range set.keys() {
if lbs.get(key) != set.get(key) { if lbs.get(key) != set.get(key) {
return false return false
} }
} }
return true return true
} }
func (lbs labels) toMap() map[string]string { return lbs } func (lbs labels) toMap() map[string]string { return lbs }
func (lbs *labels) fromMap(kvs map[string]string) { func (lbs *labels) fromMap(kvs map[string]string) {
for k, v := range kvs { for k, v := range kvs {
lbs.set(k, v) lbs.set(k, v)
} }
} }
func (lbs labels) dump(w io.Writer) error { func (lbs labels) dump(w io.Writer) error {
var b bytes.Buffer var b bytes.Buffer
fmt.Fprintln(&b, "labels:") fmt.Fprintln(&b, "labels:")
for k, v := range lbs { for k, v := range lbs {
fmt.Fprintf(&b, "\t- %q -> %q\n", k, v) fmt.Fprintf(&b, "\t- %q -> %q\n", k, v)
} }
_, err := w.Write(b.Bytes()) _, err := w.Write(b.Bytes())
return err return err
} }

@ -1,33 +1,49 @@
package driver /*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 "k8s.io/helm/pkg/storage/driver"
import ( import (
"testing" "testing"
) )
func TestLabelsMatch(t *testing.T) { func TestLabelsMatch(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
set1 labels set1 labels
set2 labels set2 labels
expect bool expect bool
}{ }{
{ {
"equal labels sets", "equal labels sets",
labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}),
labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}),
true, true,
}, },
{ {
"disjoint label sets", "disjoint label sets",
labels(map[string]string{"KEY_C": "VAL_C", "KEY_D": "VAL_D"}), labels(map[string]string{"KEY_C": "VAL_C", "KEY_D": "VAL_D"}),
labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}),
false, false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
if !tt.set1.match(tt.set2) && tt.expect { if !tt.set1.match(tt.set2) && tt.expect {
t.Fatalf("Expected match '%s'\n", tt.desc) t.Fatalf("Expected match '%s'\n", tt.desc)
} }
} }
} }

@ -17,14 +17,14 @@ limitations under the License.
package driver package driver
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
var _ Driver = (*Memory)(nil) var _ Driver = (*Memory)(nil)
@ -34,158 +34,158 @@ const MemoryDriverName = "Memory"
// Memory is the in-memory storage driver implementation. // Memory is the in-memory storage driver implementation.
type Memory struct { type Memory struct {
sync.RWMutex sync.RWMutex
cache map[string]records cache map[string]records
} }
// NewMemory initializes a new memory driver. // NewMemory initializes a new memory driver.
func NewMemory() *Memory { func NewMemory() *Memory {
return &Memory{cache: map[string]records{}} return &Memory{cache: map[string]records{}}
} }
// Name returns the name of the driver. // Name returns the name of the driver.
func (mem *Memory) Name() string { func (mem *Memory) Name() string {
return MemoryDriverName return MemoryDriverName
} }
// Get returns the release named by key or returns ErrReleaseNotFound. // Get returns the release named by key or returns ErrReleaseNotFound.
func (mem *Memory) Get(key string) (*rspb.Release, error) { func (mem *Memory) Get(key string) (*rspb.Release, error) {
defer unlock(mem.rlock()) defer unlock(mem.rlock())
switch elems := strings.Split(key, ".v"); len(elems) { switch elems := strings.Split(key, ".v"); len(elems) {
case 2: case 2:
name, ver := elems[0], elems[1] name, ver := elems[0], elems[1]
if _, err := strconv.Atoi(ver); err != nil { if _, err := strconv.Atoi(ver); err != nil {
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
if recs, ok := mem.cache[name]; ok { if recs, ok := mem.cache[name]; ok {
if r := recs.Get(key); r != nil { if r := recs.Get(key); r != nil {
return r.rls, nil return r.rls, nil
} }
} }
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
default: default:
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
} }
// List returns the list of all releases such that filter(release) == true // List returns the list of all releases such that filter(release) == true
func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
defer unlock(mem.rlock()) defer unlock(mem.rlock())
var ls []*rspb.Release var ls []*rspb.Release
for _, recs := range mem.cache { for _, recs := range mem.cache {
recs.Iter(func(_ int, rec *record) bool { recs.Iter(func(_ int, rec *record) bool {
if filter(rec.rls) { if filter(rec.rls) {
ls = append(ls, rec.rls) ls = append(ls, rec.rls)
} }
return true return true
}) })
} }
return ls, nil return ls, nil
} }
// Query returns the set of releases that match the provided set of labels // Query returns the set of releases that match the provided set of labels
func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) { func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
defer unlock(mem.rlock()) defer unlock(mem.rlock())
var lbs labels var lbs labels
lbs.init() lbs.init()
lbs.fromMap(keyvals) lbs.fromMap(keyvals)
var ls []*rspb.Release var ls []*rspb.Release
for _, recs := range mem.cache { for _, recs := range mem.cache {
recs.Iter(func(_ int, rec *record) bool { recs.Iter(func(_ int, rec *record) bool {
if rec.lbs.match(lbs) { if rec.lbs.match(lbs) {
ls = append(ls, rec.rls) ls = append(ls, rec.rls)
} }
return true return true
}) })
} }
return ls, nil return ls, nil
} }
// Create creates a new release or returns ErrReleaseExists. // Create creates a new release or returns ErrReleaseExists.
func (mem *Memory) Create(key string, rls *rspb.Release) error { func (mem *Memory) Create(key string, rls *rspb.Release) error {
defer unlock(mem.wlock()) defer unlock(mem.wlock())
if recs, ok := mem.cache[rls.Name]; ok { if recs, ok := mem.cache[rls.Name]; ok {
if err := recs.Add(newRecord(key, rls)); err != nil { if err := recs.Add(newRecord(key, rls)); err != nil {
return err return err
} }
mem.cache[rls.Name] = recs mem.cache[rls.Name] = recs
return nil return nil
} }
mem.cache[rls.Name] = records{newRecord(key, rls)} mem.cache[rls.Name] = records{newRecord(key, rls)}
return nil return nil
} }
// Update updates a release or returns ErrReleaseNotFound. // Update updates a release or returns ErrReleaseNotFound.
func (mem *Memory) Update(key string, rls *rspb.Release) error { func (mem *Memory) Update(key string, rls *rspb.Release) error {
defer unlock(mem.wlock()) defer unlock(mem.wlock())
if rs, ok := mem.cache[rls.Name]; ok && rs.Exists(key) { if rs, ok := mem.cache[rls.Name]; ok && rs.Exists(key) {
rs.Replace(key, newRecord(key, rls)) rs.Replace(key, newRecord(key, rls))
return nil return nil
} }
return ErrReleaseNotFound return ErrReleaseNotFound
} }
// Delete deletes a release or returns ErrReleaseNotFound. // Delete deletes a release or returns ErrReleaseNotFound.
func (mem *Memory) Delete(key string) (*rspb.Release, error) { func (mem *Memory) Delete(key string) (*rspb.Release, error) {
defer unlock(mem.wlock()) defer unlock(mem.wlock())
switch elems := strings.Split(key, ".v"); len(elems) { switch elems := strings.Split(key, ".v"); len(elems) {
case 2: case 2:
name, ver := elems[0], elems[1] name, ver := elems[0], elems[1]
if _, err := strconv.Atoi(ver); err != nil { if _, err := strconv.Atoi(ver); err != nil {
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
if recs, ok := mem.cache[name]; ok { if recs, ok := mem.cache[name]; ok {
if r := recs.Remove(key); r != nil { if r := recs.Remove(key); r != nil {
return r.rls, nil return r.rls, nil
} }
} }
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
default: default:
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
return nil, ErrReleaseNotFound return nil, ErrReleaseNotFound
} }
func (mem *Memory) dump(w io.Writer) error { func (mem *Memory) dump(w io.Writer) error {
var b bytes.Buffer var b bytes.Buffer
fmt.Fprintln(&b, "memory:") fmt.Fprintln(&b, "memory:")
for key, recs := range mem.cache { for key, recs := range mem.cache {
fmt.Fprintf(&b, "\t# %q\n", key) fmt.Fprintf(&b, "\t# %q\n", key)
recs.Iter(func(index int, r *record) bool { recs.Iter(func(index int, r *record) bool {
fmt.Fprintf(&b, "\t\t- [%d] v%d (status = %s)\n", fmt.Fprintf(&b, "\t\t- [%d] v%d (status = %s)\n",
index, index,
r.rls.Version, r.rls.Version,
r.rls.Info.Status.Code, r.rls.Info.Status.Code,
) )
return true return true
}) })
} }
_, err := w.Write(b.Bytes()) _, err := w.Write(b.Bytes())
return err return err
} }
// wlock locks mem for writing // wlock locks mem for writing
func (mem *Memory) wlock() func() { func (mem *Memory) wlock() func() {
mem.Lock() mem.Lock()
return func() { mem.Unlock() } return func() { mem.Unlock() }
} }
// rlock locks mem for reading // rlock locks mem for reading
func (mem *Memory) rlock() func() { func (mem *Memory) rlock() func() {
mem.RLock() mem.RLock()
return func() { mem.RUnlock() } return func() { mem.RUnlock() }
} }
// unlock calls fn which reverses a mem.rlock or mem.wlock. e.g: // unlock calls fn which reverses a mem.rlock or mem.wlock. e.g:

@ -17,152 +17,152 @@ limitations under the License.
package driver package driver
import ( import (
"reflect" "reflect"
"testing" "testing"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
func TestMemoryName(t *testing.T) { func TestMemoryName(t *testing.T) {
if mem := NewMemory(); mem.Name() != MemoryDriverName { if mem := NewMemory(); mem.Name() != MemoryDriverName {
t.Errorf("Expected name to be %q, got %q", MemoryDriverName, mem.Name()) t.Errorf("Expected name to be %q, got %q", MemoryDriverName, mem.Name())
} }
} }
func TestMemoryCreate(t *testing.T) { func TestMemoryCreate(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
rls *rspb.Release rls *rspb.Release
err bool err bool
}{ }{
{ {
"create should success", "create should success",
releaseStub("rls-c", 1, rspb.Status_DEPLOYED), releaseStub("rls-c", 1, rspb.Status_DEPLOYED),
false, false,
}, },
{ {
"create should fail (release already exists)", "create should fail (release already exists)",
releaseStub("rls-a", 1, rspb.Status_DEPLOYED), releaseStub("rls-a", 1, rspb.Status_DEPLOYED),
true, true,
}, },
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
key := testKey(tt.rls.Name, tt.rls.Version) key := testKey(tt.rls.Name, tt.rls.Version)
rls := tt.rls rls := tt.rls
if err := ts.Create(key, rls); err != nil { if err := ts.Create(key, rls); err != nil {
if !tt.err { if !tt.err {
t.Fatalf("failed to create %q: %s", tt.desc, err) t.Fatalf("failed to create %q: %s", tt.desc, err)
} }
} }
} }
} }
func TestMemoryGet(t *testing.T) { func TestMemoryGet(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
key string key string
err bool err bool
}{ }{
{"release key should exist", "rls-a.v1", false}, {"release key should exist", "rls-a.v1", false},
{"release key should not exist", "rls-a.v5", true}, {"release key should not exist", "rls-a.v5", true},
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
if _, err := ts.Get(tt.key); err != nil { if _, err := ts.Get(tt.key); err != nil {
if !tt.err { if !tt.err {
t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err) t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err)
} }
} }
} }
} }
func TestMemoryQuery(t *testing.T) { func TestMemoryQuery(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
xlen int xlen int
lbs map[string]string lbs map[string]string
}{ }{
{ {
"should be 2 query results", "should be 2 query results",
2, 2,
map[string]string{"STATUS": "DEPLOYED"}, map[string]string{"STATUS": "DEPLOYED"},
}, },
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
l, err := ts.Query(tt.lbs) l, err := ts.Query(tt.lbs)
if err != nil { if err != nil {
t.Fatalf("Failed to query: %s\n", err) t.Fatalf("Failed to query: %s\n", err)
} }
if tt.xlen != len(l) { if tt.xlen != len(l) {
t.Fatalf("Expected %d results, actual %d\n", tt.xlen, len(l)) t.Fatalf("Expected %d results, actual %d\n", tt.xlen, len(l))
} }
} }
} }
func TestMemoryUpdate(t *testing.T) { func TestMemoryUpdate(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
key string key string
rls *rspb.Release rls *rspb.Release
err bool err bool
}{ }{
{ {
"update release status", "update release status",
"rls-a.v4", "rls-a.v4",
releaseStub("rls-a", 4, rspb.Status_SUPERSEDED), releaseStub("rls-a", 4, rspb.Status_SUPERSEDED),
false, false,
}, },
{ {
"update release does not exist", "update release does not exist",
"rls-z.v1", "rls-z.v1",
releaseStub("rls-z", 1, rspb.Status_DELETED), releaseStub("rls-z", 1, rspb.Status_DELETED),
true, true,
}, },
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
if err := ts.Update(tt.key, tt.rls); err != nil { if err := ts.Update(tt.key, tt.rls); err != nil {
if !tt.err { if !tt.err {
t.Fatalf("Failed %q: %s\n", tt.desc, err) t.Fatalf("Failed %q: %s\n", tt.desc, err)
} }
continue continue
} }
r, err := ts.Get(tt.key) r, err := ts.Get(tt.key)
if err != nil { if err != nil {
t.Fatalf("Failed to get: %s\n", err) t.Fatalf("Failed to get: %s\n", err)
} }
if !reflect.DeepEqual(r, tt.rls) { if !reflect.DeepEqual(r, tt.rls) {
t.Fatalf("Expected %s, actual %s\n", tt.rls, r) t.Fatalf("Expected %s, actual %s\n", tt.rls, r)
} }
} }
} }
func TestMemoryDelete(t *testing.T) { func TestMemoryDelete(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
key string key string
err bool err bool
}{ }{
{"release key should exist", "rls-a.v1", false}, {"release key should exist", "rls-a.v1", false},
{"release key should not exist", "rls-a.v5", true}, {"release key should not exist", "rls-a.v5", true},
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
if _, err := ts.Delete(tt.key); err != nil { if _, err := ts.Delete(tt.key); err != nil {
if !tt.err { if !tt.err {
t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err) t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err)
} }
} }
} }
} }

@ -1,10 +1,26 @@
package driver /*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 "k8s.io/helm/pkg/storage/driver"
import ( import (
"sort" "sort"
"strconv" "strconv"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
// records holds a list of in-memory release records // records holds a list of in-memory release records
@ -15,102 +31,102 @@ 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) Less(i, j int) bool { return rs[i].rls.Version < rs[j].rls.Version }
func (rs *records) Add(r *record) error { func (rs *records) Add(r *record) error {
if r == nil { if r == nil {
return nil return nil
} }
if rs.Exists(r.key) { if rs.Exists(r.key) {
return ErrReleaseExists return ErrReleaseExists
} }
*rs = append(*rs, r) *rs = append(*rs, r)
sort.Sort(*rs) sort.Sort(*rs)
return nil return nil
} }
func (rs records) Get(key string) *record { func (rs records) Get(key string) *record {
if i, ok := rs.Index(key); ok { if i, ok := rs.Index(key); ok {
return rs[i] return rs[i]
} }
return nil return nil
} }
func (rs *records) Iter(fn func(int, *record) bool) { func (rs *records) Iter(fn func(int, *record) bool) {
cp := make([]*record, len(*rs)) cp := make([]*record, len(*rs))
copy(cp, *rs) copy(cp, *rs)
for i, r := range cp { for i, r := range cp {
if !fn(i, r) { if !fn(i, r) {
return return
} }
} }
} }
func (rs *records) Index(key string) (int, bool) { func (rs *records) Index(key string) (int, bool) {
for i, r := range *rs { for i, r := range *rs {
if r.key == key { if r.key == key {
return i, true return i, true
} }
} }
return -1, false return -1, false
} }
func (rs records) Exists(key string) bool { func (rs records) Exists(key string) bool {
_, ok := rs.Index(key) _, ok := rs.Index(key)
return ok return ok
} }
func (rs *records) Remove(key string) (r *record) { func (rs *records) Remove(key string) (r *record) {
if i, ok := rs.Index(key); ok { if i, ok := rs.Index(key); ok {
return rs.removeAt(i) return rs.removeAt(i)
} }
return nil return nil
} }
func (rs *records) Replace(key string, rec *record) *record { func (rs *records) Replace(key string, rec *record) *record {
if i, ok := rs.Index(key); ok { if i, ok := rs.Index(key); ok {
old := (*rs)[i] old := (*rs)[i]
(*rs)[i] = rec (*rs)[i] = rec
return old return old
} }
return nil return nil
} }
func (rs records) FindByVersion(vers int32) (int, bool) { func (rs records) FindByVersion(vers int32) (int, bool) {
i := sort.Search(len(rs), func(i int) bool { i := sort.Search(len(rs), func(i int) bool {
return rs[i].rls.Version == vers return rs[i].rls.Version == vers
}) })
if i < len(rs) && rs[i].rls.Version == vers { if i < len(rs) && rs[i].rls.Version == vers {
return i, true return i, true
} }
return i, false return i, false
} }
func (rs *records) removeAt(index int) *record { func (rs *records) removeAt(index int) *record {
r := (*rs)[index] r := (*rs)[index]
(*rs)[index] = nil (*rs)[index] = nil
copy((*rs)[index:], (*rs)[index+1:]) copy((*rs)[index:], (*rs)[index+1:])
*rs = (*rs)[:len(*rs)-1] *rs = (*rs)[:len(*rs)-1]
return r return r
} }
// record is the data structure used to cache releases // record is the data structure used to cache releases
// for the in-memory storage driver // for the in-memory storage driver
type record struct { type record struct {
key string key string
lbs labels lbs labels
rls *rspb.Release rls *rspb.Release
} }
// newRecord creates a new in-memory release record // newRecord creates a new in-memory release record
func newRecord(key string, rls *rspb.Release) *record { func newRecord(key string, rls *rspb.Release) *record {
var lbs labels var lbs labels
lbs.init() lbs.init()
lbs.set("NAME", rls.Name) lbs.set("NAME", rls.Name)
lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)]) lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)])
lbs.set("VERSION", strconv.Itoa(int(rls.Version))) lbs.set("VERSION", strconv.Itoa(int(rls.Version)))
return &record{key: key, lbs: lbs, rls: rls} return &record{key: key, lbs: lbs, rls: rls}
} }

@ -1,71 +1,87 @@
package driver /*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 "k8s.io/helm/pkg/storage/driver"
import ( import (
"testing" "testing"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
) )
func TestRecordsAdd(t *testing.T) { func TestRecordsAdd(t *testing.T) {
rs := records([]*record{ rs := records([]*record{
newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_SUPERSEDED)), newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_SUPERSEDED)),
newRecord("rls-a.v2", releaseStub("rls-a", 2, rspb.Status_DEPLOYED)), newRecord("rls-a.v2", releaseStub("rls-a", 2, rspb.Status_DEPLOYED)),
}) })
var tests = []struct { var tests = []struct {
desc string desc string
key string key string
ok bool ok bool
rec *record rec *record
}{ }{
{ {
"add valid key", "add valid key",
"rls-a.v3", "rls-a.v3",
false, false,
newRecord("rls-a.v3", releaseStub("rls-a", 3, rspb.Status_SUPERSEDED)), newRecord("rls-a.v3", releaseStub("rls-a", 3, rspb.Status_SUPERSEDED)),
}, },
{ {
"add already existing key", "add already existing key",
"rls-a.v1", "rls-a.v1",
true, true,
newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_DEPLOYED)), newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_DEPLOYED)),
}, },
} }
for _, tt := range tests { for _, tt := range tests {
if err := rs.Add(tt.rec); err != nil { if err := rs.Add(tt.rec); err != nil {
if !tt.ok { if !tt.ok {
t.Fatalf("failed: %q: %s\n", tt.desc, err) t.Fatalf("failed: %q: %s\n", tt.desc, err)
} }
} }
} }
} }
func TestRecordsRemove(t *testing.T) { func TestRecordsRemove(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
key string key string
ok bool ok bool
}{ }{
{"remove valid key", "rls-a.v1", false}, {"remove valid key", "rls-a.v1", false},
{"remove invalid key", "rls-a.v", true}, {"remove invalid key", "rls-a.v", true},
{"remove non-existant key", "rls-z.v1", true}, {"remove non-existant key", "rls-z.v1", true},
} }
rs := records([]*record{ rs := records([]*record{
newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_SUPERSEDED)), newRecord("rls-a.v1", releaseStub("rls-a", 1, rspb.Status_SUPERSEDED)),
newRecord("rls-a.v2", releaseStub("rls-a", 2, rspb.Status_DEPLOYED)), newRecord("rls-a.v2", releaseStub("rls-a", 2, rspb.Status_DEPLOYED)),
}) })
for _, tt := range tests { for _, tt := range tests {
if r := rs.Remove(tt.key); r == nil { if r := rs.Remove(tt.key); r == nil {
if !tt.ok { if !tt.ok {
t.Fatalf("Failed to %q (key = %s). Expected nil, got %s", t.Fatalf("Failed to %q (key = %s). Expected nil, got %s",
tt.desc, tt.desc,
tt.key, tt.key,
r, r,
) )
} }
} }
} }
} }

@ -1,119 +1,135 @@
package driver /*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 "k8s.io/helm/pkg/storage/driver"
import ( import (
"fmt" "fmt"
"testing" "testing"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/api"
kberrs "k8s.io/kubernetes/pkg/api/errors" kberrs "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/client/unversioned"
) )
func releaseStub(name string, vers int32, code rspb.Status_Code) *rspb.Release { func releaseStub(name string, vers int32, code rspb.Status_Code) *rspb.Release {
return &rspb.Release{ return &rspb.Release{
Name: name, Name: name,
Version: vers, Version: vers,
Info: &rspb.Info{Status: &rspb.Status{Code: code}}, Info: &rspb.Info{Status: &rspb.Status{Code: code}},
} }
} }
func testKey(name string, vers int32) string { func testKey(name string, vers int32) string {
return fmt.Sprintf("%s.v%d", name, vers) return fmt.Sprintf("%s.v%d", name, vers)
} }
func tsFixtureMemory(t *testing.T) *Memory { func tsFixtureMemory(t *testing.T) *Memory {
hs := []*rspb.Release{ hs := []*rspb.Release{
// rls-a // rls-a
releaseStub("rls-a", 4, rspb.Status_DEPLOYED), releaseStub("rls-a", 4, rspb.Status_DEPLOYED),
releaseStub("rls-a", 1, rspb.Status_SUPERSEDED), releaseStub("rls-a", 1, rspb.Status_SUPERSEDED),
releaseStub("rls-a", 3, rspb.Status_SUPERSEDED), releaseStub("rls-a", 3, rspb.Status_SUPERSEDED),
releaseStub("rls-a", 2, rspb.Status_SUPERSEDED), releaseStub("rls-a", 2, rspb.Status_SUPERSEDED),
// rls-b // rls-b
releaseStub("rls-b", 4, rspb.Status_DEPLOYED), releaseStub("rls-b", 4, rspb.Status_DEPLOYED),
releaseStub("rls-b", 1, rspb.Status_SUPERSEDED), releaseStub("rls-b", 1, rspb.Status_SUPERSEDED),
releaseStub("rls-b", 3, rspb.Status_SUPERSEDED), releaseStub("rls-b", 3, rspb.Status_SUPERSEDED),
releaseStub("rls-b", 2, rspb.Status_SUPERSEDED), releaseStub("rls-b", 2, rspb.Status_SUPERSEDED),
} }
mem := NewMemory() mem := NewMemory()
for _, tt := range hs { for _, tt := range hs {
err := mem.Create(testKey(tt.Name, tt.Version), tt) err := mem.Create(testKey(tt.Name, tt.Version), tt)
if err != nil { if err != nil {
t.Fatalf("Test setup failed to create: %s\n", err) t.Fatalf("Test setup failed to create: %s\n", err)
} }
} }
return mem return mem
} }
// newTestFixture initializes a MockConfigMapsInterface. // newTestFixture initializes a MockConfigMapsInterface.
// ConfigMaps are created for each release provided. // ConfigMaps are created for each release provided.
func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps { func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps {
var mock MockConfigMapsInterface var mock MockConfigMapsInterface
mock.Init(t, releases...) mock.Init(t, releases...)
return NewConfigMaps(&mock) return NewConfigMaps(&mock)
} }
// MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface // MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface
type MockConfigMapsInterface struct { type MockConfigMapsInterface struct {
unversioned.ConfigMapsInterface unversioned.ConfigMapsInterface
objects map[string]*api.ConfigMap objects map[string]*api.ConfigMap
} }
func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) { func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) {
mock.objects = map[string]*api.ConfigMap{} mock.objects = map[string]*api.ConfigMap{}
for _, rls := range releases { for _, rls := range releases {
objkey := testKey(rls.Name, rls.Version) objkey := testKey(rls.Name, rls.Version)
cfgmap, err := newConfigMapsObject(objkey, rls, nil) cfgmap, err := newConfigMapsObject(objkey, rls, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create configmap: %s", err) t.Fatalf("Failed to create configmap: %s", err)
} }
mock.objects[objkey] = cfgmap mock.objects[objkey] = cfgmap
} }
} }
func (mock *MockConfigMapsInterface) Get(name string) (*api.ConfigMap, error) { func (mock *MockConfigMapsInterface) Get(name string) (*api.ConfigMap, error) {
object, ok := mock.objects[name] object, ok := mock.objects[name]
if !ok { if !ok {
return nil, kberrs.NewNotFound(api.Resource("tests"), name) return nil, kberrs.NewNotFound(api.Resource("tests"), name)
} }
return object, nil return object, nil
} }
func (mock *MockConfigMapsInterface) List(opts api.ListOptions) (*api.ConfigMapList, error) { func (mock *MockConfigMapsInterface) List(opts api.ListOptions) (*api.ConfigMapList, error) {
var list api.ConfigMapList var list api.ConfigMapList
for _, cfgmap := range mock.objects { for _, cfgmap := range mock.objects {
list.Items = append(list.Items, *cfgmap) list.Items = append(list.Items, *cfgmap)
} }
return &list, nil return &list, nil
} }
func (mock *MockConfigMapsInterface) Create(cfgmap *api.ConfigMap) (*api.ConfigMap, error) { func (mock *MockConfigMapsInterface) Create(cfgmap *api.ConfigMap) (*api.ConfigMap, error) {
name := cfgmap.ObjectMeta.Name name := cfgmap.ObjectMeta.Name
if object, ok := mock.objects[name]; ok { if object, ok := mock.objects[name]; ok {
return object, kberrs.NewAlreadyExists(api.Resource("tests"), name) return object, kberrs.NewAlreadyExists(api.Resource("tests"), name)
} }
mock.objects[name] = cfgmap mock.objects[name] = cfgmap
return cfgmap, nil return cfgmap, nil
} }
func (mock *MockConfigMapsInterface) Update(cfgmap *api.ConfigMap) (*api.ConfigMap, error) { func (mock *MockConfigMapsInterface) Update(cfgmap *api.ConfigMap) (*api.ConfigMap, error) {
name := cfgmap.ObjectMeta.Name name := cfgmap.ObjectMeta.Name
if _, ok := mock.objects[name]; !ok { if _, ok := mock.objects[name]; !ok {
return nil, kberrs.NewNotFound(api.Resource("tests"), name) return nil, kberrs.NewNotFound(api.Resource("tests"), name)
} }
mock.objects[name] = cfgmap mock.objects[name] = cfgmap
return cfgmap, nil return cfgmap, nil
} }
func (mock *MockConfigMapsInterface) Delete(name string) error { func (mock *MockConfigMapsInterface) Delete(name string) error {
if _, ok := mock.objects[name]; !ok { if _, ok := mock.objects[name]; !ok {
return kberrs.NewNotFound(api.Resource("tests"), name) return kberrs.NewNotFound(api.Resource("tests"), name)
} }
delete(mock.objects, name) delete(mock.objects, name)
return nil return nil
} }

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package storage package storage // import "k8s.io/helm/pkg/storage"
import rspb "k8s.io/helm/pkg/proto/hapi/release" import rspb "k8s.io/helm/pkg/proto/hapi/release"

@ -17,127 +17,127 @@ limitations under the License.
package storage // import "k8s.io/helm/pkg/storage" package storage // import "k8s.io/helm/pkg/storage"
import ( import (
"fmt" "fmt"
"log" "log"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
) )
// Storage represents a storage engine for a Release. // Storage represents a storage engine for a Release.
type Storage struct { type Storage struct {
driver.Driver driver.Driver
} }
// Get retrieves the release from storage. An error is returned // Get retrieves the release from storage. An error is returned
// if the storage driver failed to fetch the release, or the // if the storage driver failed to fetch the release, or the
// release identified by the key, version pair does not exist. // release identified by the key, version pair does not exist.
func (s *Storage) Get(name string, version int32) (*rspb.Release, error) { func (s *Storage) Get(name string, version int32) (*rspb.Release, error) {
log.Printf("Getting release %q (v%d) from storage\n", name, version) log.Printf("Getting release %q (v%d) from storage\n", name, version)
return s.Driver.Get(makeKey(name, version)) return s.Driver.Get(makeKey(name, version))
} }
// Create creates a new storage entry holding the release. An // Create creates a new storage entry holding the release. An
// error is returned if the storage driver failed to store the // error is returned if the storage driver failed to store the
// release, or a release with identical an key already exists. // release, or a release with identical an key already exists.
func (s *Storage) Create(rls *rspb.Release) error { func (s *Storage) Create(rls *rspb.Release) error {
log.Printf("Create release %q (v%d) in storage\n", rls.Name, rls.Version) log.Printf("Create release %q (v%d) in storage\n", rls.Name, rls.Version)
return s.Driver.Create(makeKey(rls.Name, rls.Version), rls) return s.Driver.Create(makeKey(rls.Name, rls.Version), rls)
} }
// Update update the release in storage. An error is returned if the // Update update the release in storage. An error is returned if the
// storage backend fails to update the release or if the release // storage backend fails to update the release or if the release
// does not exist. // does not exist.
func (s *Storage) Update(rls *rspb.Release) error { func (s *Storage) Update(rls *rspb.Release) error {
log.Printf("Updating %q (v%d) in storage\n", rls.Name, rls.Version) log.Printf("Updating %q (v%d) in storage\n", rls.Name, rls.Version)
return s.Driver.Update(makeKey(rls.Name, rls.Version), rls) return s.Driver.Update(makeKey(rls.Name, rls.Version), rls)
} }
// Delete deletes the release from storage. An error is returned if // Delete deletes the release from storage. An error is returned if
// the storage backend fails to delete the release or if the release // the storage backend fails to delete the release or if the release
// does not exist. // does not exist.
func (s *Storage) Delete(name string, version int32) (*rspb.Release, error) { func (s *Storage) Delete(name string, version int32) (*rspb.Release, error) {
log.Printf("Deleting release %q (v%d) from storage\n", name, version) log.Printf("Deleting release %q (v%d) from storage\n", name, version)
return s.Driver.Delete(makeKey(name, version)) return s.Driver.Delete(makeKey(name, version))
} }
// ListReleases returns all releases from storage. An error is returned if the // ListReleases returns all releases from storage. An error is returned if the
// storage backend fails to retrieve the releases. // storage backend fails to retrieve the releases.
func (s *Storage) ListReleases() ([]*rspb.Release, error) { func (s *Storage) ListReleases() ([]*rspb.Release, error) {
log.Println("Listing all releases in storage") log.Println("Listing all releases in storage")
return s.Driver.List(func(_ *rspb.Release) bool { return true }) return s.Driver.List(func(_ *rspb.Release) bool { return true })
} }
// ListDeleted returns all releases with Status == DELETED. An error is returned // ListDeleted returns all releases with Status == DELETED. An error is returned
// if the storage backend fails to retrieve the releases. // if the storage backend fails to retrieve the releases.
func (s *Storage) ListDeleted() ([]*rspb.Release, error) { func (s *Storage) ListDeleted() ([]*rspb.Release, error) {
log.Println("List deleted releases in storage") log.Println("List deleted releases in storage")
return s.Driver.List(func(rls *rspb.Release) bool { return s.Driver.List(func(rls *rspb.Release) bool {
return StatusFilter(rspb.Status_DELETED).Check(rls) return StatusFilter(rspb.Status_DELETED).Check(rls)
}) })
} }
// ListDeployed returns all releases with Status == DEPLOYED. An error is returned // ListDeployed returns all releases with Status == DEPLOYED. An error is returned
// if the storage backend fails to retrieve the releases. // if the storage backend fails to retrieve the releases.
func (s *Storage) ListDeployed() ([]*rspb.Release, error) { func (s *Storage) ListDeployed() ([]*rspb.Release, error) {
log.Println("Listing all deployed releases in storage") log.Println("Listing all deployed releases in storage")
return s.Driver.List(func(rls *rspb.Release) bool { return s.Driver.List(func(rls *rspb.Release) bool {
return StatusFilter(rspb.Status_DEPLOYED).Check(rls) return StatusFilter(rspb.Status_DEPLOYED).Check(rls)
}) })
} }
// ListFilterAll returns the set of releases satisfying satisfying the predicate // ListFilterAll returns the set of releases satisfying satisfying the predicate
// (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results // (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results
// if and only if all filters return true. // if and only if all filters return true.
func (s *Storage) ListFilterAll(filters ...FilterFunc) ([]*rspb.Release, error) { func (s *Storage) ListFilterAll(filters ...FilterFunc) ([]*rspb.Release, error) {
log.Println("Listing all releases with filter") log.Println("Listing all releases with filter")
return s.Driver.List(func(rls *rspb.Release) bool { return s.Driver.List(func(rls *rspb.Release) bool {
return All(filters...).Check(rls) return All(filters...).Check(rls)
}) })
} }
// ListFilterAny returns the set of releases satisfying satisfying the predicate // ListFilterAny returns the set of releases satisfying satisfying the predicate
// (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results // (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results
// if at least one of the filters returns true. // if at least one of the filters returns true.
func (s *Storage) ListFilterAny(filters ...FilterFunc) ([]*rspb.Release, error) { func (s *Storage) ListFilterAny(filters ...FilterFunc) ([]*rspb.Release, error) {
log.Println("Listing any releases with filter") log.Println("Listing any releases with filter")
return s.Driver.List(func(rls *rspb.Release) bool { return s.Driver.List(func(rls *rspb.Release) bool {
return Any(filters...).Check(rls) return Any(filters...).Check(rls)
}) })
} }
// Deployed returns the deployed release with the provided release name, or // Deployed returns the deployed release with the provided release name, or
// returns ErrReleaseNotFound if not found. // returns ErrReleaseNotFound if not found.
func (s *Storage) Deployed(name string) (*rspb.Release, error) { func (s *Storage) Deployed(name string) (*rspb.Release, error) {
log.Printf("Getting deployed release from '%s' history\n", name) log.Printf("Getting deployed release from '%s' history\n", name)
ls, err := s.Driver.Query(map[string]string{ ls, err := s.Driver.Query(map[string]string{
"NAME": name, "NAME": name,
"STATUS": "DEPLOYED", "STATUS": "DEPLOYED",
}) })
switch { switch {
case err != nil: case err != nil:
return nil, err return nil, err
case len(ls) == 0: case len(ls) == 0:
return nil, fmt.Errorf("'%s' has no deployed releases", name) return nil, fmt.Errorf("'%s' has no deployed releases", name)
default: default:
return ls[0], nil return ls[0], nil
} }
} }
// makeKey concatenates a release name and version into // makeKey concatenates a release name and version into
// a string with format ```<release_name>#v<version>```. // a string with format ```<release_name>#v<version>```.
// This key is used to uniquely identify storage objects. // This key is used to uniquely identify storage objects.
func makeKey(rlsname string, version int32) string { func makeKey(rlsname string, version int32) string {
return fmt.Sprintf("%s.v%d", rlsname, version) return fmt.Sprintf("%s.v%d", rlsname, version)
} }
// Init initializes a new storage backend with the driver d. // Init initializes a new storage backend with the driver d.
// If d is nil, the default in-memory driver is used. // If d is nil, the default in-memory driver is used.
func Init(d driver.Driver) *Storage { func Init(d driver.Driver) *Storage {
// default driver is in memory // default driver is in memory
if d == nil { if d == nil {
d = driver.NewMemory() d = driver.NewMemory()
} }
return &Storage{Driver: d} return &Storage{Driver: d}
} }

@ -17,195 +17,195 @@ limitations under the License.
package storage // import "k8s.io/helm/pkg/storage" package storage // import "k8s.io/helm/pkg/storage"
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"testing" "testing"
rspb "k8s.io/helm/pkg/proto/hapi/release" rspb "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
) )
func TestStorageCreate(t *testing.T) { func TestStorageCreate(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
Name: "angry-beaver", Name: "angry-beaver",
Version: 1, Version: 1,
}.ToRelease() }.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// fetch the release // fetch the release
res, err := storage.Get(rls.Name, rls.Version) res, err := storage.Get(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "QueryRelease") assertErrNil(t.Fatal, err, "QueryRelease")
// verify the fetched and created release are the same // verify the fetched and created release are the same
if !reflect.DeepEqual(rls, res) { if !reflect.DeepEqual(rls, res) {
t.Fatalf("Expected %q, got %q", rls, res) t.Fatalf("Expected %q, got %q", rls, res)
} }
} }
func TestStorageUpdate(t *testing.T) { func TestStorageUpdate(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
Name: "angry-beaver", Name: "angry-beaver",
Version: 1, Version: 1,
Status: rspb.Status_DEPLOYED, Status: rspb.Status_DEPLOYED,
}.ToRelease() }.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// modify the release // modify the release
rls.Info.Status.Code = rspb.Status_DELETED rls.Info.Status.Code = rspb.Status_DELETED
assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease") assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease")
// retrieve the updated release // retrieve the updated release
res, err := storage.Get(rls.Name, rls.Version) res, err := storage.Get(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "QueryRelease") assertErrNil(t.Fatal, err, "QueryRelease")
// verify updated and fetched releases are the same. // verify updated and fetched releases are the same.
if !reflect.DeepEqual(rls, res) { if !reflect.DeepEqual(rls, res) {
t.Fatalf("Expected %q, got %q", rls, res) t.Fatalf("Expected %q, got %q", rls, res)
} }
} }
func TestStorageDelete(t *testing.T) { func TestStorageDelete(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
Name: "angry-beaver", Name: "angry-beaver",
Version: 1, Version: 1,
}.ToRelease() }.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
// delete the release // delete the release
res, err := storage.Delete(rls.Name, rls.Version) res, err := storage.Delete(rls.Name, rls.Version)
assertErrNil(t.Fatal, err, "DeleteRelease") assertErrNil(t.Fatal, err, "DeleteRelease")
// verify updated and fetched releases are the same. // verify updated and fetched releases are the same.
if !reflect.DeepEqual(rls, res) { if !reflect.DeepEqual(rls, res) {
t.Fatalf("Expected %q, got %q", rls, res) t.Fatalf("Expected %q, got %q", rls, res)
} }
} }
func TestStorageList(t *testing.T) { func TestStorageList(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
// setup storage with test releases // setup storage with test releases
setup := func() { setup := func() {
// release records // release records
rls0 := ReleaseTestData{Name: "happy-catdog", Status: rspb.Status_SUPERSEDED}.ToRelease() rls0 := ReleaseTestData{Name: "happy-catdog", Status: rspb.Status_SUPERSEDED}.ToRelease()
rls1 := ReleaseTestData{Name: "livid-human", Status: rspb.Status_SUPERSEDED}.ToRelease() rls1 := ReleaseTestData{Name: "livid-human", Status: rspb.Status_SUPERSEDED}.ToRelease()
rls2 := ReleaseTestData{Name: "relaxed-cat", Status: rspb.Status_SUPERSEDED}.ToRelease() rls2 := ReleaseTestData{Name: "relaxed-cat", Status: rspb.Status_SUPERSEDED}.ToRelease()
rls3 := ReleaseTestData{Name: "hungry-hippo", Status: rspb.Status_DEPLOYED}.ToRelease() rls3 := ReleaseTestData{Name: "hungry-hippo", Status: rspb.Status_DEPLOYED}.ToRelease()
rls4 := ReleaseTestData{Name: "angry-beaver", Status: rspb.Status_DEPLOYED}.ToRelease() rls4 := ReleaseTestData{Name: "angry-beaver", Status: rspb.Status_DEPLOYED}.ToRelease()
rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.Status_DELETED}.ToRelease() rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.Status_DELETED}.ToRelease()
rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.Status_DELETED}.ToRelease() rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.Status_DELETED}.ToRelease()
// create the release records in the storage // create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'") assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'rls1'") assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'rls1'")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'rls2'") assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'rls2'")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'rls3'") assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'rls3'")
assertErrNil(t.Fatal, storage.Create(rls4), "Storing release 'rls4'") assertErrNil(t.Fatal, storage.Create(rls4), "Storing release 'rls4'")
assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'rls5'") assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'rls5'")
assertErrNil(t.Fatal, storage.Create(rls6), "Storing release 'rls6'") assertErrNil(t.Fatal, storage.Create(rls6), "Storing release 'rls6'")
} }
var listTests = []struct { var listTests = []struct {
Description string Description string
NumExpected int NumExpected int
ListFunc func() ([]*rspb.Release, error) ListFunc func() ([]*rspb.Release, error)
}{ }{
{"ListDeleted", 2, storage.ListDeleted}, {"ListDeleted", 2, storage.ListDeleted},
{"ListDeployed", 2, storage.ListDeployed}, {"ListDeployed", 2, storage.ListDeployed},
{"ListReleases", 7, storage.ListReleases}, {"ListReleases", 7, storage.ListReleases},
} }
setup() setup()
for _, tt := range listTests { for _, tt := range listTests {
list, err := tt.ListFunc() list, err := tt.ListFunc()
assertErrNil(t.Fatal, err, tt.Description) assertErrNil(t.Fatal, err, tt.Description)
// verify the count of releases returned // verify the count of releases returned
if len(list) != tt.NumExpected { if len(list) != tt.NumExpected {
t.Errorf("ListReleases(%s): expected %d, actual %d", t.Errorf("ListReleases(%s): expected %d, actual %d",
tt.Description, tt.Description,
tt.NumExpected, tt.NumExpected,
len(list)) len(list))
} }
} }
} }
func TestStorageDeployed(t *testing.T) { func TestStorageDeployed(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory())
const name = "angry-bird" const name = "angry-bird"
const vers = int32(4) const vers = int32(4)
// setup storage with test releases // setup storage with test releases
setup := func() { setup := func() {
// release records // release records
rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.Status_SUPERSEDED}.ToRelease() rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.Status_SUPERSEDED}.ToRelease() rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.Status_SUPERSEDED}.ToRelease() rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.Status_DEPLOYED}.ToRelease() rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.Status_DEPLOYED}.ToRelease()
// create the release records in the storage // create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)")
} }
setup() setup()
rls, err := storage.Deployed(name) rls, err := storage.Deployed(name)
if err != nil { if err != nil {
t.Fatalf("Failed to query for deployed release: %s\n", err) t.Fatalf("Failed to query for deployed release: %s\n", err)
} }
switch { switch {
case rls == nil: case rls == nil:
t.Fatalf("Release is nil") t.Fatalf("Release is nil")
case rls.Name != name: case rls.Name != name:
t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name)
case rls.Version != vers: case rls.Version != vers:
t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version)
case rls.Info.Status.Code != rspb.Status_DEPLOYED: case rls.Info.Status.Code != rspb.Status_DEPLOYED:
t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.Code) t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.Code)
} }
} }
type ReleaseTestData struct { type ReleaseTestData struct {
Name string Name string
Version int32 Version int32
Manifest string Manifest string
Namespace string Namespace string
Status rspb.Status_Code Status rspb.Status_Code
} }
func (test ReleaseTestData) ToRelease() *rspb.Release { func (test ReleaseTestData) ToRelease() *rspb.Release {
return &rspb.Release{ return &rspb.Release{
Name: test.Name, Name: test.Name,
Version: test.Version, Version: test.Version,
Manifest: test.Manifest, Manifest: test.Manifest,
Namespace: test.Namespace, Namespace: test.Namespace,
Info: &rspb.Info{Status: &rspb.Status{Code: test.Status}}, Info: &rspb.Info{Status: &rspb.Status{Code: test.Status}},
} }
} }
func assertErrNil(eh func(args ...interface{}), err error, message string) { func assertErrNil(eh func(args ...interface{}), err error, message string) {
if err != nil { if err != nil {
eh(fmt.Sprintf("%s: %q", message, err)) eh(fmt.Sprintf("%s: %q", message, err))
} }
} }

Loading…
Cancel
Save