drain.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package drain
  14. import (
  15. "io"
  16. "time"
  17. corev1 "k8s.io/api/core/v1"
  18. policyv1beta1 "k8s.io/api/policy/v1beta1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/fields"
  21. "k8s.io/apimachinery/pkg/labels"
  22. "k8s.io/client-go/kubernetes"
  23. )
  24. const (
  25. // EvictionKind represents the kind of evictions object
  26. EvictionKind = "Eviction"
  27. // EvictionSubresource represents the kind of evictions object as pod's subresource
  28. EvictionSubresource = "pods/eviction"
  29. )
  30. // Helper contains the parameters to control the behaviour of drainer
  31. type Helper struct {
  32. Client kubernetes.Interface
  33. Force bool
  34. DryRun bool
  35. GracePeriodSeconds int
  36. IgnoreAllDaemonSets bool
  37. Timeout time.Duration
  38. DeleteLocalData bool
  39. Selector string
  40. PodSelector string
  41. ErrOut io.Writer
  42. }
  43. // CheckEvictionSupport uses Discovery API to find out if the server support
  44. // eviction subresource If support, it will return its groupVersion; Otherwise,
  45. // it will return an empty string
  46. func CheckEvictionSupport(clientset kubernetes.Interface) (string, error) {
  47. discoveryClient := clientset.Discovery()
  48. groupList, err := discoveryClient.ServerGroups()
  49. if err != nil {
  50. return "", err
  51. }
  52. foundPolicyGroup := false
  53. var policyGroupVersion string
  54. for _, group := range groupList.Groups {
  55. if group.Name == "policy" {
  56. foundPolicyGroup = true
  57. policyGroupVersion = group.PreferredVersion.GroupVersion
  58. break
  59. }
  60. }
  61. if !foundPolicyGroup {
  62. return "", nil
  63. }
  64. resourceList, err := discoveryClient.ServerResourcesForGroupVersion("v1")
  65. if err != nil {
  66. return "", err
  67. }
  68. for _, resource := range resourceList.APIResources {
  69. if resource.Name == EvictionSubresource && resource.Kind == EvictionKind {
  70. return policyGroupVersion, nil
  71. }
  72. }
  73. return "", nil
  74. }
  75. func (d *Helper) makeDeleteOptions() *metav1.DeleteOptions {
  76. deleteOptions := &metav1.DeleteOptions{}
  77. if d.GracePeriodSeconds >= 0 {
  78. gracePeriodSeconds := int64(d.GracePeriodSeconds)
  79. deleteOptions.GracePeriodSeconds = &gracePeriodSeconds
  80. }
  81. return deleteOptions
  82. }
  83. // DeletePod will delete the given pod, or return an error if it couldn't
  84. func (d *Helper) DeletePod(pod corev1.Pod) error {
  85. return d.Client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, d.makeDeleteOptions())
  86. }
  87. // EvictPod will evict the give pod, or return an error if it couldn't
  88. func (d *Helper) EvictPod(pod corev1.Pod, policyGroupVersion string) error {
  89. eviction := &policyv1beta1.Eviction{
  90. TypeMeta: metav1.TypeMeta{
  91. APIVersion: policyGroupVersion,
  92. Kind: EvictionKind,
  93. },
  94. ObjectMeta: metav1.ObjectMeta{
  95. Name: pod.Name,
  96. Namespace: pod.Namespace,
  97. },
  98. DeleteOptions: d.makeDeleteOptions(),
  99. }
  100. // Remember to change change the URL manipulation func when Eviction's version change
  101. return d.Client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(eviction)
  102. }
  103. // GetPodsForDeletion receives resource info for a node, and returns those pods as PodDeleteList,
  104. // or error if it cannot list pods. All pods that are ready to be deleted can be obtained with .Pods(),
  105. // and string with all warning can be obtained with .Warnings(), and .Errors() for all errors that
  106. // occurred during deletion.
  107. func (d *Helper) GetPodsForDeletion(nodeName string) (*podDeleteList, []error) {
  108. labelSelector, err := labels.Parse(d.PodSelector)
  109. if err != nil {
  110. return nil, []error{err}
  111. }
  112. podList, err := d.Client.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
  113. LabelSelector: labelSelector.String(),
  114. FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName}).String()})
  115. if err != nil {
  116. return nil, []error{err}
  117. }
  118. pods := []podDelete{}
  119. for _, pod := range podList.Items {
  120. var status podDeleteStatus
  121. for _, filter := range d.makeFilters() {
  122. status = filter(pod)
  123. if !status.delete {
  124. // short-circuit as soon as pod is filtered out
  125. // at that point, there is no reason to run pod
  126. // through any additional filters
  127. break
  128. }
  129. }
  130. pods = append(pods, podDelete{
  131. pod: pod,
  132. status: status,
  133. })
  134. }
  135. list := &podDeleteList{items: pods}
  136. if errs := list.errors(); len(errs) > 0 {
  137. return list, errs
  138. }
  139. return list, nil
  140. }