forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
evacuate.go
205 lines (172 loc) · 5.98 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package node
import (
"fmt"
"github.com/golang/glog"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
kerrors "k8s.io/apimachinery/pkg/util/errors"
kapi "k8s.io/kubernetes/pkg/apis/core"
)
const (
flagGracePeriod = "grace-period"
flagDryRun = "dry-run"
flagForce = "force"
)
type EvacuateOptions struct {
Options *NodeOptions
// Optional params
DryRun bool
Force bool
GracePeriod int64
printPodHeaders bool
}
// NewEvacuateOptions creates a new EvacuateOptions with default values.
func NewEvacuateOptions(nodeOptions *NodeOptions) *EvacuateOptions {
return &EvacuateOptions{
Options: nodeOptions,
DryRun: false,
Force: false,
GracePeriod: 30,
printPodHeaders: true,
}
}
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. Ignored if negative. Optional param for --evacuate")
}
func (e *EvacuateOptions) Run() error {
if e.DryRun {
listpodsOp := ListPodsOptions{Options: e.Options, printPodHeaders: e.printPodHeaders}
return listpodsOp.Run()
}
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 {
// 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 'oc adm 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.KubeClient.Core().Pods(metav1.NamespaceAll).List(metav1.ListOptions{LabelSelector: labelSelector.String(), FieldSelector: fieldSelector.String()})
if err != nil {
return err
}
if len(pods.Items) == 0 {
fmt.Fprint(e.Options.ErrOut, "\nNo pods found on node: ", node.ObjectMeta.Name, "\n\n")
return nil
}
rcs, err := e.Options.KubeClient.Core().ReplicationControllers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
return err
}
rss, err := e.Options.KubeClient.Extensions().ReplicaSets(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
return err
}
dss, err := e.Options.KubeClient.Extensions().DaemonSets(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
return err
}
jobs, err := e.Options.KubeClient.Batch().Jobs(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
return err
}
printer, err := e.Options.GetPrintersByResource(schema.GroupVersionResource{Resource: "pod"}, false)
if err != nil {
return err
}
errList := []error{}
firstPod := true
numUnmanagedPods := 0
var deleteOptions *metav1.DeleteOptions
if e.GracePeriod >= 0 {
deleteOptions = e.makeDeleteOptions()
}
for _, pod := range pods.Items {
isManaged := false
for _, rc := range rcs.Items {
selector := labels.SelectorFromSet(rc.Spec.Selector)
if selector.Matches(labels.Set(pod.Labels)) {
isManaged = true
break
}
}
for _, rs := range rss.Items {
selector := labels.SelectorFromSet(rs.Spec.Selector.MatchLabels)
if selector.Matches(labels.Set(pod.Labels)) {
isManaged = true
break
}
}
for _, ds := range dss.Items {
selector := labels.SelectorFromSet(ds.Spec.Selector.MatchLabels)
if selector.Matches(labels.Set(pod.Labels)) {
isManaged = true
break
}
}
for _, job := range jobs.Items {
selector := labels.SelectorFromSet(job.Spec.Selector.MatchLabels)
if selector.Matches(labels.Set(pod.Labels)) {
isManaged = true
break
}
}
if firstPod {
fmt.Fprint(e.Options.ErrOut, "\nMigrating these pods on node: ", node.ObjectMeta.Name, "\n\n")
firstPod = false
}
printer.PrintObj(&pod, e.Options.Out)
if isManaged || e.Force {
if err := e.Options.KubeClient.Core().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
numUnmanagedPods++
}
}
if numUnmanagedPods > 0 {
err := fmt.Errorf(`Unable to evacuate some pods because they are not managed by replication controller or replica set or deaemon set.
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() *metav1.DeleteOptions {
return &metav1.DeleteOptions{GracePeriodSeconds: &e.GracePeriod}
}