mirror of
https://github.com/helm/helm.git
synced 2026-08-05 23:51:03 +00:00
add filtering mechanism to storage and embed Driver directly into Storage
This commit is contained in:
51
pkg/storage/filter.go
Normal file
51
pkg/storage/filter.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package storage
|
||||
|
||||
import rspb "k8s.io/helm/pkg/proto/hapi/release"
|
||||
|
||||
// FilterFunc returns true if the release object satisfies
|
||||
// the predicate of the underlying func.
|
||||
type FilterFunc func(*rspb.Release) bool
|
||||
|
||||
// Check applies the FilterFunc to the release object.
|
||||
func (fn FilterFunc) Check(rls *rspb.Release) bool {
|
||||
if rls == nil {
|
||||
return false
|
||||
}
|
||||
return fn(rls)
|
||||
}
|
||||
|
||||
// Any returns a FilterFunc that filters a list of releases
|
||||
// determined by the predicate 'f0 || f1 || ... || fn'.
|
||||
func Any(filters ...FilterFunc) FilterFunc {
|
||||
return func(rls *rspb.Release) bool {
|
||||
for _, filter := range filters {
|
||||
if filter(rls) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// All returns a FilterFunc that filters a list of releases
|
||||
// determined by the predicate 'f0 && f1 && ... && fn'.
|
||||
func All(filters ...FilterFunc) FilterFunc {
|
||||
return func(rls *rspb.Release) bool {
|
||||
for _, filter := range filters {
|
||||
if !filter(rls) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// StatusFilter filters a set of releases by status code.
|
||||
func StatusFilter(status rspb.Status_Code) FilterFunc {
|
||||
return FilterFunc(func(rls *rspb.Release) bool {
|
||||
if rls == nil {
|
||||
return true
|
||||
}
|
||||
return rls.GetInfo().GetStatus().Code == status
|
||||
})
|
||||
}
|
||||
@@ -21,28 +21,76 @@ import (
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
drv driver.Driver
|
||||
driver.Driver
|
||||
}
|
||||
|
||||
func (st *Storage) StoreRelease(rls *rspb.Release) error {
|
||||
return st.drv.Create(rls)
|
||||
// Create creates a new storage entry holding the release.
|
||||
// An error is returned if storage failed to store the release.
|
||||
//
|
||||
// TODO: Is marking the release as deployed the only task here?
|
||||
// What happens if an identical release already exists?
|
||||
func (s *Storage) Create(rls *rspb.Release) error {
|
||||
return s.Driver.Create(rls)
|
||||
}
|
||||
|
||||
func (st *Storage) UpdateRelease(rls *rspb.Release) error {
|
||||
return st.drv.Update(rls)
|
||||
// Update update the release in storage. An error is returned if the
|
||||
// storage backend fails to update the release or if the release does not exist.
|
||||
//
|
||||
// TODO: Fetch most recent deployed release, if it exists, mark
|
||||
// as SUPERSEDED, then store both new and old.
|
||||
func (s *Storage) Update(rls *rspb.Release) error {
|
||||
return s.Driver.Update(rls)
|
||||
}
|
||||
|
||||
func (st *Storage) QueryRelease(key string) (*rspb.Release, error) {
|
||||
return st.drv.Get(key)
|
||||
// Delete deletes the release from storage. An error is returned if the
|
||||
// storage backend fails to delete the release or if the release does not exist.
|
||||
//
|
||||
// TODO: The release status should be modified to reflect the DELETED status.
|
||||
func (s *Storage) Delete(key string) (*rspb.Release, error) {
|
||||
return s.Driver.Delete(key)
|
||||
}
|
||||
|
||||
func (st *Storage) DeleteRelease(key string) (*rspb.Release, error) {
|
||||
return st.drv.Delete(key)
|
||||
// ListReleases returns all releases from storage. An error is returned if the
|
||||
// storage backend fails to retrieve the releases.
|
||||
func (s *Storage) ListReleases() ([]*rspb.Release, error) {
|
||||
return s.Driver.List(func(_ *rspb.Release) bool { return true })
|
||||
}
|
||||
|
||||
func Init(drv driver.Driver) *Storage {
|
||||
if drv == nil {
|
||||
drv = driver.NewMemory()
|
||||
}
|
||||
return &Storage{drv: drv}
|
||||
// ListDeleted returns all releases with Status == DELETED. An error is returned
|
||||
// if the storage backend fails to retrieve the releases.
|
||||
func (s *Storage) ListDeleted() ([]*rspb.Release, error) {
|
||||
return s.Driver.List(func(rls *rspb.Release) bool {
|
||||
return StatusFilter(rspb.Status_DELETED).Check(rls)
|
||||
})
|
||||
}
|
||||
|
||||
// ListDeployed returns all releases with Status == DEPLOYED. An error is returned
|
||||
// if the storage backend fails to retrieve the releases.
|
||||
func (s *Storage) ListDeployed() ([]*rspb.Release, error) {
|
||||
return s.Driver.List(func(rls *rspb.Release) bool {
|
||||
return StatusFilter(rspb.Status_DEPLOYED).Check(rls)
|
||||
})
|
||||
}
|
||||
|
||||
// ListFilterAll returns the set of releases satisfying satisfying the predicate
|
||||
// (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results
|
||||
// if and only if all filters return true.
|
||||
func (s *Storage) ListFilterAll(filters ...FilterFunc) ([]*rspb.Release, error) {
|
||||
return s.Driver.List(func(rls *rspb.Release) bool {
|
||||
return All(filters...).Check(rls)
|
||||
})
|
||||
}
|
||||
|
||||
// ListFilterAny returns the set of releases satisfying satisfying the predicate
|
||||
// (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results
|
||||
// if at least one of the filters returns true.
|
||||
func (s *Storage) ListFilterAny(filters ...FilterFunc) ([]*rspb.Release, error) {
|
||||
return s.Driver.List(func(rls *rspb.Release) bool {
|
||||
return Any(filters...).Check(rls)
|
||||
})
|
||||
}
|
||||
|
||||
func Init(d driver.Driver) *Storage {
|
||||
if d == nil { d = driver.NewMemory() }
|
||||
return &Storage{Driver: d}
|
||||
}
|
||||
@@ -19,16 +19,136 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
rspb "k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/storage/driver"
|
||||
|
||||
tspb "github.com/golang/protobuf/ptypes"
|
||||
)
|
||||
|
||||
var storage = Init(driver.NewMemory())
|
||||
|
||||
func TestStorageCreate(t *testing.T) {
|
||||
// create fake release
|
||||
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease()
|
||||
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
|
||||
|
||||
// fetch the release
|
||||
res, err := storage.Get(rls.Name)
|
||||
assertErrNil(t.Fatal, err, "QueryRelease")
|
||||
|
||||
// verify the fetched and created release are the same
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageUpdate(t *testing.T) {
|
||||
// create fake release
|
||||
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease()
|
||||
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
|
||||
|
||||
// modify the release
|
||||
rls.Version = 2
|
||||
rls.Manifest = "new-manifest"
|
||||
assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease")
|
||||
|
||||
// retrieve the updated release
|
||||
res, err := storage.Get(rls.Name)
|
||||
assertErrNil(t.Fatal, err, "QueryRelease")
|
||||
|
||||
// verify updated and fetched releases are the same.
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageDelete(t *testing.T) {
|
||||
// create fake release
|
||||
rls := ReleaseTestData{Name: "angry-beaver"}.ToRelease()
|
||||
assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease")
|
||||
|
||||
// delete the release
|
||||
res, err := storage.Delete(rls.Name)
|
||||
assertErrNil(t.Fatal, err, "DeleteRelease")
|
||||
|
||||
// verify updated and fetched releases are the same.
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageList(t *testing.T) {
|
||||
// setup storage with test releases
|
||||
setup := func() {
|
||||
// release records
|
||||
rls0 := ReleaseTestData{Name: "happy-catdog", 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()
|
||||
rls3 := ReleaseTestData{Name: "hungry-hippo", 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()
|
||||
rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.Status_DELETED}.ToRelease()
|
||||
|
||||
// create the release records in the storage
|
||||
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'rls1'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'rls2'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'rls3'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls4), "Storing release 'rls4'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'rls5'")
|
||||
assertErrNil(t.Fatal, storage.Create(rls6), "Storing release 'rls6'")
|
||||
}
|
||||
|
||||
var listTests = []struct{
|
||||
Description string
|
||||
NumExpected int
|
||||
ListFunc func() ([]*rspb.Release,error)
|
||||
}{
|
||||
{"ListDeleted", 2, storage.ListDeleted},
|
||||
{"ListDeployed", 2, storage.ListDeployed},
|
||||
{"ListReleases", 7, storage.ListReleases},
|
||||
}
|
||||
|
||||
setup()
|
||||
|
||||
for _, tt := range listTests {
|
||||
list, err := tt.ListFunc()
|
||||
assertErrNil(t.Fatal, err, tt.Description)
|
||||
// verify the count of releases returned
|
||||
if len(list) != tt.NumExpected {
|
||||
t.Errorf("ListReleases(%s): expected %d, actual %d",
|
||||
tt.Description,
|
||||
tt.NumExpected,
|
||||
len(list))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ReleaseTestData struct {
|
||||
Name string
|
||||
Version int32
|
||||
Manifest string
|
||||
Namespace string
|
||||
Status rspb.Status_Code
|
||||
}
|
||||
|
||||
func (test ReleaseTestData) ToRelease() *rspb.Release {
|
||||
return &rspb.Release{
|
||||
Name: test.Name,
|
||||
Version: test.Version,
|
||||
Manifest: test.Manifest,
|
||||
Namespace: test.Namespace,
|
||||
Info: &rspb.Info{Status: &rspb.Status{Code: test.Status}},
|
||||
}
|
||||
}
|
||||
|
||||
func assertErrNil(eh func(args ...interface{}), err error, message string) {
|
||||
if err != nil {
|
||||
eh(fmt.Sprintf("%s: %q", message, err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
func releaseData() *rspb.Release {
|
||||
var manifest = `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
@@ -54,88 +174,4 @@ func releaseData() *rspb.Release {
|
||||
Namespace: "kube-system",
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreRelease(t *testing.T) {
|
||||
ckerr := func(err error, msg string) {
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("Failed to %s: %q", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
rls := releaseData()
|
||||
ckerr(storage.StoreRelease(rls), "StoreRelease")
|
||||
|
||||
res, err := storage.QueryRelease(rls.Name)
|
||||
ckerr(err, "QueryRelease")
|
||||
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryRelease(t *testing.T) {
|
||||
ckerr := func(err error, msg string) {
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("Failed to %s: %q", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
rls := releaseData()
|
||||
ckerr(storage.StoreRelease(rls), "StoreRelease")
|
||||
|
||||
res, err := storage.QueryRelease(rls.Name)
|
||||
ckerr(err, "QueryRelease")
|
||||
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRelease(t *testing.T) {
|
||||
ckerr := func(err error, msg string) {
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("Failed to %s: %q", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
rls := releaseData()
|
||||
ckerr(storage.StoreRelease(rls), "StoreRelease")
|
||||
|
||||
res, err := storage.DeleteRelease(rls.Name)
|
||||
ckerr(err, "DeleteRelease")
|
||||
|
||||
if !reflect.DeepEqual(rls, res) {
|
||||
t.Fatalf("Expected %q, got %q", rls, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRelease(t *testing.T) {
|
||||
ckeql := func(got, want interface{}, msg string) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf(fmt.Sprintf("%s: got %T, want %T", msg, got, want))
|
||||
}
|
||||
}
|
||||
|
||||
ckerr := func(err error, msg string) {
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("Failed to %s: %q", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
rls := releaseData()
|
||||
ckerr(storage.StoreRelease(rls), "StoreRelease")
|
||||
|
||||
rls.Name = "hungry-hippo"
|
||||
rls.Version = 2
|
||||
rls.Manifest = "old-manifest"
|
||||
|
||||
err := storage.UpdateRelease(rls)
|
||||
ckerr(err, "UpdateRelease")
|
||||
|
||||
res, err := storage.QueryRelease(rls.Name)
|
||||
ckerr(err, "QueryRelease")
|
||||
ckeql(res, rls, "Expected Release")
|
||||
ckeql(res.Name, rls.Name, "Expected Name")
|
||||
ckeql(res.Version, rls.Version, "Expected Version")
|
||||
ckeql(res.Manifest, rls.Manifest, "Expected Manifest")
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user