dry-run: Use dry-runnable structure

pull/8/head
Antoine Pelisse 2018-06-21 08:49:13 -07:00
parent dab04dc6e0
commit 68937c4934
34 changed files with 582 additions and 55 deletions

View File

@ -59,6 +59,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:go_default_library",
],
)

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
storeerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
"k8s.io/kubernetes/pkg/apis/autoscaling"
@ -171,7 +172,7 @@ func (r *RollbackREST) Create(ctx context.Context, obj runtime.Object, createVal
}
// Update the Deployment with information in DeploymentRollback to trigger rollback
err := r.rollbackDeployment(ctx, rollback.Name, &rollback.RollbackTo, rollback.UpdatedAnnotations)
err := r.rollbackDeployment(ctx, rollback.Name, &rollback.RollbackTo, rollback.UpdatedAnnotations, dryrun.IsDryRun(options.DryRun))
if err != nil {
return nil, err
}
@ -182,8 +183,8 @@ func (r *RollbackREST) Create(ctx context.Context, obj runtime.Object, createVal
}, nil
}
func (r *RollbackREST) rollbackDeployment(ctx context.Context, deploymentID string, config *extensions.RollbackConfig, annotations map[string]string) error {
if _, err := r.setDeploymentRollback(ctx, deploymentID, config, annotations); err != nil {
func (r *RollbackREST) rollbackDeployment(ctx context.Context, deploymentID string, config *extensions.RollbackConfig, annotations map[string]string, dryRun bool) error {
if _, err := r.setDeploymentRollback(ctx, deploymentID, config, annotations, dryRun); err != nil {
err = storeerr.InterpretGetError(err, extensions.Resource("deployments"), deploymentID)
err = storeerr.InterpretUpdateError(err, extensions.Resource("deployments"), deploymentID)
if _, ok := err.(*errors.StatusError); !ok {
@ -194,7 +195,7 @@ func (r *RollbackREST) rollbackDeployment(ctx context.Context, deploymentID stri
return nil
}
func (r *RollbackREST) setDeploymentRollback(ctx context.Context, deploymentID string, config *extensions.RollbackConfig, annotations map[string]string) (*extensions.Deployment, error) {
func (r *RollbackREST) setDeploymentRollback(ctx context.Context, deploymentID string, config *extensions.RollbackConfig, annotations map[string]string, dryRun bool) (*extensions.Deployment, error) {
dKey, err := r.store.KeyFunc(ctx, deploymentID)
if err != nil {
return nil, err
@ -214,7 +215,7 @@ func (r *RollbackREST) setDeploymentRollback(ctx context.Context, deploymentID s
d.Spec.RollbackTo = config
finalDeployment = d
return d, nil
}))
}), dryRun)
return finalDeployment, err
}

View File

@ -205,7 +205,7 @@ func TestScaleGet(t *testing.T) {
var deployment extensions.Deployment
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
key := "/deployments/" + namespace + "/" + name
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0); err != nil {
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0, false); err != nil {
t.Fatalf("error setting new deployment (key: %s) %v: %v", key, validDeployment, err)
}
@ -246,7 +246,7 @@ func TestScaleUpdate(t *testing.T) {
var deployment extensions.Deployment
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
key := "/deployments/" + namespace + "/" + name
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0); err != nil {
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0, false); err != nil {
t.Fatalf("error setting new deployment (key: %s) %v: %v", key, validDeployment, err)
}
replicas := int32(12)
@ -283,7 +283,7 @@ func TestStatusUpdate(t *testing.T) {
defer storage.Deployment.Store.DestroyFunc()
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
key := "/deployments/" + namespace + "/" + name
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, nil, 0); err != nil {
if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := extensions.Deployment{

View File

@ -260,7 +260,7 @@ func TestScaleGet(t *testing.T) {
var rs extensions.ReplicaSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/replicasets/" + metav1.NamespaceDefault + "/" + name
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0); err != nil {
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0, false); err != nil {
t.Fatalf("error setting new replica set (key: %s) %v: %v", key, validReplicaSet, err)
}
@ -305,7 +305,7 @@ func TestScaleUpdate(t *testing.T) {
var rs extensions.ReplicaSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/replicasets/" + metav1.NamespaceDefault + "/" + name
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0); err != nil {
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0, false); err != nil {
t.Fatalf("error setting new replica set (key: %s) %v: %v", key, validReplicaSet, err)
}
replicas := 12
@ -347,7 +347,7 @@ func TestStatusUpdate(t *testing.T) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/replicasets/" + metav1.NamespaceDefault + "/foo"
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, nil, 0); err != nil {
if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := extensions.ReplicaSet{

View File

@ -102,7 +102,7 @@ func TestStatusUpdate(t *testing.T) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/foo"
validStatefulSet := validNewStatefulSet()
if err := storage.StatefulSet.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil {
if err := storage.StatefulSet.Storage.Create(ctx, key, validStatefulSet, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := apps.StatefulSet{
@ -210,7 +210,7 @@ func TestScaleGet(t *testing.T) {
var sts apps.StatefulSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0, false); err != nil {
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
}
@ -254,7 +254,7 @@ func TestScaleUpdate(t *testing.T) {
var sts apps.StatefulSet
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0, false); err != nil {
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
}
replicas := 12

View File

@ -177,7 +177,7 @@ func TestUpdateStatus(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
key, _ := storage.KeyFunc(ctx, "foo")
autoscalerStart := validNewHorizontalPodAutoscaler("foo")
err := storage.Storage.Create(ctx, key, autoscalerStart, nil, 0)
err := storage.Storage.Create(ctx, key, autoscalerStart, nil, 0, false)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

View File

@ -45,6 +45,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:go_default_library",
],
)

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
storageerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/printers"
@ -190,6 +191,7 @@ func (r *REST) Delete(ctx context.Context, name string, options *metav1.DeleteOp
}
return existingNamespace, nil
}),
dryrun.IsDryRun(options.DryRun),
)
if err != nil {

View File

@ -156,7 +156,7 @@ func TestDeleteNamespaceWithIncompleteFinalizers(t *testing.T) {
},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
}
if err := storage.store.Storage.Create(ctx, key, namespace, nil, 0); err != nil {
if err := storage.store.Storage.Create(ctx, key, namespace, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, _, err := storage.Delete(ctx, "foo", nil); err == nil {
@ -181,7 +181,7 @@ func TestDeleteNamespaceWithCompleteFinalizers(t *testing.T) {
},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
}
if err := storage.store.Storage.Create(ctx, key, namespace, nil, 0); err != nil {
if err := storage.store.Storage.Create(ctx, key, namespace, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, _, err := storage.Delete(ctx, "foo", nil); err != nil {

View File

@ -169,7 +169,7 @@ func TestUpdateStatus(t *testing.T) {
ctx := genericapirequest.NewContext()
key, _ := storage.KeyFunc(ctx, "foo")
pvStart := validNewPersistentVolume("foo")
err := storage.Storage.Create(ctx, key, pvStart, nil, 0)
err := storage.Storage.Create(ctx, key, pvStart, nil, 0, false)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

View File

@ -159,7 +159,7 @@ func TestUpdateStatus(t *testing.T) {
key, _ := storage.KeyFunc(ctx, "foo")
pvcStart := validNewPersistentVolumeClaim("foo", metav1.NamespaceDefault)
err := storage.Storage.Create(ctx, key, pvcStart, nil, 0)
err := storage.Storage.Create(ctx, key, pvcStart, nil, 0, false)
pvc := &api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{

View File

@ -33,7 +33,7 @@ func TestPodLogValidates(t *testing.T) {
s, destroyFunc := generic.NewRawStorage(config)
defer destroyFunc()
store := &genericregistry.Store{
Storage: s,
Storage: genericregistry.DryRunnableStorage{Storage: s},
}
logRest := &LogREST{Store: store, KubeletConn: nil}

View File

@ -26,6 +26,7 @@ go_test(
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
@ -66,6 +67,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:go_default_library",
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
],
)

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
storeerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun"
podutil "k8s.io/kubernetes/pkg/api/pod"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation"
@ -148,7 +149,7 @@ func (r *BindingREST) Create(ctx context.Context, obj runtime.Object, createVali
return nil, errs.ToAggregate()
}
err = r.assignPod(ctx, binding.Name, binding.Target.Name, binding.Annotations)
err = r.assignPod(ctx, binding.Name, binding.Target.Name, binding.Annotations, dryrun.IsDryRun(options.DryRun))
out = &metav1.Status{Status: metav1.StatusSuccess}
return
}
@ -156,7 +157,7 @@ func (r *BindingREST) Create(ctx context.Context, obj runtime.Object, createVali
// setPodHostAndAnnotations sets the given pod's host to 'machine' if and only if it was
// previously 'oldMachine' and merges the provided annotations with those of the pod.
// Returns the current state of the pod, or an error.
func (r *BindingREST) setPodHostAndAnnotations(ctx context.Context, podID, oldMachine, machine string, annotations map[string]string) (finalPod *api.Pod, err error) {
func (r *BindingREST) setPodHostAndAnnotations(ctx context.Context, podID, oldMachine, machine string, annotations map[string]string, dryRun bool) (finalPod *api.Pod, err error) {
podKey, err := r.store.KeyFunc(ctx, podID)
if err != nil {
return nil, err
@ -185,13 +186,13 @@ func (r *BindingREST) setPodHostAndAnnotations(ctx context.Context, podID, oldMa
})
finalPod = pod
return pod, nil
}))
}), dryRun)
return finalPod, err
}
// assignPod assigns the given pod to the given machine.
func (r *BindingREST) assignPod(ctx context.Context, podID string, machine string, annotations map[string]string) (err error) {
if _, err = r.setPodHostAndAnnotations(ctx, podID, "", machine, annotations); err != nil {
func (r *BindingREST) assignPod(ctx context.Context, podID string, machine string, annotations map[string]string, dryRun bool) (err error) {
if _, err = r.setPodHostAndAnnotations(ctx, podID, "", machine, annotations, dryRun); err != nil {
err = storeerr.InterpretGetError(err, api.Resource("pods"), podID)
err = storeerr.InterpretUpdateError(err, api.Resource("pods"), podID)
if _, ok := err.(*errors.StatusError); !ok {

View File

@ -34,6 +34,7 @@ import (
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
@ -170,7 +171,7 @@ func newFailDeleteStorage(t *testing.T, called *bool) (*REST, *etcdtesting.EtcdT
ResourcePrefix: "pods",
}
storage := NewStorage(restOptions, nil, nil, nil)
storage.Pod.Store.Storage = FailDeletionStorage{storage.Pod.Store.Storage, called}
storage.Pod.Store.Storage = genericregistry.DryRunnableStorage{Storage: FailDeletionStorage{storage.Pod.Store.Storage.Storage, called}}
return storage.Pod, server
}
@ -340,7 +341,7 @@ func TestResourceLocation(t *testing.T) {
for _, tc := range testCases {
storage, _, _, server := newStorage(t)
key, _ := storage.KeyFunc(ctx, tc.pod.Name)
if err := storage.Storage.Create(ctx, key, &tc.pod, nil, 0); err != nil {
if err := storage.Storage.Create(ctx, key, &tc.pod, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -825,7 +826,7 @@ func TestEtcdUpdateScheduled(t *testing.T) {
SecurityContext: &api.PodSecurityContext{},
SchedulerName: api.DefaultSchedulerName,
},
}, nil, 1)
}, nil, 1, false)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
@ -897,7 +898,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
SchedulerName: api.DefaultSchedulerName,
},
}
err := storage.Storage.Create(ctx, key, &podStart, nil, 0)
err := storage.Storage.Create(ctx, key, &podStart, nil, 0, false)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

View File

@ -162,7 +162,7 @@ func TestUpdateStatus(t *testing.T) {
key, _ := storage.KeyFunc(ctx, "foo")
resourcequotaStart := validNewResourceQuota()
err := storage.Storage.Create(ctx, key, resourcequotaStart, nil, 0)
err := storage.Storage.Create(ctx, key, resourcequotaStart, nil, 0, false)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}

View File

@ -174,7 +174,7 @@ func NewTestRESTWithPods(t *testing.T, endpoints *api.EndpointsList, pods *api.P
ctx := genericapirequest.NewDefaultContext()
for ix := range pods.Items {
key, _ := podStorage.Pod.KeyFunc(ctx, pods.Items[ix].Name)
if err := podStorage.Pod.Storage.Create(ctx, key, &pods.Items[ix], nil, 0); err != nil {
if err := podStorage.Pod.Storage.Create(ctx, key, &pods.Items[ix], nil, 0, false); err != nil {
t.Fatalf("Couldn't create pod: %v", err)
}
}
@ -188,7 +188,7 @@ func NewTestRESTWithPods(t *testing.T, endpoints *api.EndpointsList, pods *api.P
ctx := genericapirequest.NewDefaultContext()
for ix := range endpoints.Items {
key, _ := endpointStorage.KeyFunc(ctx, endpoints.Items[ix].Name)
if err := endpointStorage.Store.Storage.Create(ctx, key, &endpoints.Items[ix], nil, 0); err != nil {
if err := endpointStorage.Store.Storage.Create(ctx, key, &endpoints.Items[ix], nil, 0, false); err != nil {
t.Fatalf("Couldn't create endpoint: %v", err)
}
}

View File

@ -78,7 +78,7 @@ func TestStatusUpdate(t *testing.T) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/poddisruptionbudgets/" + metav1.NamespaceDefault + "/foo"
validPodDisruptionBudget := validNewPodDisruptionBudget()
if err := storage.Storage.Create(ctx, key, validPodDisruptionBudget, nil, 0); err != nil {
if err := storage.Storage.Create(ctx, key, validPodDisruptionBudget, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -114,7 +114,7 @@ func TestDeleteSystemPriorityClass(t *testing.T) {
key := "test/system-node-critical"
ctx := genericapirequest.NewContext()
pc := scheduling.SystemPriorityClasses()[0]
if err := storage.Store.Storage.Create(ctx, key, pc, nil, 0); err != nil {
if err := storage.Store.Storage.Create(ctx, key, pc, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, _, err := storage.Delete(ctx, pc.Name, nil); err == nil {

View File

@ -93,6 +93,7 @@ filegroup(
"//staging/src/k8s.io/apiserver/pkg/registry:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/server:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/storage:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/util/feature:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/util/flag:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/util/flushwriter:all-srcs",

View File

@ -1462,6 +1462,10 @@
"ImportPath": "k8s.io/apiserver/pkg/storage/value",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/dryrun",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@ -2282,6 +2286,10 @@
"ImportPath": "k8s.io/apiserver/pkg/storage/storagebackend",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/dryrun",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -254,7 +254,7 @@ func TestColumns(t *testing.T) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/noxus/" + metav1.NamespaceDefault + "/foo"
validCustomResource := validNewCustomResource()
if err := storage.CustomResource.Storage.Create(ctx, key, validCustomResource, nil, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, validCustomResource, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -324,7 +324,7 @@ func TestStatusUpdate(t *testing.T) {
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/noxus/" + metav1.NamespaceDefault + "/foo"
validCustomResource := validNewCustomResource()
if err := storage.CustomResource.Storage.Create(ctx, key, validCustomResource, nil, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, validCustomResource, nil, 0, false); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -375,7 +375,7 @@ func TestScaleGet(t *testing.T) {
var cr unstructured.Unstructured
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/noxus/" + metav1.NamespaceDefault + "/" + name
if err := storage.CustomResource.Storage.Create(ctx, key, &validCustomResource, &cr, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, &validCustomResource, &cr, 0, false); err != nil {
t.Fatalf("error setting new custom resource (key: %s) %v: %v", key, validCustomResource, err)
}
@ -415,7 +415,7 @@ func TestScaleGetWithoutSpecReplicas(t *testing.T) {
key := "/noxus/" + metav1.NamespaceDefault + "/" + name
withoutSpecReplicas := validCustomResource.DeepCopy()
unstructured.RemoveNestedField(withoutSpecReplicas.Object, "spec", "replicas")
if err := storage.CustomResource.Storage.Create(ctx, key, withoutSpecReplicas, &cr, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, withoutSpecReplicas, &cr, 0, false); err != nil {
t.Fatalf("error setting new custom resource (key: %s) %v: %v", key, withoutSpecReplicas, err)
}
@ -438,7 +438,7 @@ func TestScaleUpdate(t *testing.T) {
var cr unstructured.Unstructured
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
key := "/noxus/" + metav1.NamespaceDefault + "/" + name
if err := storage.CustomResource.Storage.Create(ctx, key, &validCustomResource, &cr, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, &validCustomResource, &cr, 0, false); err != nil {
t.Fatalf("error setting new custom resource (key: %s) %v: %v", key, validCustomResource, err)
}
@ -492,7 +492,7 @@ func TestScaleUpdateWithoutSpecReplicas(t *testing.T) {
key := "/noxus/" + metav1.NamespaceDefault + "/" + name
withoutSpecReplicas := validCustomResource.DeepCopy()
unstructured.RemoveNestedField(withoutSpecReplicas.Object, "spec", "replicas")
if err := storage.CustomResource.Storage.Create(ctx, key, withoutSpecReplicas, &cr, 0); err != nil {
if err := storage.CustomResource.Storage.Create(ctx, key, withoutSpecReplicas, &cr, 0, false); err != nil {
t.Fatalf("error setting new custom resource (key: %s) %v: %v", key, withoutSpecReplicas, err)
}

View File

@ -30,6 +30,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
storageerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun"
)
// rest implements a RESTStorage for API services against etcd
@ -129,6 +130,7 @@ func (r *REST) Delete(ctx context.Context, name string, options *metav1.DeleteOp
})
return existingCRD, nil
}),
dryrun.IsDryRun(options.DryRun),
)
if err != nil {

View File

@ -10,6 +10,7 @@ go_test(
name = "go_default_test",
srcs = [
"decorated_watcher_test.go",
"dryrun_test.go",
"store_test.go",
],
embed = [":go_default_library"],
@ -20,12 +21,14 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/selection:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
@ -53,6 +56,7 @@ go_library(
srcs = [
"decorated_watcher.go",
"doc.go",
"dryrun.go",
"storage_factory.go",
"store.go",
],
@ -84,6 +88,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/metrics:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/dryrun:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
)

View File

@ -0,0 +1,134 @@
/*
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"
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
"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 checkPreconditions(key, preconditions, 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 = checkPreconditions(key, preconditions, 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 checkPreconditions(key string, preconditions *storage.Preconditions, obj runtime.Object) error {
if preconditions == nil {
return nil
}
objMeta, err := meta.Accessor(obj)
if err != nil {
return storage.NewInternalErrorf("can't enforce preconditions %v on un-introspectable object %v, got error: %v", *preconditions, obj, err)
}
if preconditions.UID != nil && *preconditions.UID != objMeta.GetUID() {
errMsg := fmt.Sprintf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *preconditions.UID, objMeta.GetUID())
return storage.NewInvalidObjError(key, errMsg)
}
return nil
}
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

@ -0,0 +1,304 @@
/*
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"
"encoding/json"
"errors"
"fmt"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/api/apitesting"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/storage"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
)
func NewDryRunnableTestStorage(t *testing.T) (DryRunnableStorage, func()) {
server, sc := etcdtesting.NewUnsecuredEtcd3TestClientServer(t)
sc.Codec = apitesting.TestStorageCodec(codecs, examplev1.SchemeGroupVersion)
s, destroy, err := factory.Create(*sc)
if err != nil {
t.Fatalf("Error creating storage: %v", err)
}
return DryRunnableStorage{Storage: s, Codec: sc.Codec}, func() {
destroy()
server.Terminate(t)
}
}
func UnstructuredOrDie(j string) *unstructured.Unstructured {
m := map[string]interface{}{}
err := json.Unmarshal([]byte(j), &m)
if err != nil {
panic(fmt.Errorf("Failed to unmarshal into Unstructured: %v", err))
}
return &unstructured.Unstructured{Object: m}
}
func TestDryRunCreateDoesntCreate(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, true)
if err != nil {
t.Fatalf("Failed to create new dry-run object: %v", err)
}
err = s.Get(context.Background(), "key", "", out, false)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeKeyNotFound {
t.Errorf("Expected key to be not found, error: %v", err)
}
}
func TestDryRunCreateReturnsObject(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, true)
if err != nil {
t.Fatalf("Failed to create new dry-run object: %v", err)
}
if !reflect.DeepEqual(obj, out) {
t.Errorf("Returned object different from input object:\nExpected: %v\nGot: %v", obj, out)
}
}
func TestDryRunCreateExistingObjectFails(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
err = s.Create(context.Background(), "key", obj, out, 0, true)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeKeyExists {
t.Errorf("Expected KeyExists error: %v", err)
}
}
func TestDryRunUpdateMissingObjectFails(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
updateFunc := func(input runtime.Object, res storage.ResponseMeta) (output runtime.Object, ttl *uint64, err error) {
return input, nil, errors.New("UpdateFunction shouldn't be called")
}
err := s.GuaranteedUpdate(context.Background(), "key", obj, false, nil, updateFunc, true)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeKeyNotFound {
t.Errorf("Expected key to be not found, error: %v", err)
}
}
func TestDryRunUpdatePreconditions(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod", "metadata": {"uid": "my-uid"}}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
updateFunc := func(input runtime.Object, res storage.ResponseMeta) (output runtime.Object, ttl *uint64, err error) {
u, ok := input.(*unstructured.Unstructured)
if !ok {
return input, nil, errors.New("Input object is not unstructured")
}
unstructured.SetNestedField(u.Object, "value", "field")
return u, nil, nil
}
wrongID := types.UID("wrong-uid")
myID := types.UID("my-uid")
err = s.GuaranteedUpdate(context.Background(), "key", obj, false, &storage.Preconditions{UID: &wrongID}, updateFunc, true)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeInvalidObj {
t.Errorf("Expected invalid object, error: %v", err)
}
err = s.GuaranteedUpdate(context.Background(), "key", obj, false, &storage.Preconditions{UID: &myID}, updateFunc, true)
if err != nil {
t.Fatalf("Failed to update with valid precondition: %v", err)
}
}
func TestDryRunUpdateDoesntUpdate(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
created := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, created, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
updateFunc := func(input runtime.Object, res storage.ResponseMeta) (output runtime.Object, ttl *uint64, err error) {
u, ok := input.(*unstructured.Unstructured)
if !ok {
return input, nil, errors.New("Input object is not unstructured")
}
unstructured.SetNestedField(u.Object, "value", "field")
return u, nil, nil
}
err = s.GuaranteedUpdate(context.Background(), "key", obj, false, nil, updateFunc, true)
if err != nil {
t.Fatalf("Failed to dry-run update: %v", err)
}
out := UnstructuredOrDie(`{}`)
err = s.Get(context.Background(), "key", "", out, false)
if !reflect.DeepEqual(created, out) {
t.Fatalf("Returned object %q different from expected %q", created, out)
}
}
func TestDryRunUpdateReturnsObject(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
updateFunc := func(input runtime.Object, res storage.ResponseMeta) (output runtime.Object, ttl *uint64, err error) {
u, ok := input.(*unstructured.Unstructured)
if !ok {
return input, nil, errors.New("Input object is not unstructured")
}
unstructured.SetNestedField(u.Object, "value", "field")
return u, nil, nil
}
err = s.GuaranteedUpdate(context.Background(), "key", obj, false, nil, updateFunc, true)
if err != nil {
t.Fatalf("Failed to dry-run update: %v", err)
}
out = UnstructuredOrDie(`{"field": "value", "kind": "Pod", "metadata": {"resourceVersion": "2", "selfLink": ""}}`)
if !reflect.DeepEqual(obj, out) {
t.Fatalf("Returned object %#v different from expected %#v", obj, out)
}
}
func TestDryRunDeleteDoesntDelete(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
err = s.Delete(context.Background(), "key", out, nil, true)
if err != nil {
t.Fatalf("Failed to dry-run delete the object: %v", err)
}
err = s.Get(context.Background(), "key", "", out, false)
if err != nil {
t.Fatalf("Failed to retrieve dry-run deleted object: %v", err)
}
}
func TestDryRunDeleteMissingObjectFails(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
out := UnstructuredOrDie(`{}`)
err := s.Delete(context.Background(), "key", out, nil, true)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeKeyNotFound {
t.Errorf("Expected key to be not found, error: %v", err)
}
}
func TestDryRunDeleteReturnsObject(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod"}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
out = UnstructuredOrDie(`{}`)
expected := UnstructuredOrDie(`{"kind": "Pod", "metadata": {"resourceVersion": "2", "selfLink": ""}}`)
err = s.Delete(context.Background(), "key", out, nil, true)
if err != nil {
t.Fatalf("Failed to delete with valid precondition: %v", err)
}
if !reflect.DeepEqual(expected, out) {
t.Fatalf("Returned object %q doesn't match expected: %q", out, expected)
}
}
func TestDryRunDeletePreconditions(t *testing.T) {
s, destroy := NewDryRunnableTestStorage(t)
defer destroy()
obj := UnstructuredOrDie(`{"kind": "Pod", "metadata": {"uid": "my-uid"}}`)
out := UnstructuredOrDie(`{}`)
err := s.Create(context.Background(), "key", obj, out, 0, false)
if err != nil {
t.Fatalf("Failed to create new object: %v", err)
}
wrongID := types.UID("wrong-uid")
myID := types.UID("my-uid")
err = s.Delete(context.Background(), "key", out, &storage.Preconditions{UID: &wrongID}, true)
if e, ok := err.(*storage.StorageError); !ok || e.Code != storage.ErrCodeInvalidObj {
t.Errorf("Expected invalid object, error: %v", err)
}
err = s.Delete(context.Background(), "key", out, &storage.Preconditions{UID: &myID}, true)
if err != nil {
t.Fatalf("Failed to delete with valid precondition: %v", err)
}
}

View File

@ -45,6 +45,7 @@ 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"
)
@ -172,8 +173,10 @@ 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
// Called to cleanup clients used by the underlying Storage; optional.
DestroyFunc func()
}
@ -348,7 +351,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) {
@ -496,10 +499,10 @@ func (e *Store) shouldDeleteForFailedInitialization(ctx context.Context, obj run
// 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 {
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.
@ -633,12 +636,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)
@ -650,7 +653,7 @@ func (e *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
}
if e.shouldDeleteForFailedInitialization(ctx, out) {
return e.deleteWithoutFinalizers(ctx, name, key, out, storagePreconditions)
return e.deleteWithoutFinalizers(ctx, name, key, out, storagePreconditions, dryrun.IsDryRun(options.DryRun))
}
if creating {
@ -919,6 +922,7 @@ func (e *Store) updateForGracefulDeletionAndFinalizers(ctx context.Context, name
lastExisting = existing
return existing, nil
}),
dryrun.IsDryRun(options.DryRun),
)
switch err {
case nil:
@ -1000,10 +1004,15 @@ func (e *Store) Delete(ctx context.Context, name string, options *metav1.DeleteO
return out, false, err
}
// If dry-run, then just return the object as just saved.
if dryrun.IsDryRun(options.DryRun) {
return out, true, nil
}
// delete immediately, or no graceful deletion supported
glog.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 {
@ -1364,8 +1373,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,

View File

@ -213,7 +213,7 @@ func TestStoreList(t *testing.T) {
destroyFunc, registry := NewTestGenericStoreRegistry(t)
if item.in != nil {
if err := storagetesting.CreateList("/pods", registry.Storage, item.in); err != nil {
if err := storagetesting.CreateList("/pods", registry.Storage.Storage, item.in); err != nil {
t.Errorf("Unexpected error %v", err)
}
}
@ -1901,7 +1901,7 @@ func newTestGenericStoreRegistry(t *testing.T, scheme *runtime.Scheme, hasCacheE
},
}
},
Storage: s,
Storage: DryRunnableStorage{Storage: s},
}
}

View File

@ -164,7 +164,7 @@ func (t *Tester) createObject(ctx context.Context, obj runtime.Object) error {
if err != nil {
return err
}
return t.storage.Storage.Create(ctx, key, obj, nil, 0)
return t.storage.Storage.Create(ctx, key, obj, nil, 0, false)
}
func (t *Tester) setObjectsForList(objects []runtime.Object) []runtime.Object {
@ -173,7 +173,7 @@ func (t *Tester) setObjectsForList(objects []runtime.Object) []runtime.Object {
t.tester.Errorf("unable to clear collection: %v", err)
return nil
}
if err := storagetesting.CreateObjList(key, t.storage.Storage, objects); err != nil {
if err := storagetesting.CreateObjList(key, t.storage.Storage.Storage, objects); err != nil {
t.tester.Errorf("unexpected error: %v", err)
return nil
}

View File

@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["dryrun.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/util/dryrun",
importpath = "k8s.io/apiserver/pkg/util/dryrun",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,22 @@
/*
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 dryrun
// IsDryRun returns true if the DryRun flag is an actual dry-run.
func IsDryRun(flag []string) bool {
return len(flag) > 0
}

View File

@ -1134,6 +1134,10 @@
"ImportPath": "k8s.io/apiserver/pkg/storage/value",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/dryrun",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -1106,6 +1106,10 @@
"ImportPath": "k8s.io/apiserver/pkg/storage/value",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/dryrun",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"