Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the CloneSet pod lifecycle can be transformed to Normal only if ContainerReady is true #1489

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apis/apps/pub/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (
LifecycleTimestampKey = "lifecycle.apps.kruise.io/timestamp"

// LifecycleStatePreparingNormal means the Pod is created but unavailable.
// It will translate to Normal state if Lifecycle.PreNormal is hooked.
// It will translate to Normal state if Lifecycle.PreNormal is hooked and pod condition ContainerReady is true.
LifecycleStatePreparingNormal LifecycleStateType = "PreparingNormal"
// LifecycleStateNormal is a necessary condition for Pod to be available.
LifecycleStateNormal LifecycleStateType = "Normal"
Expand Down
9 changes: 6 additions & 3 deletions pkg/controller/cloneset/sync/cloneset_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,12 @@ func (c *realControl) refreshPodState(cs *appsv1alpha1.CloneSet, coreControl clo
var state appspub.LifecycleStateType
switch lifecycle.GetPodLifecycleState(pod) {
case appspub.LifecycleStatePreparingNormal:
if cs.Spec.Lifecycle == nil ||
cs.Spec.Lifecycle.PreNormal == nil ||
lifecycle.IsPodAllHooked(cs.Spec.Lifecycle.PreNormal, pod) {
// 1.the pod lifecycle can be transformed from PreparingNormal to Normal only if ContainerReady is true.
// If lifecycle transformed from PreparingNormal to Normal immediately after pod created,
// it may trigger a BUG of old version kube-scheduler with some probability. https://github.com/openkruise/kruise/issues/1485
// 2.the semantic of this hook, let's leave it as it is:
// PreNormalHook: only if all hooks are hooked (or no hook), the pod can be transformed to Normal
if util.IsRunningAndContainerReady(pod) && lifecycle.IsPreNormalHookNilOrAllHooked(cs.Spec.Lifecycle, pod) {
state = appspub.LifecycleStateNormal
}
case appspub.LifecycleStatePreparingUpdate:
Expand Down
41 changes: 41 additions & 0 deletions pkg/controller/cloneset/sync/cloneset_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ func TestUpdate(t *testing.T) {
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
Expand All @@ -606,6 +607,7 @@ func TestUpdate(t *testing.T) {
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
Expand Down Expand Up @@ -669,6 +671,7 @@ func TestUpdate(t *testing.T) {
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
Expand All @@ -684,6 +687,7 @@ func TestUpdate(t *testing.T) {
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
Expand Down Expand Up @@ -843,6 +847,7 @@ func TestUpdate(t *testing.T) {
Phase: v1.PodRunning,
Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionTrue},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue, LastTransitionTime: now},
},
ContainerStatuses: []v1.ContainerStatus{{Name: "c1", ImageID: "image-id-xyz"}},
Expand All @@ -867,6 +872,7 @@ func TestUpdate(t *testing.T) {
Phase: v1.PodRunning,
Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionTrue},
{Type: v1.ContainersReady, Status: v1.ConditionTrue},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue, LastTransitionTime: now},
},
ContainerStatuses: []v1.ContainerStatus{{Name: "c1", ImageID: "image-id-xyz"}},
Expand Down Expand Up @@ -945,6 +951,41 @@ func TestUpdate(t *testing.T) {
},
},
},
{
name: "create: preparingNormal->Normal without hook, pod not ready",
cs: &appsv1alpha1.CloneSet{Spec: appsv1alpha1.CloneSetSpec{Replicas: getInt32Pointer(1)}},
updateRevision: &apps.ControllerRevision{ObjectMeta: metav1.ObjectMeta{Name: "rev_new"}},
pods: []*v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod-0", Labels: map[string]string{
apps.ControllerRevisionHashLabelKey: "rev_new",
apps.DefaultDeploymentUniqueLabelKey: "rev_new",
appspub.LifecycleStateKey: string(appspub.LifecycleStatePreparingNormal),
}},
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionFalse},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
},
expectedPods: []*v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod-0", Labels: map[string]string{
apps.ControllerRevisionHashLabelKey: "rev_new",
apps.DefaultDeploymentUniqueLabelKey: "rev_new",
appspub.LifecycleStateKey: string(appspub.LifecycleStatePreparingNormal),
}},
Spec: v1.PodSpec{ReadinessGates: []v1.PodReadinessGate{{ConditionType: appspub.InPlaceUpdateReady}}},
Status: v1.PodStatus{Phase: v1.PodRunning, Conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.ContainersReady, Status: v1.ConditionFalse},
{Type: appspub.InPlaceUpdateReady, Status: v1.ConditionTrue},
}},
},
},
},
}

inplaceupdate.Clock = clock.NewFakeClock(now.Time)
Expand Down
9 changes: 9 additions & 0 deletions pkg/util/lifecycle/lifecycle_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,15 @@ func IsPodAllHooked(hook *appspub.LifecycleHook, pod *v1.Pod) bool {
return true
}

// IsPreNormalHookNilOrAllHooked
// pod lifecycle can be transformed to Normal only if no hook or all hooks are hooked
func IsPreNormalHookNilOrAllHooked(hooks *appspub.Lifecycle, pod *v1.Pod) bool {
// nothing changed, but avoiding excessive complexity in if conditions
return hooks == nil ||
hooks.PreNormal == nil ||
IsPodAllHooked(hooks.PreNormal, pod)
}

func getReadinessMessage(key string) podreadiness.Message {
return podreadiness.Message{UserAgent: "Lifecycle", Key: key}
}
4 changes: 4 additions & 0 deletions pkg/util/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@
return pod.Status.Phase == v1.PodRunning && podutil.IsPodReady(pod) && pod.DeletionTimestamp.IsZero()
}

func IsRunningAndContainerReady(pod *v1.Pod) bool {
return pod.Status.Phase == v1.PodRunning && podutil.IsContainersReadyConditionTrue(pod.Status) && pod.DeletionTimestamp.IsZero()

Check warning on line 208 in pkg/util/pods.go

View check run for this annotation

Codecov / codecov/patch

pkg/util/pods.go#L207-L208

Added lines #L207 - L208 were not covered by tests
}

func GetPodContainerImageIDs(pod *v1.Pod) map[string]string {
cImageIDs := make(map[string]string, len(pod.Status.ContainerStatuses))
for i := range pod.Status.ContainerStatuses {
Expand Down
Loading