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
}