Merge pull request #5915 from piosz/api

Changed PodInfo to be a list
This commit is contained in:
Brian Grant 2015-03-26 12:46:50 -07:00
commit 3b0db99692
14 changed files with 392 additions and 185 deletions

View File

@ -95,12 +95,9 @@ func (fakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.Pod
return r, err return r, err
} }
r.Status.PodIP = "1.2.3.4" r.Status.PodIP = "1.2.3.4"
m := make(api.PodInfo) for i := range r.Status.ContainerStatuses {
for k, v := range r.Status.Info { r.Status.ContainerStatuses[i].Ready = true
v.Ready = true
m[k] = v
} }
r.Status.Info = m
return r, nil return r, nil
} }

View File

@ -40,3 +40,21 @@ func (self *ResourceList) Memory() *resource.Quantity {
} }
return &resource.Quantity{} return &resource.Quantity{}
} }
func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) {
for i := range statuses {
if statuses[i].Name == name {
return statuses[i], true
}
}
return ContainerStatus{}, false
}
func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus {
for i := range statuses {
if statuses[i].Name == name {
return statuses[i]
}
}
return ContainerStatus{}
}

View File

@ -593,6 +593,8 @@ type ContainerState struct {
} }
type ContainerStatus struct { type ContainerStatus struct {
// Each container in a pod must have a unique name.
Name string `name of the container; must be a DNS_LABEL and unique within the pod; cannot be updated"`
// TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states // TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states
// defined for container? // defined for container?
State ContainerState `json:"state,omitempty"` State ContainerState `json:"state,omitempty"`
@ -646,15 +648,12 @@ type PodCondition struct {
Status ConditionStatus `json:"status"` Status ConditionStatus `json:"status"`
} }
// PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus
// PodContainerInfo is a wrapper for PodInfo that can be encode/decoded // PodContainerInfo is a wrapper for PodInfo that can be encode/decoded
// DEPRECATED: Replaced with PodStatusResult // DEPRECATED: Replaced with PodStatusResult
type PodContainerInfo struct { type PodContainerInfo struct {
TypeMeta `json:",inline"` TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"` ObjectMeta `json:"metadata,omitempty"`
ContainerInfo PodInfo `json:"containerInfo"` ContainerInfo []ContainerStatus `json:"containerInfo"`
} }
// RestartPolicy describes how the container should be restarted. // RestartPolicy describes how the container should be restarted.
@ -726,13 +725,12 @@ type PodStatus struct {
HostIP string `json:"hostIP,omitempty"` HostIP string `json:"hostIP,omitempty"`
PodIP string `json:"podIP,omitempty"` PodIP string `json:"podIP,omitempty"`
// The key of this map is the *name* of the container within the manifest; it has one // The list has one entry per container in the manifest. Each entry is
// entry per container in the manifest. The value of this map is currently the output // currently the output of `docker inspect`. This output format is *not*
// of `docker inspect`. This output format is *not* final and should not be relied // final and should not be relied upon.
// upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test // TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// when we have done this. // when we have done this.
Info PodInfo `json:"info,omitempty"` ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty"`
} }
// PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded // PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded

View File

