// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields // that cannot be changed. func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldPod.ObjectMeta, &newPod.ObjectMeta).Prefix("metadata")...) if len(newPod.Spec.Containers) != len(oldPod.Spec.Containers) { allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "may not add or remove containers")) return allErrs } pod := *newPod // Tricky, we need to copy the container list so that we don't overwrite the update var newContainers []api.Container for ix, container := range pod.Spec.Containers { container.Image = oldPod.Spec.Containers[ix].Image newContainers = append(newContainers, container) } pod.Spec.Containers = newContainers if !api.Semantic.DeepEqual(pod.Spec, oldPod.Spec) { // TODO: a better error would include all immutable fields explicitly. allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "some fields are immutable")) } newPod.Status = oldPod.Status return allErrs }
// ToAPIPod converts Pod to api.Pod. Note that if a field in api.Pod has no // corresponding field in Pod, the field would not be populated. func (p *Pod) ToAPIPod() *api.Pod { var pod api.Pod pod.UID = p.ID pod.Name = p.Name pod.Namespace = p.Namespace pod.Status = p.Status for _, c := range p.Containers { var container api.Container container.Name = c.Name container.Image = c.Image pod.Spec.Containers = append(pod.Spec.Containers, container) } return &pod }
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) }