Skip to content

Commit 9061ee5

Browse files
Add feature flag pod-is-always-schedulable
The feature flag allows to declare that Pods in the system will eventually all get scheduled and Revisions should therefore not be marked unschedulable Signed-off-by: Sascha Schwarze <schwarzs@de.ibm.com>
1 parent 9786b60 commit 9061ee5

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
lines changed

config/core/configmaps/deployment.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ metadata:
2222
app.kubernetes.io/component: controller
2323
app.kubernetes.io/version: devel
2424
annotations:
25-
knative.dev/example-checksum: "720ddb97"
25+
knative.dev/example-checksum: "b99000ec"
2626
data:
2727
# This is the Go import path for the binary that is containerized
2828
# and substituted here.
@@ -123,3 +123,12 @@ data:
123123
# selector:
124124
# use-gvisor: "please"
125125
runtime-class-name: ""
126+
127+
# pod-is-always-schedulable can be used to define that Pods in the system will always be
128+
# scheduled, and a Revision should not be marked unschedulable.
129+
# Setting this to `true` makes sense if you have cluster-autoscaling set up for your cluster
130+
# where unschedulable Pods trigger the addition of a new Node and are therefore a short and
131+
# transient state.
132+
#
133+
# See https://github.com/knative/serving/issues/14862
134+
pod-is-always-schedulable: "false"

pkg/deployment/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ const (
7878
defaultAffinityTypeValue = PreferSpreadRevisionOverNodes
7979

8080
RuntimeClassNameKey = "runtime-class-name"
81+
82+
// pod-is-always-schedulable
83+
podIsAlwaysSchedulableKey = "pod-is-always-schedulable"
8184
)
8285

8386
var (
@@ -200,6 +203,8 @@ func NewConfigFromMap(configMap map[string]string) (*Config, error) {
200203
cm.AsString(queueSidecarRooCAKey, &nc.QueueSidecarRootCA),
201204

202205
cm.AsString(RuntimeClassNameKey, &runtimeClassNames),
206+
207+
cm.AsBool(podIsAlwaysSchedulableKey, &nc.PodIsAlwaysSchedulable),
203208
); err != nil {
204209
return nil, err
205210
}
@@ -309,4 +314,7 @@ type Config struct {
309314

310315
// RuntimeClassNames specifies which runtime the Pod will use
311316
RuntimeClassNames map[string]RuntimeClassNameLabelSelector
317+
318+
// PodIsAlwaysSchedulable specifies whether pods are considered to be always schedulable
319+
PodIsAlwaysSchedulable bool
312320
}

pkg/deployment/config_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,22 @@ kata:
456456
return string(b)
457457
}(),
458458
},
459+
}, {
460+
name: "controller configuration with always schedulable pods",
461+
wantConfig: &Config{
462+
PodIsAlwaysSchedulable: true,
463+
RegistriesSkippingTagResolving: sets.New("kind.local", "ko.local", "dev.local"),
464+
DigestResolutionTimeout: digestResolutionTimeoutDefault,
465+
QueueSidecarImage: defaultSidecarImage,
466+
QueueSidecarCPURequest: &QueueSidecarCPURequestDefault,
467+
QueueSidecarTokenAudiences: sets.New(""),
468+
ProgressDeadline: ProgressDeadlineDefault,
469+
DefaultAffinityType: defaultAffinityTypeValue,
470+
},
471+
data: map[string]string{
472+
podIsAlwaysSchedulableKey: "true",
473+
QueueSidecarImageKey: defaultSidecarImage,
474+
},
459475
}}
460476

