mirror of https://github.com/k3s-io/k3s
dry-run: Use dry-runnable structure
parent
dab04dc6e0
commit
68937c4934
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue