Skip to content
3 changes: 3 additions & 0 deletions apis/placement/v1beta1/binding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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"`
}

// ResourceBindingStatus represents the current status of a ClusterResourceBinding.
Expand Down
5 changes: 5 additions & 0 deletions apis/placement/v1beta1/policysnapshot_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const (

// PolicySnapshotNameFmt is clusterPolicySnapshot name format: {CRPName}-{PolicySnapshotIndex}.
PolicySnapshotNameFmt = "%s-%d"

// NumOfClustersAnnotation is an annotation that indicates the desired number of clusters where
// the selected resources should be placed. It is annotated on policy snapshots and is sync'd
// from the CRP to the currently active policy snapshot.
NumOfClustersAnnotation = fleetPrefix + "numOfClusters"
)

// +genclient
Expand Down
2 changes: 1 addition & 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 @@ -58,6 +100,7 @@ spec:
assigns the resources to.
type: string
required:
- clusterDecision
- resourceSnapshotName
- targetCluster
type: object
Expand Down
15 changes: 14 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,14 @@ 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
// 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 +64,8 @@ func (c *CycleState) Delete(key StateKey) {

// NewCycleState creates a CycleState.
func NewCycleState() *CycleState {
return &CycleState{}
return &CycleState{
store: sync.Map{},
skippedFilterPlugins: 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