github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/plugin/taint_plugin.go (about)

     1  // Copyright © 2021 Alibaba Group Holding Ltd.
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package plugin
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/alibaba/sealer/utils"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  
    25  	"github.com/alibaba/sealer/logger"
    26  	"github.com/alibaba/sealer/pkg/client/k8s"
    27  )
    28  
    29  var TaintEffectValues = []v1.TaintEffect{v1.TaintEffectNoSchedule, v1.TaintEffectNoExecute, v1.TaintEffectPreferNoSchedule}
    30  
    31  type TaintList map[string]*taintList //map[ip]taint
    32  
    33  type Taint struct {
    34  	IPList []string
    35  	TaintList
    36  }
    37  
    38  type taintList struct {
    39  	DelTaintList []v1.Taint
    40  	AddTaintList []v1.Taint
    41  }
    42  
    43  func NewTaintPlugin() Interface {
    44  	return &Taint{TaintList: map[string]*taintList{}}
    45  }
    46  
    47  func init() {
    48  	Register(TaintPlugin, NewTaintPlugin())
    49  }
    50  
    51  func newTaintStruct(key, value, effect string) v1.Taint {
    52  	return v1.Taint{Key: key, Value: value, Effect: v1.TaintEffect(effect)}
    53  }
    54  
    55  //Run taint_plugin file:
    56  //apiVersion: sealer.aliyun.com/v1alpha1
    57  //kind: Plugin
    58  //metadata:
    59  //  name: taint
    60  //spec:
    61  //  type: Taint
    62  //  action: PreGuest
    63  //  data: 192.168.56.3 key1=value1:NoSchedule ## add taint
    64  //  #data: 192.168.56.3 key1=value1:NoSchedule- ## del taint
    65  
    66  func (l *Taint) Run(context Context, phase Phase) (err error) {
    67  	if phase != PhasePreGuest || context.Plugin.Spec.Type != TaintPlugin {
    68  		return nil
    69  	}
    70  
    71  	err = l.formatData(context.Plugin.Spec.Data)
    72  	if err != nil {
    73  		return fmt.Errorf("failed to format data from %s: %v", context.Plugin.Spec.Data, err)
    74  	}
    75  
    76  	k8sClient, err := k8s.Newk8sClient()
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	nodeList, err := k8sClient.ListNodes()
    82  	if err != nil {
    83  		return err
    84  	}
    85  	for _, n := range nodeList.Items {
    86  		node := n
    87  		for _, v := range node.Status.Addresses {
    88  			if !utils.InList(v.Address, l.IPList) {
    89  				continue
    90  			}
    91  			if utils.NotIn(v.Address, context.Host) {
    92  				continue
    93  			}
    94  			updateTaints := l.UpdateTaints(node.Spec.Taints, v.Address)
    95  			if updateTaints != nil {
    96  				node.Spec.Taints = updateTaints
    97  				_, err := k8sClient.UpdateNode(node)
    98  				if err != nil {
    99  					return err
   100  				}
   101  				logger.Info("successfully updated node %s taints to %v.", v.Address, updateTaints)
   102  			}
   103  			break
   104  		}
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  //key1=value1:NoSchedule;key1=value1:NoSchedule-;key1:NoSchedule;key1:NoSchedule-;key1=:NoSchedule-;key1=value1:NoSchedule
   111  func (l *Taint) formatData(data string) error {
   112  	items := strings.Split(data, "\n")
   113  	for _, v := range items {
   114  		v = strings.TrimSpace(v)
   115  		if strings.HasPrefix(v, "#") || v == "" {
   116  			continue
   117  		}
   118  		temps := strings.Split(v, " ")
   119  		if len(temps) != 2 {
   120  			return fmt.Errorf("faild to split taint argument: %s", v)
   121  		}
   122  		ips := temps[0]
   123  		err := utils.AssemblyIPList(&ips)
   124  		if err != nil {
   125  			return err
   126  		}
   127  		l.IPList = append(l.IPList, ips)
   128  		//kubectl taint nodes xxx key- : remove all key related taints
   129  		if strings.HasSuffix(temps[1], DelSymbol) && !strings.Contains(temps[1], ColonSymbol) && !strings.Contains(temps[1], EqualSymbol) {
   130  			l.TaintList[ips].DelTaintList = append(l.TaintList[ips].DelTaintList, newTaintStruct(strings.TrimSuffix(temps[1], DelSymbol), "", ""))
   131  			continue
   132  		}
   133  		taintArgs := strings.Split(temps[1], ColonSymbol)
   134  		if len(taintArgs) != 2 {
   135  			return fmt.Errorf("error: invalid taint data: %s", v)
   136  		}
   137  		kv, effect := taintArgs[0], taintArgs[1]
   138  		effect = strings.TrimSuffix(effect, DelSymbol)
   139  		if NotInEffect(v1.TaintEffect(effect), TaintEffectValues) {
   140  			return fmt.Errorf("taint effect %s need in %v", v, TaintEffectValues)
   141  		}
   142  		kvList := strings.Split(kv, EqualSymbol)
   143  		key, value := kvList[0], ""
   144  		if len(kvList) > 2 || key == "" {
   145  			return fmt.Errorf("error: invalid taint data: %s", temps[1])
   146  		}
   147  		if len(kvList) == 2 {
   148  			value = kvList[1]
   149  		}
   150  		taint := newTaintStruct(key, value, effect)
   151  		if _, ok := l.TaintList[ips]; !ok {
   152  			l.TaintList[ips] = &taintList{}
   153  		}
   154  		if strings.HasSuffix(temps[1], DelSymbol) {
   155  			l.TaintList[ips].DelTaintList = append(l.TaintList[ips].DelTaintList, taint)
   156  			continue
   157  		}
   158  		l.TaintList[ips].AddTaintList = append(l.TaintList[ips].AddTaintList, taint)
   159  	}
   160  	return nil
   161  }
   162  
   163  // UpdateTaints return nil mean's needn't update taint
   164  func (l *Taint) UpdateTaints(taints []v1.Taint, ip string) []v1.Taint {
   165  	needDel := false
   166  	for k, v := range taints {
   167  		l.removePresenceTaint(v, ip)
   168  		if l.isDelTaint(v, ip) {
   169  			needDel = true
   170  			taints = append(taints[:k], taints[k+1:]...)
   171  		}
   172  	}
   173  	if len(l.TaintList[ip].AddTaintList) == 0 && !needDel {
   174  		return nil
   175  	}
   176  	return append(taints, l.TaintList[ip].AddTaintList...)
   177  }
   178  
   179  //Remove existing taint
   180  func (l *Taint) removePresenceTaint(taint v1.Taint, ip string) {
   181  	for k, v := range l.TaintList[ip].AddTaintList {
   182  		if v.Key == taint.Key && v.Value == taint.Value && v.Effect == taint.Effect {
   183  			logger.Info("taint %s already exist", l.TaintList[ip].AddTaintList[k].String())
   184  			l.TaintList[ip].AddTaintList = append(l.TaintList[ip].AddTaintList[:k], l.TaintList[ip].AddTaintList[k+1:]...)
   185  		}
   186  	}
   187  }
   188  
   189  func (l *Taint) isDelTaint(taint v1.Taint, ip string) bool {
   190  	for _, v := range l.TaintList[ip].AddTaintList {
   191  		if v.Key == taint.Key && (v.Effect == taint.Effect || v.Effect == "") {
   192  			return true
   193  		}
   194  	}
   195  	return false
   196  }
   197  
   198  func NotInEffect(effect v1.TaintEffect, effects []v1.TaintEffect) bool {
   199  	for _, e := range effects {
   200  		if e == effect {
   201  			return false
   202  		}
   203  	}
   204  	return true
   205  }