mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 02:09:56 +00:00
Put restart count into docker label
This commit is contained in:
parent
03ee15a460
commit
67a7633202
@ -147,6 +147,9 @@ func (f *FakeDockerClient) ListContainers(options docker.ListContainersOptions)
|
|||||||
f.called = append(f.called, "list")
|
f.called = append(f.called, "list")
|
||||||
err := f.popError("list")
|
err := f.popError("list")
|
||||||
if options.All {
|
if options.All {
|
||||||
|
// Althought the container is not sorted, but the container with the same name should be in order,
|
||||||
|
// that is enough for us now.
|
||||||
|
// TODO (random-liu) Is a fully sorted array needed?
|
||||||
return append(f.ContainerList, f.ExitedContainerList...), err
|
return append(f.ContainerList, f.ExitedContainerList...), err
|
||||||
}
|
}
|
||||||
return append([]docker.APIContainers{}, f.ContainerList...), err
|
return append([]docker.APIContainers{}, f.ContainerList...), err
|
||||||
@ -204,7 +207,10 @@ func (f *FakeDockerClient) CreateContainer(c docker.CreateContainerOptions) (*do
|
|||||||
// This is not a very good fake. We'll just add this container's name to the list.
|
// This is not a very good fake. We'll just add this container's name to the list.
|
||||||
// Docker likes to add a '/', so copy that behavior.
|
// Docker likes to add a '/', so copy that behavior.
|
||||||
name := "/" + c.Name
|
name := "/" + c.Name
|
||||||
f.ContainerList = append(f.ContainerList, docker.APIContainers{ID: name, Names: []string{name}, Image: c.Config.Image})
|
// The newest container should be in front, because we assume so in GetPodStatus()
|
||||||
|
f.ContainerList = append([]docker.APIContainers{
|
||||||
|
{ID: name, Names: []string{name}, Image: c.Config.Image, Labels: c.Config.Labels},
|
||||||
|
}, f.ContainerList...)
|
||||||
container := docker.Container{ID: name, Name: name, Config: c.Config}
|
container := docker.Container{ID: name, Name: name, Config: c.Config}
|
||||||
if f.ContainerMap != nil {
|
if f.ContainerMap != nil {
|
||||||
containerCopy := container
|
containerCopy := container
|
||||||
@ -266,7 +272,8 @@ func (f *FakeDockerClient) StopContainer(id string, timeout uint) error {
|
|||||||
var newList []docker.APIContainers
|
var newList []docker.APIContainers
|
||||||
for _, container := range f.ContainerList {
|
for _, container := range f.ContainerList {
|
||||||
if container.ID == id {
|
if container.ID == id {
|
||||||
f.ExitedContainerList = append(f.ExitedContainerList, container)
|
// The newest exited container should be in front. Because we assume so in GetPodStatus()
|
||||||
|
f.ExitedContainerList = append([]docker.APIContainers{container}, f.ExitedContainerList...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newList = append(newList, container)
|
newList = append(newList, container)
|
||||||
|
@ -73,6 +73,7 @@ const (
|
|||||||
kubernetesPodLabel = "io.kubernetes.pod.data"
|
kubernetesPodLabel = "io.kubernetes.pod.data"
|
||||||
kubernetesTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
|
kubernetesTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
|
||||||
kubernetesContainerLabel = "io.kubernetes.container.name"
|
kubernetesContainerLabel = "io.kubernetes.container.name"
|
||||||
|
kubernetesContainerRestartCountLabel = "io.kubernetes.container.restartCount"
|
||||||
|
|
||||||
DockerNetnsFmt = "/proc/%v/ns/net"
|
DockerNetnsFmt = "/proc/%v/ns/net"
|
||||||
)
|
)
|
||||||
@ -368,11 +369,27 @@ func (dm *DockerManager) inspectContainer(dockerID, containerName, tPath string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
glog.V(4).Infof("Container inspect result: %+v", *inspectResult)
|
glog.V(4).Infof("Container inspect result: %+v", *inspectResult)
|
||||||
|
|
||||||
|
// Get restartCount from docker label, and add into the result.
|
||||||
|
// If there is no restart count label in an container:
|
||||||
|
// 1. It is an infraContainer, it will never use restart count.
|
||||||
|
// 2. It is an old container or an invalid container, we just set restart count to 0 now.
|
||||||
|
var restartCount int
|
||||||
|
if restartCountString, found := inspectResult.Config.Labels[kubernetesContainerRestartCountLabel]; found {
|
||||||
|
restartCount, err = strconv.Atoi(restartCountString)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error parsing restart count string %s for container %s: %v,", restartCountString, dockerID, err)
|
||||||
|
// Just set restartCount to 0 to handle this abnormal case
|
||||||
|
restartCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.status = api.ContainerStatus{
|
result.status = api.ContainerStatus{
|
||||||
Name: containerName,
|
Name: containerName,
|
||||||
Image: inspectResult.Config.Image,
|
RestartCount: restartCount,
|
||||||
ImageID: DockerPrefix + inspectResult.Image,
|
Image: inspectResult.Config.Image,
|
||||||
ContainerID: DockerPrefix + dockerID,
|
ImageID: DockerPrefix + inspectResult.Image,
|
||||||
|
ContainerID: DockerPrefix + dockerID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if inspectResult.State.Running {
|
if inspectResult.State.Running {
|
||||||
@ -431,23 +448,21 @@ func (dm *DockerManager) inspectContainer(dockerID, containerName, tPath string,
|
|||||||
// GetPodStatus returns docker related status for all containers in the pod as
|
// GetPodStatus returns docker related status for all containers in the pod as
|
||||||
// well as the infrastructure container.
|
// well as the infrastructure container.
|
||||||
func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
|
func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
|
||||||
|
// Now we retain restart count of container as a docker label. Each time a container
|
||||||
|
// restarts, pod will read the restart count from the latest dead container, increment
|
||||||
|
// it to get the new restart count, and then add a label with the new restart count on
|
||||||
|
// the newly started container.
|
||||||
|
// However, there are some limitations of this method:
|
||||||
|
// 1. When all dead containers were garbage collected, the container status could
|
||||||
|
// not get the historical value and would be *inaccurate*. Fortunately, the chance
|
||||||
|
// is really slim.
|
||||||
|
// 2. When working with old version containers which have no restart count label,
|
||||||
|
// we can only assume their restart count is 0.
|
||||||
|
// Anyhow, we only promised "best-effort" restart count reporting, we can just ignore
|
||||||
|
// these limitations now.
|
||||||
podFullName := kubecontainer.GetPodFullName(pod)
|
podFullName := kubecontainer.GetPodFullName(pod)
|
||||||
uid := pod.UID
|
uid := pod.UID
|
||||||
manifest := pod.Spec
|
manifest := pod.Spec
|
||||||
|
|
||||||
oldStatuses := make(map[string]api.ContainerStatus, len(pod.Spec.Containers))
|
|
||||||
lastObservedTime := make(map[string]unversioned.Time, len(pod.Spec.Containers))
|
|
||||||
// Record the last time we observed a container termination.
|
|
||||||
for _, status := range pod.Status.ContainerStatuses {
|
|
||||||
oldStatuses[status.Name] = status
|
|
||||||
if status.LastTerminationState.Terminated != nil {
|
|
||||||
timestamp, ok := lastObservedTime[status.Name]
|
|
||||||
if !ok || timestamp.Before(status.LastTerminationState.Terminated.FinishedAt) {
|
|
||||||
lastObservedTime[status.Name] = status.LastTerminationState.Terminated.FinishedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var podStatus api.PodStatus
|
var podStatus api.PodStatus
|
||||||
statuses := make(map[string]*api.ContainerStatus, len(pod.Spec.Containers))
|
statuses := make(map[string]*api.ContainerStatus, len(pod.Spec.Containers))
|
||||||
|
|
||||||
@ -467,6 +482,7 @@ func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
|
|||||||
// the statuses. We assume docker returns a list of containers sorted in
|
// the statuses. We assume docker returns a list of containers sorted in
|
||||||
// reverse by time.
|
// reverse by time.
|
||||||
for _, value := range containers {
|
for _, value := range containers {
|
||||||
|
// TODO (random-liu) Filter by docker label later
|
||||||
if len(value.Names) == 0 {
|
if len(value.Names) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -490,65 +506,41 @@ func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var terminationState *api.ContainerState = nil
|
|
||||||
// Inspect the container.
|
// Inspect the container.
|
||||||
result := dm.inspectContainer(value.ID, dockerContainerName, terminationMessagePath, pod)
|
result := dm.inspectContainer(value.ID, dockerContainerName, terminationMessagePath, pod)
|
||||||
if result.err != nil {
|
if result.err != nil {
|
||||||
return nil, result.err
|
return nil, result.err
|
||||||
} else if result.status.State.Terminated != nil {
|
|
||||||
terminationState = &result.status.State
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if containerStatus, found := statuses[dockerContainerName]; found {
|
if containerStatus, found := statuses[dockerContainerName]; found {
|
||||||
if containerStatus.LastTerminationState.Terminated == nil && terminationState != nil {
|
// There should be no alive containers with the same name. Just in case.
|
||||||
// Populate the last termination state.
|
if result.status.State.Terminated == nil {
|
||||||
containerStatus.LastTerminationState = *terminationState
|
|
||||||
}
|
|
||||||
if terminationState == nil {
|
|
||||||
// Not a dead container.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Only count dead containers terminated after last time we observed,
|
containerStatus.LastTerminationState = result.status.State
|
||||||
lastObservedTime, ok := lastObservedTime[dockerContainerName]
|
// Got the last termination state, we do not need to care about the other containers any more
|
||||||
if !ok || terminationState.Terminated.FinishedAt.After(lastObservedTime.Time) {
|
containerDone.Insert(dockerContainerName)
|
||||||
containerStatus.RestartCount += 1
|
|
||||||
} else {
|
|
||||||
// The container finished before the last observation. No
|
|
||||||
// need to examine/count the older containers. Mark the
|
|
||||||
// container name as done.
|
|
||||||
containerDone.Insert(dockerContainerName)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if dockerContainerName == PodInfraContainerName {
|
if dockerContainerName == PodInfraContainerName {
|
||||||
// Found network container
|
// Found network container
|
||||||
if result.status.State.Running != nil {
|
if result.status.State.Running != nil {
|
||||||
podStatus.PodIP = result.ip
|
podStatus.PodIP = result.ip
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add user container information.
|
|
||||||
if oldStatus, found := oldStatuses[dockerContainerName]; found {
|
|
||||||
// Use the last observed restart count if it's available.
|
|
||||||
result.status.RestartCount = oldStatus.RestartCount
|
|
||||||
}
|
|
||||||
statuses[dockerContainerName] = &result.status
|
statuses[dockerContainerName] = &result.status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the containers for which we cannot find any associated active or dead docker containers or are in restart backoff
|
// Handle the containers for which we cannot find any associated active or dead docker containers or are in restart backoff
|
||||||
|
// Fetch old containers statuses from old pod status.
|
||||||
|
oldStatuses := make(map[string]api.ContainerStatus, len(manifest.Containers))
|
||||||
|
for _, status := range pod.Status.ContainerStatuses {
|
||||||
|
oldStatuses[status.Name] = status
|
||||||
|
}
|
||||||
for _, container := range manifest.Containers {
|
for _, container := range manifest.Containers {
|
||||||
if containerStatus, found := statuses[container.Name]; found {
|
if containerStatus, found := statuses[container.Name]; found {
|
||||||
reasonInfo, ok := dm.reasonCache.Get(uid, container.Name)
|
reasonInfo, ok := dm.reasonCache.Get(uid, container.Name)
|
||||||
if ok && reasonInfo.reason == kubecontainer.ErrCrashLoopBackOff.Error() {
|
if ok && reasonInfo.reason == kubecontainer.ErrCrashLoopBackOff.Error() {
|
||||||
// We need to increment the restart count if we are going to
|
|
||||||
// move the current state to last terminated state.
|
|
||||||
if containerStatus.State.Terminated != nil {
|
|
||||||
lastObservedTime, ok := lastObservedTime[container.Name]
|
|
||||||
if !ok || containerStatus.State.Terminated.FinishedAt.After(lastObservedTime.Time) {
|
|
||||||
containerStatus.RestartCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
containerStatus.LastTerminationState = containerStatus.State
|
containerStatus.LastTerminationState = containerStatus.State
|
||||||
containerStatus.State = api.ContainerState{
|
containerStatus.State = api.ContainerState{
|
||||||
Waiting: &api.ContainerStateWaiting{
|
Waiting: &api.ContainerStateWaiting{
|
||||||
@ -690,7 +682,8 @@ func (dm *DockerManager) runContainer(
|
|||||||
netMode string,
|
netMode string,
|
||||||
ipcMode string,
|
ipcMode string,
|
||||||
utsMode string,
|
utsMode string,
|
||||||
pidMode string) (kubecontainer.ContainerID, error) {
|
pidMode string,
|
||||||
|
labels map[string]string) (kubecontainer.ContainerID, error) {
|
||||||
|
|
||||||
dockerName := KubeletContainerName{
|
dockerName := KubeletContainerName{
|
||||||
PodFullName: kubecontainer.GetPodFullName(pod),
|
PodFullName: kubecontainer.GetPodFullName(pod),
|
||||||
@ -712,9 +705,11 @@ func (dm *DockerManager) runContainer(
|
|||||||
// termination information like the termination grace period and the pre stop hooks.
|
// termination information like the termination grace period and the pre stop hooks.
|
||||||
// TODO: keep these labels up to date if the pod changes
|
// TODO: keep these labels up to date if the pod changes
|
||||||
namespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
|
namespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
|
||||||
labels := map[string]string{
|
// Just in case. If there is no label, just pass nil. An empty map will be created here.
|
||||||
kubernetesNameLabel: namespacedName.String(),
|
if labels == nil {
|
||||||
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
labels[kubernetesNameLabel] = namespacedName.String()
|
||||||
if pod.Spec.TerminationGracePeriodSeconds != nil {
|
if pod.Spec.TerminationGracePeriodSeconds != nil {
|
||||||
labels[kubernetesTerminationGracePeriodLabel] = strconv.FormatInt(*pod.Spec.TerminationGracePeriodSeconds, 10)
|
labels[kubernetesTerminationGracePeriodLabel] = strconv.FormatInt(*pod.Spec.TerminationGracePeriodSeconds, 10)
|
||||||
}
|
}
|
||||||
@ -1500,7 +1495,9 @@ func containerAndPodFromLabels(inspect *docker.Container) (pod *api.Pod, contain
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run a single container from a pod. Returns the docker container ID
|
// Run a single container from a pod. Returns the docker container ID
|
||||||
func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Container, netMode, ipcMode, pidMode string) (kubecontainer.ContainerID, error) {
|
// If do not need to pass labels, just pass nil.
|
||||||
|
// TODO (random-liu) Just add labels directly now, maybe should make some abstraction.
|
||||||
|
func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Container, netMode, ipcMode, pidMode string, labels map[string]string) (kubecontainer.ContainerID, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
metrics.ContainerManagerLatency.WithLabelValues("runContainerInPod").Observe(metrics.SinceInMicroseconds(start))
|
metrics.ContainerManagerLatency.WithLabelValues("runContainerInPod").Observe(metrics.SinceInMicroseconds(start))
|
||||||
@ -1520,7 +1517,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
|
|||||||
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork {
|
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork {
|
||||||
utsMode = "host"
|
utsMode = "host"
|
||||||
}
|
}
|
||||||
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode)
|
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode, labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kubecontainer.ContainerID{}, err
|
return kubecontainer.ContainerID{}, err
|
||||||
}
|
}
|
||||||
@ -1657,7 +1654,8 @@ func (dm *DockerManager) createPodInfraContainer(pod *api.Pod) (kubetypes.Docker
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := dm.runContainerInPod(pod, container, netNamespace, getIPCMode(pod), getPidMode(pod))
|
// There is no meaningful labels for infraContainer now, so just pass nil.
|
||||||
|
id, err := dm.runContainerInPod(pod, container, netNamespace, getIPCMode(pod), getPidMode(pod), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -1920,13 +1918,28 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labels := map[string]string{}
|
||||||
|
containerStatuses := podStatus.ContainerStatuses
|
||||||
|
// podStatus is generated by GetPodStatus(). In GetPodStatus(), we make sure that ContainerStatuses
|
||||||
|
// contains statuses of all containers in pod.Spec.Containers.
|
||||||
|
// ContainerToStart is a subset of pod.Spec.Containers, we should always find a result here.
|
||||||
|
// For a new container, the RestartCount should be 0
|
||||||
|
labels[kubernetesContainerRestartCountLabel] = "0"
|
||||||
|
for _, containerStatus := range containerStatuses {
|
||||||
|
// If the container's terminate state is not empty, it exited before. Increment the restart count.
|
||||||
|
if containerStatus.Name == container.Name && (containerStatus.State.Terminated != nil || containerStatus.LastTerminationState.Terminated != nil) {
|
||||||
|
labels[kubernetesContainerRestartCountLabel] = strconv.Itoa(containerStatus.RestartCount + 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
|
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
|
||||||
// Note: when configuring the pod's containers anything that can be configured by pointing
|
// Note: when configuring the pod's containers anything that can be configured by pointing
|
||||||
// to the namespace of the infra container should use namespaceMode. This includes things like the net namespace
|
// to the namespace of the infra container should use namespaceMode. This includes things like the net namespace
|
||||||
// and IPC namespace. PID mode cannot point to another container right now.
|
// and IPC namespace. PID mode cannot point to another container right now.
|
||||||
// See createPodInfraContainer for infra container setup.
|
// See createPodInfraContainer for infra container setup.
|
||||||
namespaceMode := fmt.Sprintf("container:%v", podInfraContainerID)
|
namespaceMode := fmt.Sprintf("container:%v", podInfraContainerID)
|
||||||
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode, getPidMode(pod))
|
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode, getPidMode(pod), labels)
|
||||||
dm.updateReasonCache(pod, container, kubecontainer.ErrRunContainer.Error(), err)
|
dm.updateReasonCache(pod, container, kubecontainer.ErrRunContainer.Error(), err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(bburns) : Perhaps blacklist a container after N failures?
|
// TODO(bburns) : Perhaps blacklist a container after N failures?
|
||||||
|
@ -544,6 +544,7 @@ func runSyncPod(t *testing.T, dm *DockerManager, fakeDocker *FakeDockerClient, p
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
runningPod := kubecontainer.Pods(runningPods).FindPodByID(pod.UID)
|
runningPod := kubecontainer.Pods(runningPods).FindPodByID(pod.UID)
|
||||||
|
|
||||||
podStatus, err := dm.GetPodStatus(pod)
|
podStatus, err := dm.GetPodStatus(pod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
@ -713,6 +714,14 @@ func TestSyncPodDeletesWithNoPodInfraContainer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fakeDocker.ContainerMap = map[string]*docker.Container{
|
||||||
|
"1234": {
|
||||||
|
ID: "1234",
|
||||||
|
Config: &docker.Config{},
|
||||||
|
HostConfig: &docker.HostConfig{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
runSyncPod(t, dm, fakeDocker, pod, nil)
|
runSyncPod(t, dm, fakeDocker, pod, nil)
|
||||||
|
|
||||||
verifyCalls(t, fakeDocker, []string{
|
verifyCalls(t, fakeDocker, []string{
|
||||||
@ -1526,49 +1535,16 @@ func TestGetRestartCount(t *testing.T) {
|
|||||||
Namespace: "new",
|
Namespace: "new",
|
||||||
},
|
},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Containers: containers,
|
Containers: containers,
|
||||||
|
RestartPolicy: "Always",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
|
fakeDocker.ContainerMap = map[string]*docker.Container{}
|
||||||
names := []string{"/k8s_bar." + strconv.FormatUint(kubecontainer.HashContainer(&containers[0]), 16) + "_foo_new_12345678_0"}
|
|
||||||
currTime := time.Now()
|
|
||||||
containerMap := map[string]*docker.Container{
|
|
||||||
"1234": {
|
|
||||||
ID: "1234",
|
|
||||||
Name: "bar",
|
|
||||||
Config: &docker.Config{},
|
|
||||||
State: docker.State{
|
|
||||||
ExitCode: 42,
|
|
||||||
StartedAt: currTime.Add(-60 * time.Second),
|
|
||||||
FinishedAt: currTime.Add(-60 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"5678": {
|
|
||||||
ID: "5678",
|
|
||||||
Name: "bar",
|
|
||||||
Config: &docker.Config{},
|
|
||||||
State: docker.State{
|
|
||||||
ExitCode: 42,
|
|
||||||
StartedAt: currTime.Add(-30 * time.Second),
|
|
||||||
FinishedAt: currTime.Add(-30 * time.Second),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"9101": {
|
|
||||||
ID: "9101",
|
|
||||||
Name: "bar",
|
|
||||||
Config: &docker.Config{},
|
|
||||||
State: docker.State{
|
|
||||||
ExitCode: 42,
|
|
||||||
StartedAt: currTime.Add(30 * time.Minute),
|
|
||||||
FinishedAt: currTime.Add(30 * time.Minute),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
fakeDocker.ContainerMap = containerMap
|
|
||||||
|
|
||||||
// Helper function for verifying the restart count.
|
// Helper function for verifying the restart count.
|
||||||
verifyRestartCount := func(pod *api.Pod, expectedCount int) api.PodStatus {
|
verifyRestartCount := func(pod *api.Pod, expectedCount int) api.PodStatus {
|
||||||
|
runSyncPod(t, dm, fakeDocker, pod, nil)
|
||||||
status, err := dm.GetPodStatus(pod)
|
status, err := dm.GetPodStatus(pod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
@ -1580,21 +1556,48 @@ func TestGetRestartCount(t *testing.T) {
|
|||||||
return *status
|
return *status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container "bar" has failed twice; create two dead docker containers.
|
killOneContainer := func(pod *api.Pod) {
|
||||||
|
status, err := dm.GetPodStatus(pod)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
containerID := kubecontainer.ParseContainerID(status.ContainerStatuses[0].ContainerID)
|
||||||
|
dm.KillContainerInPod(containerID, &pod.Spec.Containers[0], pod)
|
||||||
|
}
|
||||||
|
// Container "bar" starts the first time.
|
||||||
// TODO: container lists are expected to be sorted reversely by time.
|
// TODO: container lists are expected to be sorted reversely by time.
|
||||||
// We should fix FakeDockerClient to sort the list before returning.
|
// We should fix FakeDockerClient to sort the list before returning.
|
||||||
fakeDocker.ExitedContainerList = []docker.APIContainers{{Names: names, ID: "5678"}, {Names: names, ID: "1234"}}
|
// (randome-liu) Just partially sorted now.
|
||||||
|
pod.Status = verifyRestartCount(&pod, 0)
|
||||||
|
killOneContainer(&pod)
|
||||||
|
|
||||||
|
// Poor container "bar" has been killed, and should be restarted with restart count 1
|
||||||
pod.Status = verifyRestartCount(&pod, 1)
|
pod.Status = verifyRestartCount(&pod, 1)
|
||||||
|
killOneContainer(&pod)
|
||||||
|
|
||||||
// Found a new dead container. The restart count should be incremented.
|
// Poor container "bar" has been killed again, and should be restarted with restart count 2
|
||||||
fakeDocker.ExitedContainerList = []docker.APIContainers{
|
|
||||||
{Names: names, ID: "9101"}, {Names: names, ID: "5678"}, {Names: names, ID: "1234"}}
|
|
||||||
pod.Status = verifyRestartCount(&pod, 2)
|
pod.Status = verifyRestartCount(&pod, 2)
|
||||||
|
killOneContainer(&pod)
|
||||||
|
|
||||||
// All dead containers have been GC'd. The restart count should persist
|
// Poor container "bar" has been killed again ang again, and should be restarted with restart count 3
|
||||||
// (i.e., remain the same).
|
pod.Status = verifyRestartCount(&pod, 3)
|
||||||
|
|
||||||
|
// The oldest container has been garbage collected
|
||||||
|
exitedContainers := fakeDocker.ExitedContainerList
|
||||||
|
fakeDocker.ExitedContainerList = exitedContainers[:len(exitedContainers)-1]
|
||||||
|
pod.Status = verifyRestartCount(&pod, 3)
|
||||||
|
|
||||||
|
// The last two oldest containers have been garbage collected
|
||||||
|
fakeDocker.ExitedContainerList = exitedContainers[:len(exitedContainers)-2]
|
||||||
|
pod.Status = verifyRestartCount(&pod, 3)
|
||||||
|
|
||||||
|
// All exited containers have been garbage collected
|
||||||
fakeDocker.ExitedContainerList = []docker.APIContainers{}
|
fakeDocker.ExitedContainerList = []docker.APIContainers{}
|
||||||
verifyRestartCount(&pod, 2)
|
pod.Status = verifyRestartCount(&pod, 3)
|
||||||
|
killOneContainer(&pod)
|
||||||
|
|
||||||
|
// Poor container "bar" has been killed again ang again and again, and should be restarted with restart count 4
|
||||||
|
pod.Status = verifyRestartCount(&pod, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyncPodWithPodInfraCreatesContainerCallsHandler(t *testing.T) {
|
func TestSyncPodWithPodInfraCreatesContainerCallsHandler(t *testing.T) {
|
||||||
|
@ -2630,17 +2630,6 @@ 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)
|
||||||
if existingStatus, hasExistingStatus := kl.statusManager.GetPodStatus(pod.UID); 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
|
|
||||||
// the pod so that we update restart count appropriately.
|
|
||||||
// TODO(yujuhong): We will not need to count dead containers every time
|
|
||||||
// once we add the runtime pod cache.
|
|
||||||
// Note that kubelet restarts may still cause temporarily setback of
|
|
||||||
// restart counts.
|
|
||||||
pod.Status = existingStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Consider include the container information.
|
// TODO: Consider include the container information.
|
||||||
if kl.pastActiveDeadline(pod) {
|
if kl.pastActiveDeadline(pod) {
|
||||||
|
Loading…
Reference in New Issue
Block a user