mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +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",
 | 
					      "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"
 | 
					      "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": {
 | 
					     "labels": {
 | 
				
			||||||
      "type": "any",
 | 
					      "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"
 | 
					      "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": {
 | 
					     "terminationGracePeriodSeconds": {
 | 
				
			||||||
      "type": "integer",
 | 
					      "type": "integer",
 | 
				
			||||||
      "format": "int64",
 | 
					      "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": {
 | 
					     "activeDeadlineSeconds": {
 | 
				
			||||||
      "type": "integer",
 | 
					      "type": "integer",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ spec:
 | 
				
			|||||||
      mountPath: /varlog
 | 
					      mountPath: /varlog
 | 
				
			||||||
    - name: containers
 | 
					    - name: containers
 | 
				
			||||||
      mountPath: /var/lib/docker/containers
 | 
					      mountPath: /var/lib/docker/containers
 | 
				
			||||||
 | 
					  terminationGracePeriodSeconds: 30
 | 
				
			||||||
  volumes:
 | 
					  volumes:
 | 
				
			||||||
  - name: varlog
 | 
					  - name: varlog
 | 
				
			||||||
    hostPath:
 | 
					    hostPath:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ spec:
 | 
				
			|||||||
      mountPath: /varlog
 | 
					      mountPath: /varlog
 | 
				
			||||||
    - name: containers
 | 
					    - name: containers
 | 
				
			||||||
      mountPath: /var/lib/docker/containers
 | 
					      mountPath: /var/lib/docker/containers
 | 
				
			||||||
 | 
					  terminationGracePeriodSeconds: 30
 | 
				
			||||||
  volumes:
 | 
					  volumes:
 | 
				
			||||||
  - name: varlog
 | 
					  - name: varlog
 | 
				
			||||||
    hostPath:
 | 
					    hostPath:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -182,6 +182,7 @@ spec:
 | 
				
			|||||||
      mountPath: /varlog
 | 
					      mountPath: /varlog
 | 
				
			||||||
    - name: containers
 | 
					    - name: containers
 | 
				
			||||||
      mountPath: /var/lib/docker/containers
 | 
					      mountPath: /var/lib/docker/containers
 | 
				
			||||||
 | 
					  terminationGracePeriodSeconds: 30
 | 
				
			||||||
  volumes:
 | 
					  volumes:
 | 
				
			||||||
  - name: varlog
 | 
					  - name: varlog
 | 
				
			||||||
    hostPath:
 | 
					    hostPath:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1011,6 +1011,12 @@ func deepCopy_api_ObjectMeta(in ObjectMeta, out *ObjectMeta, c *conversion.Clone
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,8 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Obje
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		objectMeta.Namespace = api.NamespaceNone
 | 
							objectMeta.Namespace = api.NamespaceNone
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						objectMeta.DeletionTimestamp = nil
 | 
				
			||||||
 | 
						objectMeta.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
	strategy.PrepareForCreate(obj)
 | 
						strategy.PrepareForCreate(obj)
 | 
				
			||||||
	api.FillObjectMetaSystemFields(ctx, objectMeta)
 | 
						api.FillObjectMetaSystemFields(ctx, objectMeta)
 | 
				
			||||||
	api.GenerateName(strategy, objectMeta)
 | 
						api.GenerateName(strategy, objectMeta)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,8 +17,11 @@ limitations under the License.
 | 
				
			|||||||
package rest
 | 
					package rest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes
 | 
					// 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 {
 | 
						if strategy == nil {
 | 
				
			||||||
		return false, false, nil
 | 
							return false, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, kerr := objectMetaAndKind(strategy, obj)
 | 
						objectMeta, _, kerr := objectMetaAndKind(strategy, obj)
 | 
				
			||||||
	if kerr != nil {
 | 
						if kerr != nil {
 | 
				
			||||||
		return false, false, kerr
 | 
							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) {
 | 
						if !strategy.CheckGracefulDelete(obj, options) {
 | 
				
			||||||
		return false, false, nil
 | 
							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
 | 
						return true, false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,9 +123,9 @@ func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object
 | 
				
			|||||||
// Test deleting an object.
 | 
					// Test deleting an object.
 | 
				
			||||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
					// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
				
			||||||
func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
 | 
					func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
 | 
				
			||||||
	t.testDeleteNonExist(createFn)
 | 
						t.TestDeleteNonExist(createFn)
 | 
				
			||||||
	t.testDeleteNoGraceful(createFn, wasGracefulFn)
 | 
						t.TestDeleteNoGraceful(createFn, wasGracefulFn)
 | 
				
			||||||
	t.testDeleteInvokesValidation(invalid...)
 | 
						t.TestDeleteInvokesValidation(invalid...)
 | 
				
			||||||
	// TODO: Test delete namespace mismatch rejection
 | 
						// TODO: Test delete namespace mismatch rejection
 | 
				
			||||||
	// once #5684 is fixed.
 | 
						// once #5684 is fixed.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -133,8 +133,11 @@ func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func()
 | 
				
			|||||||
// Test graceful deletion.
 | 
					// Test graceful deletion.
 | 
				
			||||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
					// TODO(wojtek-t): Change it to use AssignFunc instead.
 | 
				
			||||||
func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
					func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
				
			||||||
	t.testDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
 | 
						t.TestDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
 | 
				
			||||||
	t.testDeleteGracefulUsesZeroOnNil(createFn(), 0)
 | 
						t.TestDeleteGracefulWithValue(createFn(), expectedGrace, wasGracefulFn)
 | 
				
			||||||
 | 
						t.TestDeleteGracefulUsesZeroOnNil(createFn(), 0)
 | 
				
			||||||
 | 
						t.TestDeleteGracefulExtend(createFn(), expectedGrace, wasGracefulFn)
 | 
				
			||||||
 | 
						t.TestDeleteGracefulImmediate(createFn(), expectedGrace, wasGracefulFn)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Test getting object.
 | 
					// Test getting object.
 | 
				
			||||||
@@ -316,7 +319,7 @@ func (t *Tester) testUpdateFailsOnVersion(older runtime.Object) {
 | 
				
			|||||||
// =============================================================================
 | 
					// =============================================================================
 | 
				
			||||||
// Deletion tests.
 | 
					// Deletion tests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Tester) testDeleteInvokesValidation(invalid ...runtime.Object) {
 | 
					func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) {
 | 
				
			||||||
	for i, obj := range invalid {
 | 
						for i, obj := range invalid {
 | 
				
			||||||
		objectMeta := t.getObjectMetaOrFail(obj)
 | 
							objectMeta := t.getObjectMetaOrFail(obj)
 | 
				
			||||||
		ctx := t.TestContext()
 | 
							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()
 | 
						existing := createFn()
 | 
				
			||||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
						objectMeta := t.getObjectMetaOrFail(existing)
 | 
				
			||||||
	context := t.TestContext()
 | 
						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()
 | 
						existing := createFn()
 | 
				
			||||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
						objectMeta := t.getObjectMetaOrFail(existing)
 | 
				
			||||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
						ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
				
			||||||
@@ -356,25 +362,142 @@ func (t *Tester) testDeleteNoGraceful(createFn func() runtime.Object, wasGracefu
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// =============================================================================
 | 
					func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
				
			||||||
// Graceful Deletion tests.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (t *Tester) testDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
 | 
					 | 
				
			||||||
	objectMeta := t.getObjectMetaOrFail(existing)
 | 
						objectMeta := t.getObjectMetaOrFail(existing)
 | 
				
			||||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
						ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
				
			||||||
	_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
 | 
						_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							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)
 | 
							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() {
 | 
						if !wasGracefulFn() {
 | 
				
			||||||
		t.Errorf("did not gracefully delete resource")
 | 
							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)
 | 
						objectMeta := t.getObjectMetaOrFail(existing)
 | 
				
			||||||
	ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
						ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
 | 
				
			||||||
	_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
 | 
						_, 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)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(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) {
 | 
					func TestEncode_Ptr(t *testing.T) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Labels: map[string]string{"name": "foo"},
 | 
								Labels: map[string]string{"name": "foo"},
 | 
				
			||||||
@@ -158,6 +159,8 @@ func TestEncode_Ptr(t *testing.T) {
 | 
				
			|||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	obj := runtime.Object(pod)
 | 
						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.LabelSelector, _ = labels.Parse("a=b")
 | 
				
			||||||
			j.FieldSelector, _ = fields.ParseSelector("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) {
 | 
							func(j *api.PodPhase, c fuzz.Continue) {
 | 
				
			||||||
			statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
 | 
								statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
 | 
				
			||||||
			*j = statuses[c.Rand.Intn(len(statuses))]
 | 
								*j = statuses[c.Rand.Intn(len(statuses))]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,6 +143,10 @@ type ObjectMeta struct {
 | 
				
			|||||||
	// will send a hard termination signal to the container.
 | 
						// will send a hard termination signal to the container.
 | 
				
			||||||
	DeletionTimestamp *util.Time `json:"deletionTimestamp,omitempty"`
 | 
						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.
 | 
						// Labels are key value pairs that may be used to scope and select individual resources.
 | 
				
			||||||
	// Label keys are of the form:
 | 
						// Label keys are of the form:
 | 
				
			||||||
	//     label-key ::= prefixed-name | name
 | 
						//     label-key ::= prefixed-name | name
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1176,6 +1176,12 @@ func convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *ObjectMeta
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
@@ -3591,6 +3597,12 @@ func convert_v1_ObjectMeta_To_api_ObjectMeta(in *ObjectMeta, out *api.ObjectMeta
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1010,6 +1010,12 @@ func deepCopy_v1_ObjectMeta(in ObjectMeta, out *ObjectMeta, c *conversion.Cloner
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,6 +113,10 @@ func addDefaultingFuncs() {
 | 
				
			|||||||
			if obj.HostNetwork {
 | 
								if obj.HostNetwork {
 | 
				
			||||||
				defaultHostNetworkPorts(&obj.Containers)
 | 
									defaultHostNetworkPorts(&obj.Containers)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if obj.TerminationGracePeriodSeconds == nil {
 | 
				
			||||||
 | 
									period := int64(DefaultTerminationGracePeriodSeconds)
 | 
				
			||||||
 | 
									obj.TerminationGracePeriodSeconds = &period
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		func(obj *Probe) {
 | 
							func(obj *Probe) {
 | 
				
			||||||
			if obj.TimeoutSeconds == 0 {
 | 
								if obj.TimeoutSeconds == 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,6 +141,10 @@ type ObjectMeta struct {
 | 
				
			|||||||
	// will send a hard termination signal to the container.
 | 
						// 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"`
 | 
						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.
 | 
						// Labels are key value pairs that may be used to scope and select individual resources.
 | 
				
			||||||
	// TODO: replace map[string]string with labels.LabelSet type
 | 
						// 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"`
 | 
						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
 | 
						// DNSDefault indicates that the pod should use the default (as
 | 
				
			||||||
	// determined by kubelet) DNS settings.
 | 
						// determined by kubelet) DNS settings.
 | 
				
			||||||
	DNSDefault DNSPolicy = "Default"
 | 
						DNSDefault DNSPolicy = "Default"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DefaultTerminationGracePeriodSeconds = 30
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PodSpec is a description of a pod
 | 
					// 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
 | 
						// 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.
 | 
						// 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.
 | 
						// 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"`
 | 
						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"
 | 
						// 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'"`
 | 
						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 {
 | 
						} else {
 | 
				
			||||||
		new.CreationTimestamp = old.CreationTimestamp
 | 
							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
 | 
						// Reject updates that don't specify a resource version
 | 
				
			||||||
	if new.ResourceVersion == "" {
 | 
						if new.ResourceVersion == "" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1317,6 +1317,9 @@ func TestValidatePod(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestValidatePodUpdate(t *testing.T) {
 | 
					func TestValidatePodUpdate(t *testing.T) {
 | 
				
			||||||
 | 
						now := util.Now()
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
 | 
						grace2 := int64(31)
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		a       api.Pod
 | 
							a       api.Pod
 | 
				
			||||||
		b       api.Pod
 | 
							b       api.Pod
 | 
				
			||||||
@@ -1403,6 +1406,30 @@ func TestValidatePodUpdate(t *testing.T) {
 | 
				
			|||||||
			false,
 | 
								false,
 | 
				
			||||||
			"more containers",
 | 
								"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{
 | 
								api.Pod{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -322,7 +322,8 @@ func FilterActivePods(pods []api.Pod) []*api.Pod {
 | 
				
			|||||||
	var result []*api.Pod
 | 
						var result []*api.Pod
 | 
				
			||||||
	for i := range pods {
 | 
						for i := range pods {
 | 
				
			||||||
		if api.PodSucceeded != pods[i].Status.Phase &&
 | 
							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])
 | 
								result = append(result, &pods[i])
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,7 +310,11 @@ func (e *EndpointController) syncService(key string) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if len(pod.Status.PodIP) == 0 {
 | 
								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
 | 
									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.
 | 
					// When a pod is created, enqueue the controller that manages it and update it's expectations.
 | 
				
			||||||
func (rm *ReplicationManager) addPod(obj interface{}) {
 | 
					func (rm *ReplicationManager) addPod(obj interface{}) {
 | 
				
			||||||
	pod := obj.(*api.Pod)
 | 
						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 {
 | 
						if rc := rm.getPodController(pod); rc != nil {
 | 
				
			||||||
		rcKey, err := controller.KeyFunc(rc)
 | 
							rcKey, err := controller.KeyFunc(rc)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -234,6 +240,15 @@ func (rm *ReplicationManager) updatePod(old, cur interface{}) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	// TODO: Write a unittest for this case
 | 
						// TODO: Write a unittest for this case
 | 
				
			||||||
	curPod := cur.(*api.Pod)
 | 
						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 {
 | 
						if rc := rm.getPodController(curPod); rc != nil {
 | 
				
			||||||
		rm.enqueueController(rc)
 | 
							rm.enqueueController(rc)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,12 @@ func deepCopy_api_ObjectMeta(in api.ObjectMeta, out *api.ObjectMeta, c *conversi
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,12 @@ func convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectM
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
@@ -115,6 +121,12 @@ func convert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectM
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,6 +70,12 @@ func deepCopy_v1_ObjectMeta(in v1.ObjectMeta, out *v1.ObjectMeta, c *conversion.
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		out.DeletionTimestamp = nil
 | 
							out.DeletionTimestamp = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.DeletionGracePeriodSeconds != nil {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = new(int64)
 | 
				
			||||||
 | 
							*out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							out.DeletionGracePeriodSeconds = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if in.Labels != nil {
 | 
						if in.Labels != nil {
 | 
				
			||||||
		out.Labels = make(map[string]string)
 | 
							out.Labels = make(map[string]string)
 | 
				
			||||||
		for key, val := range in.Labels {
 | 
							for key, val := range in.Labels {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
 | 
					func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pods := &api.PodList{
 | 
						pods := &api.PodList{
 | 
				
			||||||
		ListMeta: api.ListMeta{
 | 
							ListMeta: api.ListMeta{
 | 
				
			||||||
			ResourceVersion: "15",
 | 
								ResourceVersion: "15",
 | 
				
			||||||
@@ -46,15 +47,17 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -563,6 +566,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
func watchTestData() ([]api.Pod, []watch.Event) {
 | 
					func watchTestData() ([]api.Pod, []watch.Event) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pods := []api.Pod{
 | 
						pods := []api.Pod{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ObjectMeta: api.ObjectMeta{
 | 
								ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
@@ -571,8 +575,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
				
			|||||||
				ResourceVersion: "10",
 | 
									ResourceVersion: "10",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Spec: api.PodSpec{
 | 
								Spec: api.PodSpec{
 | 
				
			||||||
				RestartPolicy: api.RestartPolicyAlways,
 | 
									RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
				DNSPolicy:     api.DNSClusterFirst,
 | 
									DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
									TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -586,8 +591,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
				
			|||||||
					ResourceVersion: "11",
 | 
										ResourceVersion: "11",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -600,8 +606,9 @@ func watchTestData() ([]api.Pod, []watch.Event) {
 | 
				
			|||||||
					ResourceVersion: "12",
 | 
										ResourceVersion: "12",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMerge(t *testing.T) {
 | 
					func TestMerge(t *testing.T) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		obj       runtime.Object
 | 
							obj       runtime.Object
 | 
				
			||||||
		fragment  string
 | 
							fragment  string
 | 
				
			||||||
@@ -54,8 +55,9 @@ func TestMerge(t *testing.T) {
 | 
				
			|||||||
					Name: "foo",
 | 
										Name: "foo",
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -122,8 +124,9 @@ func TestMerge(t *testing.T) {
 | 
				
			|||||||
							VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}},
 | 
												VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}},
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										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, "Image(s):\t%s\n", makeImageList(&pod.Spec))
 | 
				
			||||||
		fmt.Fprintf(out, "Node:\t%s\n", pod.Spec.NodeName+"/"+pod.Status.HostIP)
 | 
							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, "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, "Reason:\t%s\n", pod.Status.Reason)
 | 
				
			||||||
		fmt.Fprintf(out, "Message:\t%s\n", pod.Status.Message)
 | 
							fmt.Fprintf(out, "Message:\t%s\n", pod.Status.Message)
 | 
				
			||||||
		fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP)
 | 
							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) {
 | 
					func testData() (*api.PodList, *api.ServiceList) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pods := &api.PodList{
 | 
						pods := &api.PodList{
 | 
				
			||||||
		ListMeta: api.ListMeta{
 | 
							ListMeta: api.ListMeta{
 | 
				
			||||||
			ResourceVersion: "15",
 | 
								ResourceVersion: "15",
 | 
				
			||||||
@@ -91,15 +92,17 @@ func testData() (*api.PodList, *api.ServiceList) {
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,6 +128,7 @@ func TestHelperCreate(t *testing.T) {
 | 
				
			|||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		Resp     *http.Response
 | 
							Resp     *http.Response
 | 
				
			||||||
		RespFunc client.HTTPClientFunc
 | 
							RespFunc client.HTTPClientFunc
 | 
				
			||||||
@@ -172,8 +173,9 @@ func TestHelperCreate(t *testing.T) {
 | 
				
			|||||||
			ExpectObject: &api.Pod{
 | 
								ExpectObject: &api.Pod{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})},
 | 
								Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})},
 | 
				
			||||||
@@ -381,6 +383,7 @@ func TestHelperReplace(t *testing.T) {
 | 
				
			|||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		Resp      *http.Response
 | 
							Resp      *http.Response
 | 
				
			||||||
		RespFunc  client.HTTPClientFunc
 | 
							RespFunc  client.HTTPClientFunc
 | 
				
			||||||
@@ -418,8 +421,9 @@ func TestHelperReplace(t *testing.T) {
 | 
				
			|||||||
			ExpectObject: &api.Pod{
 | 
								ExpectObject: &api.Pod{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Overwrite: true,
 | 
								Overwrite: true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -435,6 +435,9 @@ func printPod(pod *api.Pod, w io.Writer, withNamespace bool, wide bool, showAll
 | 
				
			|||||||
			readyContainers++
 | 
								readyContainers++
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if pod.DeletionTimestamp != nil {
 | 
				
			||||||
 | 
							reason = "Terminating"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if withNamespace {
 | 
						if withNamespace {
 | 
				
			||||||
		if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
 | 
							if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -880,6 +880,7 @@ func TestUpdateExistingReplicationController(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestUpdateWithRetries(t *testing.T) {
 | 
					func TestUpdateWithRetries(t *testing.T) {
 | 
				
			||||||
	codec := testapi.Codec()
 | 
						codec := testapi.Codec()
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	rc := &api.ReplicationController{
 | 
						rc := &api.ReplicationController{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "rc",
 | 
							ObjectMeta: api.ObjectMeta{Name: "rc",
 | 
				
			||||||
			Labels: map[string]string{
 | 
								Labels: map[string]string{
 | 
				
			||||||
@@ -897,8 +898,9 @@ func TestUpdateWithRetries(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
func noDefault(*api.Pod) error { return nil }
 | 
					func noDefault(*api.Pod) error { return nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDecodeSinglePod(t *testing.T) {
 | 
					func TestDecodeSinglePod(t *testing.T) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		TypeMeta: api.TypeMeta{
 | 
							TypeMeta: api.TypeMeta{
 | 
				
			||||||
			APIVersion: "",
 | 
								APIVersion: "",
 | 
				
			||||||
@@ -41,8 +42,9 @@ func TestDecodeSinglePod(t *testing.T) {
 | 
				
			|||||||
			Namespace: "mynamespace",
 | 
								Namespace: "mynamespace",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
			Containers: []api.Container{{
 | 
								Containers: []api.Container{{
 | 
				
			||||||
				Name:                   "image",
 | 
									Name:                   "image",
 | 
				
			||||||
				Image:                  "test/image",
 | 
									Image:                  "test/image",
 | 
				
			||||||
@@ -91,6 +93,7 @@ func TestDecodeSinglePod(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDecodePodList(t *testing.T) {
 | 
					func TestDecodePodList(t *testing.T) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	pod := &api.Pod{
 | 
						pod := &api.Pod{
 | 
				
			||||||
		TypeMeta: api.TypeMeta{
 | 
							TypeMeta: api.TypeMeta{
 | 
				
			||||||
			APIVersion: "",
 | 
								APIVersion: "",
 | 
				
			||||||
@@ -101,8 +104,9 @@ func TestDecodePodList(t *testing.T) {
 | 
				
			|||||||
			Namespace: "mynamespace",
 | 
								Namespace: "mynamespace",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
			Containers: []api.Container{{
 | 
								Containers: []api.Container{{
 | 
				
			||||||
				Name:                   "image",
 | 
									Name:                   "image",
 | 
				
			||||||
				Image:                  "test/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) {
 | 
					func TestReadPodsFromFile(t *testing.T) {
 | 
				
			||||||
	hostname := "random-test-hostname"
 | 
						hostname := "random-test-hostname"
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	var testCases = []struct {
 | 
						var testCases = []struct {
 | 
				
			||||||
		desc     string
 | 
							desc     string
 | 
				
			||||||
		pod      runtime.Object
 | 
							pod      runtime.Object
 | 
				
			||||||
@@ -98,9 +99,10 @@ func TestReadPodsFromFile(t *testing.T) {
 | 
				
			|||||||
					SelfLink:  getSelfLink("test-"+hostname, "mynamespace"),
 | 
										SelfLink:  getSelfLink("test-"+hostname, "mynamespace"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					NodeName:      hostname,
 | 
										NodeName:                      hostname,
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
					Containers: []api.Container{{
 | 
										Containers: []api.Container{{
 | 
				
			||||||
						Name:  "image",
 | 
											Name:  "image",
 | 
				
			||||||
						Image: "test/image",
 | 
											Image: "test/image",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,6 +123,7 @@ func TestExtractInvalidPods(t *testing.T) {
 | 
				
			|||||||
func TestExtractPodsFromHTTP(t *testing.T) {
 | 
					func TestExtractPodsFromHTTP(t *testing.T) {
 | 
				
			||||||
	hostname := "different-value"
 | 
						hostname := "different-value"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	var testCases = []struct {
 | 
						var testCases = []struct {
 | 
				
			||||||
		desc     string
 | 
							desc     string
 | 
				
			||||||
		pods     runtime.Object
 | 
							pods     runtime.Object
 | 
				
			||||||
@@ -156,9 +157,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
				
			|||||||
						SelfLink: getSelfLink("foo-"+hostname, "mynamespace"),
 | 
											SelfLink: getSelfLink("foo-"+hostname, "mynamespace"),
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					Spec: api.PodSpec{
 | 
										Spec: api.PodSpec{
 | 
				
			||||||
						NodeName:      hostname,
 | 
											NodeName:                      hostname,
 | 
				
			||||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
											RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
											DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
											TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						Containers: []api.Container{{
 | 
											Containers: []api.Container{{
 | 
				
			||||||
							Name:  "1",
 | 
												Name:  "1",
 | 
				
			||||||
							Image: "foo",
 | 
												Image: "foo",
 | 
				
			||||||
@@ -209,9 +212,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
				
			|||||||
						SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault),
 | 
											SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault),
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					Spec: api.PodSpec{
 | 
										Spec: api.PodSpec{
 | 
				
			||||||
						NodeName:      hostname,
 | 
											NodeName:                      hostname,
 | 
				
			||||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
											RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
											DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
											TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						Containers: []api.Container{{
 | 
											Containers: []api.Container{{
 | 
				
			||||||
							Name:  "1",
 | 
												Name:  "1",
 | 
				
			||||||
							Image: "foo",
 | 
												Image: "foo",
 | 
				
			||||||
@@ -229,9 +234,11 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
				
			|||||||
						SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault),
 | 
											SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault),
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					Spec: api.PodSpec{
 | 
										Spec: api.PodSpec{
 | 
				
			||||||
						NodeName:      hostname,
 | 
											NodeName:                      hostname,
 | 
				
			||||||
						RestartPolicy: api.RestartPolicyAlways,
 | 
											RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
						DNSPolicy:     api.DNSClusterFirst,
 | 
											DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
											TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						Containers: []api.Container{{
 | 
											Containers: []api.Container{{
 | 
				
			||||||
							Name:  "2",
 | 
												Name:  "2",
 | 
				
			||||||
							Image: "bar",
 | 
												Image: "bar",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,7 @@ var testTTL uint64 = 60
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
 | 
					func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
 | 
				
			||||||
	etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
 | 
						etcdStorage, fakeClient := registrytest.NewEtcdStorage(t)
 | 
				
			||||||
 | 
						fakeClient.HideExpires = true
 | 
				
			||||||
	return NewREST(etcdStorage, testTTL), fakeClient
 | 
						return NewREST(etcdStorage, testTTL), fakeClient
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -341,6 +341,11 @@ func (e *Etcd) Get(ctx api.Context, name string) (runtime.Object, error) {
 | 
				
			|||||||
	return obj, nil
 | 
						return obj, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						errAlreadyDeleting = fmt.Errorf("abort delete")
 | 
				
			||||||
 | 
						errDeleteNow       = fmt.Errorf("delete now")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete removes the item from etcd.
 | 
					// Delete removes the item from etcd.
 | 
				
			||||||
func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
 | 
					func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
 | 
				
			||||||
	key, err := e.KeyFunc(ctx, name)
 | 
						key, err := e.KeyFunc(ctx, name)
 | 
				
			||||||
@@ -367,13 +372,41 @@ func (e *Etcd) Delete(ctx api.Context, name string, options *api.DeleteOptions)
 | 
				
			|||||||
	if pendingGraceful {
 | 
						if pendingGraceful {
 | 
				
			||||||
		return e.finalizeDelete(obj, false)
 | 
							return e.finalizeDelete(obj, false)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if graceful && *options.GracePeriodSeconds != 0 {
 | 
						if graceful {
 | 
				
			||||||
		trace.Step("Graceful deletion")
 | 
							trace.Step("Graceful deletion")
 | 
				
			||||||
		out := e.NewFunc()
 | 
							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 nil, etcderr.InterpretUpdateError(err, e.EndpointName, name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return e.finalizeDelete(out, true)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// delete immediately, or no graceful deletion supported
 | 
						// delete immediately, or no graceful deletion supported
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,7 +212,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
				
			|||||||
	key, _ := storage.KeyFunc(ctx, "foo")
 | 
						key, _ := storage.KeyFunc(ctx, "foo")
 | 
				
			||||||
	key = etcdtest.AddPrefix(key)
 | 
						key = etcdtest.AddPrefix(key)
 | 
				
			||||||
	pvStart := validNewPersistentVolume("foo")
 | 
						pvStart := validNewPersistentVolume("foo")
 | 
				
			||||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvStart), 1)
 | 
						fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), pvStart), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pvIn := &api.PersistentVolume{
 | 
						pvIn := &api.PersistentVolume{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -209,7 +209,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
				
			|||||||
	key, _ := storage.KeyFunc(ctx, "foo")
 | 
						key, _ := storage.KeyFunc(ctx, "foo")
 | 
				
			||||||
	key = etcdtest.AddPrefix(key)
 | 
						key = etcdtest.AddPrefix(key)
 | 
				
			||||||
	pvcStart := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
 | 
						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{
 | 
						pvc := &api.PersistentVolumeClaim{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,6 +48,7 @@ func newStorage(t *testing.T) (*REST, *BindingREST, *StatusREST, *tools.FakeEtcd
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validNewPod() *api.Pod {
 | 
					func validNewPod() *api.Pod {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	return &api.Pod{
 | 
						return &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name:      "foo",
 | 
								Name:      "foo",
 | 
				
			||||||
@@ -56,6 +57,8 @@ func validNewPod() *api.Pod {
 | 
				
			|||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
			Containers: []api.Container{
 | 
								Containers: []api.Container{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					Name:            "foo",
 | 
										Name:            "foo",
 | 
				
			||||||
@@ -783,6 +786,7 @@ func TestEtcdUpdateScheduled(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}), 1)
 | 
						}), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	podIn := api.Pod{
 | 
						podIn := api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
			Name:            "foo",
 | 
								Name:            "foo",
 | 
				
			||||||
@@ -804,6 +808,8 @@ func TestEtcdUpdateScheduled(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, err := storage.Update(ctx, &podIn)
 | 
						_, _, 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{
 | 
						podIn := api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
@@ -873,6 +879,8 @@ func TestEtcdUpdateStatus(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	expected := podStart
 | 
						expected := podStart
 | 
				
			||||||
	expected.ResourceVersion = "2"
 | 
						expected.ResourceVersion = "2"
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
 | 
						expected.Spec.TerminationGracePeriodSeconds = &grace
 | 
				
			||||||
	expected.Spec.RestartPolicy = api.RestartPolicyAlways
 | 
						expected.Spec.RestartPolicy = api.RestartPolicyAlways
 | 
				
			||||||
	expected.Spec.DNSPolicy = api.DNSClusterFirst
 | 
						expected.Spec.DNSPolicy = api.DNSClusterFirst
 | 
				
			||||||
	expected.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent
 | 
						expected.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,6 +100,7 @@ func (podStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
 | 
				
			|||||||
	newPod := obj.(*api.Pod)
 | 
						newPod := obj.(*api.Pod)
 | 
				
			||||||
	oldPod := old.(*api.Pod)
 | 
						oldPod := old.(*api.Pod)
 | 
				
			||||||
	newPod.Spec = oldPod.Spec
 | 
						newPod.Spec = oldPod.Spec
 | 
				
			||||||
 | 
						newPod.DeletionTimestamp = nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (podStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
 | 
					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, _ := storage.KeyFunc(ctx, "foo")
 | 
				
			||||||
	key = etcdtest.AddPrefix(key)
 | 
						key = etcdtest.AddPrefix(key)
 | 
				
			||||||
	resourcequotaStart := validNewResourceQuota()
 | 
						resourcequotaStart := validNewResourceQuota()
 | 
				
			||||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), resourcequotaStart), 1)
 | 
						fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), resourcequotaStart), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resourcequotaIn := &api.ResourceQuota{
 | 
						resourcequotaIn := &api.ResourceQuota{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
							ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,11 +54,13 @@ func newTestCacher(client tools.EtcdClient) *storage.Cacher {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeTestPod(name string) *api.Pod {
 | 
					func makeTestPod(name string) *api.Pod {
 | 
				
			||||||
 | 
						gracePeriod := int64(30)
 | 
				
			||||||
	return &api.Pod{
 | 
						return &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Namespace: "ns", Name: name},
 | 
							ObjectMeta: api.ObjectMeta{Namespace: "ns", Name: name},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								TerminationGracePeriodSeconds: &gracePeriod,
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
								RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -394,14 +394,21 @@ func (h *etcdHelper) GuaranteedUpdate(key string, ptrToType runtime.Object, igno
 | 
				
			|||||||
		ttl := uint64(0)
 | 
							ttl := uint64(0)
 | 
				
			||||||
		if node != nil {
 | 
							if node != nil {
 | 
				
			||||||
			index = node.ModifiedIndex
 | 
								index = node.ModifiedIndex
 | 
				
			||||||
			if node.TTL > 0 {
 | 
								if node.TTL != 0 {
 | 
				
			||||||
				ttl = uint64(node.TTL)
 | 
									ttl = uint64(node.TTL)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if node.Expiration != nil && ttl == 0 {
 | 
				
			||||||
 | 
									ttl = 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else if res != nil {
 | 
							} else if res != nil {
 | 
				
			||||||
			index = res.EtcdIndex
 | 
								index = res.EtcdIndex
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if newTTL != nil {
 | 
							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
 | 
								ttl = *newTTL
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,28 +123,32 @@ func TestList(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	expect := api.PodList{
 | 
						expect := api.PodList{
 | 
				
			||||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
							ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
				
			||||||
		Items: []api.Pod{
 | 
							Items: []api.Pod{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -206,6 +210,7 @@ func TestListAcrossDirectories(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	expect := api.PodList{
 | 
						expect := api.PodList{
 | 
				
			||||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
							ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
				
			||||||
		Items: []api.Pod{
 | 
							Items: []api.Pod{
 | 
				
			||||||
@@ -213,22 +218,25 @@ func TestListAcrossDirectories(t *testing.T) {
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -278,28 +286,32 @@ func TestListExcludesDirectories(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	expect := api.PodList{
 | 
						expect := api.PodList{
 | 
				
			||||||
		ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
							ListMeta: api.ListMeta{ResourceVersion: "10"},
 | 
				
			||||||
		Items: []api.Pod{
 | 
							Items: []api.Pod{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
 | 
				
			||||||
				Spec: api.PodSpec{
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
					RestartPolicy: api.RestartPolicyAlways,
 | 
										RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
										DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -319,11 +331,13 @@ func TestGet(t *testing.T) {
 | 
				
			|||||||
	fakeClient := tools.NewFakeEtcdClient(t)
 | 
						fakeClient := tools.NewFakeEtcdClient(t)
 | 
				
			||||||
	helper := newEtcdHelper(fakeClient, testapi.Codec(), etcdtest.PathPrefix())
 | 
						helper := newEtcdHelper(fakeClient, testapi.Codec(), etcdtest.PathPrefix())
 | 
				
			||||||
	key := etcdtest.AddPrefix("/some/key")
 | 
						key := etcdtest.AddPrefix("/some/key")
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	expect := api.Pod{
 | 
						expect := api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
							ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &expect), 0)
 | 
						fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &expect), 0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,7 @@ const (
 | 
				
			|||||||
	EtcdSet    = "set"
 | 
						EtcdSet    = "set"
 | 
				
			||||||
	EtcdCAS    = "compareAndSwap"
 | 
						EtcdCAS    = "compareAndSwap"
 | 
				
			||||||
	EtcdDelete = "delete"
 | 
						EtcdDelete = "delete"
 | 
				
			||||||
 | 
						EtcdExpire = "expire"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TransformFunc attempts to convert an object to another object for use with a watcher.
 | 
					// 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)
 | 
							w.sendAdd(res)
 | 
				
			||||||
	case EtcdSet, EtcdCAS:
 | 
						case EtcdSet, EtcdCAS:
 | 
				
			||||||
		w.sendModify(res)
 | 
							w.sendModify(res)
 | 
				
			||||||
	case EtcdDelete:
 | 
						case EtcdDelete, EtcdExpire:
 | 
				
			||||||
		w.sendDelete(res)
 | 
							w.sendDelete(res)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		glog.Errorf("unknown action: %v", res.Action)
 | 
							glog.Errorf("unknown action: %v", res.Action)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/coreos/go-etcd/etcd"
 | 
						"github.com/coreos/go-etcd/etcd"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -52,6 +53,8 @@ type FakeEtcdClient struct {
 | 
				
			|||||||
	TestIndex   bool
 | 
						TestIndex   bool
 | 
				
			||||||
	ChangeIndex uint64
 | 
						ChangeIndex uint64
 | 
				
			||||||
	LastSetTTL  uint64
 | 
						LastSetTTL  uint64
 | 
				
			||||||
 | 
						// Will avoid setting the expires header on objects to make comparison easier
 | 
				
			||||||
 | 
						HideExpires bool
 | 
				
			||||||
	Machines    []string
 | 
						Machines    []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Will become valid after Watch is called; tester may write to it. Tester may
 | 
						// 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]
 | 
							prevResult := f.Data[key]
 | 
				
			||||||
		createdIndex := prevResult.R.Node.CreatedIndex
 | 
							createdIndex := prevResult.R.Node.CreatedIndex
 | 
				
			||||||
		f.t.Logf("updating %v, index %v -> %v (ttl: %d)", key, createdIndex, i, ttl)
 | 
							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{
 | 
							result := EtcdResponseWithError{
 | 
				
			||||||
			R: &etcd.Response{
 | 
								R: &etcd.Response{
 | 
				
			||||||
				Node: &etcd.Node{
 | 
									Node: &etcd.Node{
 | 
				
			||||||
@@ -182,6 +190,7 @@ func (f *FakeEtcdClient) setLocked(key, value string, ttl uint64) (*etcd.Respons
 | 
				
			|||||||
					CreatedIndex:  createdIndex,
 | 
										CreatedIndex:  createdIndex,
 | 
				
			||||||
					ModifiedIndex: i,
 | 
										ModifiedIndex: i,
 | 
				
			||||||
					TTL:           int64(ttl),
 | 
										TTL:           int64(ttl),
 | 
				
			||||||
 | 
										Expiration:    expires,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,11 +132,13 @@ func PriorityTwo(pod *api.Pod, podLister algorithm.PodLister, minionLister algor
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDefaultErrorFunc(t *testing.T) {
 | 
					func TestDefaultErrorFunc(t *testing.T) {
 | 
				
			||||||
 | 
						grace := int64(30)
 | 
				
			||||||
	testPod := &api.Pod{
 | 
						testPod := &api.Pod{
 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
 | 
							ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
 | 
				
			||||||
		Spec: api.PodSpec{
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy:                 api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:                     api.DNSClusterFirst,
 | 
				
			||||||
 | 
								TerminationGracePeriodSeconds: &grace,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	handler := util.FakeHandler{
 | 
						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) {
 | 
					func TestWatch(t *testing.T) {
 | 
				
			||||||
	client := framework.NewEtcdClient()
 | 
						client := framework.NewEtcdClient()
 | 
				
			||||||
	etcdStorage := etcd.NewEtcdStorage(client, testapi.Codec(), etcdtest.PathPrefix())
 | 
						etcdStorage := etcd.NewEtcdStorage(client, testapi.Codec(), etcdtest.PathPrefix())
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user