From ae152a64fa45dae43dd66270a1451aa654951b22 Mon Sep 17 00:00:00 2001 From: Lukasz Szaszkiewicz Date: Mon, 13 Feb 2023 14:29:40 +0100 Subject: [PATCH] workspace_reconcile_scheduling: allow for skipping a shard with a special annotation during scheduling introduces an annotation that can be used to indicate that a shard is unschedulable. The annotation is meant to be used by e2e tests that otherwise started a private instance of kcp server. --- .../workspace_reconcile_scheduling.go | 9 +++++++ .../workspace_reconcile_scheduling_test.go | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling.go b/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling.go index 1baa165dc04..2342a9cfa16 100644 --- a/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling.go +++ b/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling.go @@ -53,8 +53,13 @@ const ( // WorkspaceShardHashAnnotationKey keeps track on which shard LogicalCluster must be scheduled. The value // is a base36(sha224) hash of the Shard name. WorkspaceShardHashAnnotationKey = "internal.tenancy.kcp.io/shard" + // workspaceClusterAnnotationKey keeps track of the logical cluster on the shard. workspaceClusterAnnotationKey = "internal.tenancy.kcp.io/cluster" + + // unschedulableAnnotationKey is the annotation key used to indicate that a shard is unschedulable. + // The annotation is meant to be used by e2e tests that otherwise started a private instance of kcp server. + unschedulableAnnotationKey = "experimental.core.kcp.io/unschedulable" ) type schedulingReconciler struct { @@ -194,6 +199,10 @@ func (r *schedulingReconciler) chooseShardAndMarkCondition(logger klog.Logger, w reason, message string }{} for _, shard := range shards { + if _, ok := shard.Annotations[unschedulableAnnotationKey]; ok { + logger.V(4).Info("Skipping a shard because it is annotated as unschedulable", "shard", shard.Name, "annotation", unschedulableAnnotationKey) + continue + } if valid, reason, message := isValidShard(shard); valid { validShards = append(validShards, shard) } else { diff --git a/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling_test.go b/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling_test.go index 880d6fe2e73..45d56dacec2 100644 --- a/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling_test.go +++ b/pkg/reconciler/tenancy/workspace/workspace_reconcile_scheduling_test.go @@ -253,6 +253,32 @@ func TestReconcileScheduling(t *testing.T) { }, expectedStatus: reconcileStatusStopAndRequeue, }, + { + name: "only an unschedulable shard is available, the ws is unscheduled", + initialShards: []*corev1alpha1.Shard{func() *corev1alpha1.Shard { + s := shard("amber") + s.Annotations[unschedulableAnnotationKey] = "true" + return s + }()}, + targetWorkspace: workspace("foo"), + targetLogicalCluster: &corev1alpha1.LogicalCluster{}, + validateWorkspace: func(t *testing.T, initialWS, wsAfterReconciliation *tenancyv1alpha1.Workspace) { + t.Helper() + + clearLastTransitionTimeOnWsConditions(wsAfterReconciliation) + initialWS.Status.Conditions = append(initialWS.Status.Conditions, conditionsapi.Condition{ + Type: tenancyv1alpha1.WorkspaceScheduled, + Severity: conditionsapi.ConditionSeverityError, + Status: corev1.ConditionFalse, + Reason: tenancyv1alpha1.WorkspaceReasonUnschedulable, + Message: "No available shards to schedule the workspace", + }) + if !equality.Semantic.DeepEqual(wsAfterReconciliation, initialWS) { + t.Fatal(fmt.Errorf("unexpected Workspace:\n%s", cmp.Diff(wsAfterReconciliation, initialWS))) + } + }, + expectedStatus: reconcileStatusContinue, + }, } for _, scenario := range scenarios { t.Run(scenario.name, func(t *testing.T) {