feat(test): add ability to store releases in multiple namespaces in the memory storage driver

this is needed to test things like listing releases in multiple namespaces

Signed-off-by: Karuppiah Natarajan <karuppiah7890@gmail.com>
pull/6684/head
Karuppiah Natarajan 6 years ago
parent e7413bd61c
commit fd89e20d55
No known key found for this signature in database
GPG Key ID: C674A28337662A96

@ -88,7 +88,7 @@ func runTestActionCmd(t *testing.T, tests []cmdTestCase) {
} }
func storageFixture() *storage.Storage { func storageFixture() *storage.Storage {
return storage.Init(driver.NewMemory()) return storage.Init(driver.NewMemory("default"))
} }
func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) { func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {

@ -236,7 +236,7 @@ func (c *Configuration) Init(envSettings *cli.EnvSettings, allNamespaces bool, h
d.Log = log d.Log = log
store = storage.Init(d) store = storage.Init(d)
case "memory": case "memory":
d := driver.NewMemory() d := driver.NewMemory(namespace)
store = storage.Init(d) store = storage.Init(d)
default: default:
// Not sure what to do here. // Not sure what to do here.

@ -77,7 +77,7 @@ func actionConfigFixture(t *testing.T) *Configuration {
} }
return &Configuration{ return &Configuration{
Releases: storage.Init(driver.NewMemory()), Releases: storage.Init(driver.NewMemory("default")),
KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}}, KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}},
Capabilities: chartutil.DefaultCapabilities, Capabilities: chartutil.DefaultCapabilities,
RegistryClient: registryClient, RegistryClient: registryClient,

@ -179,7 +179,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
i.cfg.Capabilities = chartutil.DefaultCapabilities i.cfg.Capabilities = chartutil.DefaultCapabilities
i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...) i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...)
i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard} i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}
i.cfg.Releases = storage.Init(driver.NewMemory()) i.cfg.Releases = storage.Init(driver.NewMemory(i.Namespace))
} else if !i.ClientOnly && len(i.APIVersions) > 0 { } else if !i.ClientOnly && len(i.APIVersions) > 0 {
i.cfg.Log("API Version list given outside of client only mode, this list will be ignored") i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
} }

