Skip to content

Commit

Permalink
Added scheduler framework logic
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelawyu committed Jun 25, 2023
1 parent 7899fa5 commit a681896
Show file tree
Hide file tree
Showing 21 changed files with 3,022 additions and 182 deletions.
9 changes: 9 additions & 0 deletions apis/placement/v1beta1/binding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// SchedulerCleanupFinalizer is a finalizer added by the scheduler to all bindings, to make sure
// that the scheduler can react to binding deletions if necessary.
SchedulerCleanupFinalizer = fleetPrefix + "scheduler-cleanup"
)

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,categories={fleet},shortName=rb
// +kubebuilder:subresource:status
Expand Down Expand Up @@ -44,6 +50,9 @@ type ResourceBindingSpec struct {

// TargetCluster is the name of the cluster that the scheduler assigns the resources to.
TargetCluster string `json:"targetCluster"`

// ClusterDecision explains why the scheduler makes this binding.
ClusterDecision ClusterDecision `json:"clusterDecision"`
}

// BindingState is the state of the binding
Expand Down
3 changes: 2 additions & 1 deletion apis/placement/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions config/crd/bases/placement.karavel.io_clusterresourcebindings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ spec:
spec:
description: The desired state of ClusterResourceBinding.
properties:
clusterDecision:
description: ClusterDecision explains why the scheduler makes this
binding.
properties:
clusterName:
description: ClusterName is the name of the ManagedCluster. If
it is not empty, its value should be unique cross all placement
decisions for the Placement.
type: string
clusterScore:
description: ClusterScore represents the score of the cluster
calculated by the scheduler.
properties:
affinityScore:
description: AffinityScore represents the affinity score of
the cluster calculated by the last scheduling decision based
on the preferred affinity selector. An affinity score may
not present if the cluster does not meet the required affinity.
format: int32
type: integer
priorityScore:
description: TopologySpreadScore represents the priority score
of the cluster calculated by the last scheduling decision
based on the topology spread applied to the cluster. A priority
score may not present if the cluster does not meet the topology
spread.
format: int32
type: integer
type: object
reason:
description: Reason represents the reason why the cluster is selected
or not.
type: string
selected:
description: Selected indicates if this cluster is selected by
the scheduler.
type: boolean
required:
- clusterName
- reason
- selected
type: object
resourceSnapshotName:
description: ResourceSnapshotName is the name of the resource snapshot
that this resource binding points to. If the resources are divided
Expand All @@ -62,6 +104,7 @@ spec:
assigns the resources to.
type: string
required:
- clusterDecision
- resourceSnapshotName
- state
- targetCluster
Expand Down
18 changes: 17 additions & 1 deletion pkg/scheduler/framework/cyclestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package framework
import (
"fmt"
"sync"

"k8s.io/apimachinery/pkg/util/sets"
)

