Skip to content

Commit

Permalink
add PlacementConditionMisconfigured
Browse files Browse the repository at this point in the history
Signed-off-by: haoqing0110 <qhao@redhat.com>
  • Loading branch information
haoqing0110 committed Sep 8, 2022
1 parent 91f6c49 commit 01154cf
Show file tree
Hide file tree
Showing 20 changed files with 362 additions and 156 deletions.
85 changes: 85 additions & 0 deletions pkg/controllers/framework/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package framework

import (
"errors"
"strings"
)

// Code is the Status code/type which is returned from plugins.
type Code int

// These are predefined codes used in a Status.
const (
// Success means that plugin ran correctly and found placement schedulable.
// NOTE: A nil status is also considered as "Success".
Success Code = iota
// Warning means that plugin ran correctly and found placement schedulable, but with some failures to notice.
Warning
// Error is used for internal plugin errors etc.
Error
// Misconfigured is used for internal plugin configuration errors, unexpected input, etc.
Misconfigured
// Skip is used when a plugin chooses to skip running.
Skip
)

type Status struct {
code Code
// reasons contains the message about status.
reasons []string
// Err contains the error message.
err error
// plugin is an optional field that records the plugin name.
plugin string
}

// Code returns code of the Status.
func (s *Status) Code() Code {
if s == nil {
return Success
}
return s.code
}

// Message returns a concatenated message on reasons of the Status.
func (s *Status) Message() string {
if s == nil {
return ""
}
return strings.Join(s.reasons, ", ")
}

// AppendReason appends given reason to the Status.
func (s *Status) AppendReason(reason string) {
s.reasons = append(s.reasons, reason)
}

// AsError returns nil if the status is a success; otherwise returns an "error" object
// with a concatenated message on reasons of the Status.
func (s *Status) AsError() error {
if s.Code() == Success {
return nil
}
if s.err != nil {
return s.err
}
return errors.New(s.Message())
}

// FailedPlugin returns the failed plugin name.
func (s *Status) Plugin() string {
return s.plugin
}

// NewStatus makes a Status out of the given arguments and returns its pointer.
func NewStatus(plugin string, code Code, reasons ...string) *Status {
s := &Status{
code: code,
reasons: reasons,
plugin: plugin,
}
if code == Error {
s.err = errors.New(s.Message())
}
return s
}
36 changes: 19 additions & 17 deletions pkg/controllers/scheduling/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
clusterlisterv1beta1 "open-cluster-management.io/api/client/cluster/listers/cluster/v1beta1"
clusterapiv1 "open-cluster-management.io/api/cluster/v1"
clusterapiv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
"open-cluster-management.io/placement/pkg/controllers/framework"
"open-cluster-management.io/placement/pkg/plugins"
"open-cluster-management.io/placement/pkg/plugins/addon"
"open-cluster-management.io/placement/pkg/plugins/balance"
Expand All @@ -39,7 +40,7 @@ type Scheduler interface {
ctx context.Context,
placement *clusterapiv1beta1.Placement,
clusters []*clusterapiv1.ManagedCluster,
) (ScheduleResult, error)
) (ScheduleResult, *framework.Status)
}

