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 }