// Creates a name which can be reversed to identify both full pod name and container name. func BuildDockerName(dockerName KubeletContainerName, container *api.Container) string { containerName := dockerName.ContainerName + "." + strconv.FormatUint(kubecontainer.HashContainer(container), 16) return fmt.Sprintf("%s_%s_%s_%s_%08x", containerNamePrefix, containerName, dockerName.PodFullName, dockerName.PodUID, rand.Uint32()) }
func generatePodInfraContainerHash(pod *api.Pod) uint64 { var ports []api.ContainerPort if !pod.Spec.HostNetwork { for _, container := range pod.Spec.Containers { ports = append(ports, container.Ports...) } } container := &api.Container{ Name: PodInfraContainerName, Image: PodInfraContainerImage, Ports: ports, } return kubecontainer.HashContainer(container) }
func TestSyncPodsDoesNothing(t *testing.T) { dm, fakeDocker := newTestDockerManager() container := api.Container{Name: "bar"} pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Spec: api.PodSpec{ Containers: []api.Container{ container, }, }, } fakeDocker.ContainerList = []docker.APIContainers{ { // format is // k8s_<container-id>_<pod-fullname>_<pod-uid>_<random> Names: []string{"/k8s_bar." + strconv.FormatUint(kubecontainer.HashContainer(&container), 16) + "_foo_new_12345678_0"}, ID: "1234", }, { // pod infra container Names: []string{"/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0"}, ID: "9876", }, } fakeDocker.ContainerMap = map[string]*docker.Container{ "1234": { ID: "1234", HostConfig: &docker.HostConfig{}, Config: &docker.Config{}, }, "9876": { ID: "9876", HostConfig: &docker.HostConfig{}, Config: &docker.Config{}, }, } runSyncPod(t, dm, fakeDocker, pod) verifyCalls(t, fakeDocker, []string{ // Check the pod infra contianer. "inspect_container", }) }
// TODO(yifan): Remove the receiver once we can solve the appName->imageID problem. func (r *runtime) apiPodToruntimePod(uuid string, pod *api.Pod) *kubecontainer.Pod { p := &kubecontainer.Pod{ ID: pod.UID, Name: pod.Name, Namespace: pod.Namespace, } for i := range pod.Spec.Containers { c := &pod.Spec.Containers[i] img, err := r.getImageByName(c.Image) if err != nil { glog.Warningf("rkt: Cannot get image for %q: %v", c.Image, err) } p.Containers = append(p.Containers, &kubecontainer.Container{ ID: types.UID(buildContainerID(&containerID{uuid, c.Name, img.id})), Name: c.Name, Image: c.Image, Hash: kubecontainer.HashContainer(c), Created: time.Now().Unix(), }) } return p }
// SyncPod syncs the running pod to match the specified desired pod. func (r *runtime) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, podStatus api.PodStatus, pullSecrets []api.Secret) error { podFullName := kubecontainer.GetPodFullName(pod) if len(runningPod.Containers) == 0 { glog.V(4).Infof("Pod %q is not running, will start it", podFullName) return r.RunPod(pod) } // Add references to all containers. unidentifiedContainers := make(map[types.UID]*kubecontainer.Container) for _, c := range runningPod.Containers { unidentifiedContainers[c.ID] = c } restartPod := false for _, container := range pod.Spec.Containers { expectedHash := kubecontainer.HashContainer(&container) c := runningPod.FindContainerByName(container.Name) if c == nil { if kubecontainer.ShouldContainerBeRestarted(&container, pod, &podStatus, r.readinessManager) { glog.V(3).Infof("Container %+v is dead, but RestartPolicy says that we should restart it.", container) // TODO(yifan): Containers in one pod are fate-sharing at this moment, see: // https://github.com/appc/spec/issues/276. restartPod = true break } continue } // TODO(yifan): Take care of host network change. containerChanged := c.Hash != 0 && c.Hash != expectedHash if containerChanged { glog.Infof("Pod %q container %q hash changed (%d vs %d), it will be killed and re-created.", podFullName, container.Name, c.Hash, expectedHash) restartPod = true break } result, err := r.prober.Probe(pod, podStatus, container, string(c.ID), c.Created) // TODO(vmarmol): examine this logic. if err == nil && result != probe.Success { glog.Infof("Pod %q container %q is unhealthy (probe result: %v), it will be killed and re-created.", podFullName, container.Name, result) restartPod = true break } if err != nil { glog.V(2).Infof("Probe container %q failed: %v", container.Name, err) } delete(unidentifiedContainers, c.ID) } // If there is any unidentified containers, restart the pod. if len(unidentifiedContainers) > 0 { restartPod = true } if restartPod { // TODO(yifan): Handle network plugin. if err := r.KillPod(runningPod); err != nil { return err } if err := r.RunPod(pod); err != nil { return err } } return nil }
func TestRunOnce(t *testing.T) { cadvisor := &cadvisor.Mock{} cadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil) podManager, _ := newFakePodManager() kb := &Kubelet{ rootDirectory: "/tmp/kubelet", recorder: &record.FakeRecorder{}, cadvisor: cadvisor, nodeLister: testNodeLister{}, statusManager: newStatusManager(nil), containerRefManager: kubecontainer.NewRefManager(), readinessManager: kubecontainer.NewReadinessManager(), podManager: podManager, os: kubecontainer.FakeOS{}, volumeManager: newVolumeManager(), } kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil)) if err := kb.setupDataDirs(); err != nil { t.Errorf("Failed to init data dirs: %v", err) } podContainers := []docker.APIContainers{ { Names: []string{"/k8s_bar." + strconv.FormatUint(kubecontainer.HashContainer(&api.Container{Name: "bar"}), 16) + "_foo_new_12345678_42"}, ID: "1234", Status: "running", }, { Names: []string{"/k8s_net_foo.new.test_abcdefgh_42"}, ID: "9876", Status: "running", }, } kb.dockerClient = &testDocker{ listContainersResults: []listContainersResult{ {label: "list pod container", containers: []docker.APIContainers{}}, {label: "syncPod", containers: []docker.APIContainers{}}, {label: "list pod container", containers: []docker.APIContainers{}}, {label: "syncPod", containers: podContainers}, {label: "list pod container", containers: podContainers}, {label: "list pod container", containers: podContainers}, }, inspectContainersResults: []inspectContainersResult{ { label: "syncPod", container: docker.Container{ Config: &docker.Config{Image: "someimage"}, State: docker.State{Running: true, Pid: 42}, }, }, { label: "syncPod", container: docker.Container{ Config: &docker.Config{Image: "someimage"}, State: docker.State{Running: true, Pid: 42}, }, }, { label: "syncPod", container: docker.Container{ Config: &docker.Config{Image: "someimage"}, State: docker.State{Running: true, Pid: 42}, }, }, { label: "syncPod", container: docker.Container{ Config: &docker.Config{Image: "someimage"}, State: docker.State{Running: true, Pid: 42}, }, }, }, t: t, } kb.containerRuntime = dockertools.NewFakeDockerManager( kb.dockerClient, kb.recorder, kb.readinessManager, kb.containerRefManager, dockertools.PodInfraContainerImage, 0, 0, "", kubecontainer.FakeOS{}, kb.networkPlugin, kb, nil, newKubeletRuntimeHooks(kb.recorder)) pods := []*api.Pod{ { ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Spec: api.PodSpec{ Containers: []api.Container{ {Name: "bar"}, }, }, }, } podManager.SetPods(pods) results, err := kb.runOnce(pods, time.Millisecond) if err != nil { t.Errorf("unexpected error: %v", err) } if results[0].Err != nil { t.Errorf("unexpected run pod error: %v", results[0].Err) } if results[0].Pod.Name != "foo" { t.Errorf("unexpected pod: %q", results[0].Pod.Name) } }
func TestGetRestartCount(t *testing.T) { dm, fakeDocker := newTestDockerManager() containers := []api.Container{ {Name: "bar"}, } pod := api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Spec: api.PodSpec{ Containers: containers, }, } // format is // k8s_<container-id>_<pod-fullname>_<pod-uid> 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. verifyRestartCount := func(pod *api.Pod, expectedCount int) api.PodStatus { status, err := dm.GetPodStatus(pod) if err != nil { t.Fatalf("unexpected error %v", err) } restartCount := status.ContainerStatuses[0].RestartCount if restartCount != expectedCount { t.Errorf("expected %d restart count, got %d", expectedCount, restartCount) } return *status } // Container "bar" has failed twice; create two dead docker containers. // TODO: container lists are expected to be sorted reversely by time. // We should fix FakeDockerClient to sort the list before returning. fakeDocker.ExitedContainerList = []docker.APIContainers{{Names: names, ID: "5678"}, {Names: names, ID: "1234"}} pod.Status = verifyRestartCount(&pod, 1) // Found a new dead container. The restart count should be incremented. fakeDocker.ExitedContainerList = []docker.APIContainers{ {Names: names, ID: "9101"}, {Names: names, ID: "5678"}, {Names: names, ID: "1234"}} pod.Status = verifyRestartCount(&pod, 2) // All dead containers have been GC'd. The restart count should persist // (i.e., remain the same). fakeDocker.ExitedContainerList = []docker.APIContainers{} verifyRestartCount(&pod, 2) }
func TestGetPodStatusWithLastTermination(t *testing.T) { dm, fakeDocker := newTestDockerManager() containers := []api.Container{ {Name: "succeeded"}, {Name: "failed"}, } exitedAPIContainers := []docker.APIContainers{ { // format is // k8s_<container-id>_<pod-fullname>_<pod-uid> Names: []string{"/k8s_succeeded." + strconv.FormatUint(kubecontainer.HashContainer(&containers[0]), 16) + "_foo_new_12345678_0"}, ID: "1234", }, { // format is // k8s_<container-id>_<pod-fullname>_<pod-uid> Names: []string{"/k8s_failed." + strconv.FormatUint(kubecontainer.HashContainer(&containers[1]), 16) + "_foo_new_12345678_0"}, ID: "5678", }, } containerMap := map[string]*docker.Container{ "9876": { ID: "9876", Name: "POD", Config: &docker.Config{}, HostConfig: &docker.HostConfig{}, State: docker.State{ StartedAt: time.Now(), FinishedAt: time.Now(), Running: true, }, }, "1234": { ID: "1234", Name: "succeeded", Config: &docker.Config{}, HostConfig: &docker.HostConfig{}, State: docker.State{ ExitCode: 0, StartedAt: time.Now(), FinishedAt: time.Now(), }, }, "5678": { ID: "5678", Name: "failed", Config: &docker.Config{}, HostConfig: &docker.HostConfig{}, State: docker.State{ ExitCode: 42, StartedAt: time.Now(), FinishedAt: time.Now(), }, }, } tests := []struct { policy api.RestartPolicy created []string stopped []string lastTerminations []string }{ { api.RestartPolicyAlways, []string{"succeeded", "failed"}, []string{}, []string{"docker://1234", "docker://5678"}, }, { api.RestartPolicyOnFailure, []string{"failed"}, []string{}, []string{"docker://5678"}, }, { api.RestartPolicyNever, []string{}, []string{"9876"}, []string{}, }, } for i, tt := range tests { 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, }, } fakeDocker.ContainerList = []docker.APIContainers{ { // pod infra container Names: []string{"/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0"}, ID: "9876", }, } runSyncPod(t, dm, fakeDocker, pod) // Check if we can retrieve the pod status. status, err := dm.GetPodStatus(pod) if err != nil { t.Fatalf("unexpected error %v", err) } terminatedContainers := []string{} for _, cs := range status.ContainerStatuses { if cs.LastTerminationState.Terminated != nil { terminatedContainers = append(terminatedContainers, cs.LastTerminationState.Terminated.ContainerID) } } sort.StringSlice(terminatedContainers).Sort() sort.StringSlice(tt.lastTerminations).Sort() if !reflect.DeepEqual(terminatedContainers, tt.lastTerminations) { t.Errorf("Expected(sorted): %#v, Actual(sorted): %#v", tt.lastTerminations, terminatedContainers) } if err := fakeDocker.AssertCreated(tt.created); err != nil { t.Errorf("%d: %v", i, err) } if err := fakeDocker.AssertStopped(tt.stopped); err != nil { t.Errorf("%d: %v", i, err) } } }
func TestSyncPodWithRestartPolicy(t *testing.T) { dm, fakeDocker := newTestDockerManager() containers := []api.Container{ {Name: "succeeded"}, {Name: "failed"}, } pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Spec: api.PodSpec{ Containers: containers, }, } runningAPIContainers := []docker.APIContainers{ { // pod infra container Names: []string{"/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0"}, ID: "9876", }, } exitedAPIContainers := []docker.APIContainers{ { // format is // k8s_<container-id>_<pod-fullname>_<pod-uid> Names: []string{"/k8s_succeeded." + strconv.FormatUint(kubecontainer.HashContainer(&containers[0]), 16) + "_foo_new_12345678_0"}, ID: "1234", }, { // format is // k8s_<container-id>_<pod-fullname>_<pod-uid> Names: []string{"/k8s_failed." + strconv.FormatUint(kubecontainer.HashContainer(&containers[1]), 16) + "_foo_new_12345678_0"}, ID: "5678", }, } containerMap := map[string]*docker.Container{ "9876": { ID: "9876", Name: "POD", Config: &docker.Config{}, State: docker.State{ StartedAt: time.Now(), Running: true, }, }, "1234": { ID: "1234", Name: "succeeded", Config: &docker.Config{}, State: docker.State{ ExitCode: 0, StartedAt: time.Now(), FinishedAt: time.Now(), }, }, "5678": { ID: "5678", Name: "failed", Config: &docker.Config{}, State: docker.State{ ExitCode: 42, StartedAt: time.Now(), FinishedAt: time.Now(), }, }, } tests := []struct { policy api.RestartPolicy calls []string created []string stopped []string }{ { api.RestartPolicyAlways, []string{ // Check the pod infra container. "inspect_container", // Restart both containers. "create", "start", "inspect_container", "create", "start", "inspect_container", }, []string{"succeeded", "failed"}, []string{}, }, { api.RestartPolicyOnFailure, []string{ // Check the pod infra container. "inspect_container", // Restart the failed container. "create", "start", "inspect_container", }, []string{"failed"}, []string{}, }, { api.RestartPolicyNever, []string{ // Check the pod infra container. "inspect_container", // Stop the last pod infra container. "inspect_container", "stop", }, []string{}, []string{"9876"}, }, } for i, tt := range tests { fakeDocker.ContainerList = runningAPIContainers fakeDocker.ExitedContainerList = exitedAPIContainers fakeDocker.ContainerMap = containerMap pod.Spec.RestartPolicy = tt.policy runSyncPod(t, dm, fakeDocker, pod) // 'stop' is because the pod infra container is killed when no container is running. verifyCalls(t, fakeDocker, tt.calls) if err := fakeDocker.AssertCreated(tt.created); err != nil { t.Errorf("%d: %v", i, err) } if err := fakeDocker.AssertStopped(tt.stopped); err != nil { t.Errorf("%d: %v", i, err) } } }