package controller import ( "fmt" "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" api "github.com/openshift/origin/pkg/api/latest" deployapi "github.com/openshift/origin/pkg/deploy/api" deploytest "github.com/openshift/origin/pkg/deploy/controller/test" deployutil "github.com/openshift/origin/pkg/deploy/util" ) func TestHandleNewDeploymentCreatePodOk(t *testing.T) { var ( updatedDeployment *kapi.ReplicationController createdPod *kapi.Pod expectedContainer = basicContainer() ) controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return updatedDeployment, nil }, }, PodInterface: &testDcPodInterface{ CreatePodFunc: func(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { createdPod = pod return pod, nil }, }, NextDeployment: func() *kapi.ReplicationController { deployment := basicDeployment() deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew) return deployment }, ContainerCreator: &testContainerCreator{ CreateContainerFunc: func(strategy *deployapi.DeploymentStrategy) *kapi.Container { return expectedContainer }, }, } // Verify new -> pending controller.HandleDeployment() if updatedDeployment == nil { t.Fatalf("expected an updated deployment") } if e, a := string(deployapi.DeploymentStatusPending), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } if createdPod == nil { t.Fatalf("expected a pod to be created") } if e, a := createdPod.Name, updatedDeployment.Annotations[deployapi.DeploymentPodAnnotation]; e != a { t.Fatalf("expected deployment pod annotation %s, got %s", e, a) } if e, a := updatedDeployment.Name, createdPod.Annotations[deployapi.DeploymentAnnotation]; e != a { t.Fatalf("expected pod deployment annotation %s, got %s", e, a) } actualContainer := createdPod.Spec.Containers[0] if e, a := expectedContainer.Image, actualContainer.Image; e != a { t.Fatalf("expected container image %s, got %s", expectedContainer.Image, actualContainer.Image) } if e, a := expectedContainer.Command[0], actualContainer.Command[0]; e != a { t.Fatalf("expected container command %s, got %s", expectedContainer.Command[0], actualContainer.Command[0]) } if e, a := expectedContainer.Env[0].Name, actualContainer.Env[0].Name; e != a { t.Fatalf("expected container env name %s, got %s", expectedContainer.Env[0].Name, actualContainer.Env[0].Name) } if e, a := expectedContainer.Env[0].Value, actualContainer.Env[0].Value; e != a { t.Fatalf("expected container env value %s, got %s", expectedContainer.Env[0].Value, actualContainer.Env[0].Value) } } func TestHandleNewDeploymentCreatePodFail(t *testing.T) { var updatedDeployment *kapi.ReplicationController controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namspace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return updatedDeployment, nil }, }, PodInterface: &testDcPodInterface{ CreatePodFunc: func(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return nil, fmt.Errorf("Failed to create pod %s", pod.Name) }, }, NextDeployment: func() *kapi.ReplicationController { deployment := basicDeployment() deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew) return deployment }, ContainerCreator: &testContainerCreator{ CreateContainerFunc: func(strategy *deployapi.DeploymentStrategy) *kapi.Container { return basicContainer() }, }, } // Verify new -> failed controller.HandleDeployment() if updatedDeployment == nil { t.Fatalf("expected an updated deployment") } if e, a := string(deployapi.DeploymentStatusFailed), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } } func TestHandleNewDeploymentCreatePodAlreadyExists(t *testing.T) { var updatedDeployment *kapi.ReplicationController controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return updatedDeployment, nil }, }, PodInterface: &testDcPodInterface{ CreatePodFunc: func(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return nil, kerrors.NewAlreadyExists("pod", pod.Name) }, }, NextDeployment: func() *kapi.ReplicationController { deployment := basicDeployment() deployment.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew) return deployment }, ContainerCreator: &testContainerCreator{ CreateContainerFunc: func(strategy *deployapi.DeploymentStrategy) *kapi.Container { return basicContainer() }, }, } // Verify new -> pending controller.HandleDeployment() if updatedDeployment == nil { t.Fatalf("expected an updated deployment") } if e, a := string(deployapi.DeploymentStatusPending), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } } func TestHandleUncorrelatedPod(t *testing.T) { controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { t.Fatalf("Unexpected deployment update") return nil, nil }, }, PodInterface: &testDcPodInterface{}, NextDeployment: func() *kapi.ReplicationController { return nil }, NextPod: func() *kapi.Pod { pod := runningPod() pod.Annotations = make(map[string]string) return pod }, DeploymentStore: deploytest.NewFakeDeploymentStore(pendingDeployment()), } // Verify no-op controller.HandlePod() } func TestHandleOrphanedPod(t *testing.T) { controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { t.Fatalf("Unexpected deployment update") return nil, nil }, }, PodInterface: &testDcPodInterface{}, NextDeployment: func() *kapi.ReplicationController { return nil }, NextPod: func() *kapi.Pod { return runningPod() }, DeploymentStore: deploytest.NewFakeDeploymentStore(nil), } // Verify no-op controller.HandlePod() } func TestHandlePodRunning(t *testing.T) { var updatedDeployment *kapi.ReplicationController controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return deployment, nil }, }, PodInterface: &testDcPodInterface{}, NextDeployment: func() *kapi.ReplicationController { return nil }, NextPod: func() *kapi.Pod { return runningPod() }, DeploymentStore: deploytest.NewFakeDeploymentStore(pendingDeployment()), } controller.HandlePod() if updatedDeployment == nil { t.Fatalf("Expected a deployment to be updated") } if e, a := string(deployapi.DeploymentStatusRunning), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } } func TestHandlePodTerminatedOk(t *testing.T) { var updatedDeployment *kapi.ReplicationController var deletedPodID string controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return deployment, nil }, }, PodInterface: &testDcPodInterface{ DeletePodFunc: func(namespace, name string) error { deletedPodID = name return nil }, }, NextDeployment: func() *kapi.ReplicationController { return nil }, NextPod: func() *kapi.Pod { return succeededPod() }, DeploymentStore: deploytest.NewFakeDeploymentStore(runningDeployment()), } controller.HandlePod() if updatedDeployment == nil { t.Fatalf("Expected a deployment to be updated") } if e, a := string(deployapi.DeploymentStatusComplete), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } if len(deletedPodID) == 0 { t.Fatalf("expected pod to be deleted") } } func TestHandlePodTerminatedNotOk(t *testing.T) { var updatedDeployment *kapi.ReplicationController controller := &DeploymentController{ Codec: api.Codec, DeploymentInterface: &testDcDeploymentInterface{ UpdateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { updatedDeployment = deployment return deployment, nil }, }, PodInterface: &testDcPodInterface{ DeletePodFunc: func(namespace, name string) error { t.Fatalf("unexpected delete of pod %s", name) return nil }, }, ContainerCreator: &testContainerCreator{ CreateContainerFunc: func(strategy *deployapi.DeploymentStrategy) *kapi.Container { return basicContainer() }, }, NextDeployment: func() *kapi.ReplicationController { return nil }, NextPod: func() *kapi.Pod { return failedPod() }, DeploymentStore: deploytest.NewFakeDeploymentStore(runningDeployment()), } controller.HandlePod() if updatedDeployment == nil { t.Fatalf("Expected a deployment to be updated") } if e, a := string(deployapi.DeploymentStatusFailed), updatedDeployment.Annotations[deployapi.DeploymentStatusAnnotation]; e != a { t.Fatalf("expected updated deployment status %s, got %s", e, a) } } type testContainerCreator struct { CreateContainerFunc func(strategy *deployapi.DeploymentStrategy) *kapi.Container } func (t *testContainerCreator) CreateContainer(strategy *deployapi.DeploymentStrategy) *kapi.Container { return t.CreateContainerFunc(strategy) } type testDcDeploymentInterface struct { UpdateDeploymentFunc func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) } func (i *testDcDeploymentInterface) UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) { return i.UpdateDeploymentFunc(namespace, deployment) } type testDcPodInterface struct { CreatePodFunc func(namespace string, pod *kapi.Pod) (*kapi.Pod, error) DeletePodFunc func(namespace, name string) error } func (i *testDcPodInterface) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return i.CreatePodFunc(namespace, pod) } func (i *testDcPodInterface) DeletePod(namespace, name string) error { return i.DeletePodFunc(namespace, name) } func basicDeploymentConfig() *deployapi.DeploymentConfig { return &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Name: "deploy1"}, Triggers: []deployapi.DeploymentTriggerPolicy{ { Type: deployapi.DeploymentTriggerManual, }, }, Template: deployapi.DeploymentTemplate{ Strategy: deployapi.DeploymentStrategy{ Type: deployapi.DeploymentStrategyTypeRecreate, }, ControllerTemplate: kapi.ReplicationControllerSpec{ Replicas: 1, Selector: map[string]string{ "name": "test-pod", }, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "name": "test-pod", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "container-1", Image: "registry:8080/openshift/test-image:ref-1", }, }, }, }, }, }, } } func basicDeployment() *kapi.ReplicationController { config := basicDeploymentConfig() encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec) return &kapi.ReplicationController{ ObjectMeta: kapi.ObjectMeta{ Name: "deploy1", Annotations: map[string]string{ deployapi.DeploymentConfigAnnotation: config.Name, deployapi.DeploymentStatusAnnotation: string(deployapi.DeploymentStatusNew), deployapi.DeploymentEncodedConfigAnnotation: encodedConfig, }, Labels: config.Labels, }, Spec: kapi.ReplicationControllerSpec{ Template: &kapi.PodTemplateSpec{ Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "container1", Image: "registry:8080/repo1:ref1", }, }, }, }, }, } } func pendingDeployment() *kapi.ReplicationController { d := basicDeployment() d.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusPending) return d } func runningDeployment() *kapi.ReplicationController { d := basicDeployment() d.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusRunning) return d } func basicContainer() *kapi.Container { return &kapi.Container{ Image: "test/image", Command: []string{"command"}, Env: []kapi.EnvVar{ { Name: "env1", Value: "val1", }, }, } } func basicPod() *kapi.Pod { return &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: "deploy-deploy1", Annotations: map[string]string{ deployapi.DeploymentAnnotation: "1234", }, }, Status: kapi.PodStatus{ Info: kapi.PodInfo{ "container1": kapi.ContainerStatus{}, }, }, } } func succeededPod() *kapi.Pod { p := basicPod() p.Status.Phase = kapi.PodSucceeded return p } func failedPod() *kapi.Pod { p := basicPod() p.Status.Phase = kapi.PodFailed p.Status.Info["container1"] = kapi.ContainerStatus{ State: kapi.ContainerState{ Termination: &kapi.ContainerStateTerminated{ ExitCode: 1, }, }, } return p } func runningPod() *kapi.Pod { p := basicPod() p.Status.Phase = kapi.PodRunning return p }