vendor dependencies

This commit is contained in:
Sergiusz Urbaniak 2019-04-24 11:06:03 +02:00
parent 604208ef4f
commit 72abf135d6
1156 changed files with 78178 additions and 105799 deletions

2
vendor/k8s.io/apiserver/pkg/registry/generic/OWNERS generated vendored Executable file → Normal file
View file

@ -1,3 +1,5 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- thockin
- lavalamp

View file

@ -0,0 +1,117 @@
/*
Copyright 2018 The Kubernetes Authors.
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 registry
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/storage"
)
type DryRunnableStorage struct {
Storage storage.Interface
Codec runtime.Codec
}
func (s *DryRunnableStorage) Versioner() storage.Versioner {
return s.Storage.Versioner()
}
func (s *DryRunnableStorage) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64, dryRun bool) error {
if dryRun {
if err := s.Storage.Get(ctx, key, "", out, false); err == nil {
return storage.NewKeyExistsError(key, 0)
}
s.copyInto(obj, out)
return nil
}
return s.Storage.Create(ctx, key, obj, out, ttl)
}
func (s *DryRunnableStorage) Delete(ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, dryRun bool) error {
if dryRun {
if err := s.Storage.Get(ctx, key, "", out, false); err != nil {
return err
}
return preconditions.Check(key, out)
}
return s.Storage.Delete(ctx, key, out, preconditions)
}
func (s *DryRunnableStorage) Watch(ctx context.Context, key string, resourceVersion string, p storage.SelectionPredicate) (watch.Interface, error) {
return s.Storage.Watch(ctx, key, resourceVersion, p)
}
func (s *DryRunnableStorage) WatchList(ctx context.Context, key string, resourceVersion string, p storage.SelectionPredicate) (watch.Interface, error) {
return s.Storage.WatchList(ctx, key, resourceVersion, p)
}
func (s *DryRunnableStorage) Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error {
return s.Storage.Get(ctx, key, resourceVersion, objPtr, ignoreNotFound)
}
func (s *DryRunnableStorage) GetToList(ctx context.Context, key string, resourceVersion string, p storage.SelectionPredicate, listObj runtime.Object) error {
return s.Storage.GetToList(ctx, key, resourceVersion, p, listObj)
}
func (s *DryRunnableStorage) List(ctx context.Context, key string, resourceVersion string, p storage.SelectionPredicate, listObj runtime.Object) error {
return s.Storage.List(ctx, key, resourceVersion, p, listObj)
}
func (s *DryRunnableStorage) GuaranteedUpdate(
ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, dryRun bool, suggestion ...runtime.Object) error {
if dryRun {
err := s.Storage.Get(ctx, key, "", ptrToType, ignoreNotFound)
if err != nil {
return err
}
err = preconditions.Check(key, ptrToType)
if err != nil {
return err
}
rev, err := s.Versioner().ObjectResourceVersion(ptrToType)
out, _, err := tryUpdate(ptrToType, storage.ResponseMeta{ResourceVersion: rev})
if err != nil {
return err
}
s.copyInto(out, ptrToType)
return nil
}
return s.Storage.GuaranteedUpdate(ctx, key, ptrToType, ignoreNotFound, preconditions, tryUpdate, suggestion...)
}
func (s *DryRunnableStorage) Count(key string) (int64, error) {
return s.Storage.Count(key)
}
func (s *DryRunnableStorage) copyInto(in, out runtime.Object) error {
var data []byte
data, err := runtime.Encode(s.Codec, in)
if err != nil {
return err
}
_, _, err = s.Codec.Decode(data, nil, out)
if err != nil {
return err
}
return nil
}

View file

@ -19,11 +19,12 @@ package registry
import (
"sync"
"github.com/golang/glog"
"k8s.io/klog"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
cacherstorage "k8s.io/apiserver/pkg/storage/cacher"
etcdstorage "k8s.io/apiserver/pkg/storage/etcd"
"k8s.io/apiserver/pkg/storage/storagebackend"
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
@ -42,14 +43,14 @@ func StorageWithCacher(capacity int) generic.StorageDecorator {
s, d := generic.NewRawStorage(storageConfig)
if capacity == 0 {
glog.V(5).Infof("Storage caching is disabled for %T", objectType)
klog.V(5).Infof("Storage caching is disabled for %T", objectType)
return s, d
}
glog.V(5).Infof("Storage caching is enabled for %T with capacity %v", objectType, capacity)
klog.V(5).Infof("Storage caching is enabled for %T with capacity %v", objectType, capacity)
// TODO: we would change this later to make storage always have cacher and hide low level KV layer inside.
// Currently it has two layers of same storage interface -- cacher and low level kv.
cacherConfig := storage.CacherConfig{
cacherConfig := cacherstorage.Config{
CacheCapacity: capacity,
Storage: s,
Versioner: etcdstorage.APIObjectVersioner{},
@ -61,7 +62,7 @@ func StorageWithCacher(capacity int) generic.StorageDecorator {
TriggerPublisherFunc: triggerFunc,
Codec: storageConfig.Codec,
}
cacher := storage.NewCacherFromConfig(cacherConfig)
cacher := cacherstorage.NewCacherFromConfig(cacherConfig)
destroyFunc := func() {
cacher.Stop()
d()

View file

@ -45,8 +45,9 @@ import (
"k8s.io/apiserver/pkg/storage"
storeerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/storage/etcd/metrics"
"k8s.io/apiserver/pkg/util/dryrun"
"github.com/golang/glog"
"k8s.io/klog"
)
// ObjectFunc is a function to act on a given object. An error may be returned
@ -172,8 +173,16 @@ type Store struct {
// of items into tabular output. If unset, the default will be used.
TableConvertor rest.TableConvertor
// Storage is the interface for the underlying storage for the resource.
Storage storage.Interface
// Storage is the interface for the underlying storage for the
// resource. It is wrapped into a "DryRunnableStorage" that will
// either pass-through or simply dry-run.
Storage DryRunnableStorage
// StorageVersioner outputs the <group/version/kind> an object will be
// converted to before persisted in etcd, given a list of possible
// kinds of the object.
// If the StorageVersioner is nil, apiserver will leave the
// storageVersionHash as empty in the discovery document.
StorageVersioner runtime.GroupVersioner
// Called to cleanup clients used by the underlying Storage; optional.
DestroyFunc func()
}
@ -304,7 +313,6 @@ func (e *Store) ListPredicate(ctx context.Context, p storage.SelectionPredicate,
// By default we should serve the request from etcd.
options = &metainternalversion.ListOptions{ResourceVersion: ""}
}
p.IncludeUninitialized = options.IncludeUninitialized
p.Limit = options.Limit
p.Continue = options.Continue
list := e.NewListFunc()
@ -322,7 +330,7 @@ func (e *Store) ListPredicate(ctx context.Context, p storage.SelectionPredicate,
}
// Create inserts a new item according to the unique key from the object.
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
return nil, err
}
@ -348,7 +356,7 @@ func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation
return nil, err
}
out := e.NewFunc()
if err := e.Storage.Create(ctx, key, obj, out, ttl); err != nil {
if err := e.Storage.Create(ctx, key, obj, out, ttl, dryrun.IsDryRun(options.DryRun)); err != nil {
err = storeerr.InterpretCreateError(err, qualifiedResource, name)
err = rest.CheckGeneratedNameError(e.CreateStrategy, err, obj)
if !kubeerr.IsAlreadyExists(err) {
@ -377,92 +385,9 @@ func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation
return nil, err
}
}
if !includeUninitialized {
return e.WaitForInitialized(ctx, out)
}
return out, nil
}
// WaitForInitialized holds until the object is initialized, or returns an error if the default limit expires.
// This method is exposed publicly for consumers of generic rest tooling.
func (e *Store) WaitForInitialized(ctx context.Context, obj runtime.Object) (runtime.Object, error) {
// return early if we don't have initializers, or if they've completed already
accessor, err := meta.Accessor(obj)
if err != nil {
return obj, nil
}
initializers := accessor.GetInitializers()
if initializers == nil {
return obj, nil
}
if result := initializers.Result; result != nil {
return nil, kubeerr.FromObject(result)
}
key, err := e.KeyFunc(ctx, accessor.GetName())
if err != nil {
return nil, err
}
qualifiedResource := e.qualifiedResourceFromContext(ctx)
w, err := e.Storage.Watch(ctx, key, accessor.GetResourceVersion(), storage.SelectionPredicate{
Label: labels.Everything(),
Field: fields.Everything(),
IncludeUninitialized: true,
})
if err != nil {
return nil, err
}
defer w.Stop()
latest := obj
ch := w.ResultChan()
for {
select {
case event, ok := <-ch:
if !ok {
msg := fmt.Sprintf("server has timed out waiting for the initialization of %s %s",
qualifiedResource.String(), accessor.GetName())
return nil, kubeerr.NewTimeoutError(msg, 0)
}
switch event.Type {
case watch.Deleted:
if latest = event.Object; latest != nil {
if accessor, err := meta.Accessor(latest); err == nil {
if initializers := accessor.GetInitializers(); initializers != nil && initializers.Result != nil {
// initialization failed, but we missed the modification event
return nil, kubeerr.FromObject(initializers.Result)
}
}
}
return nil, kubeerr.NewInternalError(fmt.Errorf("object deleted while waiting for creation"))
case watch.Error:
if status, ok := event.Object.(*metav1.Status); ok {
return nil, &kubeerr.StatusError{ErrStatus: *status}
}
return nil, kubeerr.NewInternalError(fmt.Errorf("unexpected object in watch stream, can't complete initialization %T", event.Object))
case watch.Modified:
latest = event.Object
accessor, err = meta.Accessor(latest)
if err != nil {
return nil, kubeerr.NewInternalError(fmt.Errorf("object no longer has access to metadata %T: %v", latest, err))
}
initializers := accessor.GetInitializers()
if initializers == nil {
// completed initialization
return latest, nil
}
if result := initializers.Result; result != nil {
// initialization failed
return nil, kubeerr.FromObject(result)
}
}
case <-ctx.Done():
return nil, ctx.Err()
}
}
}
// shouldDeleteDuringUpdate checks if a Update is removing all the object's
// finalizers. If so, it further checks if the object's
// DeletionGracePeriodSeconds is 0.
@ -480,26 +405,12 @@ func (e *Store) shouldDeleteDuringUpdate(ctx context.Context, key string, obj, e
return len(newMeta.GetFinalizers()) == 0 && oldMeta.GetDeletionGracePeriodSeconds() != nil && *oldMeta.GetDeletionGracePeriodSeconds() == 0
}
// shouldDeleteForFailedInitialization returns true if the provided object is initializing and has
// a failure recorded.
func (e *Store) shouldDeleteForFailedInitialization(ctx context.Context, obj runtime.Object) bool {
m, err := meta.Accessor(obj)
if err != nil {
utilruntime.HandleError(err)
return false
}
if initializers := m.GetInitializers(); initializers != nil && initializers.Result != nil {
return true
}
return false
}
// deleteWithoutFinalizers handles deleting an object ignoring its finalizer list.
// Used for objects that are either been finalized or have never initialized.
func (e *Store) deleteWithoutFinalizers(ctx context.Context, name, key string, obj runtime.Object, preconditions *storage.Preconditions) (runtime.Object, bool, error) {
func (e *Store) deleteWithoutFinalizers(ctx context.Context, name, key string, obj runtime.Object, preconditions *storage.Preconditions, dryRun bool) (runtime.Object, bool, error) {
out := e.NewFunc()
glog.V(6).Infof("going to delete %s from registry, triggered by update", name)
if err := e.Storage.Delete(ctx, key, out, preconditions); err != nil {
klog.V(6).Infof("going to delete %s from registry, triggered by update", name)
if err := e.Storage.Delete(ctx, key, out, preconditions, dryRun); err != nil {
// Deletion is racy, i.e., there could be multiple update
// requests to remove all finalizers from the object, so we
// ignore the NotFound error.
@ -522,7 +433,7 @@ func (e *Store) deleteWithoutFinalizers(ctx context.Context, name, key string, o
// Update performs an atomic update and set of the object. Returns the result of the update
// or an error. If the registry allows create-on-update, the create flow will be executed.
// A bool is returned along with the object and any errors, to indicate object creation.
func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc) (runtime.Object, bool, error) {
func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
key, err := e.KeyFunc(ctx, name)
if err != nil {
return nil, false, err
@ -537,6 +448,7 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
storagePreconditions := &storage.Preconditions{}
if preconditions := objInfo.Preconditions(); preconditions != nil {
storagePreconditions.UID = preconditions.UID
storagePreconditions.ResourceVersion = preconditions.ResourceVersion
}
out := e.NewFunc()
@ -564,7 +476,7 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
return nil, nil, err
}
if version == 0 {
if !e.UpdateStrategy.AllowCreateOnUpdate() {
if !e.UpdateStrategy.AllowCreateOnUpdate() && !forceAllowCreate {
return nil, nil, kubeerr.NewNotFound(qualifiedResource, name)
}
creating = true
@ -633,12 +545,12 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
return obj, &ttl, nil
}
return obj, nil, nil
})
}, dryrun.IsDryRun(options.DryRun))
if err != nil {
// delete the object
if err == errEmptiedFinalizers {
return e.deleteWithoutFinalizers(ctx, name, key, deleteObj, storagePreconditions)
return e.deleteWithoutFinalizers(ctx, name, key, deleteObj, storagePreconditions, dryrun.IsDryRun(options.DryRun))
}
if creating {
err = storeerr.InterpretCreateError(err, qualifiedResource, name)
@ -649,10 +561,6 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
return nil, false, err
}
if e.shouldDeleteForFailedInitialization(ctx, out) {
return e.deleteWithoutFinalizers(ctx, name, key, out, storagePreconditions)
}
if creating {
if e.AfterCreate != nil {
if err := e.AfterCreate(out); err != nil {
@ -838,21 +746,25 @@ func deletionFinalizersForGarbageCollection(ctx context.Context, e *Store, acces
}
// markAsDeleting sets the obj's DeletionGracePeriodSeconds to 0, and sets the
// DeletionTimestamp to "now". Finalizers are watching for such updates and will
// DeletionTimestamp to "now" if there is no existing deletionTimestamp or if the existing
// deletionTimestamp is further in future. Finalizers are watching for such updates and will
// finalize the object if their IDs are present in the object's Finalizers list.
func markAsDeleting(obj runtime.Object) (err error) {
func markAsDeleting(obj runtime.Object, now time.Time) (err error) {
objectMeta, kerr := meta.Accessor(obj)
if kerr != nil {
return kerr
}
now := metav1.NewTime(time.Now())
// This handles Generation bump for resources that don't support graceful
// deletion. For resources that support graceful deletion is handle in
// pkg/api/rest/delete.go
if objectMeta.GetDeletionTimestamp() == nil && objectMeta.GetGeneration() > 0 {
objectMeta.SetGeneration(objectMeta.GetGeneration() + 1)
}
objectMeta.SetDeletionTimestamp(&now)
existingDeletionTimestamp := objectMeta.GetDeletionTimestamp()
if existingDeletionTimestamp == nil || existingDeletionTimestamp.After(now) {
metaNow := metav1.NewTime(now)
objectMeta.SetDeletionTimestamp(&metaNow)
}
var zero int64 = 0
objectMeta.SetDeletionGracePeriodSeconds(&zero)
return nil
@ -906,8 +818,8 @@ func (e *Store) updateForGracefulDeletionAndFinalizers(ctx context.Context, name
if !graceful {
// set the DeleteGracePeriods to 0 if the object has pendingFinalizers but not supporting graceful deletion
if pendingFinalizers {
glog.V(6).Infof("update the DeletionTimestamp to \"now\" and GracePeriodSeconds to 0 for object %s, because it has pending finalizers", name)
err = markAsDeleting(existing)
klog.V(6).Infof("update the DeletionTimestamp to \"now\" and GracePeriodSeconds to 0 for object %s, because it has pending finalizers", name)
err = markAsDeleting(existing, time.Now())
if err != nil {
return nil, err
}
@ -919,6 +831,7 @@ func (e *Store) updateForGracefulDeletionAndFinalizers(ctx context.Context, name
lastExisting = existing
return existing, nil
}),
dryrun.IsDryRun(options.DryRun),
)
switch err {
case nil:
@ -967,6 +880,7 @@ func (e *Store) Delete(ctx context.Context, name string, options *metav1.DeleteO
var preconditions storage.Preconditions
if options.Preconditions != nil {
preconditions.UID = options.Preconditions.UID
preconditions.ResourceVersion = options.Preconditions.ResourceVersion
}
graceful, pendingGraceful, err := rest.BeforeDelete(e.DeleteStrategy, ctx, obj, options)
if err != nil {
@ -1000,10 +914,22 @@ func (e *Store) Delete(ctx context.Context, name string, options *metav1.DeleteO
return out, false, err
}
// Going further in this function is not useful when we are
// performing a dry-run request. Worse, it will actually
// override "out" with the version of the object in database
// that doesn't have the finalizer and deletiontimestamp set
// (because the update above was dry-run too). If we already
// have that version available, let's just return it now,
// otherwise, we can call dry-run delete that will get us the
// latest version of the object.
if dryrun.IsDryRun(options.DryRun) && out != nil {
return out, true, nil
}
// delete immediately, or no graceful deletion supported
glog.V(6).Infof("going to delete %s from registry: ", name)
klog.V(6).Infof("going to delete %s from registry: ", name)
out = e.NewFunc()
if err := e.Storage.Delete(ctx, key, out, &preconditions); err != nil {
if err := e.Storage.Delete(ctx, key, out, &preconditions, dryrun.IsDryRun(options.DryRun)); err != nil {
// Please refer to the place where we set ignoreNotFound for the reason
// why we ignore the NotFound error .
if storage.IsNotFound(err) && ignoreNotFound && lastExisting != nil {
@ -1035,11 +961,6 @@ func (e *Store) DeleteCollection(ctx context.Context, options *metav1.DeleteOpti
listOptions = listOptions.DeepCopy()
}
// DeleteCollection must remain backwards compatible with old clients that expect it to
// remove all resources, initialized or not, within the type. It is also consistent with
// Delete which does not require IncludeUninitialized
listOptions.IncludeUninitialized = true
listObj, err := e.List(ctx, listOptions)
if err != nil {
return nil, err
@ -1087,7 +1008,7 @@ func (e *Store) DeleteCollection(ctx context.Context, options *metav1.DeleteOpti
return
}
if _, _, err := e.Delete(ctx, accessor.GetName(), options); err != nil && !kubeerr.IsNotFound(err) {
glog.V(4).Infof("Delete %s in DeleteCollection failed: %v", accessor.GetName(), err)
klog.V(4).Infof("Delete %s in DeleteCollection failed: %v", accessor.GetName(), err)
errs <- err
return
}
@ -1154,7 +1075,6 @@ func (e *Store) Watch(ctx context.Context, options *metainternalversion.ListOpti
resourceVersion := ""
if options != nil {
resourceVersion = options.ResourceVersion
predicate.IncludeUninitialized = options.IncludeUninitialized
}
return e.WatchPredicate(ctx, predicate, resourceVersion)
}
@ -1230,7 +1150,7 @@ func (e *Store) Export(ctx context.Context, name string, opts metav1.ExportOptio
if accessor, err := meta.Accessor(obj); err == nil {
exportObjectMeta(accessor, opts.Exact)
} else {
glog.V(4).Infof("Object of type %v does not have ObjectMeta: %v", reflect.TypeOf(obj), err)
klog.V(4).Infof("Object of type %v does not have ObjectMeta: %v", reflect.TypeOf(obj), err)
}
if e.ExportStrategy != nil {
@ -1364,8 +1284,9 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error {
}
}
if e.Storage == nil {
e.Storage, e.DestroyFunc = opts.Decorator(
if e.Storage.Storage == nil {
e.Storage.Codec = opts.StorageConfig.Codec
e.Storage.Storage, e.DestroyFunc = opts.Decorator(
opts.StorageConfig,
e.NewFunc(),
prefix,
@ -1374,6 +1295,7 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error {
attrFunc,
triggerFunc,
)
e.StorageVersioner = opts.StorageConfig.EncodeVersioner
if opts.CountMetricPollPeriod > 0 {
stopFunc := e.startObservingCount(opts.CountMetricPollPeriod)
@ -1394,12 +1316,12 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error {
func (e *Store) startObservingCount(period time.Duration) func() {
prefix := e.KeyRootFunc(genericapirequest.NewContext())
resourceName := e.DefaultQualifiedResource.String()
glog.V(2).Infof("Monitoring %v count at <storage-prefix>/%v", resourceName, prefix)
klog.V(2).Infof("Monitoring %v count at <storage-prefix>/%v", resourceName, prefix)
stopCh := make(chan struct{})
go wait.JitterUntil(func() {
count, err := e.Storage.Count(prefix)
if err != nil {
glog.V(5).Infof("Failed to update storage count metric: %v", err)
klog.V(5).Infof("Failed to update storage count metric: %v", err)
metrics.UpdateObjectCount(resourceName, -1)
} else {
metrics.UpdateObjectCount(resourceName, count)
@ -1414,3 +1336,7 @@ func (e *Store) ConvertToTable(ctx context.Context, object runtime.Object, table
}
return rest.NewDefaultTableConvertor(e.qualifiedResourceFromContext(ctx)).ConvertToTable(ctx, object, tableOptions)
}
func (e *Store) StorageVersion() runtime.GroupVersioner {
return e.StorageVersioner
}

View file

@ -17,11 +17,11 @@ limitations under the License.
package generic
import (
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
"k8s.io/klog"
)
// StorageDecorator is a function signature for producing a storage.Interface
@ -54,7 +54,7 @@ func UndecoratedStorage(
func NewRawStorage(config *storagebackend.Config) (storage.Interface, factory.DestroyFunc) {
s, d, err := factory.Create(*config)
if err != nil {
glog.Fatalf("Unable to create storage backend: config (%v), err (%v)", config, err)
klog.Fatalf("Unable to create storage backend: config (%v), err (%v)", config, err)
}
return s, d
}