From fa6a33c349ac7ec30d629ff0ad54f64386b871f5 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 13 Apr 2016 12:54:57 -0600 Subject: [PATCH] feat(storage): add basic implementation of storage This is an in-memory storage layer for storing releases. This will be superseded by the Kubernetes ConfigMap implementtion. --- cmd/tiller/environment/environment.go | 12 ++++ cmd/tiller/environment/environment_test.go | 8 +++ pkg/engine/engine.go | 3 + pkg/storage/doc.go | 7 +++ pkg/storage/memory.go | 53 ++++++++++++++++ pkg/storage/memory_test.go | 70 ++++++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 pkg/storage/doc.go create mode 100644 pkg/storage/memory.go create mode 100644 pkg/storage/memory_test.go diff --git a/cmd/tiller/environment/environment.go b/cmd/tiller/environment/environment.go index 61305e6f7..1845d83c7 100644 --- a/cmd/tiller/environment/environment.go +++ b/cmd/tiller/environment/environment.go @@ -12,6 +12,10 @@ var DefaultEngine = GoTplEngine // EngineYard maps engine names to engine implementations. type EngineYard map[string]Engine +// Get retrieves a template engine by name. +// +// If no matching template engine is found, the second return value will +// be false. func (y EngineYard) Get(k string) (Engine, bool) { e, ok := y[k] return e, ok @@ -49,8 +53,16 @@ type Engine interface { // // Release storage must be concurrency safe. type ReleaseStorage interface { + // Get takes a name and returns the accompanying release. Get(key string) (*hapi.Release, error) + // Set saves the release with the given name. Set(key string, val *hapi.Release) error + // List lists all active (non-deleted, non-superseded) releases. + // + // To get deleted or superseded releases, use Query. + List() ([]*hapi.Release, error) + // Query takes a map of labels and returns any releases that match. + Query(map[string]string) ([]*hapi.Release, error) } // KubeClient represents a client capable of communicating with the Kubernetes API. diff --git a/cmd/tiller/environment/environment_test.go b/cmd/tiller/environment/environment_test.go index e13f4def1..c1d037423 100644 --- a/cmd/tiller/environment/environment_test.go +++ b/cmd/tiller/environment/environment_test.go @@ -27,6 +27,14 @@ func (r *mockReleaseStorage) Set(k string, v *hapi.Release) error { return nil } +func (r *mockReleaseStorage) List() ([]*hapi.Release, error) { + return []*hapi.Release{}, nil +} + +func (r *mockReleaseStorage) Query(labels map[string]string) ([]*hapi.Release, error) { + return []*hapi.Release{}, nil +} + type mockKubeClient struct { } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 067d6b98a..59a42f96f 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -9,7 +9,10 @@ import ( "github.com/deis/tiller/pkg/hapi" ) +// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates. type Engine struct { + // FuncMap contains the template functions that will be passed to each + // render call. This may only be modified before the first call to Render. FuncMap template.FuncMap } diff --git a/pkg/storage/doc.go b/pkg/storage/doc.go new file mode 100644 index 000000000..21e11ade4 --- /dev/null +++ b/pkg/storage/doc.go @@ -0,0 +1,7 @@ +/*Package storage implements storage for Tiller objects. + +Tiller stores releases (see 'cmd/tiller/environment'.Environment). The backend +storage mechanism may be implemented with different backends. This package +and its subpackages provide storage layers for Tiller objects. +*/ +package storage diff --git a/pkg/storage/memory.go b/pkg/storage/memory.go new file mode 100644 index 000000000..a3670426a --- /dev/null +++ b/pkg/storage/memory.go @@ -0,0 +1,53 @@ +package storage + +import ( + "errors" + + "github.com/deis/tiller/pkg/hapi" +) + +// Memory is an in-memory ReleaseStorage implementation. +type Memory struct { + releases map[string]*hapi.Release +} + +func NewMemory() *Memory { + return &Memory{ + releases: map[string]*hapi.Release{}, + } +} + +var ErrNotFound = errors.New("release not found") + +// Get returns the named Release. +// +// If the release is not found, an ErrNotFound error is returned. +func (m *Memory) Get(k string) (*hapi.Release, error) { + v, ok := m.releases[k] + if !ok { + return v, ErrNotFound + } + return v, nil +} + +// Set sets a release. +// +// TODO: Is there any reason why Set doesn't just use the release name? +func (m *Memory) Set(k string, rel *hapi.Release) error { + m.releases[k] = rel + return nil +} + +// List returns all releases +func (m *Memory) List() ([]*hapi.Release, error) { + buf := make([]*hapi.Release, len(m.releases)) + i := 0 + for _, v := range m.releases { + buf[i] = v + i++ + } + return buf, nil +} +func (m *Memory) Query(labels map[string]string) ([]*hapi.Release, error) { + return []*hapi.Release{}, errors.New("Cannot implement until hapi.Release is defined.") +} diff --git a/pkg/storage/memory_test.go b/pkg/storage/memory_test.go new file mode 100644 index 000000000..7b619b5b0 --- /dev/null +++ b/pkg/storage/memory_test.go @@ -0,0 +1,70 @@ +package storage + +import ( + "testing" + + "github.com/deis/tiller/pkg/hapi" +) + +func TestSet(t *testing.T) { + k := "test-1" + r := &hapi.Release{Name: k} + + ms := NewMemory() + if err := ms.Set(k, r); err != nil { + t.Fatalf("Failed set: %s", err) + } + + if ms.releases[k].Name != k { + t.Errorf("Unexpected release name: %s", ms.releases[k].Name) + } +} + +func TestGet(t *testing.T) { + k := "test-1" + r := &hapi.Release{Name: k} + + ms := NewMemory() + ms.Set(k, r) + + if out, err := ms.Get(k); err != nil { + t.Errorf("Could not get %s: %s", k, err) + } else if out.Name != k { + t.Errorf("Expected %s, got %s", k, out.Name) + } +} + +func TestList(t *testing.T) { + ms := NewMemory() + rels := []string{"a", "b", "c"} + + for _, k := range rels { + ms.Set(k, &hapi.Release{Name: k}) + } + + l, err := ms.List() + if err != nil { + t.Error(err) + } + + if len(l) != 3 { + t.Errorf("Expected 3, got %d", len(l)) + } + + for _, n := range rels { + foundN := false + for _, rr := range l { + if rr.Name == n { + foundN = true + break + } + } + if !foundN { + t.Errorf("Did not find %s in list.", n) + } + } +} + +func TestQuery(t *testing.T) { + t.Skip("Not Implemented") +}