Skip to content

Commit

Permalink
Move docker label related functions into labels.go and add pod name, …
Browse files Browse the repository at this point in the history
…pod namespace and pod uid into docker label
  • Loading branch information
Random-Liu committed Oct 30, 2015
1 parent bffdd24 commit b3585a5
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 47 deletions.
1 change: 1 addition & 0 deletions pkg/kubelet/dockertools/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ func (p throttledDockerPuller) IsImagePresent(name string) (bool, error) {
return p.puller.IsImagePresent(name)
}

// TODO (random-liu) Almost never used, should we remove this?
// DockerContainers is a map of containers
type DockerContainers map[kubetypes.DockerID]*docker.APIContainers

Expand Down
5 changes: 3 additions & 2 deletions pkg/kubelet/dockertools/fake_docker_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,14 @@ func (f *FakeDockerClient) ListContainers(options docker.ListContainersOptions)
defer f.Unlock()
f.called = append(f.called, "list")
err := f.popError("list")
containerList := append([]docker.APIContainers{}, f.ContainerList...)
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
containerList = append(containerList, f.ExitedContainerList...)
}
return append([]docker.APIContainers{}, f.ContainerList...), err
return containerList, err
}

// InspectContainer is a test-spy implementation of DockerInterface.InspectContainer.
Expand Down
67 changes: 67 additions & 0 deletions pkg/kubelet/dockertools/labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package dockertools

import (
"strconv"

"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
)

// This file contains all docker label related constants and functions, including:
// * label setters and getters
// * label filters (maybe in the future)

const (
kubernetesPodNameLabel = "io.kubernetes.pod.name"
kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
kubernetesPodUID = "io.kubernetes.pod.uid"

kubernetesPodLabel = "io.kubernetes.pod.data"
kubernetesTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
kubernetesContainerLabel = "io.kubernetes.container.name"
kubernetesContainerRestartCountLabel = "io.kubernetes.container.restartCount"
)

func newLabels(container *api.Container, pod *api.Pod, restartCount int) map[string]string {
// TODO (random-liu) Move more label initialization here
labels := map[string]string{}
labels[kubernetesPodNameLabel] = pod.Name
labels[kubernetesPodNamespaceLabel] = pod.Namespace
labels[kubernetesPodUID] = string(pod.UID)

labels[kubernetesContainerRestartCountLabel] = strconv.Itoa(restartCount)

return labels
}

func getRestartCountFromLabel(labels map[string]string) (restartCount int, err error) {
if restartCountString, found := labels[kubernetesContainerRestartCountLabel]; found {
restartCount, err = strconv.Atoi(restartCountString)
if err != nil {
// This really should not happen. Just set restartCount to 0 to handle this abnormal case
restartCount = 0
}
} else {
// Get restartCount from docker label. If there is no restart count label in a container,
// it should be an old container or an invalid container, we just set restart count to 0.
// Do not report error, because there should be many old containers without this label now
glog.V(3).Infof("Container doesn't have label %s, it may be an old or invalid container", kubernetesContainerRestartCountLabel)
}
return restartCount, err
}
49 changes: 15 additions & 34 deletions pkg/kubelet/dockertools/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ const (
// SIGTERM for certain process types, which may justify setting this to 0.
minimumGracePeriodInSeconds = 2

kubernetesNameLabel = "io.kubernetes.pod.name"
kubernetesPodLabel = "io.kubernetes.pod.data"
kubernetesTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
kubernetesContainerLabel = "io.kubernetes.container.name"
kubernetesContainerRestartCountLabel = "io.kubernetes.container.restartCount"

DockerNetnsFmt = "/proc/%v/ns/net"
)

Expand Down Expand Up @@ -359,18 +353,9 @@ func (dm *DockerManager) inspectContainer(dockerID, containerName, tPath string,

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
}
if restartCount, err = getRestartCountFromLabel(inspectResult.Config.Labels); err != nil {
glog.Errorf("Get restart count error for container %v: %v", dockerID, err)
}

result.status = api.ContainerStatus{
Expand Down Expand Up @@ -461,6 +446,9 @@ func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
}
expectedContainers[PodInfraContainerName] = api.Container{}

