diff --git a/pkg/reconciler/revision/resources/deploy.go b/pkg/reconciler/revision/resources/deploy.go index 2df44c1bb5a4..3c3f1f228582 100644 --- a/pkg/reconciler/revision/resources/deploy.go +++ b/pkg/reconciler/revision/resources/deploy.go @@ -349,6 +349,24 @@ func MakeDeployment(rev *v1.Revision, cfg *config.Config) (*appsv1.Deployment, e progressDeadline = int32(pd.Seconds()) } + startupProbeMaxDuration := int32(0) + for _, container := range podSpec.Containers { + if container.StartupProbe != nil { + maxSuccessDuration := container.StartupProbe.PeriodSeconds * + container.StartupProbe.SuccessThreshold * + container.StartupProbe.TimeoutSeconds + + maxFailDuration := container.StartupProbe.PeriodSeconds * + container.StartupProbe.FailureThreshold * + container.StartupProbe.TimeoutSeconds + + maxDuration := max(maxSuccessDuration, maxFailDuration) + if maxDuration > startupProbeMaxDuration { + startupProbeMaxDuration = container.StartupProbe.InitialDelaySeconds + maxDuration + } + } + } + labels := makeLabels(rev) anns := makeAnnotations(rev) @@ -365,7 +383,7 @@ func MakeDeployment(rev *v1.Revision, cfg *config.Config) (*appsv1.Deployment, e Spec: appsv1.DeploymentSpec{ Replicas: ptr.Int32(replicaCount), Selector: makeSelector(rev), - ProgressDeadlineSeconds: ptr.Int32(progressDeadline), + ProgressDeadlineSeconds: ptr.Int32(progressDeadline + startupProbeMaxDuration), Strategy: appsv1.DeploymentStrategy{ Type: appsv1.RollingUpdateDeploymentStrategyType, RollingUpdate: &appsv1.RollingUpdateDeployment{ diff --git a/pkg/reconciler/revision/resources/deploy_test.go b/pkg/reconciler/revision/resources/deploy_test.go index c9fd07b69fa9..60d35ecf8dca 100644 --- a/pkg/reconciler/revision/resources/deploy_test.go +++ b/pkg/reconciler/revision/resources/deploy_test.go @@ -80,7 +80,7 @@ var ( ReadinessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ - Port: intstr.FromInt(int(queueHTTPPort.ContainerPort)), + Port: intstr.FromInt32(queueHTTPPort.ContainerPort), HTTPHeaders: []corev1.HTTPHeader{{ Name: netheader.ProbeKey, Value: queue.Name, @@ -205,7 +205,7 @@ var ( EnableServiceLinks: ptr.Bool(false), } - maxUnavailable = intstr.FromInt(0) + maxUnavailable = intstr.FromInt32(0) defaultDeployment = &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", @@ -376,6 +376,12 @@ func withLivenessProbe(handler corev1.ProbeHandler) containerOption { } } +func withStartupProbe(handler corev1.ProbeHandler) containerOption { + return func(container *corev1.Container) { + container.StartupProbe = &corev1.Probe{ProbeHandler: handler} + } +} + func withPrependedVolumeMounts(volumeMounts ...corev1.VolumeMount) containerOption { return func(c *corev1.Container) { c.VolumeMounts = append(volumeMounts, c.VolumeMounts...) @@ -1013,7 +1019,7 @@ func TestMakePodSpec(t *testing.T) { withLivenessProbe(corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: "/", - Port: intstr.FromInt(v1.DefaultUserPort), + Port: intstr.FromInt32(v1.DefaultUserPort), }, }), ), @@ -1043,8 +1049,69 @@ func TestMakePodSpec(t *testing.T) { }, withLivenessProbe(corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromInt(v1.DefaultUserPort), + Port: intstr.FromInt32(v1.DefaultUserPort), + }, + }), + ), + queueContainer(), + }), + }, { + name: "with HTTP startup probe", + rev: revision("bar", "foo", + withContainers([]corev1.Container{{ + Name: servingContainerName, + Image: "busybox", + ReadinessProbe: withTCPReadinessProbe(v1.DefaultUserPort), + StartupProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", }, + }, + }, + }}), + WithContainerStatuses([]v1.ContainerStatus{{ + ImageDigest: "busybox@sha256:deadbeef", + }}), + ), + want: podSpec( + []corev1.Container{ + servingContainer( + func(container *corev1.Container) { + container.Image = "busybox@sha256:deadbeef" + }, + withStartupProbe(corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + }, + }), + ), + queueContainer(), + }), + }, { + name: "with TCP startup probe", + rev: revision("bar", "foo", + withContainers([]corev1.Container{{ + Name: servingContainerName, + Image: "busybox", + ReadinessProbe: withTCPReadinessProbe(v1.DefaultUserPort), + StartupProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{}, + }}}}, + ), + WithContainerStatuses([]v1.ContainerStatus{{ + ImageDigest: "busybox@sha256:deadbeef", + }}), + ), + want: podSpec( + []corev1.Container{ + servingContainer( + func(container *corev1.Container) { + container.Image = "busybox@sha256:deadbeef" + }, + withStartupProbe(corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{}, }), ), queueContainer(), @@ -1554,6 +1621,81 @@ func TestMakeDeployment(t *testing.T) { deploy.Annotations = map[string]string{serving.ProgressDeadlineAnnotationKey: "42s"} deploy.Spec.Template.Annotations = map[string]string{serving.ProgressDeadlineAnnotationKey: "42s"} }), + }, { + name: "with progress-deadline increase from single startup probe", + dc: deployment.Config{ + ProgressDeadline: 42 * time.Second, + }, + rev: revision("bar", "foo", + withContainers([]corev1.Container{{ + Name: servingContainerName, + Image: "ubuntu", + ReadinessProbe: withTCPReadinessProbe(12345), + StartupProbe: &corev1.Probe{ + InitialDelaySeconds: 10, + TimeoutSeconds: 30, + PeriodSeconds: 5, + SuccessThreshold: 1, + FailureThreshold: 3, + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + }, + }, + }, + }}), + WithContainerStatuses([]v1.ContainerStatus{{ + ImageDigest: "busybox@sha256:deadbeef", + }}), withoutLabels), + want: appsv1deployment(func(deploy *appsv1.Deployment) { + deploy.Spec.ProgressDeadlineSeconds = ptr.Int32(42 + 10 + (5 * 3 * 30)) + }), + }, { + name: "with progress-deadline increase from multiple startup probes", + dc: deployment.Config{ + ProgressDeadline: 42 * time.Second, + }, + rev: revision("bar", "foo", + withContainers([]corev1.Container{{ + Name: servingContainerName, + Image: "ubuntu", + ReadinessProbe: withTCPReadinessProbe(12345), + StartupProbe: &corev1.Probe{ + InitialDelaySeconds: 10, + TimeoutSeconds: 30, + PeriodSeconds: 5, + SuccessThreshold: 1, + FailureThreshold: 3, + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + }, + }, + }, + }, { + Name: servingContainerName + "-2", + Image: "sidecar", + StartupProbe: &corev1.Probe{ + InitialDelaySeconds: 10, + TimeoutSeconds: 30, + PeriodSeconds: 5, + SuccessThreshold: 5, + FailureThreshold: 3, + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + }, + }, + }, + }}), + WithContainerStatuses([]v1.ContainerStatus{{ + ImageDigest: "busybox@sha256:deadbeef", + }, { + ImageDigest: "busybox@sha256:deadbeef", + }}), withoutLabels), + want: appsv1deployment(func(deploy *appsv1.Deployment) { + deploy.Spec.ProgressDeadlineSeconds = ptr.Int32(42 + 10 + (5 * 5 * 30)) + }), }, { name: "cluster initial scale", acMutator: func(ac *autoscalerconfig.Config) {