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