github.com/rancher/types@v0.0.0-20220328215343-4370ff10ecd5/mapper/scheduling.go (about)

     1  package mapper
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"regexp"
     9  
    10  	"github.com/rancher/norman/types"
    11  	"github.com/rancher/norman/types/convert"
    12  	"github.com/rancher/norman/types/values"
    13  	"k8s.io/api/core/v1"
    14  )
    15  
    16  var (
    17  	exprRegexp = regexp.MustCompile("^(.*?)\\s*(=|!=|<|>| in | notin )\\s*(.*)$")
    18  )
    19  
    20  type SchedulingMapper struct {
    21  }
    22  
    23  func (s SchedulingMapper) FromInternal(data map[string]interface{}) {
    24  	defer func() {
    25  		delete(data, "nodeSelector")
    26  		delete(data, "affinity")
    27  	}()
    28  
    29  	var requireAll []string
    30  	for key, value := range convert.ToMapInterface(data["nodeSelector"]) {
    31  		if value == "" {
    32  			requireAll = append(requireAll, key)
    33  		} else {
    34  			requireAll = append(requireAll, fmt.Sprintf("%s = %s", key, value))
    35  		}
    36  	}
    37  
    38  	if len(requireAll) > 0 {
    39  		values.PutValue(data, requireAll, "scheduling", "node", "requireAll")
    40  	}
    41  
    42  	v, ok := data["affinity"]
    43  	if !ok || v == nil {
    44  		return
    45  	}
    46  
    47  	affinity := &v1.Affinity{}
    48  	if err := convert.ToObj(v, affinity); err != nil {
    49  		return
    50  	}
    51  
    52  	if affinity.NodeAffinity != nil {
    53  		s.nodeAffinity(data, affinity.NodeAffinity)
    54  	}
    55  }
    56  
    57  func (s SchedulingMapper) nodeAffinity(data map[string]interface{}, nodeAffinity *v1.NodeAffinity) {
    58  	var requireAll []string
    59  	var requireAny []string
    60  	var preferred []string
    61  
    62  	if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
    63  		for _, term := range nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
    64  			exprs := NodeSelectorTermToStrings(term)
    65  			if len(exprs) == 0 {
    66  				continue
    67  			}
    68  			if len(requireAny) > 0 {
    69  				// Once any is set all new terms go to any
    70  				requireAny = append(requireAny, strings.Join(exprs, " && "))
    71  			} else if len(requireAll) > 0 {
    72  				// If all is already set, we actually need to move everything to any
    73  				requireAny = append(requireAny, strings.Join(requireAll, " && "))
    74  				requireAny = append(requireAny, strings.Join(exprs, " && "))
    75  				requireAll = []string{}
    76  			} else {
    77  				// The first term is considered all
    78  				requireAll = exprs
    79  			}
    80  		}
    81  	}
    82  
    83  	if nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
    84  		sortPreferred(nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
    85  		for _, term := range nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
    86  			exprs := NodeSelectorTermToStrings(term.Preference)
    87  			preferred = append(preferred, strings.Join(exprs, " && "))
    88  		}
    89  	}
    90  
    91  	if len(requireAll) > 0 {
    92  		values.PutValue(data, requireAll, "scheduling", "node", "requireAll")
    93  	}
    94  	if len(requireAny) > 0 {
    95  		values.PutValue(data, requireAny, "scheduling", "node", "requireAny")
    96  	}
    97  	if len(preferred) > 0 {
    98  		values.PutValue(data, preferred, "scheduling", "node", "preferred")
    99  	}
   100  }
   101  
   102  func sortPreferred(terms []v1.PreferredSchedulingTerm) {
   103  	sort.Slice(terms, func(i, j int) bool {
   104  		return terms[i].Weight > terms[j].Weight
   105  	})
   106  }
   107  
   108  func NodeSelectorTermToStrings(term v1.NodeSelectorTerm) []string {
   109  	exprs := []string{}
   110  
   111  	for _, expr := range term.MatchExpressions {
   112  		nextExpr := ""
   113  		switch expr.Operator {
   114  		case v1.NodeSelectorOpIn:
   115  			if len(expr.Values) > 1 {
   116  				nextExpr = fmt.Sprintf("%s in (%s)", expr.Key, strings.Join(expr.Values, ", "))
   117  			} else if len(expr.Values) == 1 {
   118  				nextExpr = fmt.Sprintf("%s = %s", expr.Key, expr.Values[0])
   119  			}
   120  		case v1.NodeSelectorOpNotIn:
   121  			if len(expr.Values) > 1 {
   122  				nextExpr = fmt.Sprintf("%s notin (%s)", expr.Key, strings.Join(expr.Values, ", "))
   123  			} else if len(expr.Values) == 1 {
   124  				nextExpr = fmt.Sprintf("%s != %s", expr.Key, expr.Values[0])
   125  			}
   126  		case v1.NodeSelectorOpExists:
   127  			nextExpr = expr.Key
   128  		case v1.NodeSelectorOpDoesNotExist:
   129  			nextExpr = "!" + expr.Key
   130  		case v1.NodeSelectorOpGt:
   131  			if len(expr.Values) == 1 {
   132  				nextExpr = fmt.Sprintf("%s > %s", expr.Key, expr.Values[0])
   133  			}
   134  		case v1.NodeSelectorOpLt:
   135  			if len(expr.Values) == 1 {
   136  				nextExpr = fmt.Sprintf("%s < %s", expr.Key, expr.Values[0])
   137  			}
   138  		}
   139  
   140  		if nextExpr != "" {
   141  			exprs = append(exprs, nextExpr)
   142  		}
   143  	}
   144  
   145  	return exprs
   146  }
   147  
   148  func StringsToNodeSelectorTerm(exprs []string) []v1.NodeSelectorTerm {
   149  	result := []v1.NodeSelectorTerm{}
   150  
   151  	for _, inter := range exprs {
   152  		term := v1.NodeSelectorTerm{}
   153  
   154  		for _, expr := range strings.Split(inter, "&&") {
   155  			groups := exprRegexp.FindStringSubmatch(expr)
   156  			selectorRequirement := v1.NodeSelectorRequirement{}
   157  
   158  			if groups == nil {
   159  				if strings.HasPrefix(expr, "!") {
   160  					selectorRequirement.Key = strings.TrimSpace(expr[1:])
   161  					selectorRequirement.Operator = v1.NodeSelectorOpDoesNotExist
   162  				} else {
   163  					selectorRequirement.Key = strings.TrimSpace(expr)
   164  					selectorRequirement.Operator = v1.NodeSelectorOpExists
   165  				}
   166  			} else {
   167  				selectorRequirement.Key = strings.TrimSpace(groups[1])
   168  				selectorRequirement.Values = convert.ToValuesSlice(groups[3])
   169  				op := strings.TrimSpace(groups[2])
   170  				switch op {
   171  				case "=":
   172  					selectorRequirement.Operator = v1.NodeSelectorOpIn
   173  				case "!=":
   174  					selectorRequirement.Operator = v1.NodeSelectorOpNotIn
   175  				case "notin":
   176  					selectorRequirement.Operator = v1.NodeSelectorOpNotIn
   177  				case "in":
   178  					selectorRequirement.Operator = v1.NodeSelectorOpIn
   179  				case "<":
   180  					selectorRequirement.Operator = v1.NodeSelectorOpLt
   181  				case ">":
   182  					selectorRequirement.Operator = v1.NodeSelectorOpGt
   183  				}
   184  			}
   185  
   186  			term.MatchExpressions = append(term.MatchExpressions, selectorRequirement)
   187  		}
   188  
   189  		result = append(result, term)
   190  	}
   191  
   192  	return result
   193  }
   194  
   195  func (s SchedulingMapper) ToInternal(data map[string]interface{}) error {
   196  	defer func() {
   197  		delete(data, "scheduling")
   198  	}()
   199  
   200  	nodeName := convert.ToString(values.GetValueN(data, "scheduling", "node", "nodeId"))
   201  	if nodeName != "" {
   202  		data["nodeName"] = nodeName
   203  	}
   204  
   205  	requireAllV := values.GetValueN(data, "scheduling", "node", "requireAll")
   206  	requireAnyV := values.GetValueN(data, "scheduling", "node", "requireAny")
   207  	preferredV := values.GetValueN(data, "scheduling", "node", "preferred")
   208  
   209  	if requireAllV == nil && requireAnyV == nil && preferredV == nil {
   210  		return nil
   211  	}
   212  
   213  	requireAll := convert.ToStringSlice(requireAllV)
   214  	requireAny := convert.ToStringSlice(requireAnyV)
   215  	preferred := convert.ToStringSlice(preferredV)
   216  
   217  	if len(requireAll) == 0 && len(requireAny) == 0 && len(preferred) == 0 {
   218  		values.PutValue(data, nil, "affinity", "nodeAffinity")
   219  		return nil
   220  	}
   221  
   222  	nodeAffinity := v1.NodeAffinity{}
   223  
   224  	if len(requireAll) > 0 {
   225  		nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{
   226  			NodeSelectorTerms: []v1.NodeSelectorTerm{
   227  				AggregateTerms(StringsToNodeSelectorTerm(requireAll)),
   228  			},
   229  		}
   230  	}
   231  
   232  	if len(requireAny) > 0 {
   233  		if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
   234  			nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{}
   235  		}
   236  		nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = StringsToNodeSelectorTerm(requireAny)
   237  	}
   238  
   239  	if len(preferred) > 0 {
   240  		count := int32(100)
   241  		for _, term := range StringsToNodeSelectorTerm(preferred) {
   242  			nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(
   243  				nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, v1.PreferredSchedulingTerm{
   244  					Weight:     count,
   245  					Preference: term,
   246  				})
   247  			count--
   248  		}
   249  	}
   250  
   251  	affinity, _ := convert.EncodeToMap(&v1.Affinity{
   252  		NodeAffinity: &nodeAffinity,
   253  	})
   254  
   255  	if nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution == nil {
   256  		values.PutValue(affinity, nil, "nodeAffinity", "preferredDuringSchedulingIgnoredDuringExecution")
   257  	}
   258  
   259  	if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
   260  		values.PutValue(affinity, nil, "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution")
   261  	}
   262  
   263  	data["affinity"] = affinity
   264  
   265  	return nil
   266  }
   267  
   268  func AggregateTerms(terms []v1.NodeSelectorTerm) v1.NodeSelectorTerm {
   269  	result := v1.NodeSelectorTerm{}
   270  	for _, term := range terms {
   271  		result.MatchExpressions = append(result.MatchExpressions, term.MatchExpressions...)
   272  	}
   273  	return result
   274  }
   275  
   276  func (s SchedulingMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
   277  	delete(schema.ResourceFields, "nodeSelector")
   278  	delete(schema.ResourceFields, "affinity")
   279  	return nil
   280  }