Commit 3f22854e authored by bgrant0607's avatar bgrant0607

Merge pull request #2999 from dchen1107/podstatus

Determine PodStatus based on ContainerStatus and RestartPolicy
parents fec6b887 deafd902
...@@ -294,8 +294,12 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error ...@@ -294,8 +294,12 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error
if pod.Status.Info == nil { if pod.Status.Info == nil {
return api.PodPending, nil return api.PodPending, nil
} }
// TODO(dchen1107): move the entire logic to kubelet?
running := 0 running := 0
waiting := 0
stopped := 0 stopped := 0
failed := 0
succeeded := 0
unknown := 0 unknown := 0
for _, container := range pod.Spec.Containers { for _, container := range pod.Spec.Containers {
if containerStatus, ok := pod.Status.Info[container.Name]; ok { if containerStatus, ok := pod.Status.Info[container.Name]; ok {
...@@ -303,6 +307,13 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error ...@@ -303,6 +307,13 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error
running++ running++
} else if containerStatus.State.Termination != nil { } else if containerStatus.State.Termination != nil {
stopped++ stopped++
if containerStatus.State.Termination.ExitCode == 0 {
succeeded++
} else {
failed++
}
} else if containerStatus.State.Waiting != nil {
waiting++
} else { } else {
unknown++ unknown++
} }
...@@ -311,12 +322,32 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error ...@@ -311,12 +322,32 @@ func getPodStatus(pod *api.Pod, nodes client.NodeInterface) (api.PodPhase, error
} }
} }
switch { switch {
case waiting > 0:
// One or more containers has not been started
return api.PodPending, nil
case running > 0 && unknown == 0: case running > 0 && unknown == 0:
// All containers have been started, and at least
// one container is running
return api.PodRunning, nil return api.PodRunning, nil
case running == 0 && stopped > 0 && unknown == 0: case running == 0 && stopped > 0 && unknown == 0:
return api.PodFailed, nil // All containers are terminated
case running == 0 && stopped == 0 && unknown > 0: if pod.Spec.RestartPolicy.Always != nil {
return api.PodPending, nil // All containers are in the process of restarting
return api.PodRunning, nil
}
if stopped == succeeded {
// RestartPolicy is not Always, and all
// containers are terminated in success
return api.PodSucceeded, nil
}
if pod.Spec.RestartPolicy.Never != nil {
// RestartPolicy is Never, and all containers are
// terminated with at least one in failure
return api.PodFailed, nil
}
// RestartPolicy is OnFailure, and at least one in failure
// and in the process of restarting
return api.PodRunning, nil
default: default:
return api.PodPending, nil return api.PodPending, nil
} }
......
...@@ -368,7 +368,7 @@ func TestGetPodCloud(t *testing.T) { ...@@ -368,7 +368,7 @@ func TestGetPodCloud(t *testing.T) {
} }
} }
func TestMakePodStatus(t *testing.T) { func TestPodStatusWithBadNode(t *testing.T) {
fakeClient := client.Fake{ fakeClient := client.Fake{
MinionsList: api.NodeList{ MinionsList: api.NodeList{
Items: []api.Node{ Items: []api.Node{
...@@ -383,9 +383,7 @@ func TestMakePodStatus(t *testing.T) { ...@@ -383,9 +383,7 @@ func TestMakePodStatus(t *testing.T) {
{Name: "containerA"}, {Name: "containerA"},
{Name: "containerB"}, {Name: "containerB"},
}, },
} RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
currentState := api.PodStatus{
Host: "machine",
} }
runningState := api.ContainerStatus{ runningState := api.ContainerStatus{
State: api.ContainerState{ State: api.ContainerState{
...@@ -403,7 +401,6 @@ func TestMakePodStatus(t *testing.T) { ...@@ -403,7 +401,6 @@ func TestMakePodStatus(t *testing.T) {
status api.PodPhase status api.PodPhase
test string test string
}{ }{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{ {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
...@@ -422,25 +419,87 @@ func TestMakePodStatus(t *testing.T) { ...@@ -422,25 +419,87 @@ func TestMakePodStatus(t *testing.T) {
"containerA": runningState, "containerA": runningState,
"containerB": runningState, "containerB": runningState,
}, },
Host: "machine", Host: "machine-two",
}, },
}, },
api.PodRunning, api.PodFailed,
"all running", "all running but minion is missing",
}, },
{ {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ Info: map[string]api.ContainerStatus{
"containerA": runningState, "containerA": stoppedState,
"containerB": runningState, "containerB": stoppedState,
}, },
Host: "machine-two", Host: "machine-two",
}, },
}, },
api.PodFailed, api.PodFailed,
"all running but minion is missing", "all stopped but minion missing",
},
}
for _, test := range tests {
if status, err := getPodStatus(test.pod, fakeClient.Nodes()); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
if err != nil {
t.Errorf("In test %s, unexpected error: %v", test.test, err)
}
}
}
}
func TestPodStatusWithRestartAlways(t *testing.T) {
fakeClient := client.Fake{
MinionsList: api.NodeList{
Items: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "machine"},
},
},
},
}
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
stoppedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running",
}, },
{ {
&api.Pod{ &api.Pod{
...@@ -453,22 +512,134 @@ func TestMakePodStatus(t *testing.T) { ...@@ -453,22 +512,134 @@ func TestMakePodStatus(t *testing.T) {
Host: "machine", Host: "machine",
}, },
}, },
api.PodFailed, api.PodRunning,
"all stopped", "all stopped with restart always",
}, },
{ {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ Info: map[string]api.ContainerStatus{
"containerA": stoppedState, "containerA": runningState,
"containerB": stoppedState, "containerB": stoppedState,
}, },
Host: "machine-two", Host: "machine",
},
},
api.PodRunning,
"mixed state #1 with restart always",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
},
Host: "machine",
},
},
api.PodPending,
"mixed state #2 with restart always",
},
}
for _, test := range tests {
if status, err := getPodStatus(test.pod, fakeClient.Nodes()); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
if err != nil {
t.Errorf("In test %s, unexpected error: %v", test.test, err)
}
}
}
}
func TestPodStatusWithRestartNever(t *testing.T) {
fakeClient := client.Fake{
MinionsList: api.NodeList{
Items: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "machine"},
},
},
},
}
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicy{Never: &api.RestartPolicyNever{}},
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
succeededState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: 0,
},
},
}
failedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: -1,
},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": succeededState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodSucceeded,
"all succeeded with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": failedState,
"containerB": failedState,
},
Host: "machine",
}, },
}, },
api.PodFailed, api.PodFailed,
"all stopped but minion missing", "all failed with restart never",
}, },
{ {
&api.Pod{ &api.Pod{
...@@ -476,13 +647,139 @@ func TestMakePodStatus(t *testing.T) { ...@@ -476,13 +647,139 @@ func TestMakePodStatus(t *testing.T) {
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ Info: map[string]api.ContainerStatus{
"containerA": runningState, "containerA": runningState,
"containerB": stoppedState, "containerB": succeededState,
},
Host: "machine",
},
},
api.PodRunning,
"mixed state #1 with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
},
Host: "machine",
},
},
api.PodPending,
"mixed state #2 with restart never",
},
}
for _, test := range tests {
if status, err := getPodStatus(test.pod, fakeClient.Nodes()); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
if err != nil {
t.Errorf("In test %s, unexpected error: %v", test.test, err)
}
}
}
}
func TestPodStatusWithRestartOnFailure(t *testing.T) {
fakeClient := client.Fake{
MinionsList: api.NodeList{
Items: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "machine"},
},
},
},
}
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicy{OnFailure: &api.RestartPolicyOnFailure{}},
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
succeededState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: 0,
},
},
}
failedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: -1,
},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running with restart onfailure",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": succeededState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodSucceeded,
"all succeeded with restart onfailure",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": failedState,
"containerB": failedState,
},
Host: "machine",
},
},
api.PodRunning,
"all failed with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": succeededState,
}, },
Host: "machine", Host: "machine",
}, },
}, },
api.PodRunning, api.PodRunning,
"mixed state #1", "mixed state #1 with restart onfailure",
}, },
{ {
&api.Pod{ &api.Pod{
...@@ -495,7 +792,7 @@ func TestMakePodStatus(t *testing.T) { ...@@ -495,7 +792,7 @@ func TestMakePodStatus(t *testing.T) {
}, },
}, },
api.PodPending, api.PodPending,
"mixed state #2", "mixed state #2 with restart onfailure",
}, },
} }
for _, test := range tests { for _, test := range tests {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment