mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-22 17:14:10 +00:00
Modify the incorrect format of sentence in the README.md
Signed-off-by: wangglbj <wangglbj@cn.ibm.com>
This commit is contained in:
@@ -115,7 +115,7 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Check the 'PlacementDecision' created for this placement. It contains all selected clusters in status.
|
||||
Check the `PlacementDecision` created for this placement. It contains all selected clusters in status.
|
||||
|
||||
```txt
|
||||
kubectl get placementdecisions
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"open-cluster-management.io/placement/pkg/plugins"
|
||||
"open-cluster-management.io/placement/pkg/plugins/balance"
|
||||
"open-cluster-management.io/placement/pkg/plugins/predicate"
|
||||
"open-cluster-management.io/placement/pkg/plugins/resource"
|
||||
"open-cluster-management.io/placement/pkg/plugins/steady"
|
||||
)
|
||||
|
||||
@@ -120,6 +121,10 @@ func NewPluginScheduler(handle plugins.Handle) *pluginScheduler {
|
||||
prioritizers: []plugins.Prioritizer{
|
||||
balance.New(handle),
|
||||
steady.New(handle),
|
||||
resource.NewResourcePrioritizerBuilder(handle).WithPrioritizerName("ResourceAllocatableCPU").Build(),
|
||||
resource.NewResourcePrioritizerBuilder(handle).WithPrioritizerName("ResourceAllocatableMemory").Build(),
|
||||
resource.NewResourcePrioritizerBuilder(handle).WithPrioritizerName("ResourceRatioCPU").Build(),
|
||||
resource.NewResourcePrioritizerBuilder(handle).WithPrioritizerName("ResourceRatioMemory").Build(),
|
||||
},
|
||||
prioritizerWeights: defaultPrioritizerConfig,
|
||||
}
|
||||
|
||||
@@ -59,6 +59,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).Build(),
|
||||
@@ -96,6 +116,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
expectedUnScheduled: 2,
|
||||
},
|
||||
@@ -140,6 +180,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 100, "cluster3": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
expectedUnScheduled: 0,
|
||||
},
|
||||
@@ -171,6 +231,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).Build(),
|
||||
@@ -179,69 +259,115 @@ func TestSchedule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "placement with additive Prioritizer Policy",
|
||||
placement: testinghelpers.NewPlacement(placementNamespace, placementName).WithPrioritizerPolicy("Additive").WithPrioritizerConfig("Steady", 3).Build(),
|
||||
placement: testinghelpers.NewPlacement(placementNamespace, placementName).WithNOC(2).WithPrioritizerPolicy("Additive").WithPrioritizerConfig("Balance", 3).WithPrioritizerConfig("ResourceRatioCPU", 1).Build(),
|
||||
initObjs: []runtime.Object{
|
||||
testinghelpers.NewClusterSet(clusterSetName),
|
||||
testinghelpers.NewClusterSetBinding(placementNamespace, clusterSetName),
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "10", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
},
|
||||
decisions: []runtime.Object{},
|
||||
expectedDecisions: []clusterapiv1alpha1.ClusterDecision{
|
||||
{ClusterName: "cluster1"},
|
||||
{ClusterName: "cluster2"},
|
||||
},
|
||||
expectedFilterResult: []FilterResult{
|
||||
{
|
||||
Name: "Predicate",
|
||||
FilteredClusters: []string{"cluster1"},
|
||||
FilteredClusters: []string{"cluster1", "cluster2", "cluster3"},
|
||||
},
|
||||
},
|
||||
expectedScoreResult: []PrioritizerResult{
|
||||
{
|
||||
Name: "Balance",
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 100},
|
||||
Weight: 3,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
Name: "Steady",
|
||||
Weight: 3,
|
||||
Scores: PrioritizerScore{"cluster1": 0},
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0, "cluster2": 0, "cluster3": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 0, "cluster3": -100},
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).Build(),
|
||||
},
|
||||
expectedUnScheduled: 0,
|
||||
},
|
||||
{
|
||||
name: "placement with exact Prioritizer Policy",
|
||||
placement: testinghelpers.NewPlacement(placementNamespace, placementName).WithPrioritizerPolicy("Exact").WithPrioritizerConfig("Steady", 3).Build(),
|
||||
placement: testinghelpers.NewPlacement(placementNamespace, placementName).WithNOC(2).WithPrioritizerPolicy("Exact").WithPrioritizerConfig("Balance", 3).WithPrioritizerConfig("ResourceRatioCPU", 1).Build(),
|
||||
initObjs: []runtime.Object{
|
||||
testinghelpers.NewClusterSet(clusterSetName),
|
||||
testinghelpers.NewClusterSetBinding(placementNamespace, clusterSetName),
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "10", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithLabel(clusterSetLabel, clusterSetName).WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
},
|
||||
decisions: []runtime.Object{},
|
||||
expectedDecisions: []clusterapiv1alpha1.ClusterDecision{
|
||||
{ClusterName: "cluster1"},
|
||||
{ClusterName: "cluster2"},
|
||||
},
|
||||
expectedFilterResult: []FilterResult{
|
||||
{
|
||||
Name: "Predicate",
|
||||
FilteredClusters: []string{"cluster1"},
|
||||
FilteredClusters: []string{"cluster1", "cluster2", "cluster3"},
|
||||
},
|
||||
},
|
||||
expectedScoreResult: []PrioritizerResult{
|
||||
{
|
||||
Name: "Balance",
|
||||
Weight: 3,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
Name: "Steady",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "Steady",
|
||||
Weight: 3,
|
||||
Scores: PrioritizerScore{"cluster1": 0},
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 0, "cluster3": -100},
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithLabel(clusterSetLabel, clusterSetName).Build(),
|
||||
},
|
||||
expectedUnScheduled: 0,
|
||||
},
|
||||
@@ -285,6 +411,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 100, "cluster2": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
expectedUnScheduled: 2,
|
||||
},
|
||||
@@ -326,6 +472,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0, "cluster2": 0, "cluster3": 0},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
expectedUnScheduled: 0,
|
||||
},
|
||||
@@ -377,6 +543,26 @@ func TestSchedule(t *testing.T) {
|
||||
Weight: 1,
|
||||
Scores: PrioritizerScore{"cluster1": 0, "cluster2": 0, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 0,
|
||||
Scores: nil,
|
||||
},
|
||||
},
|
||||
expectedUnScheduled: 0,
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
@@ -95,6 +96,14 @@ func (b *placementBuilder) WithSatisfiedCondition(numbOfScheduledDecisions, numb
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *placementBuilder) WithPrioritizerConfigs(name string, weight int32) *placementBuilder {
|
||||
if b.placement.Spec.PrioritizerPolicy.Configurations == nil {
|
||||
b.placement.Spec.PrioritizerPolicy.Configurations = []clusterapiv1alpha1.PrioritizerConfig{}
|
||||
}
|
||||
b.placement.Spec.PrioritizerPolicy.Configurations = append(b.placement.Spec.PrioritizerPolicy.Configurations, clusterapiv1alpha1.PrioritizerConfig{Name: name, Weight: weight})
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *placementBuilder) Build() *clusterapiv1alpha1.Placement {
|
||||
return b.placement
|
||||
}
|
||||
@@ -209,6 +218,19 @@ func (b *managedClusterBuilder) WithClaim(name, value string) *managedClusterBui
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *managedClusterBuilder) WithResource(resourceName clusterapiv1.ResourceName, allocatable, capacity string) *managedClusterBuilder {
|
||||
if b.cluster.Status.Allocatable == nil {
|
||||
b.cluster.Status.Allocatable = make(map[clusterapiv1.ResourceName]resource.Quantity)
|
||||
}
|
||||
if b.cluster.Status.Capacity == nil {
|
||||
b.cluster.Status.Capacity = make(map[clusterapiv1.ResourceName]resource.Quantity)
|
||||
}
|
||||
|
||||
b.cluster.Status.Allocatable[resourceName], _ = resource.ParseQuantity(allocatable)
|
||||
b.cluster.Status.Capacity[resourceName], _ = resource.ParseQuantity(capacity)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *managedClusterBuilder) Build() *clusterapiv1.ManagedCluster {
|
||||
return b.cluster
|
||||
}
|
||||
|
||||
181
pkg/plugins/resource/resource.go
Normal file
181
pkg/plugins/resource/resource.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
clusterapiv1 "open-cluster-management.io/api/cluster/v1"
|
||||
clusterapiv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
"open-cluster-management.io/placement/pkg/plugins"
|
||||
)
|
||||
|
||||
const (
|
||||
placementLabel = clusterapiv1alpha1.PlacementLabel
|
||||
description = `
|
||||
ResourceRatio[ResourceType] and ResourceAllocatable[ResourceType] prioritizer makes the scheduling
|
||||
decisions based on the resource allocatable to capacity ratio or allocatable of managed clusters.
|
||||
The [ResourceType] could be CPU or Memory.
|
||||
The clusters that has the most allocatable to capacity ratio or allocatable are given the highest score,
|
||||
while the least is given the lowest score.
|
||||
`
|
||||
)
|
||||
|
||||
var _ plugins.Prioritizer = &ResourcePrioritizer{}
|
||||
|
||||
var resourceMap = map[string]clusterapiv1.ResourceName{
|
||||
"CPU": clusterapiv1.ResourceCPU,
|
||||
"Memory": clusterapiv1.ResourceMemory,
|
||||
}
|
||||
|
||||
type ResourcePrioritizer struct {
|
||||
handle plugins.Handle
|
||||
prioritizerName string
|
||||
algorithm string
|
||||
resource clusterapiv1.ResourceName
|
||||
}
|
||||
|
||||
type ResourcePrioritizerBuilder struct {
|
||||
resourcePrioritizer *ResourcePrioritizer
|
||||
}
|
||||
|
||||
func NewResourcePrioritizerBuilder(handle plugins.Handle) *ResourcePrioritizerBuilder {
|
||||
return &ResourcePrioritizerBuilder{
|
||||
resourcePrioritizer: &ResourcePrioritizer{
|
||||
handle: handle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ResourcePrioritizerBuilder) WithPrioritizerName(name string) *ResourcePrioritizerBuilder {
|
||||
r.resourcePrioritizer.prioritizerName = name
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ResourcePrioritizerBuilder) Build() *ResourcePrioritizer {
|
||||
algorithm, resource := parsePrioritizerName(r.resourcePrioritizer.prioritizerName)
|
||||
r.resourcePrioritizer.algorithm = algorithm
|
||||
r.resourcePrioritizer.resource = resource
|
||||
return r.resourcePrioritizer
|
||||
}
|
||||
|
||||
// parese prioritizerName to algorithm and resource.
|
||||
// For example, prioritizerName ResourceAllocatableCPU will return Allocatable, CPU.
|
||||
func parsePrioritizerName(prioritizerName string) (algorithm string, resource clusterapiv1.ResourceName) {
|
||||
s := regexp.MustCompile("[A-Z]+[a-z]*").FindAllString(prioritizerName, -1)
|
||||
if len(s) == 3 {
|
||||
return s[1], resourceMap[s[2]]
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (r *ResourcePrioritizer) Name() string {
|
||||
return r.prioritizerName
|
||||
}
|
||||
|
||||
func (r *ResourcePrioritizer) Description() string {
|
||||
return description
|
||||
}
|
||||
|
||||
func (r *ResourcePrioritizer) Score(ctx context.Context, placement *clusterapiv1alpha1.Placement, clusters []*clusterapiv1.ManagedCluster) (map[string]int64, error) {
|
||||
switch r.algorithm {
|
||||
case "Ratio":
|
||||
return mostResourceRatioScores(r.resource, clusters)
|
||||
case "Allocatable":
|
||||
return mostResourceAllocatableScores(r.resource, clusters)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Calculate clusters scores based on the resource allocatable to capacity ratio.
|
||||
// The clusters that has the most allocatable to capacity ratio are given the highest score, while the least is given the lowest score.
|
||||
// The score range is from -100 to 100.
|
||||
func mostResourceRatioScores(resourceName clusterapiv1.ResourceName, clusters []*clusterapiv1.ManagedCluster) (map[string]int64, error) {
|
||||
scores := map[string]int64{}
|
||||
|
||||
for _, cluster := range clusters {
|
||||
// get cluster resourceName's allocatable and capacity
|
||||
allocatable, capacity, err := getClusterResource(cluster, resourceName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// score = (resource_x_allocatable / resource_x_capacity - 0.5) * 2 * 100
|
||||
if capacity != 0 {
|
||||
ratio := float64(allocatable) / float64(capacity)
|
||||
scores[cluster.Name] = int64((ratio - 0.5) * 2.0 * 100.0)
|
||||
}
|
||||
}
|
||||
|
||||
return scores, nil
|
||||
}
|
||||
|
||||
// Calculate clusters scores based on the resource allocatable.
|
||||
// The clusters that has the most allocatable are given the highest score, while the least is given the lowest score.
|
||||
// The score range is from -100 to 100.
|
||||
func mostResourceAllocatableScores(resourceName clusterapiv1.ResourceName, clusters []*clusterapiv1.ManagedCluster) (map[string]int64, error) {
|
||||
scores := map[string]int64{}
|
||||
|
||||
// get resourceName's min and max allocatable among all the clusters
|
||||
minAllocatable, maxAllocatable, err := getClustersMinMaxAllocatableResource(clusters, resourceName)
|
||||
if err != nil {
|
||||
return scores, nil
|
||||
}
|
||||
|
||||
for _, cluster := range clusters {
|
||||
// get one cluster resourceName's allocatable
|
||||
allocatable, _, err := getClusterResource(cluster, resourceName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// score = ((resource_x_allocatable - min(resource_x_allocatable)) / (max(resource_x_allocatable) - min(resource_x_allocatable)) - 0.5) * 2 * 100
|
||||
if (maxAllocatable - minAllocatable) != 0 {
|
||||
ratio := float64(allocatable-minAllocatable) / float64(maxAllocatable-minAllocatable)
|
||||
scores[cluster.Name] = int64((ratio - 0.5) * 2.0 * 100.0)
|
||||
} else {
|
||||
scores[cluster.Name] = 100.0
|
||||
}
|
||||
}
|
||||
|
||||
return scores, nil
|
||||
}
|
||||
|
||||
// Go through one cluster resources and return the allocatable and capacity of the resourceName.
|
||||
func getClusterResource(cluster *clusterapiv1.ManagedCluster, resourceName clusterapiv1.ResourceName) (allocatable, capacity float64, err error) {
|
||||
if v, exist := cluster.Status.Allocatable[resourceName]; exist {
|
||||
allocatable = v.AsApproximateFloat64()
|
||||
} else {
|
||||
return allocatable, capacity, fmt.Errorf("no allocatable %s found in cluster %s", resourceName, cluster.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
if v, exist := cluster.Status.Capacity[resourceName]; exist {
|
||||
capacity = v.AsApproximateFloat64()
|
||||
} else {
|
||||
return allocatable, capacity, fmt.Errorf("no capacity %s found in cluster %s", resourceName, cluster.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
return allocatable, capacity, nil
|
||||
}
|
||||
|
||||
// Go through all the cluster resources and return the min and max allocatable value of the resourceName.
|
||||
func getClustersMinMaxAllocatableResource(clusters []*clusterapiv1.ManagedCluster, resourceName clusterapiv1.ResourceName) (minAllocatable, maxAllocatable float64, err error) {
|
||||
allocatable := sort.Float64Slice{}
|
||||
|
||||
// get allocatable resources
|
||||
for _, cluster := range clusters {
|
||||
if alloc, _, err := getClusterResource(cluster, resourceName); err == nil {
|
||||
allocatable = append(allocatable, alloc)
|
||||
}
|
||||
}
|
||||
|
||||
// return err if no allocatable resource
|
||||
if len(allocatable) == 0 {
|
||||
return 0, 0, fmt.Errorf("no allocatable %s found in clusters", resourceName)
|
||||
}
|
||||
|
||||
// sort to get min and max
|
||||
sort.Float64s(allocatable)
|
||||
return allocatable[0], allocatable[len(allocatable)-1], nil
|
||||
}
|
||||
237
pkg/plugins/resource/resource_test.go
Normal file
237
pkg/plugins/resource/resource_test.go
Normal file
@@ -0,0 +1,237 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clusterapiv1 "open-cluster-management.io/api/cluster/v1"
|
||||
clusterapiv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
testinghelpers "open-cluster-management.io/placement/pkg/helpers/testing"
|
||||
)
|
||||
|
||||
func TestScoreClusterWithResource(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
resource clusterapiv1.ResourceName
|
||||
algorithm string
|
||||
placement *clusterapiv1alpha1.Placement
|
||||
clusters []*clusterapiv1.ManagedCluster
|
||||
existingDecisions []runtime.Object
|
||||
expectedScores map[string]int64
|
||||
}{
|
||||
{
|
||||
name: "scores of ResourceRatioMemory",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "20", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "100", "100").Build(),
|
||||
},
|
||||
existingDecisions: []runtime.Object{},
|
||||
expectedScores: map[string]int64{"cluster1": -60, "cluster2": 0, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioMemory with same resource value",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 0, "cluster2": 0, "cluster3": 0},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioMemory with zero resource value",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": -100, "cluster2": -100, "cluster3": -100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioMemory with no cluster resource",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioCPU",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "10", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "2", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 0, "cluster3": -60},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioCPU with same resource value",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 0, "cluster2": 0, "cluster3": 0},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioCPU with zero resource value",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": -100, "cluster2": -100, "cluster3": -100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceRatioCPU with no cluster resource",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Ratio",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableMemory",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "20", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "60", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "100", "100").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": -100, "cluster2": 0, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableMemory with same resource value",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "50", "100").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableMemory with zero resource value",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceMemory, "0", "100").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableMemory with no cluster resource",
|
||||
resource: clusterapiv1.ResourceMemory,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableCPU",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "10", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "6", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "2", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 0, "cluster3": -100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableCPU with same resource value",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "5", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableCPU with zero resource value",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").WithResource(clusterapiv1.ResourceCPU, "0", "10").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{"cluster1": 100, "cluster2": 100, "cluster3": 100},
|
||||
},
|
||||
{
|
||||
name: "scores of ResourceAllocatableCPU with no cluster resource",
|
||||
resource: clusterapiv1.ResourceCPU,
|
||||
algorithm: "Allocatable",
|
||||
placement: testinghelpers.NewPlacement("test", "test").Build(),
|
||||
clusters: []*clusterapiv1.ManagedCluster{
|
||||
testinghelpers.NewManagedCluster("cluster1").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster2").Build(),
|
||||
testinghelpers.NewManagedCluster("cluster3").Build(),
|
||||
},
|
||||
expectedScores: map[string]int64{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
resource := &ResourcePrioritizer{
|
||||
handle: testinghelpers.NewFakePluginHandle(t, nil, c.existingDecisions...),
|
||||
resource: c.resource,
|
||||
algorithm: c.algorithm,
|
||||
}
|
||||
|
||||
scores, err := resource.Score(context.TODO(), c.placement, c.clusters)
|
||||
if err != nil {
|
||||
t.Errorf("Expect no error, but got %v", err)
|
||||
}
|
||||
|
||||
if !apiequality.Semantic.DeepEqual(scores, c.expectedScores) {
|
||||
t.Errorf("Expect score %v, but got %v", c.expectedScores, scores)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,11 @@ import (
|
||||
"github.com/openshift/library-go/pkg/controller/controllercmd"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
clusterapiv1 "open-cluster-management.io/api/cluster/v1"
|
||||
clusterapiv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
controllers "open-cluster-management.io/placement/pkg/controllers"
|
||||
@@ -28,6 +30,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
var cancel context.CancelFunc
|
||||
var namespace string
|
||||
var placementName string
|
||||
var clusterName string
|
||||
var clusterSet1Name, clusterSet2Name string
|
||||
var suffix string
|
||||
var err error
|
||||
@@ -36,6 +39,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
suffix = rand.String(5)
|
||||
namespace = fmt.Sprintf("ns-%s", suffix)
|
||||
placementName = fmt.Sprintf("placement-%s", suffix)
|
||||
clusterName = fmt.Sprintf("cluster-%s", suffix)
|
||||
clusterSet1Name = fmt.Sprintf("clusterset-%s", suffix)
|
||||
clusterSet2Name = fmt.Sprintf("clusterset-%s", rand.String(5))
|
||||
|
||||
@@ -86,6 +90,31 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
}
|
||||
|
||||
assertCreatingPlacementDecision := func(name string, clusterNames []string) {
|
||||
ginkgo.By(fmt.Sprintf("Create placementdecision %s", name))
|
||||
placementDecision := &clusterapiv1alpha1.PlacementDecision{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
placementLabel: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
placementDecision, err = clusterClient.ClusterV1alpha1().PlacementDecisions(namespace).Create(context.Background(), placementDecision, metav1.CreateOptions{})
|
||||
|
||||
clusterDecisions := []clusterapiv1alpha1.ClusterDecision{}
|
||||
for _, clusterName := range clusterNames {
|
||||
clusterDecisions = append(clusterDecisions, clusterapiv1alpha1.ClusterDecision{
|
||||
ClusterName: clusterName,
|
||||
})
|
||||
}
|
||||
|
||||
placementDecision.Status.Decisions = clusterDecisions
|
||||
placementDecision, err = clusterClient.ClusterV1alpha1().PlacementDecisions(namespace).UpdateStatus(context.Background(), placementDecision, metav1.UpdateOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
assertPlacementDeleted := func(placementName string) {
|
||||
ginkgo.By("Check if placement is gone")
|
||||
gomega.Eventually(func() bool {
|
||||
@@ -121,6 +150,26 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
}
|
||||
|
||||
assertClusterNamesOfDecisions := func(placementName string, desiredClusters []string) {
|
||||
ginkgo.By(fmt.Sprintf("Check the cluster names of placementdecisions %s", placementName))
|
||||
gomega.Eventually(func() bool {
|
||||
pdl, err := clusterClient.ClusterV1alpha1().PlacementDecisions(namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: placementLabel + "=" + placementName,
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
actualClusters := []string{}
|
||||
for _, pd := range pdl.Items {
|
||||
for _, d := range pd.Status.Decisions {
|
||||
actualClusters = append(actualClusters, d.ClusterName)
|
||||
}
|
||||
}
|
||||
ginkgo.By(fmt.Sprintf("Expect %v, but got %v", desiredClusters, actualClusters))
|
||||
return apiequality.Semantic.DeepEqual(desiredClusters, actualClusters)
|
||||
}, eventuallyTimeout*2, eventuallyInterval).Should(gomega.BeTrue())
|
||||
}
|
||||
|
||||
assertPlacementStatus := func(placementName string, numOfSelectedClusters int, satisfied bool) {
|
||||
ginkgo.By("Check the status of placement")
|
||||
gomega.Eventually(func() bool {
|
||||
@@ -190,7 +239,52 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
}
|
||||
}
|
||||
|
||||
assertCreatingPlacement := func(name string, noc *int32, nod int) {
|
||||
assertCreatingClustersWithNames := func(clusterSetName string, managedClusterNames []string) {
|
||||
ginkgo.By(fmt.Sprintf("Create %d clusters", len(managedClusterNames)))
|
||||
for _, name := range managedClusterNames {
|
||||
cluster := &clusterapiv1.ManagedCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "cluster-",
|
||||
Labels: map[string]string{
|
||||
clusterSetLabel: clusterSetName,
|
||||
},
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
ginkgo.By(fmt.Sprintf("Create cluster %s", name))
|
||||
_, err = clusterClient.ClusterV1().ManagedClusters().Create(context.Background(), cluster, metav1.CreateOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
assertUpdatingClusterWithClusterResources := func(managedClusterName string, res []string) {
|
||||
ginkgo.By(fmt.Sprintf("Updating ManagedClusters %s cluster resources", managedClusterName))
|
||||
|
||||
mc, err := clusterClient.ClusterV1().ManagedClusters().Get(context.Background(), managedClusterName, metav1.GetOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
allocatable := map[clusterapiv1.ResourceName]resource.Quantity{}
|
||||
capacity := map[clusterapiv1.ResourceName]resource.Quantity{}
|
||||
|
||||
allocatable[clusterapiv1.ResourceCPU], err = resource.ParseQuantity(res[0])
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
allocatable[clusterapiv1.ResourceMemory], err = resource.ParseQuantity(res[2])
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
capacity[clusterapiv1.ResourceCPU], err = resource.ParseQuantity(res[1])
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
capacity[clusterapiv1.ResourceMemory], err = resource.ParseQuantity(res[3])
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
mc.Status = clusterapiv1.ManagedClusterStatus{
|
||||
Allocatable: allocatable,
|
||||
Capacity: capacity,
|
||||
Conditions: []metav1.Condition{},
|
||||
}
|
||||
_, err = clusterClient.ClusterV1().ManagedClusters().UpdateStatus(context.Background(), mc, metav1.UpdateOptions{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
assertCreatingPlacement := func(name string, noc *int32, nod int, prioritizerPolicy clusterapiv1alpha1.PrioritizerPolicy) {
|
||||
ginkgo.By("Create placement")
|
||||
placement := &clusterapiv1alpha1.Placement{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -198,7 +292,8 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
Name: name,
|
||||
},
|
||||
Spec: clusterapiv1alpha1.PlacementSpec{
|
||||
NumberOfClusters: noc,
|
||||
NumberOfClusters: noc,
|
||||
PrioritizerPolicy: prioritizerPolicy,
|
||||
},
|
||||
}
|
||||
placement, err = clusterClient.ClusterV1alpha1().Placements(namespace).Create(context.Background(), placement, metav1.CreateOptions{})
|
||||
@@ -222,7 +317,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should re-create placementdecisions successfully once placementdecisions are deleted", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
ginkgo.By("Delete placementdecisions")
|
||||
placementDecisions, err := clusterClient.ClusterV1alpha1().PlacementDecisions(namespace).List(context.Background(), metav1.ListOptions{
|
||||
@@ -246,7 +341,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
assertBindingClusterSet(clusterSet2Name)
|
||||
assertCreatingClusters(clusterSet1Name, 2)
|
||||
assertCreatingClusters(clusterSet2Name, 3)
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
// update ClusterSets
|
||||
placement, err := clusterClient.ClusterV1alpha1().Placements(namespace).Get(context.Background(), placementName, metav1.GetOptions{})
|
||||
@@ -262,7 +357,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 2)
|
||||
assertCreatingClusters(clusterSet1Name, 3, "cloud", "Amazon")
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
// add a predicates
|
||||
placement, err := clusterClient.ClusterV1alpha1().Placements(namespace).Get(context.Background(), placementName, metav1.GetOptions{})
|
||||
@@ -287,7 +382,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should schedule successfully once spec.NumberOfClusters is reduced", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
ginkgo.By("Reduce NOC of the placement")
|
||||
placement, err := clusterClient.ClusterV1alpha1().Placements(namespace).Get(context.Background(), placementName, metav1.GetOptions{})
|
||||
@@ -305,7 +400,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should schedule successfully once spec.NumberOfClusters is increased", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 10)
|
||||
assertCreatingPlacement(placementName, noc(5), 5)
|
||||
assertCreatingPlacement(placementName, noc(5), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
ginkgo.By("Increase NOC of the placement")
|
||||
placement, err := clusterClient.ClusterV1alpha1().Placements(namespace).Get(context.Background(), placementName, metav1.GetOptions{})
|
||||
@@ -323,7 +418,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should be satisfied once new clusters are added", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
// add more clusters
|
||||
assertCreatingClusters(clusterSet1Name, 5)
|
||||
@@ -336,7 +431,7 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should schedule successfully once new clusterset is bound", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5)
|
||||
assertCreatingPlacement(placementName, noc(10), 5, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
ginkgo.By("Bind one more clusterset to the placement namespace")
|
||||
assertBindingClusterSet(clusterSet2Name)
|
||||
@@ -350,12 +445,217 @@ var _ = ginkgo.Describe("Placement", func() {
|
||||
ginkgo.It("Should create multiple placementdecisions once scheduled", func() {
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClusters(clusterSet1Name, 101)
|
||||
assertCreatingPlacement(placementName, nil, 101)
|
||||
assertCreatingPlacement(placementName, nil, 101, clusterapiv1alpha1.PrioritizerPolicy{})
|
||||
|
||||
nod := 101
|
||||
assertNumberOfDecisions(placementName, nod)
|
||||
assertPlacementStatus(placementName, nod, true)
|
||||
})
|
||||
|
||||
ginkgo.It("Should schedule successfully with default SchedulePolicy", func() {
|
||||
// cluster settings
|
||||
clusterNames := []string{
|
||||
clusterName + "-1",
|
||||
clusterName + "-2",
|
||||
clusterName + "-3",
|
||||
}
|
||||
clusterResources := make([][]string, len(clusterNames))
|
||||
clusterResources[0] = []string{"10", "10", "50", "100"}
|
||||
clusterResources[1] = []string{"7", "10", "90", "100"}
|
||||
clusterResources[2] = []string{"9", "10", "80", "100"}
|
||||
|
||||
// placement settings
|
||||
prioritizerPolicy := clusterapiv1alpha1.PrioritizerPolicy{}
|
||||
|
||||
//Creating the clusters with resources
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames)
|
||||
for i, name := range clusterNames {
|
||||
assertUpdatingClusterWithClusterResources(name, clusterResources[i])
|
||||
}
|
||||
|
||||
//Checking the result of the placement
|
||||
assertCreatingPlacement(placementName, noc(2), 2, prioritizerPolicy)
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[0], clusterNames[1]})
|
||||
})
|
||||
|
||||
ginkgo.It("Should schedule successfully based on SchedulePolicy ResourceRatioCPU and ResourceRatioCPU", func() {
|
||||
// cluster settings
|
||||
clusterNames := []string{
|
||||
clusterName + "-1",
|
||||
clusterName + "-2",
|
||||
clusterName + "-3",
|
||||
}
|
||||
clusterResources := make([][]string, len(clusterNames))
|
||||
clusterResources[0] = []string{"10", "10", "50", "100"}
|
||||
clusterResources[1] = []string{"7", "10", "90", "100"}
|
||||
clusterResources[2] = []string{"9", "10", "80", "100"}
|
||||
|
||||
// placement settings
|
||||
prioritizerPolicy := clusterapiv1alpha1.PrioritizerPolicy{
|
||||
Mode: clusterapiv1alpha1.PrioritizerPolicyModeExact,
|
||||
Configurations: []clusterapiv1alpha1.PrioritizerConfig{
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//Creating the clusters with resources
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames)
|
||||
for i, name := range clusterNames {
|
||||
assertUpdatingClusterWithClusterResources(name, clusterResources[i])
|
||||
}
|
||||
|
||||
//Checking the result of the placement
|
||||
assertCreatingPlacement(placementName, noc(2), 2, prioritizerPolicy)
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[1], clusterNames[2]})
|
||||
|
||||
})
|
||||
|
||||
ginkgo.It("Should schedule successfully based on default SchedulePolicy ResourceAllocatableCPU & ResourceAllocatableMemory", func() {
|
||||
// cluster settings
|
||||
clusterNames := []string{
|
||||
clusterName + "-1",
|
||||
clusterName + "-2",
|
||||
clusterName + "-3",
|
||||
}
|
||||
clusterResources := make([][]string, len(clusterNames))
|
||||
clusterResources[0] = []string{"10", "10", "50", "100"}
|
||||
clusterResources[1] = []string{"7", "10", "90", "100"}
|
||||
clusterResources[2] = []string{"9", "10", "80", "100"}
|
||||
|
||||
// placement settings
|
||||
prioritizerPolicy := clusterapiv1alpha1.PrioritizerPolicy{
|
||||
Mode: clusterapiv1alpha1.PrioritizerPolicyModeExact,
|
||||
Configurations: []clusterapiv1alpha1.PrioritizerConfig{
|
||||
{
|
||||
Name: "ResourceAllocatableCPU",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Name: "ResourceAllocatableMemory",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//Creating the clusters with resources
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames)
|
||||
for i, name := range clusterNames {
|
||||
assertUpdatingClusterWithClusterResources(name, clusterResources[i])
|
||||
}
|
||||
|
||||
//Checking the result of the placement
|
||||
assertCreatingPlacement(placementName, noc(2), 2, prioritizerPolicy)
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[0], clusterNames[2]})
|
||||
|
||||
})
|
||||
|
||||
ginkgo.It("Should re-schedule successfully successfully once a new cluster added", func() {
|
||||
// cluster settings
|
||||
clusterNames := []string{
|
||||
clusterName + "-1",
|
||||
clusterName + "-2",
|
||||
clusterName + "-3",
|
||||
}
|
||||
clusterResources := make([][]string, len(clusterNames))
|
||||
clusterResources[0] = []string{"10", "10", "50", "100"}
|
||||
clusterResources[1] = []string{"7", "10", "90", "100"}
|
||||
clusterResources[2] = []string{"9", "10", "80", "100"}
|
||||
|
||||
// placement settings
|
||||
prioritizerPolicy := clusterapiv1alpha1.PrioritizerPolicy{
|
||||
Mode: clusterapiv1alpha1.PrioritizerPolicyModeExact,
|
||||
Configurations: []clusterapiv1alpha1.PrioritizerConfig{
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//Creating the clusters with resources
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames)
|
||||
for i, name := range clusterNames {
|
||||
assertUpdatingClusterWithClusterResources(name, clusterResources[i])
|
||||
}
|
||||
|
||||
//Checking the result of the placement
|
||||
assertCreatingPlacement(placementName, noc(2), 2, prioritizerPolicy)
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[1], clusterNames[2]})
|
||||
|
||||
ginkgo.By("Adding a new cluster with resources")
|
||||
clusterNames = append(clusterNames, clusterName+"-4")
|
||||
newClusterResources := []string{"10", "10", "100", "100"}
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames[3:4])
|
||||
assertUpdatingClusterWithClusterResources(clusterNames[3], newClusterResources)
|
||||
|
||||
//Checking the result of the placement
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[2], clusterNames[3]})
|
||||
|
||||
})
|
||||
|
||||
ginkgo.It("Should keep steady successfully even placementdecisions' balance and cluster resource changes", func() {
|
||||
// cluster settings
|
||||
clusterNames := []string{
|
||||
clusterName + "-1",
|
||||
clusterName + "-2",
|
||||
clusterName + "-3",
|
||||
}
|
||||
clusterResources := make([][]string, len(clusterNames))
|
||||
clusterResources[0] = []string{"10", "10", "50", "100"}
|
||||
clusterResources[1] = []string{"7", "10", "90", "100"}
|
||||
clusterResources[2] = []string{"9", "10", "80", "100"}
|
||||
|
||||
// placement settings
|
||||
prioritizerPolicy := clusterapiv1alpha1.PrioritizerPolicy{
|
||||
Mode: clusterapiv1alpha1.PrioritizerPolicyModeAdditive,
|
||||
Configurations: []clusterapiv1alpha1.PrioritizerConfig{
|
||||
{
|
||||
Name: "Steady",
|
||||
Weight: 3,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioCPU",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Name: "ResourceRatioMemory",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
//Creating the clusters with resources
|
||||
assertBindingClusterSet(clusterSet1Name)
|
||||
assertCreatingClustersWithNames(clusterSet1Name, clusterNames)
|
||||
for i, name := range clusterNames {
|
||||
assertUpdatingClusterWithClusterResources(name, clusterResources[i])
|
||||
}
|
||||
|
||||
//Checking the result of the placement
|
||||
assertCreatingPlacement(placementName, noc(2), 2, prioritizerPolicy)
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[1], clusterNames[2]})
|
||||
|
||||
ginkgo.By("Adding fake placement decisions and update cluster resources")
|
||||
assertCreatingPlacementDecision(placementName+"-1", []string{clusterNames[1]})
|
||||
assertUpdatingClusterWithClusterResources(clusterNames[0], []string{"10", "10", "10", "100"})
|
||||
|
||||
//Checking the result of the placement
|
||||
assertClusterNamesOfDecisions(placementName, []string{clusterNames[1], clusterNames[2]})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user