Commit de0438a5 authored by Yu-Ju Hong's avatar Yu-Ju Hong

kuberuntime: add unit tests for container status population

Also refactor a little bit to make the function more testable.
parent 7c75f5c5
...@@ -385,51 +385,58 @@ func (m *kubeGenericRuntimeManager) getPodContainerStatuses(uid kubetypes.UID, n ...@@ -385,51 +385,58 @@ func (m *kubeGenericRuntimeManager) getPodContainerStatuses(uid kubetypes.UID, n
glog.Errorf("ContainerStatus for %s error: %v", c.Id, err) glog.Errorf("ContainerStatus for %s error: %v", c.Id, err)
return nil, err return nil, err
} }
cStatus := toKubeContainerStatus(status, m.runtimeName)
if status.State == runtimeapi.ContainerState_CONTAINER_EXITED {
// Populate the termination message if needed.
annotatedInfo := getContainerInfoFromAnnotations(status.Annotations)
labeledInfo := getContainerInfoFromLabels(status.Labels)
fallbackToLogs := annotatedInfo.TerminationMessagePolicy == v1.TerminationMessageFallbackToLogsOnError && (cStatus.ExitCode != 0 || cStatus.Reason == "OOMKilled")
tMessage, checkLogs := getTerminationMessage(status, annotatedInfo.TerminationMessagePath, fallbackToLogs)
if checkLogs {
path := buildFullContainerLogsPath(uid, labeledInfo.ContainerName, annotatedInfo.RestartCount)
tMessage = m.readLastStringFromContainerLogs(path)
}
// Use the termination message written by the application is not empty
if len(tMessage) != 0 {
cStatus.Message = tMessage
}
}
statuses[i] = cStatus
}
annotatedInfo := getContainerInfoFromAnnotations(c.Annotations) sort.Sort(containerStatusByCreated(statuses))
labeledInfo := getContainerInfoFromLabels(c.Labels) return statuses, nil
}
func toKubeContainerStatus(status *runtimeapi.ContainerStatus, runtimeName string) *kubecontainer.ContainerStatus {
annotatedInfo := getContainerInfoFromAnnotations(status.Annotations)
labeledInfo := getContainerInfoFromLabels(status.Labels)
cStatus := &kubecontainer.ContainerStatus{ cStatus := &kubecontainer.ContainerStatus{
ID: kubecontainer.ContainerID{ ID: kubecontainer.ContainerID{
Type: m.runtimeName, Type: runtimeName,
ID: c.Id, ID: status.Id,
}, },
Name: labeledInfo.ContainerName, Name: labeledInfo.ContainerName,
Image: status.Image.Image, Image: status.Image.Image,
ImageID: status.ImageRef, ImageID: status.ImageRef,
Hash: annotatedInfo.Hash, Hash: annotatedInfo.Hash,
RestartCount: annotatedInfo.RestartCount, RestartCount: annotatedInfo.RestartCount,
State: toKubeContainerState(c.State), State: toKubeContainerState(status.State),
CreatedAt: time.Unix(0, status.CreatedAt), CreatedAt: time.Unix(0, status.CreatedAt),
} }
if c.State != runtimeapi.ContainerState_CONTAINER_CREATED { if status.State != runtimeapi.ContainerState_CONTAINER_CREATED {
// If container is not in the created state, we have tried and // If container is not in the created state, we have tried and
// started the container. Set the StartedAt time. // started the container. Set the StartedAt time.
cStatus.StartedAt = time.Unix(0, status.StartedAt) cStatus.StartedAt = time.Unix(0, status.StartedAt)
} }
if c.State == runtimeapi.ContainerState_CONTAINER_EXITED { if status.State == runtimeapi.ContainerState_CONTAINER_EXITED {
cStatus.Reason = status.Reason cStatus.Reason = status.Reason
cStatus.Message = status.Message cStatus.Message = status.Message
cStatus.ExitCode = int(status.ExitCode) cStatus.ExitCode = int(status.ExitCode)
cStatus.FinishedAt = time.Unix(0, status.FinishedAt) cStatus.FinishedAt = time.Unix(0, status.FinishedAt)
fallbackToLogs := annotatedInfo.TerminationMessagePolicy == v1.TerminationMessageFallbackToLogsOnError && (cStatus.ExitCode != 0 || cStatus.Reason == "OOMKilled")
tMessage, checkLogs := getTerminationMessage(status, annotatedInfo.TerminationMessagePath, fallbackToLogs)
if checkLogs {
path := buildFullContainerLogsPath(uid, labeledInfo.ContainerName, annotatedInfo.RestartCount)
tMessage = m.readLastStringFromContainerLogs(path)
} }
// Use the termination message written by the application is not empty return cStatus
if len(tMessage) != 0 {
cStatus.Message = tMessage
}
}
statuses[i] = cStatus
}
sort.Sort(containerStatusByCreated(statuses))
return statuses, nil
} }
// generateContainerEvent generates an event for the container. // generateContainerEvent generates an event for the container.
......
...@@ -19,11 +19,14 @@ package kuberuntime ...@@ -19,11 +19,14 @@ package kuberuntime
import ( import (
"path/filepath" "path/filepath"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
) )
...@@ -65,3 +68,100 @@ func TestRemoveContainer(t *testing.T) { ...@@ -65,3 +68,100 @@ func TestRemoveContainer(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, containers) assert.Empty(t, containers)
} }
// TestToKubeContainerStatus tests the converting the CRI container status to
// the internal type (i.e., toKubeContainerStatus()) for containers in
// different states.
func TestToKubeContainerStatus(t *testing.T) {
cid := &kubecontainer.ContainerID{Type: "testRuntime", ID: "dummyid"}
meta := &runtimeapi.ContainerMetadata{Name: "cname", Attempt: 3}
imageSpec := &runtimeapi.ImageSpec{Image: "fimage"}
var (
createdAt int64 = 327
startedAt int64 = 999
finishedAt int64 = 1278
)
for desc, test := range map[string]struct {
input *runtimeapi.ContainerStatus
expected *kubecontainer.ContainerStatus
}{
"created container": {
input: &runtimeapi.ContainerStatus{
Id: cid.ID,
Metadata: meta,
Image: imageSpec,
State: runtimeapi.ContainerState_CONTAINER_CREATED,
CreatedAt: createdAt,
},
expected: &kubecontainer.ContainerStatus{
ID: *cid,
Image: imageSpec.Image,
State: kubecontainer.ContainerStateCreated,
CreatedAt: time.Unix(0, createdAt),
},
},
"running container": {
input: &runtimeapi.ContainerStatus{
Id: cid.ID,
Metadata: meta,
Image: imageSpec,
State: runtimeapi.ContainerState_CONTAINER_RUNNING,
CreatedAt: createdAt,
StartedAt: startedAt,
},
expected: &kubecontainer.ContainerStatus{
ID: *cid,
Image: imageSpec.Image,
State: kubecontainer.ContainerStateRunning,
CreatedAt: time.Unix(0, createdAt),
StartedAt: time.Unix(0, startedAt),
},
},
"exited container": {
input: &runtimeapi.ContainerStatus{
Id: cid.ID,
Metadata: meta,
Image: imageSpec,
State: runtimeapi.ContainerState_CONTAINER_EXITED,
CreatedAt: createdAt,
StartedAt: startedAt,
FinishedAt: finishedAt,
ExitCode: int32(121),
Reason: "GotKilled",
Message: "The container was killed",
},
expected: &kubecontainer.ContainerStatus{
ID: *cid,
Image: imageSpec.Image,
State: kubecontainer.ContainerStateExited,
CreatedAt: time.Unix(0, createdAt),
StartedAt: time.Unix(0, startedAt),
FinishedAt: time.Unix(0, finishedAt),
ExitCode: 121,
Reason: "GotKilled",
Message: "The container was killed",
},
},
"unknown container": {
input: &runtimeapi.ContainerStatus{
Id: cid.ID,
Metadata: meta,
Image: imageSpec,
State: runtimeapi.ContainerState_CONTAINER_UNKNOWN,
CreatedAt: createdAt,
StartedAt: startedAt,
},
expected: &kubecontainer.ContainerStatus{
ID: *cid,
Image: imageSpec.Image,
State: kubecontainer.ContainerStateUnknown,
CreatedAt: time.Unix(0, createdAt),
StartedAt: time.Unix(0, startedAt),
},
},
} {
actual := toKubeContainerStatus(test.input, cid.Type)
assert.Equal(t, test.expected, actual, desc)
}
}
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