// We have added labels like pod name and pod namespace, it seems that we can do filtered list here.
// However, there may be some old containers without these labels, so at least now we can't do that.
// TODO (random-liu) Add filter when we are sure that all the containers have the labels
containers, err := dm.client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil {
return nil, err
Expand All @@ -471,7 +459,6 @@ func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
// the statuses. We assume docker returns a list of containers sorted in
// reverse by time.
for _, value := range containers {
// TODO (random-liu) Filter by docker label later
if len(value.Names) == 0 {
continue
}
Expand Down Expand Up @@ -672,7 +659,7 @@ func (dm *DockerManager) runContainer(
ipcMode string,
utsMode string,
pidMode string,
labels map[string]string) (kubecontainer.ContainerID, error) {
restartCount int) (kubecontainer.ContainerID, error) {

dockerName := KubeletContainerName{
PodFullName: kubecontainer.GetPodFullName(pod),
Expand All @@ -693,12 +680,8 @@ func (dm *DockerManager) runContainer(
// while the Kubelet is down and there is no information available to recover the pod. This includes
// termination information like the termination grace period and the pre stop hooks.
// TODO: keep these labels up to date if the pod changes
namespacedName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}
// Just in case. If there is no label, just pass nil. An empty map will be created here.
if labels == nil {
labels = map[string]string{}
}
labels[kubernetesNameLabel] = namespacedName.String()
labels := newLabels(container, pod, restartCount)

if pod.Spec.TerminationGracePeriodSeconds != nil {
labels[kubernetesTerminationGracePeriodLabel] = strconv.FormatInt(*pod.Spec.TerminationGracePeriodSeconds, 10)
}
Expand Down Expand Up @@ -1485,8 +1468,7 @@ func containerAndPodFromLabels(inspect *docker.Container) (pod *api.Pod, contain

// Run a single container from a pod. Returns the docker container ID
// 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) {
func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Container, netMode, ipcMode, pidMode string, restartCount int) (kubecontainer.ContainerID, error) {
start := time.Now()
defer func() {
metrics.ContainerManagerLatency.WithLabelValues("runContainerInPod").Observe(metrics.SinceInMicroseconds(start))
Expand All @@ -1506,7 +1488,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork {
utsMode = "host"
}
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode, labels)
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode, restartCount)
if err != nil {
return kubecontainer.ContainerID{}, err
}
Expand Down Expand Up @@ -1643,8 +1625,8 @@ func (dm *DockerManager) createPodInfraContainer(pod *api.Pod) (kubetypes.Docker
return "", err
}

// There is no meaningful labels for infraContainer now, so just pass nil.
id, err := dm.runContainerInPod(pod, container, netNamespace, getIPCMode(pod), getPidMode(pod), nil)
// Currently we don't care about restart count of infra container, just set it to 0.
id, err := dm.runContainerInPod(pod, container, netNamespace, getIPCMode(pod), getPidMode(pod), 0)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -1905,17 +1887,16 @@ 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"
restartCount := 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)
restartCount = containerStatus.RestartCount + 1
break
}
}
Expand All @@ -1926,7 +1907,7 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
// and IPC namespace. PID mode cannot point to another container right now.
// See createPodInfraContainer for infra container setup.
namespaceMode := fmt.Sprintf("container:%v", podInfraContainerID)
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode, getPidMode(pod), labels)
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode, getPidMode(pod), restartCount)
dm.updateReasonCache(pod, container, kubecontainer.ErrRunContainer.Error(), err)
if err != nil {
// TODO(bburns) : Perhaps blacklist a container after N failures?
Expand Down
23 changes: 12 additions & 11 deletions pkg/kubelet/dockertools/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,17 @@ func TestGetPodStatusWithLastTermination(t *testing.T) {
{Name: "failed"},
}

pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: containers,
},
}

exitedAPIContainers := []docker.APIContainers{
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
Expand Down Expand Up @@ -1248,17 +1259,7 @@ func TestGetPodStatusWithLastTermination(t *testing.T) {
fakeDocker.ExitedContainerList = exitedAPIContainers
fakeDocker.ContainerMap = containerMap
fakeDocker.ClearCalls()
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: containers,
RestartPolicy: tt.policy,
},
}
pod.Spec.RestartPolicy = tt.policy
fakeDocker.ContainerList = []docker.APIContainers{
{
// pod infra container
Expand Down

1 comment on commit b3585a5

@k8s-teamcity-mesosphere

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeamCity OSS :: Kubernetes Mesos :: 4 - Smoke Tests Build 2981 outcome was SUCCESS
Summary: Tests passed: 1, ignored: 193 Build time: 00:08:26

Please sign in to comment.