// StateKey is the key for a state value stored in a CycleState.
Expand All @@ -32,6 +34,16 @@ type CycleStatePluginReadWriter interface {
type CycleState struct {
// store is a concurrency-safe store (a map).
store sync.Map

// skippedFilterPlugins is a set of Filter plugins that should be skipped in the current scheduling cycle.
skippedFilterPlugins sets.String
// skippedScorePlugins is a set of Score plugins that should be skipped in the current scheduling cycle.
skippedScorePlugins sets.String
// desiredBatchSize is the desired batch size for the current scheduling cycle.
desiredBatchSize int
// batchSizeLimit is the limit on batch size for the current scheduling cycle, set by
// post-batch plugins.
batchSizeLimit int
}

// Read retrieves a value from CycleState by a key.
Expand All @@ -54,5 +66,9 @@ func (c *CycleState) Delete(key StateKey) {

// NewCycleState creates a CycleState.
func NewCycleState() *CycleState {
return &CycleState{}
return &CycleState{
store: sync.Map{},
skippedFilterPlugins: sets.NewString(),
skippedScorePlugins: sets.NewString(),
}
}
112 changes: 112 additions & 0 deletions pkg/scheduler/framework/dummyplugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/

package framework

import (
"context"

fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
)

const (
dummyAllPurposePluginName = "dummyAllPurposePlugin"
dummyPostBatchPluginNameFormat = "dummyPostBatchPlugin-%d"
dummyPreFilterPluginNameFormat = "dummyPreFilterPlugin-%d"
dummyFilterPluginNameFormat = "dummyFilterPlugin-%d"
)

// A no-op, dummy plugin which connects to all extension points.
type DummyAllPurposePlugin struct{}

// Name returns the name of the dummy plugin.
func (p *DummyAllPurposePlugin) Name() string {
return dummyAllPurposePluginName
}

// PostBatch implements the PostBatch interface for the dummy plugin.
func (p *DummyAllPurposePlugin) PostBatch(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (size int, status *Status) { //nolint:revive
return 1, nil
}

// PreFilter implements the PreFilter interface for the dummy plugin.
func (p *DummyAllPurposePlugin) PreFilter(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (status *Status) { //nolint:revive
return nil
}

// Filter implements the Filter interface for the dummy plugin.
func (p *DummyAllPurposePlugin) Filter(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot, cluster *fleetv1beta1.MemberCluster) (status *Status) { //nolint:revive
return nil
}

// PreScore implements the PreScore interface for the dummy plugin.
func (p *DummyAllPurposePlugin) PreScore(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (status *Status) { //nolint:revive
return nil
}

// Score implements the Score interface for the dummy plugin.
func (p *DummyAllPurposePlugin) Score(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot, cluster *fleetv1beta1.MemberCluster) (score *ClusterScore, status *Status) { //nolint:revive
return &ClusterScore{}, nil
}

// SetUpWithFramework is a no-op to satisfy the Plugin interface.
func (p *DummyAllPurposePlugin) SetUpWithFramework(handle Handle) {} // nolint:revive

// A dummy post batch plugin.
type dummyPostBatchPlugin struct {
name string
runner func(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (size int, status *Status)
}

// Name returns the name of the dummy plugin.
func (p *dummyPostBatchPlugin) Name() string {
return p.name
}

// PostBatch implements the PostBatch interface for the dummy plugin.
func (p *dummyPostBatchPlugin) PostBatch(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (size int, status *Status) { //nolint:revive
return p.runner(ctx, state, policy)
}

// SetUpWithFramework is a no-op to satisfy the Plugin interface.
func (p *dummyPostBatchPlugin) SetUpWithFramework(handle Handle) {} // nolint:revive

// A dummy pre-filter plugin.
type dummyPreFilterPlugin struct {
name string
runner func(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (status *Status)
}

// Name returns the name of the dummy plugin.
func (p *dummyPreFilterPlugin) Name() string {
return p.name
}

// PreFilter implements the PreFilter interface for the dummy plugin.
func (p *dummyPreFilterPlugin) PreFilter(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot) (status *Status) { //nolint:revive
return p.runner(ctx, state, policy)
}

// SetUpWithFramework is a no-op to satisfy the Plugin interface.
func (p *dummyPreFilterPlugin) SetUpWithFramework(handle Handle) {} // nolint:revive

// A dummy filter plugin.
type dummyFilterPlugin struct {
name string
runner func(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot, cluster *fleetv1beta1.MemberCluster) (status *Status)
}

// Name returns the name of the dummy plugin.
func (d *dummyFilterPlugin) Name() string {
return d.name
}

// Filter implements the Filter interface for the dummy plugin.
func (d *dummyFilterPlugin) Filter(ctx context.Context, state CycleStatePluginReadWriter, policy *fleetv1beta1.ClusterPolicySnapshot, cluster *fleetv1beta1.MemberCluster) (status *Status) { //nolint:revive
return d.runner(ctx, state, policy, cluster)
}

// SetUpWithFramework is a no-op to satisfy the Plugin interface.
func (p *dummyFilterPlugin) SetUpWithFramework(handle Handle) {} // nolint: revive
Loading

0 comments on commit a681896

Please sign in to comment.