package api
import (
"fmt"
"sort"
"strings"
"k8s.io/apimachinery/pkg/util/sets"
)
const (
NodePodNumberExceeded = "node(s) pod number exceeded"
NodeResourceFitFailed = "node(s) resource fit failed"
AllNodeUnavailableMsg = "all nodes are unavailable"
)
const (
PodReasonUnschedulable = "Unschedulable"
PodReasonSchedulable = "Schedulable"
PodReasonSchedulerError = "SchedulerError"
)
type FitErrors struct {
nodes map[string]*FitError
err string
}
func NewFitErrors() *FitErrors {
f := new(FitErrors)
f.nodes = make(map[string]*FitError)
return f
}
func (f *FitErrors) SetError(err string) {
f.err = err
}
func (f *FitErrors) SetNodeError(nodeName string, err error) {
var fe *FitError
switch obj := err.(type) {
case *FitError:
obj.NodeName = nodeName
fe = obj
default:
fe = &FitError{
NodeName: nodeName,
Status: []*Status{{Code: Error, Reason: obj.Error()}},
}
}
f.nodes[nodeName] = fe
}
func (f *FitErrors) GetUnschedulableAndUnresolvableNodes() map[string]sets.Empty {
ret := make(map[string]sets.Empty)
for _, node := range f.nodes {
if node.Status.ContainsUnschedulableAndUnresolvable() {
ret[node.NodeName] = sets.Empty{}
}
}
return ret
}
func (f *FitErrors) Error() string {
if f.err == "" {
f.err = fmt.Sprintf("0/%v", len(f.nodes)) + " nodes are unavailable"
}
if len(f.nodes) == 0 {
return f.err
}
reasons := make(map[string]int)
for _, node := range f.nodes {
for _, reason := range node.Reasons() {
reasons[reason]++
}
}
sortReasonsHistogram := func() []string {
reasonStrings := []string{}
for k, v := range reasons {
reasonStrings = append(reasonStrings, fmt.Sprintf("%v %v", v, k))
}
sort.Strings(reasonStrings)
return reasonStrings
}
reasonMsg := fmt.Sprintf(f.err+": %v.", strings.Join(sortReasonsHistogram(), ", "))
return reasonMsg
}
type FitError struct {
taskNamespace string
taskName string
NodeName string
Status StatusSets
}
func NewFitError(task *TaskInfo, node *NodeInfo, message ...string) *FitError {
fe := &FitError{
taskName: task.Name,
taskNamespace: task.Namespace,
NodeName: node.Name,
}
sts := make([]*Status, 0, len(message))
for _, msg := range message {
sts = append(sts, &Status{Reason: msg, Code: Error})
}
fe.Status = StatusSets(sts)
return fe
}
func NewFitErrWithStatus(task *TaskInfo, node *NodeInfo, sts ...*Status) *FitError {
fe := &FitError{
taskName: task.Name,
taskNamespace: task.Namespace,
NodeName: node.Name,
Status: sts,
}
return fe
}
func (fe *FitError) Reasons() []string {
if fe == nil {
return []string{}
}
return fe.Status.Reasons()
}
func (f *FitError) Error() string {
return fmt.Sprintf("task %s/%s on node %s fit failed: %s", f.taskNamespace, f.taskName, f.NodeName, strings.Join(f.Reasons(), ", "))
}
func WrapInsufficientResourceReason(resources []string) string {
if len(resources) == 0 {
return ""
}
return "Insufficient " + resources[0]
}