Commit 3b0db996 authored by Brian Grant's avatar Brian Grant

Merge pull request #5915 from piosz/api

Changed PodInfo to be a list
parents 2f29d1dd 78871ca5
......@@ -95,12 +95,9 @@ func (fakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.Pod
return r, err
}
r.Status.PodIP = "1.2.3.4"
m := make(api.PodInfo)
for k, v := range r.Status.Info {
v.Ready = true
m[k] = v
for i := range r.Status.ContainerStatuses {
r.Status.ContainerStatuses[i].Ready = true
}
r.Status.Info = m
return r, nil
}
......
......@@ -40,3 +40,21 @@ func (self *ResourceList) Memory() *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{}
}
......@@ -593,6 +593,8 @@ type ContainerState 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
// defined for container?
State ContainerState `json:"state,omitempty"`
......@@ -646,15 +648,12 @@ type PodCondition struct {
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
// DEPRECATED: Replaced with PodStatusResult
type PodContainerInfo struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"`
ContainerInfo PodInfo `json:"containerInfo"`
ContainerInfo []ContainerStatus `json:"containerInfo"`
}
// RestartPolicy describes how the container should be restarted.
......@@ -726,13 +725,12 @@ type PodStatus struct {
HostIP string `json:"hostIP,omitempty"`
PodIP string `json:"podIP,omitempty"`
// The key of this map is the *name* of the container within the manifest; it has one
// 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
// upon.
// The list has one entry per container in the manifest. Each entry is
// currently the output of `docker inspect`. This output format is *not*
// final and should not be relied upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// 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
......
......@@ -188,7 +188,7 @@ func init() {
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
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
}
out.Message = in.Message
......@@ -204,7 +204,7 @@ func init() {
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
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
}
......@@ -229,6 +229,78 @@ func init() {
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
func(in *newer.PodPhase, out *PodStatus, s conversion.Scope) error {
switch *in {
......
......@@ -484,7 +484,7 @@ func init() {
if err := s.Convert(&in.Phase, &out.Status, 0); err != nil {
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
}
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 {
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
}
if err := s.Convert(&in.Conditions, &out.Conditions, 0); err != nil {
......@@ -513,6 +513,78 @@ func init() {
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 {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
......
......@@ -607,6 +607,8 @@ type ContainerState 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
// defined for container?
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"`
}
// PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus
// RestartPolicy describes how the container should be restarted.
// Only one of the following restart policies may be specified.
// 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"`
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
// entry per container in the manifest. The value of this map is currently the output
// The list has one entry per container in the manifest. Each entry is currently the output
// of `docker inspect`. This output format is *not* final and should not be relied
// upon.
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test
// 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
......
......@@ -33,9 +33,9 @@ import (
func TestHTTPKubeletClient(t *testing.T) {
expectObj := api.PodStatusResult{
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"myID1": {},
"myID2": {},
ContainerStatuses: []api.ContainerStatus{
{Name: "myID1"},
{Name: "myID2"},
},
},
}
......@@ -73,15 +73,17 @@ func TestHTTPKubeletClient(t *testing.T) {
}
// 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)
}
}
func TestHTTPKubeletClientNotFound(t *testing.T) {
expectObj := api.PodContainerInfo{
ContainerInfo: map[string]api.ContainerStatus{
"myID": {},
ContainerInfo: []api.ContainerStatus{
{
Name: "myID",
},
},
}
_, err := json.Marshal(expectObj)
......@@ -120,8 +122,10 @@ func TestHTTPKubeletClientNotFound(t *testing.T) {
func TestHTTPKubeletClientError(t *testing.T) {
expectObj := api.PodContainerInfo{
ContainerInfo: map[string]api.ContainerStatus{
"myID": {},
ContainerInfo: []api.ContainerStatus{
{
Name: "myID",
},
},
}
_, err := json.Marshal(expectObj)
......
......@@ -327,11 +327,15 @@ func TestTemplateStrings(t *testing.T) {
expect string
}{
"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": {
api.Pod{
Status: api.PodStatus{
Info: api.PodInfo{"foo": api.ContainerStatus{}},
ContainerStatuses: []api.ContainerStatus{
{
Name: "foo",
},
},
},
},
"false",
......@@ -339,7 +343,11 @@ func TestTemplateStrings(t *testing.T) {
"barExists": {
api.Pod{
Status: api.PodStatus{
Info: api.PodInfo{"bar": api.ContainerStatus{}},
ContainerStatuses: []api.ContainerStatus{
{
Name: "bar",
},
},
},
},
"false",
......@@ -347,9 +355,13 @@ func TestTemplateStrings(t *testing.T) {
"bothExist": {
api.Pod{
Status: api.PodStatus{
Info: api.PodInfo{
"foo": api.ContainerStatus{},
"bar": api.ContainerStatus{},
ContainerStatuses: []api.ContainerStatus{
{
Name: "foo",
},
{
Name: "bar",
},
},
},
},
......@@ -358,9 +370,12 @@ func TestTemplateStrings(t *testing.T) {
"oneValid": {
api.Pod{
Status: api.PodStatus{
Info: api.PodInfo{
"foo": api.ContainerStatus{},
"bar": api.ContainerStatus{
ContainerStatuses: []api.ContainerStatus{
{
Name: "foo",
},
{
Name: "bar",
State: api.ContainerState{
Running: &api.ContainerStateRunning{
StartedAt: util.Time{},
......@@ -375,15 +390,17 @@ func TestTemplateStrings(t *testing.T) {
"bothValid": {
api.Pod{
Status: api.PodStatus{
Info: api.PodInfo{
"foo": api.ContainerStatus{
ContainerStatuses: []api.ContainerStatus{
{
Name: "foo",
State: api.ContainerState{
Running: &api.ContainerStateRunning{
StartedAt: util.Time{},
},
},
},
"bar": api.ContainerStatus{
{
Name: "bar",
State: api.ContainerState{
Running: &api.ContainerStateRunning{
StartedAt: util.Time{},
......
......@@ -558,6 +558,7 @@ func inspectContainer(client DockerInterface, dockerID, containerName, tPath str
glog.V(3).Infof("Container inspect result: %+v", *inspectResult)
result.status = api.ContainerStatus{
Name: containerName,
Image: inspectResult.Config.Image,
ImageID: DockerPrefix + inspectResult.Image,
ContainerID: DockerPrefix + dockerID,
......@@ -618,7 +619,7 @@ func inspectContainer(client DockerInterface, dockerID, containerName, tPath str
// infrastructure container
func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullName string, uid types.UID) (*api.PodStatus, error) {
var podStatus api.PodStatus
podStatus.Info = api.PodInfo{}
statuses := make(map[string]api.ContainerStatus)
expectedContainers := make(map[string]api.Container)
for _, container := range manifest.Containers {
......@@ -655,9 +656,9 @@ func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullNam
terminationMessagePath = c.TerminationMessagePath
}
// 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
podStatus.Info[dockerContainerName] = containerStatus
statuses[dockerContainerName] = containerStatus
continue
}
......@@ -670,20 +671,20 @@ func GetDockerPodStatus(client DockerInterface, manifest api.PodSpec, podFullNam
// Found network container
podStatus.PodIP = result.ip
} 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
}
// Not all containers expected are created, check if there are
// image related issues
if len(podStatus.Info) < len(manifest.Containers) {
if len(statuses) < len(manifest.Containers) {
var containerStatus api.ContainerStatus
for _, container := range manifest.Containers {
if _, found := podStatus.Info[container.Name]; found {
if _, found := statuses[container.Name]; found {
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
}
......
......@@ -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) {
for cName, cStatus := range podStatus.Info {
if containerName == cName {
if cStatus.State.Waiting != nil {
return "", fmt.Errorf("container %q is in waiting state.", containerName)
}
return strings.Replace(podStatus.Info[containerName].ContainerID, dockertools.DockerPrefix, "", 1), nil
}
cStatus, found := api.GetContainerStatus(podStatus.ContainerStatuses, containerName)
if !found {
return "", fmt.Errorf("container %q not found in pod", containerName)
}
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
......@@ -1908,7 +1907,7 @@ func (kl *Kubelet) tryUpdateNodeStatus() error {
}
// 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
waiting := 0
stopped := 0
......@@ -1916,7 +1915,7 @@ func getPhase(spec *api.PodSpec, info api.PodInfo) api.PodPhase {
succeeded := 0
unknown := 0
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 {
running++
} 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.
func getPodReadyCondition(spec *api.PodSpec, info api.PodInfo) []api.PodCondition {
func getPodReadyCondition(spec *api.PodSpec, statuses []api.ContainerStatus) []api.PodCondition {
ready := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionTrue,
......@@ -1979,11 +1978,11 @@ func getPodReadyCondition(spec *api.PodSpec, info api.PodInfo) []api.PodConditio
Type: api.PodReady,
Status: api.ConditionFalse,
}}
if info == nil {
if statuses == nil {
return unready
}
for _, container := range spec.Containers {
if containerStatus, ok := info[container.Name]; ok {
if containerStatus, ok := api.GetContainerStatus(statuses, container.Name); ok {
if !containerStatus.Ready {
return unready
}
......@@ -2038,13 +2037,16 @@ func (kl *Kubelet) generatePodStatusByPod(pod *api.Pod) (api.PodStatus, error) {
}
// Assume info is ready to process
podStatus.Phase = getPhase(spec, podStatus.Info)
podStatus.Phase = getPhase(spec, podStatus.ContainerStatuses)
for _, c := range spec.Containers {
containerStatus := podStatus.Info[c.Name]
containerStatus.Ready = kl.readiness.IsReady(containerStatus)
podStatus.Info[c.Name] = containerStatus
for i, st := range podStatus.ContainerStatuses {
if st.Name == c.Name {
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()
hostIP, err := kl.GetHostIP()
if err != nil {
......
......@@ -166,8 +166,8 @@ func getPodName(name, namespace string) string {
func TestPodStatus(t *testing.T) {
fw := newServerTest()
expected := api.PodStatus{
Info: map[string]api.ContainerStatus{
"goodpod": {},
ContainerStatuses: []api.ContainerStatus{
{Name: "goodpod"},
},
}
fw.fakeKubelet.statusFunc = func(name string) (api.PodStatus, error) {
......
......@@ -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")
pod, err := c.Pods(ns).Get(podDescr.Name)
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))
// 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)
pod, err = c.Pods(ns).Get(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))
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))
......
......@@ -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 {
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
ci, ok := pod.Status.Info[contName]
ci, ok := api.GetContainerStatus(pod.Status.ContainerStatuses, contName)
if !ok {
Logf("No Status.Info for container %s in pod %s yet", contName, podName)
} else {
......
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