type ScheduleResult interface {
Expand Down Expand Up @@ -162,8 +163,9 @@ func (s *pluginScheduler) Schedule(
ctx context.Context,
placement *clusterapiv1beta1.Placement,
clusters []*clusterapiv1.ManagedCluster,
) (ScheduleResult, error) {
) (ScheduleResult, *framework.Status) {
filtered := clusters
finalStatus := framework.NewStatus("", framework.Success, "")

results := &scheduleResult{
filteredRecords: map[string][]*clusterapiv1.ManagedCluster{},
Expand All @@ -174,12 +176,13 @@ func (s *pluginScheduler) Schedule(
filterPipline := []string{}

for _, f := range s.filters {
filterResult := f.Filter(ctx, placement, filtered)
filterResult, status := f.Filter(ctx, placement, filtered)
filtered = filterResult.Filtered
err := filterResult.Err

if err != nil {
return nil, err
if status.Code() == framework.Error || status.Code() == framework.Misconfigured {
return nil, status
} else if status.Code() == framework.Warning {
finalStatus = status
}

filterPipline = append(filterPipline, f.Name())
Expand All @@ -192,13 +195,13 @@ func (s *pluginScheduler) Schedule(
// For example, weights is {"Steady": 1, "Balance":1, "AddOn/default/ratio":3}.
weights, err := getWeights(s.prioritizerWeights, placement)
if err != nil {
return nil, err
return nil, framework.NewStatus("", framework.Misconfigured, err.Error())
}

// 2. Generate prioritizers for each placement whose weight != 0.
prioritizers, err := getPrioritizers(weights, s.handle)
if err != nil {
return nil, err
return nil, framework.NewStatus("", framework.Misconfigured, err.Error())
}

// 3. Calculate clusters scores.
Expand All @@ -208,12 +211,13 @@ func (s *pluginScheduler) Schedule(
}
for sc, p := range prioritizers {
// Get cluster score.
scoreResult := p.Score(ctx, placement, filtered)
scoreResult, status := p.Score(ctx, placement, filtered)
score := scoreResult.Scores
err := scoreResult.Err

if err != nil {
return nil, err
if status.Code() == framework.Error || status.Code() == framework.Misconfigured {
return nil, status
} else if status.Code() == framework.Warning {
finalStatus = status
}

// Record prioritizer score and weight
Expand Down Expand Up @@ -252,19 +256,19 @@ func (s *pluginScheduler) Schedule(

// set placement requeue time
for _, f := range s.filters {
if r := f.RequeueAfter(ctx, placement); r.RequeueTime != nil {
if r, _ := f.RequeueAfter(ctx, placement); r.RequeueTime != nil {
newRequeueAfter := time.Until(*r.RequeueTime)
results.requeueAfter = setRequeueAfter(results.requeueAfter, &newRequeueAfter)
}
}
for _, p := range prioritizers {
if r := p.RequeueAfter(ctx, placement); r.RequeueTime != nil {
if r, _ := p.RequeueAfter(ctx, placement); r.RequeueTime != nil {
newRequeueAfter := time.Until(*r.RequeueTime)
results.requeueAfter = setRequeueAfter(results.requeueAfter, &newRequeueAfter)
}
}

return results, nil
return results, finalStatus
}

// makeClusterDecisions selects clusters based on given cluster slice and then creates
Expand Down Expand Up @@ -352,12 +356,10 @@ func getPrioritizers(weights map[clusterapiv1beta1.ScoreCoordinate]int32, handle
case k.BuiltIn == PrioritizerResourceAllocatableCPU || k.BuiltIn == PrioritizerResourceAllocatableMemory:
result[k] = resource.NewResourcePrioritizerBuilder(handle).WithPrioritizerName(k.BuiltIn).Build()
default:
//TODO: show the failure in placement.status conditions
return nil, fmt.Errorf("incorrect builtin prioritizer: %s", k.BuiltIn)
}
} else {
if k.AddOn == nil {
//TODO: show the failure in placement.status conditions
return nil, fmt.Errorf("addOn should not be empty")
}
result[k] = addon.NewAddOnPrioritizerBuilder(handle).WithResourceName(k.AddOn.ResourceName).WithScoreName(k.AddOn.ScoreName).Build()
Expand Down
3 changes: 2 additions & 1 deletion pkg/controllers/scheduling/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,12 @@ func TestSchedule(t *testing.T) {
c.initObjs = append(c.initObjs, c.placement)
clusterClient := clusterfake.NewSimpleClientset(c.initObjs...)
s := NewPluginScheduler(testinghelpers.NewFakePluginHandle(t, clusterClient, c.initObjs...))
result, err := s.Schedule(
result, status := s.Schedule(
context.TODO(),
c.placement,
c.clusters,
)
err := status.AsError()
if err != nil {
t.Errorf("unexpected err: %v", err)
}
Expand Down
Loading

0 comments on commit 01154cf

Please sign in to comment.