forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
evacuate.go
154 lines (131 loc) · 4.67 KB
/
evacuate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package node
import (
"fmt"
"github.com/golang/glog"
"github.com/spf13/cobra"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
kerrors "k8s.io/kubernetes/pkg/util/errors"
)
const (
flagGracePeriod = "grace-period"
flagDryRun = "dry-run"
flagForce = "force"
)
type EvacuateOptions struct {
Options *NodeOptions
// Optional params
DryRun bool
Force bool
GracePeriod int64
}
// NewEvacuateOptions creates a new EvacuateOptions with default values.
func NewEvacuateOptions(nodeOptions *NodeOptions) *EvacuateOptions {
return &EvacuateOptions{
Options: nodeOptions,
DryRun: false,
Force: false,
GracePeriod: 30,
}
}
func (e *EvacuateOptions) AddFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.BoolVar(&e.DryRun, flagDryRun, e.DryRun, "Show pods that will be migrated. Optional param for --evacuate")
flags.BoolVar(&e.Force, flagForce, e.Force, "Delete pods not backed by replication controller. Optional param for --evacuate")
flags.Int64Var(&e.GracePeriod, flagGracePeriod, e.GracePeriod, "Grace period (seconds) for pods being deleted. Optional param for --evacuate")
}
func (e *EvacuateOptions) Run() error {
nodes, err := e.Options.GetNodes()
if err != nil {
return err
}
errList := []error{}
for _, node := range nodes {
err := e.RunEvacuate(node)
if err != nil {
// Don't bail out if one node fails
errList = append(errList, err)
}
}
return kerrors.NewAggregate(errList)
}
func (e *EvacuateOptions) RunEvacuate(node *kapi.Node) error {
if e.DryRun {
listpodsOp := ListPodsOptions{Options: e.Options}
return listpodsOp.Run()
}
// We do *not* automatically mark the node unschedulable to perform evacuation.
// Rationale: If we unschedule the node and later the operation is unsuccessful (stopped by user, network error, etc.),
// we may not be able to recover in some cases to mark the node back to schedulable. To avoid these cases, we recommend
// user to explicitly set the node to schedulable/unschedulable.
if !node.Spec.Unschedulable {
return fmt.Errorf("Node '%s' must be unschedulable to perform evacuation.\nYou can mark the node unschedulable with 'openshift admin manage-node %s --schedulable=false'", node.ObjectMeta.Name, node.ObjectMeta.Name)
}
labelSelector, err := labels.Parse(e.Options.PodSelector)
if err != nil {
return err
}
fieldSelector := fields.Set{GetPodHostFieldLabel(node.TypeMeta.APIVersion): node.ObjectMeta.Name}.AsSelector()
// Filter all pods that satisfies pod label selector and belongs to the given node
pods, err := e.Options.Kclient.Pods(kapi.NamespaceAll).List(kapi.ListOptions{LabelSelector: labelSelector, FieldSelector: fieldSelector})
if err != nil {
return err
}
rcs, err := e.Options.Kclient.ReplicationControllers(kapi.NamespaceAll).List(kapi.ListOptions{})
if err != nil {
return err
}
printerWithHeaders, printerNoHeaders, err := e.Options.GetPrintersByResource(unversioned.GroupVersionResource{Resource: "pod"})
if err != nil {
return err
}
errList := []error{}
firstPod := true
numPodsWithNoRC := 0
deleteOptions := e.makeDeleteOptions()
for _, pod := range pods.Items {
foundrc := false
for _, rc := range rcs.Items {
selector := labels.SelectorFromSet(rc.Spec.Selector)
if selector.Matches(labels.Set(pod.Labels)) {
foundrc = true
break
}
}
if firstPod {
fmt.Fprint(e.Options.Writer, "\nMigrating these pods on node: ", node.ObjectMeta.Name, "\n\n")
firstPod = false
printerWithHeaders.PrintObj(&pod, e.Options.Writer)
} else {
printerNoHeaders.PrintObj(&pod, e.Options.Writer)
}
if foundrc || e.Force {
if err := e.Options.Kclient.Pods(pod.Namespace).Delete(pod.Name, deleteOptions); err != nil {
glog.Errorf("Unable to delete a pod: %+v, error: %v", pod, err)
errList = append(errList, err)
continue
}
} else { // Pods without replication controller and no --force option
numPodsWithNoRC++
}
}
if numPodsWithNoRC > 0 {
err := fmt.Errorf(`Unable to evacuate some pods because they are not backed by replication controller.
Suggested options:
- You can list bare pods in json/yaml format using '--list-pods -o json|yaml'
- Force deletion of bare pods with --force option to --evacuate
- Optionally recreate these bare pods by massaging the json/yaml output from above list pods
`)
errList = append(errList, err)
}
if len(errList) != 0 {
return kerrors.NewAggregate(errList)
}
return nil
}
// makeDeleteOptions creates the delete options that will be used for pod evacuation.
func (e *EvacuateOptions) makeDeleteOptions() *kapi.DeleteOptions {
return &kapi.DeleteOptions{GracePeriodSeconds: &e.GracePeriod}
}