461477
for _, tt := range configTests {

pkg/reconciler/revision/config/store.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ type Config struct {
4141

4242
// FromContext loads the configuration from the context.
4343
func FromContext(ctx context.Context) *Config {
44-
return ctx.Value(cfgKey{}).(*Config)
44+
x, ok := ctx.Value(cfgKey{}).(*Config)
45+
if ok {
46+
return x
47+
}
48+
return nil
4549
}
4650

4751
// ToContext persists the configuration to the context.

pkg/reconciler/revision/reconcile_resources.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ func (c *Reconciler) reconcileDeployment(ctx context.Context, rev *v1.Revision)
102102

103103
// Update the revision status if pod cannot be scheduled (possibly resource constraints)
104104
// If pod cannot be scheduled then we expect the container status to be empty.
105-
for _, cond := range pod.Status.Conditions {
106-
if cond.Type == corev1.PodScheduled && cond.Status == corev1.ConditionFalse {
107-
rev.Status.MarkResourcesAvailableFalse(cond.Reason, cond.Message)
108-
break
105+
if !config.FromContext(ctx).Deployment.PodIsAlwaysSchedulable {
106+
for _, cond := range pod.Status.Conditions {
107+
if cond.Type == corev1.PodScheduled && cond.Status == corev1.ConditionFalse {
108+
rev.Status.MarkResourcesAvailableFalse(cond.Reason, cond.Message)
109+
break
110+
}
109111
}
110112
}
111113

pkg/reconciler/revision/table_test.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,30 @@ func TestReconcile(t *testing.T) {
631631
Object: pa("foo", "pod-schedule-error", WithReachabilityUnreachable),
632632
}},
633633
Key: "foo/pod-schedule-error",
634+
}, {
635+
Name: "surface no pod schedule errors if pod-is-always-schedulable is true",
636+
// Test the propagation of the scheduling errors of Pod into the
637+
// revision is not happening when treat-pod-as-always-schedulable
638+
// is enabled.
639+
Objects: []runtime.Object{
640+
Revision("foo", "pod-no-schedule-error",
641+
WithLogURL,
642+
MarkActivating("Deploying", ""),
643+
WithRoutingState(v1.RoutingStateActive, fc),
644+
withDefaultContainerStatuses(),
645+
MarkDeploying("Deploying"),
646+
WithRevisionObservedGeneration(1),
647+
MarkContainerHealthyUnknown("Deploying"),
648+
),
649+
pa("foo", "pod-no-schedule-error", WithReachabilityReachable), // PA can't be ready, since no traffic.
650+
pod(t, "foo", "pod-no-schedule-error", WithUnschedulableContainer("Insufficient energy", "Unschedulable")),
651+
deploy(t, "foo", "pod-no-schedule-error"),
652+
image("foo", "pod-no-schedule-error"),
653+
},
654+
Ctx: config.ToContext(context.Background(), mutateConfig(reconcilerTestConfig(), func(c *config.Config) {
655+
c.Deployment.PodIsAlwaysSchedulable = true
656+
})),
657+
Key: "foo/pod-no-schedule-error",
634658
}, {
635659
Name: "ready steady state",
636660
// Test the transition that Reconcile makes when Endpoints become ready on the
@@ -893,11 +917,16 @@ func TestReconcile(t *testing.T) {
893917
resolver: &nopResolver{},
894918
}
895919

920+
cfg := config.FromContext(ctx)
921+
if cfg == nil {
922+
cfg = reconcilerTestConfig()
923+
}
924+
896925
return revisionreconciler.NewReconciler(ctx, logging.FromContext(ctx), servingclient.Get(ctx),
897926
listers.GetRevisionLister(), controller.GetEventRecorder(ctx), r,
898927
controller.Options{
899928
ConfigStore: &testConfigStore{
900-
config: reconcilerTestConfig(),
929+
config: cfg,
901930
},
902931
})
903932
}))
@@ -1103,6 +1132,13 @@ func pod(t *testing.T, namespace, name string, po ...PodOption) *corev1.Pod {
11031132
return pod
11041133
}
11051134

1135+
func mutateConfig(cfg *config.Config, funcs ...func(*config.Config)) *config.Config {
1136+
for _, f := range funcs {
1137+
f(cfg)
1138+
}
1139+
return cfg
1140+
}
1141+
11061142
type testConfigStore struct {
11071143
config *config.Config
11081144
}

0 commit comments

Comments
 (0)