@ -17,6 +17,7 @@ limitations under the License.
package driver package driver
import ( import (
"github.com/pkg/errors"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -29,15 +30,53 @@ var _ Driver = (*Memory)(nil)
// MemoryDriverName is the string name of this driver. // MemoryDriverName is the string name of this driver.
const MemoryDriverName = "Memory" const MemoryDriverName = "Memory"
var (
// ErrNamespaceNotFound indicates that a namespace is not found.
ErrNamespaceNotFound = errors.New("namespace not found")
// ErrNamespaceAlreadyExists indicates that a namespace already exists.
ErrNamespaceAlreadyExists = errors.New("namespace already exists")
)
// 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 allNamespacesCache map[string]map[string]records
namespace string
} }
// NewMemory initializes a new memory driver. // NewMemory initializes a new memory driver.
func NewMemory() *Memory { func NewMemory(namespace string) *Memory {
return &Memory{cache: map[string]records{}} memory := &Memory{
allNamespacesCache: map[string]map[string]records{},
namespace: namespace,
}
if namespace != "" {
_ = memory.CreateNamespace(namespace)
}
return memory
}
// SetNamespace sets the current namespace of the memory driver
func (mem *Memory) SetNamespace(namespace string) {
defer unlock(mem.wlock())
mem.namespace = namespace
}
// GetNamespace returns the current namespace of the memory driver
func (mem *Memory) GetNamespace() string {
defer unlock(mem.rlock())
return mem.namespace
}
// Namespace creates a namespace for the memory driver
func (mem *Memory) CreateNamespace(namespace string) error {
_, exists := mem.allNamespacesCache[namespace]
if exists {
return ErrNamespaceAlreadyExists
}
mem.allNamespacesCache[namespace] = make(map[string]records)
return nil
} }
// Name returns the name of the driver. // Name returns the name of the driver.
@ -56,7 +95,11 @@ func (mem *Memory) Get(key string) (*rspb.Release, error) {
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 { cache, exists := mem.allNamespacesCache[mem.namespace]
if !exists {
return nil, ErrNamespaceNotFound
}
if recs, ok := 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
} }
@ -72,7 +115,9 @@ 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 namespace, cache := range mem.allNamespacesCache {
if mem.namespace == namespace || mem.namespace == "" {
for _, recs := range 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)
@ -80,6 +125,8 @@ func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error
return true return true
}) })
} }
}
}
return ls, nil return ls, nil
} }
@ -93,7 +140,9 @@ func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
lbs.fromMap(keyvals) lbs.fromMap(keyvals)
var ls []*rspb.Release var ls []*rspb.Release
for _, recs := range mem.cache { for namespace, cache := range mem.allNamespacesCache {
if mem.namespace == namespace || mem.namespace == "" {
for _, recs := range cache {
recs.Iter(func(_ int, rec *record) bool { recs.Iter(func(_ int, rec *record) bool {
// A query for a release name that doesn't exist (has been deleted) // A query for a release name that doesn't exist (has been deleted)
// can cause rec to be nil. // can cause rec to be nil.
@ -106,6 +155,8 @@ func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
return true return true
}) })
} }
}
}
return ls, nil return ls, nil
} }
@ -113,14 +164,18 @@ func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
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 { cache, exists := mem.allNamespacesCache[mem.namespace]
if !exists {
return ErrNamespaceNotFound
}
if recs, ok := 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 cache[rls.Name] = recs
return nil return nil
} }
mem.cache[rls.Name] = records{newRecord(key, rls)} cache[rls.Name] = records{newRecord(key, rls)}
return nil return nil
} }
@ -128,7 +183,11 @@ func (mem *Memory) Create(key string, rls *rspb.Release) error {
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) { cache, exists := mem.allNamespacesCache[mem.namespace]
if !exists {
return ErrNamespaceNotFound
}
if rs, ok := cache[rls.Name]; ok && rs.Exists(key) {
rs.Replace(key, newRecord(key, rls)) rs.Replace(key, newRecord(key, rls))
return nil return nil
} }
@ -146,14 +205,19 @@ func (mem *Memory) Delete(key string) (*rspb.Release, error) {
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
cache, exists := mem.allNamespacesCache[mem.namespace]
if !exists {
return nil, ErrNamespaceNotFound
}
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 := cache[name]; ok {
if r := recs.Remove(key); r != nil { if r := recs.Remove(key); r != nil {
// recs.Remove changes the slice reference, so we have to re-assign it. // recs.Remove changes the slice reference, so we have to re-assign it.
mem.cache[name] = recs cache[name] = recs
return r.rls, nil return r.rls, nil
} }
} }

