mirror of
https://github.com/rancher/types.git
synced 2025-06-29 15:06:48 +00:00
https://github.com/rancher/rancher/issues/14939. Problems: we are not using lazy match to match the group (!=|=) so that "foo != bar` can be parsed wrongly as (foo !) (=) (bar). Solutions: Use lazy match to match as less as possible the characters in the first group. Also improved the whitespace matches.
273 lines
7.7 KiB
Go
273 lines
7.7 KiB
Go
package mapper
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"regexp"
|
|
|
|
"github.com/rancher/norman/types"
|
|
"github.com/rancher/norman/types/convert"
|
|
"github.com/rancher/norman/types/values"
|
|
"k8s.io/api/core/v1"
|
|
)
|
|
|
|
var (
|
|
exprRegexp = regexp.MustCompile("^(.*?)\\s*(=|!=|<|>|in|notin)\\s*(.*)$")
|
|
)
|
|
|
|
type SchedulingMapper struct {
|
|
}
|
|
|
|
func (s SchedulingMapper) FromInternal(data map[string]interface{}) {
|
|
defer func() {
|
|
delete(data, "nodeSelector")
|
|
delete(data, "affinity")
|
|
}()
|
|
|
|
var requireAll []string
|
|
for key, value := range convert.ToMapInterface(data["nodeSelector"]) {
|
|
if value == "" {
|
|
requireAll = append(requireAll, key)
|
|
} else {
|
|
requireAll = append(requireAll, fmt.Sprintf("%s = %s", key, value))
|
|
}
|
|
}
|
|
|
|
if len(requireAll) > 0 {
|
|
values.PutValue(data, requireAll, "scheduling", "node", "requireAll")
|
|
}
|
|
|
|
v, ok := data["affinity"]
|
|
if !ok || v == nil {
|
|
return
|
|
}
|
|
|
|
affinity := &v1.Affinity{}
|
|
if err := convert.ToObj(v, affinity); err != nil {
|
|
return
|
|
}
|
|
|
|
if affinity.NodeAffinity != nil {
|
|
s.nodeAffinity(data, affinity.NodeAffinity)
|
|
}
|
|
}
|
|
|
|
func (s SchedulingMapper) nodeAffinity(data map[string]interface{}, nodeAffinity *v1.NodeAffinity) {
|
|
var requireAll []string
|
|
var requireAny []string
|
|
var preferred []string
|
|
|
|
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
|
for _, term := range nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
|
|
exprs := NodeSelectorTermToStrings(term)
|
|
if len(exprs) == 0 {
|
|
continue
|
|
}
|
|
if len(requireAny) > 0 {
|
|
// Once any is set all new terms go to any
|
|
requireAny = append(requireAny, strings.Join(exprs, " && "))
|
|
} else if len(requireAll) > 0 {
|
|
// If all is already set, we actually need to move everything to any
|
|
requireAny = append(requireAny, strings.Join(requireAll, " && "))
|
|
requireAny = append(requireAny, strings.Join(exprs, " && "))
|
|
requireAll = []string{}
|
|
} else {
|
|
// The first term is considered all
|
|
requireAll = exprs
|
|
}
|
|
}
|
|
}
|
|
|
|
if nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
|
|
sortPreferred(nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
|
|
for _, term := range nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
|
exprs := NodeSelectorTermToStrings(term.Preference)
|
|
preferred = append(preferred, strings.Join(exprs, " && "))
|
|
}
|
|
}
|
|
|
|
if len(requireAll) > 0 {
|
|
values.PutValue(data, requireAll, "scheduling", "node", "requireAll")
|
|
}
|
|
if len(requireAny) > 0 {
|
|
values.PutValue(data, requireAny, "scheduling", "node", "requireAny")
|
|
}
|
|
if len(preferred) > 0 {
|
|
values.PutValue(data, preferred, "scheduling", "node", "preferred")
|
|
}
|
|
}
|
|
|
|
func sortPreferred(terms []v1.PreferredSchedulingTerm) {
|
|
sort.Slice(terms, func(i, j int) bool {
|
|
return terms[i].Weight > terms[j].Weight
|
|
})
|
|
}
|
|
|
|
func NodeSelectorTermToStrings(term v1.NodeSelectorTerm) []string {
|
|
exprs := []string{}
|
|
|
|
for _, expr := range term.MatchExpressions {
|
|
nextExpr := ""
|
|
switch expr.Operator {
|
|
case v1.NodeSelectorOpIn:
|
|
if len(expr.Values) > 1 {
|
|
nextExpr = fmt.Sprintf("%s in (%s)", expr.Key, strings.Join(expr.Values, ", "))
|
|
} else if len(expr.Values) == 1 {
|
|
nextExpr = fmt.Sprintf("%s = %s", expr.Key, expr.Values[0])
|
|
}
|
|
case v1.NodeSelectorOpNotIn:
|
|
if len(expr.Values) > 1 {
|
|
nextExpr = fmt.Sprintf("%s notin (%s)", expr.Key, strings.Join(expr.Values, ", "))
|
|
} else if len(expr.Values) == 1 {
|
|
nextExpr = fmt.Sprintf("%s != %s", expr.Key, expr.Values[0])
|
|
}
|
|
case v1.NodeSelectorOpExists:
|
|
nextExpr = expr.Key
|
|
case v1.NodeSelectorOpDoesNotExist:
|
|
nextExpr = "!" + expr.Key
|
|
case v1.NodeSelectorOpGt:
|
|
if len(expr.Values) == 1 {
|
|
nextExpr = fmt.Sprintf("%s > %s", expr.Key, expr.Values[0])
|
|
}
|
|
case v1.NodeSelectorOpLt:
|
|
if len(expr.Values) == 1 {
|
|
nextExpr = fmt.Sprintf("%s < %s", expr.Key, expr.Values[0])
|
|
}
|
|
}
|
|
|
|
if nextExpr != "" {
|
|
exprs = append(exprs, nextExpr)
|
|
}
|
|
}
|
|
|
|
return exprs
|
|
}
|
|
|
|
func StringsToNodeSelectorTerm(exprs []string) []v1.NodeSelectorTerm {
|
|
result := []v1.NodeSelectorTerm{}
|
|
|
|
for _, inter := range exprs {
|
|
term := v1.NodeSelectorTerm{}
|
|
|
|
for _, expr := range strings.Split(inter, "&&") {
|
|
groups := exprRegexp.FindStringSubmatch(expr)
|
|
selectorRequirement := v1.NodeSelectorRequirement{}
|
|
|
|
if groups == nil {
|
|
if strings.HasPrefix(expr, "!") {
|
|
selectorRequirement.Key = strings.TrimSpace(expr[1:])
|
|
selectorRequirement.Operator = v1.NodeSelectorOpDoesNotExist
|
|
} else {
|
|
selectorRequirement.Key = strings.TrimSpace(expr)
|
|
selectorRequirement.Operator = v1.NodeSelectorOpExists
|
|
}
|
|
} else {
|
|
selectorRequirement.Key = strings.TrimSpace(groups[1])
|
|
selectorRequirement.Values = convert.ToValuesSlice(groups[3])
|
|
op := strings.TrimSpace(groups[2])
|
|
switch op {
|
|
case "=":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpIn
|
|
case "!=":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpNotIn
|
|
case "notin":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpNotIn
|
|
case "in":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpIn
|
|
case "<":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpLt
|
|
case ">":
|
|
selectorRequirement.Operator = v1.NodeSelectorOpGt
|
|
}
|
|
}
|
|
|
|
term.MatchExpressions = append(term.MatchExpressions, selectorRequirement)
|
|
}
|
|
|
|
result = append(result, term)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (s SchedulingMapper) ToInternal(data map[string]interface{}) error {
|
|
defer func() {
|
|
delete(data, "scheduling")
|
|
}()
|
|
|
|
nodeName := convert.ToString(values.GetValueN(data, "scheduling", "node", "nodeId"))
|
|
if nodeName != "" {
|
|
data["nodeName"] = nodeName
|
|
}
|
|
|
|
requireAll := convert.ToStringSlice(values.GetValueN(data, "scheduling", "node", "requireAll"))
|
|
requireAny := convert.ToStringSlice(values.GetValueN(data, "scheduling", "node", "requireAny"))
|
|
preferred := convert.ToStringSlice(values.GetValueN(data, "scheduling", "node", "preferred"))
|
|
|
|
if len(requireAll) == 0 && len(requireAny) == 0 && len(preferred) == 0 {
|
|
values.PutValue(data, nil, "affinity", "nodeAffinity")
|
|
return nil
|
|
}
|
|
|
|
nodeAffinity := v1.NodeAffinity{}
|
|
|
|
if len(requireAll) > 0 {
|
|
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
AggregateTerms(StringsToNodeSelectorTerm(requireAll)),
|
|
},
|
|
}
|
|
}
|
|
|
|
if len(requireAny) > 0 {
|
|
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
|
|
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{}
|
|
}
|
|
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = StringsToNodeSelectorTerm(requireAny)
|
|
}
|
|
|
|
if len(preferred) > 0 {
|
|
count := int32(100)
|
|
for _, term := range StringsToNodeSelectorTerm(preferred) {
|
|
nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(
|
|
nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, v1.PreferredSchedulingTerm{
|
|
Weight: count,
|
|
Preference: term,
|
|
})
|
|
count--
|
|
}
|
|
}
|
|
|
|
affinity, _ := convert.EncodeToMap(&v1.Affinity{
|
|
NodeAffinity: &nodeAffinity,
|
|
})
|
|
|
|
if nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution == nil {
|
|
values.PutValue(affinity, nil, "nodeAffinity", "preferredDuringSchedulingIgnoredDuringExecution")
|
|
}
|
|
|
|
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
|
|
values.PutValue(affinity, nil, "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution")
|
|
}
|
|
|
|
data["affinity"] = affinity
|
|
|
|
return nil
|
|
}
|
|
|
|
func AggregateTerms(terms []v1.NodeSelectorTerm) v1.NodeSelectorTerm {
|
|
result := v1.NodeSelectorTerm{}
|
|
for _, term := range terms {
|
|
result.MatchExpressions = append(result.MatchExpressions, term.MatchExpressions...)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (s SchedulingMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
|
|
delete(schema.ResourceFields, "nodeSelector")
|
|
delete(schema.ResourceFields, "affinity")
|
|
return nil
|
|
}
|