From 6dd5c9783e02db034aeaa4e2fab8a2d8c18943ee Mon Sep 17 00:00:00 2001 From: fibonacci1729 Date: Sun, 31 Jul 2016 23:21:57 -0600 Subject: [PATCH] add driver package to encapsulate storage backends --- pkg/storage/driver/driver.go | 53 +++++++++++++++ pkg/storage/driver/memory.go | 106 ++++++++++++++++++++++++++++++ pkg/storage/driver/memory_test.go | 92 ++++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 pkg/storage/driver/driver.go create mode 100644 pkg/storage/driver/memory.go create mode 100644 pkg/storage/driver/memory_test.go diff --git a/pkg/storage/driver/driver.go b/pkg/storage/driver/driver.go new file mode 100644 index 000000000..b6e2de5e7 --- /dev/null +++ b/pkg/storage/driver/driver.go @@ -0,0 +1,53 @@ +/* +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 ( + "errors" + rspb "k8s.io/helm/pkg/proto/hapi/release" +) + +var ( + // ErrReleaseNotFound indicates that a release is not found. + ErrReleaseNotFound = errors.New("release not found") + // Temporary error while WIP. + ErrNotImplemented = errors.New("not implemented") +) + +type Creator interface { + Create(*rspb.Release) error +} + +type Updator interface { + Update(*rspb.Release) error +} + +type Deletor interface { + Delete(string) (*rspb.Release, error) +} + +type Queryor interface { + Get(string) (*rspb.Release, error) + + All(string, ...interface{}) ([]*rspb.Release, error) +} + +type Driver interface { + Creator + Updator + Deletor + Queryor +} \ No newline at end of file diff --git a/pkg/storage/driver/memory.go b/pkg/storage/driver/memory.go new file mode 100644 index 000000000..8c5394106 --- /dev/null +++ b/pkg/storage/driver/memory.go @@ -0,0 +1,106 @@ +/* +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 ( + "sync" + + rspb "k8s.io/helm/pkg/proto/hapi/release" +) + +// Memory is an in-memory storage driver implementation. +type Memory struct { + sync.RWMutex + cache map[string]*rspb.Release +} + +func NewMemory() *Memory { + return &Memory{cache: map[string]*rspb.Release{}} +} + +// Get returns the release named by key. +func (mem *Memory) Get(key string) (*rspb.Release, error) { + defer unlock(mem.rlock()) + + if rls, ok := mem.cache[key]; ok { + return rls, nil + } + return nil, ErrReleaseNotFound +} + +// All returns all releases whose status is not Status_DELETED. +func (mem *Memory) All(key string, opts ...interface{}) ([]*rspb.Release, error) { + defer unlock(mem.rlock()) + + var releases []*rspb.Release + for _, rls := range mem.cache { + if rls.Info.Status.Code != rspb.Status_DELETED { + releases = append(releases, rls) + } + } + return releases, nil +} + +// Create creates a new release or error. +func (mem *Memory) Create(rls *rspb.Release) error { + defer unlock(mem.wlock()) + + mem.cache[rls.Name] = rls + return nil +} + +// Update updates a release or error. +func (mem *Memory) Update(rls *rspb.Release) error { + defer unlock(mem.wlock()) + + if old, ok := mem.cache[rls.Name]; ok { + // FIXME: when release update is complete, old release should + // be marked as superseded, creating the new release. + _ = old + + mem.cache[rls.Name] = rls + return nil + } + return ErrReleaseNotFound +} + +// Delete deletes a release or error. +func (mem *Memory) Delete(key string) (*rspb.Release, error) { + defer unlock(mem.wlock()) + + if old, ok := mem.cache[key]; ok { + old.Info.Status.Code = rspb.Status_DELETED + delete(mem.cache, key) + return old, nil + } + return nil, ErrReleaseNotFound +} + +func (mem *Memory) wlock() func() { + mem.Lock() + return func() { + mem.Unlock() + } +} + +func (mem *Memory) rlock() func() { + mem.RLock() + return func() { + mem.RUnlock() + } +} + +func unlock(fn func()) { fn() } \ No newline at end of file diff --git a/pkg/storage/driver/memory_test.go b/pkg/storage/driver/memory_test.go new file mode 100644 index 000000000..001718e10 --- /dev/null +++ b/pkg/storage/driver/memory_test.go @@ -0,0 +1,92 @@ +/* +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 ( + "testing" + rspb "k8s.io/helm/pkg/proto/hapi/release" +) + +func TestMemoryGet(t *testing.T) { + key := "test-1" + rls := &rspb.Release{Name: key} + + mem := NewMemory() + mem.Create(rls) + + res, err := mem.Get(key) + switch { + case err != nil: + t.Errorf("Could not get %s: %s", key, err) + case res.Name != key: + t.Errorf("Expected %s, got %s", key, res.Name) + } +} + +func TestMemoryAll(t *testing.T) { + t.Skip("MemoryAll") +} + +func TestMemoryCreate(t *testing.T) { + key := "test-1" + rls := &rspb.Release{Name: key} + + mem := NewMemory() + err := mem.Create(rls) + + switch { + case err != nil: + t.Fatalf("Failed create: %s", err) + case mem.cache[key].Name != key: + t.Errorf("Unexpected release name: %s", mem.cache[key].Name) + } +} + +func TestMemoryUpdate(t *testing.T) { + key := "test-1" + rls := &rspb.Release{Name: key} + + mem := NewMemory() + if err := mem.Create(rls); err != nil { + t.Fatalf("Failed create: %s", err) + } + if err := mem.Update(rls); err != nil { + t.Fatalf("Failed update: %s", err) + } + if mem.cache[key].Name != key { + t.Errorf("Unexpected release name: %s", mem.cache[key].Name) + } +} + +func TestMemoryDelete(t *testing.T) { + key := "test-1" + rls := &rspb.Release{Name: key} + + mem := NewMemory() + if err := mem.Create(rls); err != nil { + t.Fatalf("Failed create: %s", err) + } + + res, err := mem.Delete(key) + switch { + case err != nil: + t.Fatalf("Failed delete: %s", err) + case mem.cache[key] != nil: + t.Errorf("Expected nil, got %s", mem.cache[key]) + case res.Info.Status.Code != release.Status_DELETED: + t.Errorf("Expected Status_DELETED, got %s", res.Info.Status.Code) + } +} \ No newline at end of file