@ -25,7 +25,7 @@ import (
) )
func TestMemoryName(t *testing.T) { func TestMemoryName(t *testing.T) {
if mem := NewMemory(); mem.Name() != MemoryDriverName { if mem := NewMemory("default"); 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())
} }
} }
@ -33,23 +33,39 @@ func TestMemoryName(t *testing.T) {
func TestMemoryCreate(t *testing.T) { func TestMemoryCreate(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
namespace string
rls *rspb.Release rls *rspb.Release
err bool err bool
}{ }{
{ {
"create should success", "create should succeed in default namespace",
"default",
releaseStub("rls-c", 1, "default", rspb.StatusDeployed), releaseStub("rls-c", 1, "default", rspb.StatusDeployed),
false, false,
}, },
{ {
"create should fail (release already exists)", "create should fail (release already exists)",
"default",
releaseStub("rls-a", 1, "default", rspb.StatusDeployed), releaseStub("rls-a", 1, "default", rspb.StatusDeployed),
true, true,
}, },
{
"create should succeed in testing namespace",
"testing",
releaseStub("rls-c", 1, "default", rspb.StatusDeployed),
false,
},
{
"create should fail (release already exists) in testing namespace",
"testing",
releaseStub("rls-c", 1, "default", rspb.StatusDeployed),
true,
},
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
ts.SetNamespace(tt.namespace)
key := testKey(tt.rls.Name, tt.rls.Version) key := testKey(tt.rls.Name, tt.rls.Version)
rls := tt.rls rls := tt.rls
@ -62,17 +78,21 @@ func TestMemoryCreate(t *testing.T) {
} }
func TestMemoryGet(t *testing.T) { func TestMemoryGet(t *testing.T) {
ts := tsFixtureMemory(t)
var tests = []struct { var tests = []struct {
desc string desc string
namespace string
key string key string
err bool err bool
}{ }{
{"release key should exist", "rls-a.v1", false}, {"release key should exist in default namespace", "default", "rls-a.v1", false},
{"release key should not exist", "rls-a.v5", true}, {"release key should not exist in default namespace", "default", "rls-a.v5", true},
{"release key should exist in testing namespace", "testing", "rls-a.v1", false},
{"release key should not exist in testing namespace", "testing", "rls-a.v5", true},
} }
ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
ts.SetNamespace(tt.namespace)
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)
@ -82,20 +102,36 @@ func TestMemoryGet(t *testing.T) {
} }
func TestMemoryQuery(t *testing.T) { func TestMemoryQuery(t *testing.T) {
ts := tsFixtureMemory(t)
var tests = []struct { var tests = []struct {
desc string desc string
namespace string
xlen int xlen int
lbs map[string]string lbs map[string]string
}{ }{
{ {
"should be 2 query results", "should be 2 query results for default namespace",
"default",
2, 2,
map[string]string{"status": "deployed"}, map[string]string{"status": "deployed"},
}, },
{
"should be 1 query result for testing namespace",
"testing",
1,
map[string]string{"status": "deployed"},
},
{
"should be 3 query results for all namespaces",
"",
3,
map[string]string{"status": "deployed"},
},
} }
ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
ts.SetNamespace(tt.namespace)
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)
@ -108,28 +144,47 @@ func TestMemoryQuery(t *testing.T) {
} }
func TestMemoryUpdate(t *testing.T) { func TestMemoryUpdate(t *testing.T) {
ts := tsFixtureMemory(t)
var tests = []struct { var tests = []struct {
desc string desc string
namespace string
key string key string
rls *rspb.Release rls *rspb.Release
err bool err bool
}{ }{
{ {
"update release status", "update release status which exists in default namespace",
"default",
"rls-a.v4", "rls-a.v4",
releaseStub("rls-a", 4, "default", rspb.StatusSuperseded), releaseStub("rls-a", 4, "default", rspb.StatusSuperseded),
false, false,
}, },
{
"update release status which exists in testing namespace",
"testing",
"rls-a.v1",
releaseStub("rls-a", 1, "default", rspb.StatusSuperseded),
false,
},
{ {
"update release does not exist", "update release does not exist",
"default",
"rls-z.v1",
releaseStub("rls-z", 1, "default", rspb.StatusUninstalled),
true,
},
{
"update release does not exist",
"testing",
"rls-z.v1", "rls-z.v1",
releaseStub("rls-z", 1, "default", rspb.StatusUninstalled), releaseStub("rls-z", 1, "default", rspb.StatusUninstalled),
true, true,
}, },
} }
ts := tsFixtureMemory(t)
for _, tt := range tests { for _, tt := range tests {
ts.SetNamespace(tt.namespace)
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)
@ -151,20 +206,26 @@ func TestMemoryUpdate(t *testing.T) {
func TestMemoryDelete(t *testing.T) { func TestMemoryDelete(t *testing.T) {
var tests = []struct { var tests = []struct {
desc string desc string
namespace string
key string key string
err bool err bool
}{ }{
{"release key should exist", "rls-a.v1", false}, {"release key should exist in default namespace", "default", "rls-a.v1", false},
{"release key should not exist", "rls-a.v5", true}, {"release key should not exist in default namespace", "default", "rls-a.v5", true},
{"release key should exist in testing namespace", "testing", "rls-a.v1", false},
{"release key should not exist in testing namespace", "testing", "rls-a.v5", true},
} }
ts := tsFixtureMemory(t) ts := tsFixtureMemory(t)
// all namespaces
ts.namespace = ""
start, err := ts.Query(map[string]string{"name": "rls-a"}) start, err := ts.Query(map[string]string{"name": "rls-a"})
if err != nil { if err != nil {
t.Errorf("Query failed: %s", err) t.Errorf("Query failed: %s", err)
} }
startLen := len(start) startLen := len(start)
for _, tt := range tests { for _, tt := range tests {
ts.SetNamespace(tt.namespace)
if rel, err := ts.Delete(tt.key); err != nil { if rel, 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)
@ -179,6 +240,8 @@ func TestMemoryDelete(t *testing.T) {
} }
} }
// all namespaces
ts.namespace = ""
// Make sure that the deleted records are gone. // Make sure that the deleted records are gone.
end, err := ts.Query(map[string]string{"name": "rls-a"}) end, err := ts.Query(map[string]string{"name": "rls-a"})
if err != nil { if err != nil {

@ -55,13 +55,25 @@ func tsFixtureMemory(t *testing.T) *Memory {
releaseStub("rls-b", 2, "default", rspb.StatusSuperseded), releaseStub("rls-b", 2, "default", rspb.StatusSuperseded),
} }
mem := NewMemory() mem := NewMemory("default")
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)
} }
} }
mem.namespace = "testing"
err := mem.CreateNamespace(mem.namespace)
if err != nil {
t.Fatalf("Test setup failed to create: %s\n", err)
}
anotherRls := releaseStub("rls-a", 1, "testing", rspb.StatusDeployed)
err = mem.Create(testKey(anotherRls.Name, anotherRls.Version), anotherRls)
if err != nil {
t.Fatalf("Test setup failed to create: %s\n", err)
}
return mem return mem
} }

@ -227,7 +227,7 @@ func makeKey(rlsname string, version int) string {
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("default")
} }
return &Storage{ return &Storage{
Driver: d, Driver: d,

@ -27,7 +27,7 @@ import (
func TestStorageCreate(t *testing.T) { func TestStorageCreate(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
@ -49,7 +49,7 @@ func TestStorageCreate(t *testing.T) {
func TestStorageUpdate(t *testing.T) { func TestStorageUpdate(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
@ -76,7 +76,7 @@ func TestStorageUpdate(t *testing.T) {
func TestStorageDelete(t *testing.T) { func TestStorageDelete(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
// create fake release // create fake release
rls := ReleaseTestData{ rls := ReleaseTestData{
@ -117,7 +117,7 @@ func TestStorageDelete(t *testing.T) {
func TestStorageList(t *testing.T) { func TestStorageList(t *testing.T) {
// initialize storage // initialize storage
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
// setup storage with test releases // setup storage with test releases
setup := func() { setup := func() {
@ -166,7 +166,7 @@ func TestStorageList(t *testing.T) {
} }
func TestStorageDeployed(t *testing.T) { func TestStorageDeployed(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
const name = "angry-bird" const name = "angry-bird"
const vers = 4 const vers = 4
@ -206,7 +206,7 @@ func TestStorageDeployed(t *testing.T) {
} }
func TestStorageHistory(t *testing.T) { func TestStorageHistory(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
const name = "angry-bird" const name = "angry-bird"
@ -237,7 +237,7 @@ func TestStorageHistory(t *testing.T) {
} }
func TestStorageRemoveLeastRecent(t *testing.T) { func TestStorageRemoveLeastRecent(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
storage.Log = t.Logf storage.Log = t.Logf
// Make sure that specifying this at the outset doesn't cause any bugs. // Make sure that specifying this at the outset doesn't cause any bugs.
@ -294,7 +294,7 @@ func TestStorageRemoveLeastRecent(t *testing.T) {
} }
func TestStorageLast(t *testing.T) { func TestStorageLast(t *testing.T) {
storage := Init(driver.NewMemory()) storage := Init(driver.NewMemory("default"))
const name = "angry-bird" const name = "angry-bird"

Loading…
Cancel
Save