Merge pull request #12894 from nikhiljindal/PodCondition

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2015-09-18 18:59:51 -07:00
commit 5d67c2ce1f
9 changed files with 195 additions and 59 deletions

View File

@ -13137,6 +13137,18 @@
"status": { "status": {
"type": "string", "type": "string",
"description": "Status is the status of the condition. Can be True, False, Unknown. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions" "description": "Status is the status of the condition. Can be True, False, Unknown. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions"
},
"lastProbeTime": {
"type": "string"
},
"lastTransitionTime": {
"type": "string"
},
"reason": {
"type": "string"
},
"message": {
"type": "string"
} }
} }
}, },

View File

@ -1344,6 +1344,14 @@ func deepCopy_api_PodAttachOptions(in PodAttachOptions, out *PodAttachOptions, c
func deepCopy_api_PodCondition(in PodCondition, out *PodCondition, c *conversion.Cloner) error { func deepCopy_api_PodCondition(in PodCondition, out *PodCondition, c *conversion.Cloner) error {
out.Type = in.Type out.Type = in.Type
out.Status = in.Status out.Status = in.Status
if err := deepCopy_unversioned_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil {
return err
}
if err := deepCopy_unversioned_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil return nil
} }

View File

@ -68,10 +68,22 @@ func GetExistingContainerStatus(statuses []ContainerStatus, name string) Contain
// IsPodReady retruns true if a pod is ready; false otherwise. // IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReady(pod *Pod) bool { func IsPodReady(pod *Pod) bool {
for _, c := range pod.Status.Conditions { return IsPodReadyConditionTrue(pod.Status)
if c.Type == PodReady && c.Status == ConditionTrue { }
return true
// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReadyConditionTrue(status PodStatus) bool {
condition := GetPodReadyCondition(status)
return condition != nil && condition.Status == ConditionTrue
}
// Extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodReadyCondition(status PodStatus) *PodCondition {
for _, c := range status.Conditions {
if c.Type == PodReady {
return &c
} }
} }
return false return nil
} }

View File

@ -883,10 +883,13 @@ const (
PodReady PodConditionType = "Ready" PodReady PodConditionType = "Ready"
) )
// TODO: add LastTransitionTime, Reason, Message to match NodeCondition api.
type PodCondition struct { type PodCondition struct {
Type PodConditionType `json:"type"` Type PodConditionType `json:"type"`
Status ConditionStatus `json:"status"` Status ConditionStatus `json:"status"`
LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"`
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
} }
// RestartPolicy describes how the container should be restarted. // RestartPolicy describes how the container should be restarted.

View File

@ -1550,6 +1550,14 @@ func convert_api_PodCondition_To_v1_PodCondition(in *api.PodCondition, out *PodC
} }
out.Type = PodConditionType(in.Type) out.Type = PodConditionType(in.Type)
out.Status = ConditionStatus(in.Status) out.Status = ConditionStatus(in.Status)
if err := s.Convert(&in.LastProbeTime, &out.LastProbeTime, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTransitionTime, &out.LastTransitionTime, 0); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil return nil
} }
@ -3912,6 +3920,14 @@ func convert_v1_PodCondition_To_api_PodCondition(in *PodCondition, out *api.PodC
} }
out.Type = api.PodConditionType(in.Type) out.Type = api.PodConditionType(in.Type)
out.Status = api.ConditionStatus(in.Status) out.Status = api.ConditionStatus(in.Status)
if err := s.Convert(&in.LastProbeTime, &out.LastProbeTime, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTransitionTime, &out.LastTransitionTime, 0); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil return nil
} }

View File

@ -1364,6 +1364,14 @@ func deepCopy_v1_PodAttachOptions(in PodAttachOptions, out *PodAttachOptions, c
func deepCopy_v1_PodCondition(in PodCondition, out *PodCondition, c *conversion.Cloner) error { func deepCopy_v1_PodCondition(in PodCondition, out *PodCondition, c *conversion.Cloner) error {
out.Type = in.Type out.Type = in.Type
out.Status = in.Status out.Status = in.Status
if err := deepCopy_unversioned_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil {
return err
}
if err := deepCopy_unversioned_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil return nil
} }

View File

@ -1102,7 +1102,6 @@ const (
) )
// PodCondition contains details for the current condition of this pod. // PodCondition contains details for the current condition of this pod.
// TODO: add LastTransitionTime, Reason, Message to match NodeCondition api.
type PodCondition struct { type PodCondition struct {
// Type is the type of the condition. // Type is the type of the condition.
// Currently only Ready. // Currently only Ready.
@ -1112,6 +1111,14 @@ type PodCondition struct {
// Can be True, False, Unknown. // Can be True, False, Unknown.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions
Status ConditionStatus `json:"status"` Status ConditionStatus `json:"status"`
// Last time we probed the condition.
LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transitioned from one status to another.
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"`
// Unique, one-word, CamelCase reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Human-readable message indicating details about last transition.
Message string `json:"message,omitempty"`
} }
// RestartPolicy describes how the container should be restarted. // RestartPolicy describes how the container should be restarted.

View File

@ -2126,29 +2126,65 @@ func GetPhase(spec *api.PodSpec, info []api.ContainerStatus) api.PodPhase {
} }
} }
// getPodReadyCondition returns ready condition if all containers in a pod are ready, else it returns an notReady condition. func readyPodCondition(isPodReady bool, reason, message string, existingStatus *api.PodStatus) []api.PodCondition {
func getPodReadyCondition(spec *api.PodSpec, statuses []api.ContainerStatus) []api.PodCondition { currentTime := unversioned.Now()
ready := []api.PodCondition{{ condition := api.PodCondition{
Type: api.PodReady, Type: api.PodReady,
Status: api.ConditionTrue,
}}
notReady := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionFalse,
}}
if statuses == nil {
return notReady
} }
if isPodReady {
condition.Status = api.ConditionTrue
} else {
condition.Status = api.ConditionFalse
}
condition.LastProbeTime = currentTime
condition.Reason = reason
condition.Message = message
if existingStatus != nil {
if api.IsPodReadyConditionTrue(*existingStatus) != isPodReady {
// condition has transitioned, update transition time.
condition.LastTransitionTime = currentTime
} else {
// retain the existing transition time.
existingCondition := api.GetPodReadyCondition(*existingStatus)
if existingCondition != nil {
condition.LastTransitionTime = existingCondition.LastTransitionTime
}
}
}
return []api.PodCondition{condition}
}
// getPodReadyCondition returns ready condition if all containers in a pod are ready, else it returns an unready condition.
func getPodReadyCondition(spec *api.PodSpec, containerStatuses []api.ContainerStatus, existingStatus *api.PodStatus) []api.PodCondition {
// Find if all containers are ready or not.
if containerStatuses == nil {
return readyPodCondition(false, "UnknownContainerStatuses", "", existingStatus)
}
unknownContainers := []string{}
unreadyContainers := []string{}
for _, container := range spec.Containers { for _, container := range spec.Containers {
if containerStatus, ok := api.GetContainerStatus(statuses, container.Name); ok { if containerStatus, ok := api.GetContainerStatus(containerStatuses, container.Name); ok {
if !containerStatus.Ready { if !containerStatus.Ready {
return notReady unreadyContainers = append(unreadyContainers, container.Name)
} }
} else { } else {
return notReady unknownContainers = append(unknownContainers, container.Name)
} }
} }
return ready unreadyMessages := []string{}
if len(unknownContainers) > 0 {
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unknown status: %s", unknownContainers))
}
if len(unreadyContainers) > 0 {
unreadyMessages = append(unreadyMessages, fmt.Sprintf("containers with unready status: %s", unreadyContainers))
}
unreadyMessage := strings.Join(unreadyMessages, ", ")
if unreadyMessage != "" {
// return unready status.
return readyPodCondition(false, fmt.Sprint("ContainersNotReady"), unreadyMessage, existingStatus)
}
// return ready status.
return readyPodCondition(true, "", "", existingStatus)
} }
// By passing the pod directly, this method avoids pod lookup, which requires // By passing the pod directly, this method avoids pod lookup, which requires
@ -2162,8 +2198,8 @@ func (kl *Kubelet) generatePodStatus(pod *api.Pod) (api.PodStatus, error) {
podFullName := kubecontainer.GetPodFullName(pod) podFullName := kubecontainer.GetPodFullName(pod)
glog.V(3).Infof("Generating status for %q", podFullName) glog.V(3).Infof("Generating status for %q", podFullName)
existingStatus, hasExistingStatus := kl.statusManager.GetPodStatus(pod.UID)
if existingStatus, ok := kl.statusManager.GetPodStatus(pod.UID); ok { if hasExistingStatus {
// This is a hacky fix to ensure container restart counts increment // This is a hacky fix to ensure container restart counts increment
// monotonically. Normally, we should not modify given pod. In this // monotonically. Normally, we should not modify given pod. In this
// case, we check if there are cached status for this pod, and update // case, we check if there are cached status for this pod, and update
@ -2215,8 +2251,11 @@ func (kl *Kubelet) generatePodStatus(pod *api.Pod) (api.PodStatus, error) {
} }
} }
} }
if hasExistingStatus {
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses)...) podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses, &existingStatus)...)
} else {
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses, nil)...)
}
if !kl.standaloneMode { if !kl.standaloneMode {
hostIP, err := kl.nodeManager.GetHostIP() hostIP, err := kl.nodeManager.GetHostIP()

View File

@ -1732,30 +1732,47 @@ func getNotReadyStatus(cName string) api.ContainerStatus {
Ready: false, Ready: false,
} }
} }
func getReadyCondition(status api.ConditionStatus, transitionTime unversioned.Time, reason, message string) []api.PodCondition {
return []api.PodCondition{{
Type: api.PodReady,
Status: status,
LastTransitionTime: transitionTime,
Reason: reason,
Message: message,
}}
}
func TestGetPodReadyCondition(t *testing.T) { func TestGetPodReadyCondition(t *testing.T) {
ready := []api.PodCondition{{ transitionTime := unversioned.Now()
Type: api.PodReady,
Status: api.ConditionTrue,
}}
notReady := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionFalse,
}}
tests := []struct { tests := []struct {
spec *api.PodSpec spec *api.PodSpec
info []api.ContainerStatus containerStatuses []api.ContainerStatus
existingStatus *api.PodStatus
expected []api.PodCondition expected []api.PodCondition
clearTimestamp bool
}{ }{
{ {
spec: nil, spec: nil,
info: nil, containerStatuses: nil,
expected: notReady, existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "UnknownContainerStatuses", ""),
clearTimestamp: true,
},
{
spec: nil,
containerStatuses: nil,
existingStatus: &api.PodStatus{
Conditions: getReadyCondition(api.ConditionFalse, transitionTime, "", ""),
},
expected: getReadyCondition(api.ConditionFalse, transitionTime, "UnknownContainerStatuses", ""),
clearTimestamp: false,
}, },
{ {
spec: &api.PodSpec{}, spec: &api.PodSpec{},
info: []api.ContainerStatus{}, containerStatuses: []api.ContainerStatus{},
expected: ready, existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
}, },
{ {
spec: &api.PodSpec{ spec: &api.PodSpec{
@ -1763,8 +1780,10 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"}, {Name: "1234"},
}, },
}, },
info: []api.ContainerStatus{}, containerStatuses: []api.ContainerStatus{},
expected: notReady, existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unknown status: [1234]"),
clearTimestamp: true,
}, },
{ {
spec: &api.PodSpec{ spec: &api.PodSpec{
@ -1772,10 +1791,12 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"}, {Name: "1234"},
}, },
}, },
info: []api.ContainerStatus{ containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"), getReadyStatus("1234"),
}, },
expected: ready, existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
}, },
{ {
spec: &api.PodSpec{ spec: &api.PodSpec{
@ -1784,11 +1805,13 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: []api.ContainerStatus{ containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"), getReadyStatus("1234"),
getReadyStatus("5678"), getReadyStatus("5678"),
}, },
expected: ready, existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
}, },
{ {
spec: &api.PodSpec{ spec: &api.PodSpec{
@ -1797,10 +1820,12 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: []api.ContainerStatus{ containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"), getReadyStatus("1234"),
}, },
expected: notReady, existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unknown status: [5678]"),
clearTimestamp: true,
}, },
{ {
spec: &api.PodSpec{ spec: &api.PodSpec{
@ -1809,16 +1834,22 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"}, {Name: "5678"},
}, },
}, },
info: []api.ContainerStatus{ containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"), getReadyStatus("1234"),
getNotReadyStatus("5678"), getNotReadyStatus("5678"),
}, },
expected: notReady, expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unready status: [5678]"),
clearTimestamp: true,
}, },
} }
for i, test := range tests { for i, test := range tests {
condition := getPodReadyCondition(test.spec, test.info) condition := getPodReadyCondition(test.spec, test.containerStatuses, test.existingStatus)
if test.clearTimestamp {
condition[0].LastTransitionTime = transitionTime
test.expected[0].LastTransitionTime = transitionTime
}
condition[0].LastProbeTime = unversioned.Time{}
if !reflect.DeepEqual(condition, test.expected) { if !reflect.DeepEqual(condition, test.expected) {
t.Errorf("On test case %v, expected:\n%+v\ngot\n%+v\n", i, test.expected, condition) t.Errorf("On test case %v, expected:\n%+v\ngot\n%+v\n", i, test.expected, condition)
} }