Adding more fields to PodCondition

This commit is contained in:
nikhiljindal 2015-08-18 20:09:36 -07:00
parent 0f8cc8926f
commit 6523ec142b
9 changed files with 195 additions and 59 deletions

View File

@ -13137,6 +13137,18 @@
"status": {
"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"
},
"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 {
out.Type = in.Type
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
}

View File

@ -68,10 +68,22 @@ func GetExistingContainerStatus(statuses []ContainerStatus, name string) Contain
// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReady(pod *Pod) bool {
for _, c := range pod.Status.Conditions {
if c.Type == PodReady && c.Status == ConditionTrue {
return true
return IsPodReadyConditionTrue(pod.Status)
}
// 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"
)
// TODO: add LastTransitionTime, Reason, Message to match NodeCondition api.
type PodCondition struct {
Type PodConditionType `json:"type"`
Status ConditionStatus `json:"status"`
Type PodConditionType `json:"type"`
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.

View File

@ -1550,6 +1550,14 @@ func convert_api_PodCondition_To_v1_PodCondition(in *api.PodCondition, out *PodC
}
out.Type = PodConditionType(in.Type)
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
}
@ -3912,6 +3920,14 @@ func convert_v1_PodCondition_To_api_PodCondition(in *PodCondition, out *api.PodC
}
out.Type = api.PodConditionType(in.Type)
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
}

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 {
out.Type = in.Type
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
}

View File

@ -1102,7 +1102,6 @@ const (
)
// PodCondition contains details for the current condition of this pod.
// TODO: add LastTransitionTime, Reason, Message to match NodeCondition api.
type PodCondition struct {
// Type is the type of the condition.
// Currently only Ready.
@ -1112,6 +1111,14 @@ type PodCondition struct {
// Can be True, False, Unknown.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions
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.

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 getPodReadyCondition(spec *api.PodSpec, statuses []api.ContainerStatus) []api.PodCondition {
ready := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionTrue,
}}
notReady := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionFalse,
}}
if statuses == nil {
return notReady
func readyPodCondition(isPodReady bool, reason, message string, existingStatus *api.PodStatus) []api.PodCondition {
currentTime := unversioned.Now()
condition := api.PodCondition{
Type: api.PodReady,
}
for _, container := range spec.Containers {
if containerStatus, ok := api.GetContainerStatus(statuses, container.Name); ok {
if !containerStatus.Ready {
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 {
return notReady
// retain the existing transition time.
existingCondition := api.GetPodReadyCondition(*existingStatus)
if existingCondition != nil {
condition.LastTransitionTime = existingCondition.LastTransitionTime
}
}
}
return ready
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 {
if containerStatus, ok := api.GetContainerStatus(containerStatuses, container.Name); ok {
if !containerStatus.Ready {
unreadyContainers = append(unreadyContainers, container.Name)
}
} else {
unknownContainers = append(unknownContainers, container.Name)
}
}
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
@ -2162,8 +2198,8 @@ func (kl *Kubelet) generatePodStatus(pod *api.Pod) (api.PodStatus, error) {
podFullName := kubecontainer.GetPodFullName(pod)
glog.V(3).Infof("Generating status for %q", podFullName)
if existingStatus, ok := kl.statusManager.GetPodStatus(pod.UID); ok {
existingStatus, hasExistingStatus := kl.statusManager.GetPodStatus(pod.UID)
if hasExistingStatus {
// This is a hacky fix to ensure container restart counts increment
// monotonically. Normally, we should not modify given pod. In this
// 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) {
}
}
}
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses)...)
if hasExistingStatus {
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses, &existingStatus)...)
} else {
podStatus.Conditions = append(podStatus.Conditions, getPodReadyCondition(spec, podStatus.ContainerStatuses, nil)...)
}
if !kl.standaloneMode {
hostIP, err := kl.nodeManager.GetHostIP()

View File

@ -1732,30 +1732,47 @@ func getNotReadyStatus(cName string) api.ContainerStatus {
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) {
ready := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionTrue,
}}
notReady := []api.PodCondition{{
Type: api.PodReady,
Status: api.ConditionFalse,
}}
transitionTime := unversioned.Now()
tests := []struct {
spec *api.PodSpec
info []api.ContainerStatus
expected []api.PodCondition
spec *api.PodSpec
containerStatuses []api.ContainerStatus
existingStatus *api.PodStatus
expected []api.PodCondition
clearTimestamp bool
}{
{
spec: nil,
info: nil,
expected: notReady,
spec: nil,
containerStatuses: nil,
existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "UnknownContainerStatuses", ""),
clearTimestamp: true,
},
{
spec: &api.PodSpec{},
info: []api.ContainerStatus{},
expected: ready,
spec: nil,
containerStatuses: nil,
existingStatus: &api.PodStatus{
Conditions: getReadyCondition(api.ConditionFalse, transitionTime, "", ""),
},
expected: getReadyCondition(api.ConditionFalse, transitionTime, "UnknownContainerStatuses", ""),
clearTimestamp: false,
},
{
spec: &api.PodSpec{},
containerStatuses: []api.ContainerStatus{},
existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
},
{
spec: &api.PodSpec{
@ -1763,8 +1780,10 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"},
},
},
info: []api.ContainerStatus{},
expected: notReady,
containerStatuses: []api.ContainerStatus{},
existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unknown status: [1234]"),
clearTimestamp: true,
},
{
spec: &api.PodSpec{
@ -1772,10 +1791,12 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "1234"},
},
},
info: []api.ContainerStatus{
containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"),
},
expected: ready,
existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
},
{
spec: &api.PodSpec{
@ -1784,11 +1805,13 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"},
},
},
info: []api.ContainerStatus{
containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"),
getReadyStatus("5678"),
},
expected: ready,
existingStatus: nil,
expected: getReadyCondition(api.ConditionTrue, transitionTime, "", ""),
clearTimestamp: true,
},
{
spec: &api.PodSpec{
@ -1797,10 +1820,12 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"},
},
},
info: []api.ContainerStatus{
containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"),
},
expected: notReady,
existingStatus: nil,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unknown status: [5678]"),
clearTimestamp: true,
},
{
spec: &api.PodSpec{
@ -1809,16 +1834,22 @@ func TestGetPodReadyCondition(t *testing.T) {
{Name: "5678"},
},
},
info: []api.ContainerStatus{
containerStatuses: []api.ContainerStatus{
getReadyStatus("1234"),
getNotReadyStatus("5678"),
},
expected: notReady,
expected: getReadyCondition(api.ConditionFalse, transitionTime, "ContainersNotReady", "containers with unready status: [5678]"),
clearTimestamp: true,
},
}
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) {
t.Errorf("On test case %v, expected:\n%+v\ngot\n%+v\n", i, test.expected, condition)
}