@ -188,7 +188,7 @@ func init() {
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil { if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Info, &out.Info, 0); err != nil { if err := s.Convert(&in.ContainerStatuses, &out.Info, 0); err != nil {
return err return err
} }
out.Message = in.Message out.Message = in.Message
@ -204,7 +204,7 @@ func init() {
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil { if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Info, &out.Info, 0); err != nil { if err := s.Convert(&in.Info, &out.ContainerStatuses, 0); err != nil {
return err return err
} }
@ -229,6 +229,78 @@ func init() {
return nil return nil
}, },
func(in *[]newer.ContainerStatus, out *PodInfo, s conversion.Scope) error {
*out = make(map[string]ContainerStatus)
for _, st := range *in {
v := ContainerStatus{}
if err := s.Convert(&st, &v, 0); err != nil {
return err
}
(*out)[st.Name] = v
}
return nil
},
func(in *PodInfo, out *[]newer.ContainerStatus, s conversion.Scope) error {
for k, v := range *in {
st := newer.ContainerStatus{}
if err := s.Convert(&v, &st, 0); err != nil {
return err
}
st.Name = k
*out = append(*out, st)
}
return nil
},
func(in *newer.ContainerStatus, out *ContainerStatus, s conversion.Scope) error {
if err := s.Convert(&in.State, &out.State, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTerminationState, &out.LastTerminationState, 0); err != nil {
return err
}
if err := s.Convert(&in.Ready, &out.Ready, 0); err != nil {
return err
}
if err := s.Convert(&in.RestartCount, &out.RestartCount, 0); err != nil {
return err
}
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.ImageID, &out.ImageID, 0); err != nil {
return err
}
if err := s.Convert(&in.ContainerID, &out.ContainerID, 0); err != nil {
return err
}
return nil
},
func(in *ContainerStatus, out *newer.ContainerStatus, s conversion.Scope) error {
if err := s.Convert(&in.State, &out.State, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTerminationState, &out.LastTerminationState, 0); err != nil {
return err
}
if err := s.Convert(&in.Ready, &out.Ready, 0); err != nil {
return err
}
if err := s.Convert(&in.RestartCount, &out.RestartCount, 0); err != nil {
return err
}
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.ImageID, &out.ImageID, 0); err != nil {
return err
}
if err := s.Convert(&in.ContainerID, &out.ContainerID, 0); err != nil {
return err
}
return nil
},
// Convert all to the new PodPhase constants // Convert all to the new PodPhase constants
func(in *newer.PodPhase, out *PodStatus, s conversion.Scope) error { func(in *newer.PodPhase, out *PodStatus, s conversion.Scope) error {
switch *in { switch *in {

View File

@ -484,7 +484,7 @@ func init() {
if err := s.Convert(&in.Phase, &out.Status, 0); err != nil { if err := s.Convert(&in.Phase, &out.Status, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Info, &out.Info, 0); err != nil { if err := s.Convert(&in.ContainerStatuses, &out.Info, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil { if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
@ -500,7 +500,7 @@ func init() {
if err := s.Convert(&in.Status, &out.Phase, 0); err != nil { if err := s.Convert(&in.Status, &out.Phase, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Info, &out.Info, 0); err != nil { if err := s.Convert(&in.Info, &out.ContainerStatuses, 0); err != nil {
return err return err
} }
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil { if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
@ -513,6 +513,78 @@ func init() {
return nil return nil
}, },
func(in *[]newer.ContainerStatus, out *PodInfo, s conversion.Scope) error {
*out = make(map[string]ContainerStatus)
for _, st := range *in {
v := ContainerStatus{}
if err := s.Convert(&st, &v, 0); err != nil {
return err
}
(*out)[st.Name] = v
}
return nil
},
func(in *PodInfo, out *[]newer.ContainerStatus, s conversion.Scope) error {
for k, v := range *in {
st := newer.ContainerStatus{}
if err := s.Convert(&v, &st, 0); err != nil {
return err
}
st.Name = k
*out = append(*out, st)
}
return nil
},
func(in *newer.ContainerStatus, out *ContainerStatus, s conversion.Scope) error {
if err := s.Convert(&in.State, &out.State, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTerminationState, &out.LastTerminationState, 0); err != nil {
return err
}
if err := s.Convert(&in.Ready, &out.Ready, 0); err != nil {
return err
}
if err := s.Convert(&in.RestartCount, &out.RestartCount, 0); err != nil {
return err
}
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.ImageID, &out.ImageID, 0); err != nil {
return err
}
if err := s.Convert(&in.ContainerID, &out.ContainerID, 0); err != nil {
return err
}
return nil
},
func(in *ContainerStatus, out *newer.ContainerStatus, s conversion.Scope) error {
if err := s.Convert(&in.State, &out.State, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTerminationState, &out.LastTerminationState, 0); err != nil {
return err
}
if err := s.Convert(&in.Ready, &out.Ready, 0); err != nil {
return err
}
if err := s.Convert(&in.RestartCount, &out.RestartCount, 0); err != nil {
return err
}
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.ImageID, &out.ImageID, 0); err != nil {
return err
}
if err := s.Convert(&in.ContainerID, &out.ContainerID, 0); err != nil {
return err
}
return nil
},
func(in *newer.PodStatusResult, out *PodStatusResult, s conversion.Scope) error { func(in *newer.PodStatusResult, out *PodStatusResult, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err return err

View File

@ -607,6 +607,8 @@ type ContainerState struct {
} }
type ContainerStatus struct { type ContainerStatus struct {
// Required: This must be a DNS_LABEL. Each container in a pod must have a unique name.
Name string `json:"name" description:"name of the container; must be a DNS_LABEL and unique within the pod; cannot be updated"`
// TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states // TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states
// defined for container? // defined for container?
State ContainerState `json:"state,omitempty" description:"details about the container's current condition"` State ContainerState `json:"state,omitempty" description:"details about the container's current condition"`
@ -663,9 +665,6 @@ type PodCondition struct {
Status ConditionStatus `json:"status" description:"status of the condition, one of Full, None, Unknown"` Status ConditionStatus `json:"status" description:"status of the condition, one of Full, None, Unknown"`
} }
// PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus
// RestartPolicy describes how the container should be restarted. // RestartPolicy describes how the container should be restarted.
// Only one of the following restart policies may be specified. // Only one of the following restart policies may be specified.
// If none of the following policies is specified, the default one // If none of the following policies is specified, the default one
@ -727,13 +726,12 @@ type PodStatus struct {
HostIP string `json:"hostIP,omitempty" description:"IP address of the host to which the pod is assigned; empty if not yet scheduled"` HostIP string `json:"hostIP,omitempty" description:"IP address of the host to which the pod is assigned; empty if not yet scheduled"`
PodIP string `json:"podIP,omitempty" description:"IP address allocated to the pod; routable at least within the cluster; empty if not yet allocated"` PodIP string `json:"podIP,omitempty" description:"IP address allocated to the pod; routable at least within the cluster; empty if not yet allocated"`
// The key of this map is the *name* of the container within the manifest; it has one // The list has one entry per container in the manifest. Each entry is currently the output
// entry per container in the manifest. The value of this map is currently the output
// of `docker inspect`. This output format is *not* final and should not be relied // of `docker inspect`. This output format is *not* final and should not be relied
// upon. // upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test // TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// when we have done this. // when we have done this.
Info PodInfo `json:"info,omitempty" description:"map of container name to container status"` ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty" description:"list of container statuses"`
} }
// PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded // PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded

View File

@ -33,9 +33,9 @@ import (
func TestHTTPKubeletClient(t *testing.T) { func TestHTTPKubeletClient(t *testing.T) {
expectObj := api.PodStatusResult{ expectObj := api.PodStatusResult{
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"myID1": {}, {Name: "myID1"},
"myID2": {}, {Name: "myID2"},
}, },
}, },
} }
@ -73,15 +73,17 @@ func TestHTTPKubeletClient(t *testing.T) {
} }
// reflect.DeepEqual(expectObj, gotObj) doesn't handle blank times well // reflect.DeepEqual(expectObj, gotObj) doesn't handle blank times well
if len(gotObj.Status.Info) != len(expectObj.Status.Info) { if len(gotObj.Status.ContainerStatuses) != len(expectObj.Status.ContainerStatuses) {
t.Errorf("Unexpected response. Expected: %#v, received %#v", expectObj, gotObj) t.Errorf("Unexpected response. Expected: %#v, received %#v", expectObj, gotObj)
} }
} }
func TestHTTPKubeletClientNotFound(t *testing.T) { func TestHTTPKubeletClientNotFound(t *testing.T) {
expectObj := api.PodContainerInfo{ expectObj := api.PodContainerInfo{
ContainerInfo: map[string]api.ContainerStatus{ ContainerInfo: []api.ContainerStatus{
"myID": {}, {
Name: "myID",
},
}, },
} }
_, err := json.Marshal(expectObj) _, err := json.Marshal(expectObj)
@ -120,8 +122,10 @@ func TestHTTPKubeletClientNotFound(t *testing.T) {
func TestHTTPKubeletClientError(t *testing.T) { func TestHTTPKubeletClientError(t *testing.T) {
expectObj := api.PodContainerInfo{ expectObj := api.PodContainerInfo{
ContainerInfo: map[string]api.ContainerStatus{ ContainerInfo: []api.ContainerStatus{
"myID": {}, {
Name: "myID",
},
}, },
} }
_, err := json.Marshal(expectObj) _, err := json.Marshal(expectObj)

View File

@ -327,11 +327,15 @@ func TestTemplateStrings(t *testing.T) {
expect string expect string
}{ }{
"nilInfo": {api.Pod{}, "false"}, "nilInfo": {api.Pod{}, "false"},
"emptyInfo": {api.Pod{Status: api.PodStatus{Info: api.PodInfo{}}}, "false"}, "emptyInfo": {api.Pod{Status: api.PodStatus{ContainerStatuses: []api.ContainerStatus{}}}, "false"},
"fooExists": { "fooExists": {
api.Pod{ api.Pod{
Status: api.PodStatus{ Status: api.PodStatus{
Info: api.PodInfo{"foo": api.ContainerStatus{}}, ContainerStatuses: []api.ContainerStatus{
{
Name: "foo",
},
},
}, },
}, },
"false", "false",
@ -339,7 +343,11 @@ func TestTemplateStrings(t *testing.T) {
"barExists": { "barExists": {
api.Pod{ api.Pod{
Status: api.PodStatus{ Status: api.PodStatus{
Info: api.PodInfo{"bar": api.ContainerStatus{}}, ContainerStatuses: []api.ContainerStatus{
{
Name: "bar",
},
},
}, },
}, },
"false", "false",
@ -347,9 +355,13 @@ func TestTemplateStrings(t *testing.T) {
"bothExist": { "bothExist": {
api.Pod{ api.Pod{
Status: api.PodStatus{ Status: api.PodStatus{
Info: api.PodInfo{ ContainerStatuses: []api.ContainerStatus{
"foo": api.ContainerStatus{}, {
"bar": api.ContainerStatus{}, Name: "foo",
},
{
Name: "bar",
},
}, },
}, },
}, },
@ -358,9 +370,12 @@ func TestTemplateStrings(t *testing.T) {
"oneValid": { "oneValid": {
api.Pod{ api.Pod{
Status: api.PodStatus{ Status: api.PodStatus{
Info: api.PodInfo{ ContainerStatuses: []api.ContainerStatus{
"foo": api.ContainerStatus{}, {
"bar": api.ContainerStatus{ Name: "foo",
},
{
Name: "bar",
State: api.ContainerState{ State: api.ContainerState{
Running: &api.ContainerStateRunning{ Running: &api.ContainerStateRunning{
StartedAt: util.Time{}, StartedAt: util.Time{},
@ -375,15 +390,17 @@ func TestTemplateStrings(t *testing.T) {
"bothValid": { "bothValid": {
api.Pod{ api.Pod{
Status: api.PodStatus{ Status: api.PodStatus{
Info: api.PodInfo{ ContainerStatuses: []api.ContainerStatus{
"foo": api.ContainerStatus{ {
Name: "foo",
State: api.ContainerState{ State: api.ContainerState{
Running: &api.ContainerStateRunning{ Running: &api.ContainerStateRunning{
StartedAt: util.Time{}, StartedAt: util.Time{},
}, },
}, },
}, },
"bar": api.ContainerStatus{ {
Name: "bar",
State: api.ContainerState{ State: api.ContainerState{
Running: &api.ContainerStateRunning{ Running: &api.ContainerStateRunning{
StartedAt: util.Time{}, StartedAt: util.Time{},

View File

@ -558,6 +558,7 @@ func inspectContainer(client DockerInterface, dockerID, containerName, tPath str
glog.V(3).Infof("Container inspect result: %+v", *inspectResult) glog.V(3).Infof("Container inspect result: %+v", *inspectResult)
result.status = api.ContainerStatus{ result.status = api.ContainerStatus{
Name: containerName,
Image: inspectResult.Config.Image, Image: inspectResult.Config.Image,
ImageID: DockerPrefix + inspectResult.Image, ImageID: DockerPrefix + inspectResult.Image,
ContainerID: DockerPrefix + dockerID, ContainerID: DockerPrefix + dockerID,
@ -618,7 +619,7 @@ func inspectContainer(client DockerInterface, dockerID, containerName, tPath str
// infrastructure container // infrastructure container
func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullName string, uid types.UID) (*api.PodStatus, error) { func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullName string, uid types.UID) (*api.PodStatus, error) {
var podStatus api.PodStatus var podStatus api.PodStatus
podStatus.Info = api.PodInfo{} statuses := make(map[string]api.ContainerStatus)
expectedContainers := make(map[string]api.Container) expectedContainers := make(map[string]api.Container)
for _, container := range manifest.Containers { for _, container := range manifest.Containers {
@ -655,9 +656,9 @@ func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullNam
terminationMessagePath = c.TerminationMessagePath terminationMessagePath = c.TerminationMessagePath
} }
// We assume docker return us a list of containers in time order // We assume docker return us a list of containers in time order
if containerStatus, found := podStatus.Info[dockerContainerName]; found { if containerStatus, found := statuses[dockerContainerName]; found {
containerStatus.RestartCount += 1 containerStatus.RestartCount += 1
podStatus.Info[dockerContainerName] = containerStatus statuses[dockerContainerName] = containerStatus
continue continue
} }
@ -670,20 +671,20 @@ func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullNam
// Found network container // Found network container
podStatus.PodIP = result.ip podStatus.PodIP = result.ip
} else { } else {
podStatus.Info[dockerContainerName] = result.status statuses[dockerContainerName] = result.status
} }
} }
if len(podStatus.Info) == 0 && podStatus.PodIP == "" { if len(statuses) == 0 && podStatus.PodIP == "" {
return nil, ErrNoContainersInPod return nil, ErrNoContainersInPod
} }
// Not all containers expected are created, check if there are // Not all containers expected are created, check if there are
// image related issues // image related issues
if len(podStatus.Info) < len(manifest.Containers) { if len(statuses) < len(manifest.Containers) {
var containerStatus api.ContainerStatus var containerStatus api.ContainerStatus
for _, container := range manifest.Containers { for _, container := range manifest.Containers {
if _, found := podStatus.Info[container.Name]; found { if _, found := statuses[container.Name]; found {
continue continue
} }
@ -705,10 +706,15 @@ func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullNam
} }
} }
podStatus.Info[container.Name] = containerStatus statuses[container.Name] = containerStatus
} }
} }
podStatus.ContainerStatuses = make([]api.ContainerStatus, 0)
for _, status := range statuses {
podStatus.ContainerStatuses = append(podStatus.ContainerStatuses, status)
}
return &podStatus, nil return &podStatus, nil
} }

View File

@ -1770,15 +1770,14 @@ func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error {
} }
func (kl *Kubelet) validateContainerStatus(podStatus *api.PodStatus, containerName string) (dockerID string, err error) { func (kl *Kubelet) validateContainerStatus(podStatus *api.PodStatus, containerName string) (dockerID string, err error) {
for cName, cStatus := range podStatus.Info { cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName)
if containerName == cName { if !found {
if cStatus.State.Waiting != nil { return "", fmt.Errorf("container %q not found in pod", containerName)
return "", fmt.Errorf("container %q is in waiting state.", containerName)
}
return strings.Replace(podStatus.Info[containerName].ContainerID, dockertools.DockerPrefix, "", 1), nil
}
} }
return "", fmt.Errorf("container %q not found in pod", containerName) if cStatus.State.Waiting != nil {
return "", fmt.Errorf("container %q is in waiting state.", containerName)
}
return strings.Replace(cStatus.ContainerID, dockertools.DockerPrefix, "", 1), nil
} }
// GetKubeletContainerLogs returns logs from the container // GetKubeletContainerLogs returns logs from the container
@ -1908,7 +1907,7 @@ func (kl *Kubelet) tryUpdateNodeStatus() error {
} }
// getPhase returns the phase of a pod given its container info. // getPhase returns the phase of a pod given its container info.
func getPhase(spec *api.PodSpec, info api.PodInfo) api.PodPhase { func getPhase(spec *api.PodSpec, info []api.ContainerStatus) api.PodPhase {
running := 0 running := 0
waiting := 0 waiting := 0
stopped := 0 stopped := 0
@ -1916,7 +1915,7 @@ func getPhase(spec *api.PodSpec, info api.PodInfo) api.PodPhase {
succeeded := 0 succeeded := 0
unknown := 0 unknown := 0
for _, container := range spec.Containers { for _, container := range spec.Containers {
if containerStatus, ok := info[container.Name]; ok { if containerStatus, ok := api.GetContainerStatus(info, container.Name); ok {
if containerStatus.State.Running != nil { if containerStatus.State.Running != nil {
running++ running++
} else if containerStatus.State.Termination != nil { } else if containerStatus.State.Termination != nil {
@ -1970,7 +1969,7 @@ func getPhase(spec *api.PodSpec, info api.PodInfo) api.PodPhase {
} }
// getPodReadyCondition returns ready condition if all containers in a pod are ready, else it returns an unready condition. // getPodReadyCondition returns ready condition if all containers in a pod are ready, else it returns an unready condition.
func getPodReadyCondition(spec *api.PodSpec, info api.PodInfo) []api.PodCondition { func getPodReadyCondition(spec *api.PodSpec, statuses []api.ContainerStatus) []api.PodCondition {
ready := []api.PodCondition{{ ready := []api.PodCondition{{
Type: api.PodReady, Type: api.PodReady,
Status: api.ConditionTrue, Status: api.ConditionTrue,
@ -1979,11 +1978,11 @@ func getPodReadyCondition(spec *api.PodSpec, info api.PodInfo) []api.PodConditio
Type: api.PodReady, Type: api.PodReady,
Status: api.ConditionFalse, Status: api.ConditionFalse,
}} }}
if info == nil { if statuses == nil {
return unready return unready
} }
for _, container := range spec.Containers { for _, container := range spec.Containers {
if containerStatus, ok := info[container.Name]; ok { if containerStatus, ok := api.GetContainerStatus(statuses, container.Name); ok {
if !containerStatus.Ready { if !containerStatus.Ready {
return unready return unready
} }
@ -2038,13 +2037,16 @@ func (kl *Kubelet) generatePodStatusByPod(pod *api.Pod) (api.PodStatus, error) {
} }
// Assume info is ready to process // Assume info is ready to process
podStatus.Phase = getPhase(spec, podStatus.Info) podStatus.Phase = getPhase(spec, podStatus.ContainerStatuses)
for _, c := range spec.Containers { for _, c := range spec.Containers {
containerStatus := podStatus.Info[c.Name] for i, st := range podStatus.ContainerStatuses {
containerStatus.Ready = kl.readiness.IsReady(containerStatus) if st.Name == c.Name {
podStatus.Info[c.Name] = containerStatus podStatus.ContainerStatuses[i].Ready = kl.readiness.IsReady(st)
break
}
}
} }
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.Info)...) podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses)...)
podStatus.Host = kl.GetHostname() podStatus.Host = kl.GetHostname()
hostIP, err := kl.GetHostIP() hostIP, err := kl.GetHostIP()
if err != nil { if err != nil {

View File

@ -2183,6 +2183,43 @@ func TestMakeEnvironmentVariables(t *testing.T) {
} }
} }
func runningState(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
}
func stoppedState(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
}
}
func succeededState(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: 0,
},
},
}
}
func failedState(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: -1,
},
},
}
}
func TestPodPhaseWithRestartAlways(t *testing.T) { func TestPodPhaseWithRestartAlways(t *testing.T) {
desiredState := api.PodSpec{ desiredState := api.PodSpec{
Containers: []api.Container{ Containers: []api.Container{
@ -2194,16 +2231,6 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
currentState := api.PodStatus{ currentState := api.PodStatus{
Host: "machine", Host: "machine",
} }
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
stoppedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
}
tests := []struct { tests := []struct {
pod *api.Pod pod *api.Pod
@ -2215,9 +2242,9 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": runningState, runningState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2229,9 +2256,9 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": stoppedState, stoppedState("containerA"),
"containerB": stoppedState, stoppedState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2243,9 +2270,9 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": stoppedState, stoppedState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2257,8 +2284,8 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2268,7 +2295,7 @@ func TestPodPhaseWithRestartAlways(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status { if status := getPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status) t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
} }
} }
@ -2285,25 +2312,6 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
currentState := api.PodStatus{ currentState := api.PodStatus{
Host: "machine", 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 { tests := []struct {
pod *api.Pod pod *api.Pod
@ -2315,9 +2323,9 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": runningState, runningState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2329,9 +2337,9 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": succeededState, succeededState("containerA"),
"containerB": succeededState, succeededState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2343,9 +2351,9 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": failedState, failedState("containerA"),
"containerB": failedState, failedState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2357,9 +2365,9 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": succeededState, succeededState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2371,8 +2379,8 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2382,7 +2390,7 @@ func TestPodPhaseWithRestartNever(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status { if status := getPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status) t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
} }
} }
@ -2399,25 +2407,6 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
currentState := api.PodStatus{ currentState := api.PodStatus{
Host: "machine", 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 { tests := []struct {
pod *api.Pod pod *api.Pod
@ -2429,9 +2418,9 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": runningState, runningState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2443,9 +2432,9 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": succeededState, succeededState("containerA"),
"containerB": succeededState, succeededState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2457,9 +2446,9 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": failedState, failedState("containerA"),
"containerB": failedState, failedState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2471,9 +2460,9 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
"containerB": succeededState, succeededState("containerB"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2485,8 +2474,8 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
&api.Pod{ &api.Pod{
Spec: desiredState, Spec: desiredState,
Status: api.PodStatus{ Status: api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"containerA": runningState, runningState("containerA"),
}, },
Host: "machine", Host: "machine",
}, },
@ -2496,12 +2485,25 @@ func TestPodPhaseWithRestartOnFailure(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status { if status := getPhase(&test.pod.Spec, test.pod.Status.ContainerStatuses); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status) t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
} }
} }
} }
func getReadyStatus(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
Ready: true,
}
}
func getNotReadyStatus(cName string) api.ContainerStatus {
return api.ContainerStatus{
Name: cName,
Ready: false,
}
}
func TestGetPodReadyCondition(t *testing.T) { func TestGetPodReadyCondition(t *testing.T) {
ready := []api.PodCondition{{ ready := []api.PodCondition{{
Type: api.PodReady, Type: api.PodReady,
@ -2513,7 +2515,7 @@ func TestGetPodReadyCondition(t *testing.T) {
}} }}
tests := []struct { tests := []struct {
spec *api.PodSpec spec *api.PodSpec
info api.PodInfo info []api.ContainerStatus
expected []api.PodCondition expected []api.PodCondition
}{ }{
{ {
@ -2523,7 +2525,7 @@ func TestGetPodReadyCondition(t *testing.T) {
}, },
{ {
spec: &api.PodSpec{}, spec: &api.PodSpec{},
info: api.PodInfo{}, info: []api.ContainerStatus{},
expected: ready, expected: ready,
}, },
{ {
@ -2532,7 +2534,7 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"}, {Name: "1234"},
}, },
}, },
info: api.PodInfo{}, info: []api.ContainerStatus{},
expected: unready, expected: unready,
}, },
{ {
@ -2541,8 +2543,8 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"}, {Name: "1234"},
}, },
}, },
info: api.PodInfo{ info: []api.ContainerStatus{
"1234": api.ContainerStatus{Ready: true}, getReadyStatus("1234"),
}, },
expected: ready, expected: ready,
}, },
@ -2553,9 +2555,9 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: api.PodInfo{ info: []api.ContainerStatus{
"1234": api.ContainerStatus{Ready: true}, getReadyStatus("1234"),
"5678": api.ContainerStatus{Ready: true}, getReadyStatus("5678"),
}, },
expected: ready, expected: ready,
}, },
@ -2566,8 +2568,8 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: api.PodInfo{ info: []api.ContainerStatus{
"1234": api.ContainerStatus{Ready: true}, getReadyStatus("1234"),
}, },
expected: unready, expected: unready,
}, },
@ -2578,9 +2580,9 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: api.PodInfo{ info: []api.ContainerStatus{
"1234": api.ContainerStatus{Ready: true}, getReadyStatus("1234"),
"5678": api.ContainerStatus{Ready: false}, getNotReadyStatus("5678"),
}, },
expected: unready, expected: unready,
}, },
@ -3085,26 +3087,47 @@ func TestValidateContainerStatus(t *testing.T) {
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
containerName := "x" containerName := "x"
testCases := []struct { testCases := []struct {
podInfo api.PodInfo statuses []api.ContainerStatus
success bool success bool
}{ }{
{ {
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Running: &api.ContainerStateRunning{}}}}, statuses: []api.ContainerStatus{
{
Name: containerName,
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
},
},
success: true, success: true,
}, },
{ {
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Termination: &api.ContainerStateTerminated{}}}}, statuses: []api.ContainerStatus{
{
Name: containerName,
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
},
},
success: true, success: true,
}, },
{ {
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}}}}, statuses: []api.ContainerStatus{
{
Name: containerName,
State: api.ContainerState{
Waiting: &api.ContainerStateWaiting{},
},
},
},
success: false, success: false,
}, },
} }
for i, tc := range testCases { for i, tc := range testCases {
_, err := kubelet.validateContainerStatus(&api.PodStatus{ _, err := kubelet.validateContainerStatus(&api.PodStatus{
Info: tc.podInfo, ContainerStatuses: tc.statuses,
}, containerName) }, containerName)
if tc.success { if tc.success {
if err != nil { if err != nil {
@ -3115,7 +3138,7 @@ func TestValidateContainerStatus(t *testing.T) {
} }
} }
if _, err := kubelet.validateContainerStatus(&api.PodStatus{ if _, err := kubelet.validateContainerStatus(&api.PodStatus{
Info: testCases[0].podInfo, ContainerStatuses: testCases[0].statuses,
}, "blah"); err == nil { }, "blah"); err == nil {
t.Errorf("expected error with invalid container name") t.Errorf("expected error with invalid container name")
} }

View File

@ -166,8 +166,8 @@ func getPodName(name, namespace string) string {
func TestPodStatus(t *testing.T) { func TestPodStatus(t *testing.T) {
fw := newServerTest() fw := newServerTest()
expected := api.PodStatus{ expected := api.PodStatus{
Info: map[string]api.ContainerStatus{ ContainerStatuses: []api.ContainerStatus{
"goodpod": {}, {Name: "goodpod"},
}, },
} }
fw.fakeKubelet.statusFunc = func(name string) (api.PodStatus, error) { fw.fakeKubelet.statusFunc = func(name string) (api.PodStatus, error) {

View File

@ -57,7 +57,7 @@ func runLivenessTest(c *client.Client, podDescr *api.Pod) {
By("checking the pod's current state and verifying that restartCount is present") By("checking the pod's current state and verifying that restartCount is present")
pod, err := c.Pods(ns).Get(podDescr.Name) pod, err := c.Pods(ns).Get(podDescr.Name)
expectNoError(err, fmt.Sprintf("getting pod %s in namespace %s", podDescr.Name, ns)) expectNoError(err, fmt.Sprintf("getting pod %s in namespace %s", podDescr.Name, ns))
initialRestartCount := pod.Status.Info["liveness"].RestartCount initialRestartCount := api.GetExistingContainerStatus(pod.Status.ContainerStatuses, "liveness").RestartCount
By(fmt.Sprintf("Initial restart count of pod %s is %d", podDescr.Name, initialRestartCount)) By(fmt.Sprintf("Initial restart count of pod %s is %d", podDescr.Name, initialRestartCount))
// Wait for at most 48 * 5 = 240s = 4 minutes until restartCount is incremented // Wait for at most 48 * 5 = 240s = 4 minutes until restartCount is incremented
@ -67,7 +67,7 @@ func runLivenessTest(c *client.Client, podDescr *api.Pod) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
pod, err = c.Pods(ns).Get(podDescr.Name) pod, err = c.Pods(ns).Get(podDescr.Name)
expectNoError(err, fmt.Sprintf("getting pod %s", podDescr.Name)) expectNoError(err, fmt.Sprintf("getting pod %s", podDescr.Name))
restartCount := pod.Status.Info["liveness"].RestartCount restartCount := api.GetExistingContainerStatus(pod.Status.ContainerStatuses, "liveness").RestartCount
By(fmt.Sprintf("Restart count of pod %s in namespace %s is now %d", podDescr.Name, ns, restartCount)) By(fmt.Sprintf("Restart count of pod %s in namespace %s is now %d", podDescr.Name, ns, restartCount))
if restartCount > initialRestartCount { if restartCount > initialRestartCount {
By(fmt.Sprintf("Restart count of pod %s in namespace %s increased from %d to %d during the test", podDescr.Name, ns, initialRestartCount, restartCount)) By(fmt.Sprintf("Restart count of pod %s in namespace %s increased from %d to %d during the test", podDescr.Name, ns, initialRestartCount, restartCount))

View File

@ -101,7 +101,7 @@ func waitForPodNotPending(c *client.Client, ns, podName string) error {
func waitForPodSuccessInNamespace(c *client.Client, podName string, contName string, namespace string) error { func waitForPodSuccessInNamespace(c *client.Client, podName string, contName string, namespace string) error {
return waitForPodCondition(c, namespace, podName, "success or failure", func(pod *api.Pod) (bool, error) { return waitForPodCondition(c, namespace, podName, "success or failure", func(pod *api.Pod) (bool, error) {
// Cannot use pod.Status.Phase == api.PodSucceeded/api.PodFailed due to #2632 // Cannot use pod.Status.Phase == api.PodSucceeded/api.PodFailed due to #2632
ci, ok := pod.Status.Info[contName] ci, ok := api.GetContainerStatus(pod.Status.ContainerStatuses, contName)
if !ok { if !ok {
Logf("No Status.Info for container %s in pod %s yet", contName, podName) Logf("No Status.Info for container %s in pod %s yet", contName, podName)
} else { } else {