зеркало из https://github.com/Azure/draft-classic.git
basic tests for pkg/storage/kube.ConfigMaps
This commit is contained in:
Родитель
e5cf83eb42
Коммит
1a711c491a
|
@ -0,0 +1,98 @@
|
|||
package kube
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/pkg/api"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"github.com/Azure/draft/pkg/storage"
|
||||
)
|
||||
|
||||
// MockConfigMaps mocks a kubernetes ConfigMapsInterface.
|
||||
//
|
||||
// For use in testing only.
|
||||
type MockConfigMaps struct {
|
||||
corev1.ConfigMapInterface
|
||||
cfgmaps map[string]*v1.ConfigMap
|
||||
}
|
||||
|
||||
// NewConfigMapsWithMocks initializes a new ConfigMaps store initialized
|
||||
// with kubernetes ConfigMap objects created from the provided entries.
|
||||
func NewConfigMapsWithMocks(t *testing.T, entries ...struct {
|
||||
appName string
|
||||
objects []*storage.Object
|
||||
}) *ConfigMaps {
|
||||
var mock MockConfigMaps
|
||||
mock.Init(t, entries...)
|
||||
return NewConfigMaps(&mock)
|
||||
}
|
||||
|
||||
// Init initializes the MockConfigMaps mock with the set of storage objects.
|
||||
func (mock *MockConfigMaps) Init(t *testing.T, entries ...struct {
|
||||
appName string
|
||||
objects []*storage.Object
|
||||
}) {
|
||||
mock.cfgmaps = make(map[string]*v1.ConfigMap)
|
||||
for _, entry := range entries {
|
||||
var cfgmap *v1.ConfigMap
|
||||
for _, object := range entry.objects {
|
||||
if cfgmap != nil {
|
||||
if _, ok := cfgmap.Data[object.BuildID]; !ok {
|
||||
content, err := storage.EncodeToString(object)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to encode storage object: %v", err)
|
||||
}
|
||||
cfgmap.Data[object.BuildID] = content
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if cfgmap, err = newConfigMap(entry.appName, object); err != nil {
|
||||
t.Fatalf("failed to create configmap: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
mock.cfgmaps[entry.appName] = cfgmap
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the ConfigMap by name.
|
||||
func (mock *MockConfigMaps) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) {
|
||||
cfgmap, ok := mock.cfgmaps[name]
|
||||
if !ok {
|
||||
return nil, apierrors.NewNotFound(api.Resource("tests"), name)
|
||||
}
|
||||
return cfgmap, nil
|
||||
}
|
||||
|
||||
// Create creates a new ConfigMap.
|
||||
func (mock *MockConfigMaps) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) {
|
||||
name := cfgmap.ObjectMeta.Name
|
||||
if object, ok := mock.cfgmaps[name]; ok {
|
||||
return object, apierrors.NewAlreadyExists(api.Resource("tests"), name)
|
||||
}
|
||||
mock.cfgmaps[name] = cfgmap
|
||||
return cfgmap, nil
|
||||
}
|
||||
|
||||
// Update updates a ConfigMap.
|
||||
func (mock *MockConfigMaps) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) {
|
||||
name := cfgmap.ObjectMeta.Name
|
||||
if _, ok := mock.cfgmaps[name]; !ok {
|
||||
return nil, apierrors.NewNotFound(api.Resource("tests"), name)
|
||||
}
|
||||
mock.cfgmaps[name] = cfgmap
|
||||
return cfgmap, nil
|
||||
}
|
||||
|
||||
// Delete deletes a ConfigMap by name.
|
||||
func (mock *MockConfigMaps) Delete(name string, opts *metav1.DeleteOptions) error {
|
||||
if _, ok := mock.cfgmaps[name]; !ok {
|
||||
return apierrors.NewNotFound(api.Resource("tests"), name)
|
||||
}
|
||||
delete(mock.cfgmaps, name)
|
||||
return nil
|
||||
}
|
|
@ -6,36 +6,36 @@ import (
|
|||
|
||||
"github.com/Azure/draft/pkg/storage"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8s "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// Store represents a Kubernetes configmap storage engine for a storage.Object .
|
||||
type Store struct {
|
||||
client k8s.Interface
|
||||
namespace string
|
||||
// ConfigMaps represents a Kubernetes configmap storage engine for a storage.Object .
|
||||
type ConfigMaps struct {
|
||||
impl corev1.ConfigMapInterface
|
||||
}
|
||||
|
||||
var _ storage.Store = (*Store)(nil)
|
||||
var _ storage.Store = (*ConfigMaps)(nil)
|
||||
|
||||
func NewStore(c k8s.Interface, namespace string) *Store {
|
||||
return &Store{c, namespace}
|
||||
func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps {
|
||||
return &ConfigMaps{impl}
|
||||
}
|
||||
|
||||
// DeleteBuilds deletes all draft builds for the application specified by appName.
|
||||
func (s *Store) DeleteBuilds(ctx context.Context, appName string) ([]*storage.Object, error) {
|
||||
builds, err := s.GetBuilds(ctx, appName)
|
||||
func (this *ConfigMaps) DeleteBuilds(ctx context.Context, appName string) ([]*storage.Object, error) {
|
||||
builds, err := this.GetBuilds(ctx, appName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = s.client.CoreV1().ConfigMaps(s.namespace).Delete(appName, &metav1.DeleteOptions{})
|
||||
err = this.impl.Delete(appName, &metav1.DeleteOptions{})
|
||||
return builds, err
|
||||
}
|
||||
|
||||
// DeleteBuild deletes the draft build given by buildID for the application specified by appName.
|
||||
func (s *Store) DeleteBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
|
||||
func (this *ConfigMaps) DeleteBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
|
||||
var cfgmap *v1.ConfigMap
|
||||
if cfgmap, err = s.client.CoreV1().ConfigMaps(s.namespace).Get(appName, metav1.GetOptions{}); err != nil {
|
||||
if cfgmap, err = this.impl.Get(appName, metav1.GetOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if build, ok := cfgmap.Data[buildID]; ok {
|
||||
|
@ -43,36 +43,26 @@ func (s *Store) DeleteBuild(ctx context.Context, appName, buildID string) (obj *
|
|||
return nil, err
|
||||
}
|
||||
delete(cfgmap.Data, buildID)
|
||||
_, err = s.client.CoreV1().ConfigMaps(s.namespace).Update(cfgmap)
|
||||
_, err = this.impl.Update(cfgmap)
|
||||
return obj, err
|
||||
}
|
||||
return nil, fmt.Errorf("application %q storage object %q not found", appName, buildID)
|
||||
}
|
||||
|
||||
// CreateBuild stores a draft.Build for the application specified by appName.
|
||||
func (s *Store) CreateBuild(ctx context.Context, appName string, build *storage.Object) error {
|
||||
content, err := storage.EncodeToString(build)
|
||||
func (this *ConfigMaps) CreateBuild(ctx context.Context, appName string, build *storage.Object) error {
|
||||
cfgmap, err := newConfigMap(appName, build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgmap := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Labels: map[string]string{
|
||||
"heritage": "draft",
|
||||
"appname": appName,
|
||||
},
|
||||
},
|
||||
Data: map[string]string{build.BuildID: content},
|
||||
}
|
||||
_, err = s.client.CoreV1().ConfigMaps(s.namespace).Create(cfgmap)
|
||||
_, err = this.impl.Create(cfgmap)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetBuilds returns a slice of builds for the given app name.
|
||||
func (s *Store) GetBuilds(ctx context.Context, appName string) (builds []*storage.Object, err error) {
|
||||
func (this *ConfigMaps) GetBuilds(ctx context.Context, appName string) (builds []*storage.Object, err error) {
|
||||
var cfgmap *v1.ConfigMap
|
||||
if cfgmap, err = s.client.CoreV1().ConfigMaps(s.namespace).Get(appName, metav1.GetOptions{}); err != nil {
|
||||
if cfgmap, err = this.impl.Get(appName, metav1.GetOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, obj := range cfgmap.Data {
|
||||
|
@ -86,9 +76,9 @@ func (s *Store) GetBuilds(ctx context.Context, appName string) (builds []*storag
|
|||
}
|
||||
|
||||
// GetBuild returns the build associated with buildID for the specified app name.
|
||||
func (s *Store) GetBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
|
||||
func (this *ConfigMaps) GetBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
|
||||
var cfgmap *v1.ConfigMap
|
||||
if cfgmap, err = s.client.CoreV1().ConfigMaps(s.namespace).Get(appName, metav1.GetOptions{}); err != nil {
|
||||
if cfgmap, err = this.impl.Get(appName, metav1.GetOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data, ok := cfgmap.Data[buildID]; ok {
|
||||
|
@ -99,3 +89,25 @@ func (s *Store) GetBuild(ctx context.Context, appName, buildID string) (obj *sto
|
|||
}
|
||||
return nil, fmt.Errorf("application %q storage object %q not found", appName, buildID)
|
||||
}
|
||||
|
||||
// newConfigMap constructs a kubernetes ConfigMap object to store a build.
|
||||
//
|
||||
// Each configmap data entry is the base64 encoded string of a *storage.Object
|
||||
// binary protobuf encoding.
|
||||
func newConfigMap(appName string, build *storage.Object) (*v1.ConfigMap, error) {
|
||||
content, err := storage.EncodeToString(build)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfgmap := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Labels: map[string]string{
|
||||
"heritage": "draft",
|
||||
"appname": appName,
|
||||
},
|
||||
},
|
||||
Data: map[string]string{build.BuildID: content},
|
||||
}
|
||||
return cfgmap, nil
|
||||
}
|
||||
|
|
|
@ -1,25 +1,113 @@
|
|||
package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/draft/pkg/storage"
|
||||
)
|
||||
|
||||
func TestStore_DeleteBuilds(t *testing.T) {
|
||||
t.Skip("TestStore_DeleteBuilds: TODO")
|
||||
func TestStoreDeleteBuilds(t *testing.T) {
|
||||
var (
|
||||
store = newMockConfigMapsTestFixture(t)
|
||||
ctx = context.Background()
|
||||
)
|
||||
switch objs, err := store.DeleteBuilds(ctx, "app1"); {
|
||||
case err != nil:
|
||||
t.Fatalf("failed to delete builds: %v", err)
|
||||
case len(objs) != 4:
|
||||
t.Fatalf("expected 4 deleted builds, got %d", len(objs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_DeleteBuild(t *testing.T) {
|
||||
t.Skip("TestStore_DeleteBuild: TODO")
|
||||
func TestStoreDeleteBuild(t *testing.T) {
|
||||
var (
|
||||
store = newMockConfigMapsTestFixture(t)
|
||||
ctx = context.Background()
|
||||
)
|
||||
obj, err := store.DeleteBuild(ctx, "app1", "foo4")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to delete build: %v", err)
|
||||
}
|
||||
assertEqual(t, "DeleteBuild", obj, objectStub("foo4", "bar4", []byte("foobar4")))
|
||||
}
|
||||
|
||||
func TestStore_CreateBuild(t *testing.T) {
|
||||
t.Skip("TestStore_CreateBuild: TODO")
|
||||
func TestStoreCreateBuild(t *testing.T) {
|
||||
var (
|
||||
store = newMockConfigMapsTestFixture(t)
|
||||
ctx = context.Background()
|
||||
)
|
||||
obj := objectStub("foo1", "bar1", []byte("foobar1"))
|
||||
err := store.CreateBuild(ctx, "app2", obj)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to created build: %v", err)
|
||||
}
|
||||
got, err := store.GetBuild(ctx, "app2", "foo1")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get storage object: %v", err)
|
||||
}
|
||||
assertEqual(t, "CreateBuild", got, obj)
|
||||
}
|
||||
|
||||
func TestStore_GetBuilds(t *testing.T) {
|
||||
t.Skip("TestStore_GetBuilds: TODO")
|
||||
func TestStoreGetBuilds(t *testing.T) {
|
||||
var (
|
||||
store = newMockConfigMapsTestFixture(t)
|
||||
ctx = context.Background()
|
||||
)
|
||||
switch got, err := store.GetBuilds(ctx, "app1"); {
|
||||
case err != nil:
|
||||
t.Fatalf("failed to get builds: %v", err)
|
||||
case len(got) != 4:
|
||||
t.Fatalf("expected 4 storage objects, got %d", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_GetBuild(t *testing.T) {
|
||||
t.Skip("TestStore_GetBuild: TODO")
|
||||
func TestStoreGetBuild(t *testing.T) {
|
||||
var (
|
||||
store = newMockConfigMapsTestFixture(t)
|
||||
ctx = context.Background()
|
||||
want = objectStub("foo1", "bar1", []byte("foobar1"))
|
||||
)
|
||||
got, err := store.GetBuild(ctx, "app1", "foo1")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get storage object: %v", err)
|
||||
}
|
||||
assertEqual(t, "GetBuild", got, want)
|
||||
}
|
||||
|
||||
//
|
||||
// test fixtures / helpers
|
||||
//
|
||||
|
||||
func newMockConfigMapsTestFixture(t *testing.T) *ConfigMaps {
|
||||
var mocks = []struct {
|
||||
appName string
|
||||
objects []*storage.Object
|
||||
}{
|
||||
{
|
||||
appName: "app1",
|
||||
objects: []*storage.Object{
|
||||
objectStub("foo1", "bar1", []byte("foobar1")),
|
||||
objectStub("foo2", "bar2", []byte("foobar2")),
|
||||
objectStub("foo3", "bar3", []byte("foobar3")),
|
||||
objectStub("foo4", "bar4", []byte("foobar4")),
|
||||
},
|
||||
},
|
||||
}
|
||||
return NewConfigMapsWithMocks(t, mocks...)
|
||||
}
|
||||
|
||||
func objectStub(buildID, release string, contextID []byte) *storage.Object {
|
||||
return &storage.Object{
|
||||
BuildID: buildID,
|
||||
Release: release,
|
||||
ContextID: contextID,
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, label string, a, b interface{}) {
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
t.Errorf("failed equality for %s", label)
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче