Merge pull request #68767 from ravisantoshgudimetla/fix-drain

Fix drain for evicting terminal DS pods and pods with local storage
pull/8/head
k8s-ci-robot 2018-09-18 22:34:40 -07:00 committed by GitHub
commit 400981797e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 3 deletions

View File

@ -403,8 +403,7 @@ func (o *DrainOptions) unreplicatedFilter(pod corev1.Pod) (bool, *warning, *fata
func (o *DrainOptions) daemonsetFilter(pod corev1.Pod) (bool, *warning, *fatal) { func (o *DrainOptions) daemonsetFilter(pod corev1.Pod) (bool, *warning, *fatal) {
// Note that we return false in cases where the pod is DaemonSet managed, // Note that we return false in cases where the pod is DaemonSet managed,
// regardless of flags. We never delete them, the only question is whether // regardless of flags.
// their presence constitutes an error.
// //
// The exception is for pods that are orphaned (the referencing // The exception is for pods that are orphaned (the referencing
// management resource - including DaemonSet - is not found). // management resource - including DaemonSet - is not found).
@ -413,12 +412,17 @@ func (o *DrainOptions) daemonsetFilter(pod corev1.Pod) (bool, *warning, *fatal)
if controllerRef == nil || controllerRef.Kind != "DaemonSet" { if controllerRef == nil || controllerRef.Kind != "DaemonSet" {
return true, nil, nil return true, nil, nil
} }
// Any finished pod can be removed.
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed {
return true, nil, nil
}
if _, err := o.client.ExtensionsV1beta1().DaemonSets(pod.Namespace).Get(controllerRef.Name, metav1.GetOptions{}); err != nil { if _, err := o.client.ExtensionsV1beta1().DaemonSets(pod.Namespace).Get(controllerRef.Name, metav1.GetOptions{}); err != nil {
// remove orphaned pods with a warning if --force is used // remove orphaned pods with a warning if --force is used
if apierrors.IsNotFound(err) && o.Force { if apierrors.IsNotFound(err) && o.Force {
return true, &warning{err.Error()}, nil return true, &warning{err.Error()}, nil
} }
return false, nil, &fatal{err.Error()} return false, nil, &fatal{err.Error()}
} }
@ -450,9 +454,14 @@ func (o *DrainOptions) localStorageFilter(pod corev1.Pod) (bool, *warning, *fata
if !hasLocalStorage(pod) { if !hasLocalStorage(pod) {
return true, nil, nil return true, nil, nil
} }
// Any finished pod can be removed.
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed {
return true, nil, nil
}
if !o.DeleteLocalData { if !o.DeleteLocalData {
return false, nil, &fatal{kLocalStorageFatal} return false, nil, &fatal{kLocalStorageFatal}
} }
return true, &warning{kLocalStorageWarning}, nil return true, &warning{kLocalStorageWarning}, nil
} }

View File

@ -308,6 +308,31 @@ func TestDrain(t *testing.T) {
}, },
} }
ds_terminated_pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: labels,
SelfLink: testapi.Default.SelfLink("pods", "bar"),
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "extensions/v1beta1",
Kind: "DaemonSet",
Name: "ds",
BlockOwnerDeletion: boolptr(true),
Controller: boolptr(true),
},
},
},
Spec: corev1.PodSpec{
NodeName: "node",
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
},
}
ds_pod_with_emptyDir := corev1.Pod{ ds_pod_with_emptyDir := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "bar", Name: "bar",
@ -378,6 +403,46 @@ func TestDrain(t *testing.T) {
}, },
}, },
}, },
Spec: corev1.PodSpec{
NodeName: "node",
Volumes: []corev1.Volume{
{
Name: "scratch",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}},
},
},
},
}
terminated_job_pod_with_local_storage := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: labels,
SelfLink: testapi.Default.SelfLink("pods", "bar"),
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "Job",
Name: "job",
BlockOwnerDeletion: boolptr(true),
Controller: boolptr(true),
},
},
},
Spec: corev1.PodSpec{
NodeName: "node",
Volumes: []corev1.Volume{
{
Name: "scratch",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
},
} }
rs := extensions.ReplicaSet{ rs := extensions.ReplicaSet{
@ -444,6 +509,26 @@ func TestDrain(t *testing.T) {
}, },
}, },
} }
emptydir_terminated_pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: labels,
},
Spec: corev1.PodSpec{
NodeName: "node",
Volumes: []corev1.Volume{
{
Name: "scratch",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodFailed,
},
}
tests := []struct { tests := []struct {
description string description string
@ -477,6 +562,16 @@ func TestDrain(t *testing.T) {
expectFatal: true, expectFatal: true,
expectDelete: false, expectDelete: false,
}, },
{
description: "DS-managed terminated pod",
node: node,
expected: cordoned_node,
pods: []corev1.Pod{ds_terminated_pod},
rcs: []api.ReplicationController{rc},
args: []string{"node"},
expectFatal: false,
expectDelete: true,
},
{ {
description: "orphaned DS-managed pod", description: "orphaned DS-managed pod",
node: node, node: node,
@ -519,11 +614,21 @@ func TestDrain(t *testing.T) {
expectDelete: false, expectDelete: false,
}, },
{ {
description: "Job-managed pod", description: "Job-managed pod with local storage",
node: node, node: node,
expected: cordoned_node, expected: cordoned_node,
pods: []corev1.Pod{job_pod}, pods: []corev1.Pod{job_pod},
rcs: []api.ReplicationController{rc}, rcs: []api.ReplicationController{rc},
args: []string{"node", "--force", "--delete-local-data=true"},
expectFatal: false,
expectDelete: true,
},
{
description: "Job-managed terminated pod",
node: node,
expected: cordoned_node,
pods: []corev1.Pod{terminated_job_pod_with_local_storage},
rcs: []api.ReplicationController{rc},
args: []string{"node"}, args: []string{"node"},
expectFatal: false, expectFatal: false,
expectDelete: true, expectDelete: true,
@ -567,6 +672,16 @@ func TestDrain(t *testing.T) {
expectFatal: true, expectFatal: true,
expectDelete: false, expectDelete: false,
}, },
{
description: "terminated pod with emptyDir",
node: node,
expected: cordoned_node,
pods: []corev1.Pod{emptydir_terminated_pod},
rcs: []api.ReplicationController{rc},
args: []string{"node"},
expectFatal: false,
expectDelete: true,
},
{ {
description: "pod with EmptyDir and --delete-local-data", description: "pod with EmptyDir and --delete-local-data",
node: node, node: node,