2017-11-15 02:54:26 +00:00
|
|
|
package k8s
|
|
|
|
|
|
|
|
import (
|
2020-03-26 21:32:45 +00:00
|
|
|
"context"
|
2017-11-15 02:54:26 +00:00
|
|
|
"fmt"
|
2018-02-01 21:28:31 +00:00
|
|
|
"strings"
|
2017-11-21 19:25:08 +00:00
|
|
|
"time"
|
2017-11-15 02:54:26 +00:00
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
2020-03-26 21:32:45 +00:00
|
|
|
v1 "k8s.io/api/core/v1"
|
2018-01-19 01:48:51 +00:00
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
2017-11-15 02:54:26 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2018-03-23 18:14:11 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
2017-11-15 02:54:26 +00:00
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
)
|
|
|
|
|
2018-03-23 18:14:11 +00:00
|
|
|
const (
|
2020-03-11 06:36:17 +00:00
|
|
|
HostnameLabel = "kubernetes.io/hostname"
|
|
|
|
InternalAddressAnnotation = "rke.cattle.io/internal-ip"
|
|
|
|
ExternalAddressAnnotation = "rke.cattle.io/external-ip"
|
|
|
|
AWSCloudProvider = "aws"
|
|
|
|
MaxRetries = 5
|
|
|
|
RetryInterval = 5
|
2018-03-23 18:14:11 +00:00
|
|
|
)
|
|
|
|
|
2018-04-02 22:15:34 +00:00
|
|
|
func DeleteNode(k8sClient *kubernetes.Clientset, nodeName, cloudProvider string) error {
|
2020-07-27 12:06:50 +00:00
|
|
|
// If cloud provider is configured, the node name can be set by the cloud provider, which can be different from the original node name
|
|
|
|
if cloudProvider != "" {
|
2018-04-02 22:15:34 +00:00
|
|
|
node, err := GetNode(k8sClient, nodeName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nodeName = node.Name
|
|
|
|
}
|
2020-03-26 21:32:45 +00:00
|
|
|
return k8sClient.CoreV1().Nodes().Delete(context.TODO(), nodeName, metav1.DeleteOptions{})
|
2017-11-15 02:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func GetNodeList(k8sClient *kubernetes.Clientset) (*v1.NodeList, error) {
|
2020-03-26 21:32:45 +00:00
|
|
|
return k8sClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
2017-11-15 02:54:26 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 03:29:02 +00:00
|
|
|
func GetNode(k8sClient *kubernetes.Clientset, nodeName string) (*v1.Node, error) {
|
2020-02-04 19:27:52 +00:00
|
|
|
var listErr error
|
|
|
|
for retries := 0; retries < MaxRetries; retries++ {
|
2020-03-21 17:17:32 +00:00
|
|
|
logrus.Debugf("Checking node list for node [%v], try #%v", nodeName, retries+1)
|
2020-02-04 19:27:52 +00:00
|
|
|
nodes, err := GetNodeList(k8sClient)
|
|
|
|
if err != nil {
|
|
|
|
listErr = err
|
|
|
|
time.Sleep(time.Second * RetryInterval)
|
|
|
|
continue
|
|
|
|
}
|
2020-03-13 21:33:53 +00:00
|
|
|
// reset listErr back to nil
|
|
|
|
listErr = nil
|
2020-02-04 19:27:52 +00:00
|
|
|
for _, node := range nodes.Items {
|
|
|
|
if strings.ToLower(node.Labels[HostnameLabel]) == strings.ToLower(nodeName) {
|
|
|
|
return &node, nil
|
|
|
|
}
|
2018-03-23 18:14:11 +00:00
|
|
|
}
|
2020-02-04 19:27:52 +00:00
|
|
|
time.Sleep(time.Second * RetryInterval)
|
|
|
|
}
|
|
|
|
if listErr != nil {
|
|
|
|
return nil, listErr
|
2018-03-23 18:14:11 +00:00
|
|
|
}
|
|
|
|
return nil, apierrors.NewNotFound(schema.GroupResource{}, nodeName)
|
2017-11-30 03:29:02 +00:00
|
|
|
}
|
2018-02-01 21:28:31 +00:00
|
|
|
|
2017-11-15 02:54:26 +00:00
|
|
|
func CordonUncordon(k8sClient *kubernetes.Clientset, nodeName string, cordoned bool) error {
|
|
|
|
updated := false
|
2020-02-04 19:27:52 +00:00
|
|
|
for retries := 0; retries < MaxRetries; retries++ {
|
2018-04-02 22:15:34 +00:00
|
|
|
node, err := GetNode(k8sClient, nodeName)
|
2017-11-15 02:54:26 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("Error getting node %s: %v", nodeName, err)
|
2020-02-04 19:27:52 +00:00
|
|
|
// no need to retry here since GetNode already retries
|
|
|
|
return err
|
2017-11-15 02:54:26 +00:00
|
|
|
}
|
|
|
|
if node.Spec.Unschedulable == cordoned {
|
|
|
|
logrus.Debugf("Node %s is already cordoned: %v", nodeName, cordoned)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
node.Spec.Unschedulable = cordoned
|
2020-03-26 21:32:45 +00:00
|
|
|
_, err = k8sClient.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{})
|
2017-11-15 02:54:26 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("Error setting cordoned state for node %s: %v", nodeName, err)
|
2020-02-04 19:27:52 +00:00
|
|
|
time.Sleep(time.Second * RetryInterval)
|
2017-11-15 02:54:26 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
updated = true
|
|
|
|
}
|
|
|
|
if !updated {
|
|
|
|
return fmt.Errorf("Failed to set cordonded state for node: %s", nodeName)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsNodeReady(node v1.Node) bool {
|
|
|
|
nodeConditions := node.Status.Conditions
|
|
|
|
for _, condition := range nodeConditions {
|
2020-02-04 19:27:52 +00:00
|
|
|
if condition.Type == v1.NodeReady && condition.Status == v1.ConditionTrue {
|
2017-11-15 02:54:26 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2018-01-19 01:48:51 +00:00
|
|
|
|
|
|
|
func RemoveTaintFromNodeByKey(k8sClient *kubernetes.Clientset, nodeName, taintKey string) error {
|
|
|
|
updated := false
|
|
|
|
var err error
|
|
|
|
var node *v1.Node
|
|
|
|
for retries := 0; retries <= 5; retries++ {
|
|
|
|
node, err = GetNode(k8sClient, nodeName)
|
|
|
|
if err != nil {
|
|
|
|
if apierrors.IsNotFound(err) {
|
|
|
|
logrus.Debugf("[hosts] Can't find node by name [%s]", nodeName)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
foundTaint := false
|
|
|
|
for i, taint := range node.Spec.Taints {
|
|
|
|
if taint.Key == taintKey {
|
|
|
|
foundTaint = true
|
|
|
|
node.Spec.Taints = append(node.Spec.Taints[:i], node.Spec.Taints[i+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !foundTaint {
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-26 21:32:45 +00:00
|
|
|
_, err = k8sClient.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{})
|
2018-01-19 01:48:51 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("Error updating node [%s] with new set of taints: %v", node.Name, err)
|
|
|
|
time.Sleep(time.Second * 5)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
updated = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !updated {
|
|
|
|
return fmt.Errorf("Timeout waiting for node [%s] to be updated with new set of taints: %v", node.Name, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-02-01 21:28:31 +00:00
|
|
|
|
2018-10-16 21:52:15 +00:00
|
|
|
func SyncNodeLabels(node *v1.Node, toAddLabels, toDelLabels map[string]string) {
|
2018-03-02 00:40:00 +00:00
|
|
|
oldLabels := map[string]string{}
|
|
|
|
if node.Labels == nil {
|
|
|
|
node.Labels = map[string]string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range node.Labels {
|
|
|
|
oldLabels[k] = v
|
|
|
|
}
|
|
|
|
|
2018-02-01 21:28:31 +00:00
|
|
|
// Delete Labels
|
|
|
|
for key := range toDelLabels {
|
|
|
|
if _, ok := node.Labels[key]; ok {
|
|
|
|
delete(node.Labels, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ADD Labels
|
|
|
|
for key, value := range toAddLabels {
|
|
|
|
node.Labels[key] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 21:52:15 +00:00
|
|
|
func SyncNodeTaints(node *v1.Node, toAddTaints, toDelTaints []string) {
|
2018-02-01 21:28:31 +00:00
|
|
|
// Add taints to node
|
|
|
|
for _, taintStr := range toAddTaints {
|
|
|
|
if isTaintExist(toTaint(taintStr), node.Spec.Taints) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
node.Spec.Taints = append(node.Spec.Taints, toTaint(taintStr))
|
|
|
|
}
|
|
|
|
// Remove Taints from node
|
2018-08-03 01:16:58 +00:00
|
|
|
for _, taintStr := range toDelTaints {
|
|
|
|
node.Spec.Taints = delTaintFromList(node.Spec.Taints, toTaint(taintStr))
|
2018-02-01 21:28:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isTaintExist(taint v1.Taint, taintList []v1.Taint) bool {
|
|
|
|
for _, t := range taintList {
|
|
|
|
if t.Key == taint.Key && t.Value == taint.Value && t.Effect == taint.Effect {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func toTaint(taintStr string) v1.Taint {
|
|
|
|
taintStruct := strings.Split(taintStr, "=")
|
|
|
|
tmp := strings.Split(taintStruct[1], ":")
|
|
|
|
key := taintStruct[0]
|
|
|
|
value := tmp[0]
|
|
|
|
effect := v1.TaintEffect(tmp[1])
|
|
|
|
return v1.Taint{
|
2018-03-21 21:50:34 +00:00
|
|
|
Key: key,
|
|
|
|
Value: value,
|
|
|
|
Effect: effect,
|
2018-02-01 21:28:31 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-26 22:53:28 +00:00
|
|
|
|
2018-10-16 21:52:15 +00:00
|
|
|
func SetNodeAddressesAnnotations(node *v1.Node, internalAddress, externalAddress string) {
|
|
|
|
currentExternalAnnotation := node.Annotations[ExternalAddressAnnotation]
|
|
|
|
currentInternalAnnotation := node.Annotations[ExternalAddressAnnotation]
|
|
|
|
if currentExternalAnnotation == externalAddress && currentInternalAnnotation == internalAddress {
|
|
|
|
return
|
2018-03-26 22:53:28 +00:00
|
|
|
}
|
2018-10-16 21:52:15 +00:00
|
|
|
node.Annotations[ExternalAddressAnnotation] = externalAddress
|
|
|
|
node.Annotations[InternalAddressAnnotation] = internalAddress
|
2018-03-26 22:53:28 +00:00
|
|
|
}
|
2018-08-03 01:16:58 +00:00
|
|
|
|
|
|
|
func delTaintFromList(l []v1.Taint, t v1.Taint) []v1.Taint {
|
|
|
|
r := []v1.Taint{}
|
|
|
|
for _, i := range l {
|
|
|
|
if i == t {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r = append(r, i)
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|