mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #12957 from smarterclayton/handle_terminating_pods
Auto commit by PR queue bot
This commit is contained in:
		@@ -11017,6 +11017,11 @@
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "description": "RFC 3339 date and time at which the object will be deleted; populated by the system when a graceful deletion is requested, read-only; if not set, graceful deletion of the object has not been requested; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"
 | 
			
		||||
     },
 | 
			
		||||
     "deletionGracePeriodSeconds": {
 | 
			
		||||
      "type": "integer",
 | 
			
		||||
      "format": "int64",
 | 
			
		||||
      "description": "number of seconds allowed for this object to gracefully terminate before it will be removed from the system; only set when deletionTimestamp is also set, read-only; may only be shortened"
 | 
			
		||||
     },
 | 
			
		||||
     "labels": {
 | 
			
		||||
      "type": "any",
 | 
			
		||||
      "description": "map of string keys and values that can be used to organize and categorize objects; may match selectors of replication controllers and services; see http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
 | 
			
		||||
@@ -12327,7 +12332,7 @@
 | 
			
		||||
     "terminationGracePeriodSeconds": {
 | 
			
		||||
      "type": "integer",
 | 
			
		||||
      "format": "int64",
 | 
			
		||||
      "description": "optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process"
 | 
			
		||||
      "description": "optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process; defaults to 30 seconds"
 | 
			
		||||
     },
 | 
			
		||||
     "activeDeadlineSeconds": {
 | 
			
		||||
      "type": "integer",
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ spec:
 | 
			
		||||
      mountPath: /varlog
 | 
			
		||||
    - name: containers
 | 
			
		||||
      mountPath: /var/lib/docker/containers
 | 
			
		||||
  terminationGracePeriodSeconds: 30
 | 
			
		||||
  volumes:
 | 
			
		||||
  - name: varlog
 | 
			
		||||
    hostPath:
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ spec:
 | 
			
		||||
      mountPath: /varlog
 | 
			
		||||
    - name: containers
 | 
			
		||||
      mountPath: /var/lib/docker/containers
 | 
			
		||||
  terminationGracePeriodSeconds: 30
 | 
			
		||||
  volumes:
 | 
			
		||||
  - name: varlog
 | 
			
		||||
    hostPath:
 | 
			
		||||
 
 | 
			
		||||
@@ -182,6 +182,7 @@ spec:
 | 
			
		||||
      mountPath: /varlog
 | 
			
		||||
    - name: containers
 | 
			
		||||
      mountPath: /var/lib/docker/containers
 | 
			
		||||
  terminationGracePeriodSeconds: 30
 | 
			
		||||
  volumes:
 | 
			
		||||
  - name: varlog
 | 
			
		||||
    hostPath:
 | 
			
		||||
 
 | 
			
		||||
@@ -1011,6 +1011,12 @@ func deepCopy_api_ObjectMeta(in ObjectMeta, out *ObjectMeta, c *conversion.Clone
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,8 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Obje
 | 
			
		||||
	} else {
 | 
			
		||||
		objectMeta.Namespace = api.NamespaceNone
 | 
			
		||||
	}
 | 
			
		||||
	objectMeta.DeletionTimestamp = nil
 | 
			
		||||
	objectMeta.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	strategy.PrepareForCreate(obj)
 | 
			
		||||
	api.FillObjectMetaSystemFields(ctx, objectMeta)
 | 
			
		||||
	api.GenerateName(strategy, objectMeta)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,11 @@ limitations under the License.
 | 
			
		||||
package rest
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes
 | 
			
		||||
@@ -40,12 +43,41 @@ func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Obje
 | 
			
		||||
	if strategy == nil {
 | 
			
		||||
		return false, false, nil
 | 
			
		||||
	}
 | 
			
		||||
	_, _, kerr := objectMetaAndKind(strategy, obj)
 | 
			
		||||
	objectMeta, _, kerr := objectMetaAndKind(strategy, obj)
 | 
			
		||||
	if kerr != nil {
 | 
			
		||||
		return false, false, kerr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if the object is already being deleted
 | 
			
		||||
	if objectMeta.DeletionTimestamp != nil {
 | 
			
		||||
		// if we are already being deleted, we may only shorten the deletion grace period
 | 
			
		||||
		// this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set,
 | 
			
		||||
		// so we force deletion immediately
 | 
			
		||||
		if objectMeta.DeletionGracePeriodSeconds == nil {
 | 
			
		||||
			return false, false, nil
 | 
			
		||||
		}
 | 
			
		||||
		// only a shorter grace period may be provided by a user
 | 
			
		||||
		if options.GracePeriodSeconds != nil {
 | 
			
		||||
			period := int64(*options.GracePeriodSeconds)
 | 
			
		||||
			if period > *objectMeta.DeletionGracePeriodSeconds {
 | 
			
		||||
				return false, true, nil
 | 
			
		||||
			}
 | 
			
		||||
			now := util.NewTime(util.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
 | 
			
		||||
			objectMeta.DeletionTimestamp = &now
 | 
			
		||||
			objectMeta.DeletionGracePeriodSeconds = &period
 | 
			
		||||
			options.GracePeriodSeconds = &period
 | 
			
		||||
			return true, false, nil
 | 
			
		||||
		}
 | 
			
		||||
		// graceful deletion is pending, do nothing
 | 
			
		||||
		options.GracePeriodSeconds = objectMeta.DeletionGracePeriodSeconds
 | 
			
		||||
		return false, true, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !strategy.CheckGracefulDelete(obj, options) {
 | 
			
		||||
		return false, false, nil
 | 
			
		||||
	}
 | 
			
		||||
	now := util.NewTime(util.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
 | 
			
		||||
	objectMeta.DeletionTimestamp = &now
 | 
			
		||||
	objectMeta.DeletionGracePeriodSeconds = options.GracePeriodSeconds
 | 
			
		||||
	return true, false, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -123,9 +123,9 @@ func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object
 | 
			
		||||
// Test deleting an object.
 | 
			
		||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
			
		||||
func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
 | 
			
		||||
	t.testDeleteNonExist(createFn)
 | 
			
		||||
	t.testDeleteNoGraceful(createFn, wasGracefulFn)
 | 
			
		||||
	t.testDeleteInvokesValidation(invalid...)
 | 
			
		||||
	t.TestDeleteNonExist(createFn)
 | 
			
		||||
	t.TestDeleteNoGraceful(createFn, wasGracefulFn)
 | 
			
		||||
	t.TestDeleteInvokesValidation(invalid...)
 | 
			
		||||
	// TODO: Test delete namespace mismatch rejection
 | 
			
		||||
	// once #5684 is fixed.
 | 
			
		||||
}
 | 
			
		||||
@@ -133,8 +133,11 @@ func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func()
 | 
			
		||||
// Test graceful deletion.
 | 
			
		||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
			
		||||
func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
	t.testDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
 | 
			
		||||
	t.testDeleteGracefulUsesZeroOnNil(createFn(), 0)
 | 
			
		||||
	t.TestDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
 | 
			
		||||
	t.TestDeleteGracefulWithValue(createFn(), expectedGrace, wasGracefulFn)
 | 
			
		||||
	t.TestDeleteGracefulUsesZeroOnNil(createFn(), 0)
 | 
			
		||||
	t.TestDeleteGracefulExtend(createFn(), expectedGrace, wasGracefulFn)
 | 
			
		||||
	t.TestDeleteGracefulImmediate(createFn(), expectedGrace, wasGracefulFn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Test getting object.
 | 
			
		||||
@@ -316,7 +319,7 @@ func (t *Tester) testUpdateFailsOnVersion(older runtime.Object) {
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// Deletion tests.
 | 
			
		||||
 | 
			
		||||
func (t *Tester) testDeleteInvokesValidation(invalid ...runtime.Object) {
 | 
			
		||||
func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) {
 | 
			
		||||
	for i, obj := range invalid {
 | 
			
		||||
		objectMeta := t.getObjectMetaOrFail(obj)
 | 
			
		||||
		ctx := t.TestContext()
 | 
			
		||||
@@ -327,7 +330,7 @@ func (t *Tester) testDeleteInvokesValidation(invalid ...runtime.Object) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) testDeleteNonExist(createFn func() runtime.Object) {
 | 
			
		||||
func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) {
 | 
			
		||||
	existing := createFn()
 | 
			
		||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
			
		||||
	context := t.TestContext()
 | 
			
		||||
@@ -340,7 +343,10 @@ func (t *Tester) testDeleteNonExist(createFn func() runtime.Object) {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) testDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) {
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// Graceful Deletion tests.
 | 
			
		||||
 | 
			
		||||
func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) {
 | 
			
		||||
	existing := createFn()
 | 
			
		||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
@@ -356,25 +362,142 @@ func (t *Tester) testDeleteNoGraceful(createFn func() runtime.Object, wasGracefu
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// Graceful Deletion tests.
 | 
			
		||||
 | 
			
		||||
func (t *Tester) testDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
	_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); err != nil {
 | 
			
		||||
	if !wasGracefulFn() {
 | 
			
		||||
		t.Errorf("did not gracefully delete resource")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error, object should exist: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	objectMeta, err = api.ObjectMetaFor(object)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionTimestamp == nil {
 | 
			
		||||
		t.Errorf("did not set deletion timestamp")
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionGracePeriodSeconds == nil {
 | 
			
		||||
		t.Fatalf("did not set deletion grace period seconds")
 | 
			
		||||
	}
 | 
			
		||||
	if *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
 | 
			
		||||
		t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) TestDeleteGracefulWithValue(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
	objectMeta, err := api.ObjectMetaFor(existing)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !wasGracefulFn() {
 | 
			
		||||
		t.Errorf("did not gracefully delete resource")
 | 
			
		||||
	}
 | 
			
		||||
	object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error, object should exist: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	objectMeta, err = api.ObjectMetaFor(object)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionTimestamp == nil {
 | 
			
		||||
		t.Errorf("did not set deletion timestamp")
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionGracePeriodSeconds == nil {
 | 
			
		||||
		t.Fatalf("did not set deletion grace period seconds")
 | 
			
		||||
	}
 | 
			
		||||
	if *objectMeta.DeletionGracePeriodSeconds != expectedGrace+2 {
 | 
			
		||||
		t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) testDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) {
 | 
			
		||||
func (t *Tester) TestDeleteGracefulExtend(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
	objectMeta, err := api.ObjectMetaFor(existing)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !wasGracefulFn() {
 | 
			
		||||
		t.Errorf("did not gracefully delete resource")
 | 
			
		||||
	}
 | 
			
		||||
	// second delete duration is ignored
 | 
			
		||||
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error, object should exist: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	objectMeta, err = api.ObjectMetaFor(object)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionTimestamp == nil {
 | 
			
		||||
		t.Errorf("did not set deletion timestamp")
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionGracePeriodSeconds == nil {
 | 
			
		||||
		t.Fatalf("did not set deletion grace period seconds")
 | 
			
		||||
	}
 | 
			
		||||
	if *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
 | 
			
		||||
		t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) TestDeleteGracefulImmediate(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
			
		||||
	objectMeta, err := api.ObjectMetaFor(existing)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !wasGracefulFn() {
 | 
			
		||||
		t.Errorf("did not gracefully delete resource")
 | 
			
		||||
	}
 | 
			
		||||
	// second delete is immediate, resource is deleted
 | 
			
		||||
	out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	_, err = t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
 | 
			
		||||
	if !errors.IsNotFound(err) {
 | 
			
		||||
		t.Errorf("unexpected error, object should be deleted immediately: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	objectMeta, err = api.ObjectMetaFor(out)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != 0 {
 | 
			
		||||
		t.Errorf("unexpected deleted meta: %#v", objectMeta)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) {
 | 
			
		||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
			
		||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
			
		||||
	_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
 | 
			
		||||
@@ -382,7 +505,7 @@ func (t *Tester) testDeleteGracefulUsesZeroOnNil(existing runtime.Object, expect
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
 | 
			
		||||
		t.Errorf("unexpected error, object should exist: %v", err)
 | 
			
		||||
		t.Errorf("unexpected error, object should not exist: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -151,6 +151,7 @@ func TestRoundTripTypes(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEncode_Ptr(t *testing.T) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pod := &api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
			Labels: map[string]string{"name": "foo"},
 | 
			
		||||
@@ -158,6 +159,8 @@ func TestEncode_Ptr(t *testing.T) {
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	obj := runtime.Object(pod)
 | 
			
		||||
 
 | 
			
		||||
@@ -89,6 +89,15 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
 | 
			
		||||
			j.LabelSelector, _ = labels.Parse("a=b")
 | 
			
		||||
			j.FieldSelector, _ = fields.ParseSelector("a=b")
 | 
			
		||||
		},
 | 
			
		||||
		func(j *api.PodSpec, c fuzz.Continue) {
 | 
			
		||||
			c.FuzzNoCustom(j)
 | 
			
		||||
			// has a default value
 | 
			
		||||
			ttl := int64(30)
 | 
			
		||||
			if c.RandBool() {
 | 
			
		||||
				ttl = int64(c.Uint32())
 | 
			
		||||
			}
 | 
			
		||||
			j.TerminationGracePeriodSeconds = &ttl
 | 
			
		||||
		},
 | 
			
		||||
		func(j *api.PodPhase, c fuzz.Continue) {
 | 
			
		||||
			statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
 | 
			
		||||
			*j = statuses[c.Rand.Intn(len(statuses))]
 | 
			
		||||
 
 | 
			
		||||
@@ -143,6 +143,10 @@ type ObjectMeta struct {
 | 
			
		||||
	// will send a hard termination signal to the container.
 | 
			
		||||
	DeletionTimestamp *util.Time `json:"deletionTimestamp,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// DeletionGracePeriodSeconds records the graceful deletion value set when graceful deletion
 | 
			
		||||
	// was requested. Represents the most recent grace period, and may only be shortened once set.
 | 
			
		||||
	DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// Labels are key value pairs that may be used to scope and select individual resources.
 | 
			
		||||
	// Label keys are of the form:
 | 
			
		||||
	//     label-key ::= prefixed-name | name
 | 
			
		||||
 
 | 
			
		||||
@@ -1176,6 +1176,12 @@ func convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *ObjectMeta
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
@@ -3591,6 +3597,12 @@ func convert_v1_ObjectMeta_To_api_ObjectMeta(in *ObjectMeta, out *api.ObjectMeta
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -1010,6 +1010,12 @@ func deepCopy_v1_ObjectMeta(in ObjectMeta, out *ObjectMeta, c *conversion.Cloner
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -113,6 +113,10 @@ func addDefaultingFuncs() {
 | 
			
		||||
			if obj.HostNetwork {
 | 
			
		||||
				defaultHostNetworkPorts(&obj.Containers)
 | 
			
		||||
			}
 | 
			
		||||
			if obj.TerminationGracePeriodSeconds == nil {
 | 
			
		||||
				period := int64(DefaultTerminationGracePeriodSeconds)
 | 
			
		||||
				obj.TerminationGracePeriodSeconds = &period
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func(obj *Probe) {
 | 
			
		||||
			if obj.TimeoutSeconds == 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -141,6 +141,10 @@ type ObjectMeta struct {
 | 
			
		||||
	// will send a hard termination signal to the container.
 | 
			
		||||
	DeletionTimestamp *util.Time `json:"deletionTimestamp,omitempty" description:"RFC 3339 date and time at which the object will be deleted; populated by the system when a graceful deletion is requested, read-only; if not set, graceful deletion of the object has not been requested; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"`
 | 
			
		||||
 | 
			
		||||
	// DeletionGracePeriodSeconds records the graceful deletion value set when graceful deletion
 | 
			
		||||
	// was requested. Represents the most recent grace period, and may only be shortened once set.
 | 
			
		||||
	DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" description:"number of seconds allowed for this object to gracefully terminate before it will be removed from the system; only set when deletionTimestamp is also set, read-only; may only be shortened"`
 | 
			
		||||
 | 
			
		||||
	// Labels are key value pairs that may be used to scope and select individual resources.
 | 
			
		||||
	// TODO: replace map[string]string with labels.LabelSet type
 | 
			
		||||
	Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize objects; may match selectors of replication controllers and services; see http://releases.k8s.io/HEAD/docs/user-guide/labels.md"`
 | 
			
		||||
@@ -858,6 +862,8 @@ const (
 | 
			
		||||
	// DNSDefault indicates that the pod should use the default (as
 | 
			
		||||
	// determined by kubelet) DNS settings.
 | 
			
		||||
	DNSDefault DNSPolicy = "Default"
 | 
			
		||||
 | 
			
		||||
	DefaultTerminationGracePeriodSeconds = 30
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PodSpec is a description of a pod
 | 
			
		||||
@@ -872,7 +878,7 @@ type PodSpec struct {
 | 
			
		||||
	// The grace period is the duration in seconds after the processes running in the pod are sent
 | 
			
		||||
	// a termination signal and the time when the processes are forcibly halted with a kill signal.
 | 
			
		||||
	// Set this value longer than the expected cleanup time for your process.
 | 
			
		||||
	TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process"`
 | 
			
		||||
	TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" description:"optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process; defaults to 30 seconds"`
 | 
			
		||||
	ActiveDeadlineSeconds         *int64 `json:"activeDeadlineSeconds,omitempty" description:"optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers; value must be a positive integer"`
 | 
			
		||||
	// Optional: Set DNS policy.  Defaults to "ClusterFirst"
 | 
			
		||||
	DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" description:"DNS policy for containers within the pod; one of 'ClusterFirst' or 'Default'"`
 | 
			
		||||
 
 | 
			
		||||
@@ -265,6 +265,16 @@ func ValidateObjectMetaUpdate(new, old *api.ObjectMeta) errs.ValidationErrorList
 | 
			
		||||
	} else {
 | 
			
		||||
		new.CreationTimestamp = old.CreationTimestamp
 | 
			
		||||
	}
 | 
			
		||||
	// an object can never remove a deletion timestamp or clear/change grace period seconds
 | 
			
		||||
	if !old.DeletionTimestamp.IsZero() {
 | 
			
		||||
		new.DeletionTimestamp = old.DeletionTimestamp
 | 
			
		||||
	}
 | 
			
		||||
	if old.DeletionGracePeriodSeconds != nil && new.DeletionGracePeriodSeconds == nil {
 | 
			
		||||
		new.DeletionGracePeriodSeconds = old.DeletionGracePeriodSeconds
 | 
			
		||||
	}
 | 
			
		||||
	if new.DeletionGracePeriodSeconds != nil && old.DeletionGracePeriodSeconds != nil && *new.DeletionGracePeriodSeconds != *old.DeletionGracePeriodSeconds {
 | 
			
		||||
		allErrs = append(allErrs, errs.NewFieldInvalid("deletionGracePeriodSeconds", new.DeletionGracePeriodSeconds, "field is immutable; may only be changed via deletion"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reject updates that don't specify a resource version
 | 
			
		||||
	if new.ResourceVersion == "" {
 | 
			
		||||
 
 | 
			
		||||
@@ -1317,6 +1317,9 @@ func TestValidatePod(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidatePodUpdate(t *testing.T) {
 | 
			
		||||
	now := util.Now()
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	grace2 := int64(31)
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		a       api.Pod
 | 
			
		||||
		b       api.Pod
 | 
			
		||||
@@ -1403,6 +1406,30 @@ func TestValidatePodUpdate(t *testing.T) {
 | 
			
		||||
			false,
 | 
			
		||||
			"more containers",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
 | 
			
		||||
				Spec:       api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}},
 | 
			
		||||
			},
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
			
		||||
				Spec:       api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}},
 | 
			
		||||
			},
 | 
			
		||||
			true,
 | 
			
		||||
			"deletion timestamp filled out",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace},
 | 
			
		||||
				Spec:       api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}},
 | 
			
		||||
			},
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace2},
 | 
			
		||||
				Spec:       api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}},
 | 
			
		||||
			},
 | 
			
		||||
			false,
 | 
			
		||||
			"deletion grace period seconds cleared",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
			
		||||
 
 | 
			
		||||
@@ -322,7 +322,8 @@ func FilterActivePods(pods []api.Pod) []*api.Pod {
 | 
			
		||||
	var result []*api.Pod
 | 
			
		||||
	for i := range pods {
 | 
			
		||||
		if api.PodSucceeded != pods[i].Status.Phase &&
 | 
			
		||||
			api.PodFailed != pods[i].Status.Phase {
 | 
			
		||||
			api.PodFailed != pods[i].Status.Phase &&
 | 
			
		||||
			pods[i].DeletionTimestamp == nil {
 | 
			
		||||
			result = append(result, &pods[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -310,7 +310,11 @@ func (e *EndpointController) syncService(key string) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if len(pod.Status.PodIP) == 0 {
 | 
			
		||||
				glog.V(4).Infof("Failed to find an IP for pod %s/%s", pod.Namespace, pod.Name)
 | 
			
		||||
				glog.V(5).Infof("Failed to find an IP for pod %s/%s", pod.Namespace, pod.Name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if pod.DeletionTimestamp != nil {
 | 
			
		||||
				glog.V(5).Infof("Pod is being deleted %s/%s", pod.Namespace, pod.Name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -213,6 +213,12 @@ func (rm *ReplicationManager) getPodController(pod *api.Pod) *api.ReplicationCon
 | 
			
		||||
// When a pod is created, enqueue the controller that manages it and update it's expectations.
 | 
			
		||||
func (rm *ReplicationManager) addPod(obj interface{}) {
 | 
			
		||||
	pod := obj.(*api.Pod)
 | 
			
		||||
	if pod.DeletionTimestamp != nil {
 | 
			
		||||
		// on a restart of the controller manager, it's possible a new pod shows up in a state that
 | 
			
		||||
		// is already pending deletion. Prevent the pod from being a creation observation.
 | 
			
		||||
		rm.deletePod(pod)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if rc := rm.getPodController(pod); rc != nil {
 | 
			
		||||
		rcKey, err := controller.KeyFunc(rc)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -234,6 +240,15 @@ func (rm *ReplicationManager) updatePod(old, cur interface{}) {
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: Write a unittest for this case
 | 
			
		||||
	curPod := cur.(*api.Pod)
 | 
			
		||||
	if curPod.DeletionTimestamp != nil {
 | 
			
		||||
		// when a pod is deleted gracefully it's deletion timestamp is first modified to reflect a grace period,
 | 
			
		||||
		// and after such time has passed, the kubelet actually deletes it from the store. We receive an update
 | 
			
		||||
		// for modification of the deletion timestamp and expect an rc to create more replicas asap, not wait
 | 
			
		||||
		// until the kubelet actually deletes the pod. This is different from the Phase of a pod changing, because
 | 
			
		||||
		// an rc never initiates a phase change, and so is never asleep waiting for the same.
 | 
			
		||||
		rm.deletePod(curPod)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if rc := rm.getPodController(curPod); rc != nil {
 | 
			
		||||
		rm.enqueueController(rc)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,12 @@ func deepCopy_api_ObjectMeta(in api.ObjectMeta, out *api.ObjectMeta, c *conversi
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,12 @@ func convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectM
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
@@ -115,6 +121,12 @@ func convert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectM
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,12 @@ func deepCopy_v1_ObjectMeta(in v1.ObjectMeta, out *v1.ObjectMeta, c *conversion.
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionTimestamp = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.DeletionGracePeriodSeconds != nil {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = new(int64)
 | 
			
		||||
		*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
			
		||||
	} else {
 | 
			
		||||
		out.DeletionGracePeriodSeconds = nil
 | 
			
		||||
	}
 | 
			
		||||
	if in.Labels != nil {
 | 
			
		||||
		out.Labels = make(map[string]string)
 | 
			
		||||
		for key, val := range in.Labels {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pods := &api.PodList{
 | 
			
		||||
		ListMeta: api.ListMeta{
 | 
			
		||||
			ResourceVersion: "15",
 | 
			
		||||
@@ -46,15 +47,17 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -563,6 +566,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func watchTestData() ([]api.Pod, []watch.Event) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pods := []api.Pod{
 | 
			
		||||
		{
 | 
			
		||||
			ObjectMeta: api.ObjectMeta{
 | 
			
		||||
@@ -571,8 +575,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
			
		||||
				ResourceVersion: "10",
 | 
			
		||||
			},
 | 
			
		||||
			Spec: api.PodSpec{
 | 
			
		||||
				RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
				DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
				RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
				DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
				TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -586,8 +591,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
			
		||||
					ResourceVersion: "11",
 | 
			
		||||
				},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -600,8 +606,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
			
		||||
					ResourceVersion: "12",
 | 
			
		||||
				},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestMerge(t *testing.T) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		obj       runtime.Object
 | 
			
		||||
		fragment  string
 | 
			
		||||
@@ -54,8 +55,9 @@ func TestMerge(t *testing.T) {
 | 
			
		||||
					Name: "foo",
 | 
			
		||||
				},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -122,8 +124,9 @@ func TestMerge(t *testing.T) {
 | 
			
		||||
							VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -417,7 +417,12 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even
 | 
			
		||||
		fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&pod.Spec))
 | 
			
		||||
		fmt.Fprintf(out, "Node:\t%s\n", pod.Spec.NodeName+"/"+pod.Status.HostIP)
 | 
			
		||||
		fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels))
 | 
			
		||||
		fmt.Fprintf(out, "Status:\t%s\n", string(pod.Status.Phase))
 | 
			
		||||
		if pod.DeletionTimestamp != nil {
 | 
			
		||||
			fmt.Fprintf(out, "Status:\tTerminating (expires %s)\n", pod.DeletionTimestamp.Time.Format(time.RFC1123Z))
 | 
			
		||||
			fmt.Fprintf(out, "Termination Grace Period:\t%ds\n", pod.DeletionGracePeriodSeconds)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(out, "Status:\t%s\n", string(pod.Status.Phase))
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(out, "Reason:\t%s\n", pod.Status.Reason)
 | 
			
		||||
		fmt.Fprintf(out, "Message:\t%s\n", pod.Status.Message)
 | 
			
		||||
		fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP)
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,7 @@ func fakeClientWith(testName string, t *testing.T, data map[string]string) Clien
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testData() (*api.PodList, *api.ServiceList) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pods := &api.PodList{
 | 
			
		||||
		ListMeta: api.ListMeta{
 | 
			
		||||
			ResourceVersion: "15",
 | 
			
		||||
@@ -91,15 +92,17 @@ func testData() (*api.PodList, *api.ServiceList) {
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,7 @@ func TestHelperCreate(t *testing.T) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		Resp     *http.Response
 | 
			
		||||
		RespFunc client.HTTPClientFunc
 | 
			
		||||
@@ -172,8 +173,9 @@ func TestHelperCreate(t *testing.T) {
 | 
			
		||||
			ExpectObject: &api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})},
 | 
			
		||||
@@ -381,6 +383,7 @@ func TestHelperReplace(t *testing.T) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		Resp      *http.Response
 | 
			
		||||
		RespFunc  client.HTTPClientFunc
 | 
			
		||||
@@ -418,8 +421,9 @@ func TestHelperReplace(t *testing.T) {
 | 
			
		||||
			ExpectObject: &api.Pod{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Overwrite: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -435,6 +435,9 @@ func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, showAll
 | 
			
		||||
			readyContainers++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if pod.DeletionTimestamp != nil {
 | 
			
		||||
		reason = "Terminating"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if withNamespace {
 | 
			
		||||
		if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -880,6 +880,7 @@ func TestUpdateExistingReplicationController(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestUpdateWithRetries(t *testing.T) {
 | 
			
		||||
	codec := testapi.Codec()
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	rc := &api.ReplicationController{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{Name: "rc",
 | 
			
		||||
			Labels: map[string]string{
 | 
			
		||||
@@ -897,8 +898,9 @@ func TestUpdateWithRetries(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ import (
 | 
			
		||||
func noDefault(*api.Pod) error { return nil }
 | 
			
		||||
 | 
			
		||||
func TestDecodeSinglePod(t *testing.T) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pod := &api.Pod{
 | 
			
		||||
		TypeMeta: api.TypeMeta{
 | 
			
		||||
			APIVersion: "",
 | 
			
		||||
@@ -41,8 +42,9 @@ func TestDecodeSinglePod(t *testing.T) {
 | 
			
		||||
			Namespace: "mynamespace",
 | 
			
		||||
		},
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
			Containers: []api.Container{{
 | 
			
		||||
				Name:                   "image",
 | 
			
		||||
				Image:                  "test/image",
 | 
			
		||||
@@ -91,6 +93,7 @@ func TestDecodeSinglePod(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDecodePodList(t *testing.T) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	pod := &api.Pod{
 | 
			
		||||
		TypeMeta: api.TypeMeta{
 | 
			
		||||
			APIVersion: "",
 | 
			
		||||
@@ -101,8 +104,9 @@ func TestDecodePodList(t *testing.T) {
 | 
			
		||||
			Namespace: "mynamespace",
 | 
			
		||||
		},
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
			Containers: []api.Container{{
 | 
			
		||||
				Name:                   "image",
 | 
			
		||||
				Image:                  "test/image",
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +69,7 @@ func writeTestFile(t *testing.T, dir, name string, contents string) *os.File {
 | 
			
		||||
 | 
			
		||||
func TestReadPodsFromFile(t *testing.T) {
 | 
			
		||||
	hostname := "random-test-hostname"
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	var testCases = []struct {
 | 
			
		||||
		desc     string
 | 
			
		||||
		pod      runtime.Object
 | 
			
		||||
@@ -98,9 +99,10 @@ func TestReadPodsFromFile(t *testing.T) {
 | 
			
		||||
					SelfLink:  getSelfLink("test-"+hostname, "mynamespace"),
 | 
			
		||||
				},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					NodeName:      hostname,
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					NodeName:                      hostname,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
					Containers: []api.Container{{
 | 
			
		||||
						Name:  "image",
 | 
			
		||||
						Image: "test/image",
 | 
			
		||||
 
 | 
			
		||||
@@ -123,6 +123,7 @@ func TestExtractInvalidPods(t *testing.T) {
 | 
			
		||||
func TestExtractPodsFromHTTP(t *testing.T) {
 | 
			
		||||
	hostname := "different-value"
 | 
			
		||||
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	var testCases = []struct {
 | 
			
		||||
		desc     string
 | 
			
		||||
		pods     runtime.Object
 | 
			
		||||
@@ -156,9 +157,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
			
		||||
						SelfLink: getSelfLink("foo-"+hostname, "mynamespace"),
 | 
			
		||||
					},
 | 
			
		||||
					Spec: api.PodSpec{
 | 
			
		||||
						NodeName:      hostname,
 | 
			
		||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
						NodeName:                      hostname,
 | 
			
		||||
						RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
						TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
 | 
			
		||||
						Containers: []api.Container{{
 | 
			
		||||
							Name:  "1",
 | 
			
		||||
							Image: "foo",
 | 
			
		||||
@@ -209,9 +212,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
			
		||||
						SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault),
 | 
			
		||||
					},
 | 
			
		||||
					Spec: api.PodSpec{
 | 
			
		||||
						NodeName:      hostname,
 | 
			
		||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
						NodeName:                      hostname,
 | 
			
		||||
						RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
						TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
 | 
			
		||||
						Containers: []api.Container{{
 | 
			
		||||
							Name:  "1",
 | 
			
		||||
							Image: "foo",
 | 
			
		||||
@@ -229,9 +234,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
			
		||||
						SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault),
 | 
			
		||||
					},
 | 
			
		||||
					Spec: api.PodSpec{
 | 
			
		||||
						NodeName:      hostname,
 | 
			
		||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
						NodeName:                      hostname,
 | 
			
		||||
						RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
						DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
						TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
 | 
			
		||||
						Containers: []api.Container{{
 | 
			
		||||
							Name:  "2",
 | 
			
		||||
							Image: "bar",
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ var testTTL uint64 = 60
 | 
			
		||||
 | 
			
		||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
 | 
			
		||||
	etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
 | 
			
		||||
	fakeClient.HideExpires = true
 | 
			
		||||
	return NewREST(etcdStorage, testTTL), fakeClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -341,6 +341,11 @@ func (e *Etcd) Get(ctx api.Context, name string) (runtime.Object, error) {
 | 
			
		||||
	return obj, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errAlreadyDeleting = fmt.Errorf("abort delete")
 | 
			
		||||
	errDeleteNow       = fmt.Errorf("delete now")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Delete removes the item from etcd.
 | 
			
		||||
func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
 | 
			
		||||
	key, err := e.KeyFunc(ctx, name)
 | 
			
		||||
@@ -367,13 +372,41 @@ func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions)
 | 
			
		||||
	if pendingGraceful {
 | 
			
		||||
		return e.finalizeDelete(obj, false)
 | 
			
		||||
	}
 | 
			
		||||
	if graceful && *options.GracePeriodSeconds != 0 {
 | 
			
		||||
	if graceful {
 | 
			
		||||
		trace.Step("Graceful deletion")
 | 
			
		||||
		out := e.NewFunc()
 | 
			
		||||
		if err := e.Storage.Set(key, obj, out, uint64(*options.GracePeriodSeconds)); err != nil {
 | 
			
		||||
		lastGraceful := int64(0)
 | 
			
		||||
		err := e.Storage.GuaranteedUpdate(
 | 
			
		||||
			key, out, false,
 | 
			
		||||
			storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) {
 | 
			
		||||
				graceful, pendingGraceful, err := rest.BeforeDelete(e.DeleteStrategy, ctx, existing, options)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
				if pendingGraceful {
 | 
			
		||||
					return nil, errAlreadyDeleting
 | 
			
		||||
				}
 | 
			
		||||
				if !graceful {
 | 
			
		||||
					return nil, errDeleteNow
 | 
			
		||||
				}
 | 
			
		||||
				lastGraceful = *options.GracePeriodSeconds
 | 
			
		||||
				return existing, nil
 | 
			
		||||
			}),
 | 
			
		||||
		)
 | 
			
		||||
		switch err {
 | 
			
		||||
		case nil:
 | 
			
		||||
			if lastGraceful > 0 {
 | 
			
		||||
				return out, nil
 | 
			
		||||
			}
 | 
			
		||||
			// fall through and delete immediately
 | 
			
		||||
		case errDeleteNow:
 | 
			
		||||
			// we've updated the object to have a zero grace period, or it's already at 0, so
 | 
			
		||||
			// we should fall through and truly delete the object.
 | 
			
		||||
		case errAlreadyDeleting:
 | 
			
		||||
			return e.finalizeDelete(obj, true)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, etcderr.InterpretUpdateError(err, e.EndpointName, name)
 | 
			
		||||
		}
 | 
			
		||||
		return e.finalizeDelete(out, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// delete immediately, or no graceful deletion supported
 | 
			
		||||
 
 | 
			
		||||
@@ -212,7 +212,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
			
		||||
	key, _ := storage.KeyFunc(ctx, "foo")
 | 
			
		||||
	key = etcdtest.AddPrefix(key)
 | 
			
		||||
	pvStart := validNewPersistentVolume("foo")
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvStart), 1)
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvStart), 0)
 | 
			
		||||
 | 
			
		||||
	pvIn := &api.PersistentVolume{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
 
 | 
			
		||||
@@ -209,7 +209,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
			
		||||
	key, _ := storage.KeyFunc(ctx, "foo")
 | 
			
		||||
	key = etcdtest.AddPrefix(key)
 | 
			
		||||
	pvcStart := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvcStart), 1)
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvcStart), 0)
 | 
			
		||||
 | 
			
		||||
	pvc := &api.PersistentVolumeClaim{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ func newStorage(t *testing.T) (*REST, *BindingREST, *StatusREST, *tools.FakeEtcd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validNewPod() *api.Pod {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	return &api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
			Name:      "foo",
 | 
			
		||||
@@ -56,6 +57,8 @@ func validNewPod() *api.Pod {
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
			Containers: []api.Container{
 | 
			
		||||
				{
 | 
			
		||||
					Name:            "foo",
 | 
			
		||||
@@ -783,6 +786,7 @@ func TestEtcdUpdateScheduled(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}), 1)
 | 
			
		||||
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	podIn := api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
			Name:            "foo",
 | 
			
		||||
@@ -804,6 +808,8 @@ func TestEtcdUpdateScheduled(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err := storage.Update(ctx, &podIn)
 | 
			
		||||
@@ -844,7 +850,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &podStart), 1)
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &podStart), 0)
 | 
			
		||||
 | 
			
		||||
	podIn := api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
@@ -873,6 +879,8 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	expected := podStart
 | 
			
		||||
	expected.ResourceVersion = "2"
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	expected.Spec.TerminationGracePeriodSeconds = &grace
 | 
			
		||||
	expected.Spec.RestartPolicy = api.RestartPolicyAlways
 | 
			
		||||
	expected.Spec.DNSPolicy = api.DNSClusterFirst
 | 
			
		||||
	expected.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent
 | 
			
		||||
 
 | 
			
		||||
@@ -100,6 +100,7 @@ func (podStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
 | 
			
		||||
	newPod := obj.(*api.Pod)
 | 
			
		||||
	oldPod := old.(*api.Pod)
 | 
			
		||||
	newPod.Spec = oldPod.Spec
 | 
			
		||||
	newPod.DeletionTimestamp = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (podStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
 | 
			
		||||
 
 | 
			
		||||
@@ -239,7 +239,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
			
		||||
	key, _ := storage.KeyFunc(ctx, "foo")
 | 
			
		||||
	key = etcdtest.AddPrefix(key)
 | 
			
		||||
	resourcequotaStart := validNewResourceQuota()
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), resourcequotaStart), 1)
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), resourcequotaStart), 0)
 | 
			
		||||
 | 
			
		||||
	resourcequotaIn := &api.ResourceQuota{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
 
 | 
			
		||||
@@ -54,11 +54,13 @@ func newTestCacher(client tools.EtcdClient) *storage.Cacher {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeTestPod(name string) *api.Pod {
 | 
			
		||||
	gracePeriod := int64(30)
 | 
			
		||||
	return &api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{Namespace: "ns", Name: name},
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			TerminationGracePeriodSeconds: &gracePeriod,
 | 
			
		||||
			DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -394,14 +394,21 @@ func (h *etcdHelper) GuaranteedUpdate(key string, ptrToType runtime.Object, igno
 | 
			
		||||
		ttl := uint64(0)
 | 
			
		||||
		if node != nil {
 | 
			
		||||
			index = node.ModifiedIndex
 | 
			
		||||
			if node.TTL > 0 {
 | 
			
		||||
			if node.TTL != 0 {
 | 
			
		||||
				ttl = uint64(node.TTL)
 | 
			
		||||
			}
 | 
			
		||||
			if node.Expiration != nil && ttl == 0 {
 | 
			
		||||
				ttl = 1
 | 
			
		||||
			}
 | 
			
		||||
		} else if res != nil {
 | 
			
		||||
			index = res.EtcdIndex
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if newTTL != nil {
 | 
			
		||||
			if ttl != 0 && *newTTL == 0 {
 | 
			
		||||
				// TODO: remove this after we have verified this is no longer an issue
 | 
			
		||||
				glog.V(4).Infof("GuaranteedUpdate is clearing TTL for %q, may not be intentional", key)
 | 
			
		||||
			}
 | 
			
		||||
			ttl = *newTTL
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -123,28 +123,32 @@ func TestList(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	expect := api.PodList{
 | 
			
		||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
			
		||||
		Items: []api.Pod{
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -206,6 +210,7 @@ func TestListAcrossDirectories(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	expect := api.PodList{
 | 
			
		||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
			
		||||
		Items: []api.Pod{
 | 
			
		||||
@@ -213,22 +218,25 @@ func TestListAcrossDirectories(t *testing.T) {
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -278,28 +286,32 @@ func TestListExcludesDirectories(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	expect := api.PodList{
 | 
			
		||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
			
		||||
		Items: []api.Pod{
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
					RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
					DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
					TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -319,11 +331,13 @@ func TestGet(t *testing.T) {
 | 
			
		||||
	fakeClient := tools.NewFakeEtcdClient(t)
 | 
			
		||||
	helper := newEtcdHelper(fakeClient, testapi.Codec(), etcdtest.PathPrefix())
 | 
			
		||||
	key := etcdtest.AddPrefix("/some/key")
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	expect := api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &expect), 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ const (
 | 
			
		||||
	EtcdSet    = "set"
 | 
			
		||||
	EtcdCAS    = "compareAndSwap"
 | 
			
		||||
	EtcdDelete = "delete"
 | 
			
		||||
	EtcdExpire = "expire"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TransformFunc attempts to convert an object to another object for use with a watcher.
 | 
			
		||||
@@ -353,7 +354,7 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) {
 | 
			
		||||
		w.sendAdd(res)
 | 
			
		||||
	case EtcdSet, EtcdCAS:
 | 
			
		||||
		w.sendModify(res)
 | 
			
		||||
	case EtcdDelete:
 | 
			
		||||
	case EtcdDelete, EtcdExpire:
 | 
			
		||||
		w.sendDelete(res)
 | 
			
		||||
	default:
 | 
			
		||||
		glog.Errorf("unknown action: %v", res.Action)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/coreos/go-etcd/etcd"
 | 
			
		||||
)
 | 
			
		||||
@@ -52,6 +53,8 @@ type FakeEtcdClient struct {
 | 
			
		||||
	TestIndex   bool
 | 
			
		||||
	ChangeIndex uint64
 | 
			
		||||
	LastSetTTL  uint64
 | 
			
		||||
	// Will avoid setting the expires header on objects to make comparison easier
 | 
			
		||||
	HideExpires bool
 | 
			
		||||
	Machines    []string
 | 
			
		||||
 | 
			
		||||
	// Will become valid after Watch is called; tester may write to it. Tester may
 | 
			
		||||
@@ -175,6 +178,11 @@ func (f *FakeEtcdClient) setLocked(key, value string, ttl uint64) (*etcd.Respons
 | 
			
		||||
		prevResult := f.Data[key]
 | 
			
		||||
		createdIndex := prevResult.R.Node.CreatedIndex
 | 
			
		||||
		f.t.Logf("updating %v, index %v -> %v (ttl: %d)", key, createdIndex, i, ttl)
 | 
			
		||||
		var expires *time.Time
 | 
			
		||||
		if !f.HideExpires && ttl > 0 {
 | 
			
		||||
			now := time.Now()
 | 
			
		||||
			expires = &now
 | 
			
		||||
		}
 | 
			
		||||
		result := EtcdResponseWithError{
 | 
			
		||||
			R: &etcd.Response{
 | 
			
		||||
				Node: &etcd.Node{
 | 
			
		||||
@@ -182,6 +190,7 @@ func (f *FakeEtcdClient) setLocked(key, value string, ttl uint64) (*etcd.Respons
 | 
			
		||||
					CreatedIndex:  createdIndex,
 | 
			
		||||
					ModifiedIndex: i,
 | 
			
		||||
					TTL:           int64(ttl),
 | 
			
		||||
					Expiration:    expires,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -132,11 +132,13 @@ func PriorityTwo(pod *api.Pod, podLister algorithm.PodLister, minionLister algor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDefaultErrorFunc(t *testing.T) {
 | 
			
		||||
	grace := int64(30)
 | 
			
		||||
	testPod := &api.Pod{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
 | 
			
		||||
		Spec: api.PodSpec{
 | 
			
		||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
			
		||||
			RestartPolicy:                 api.RestartPolicyAlways,
 | 
			
		||||
			DNSPolicy:                     api.DNSClusterFirst,
 | 
			
		||||
			TerminationGracePeriodSeconds: &grace,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	handler := util.FakeHandler{
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,59 @@ func TestGet(t *testing.T) {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWriteTTL(t *testing.T) {
 | 
			
		||||
	client := framework.NewEtcdClient()
 | 
			
		||||
	etcdStorage := etcd.NewEtcdStorage(client, testapi.Codec(), "")
 | 
			
		||||
	framework.WithEtcdKey(func(key string) {
 | 
			
		||||
		testObject := api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "foo"}}
 | 
			
		||||
		if err := etcdStorage.Set(key, &testObject, nil, 0); err != nil {
 | 
			
		||||
			t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		result := &api.ServiceAccount{}
 | 
			
		||||
		err := etcdStorage.GuaranteedUpdate(key, result, false, func(obj runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
 | 
			
		||||
			if in, ok := obj.(*api.ServiceAccount); !ok || in.Name != "foo" {
 | 
			
		||||
				t.Fatalf("unexpected existing object: %v", obj)
 | 
			
		||||
			}
 | 
			
		||||
			if res.TTL != 0 {
 | 
			
		||||
				t.Fatalf("unexpected TTL: %#v", res)
 | 
			
		||||
			}
 | 
			
		||||
			ttl := uint64(10)
 | 
			
		||||
			out := &api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "out"}}
 | 
			
		||||
			return out, &ttl, nil
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if result.Name != "out" {
 | 
			
		||||
			t.Errorf("unexpected response: %#v", result)
 | 
			
		||||
		}
 | 
			
		||||
		if res, err := client.Get(key, false, false); err != nil || res == nil || res.Node.TTL != 10 {
 | 
			
		||||
			t.Fatalf("unexpected get: %v %#v", err, res)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		result = &api.ServiceAccount{}
 | 
			
		||||
		err = etcdStorage.GuaranteedUpdate(key, result, false, func(obj runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
 | 
			
		||||
			if in, ok := obj.(*api.ServiceAccount); !ok || in.Name != "out" {
 | 
			
		||||
				t.Fatalf("unexpected existing object: %v", obj)
 | 
			
		||||
			}
 | 
			
		||||
			if res.TTL <= 1 {
 | 
			
		||||
				t.Fatalf("unexpected TTL: %#v", res)
 | 
			
		||||
			}
 | 
			
		||||
			out := &api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "out2"}}
 | 
			
		||||
			return out, nil, nil
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if result.Name != "out2" {
 | 
			
		||||
			t.Errorf("unexpected response: %#v", result)
 | 
			
		||||
		}
 | 
			
		||||
		if res, err := client.Get(key, false, false); err != nil || res == nil || res.Node.TTL <= 1 {
 | 
			
		||||
			t.Fatalf("unexpected get: %v %#v", err, res)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWatch(t *testing.T) {
 | 
			
		||||
	client := framework.NewEtcdClient()
 | 
			
		||||
	etcdStorage := etcd.NewEtcdStorage(client, testapi.Codec(), etcdtest.PathPrefix())
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user