package controller import ( "errors" "reflect" "testing" kapi "k8s.io/kubernetes/pkg/api" kerrors "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/client/record" "k8s.io/kubernetes/pkg/util" buildapi "github.com/openshift/origin/pkg/build/api" buildclient "github.com/openshift/origin/pkg/build/client" buildtest "github.com/openshift/origin/pkg/build/controller/test" imageapi "github.com/openshift/origin/pkg/image/api" ) type okBuildUpdater struct{} func (okc *okBuildUpdater) Update(namespace string, build *buildapi.Build) error { return nil } type errBuildUpdater struct{} func (ec *errBuildUpdater) Update(namespace string, build *buildapi.Build) error { return errors.New("UpdateBuild error!") } type okStrategy struct { build *buildapi.Build } func (os *okStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, error) { os.build = build return &kapi.Pod{}, nil } type errStrategy struct{} func (es *errStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, error) { return nil, errors.New("CreateBuildPod error!") } type okPodManager struct{} func (*okPodManager) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return &kapi.Pod{}, nil } func (*okPodManager) DeletePod(namespace string, pod *kapi.Pod) error { return nil } func (*okPodManager) GetPod(namespace, name string) (*kapi.Pod, error) { return &kapi.Pod{}, nil } type errPodManager struct{} func (*errPodManager) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return &kapi.Pod{}, errors.New("CreatePod error!") } func (*errPodManager) DeletePod(namespace string, pod *kapi.Pod) error { return errors.New("DeletePod error!") } func (*errPodManager) GetPod(namespace, name string) (*kapi.Pod, error) { return nil, errors.New("GetPod error!") } type errExistsPodManager struct{} func (*errExistsPodManager) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return &kapi.Pod{}, kerrors.NewAlreadyExists("kind", "name") } func (*errExistsPodManager) DeletePod(namespace string, pod *kapi.Pod) error { return kerrors.NewNotFound("kind", "name") } func (*errExistsPodManager) GetPod(namespace, name string) (*kapi.Pod, error) { return nil, kerrors.NewNotFound("kind", "name") } type okImageStreamClient struct{} func (*okImageStreamClient) GetImageStream(namespace, name string) (*imageapi.ImageStream, error) { return &imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{Name: name, Namespace: namespace}, Status: imageapi.ImageStreamStatus{ DockerImageRepository: "image/repo", }, }, nil } type errImageStreamClient struct{} func (*errImageStreamClient) GetImageStream(namespace, name string) (*imageapi.ImageStream, error) { return nil, errors.New("GetImageStream error!") } type errNotFoundImageStreamClient struct{} func (*errNotFoundImageStreamClient) GetImageStream(namespace, name string) (*imageapi.ImageStream, error) { return nil, kerrors.NewNotFound("ImageStream", name) } func mockBuild(phase buildapi.BuildPhase, output buildapi.BuildOutput) *buildapi.Build { return &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{ Name: "data-build", Namespace: "namespace", Labels: map[string]string{ "name": "dataBuild", }, }, Spec: buildapi.BuildSpec{ Source: buildapi.BuildSource{ Type: buildapi.BuildSourceGit, Git: &buildapi.GitBuildSource{ URI: "http://my.build.com/the/build/Dockerfile", }, ContextDir: "contextimage", }, Strategy: buildapi.BuildStrategy{ Type: buildapi.DockerBuildStrategyType, DockerStrategy: &buildapi.DockerBuildStrategy{}, }, Output: output, }, Status: buildapi.BuildStatus{ Phase: phase, }, } } func mockBuildController() *BuildController { return &BuildController{ BuildUpdater: &okBuildUpdater{}, PodManager: &okPodManager{}, BuildStrategy: &okStrategy{}, ImageStreamClient: &okImageStreamClient{}, Recorder: &record.FakeRecorder{}, } } func mockBuildPodController(build *buildapi.Build) *BuildPodController { return &BuildPodController{ BuildStore: buildtest.NewFakeBuildStore(build), BuildUpdater: &okBuildUpdater{}, PodManager: &okPodManager{}, } } func mockPod(status kapi.PodPhase, exitCode int) *kapi.Pod { return &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Name: "data-build-build", Annotations: map[string]string{ buildapi.BuildAnnotation: "data-build", }, }, Status: kapi.PodStatus{ Phase: status, ContainerStatuses: []kapi.ContainerStatus{ { State: kapi.ContainerState{ Terminated: &kapi.ContainerStateTerminated{ExitCode: exitCode}, }, }, }, }, } } func TestHandleBuild(t *testing.T) { type handleBuildTest struct { inStatus buildapi.BuildPhase outStatus buildapi.BuildPhase buildOutput buildapi.BuildOutput buildStrategy BuildStrategy buildUpdater buildclient.BuildUpdater imageClient imageStreamClient podManager podManager outputSpec string } tests := []handleBuildTest{ { // 0 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 1 inStatus: buildapi.BuildPhasePending, outStatus: buildapi.BuildPhasePending, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 2 inStatus: buildapi.BuildPhaseRunning, outStatus: buildapi.BuildPhaseRunning, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 3 inStatus: buildapi.BuildPhaseComplete, outStatus: buildapi.BuildPhaseComplete, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 4 inStatus: buildapi.BuildPhaseFailed, outStatus: buildapi.BuildPhaseFailed, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 5 inStatus: buildapi.BuildPhaseError, outStatus: buildapi.BuildPhaseError, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 6 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseError, buildStrategy: &errStrategy{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 7 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseError, podManager: &errPodManager{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 8 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, podManager: &errExistsPodManager{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 9 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, buildUpdater: &errBuildUpdater{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/dataBuild", }, }, }, { // 10 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "foo:tag", }, }, outputSpec: "image/repo:tag", }, { // 11 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "foo:tag", Namespace: "bar", }, }, outputSpec: "image/repo:tag", }, { // 12 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseError, imageClient: &errNotFoundImageStreamClient{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "foo:tag", }, }, }, { // 13 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseError, imageClient: &errImageStreamClient{}, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "foo:tag", }, }, }, { // 14 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhasePending, buildOutput: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "foo:tag", Namespace: "bar", }, }, outputSpec: "image/repo:tag", // an error updating the build is not reported as an error. buildUpdater: &errBuildUpdater{}, }, } for i, tc := range tests { build := mockBuild(tc.inStatus, tc.buildOutput) ctrl := mockBuildController() if tc.buildStrategy != nil { ctrl.BuildStrategy = tc.buildStrategy } if tc.buildUpdater != nil { ctrl.BuildUpdater = tc.buildUpdater } if tc.podManager != nil { ctrl.PodManager = tc.podManager } if tc.imageClient != nil { ctrl.ImageStreamClient = tc.imageClient } err := ctrl.HandleBuild(build) // ensure we return an error for cases where expected output is an error. // these will be retried by the retrycontroller if tc.inStatus != buildapi.BuildPhaseError && tc.outStatus == buildapi.BuildPhaseError { if err == nil { t.Errorf("(%d) Expected an error from HandleBuild, got none!", i) } continue } if err != nil { t.Errorf("(%d) Unexpected error %v", i, err) } if build.Status.Phase != tc.outStatus { t.Errorf("(%d) Expected %s, got %s!", i, tc.outStatus, build.Status.Phase) } if tc.inStatus != buildapi.BuildPhaseError && build.Status.Phase == buildapi.BuildPhaseError && len(build.Status.Message) == 0 { t.Errorf("(%d) errored build should set message: %#v", i, build) } if len(tc.outputSpec) != 0 { build := ctrl.BuildStrategy.(*okStrategy).build if build.Spec.Output.To.Name != tc.outputSpec { t.Errorf("(%d) expected build sent to strategy to have docker spec %s, got %s", i, tc.outputSpec, build.Spec.Output.To.Name) } } } } func TestHandlePod(t *testing.T) { type handlePodTest struct { matchID bool inStatus buildapi.BuildPhase outStatus buildapi.BuildPhase startTimestamp *util.Time completionTimestamp *util.Time podStatus kapi.PodPhase exitCode int buildUpdater buildclient.BuildUpdater podManager podManager } dummy := util.Now() curtime := &dummy tests := []handlePodTest{ { // 0 matchID: false, inStatus: buildapi.BuildPhasePending, outStatus: buildapi.BuildPhasePending, podStatus: kapi.PodPending, exitCode: 0, startTimestamp: nil, completionTimestamp: nil, }, { // 1 matchID: true, inStatus: buildapi.BuildPhasePending, outStatus: buildapi.BuildPhasePending, podStatus: kapi.PodPending, exitCode: 0, startTimestamp: nil, completionTimestamp: nil, }, { // 2 matchID: true, inStatus: buildapi.BuildPhasePending, outStatus: buildapi.BuildPhaseRunning, podStatus: kapi.PodRunning, exitCode: 0, startTimestamp: curtime, completionTimestamp: nil, }, { // 3 matchID: true, inStatus: buildapi.BuildPhaseRunning, outStatus: buildapi.BuildPhaseComplete, podStatus: kapi.PodSucceeded, exitCode: 0, startTimestamp: nil, completionTimestamp: curtime, }, { // 4 matchID: true, inStatus: buildapi.BuildPhaseRunning, outStatus: buildapi.BuildPhaseFailed, podStatus: kapi.PodFailed, exitCode: -1, startTimestamp: nil, completionTimestamp: curtime, }, { // 5 matchID: true, inStatus: buildapi.BuildPhaseRunning, outStatus: buildapi.BuildPhaseComplete, podStatus: kapi.PodSucceeded, exitCode: 0, buildUpdater: &errBuildUpdater{}, startTimestamp: nil, completionTimestamp: curtime, }, } for i, tc := range tests { build := mockBuild(tc.inStatus, buildapi.BuildOutput{}) ctrl := mockBuildPodController(build) pod := mockPod(tc.podStatus, tc.exitCode) if tc.matchID { build.Name = "name" } if tc.buildUpdater != nil { ctrl.BuildUpdater = tc.buildUpdater } err := ctrl.HandlePod(pod) if tc.buildUpdater != nil && reflect.TypeOf(tc.buildUpdater).Elem().Name() == "errBuildUpdater" { if err == nil { t.Errorf("(%d) Expected error, got none", i) } // can't check tc.outStatus because the local build object does get updated // in this test (but would not updated in etcd) continue } if build.Status.Phase != tc.outStatus { t.Errorf("(%d) Expected %s, got %s!", i, tc.outStatus, build.Status.Phase) } if tc.startTimestamp == nil && build.Status.StartTimestamp != nil { t.Errorf("(%d) Expected nil start timestamp, got %v!", i, build.Status.StartTimestamp) } if tc.startTimestamp != nil && build.Status.StartTimestamp == nil { t.Errorf("(%d) nil start timestamp!", i) } if tc.startTimestamp != nil && !tc.startTimestamp.Before(*build.Status.StartTimestamp) && tc.startTimestamp.Time != build.Status.StartTimestamp.Time { t.Errorf("(%d) Expected build start timestamp %v to be equal to or later than %v!", i, build.Status.StartTimestamp, tc.startTimestamp) } if tc.completionTimestamp == nil && build.Status.CompletionTimestamp != nil { t.Errorf("(%d) Expected nil completion timestamp, got %v!", i, build.Status.CompletionTimestamp) } if tc.completionTimestamp != nil && build.Status.CompletionTimestamp == nil { t.Errorf("(%d) nil completion timestamp!", i) } if tc.completionTimestamp != nil && !tc.completionTimestamp.Before(*build.Status.CompletionTimestamp) && tc.completionTimestamp.Time != build.Status.CompletionTimestamp.Time { t.Errorf("(%d) Expected build completion timestamp %v to be equal to or later than %v!", i, build.Status.CompletionTimestamp, tc.completionTimestamp) } } } func TestCancelBuild(t *testing.T) { type handleCancelBuildTest struct { inStatus buildapi.BuildPhase outStatus buildapi.BuildPhase podStatus kapi.PodPhase exitCode int buildUpdater buildclient.BuildUpdater podManager podManager startTimestamp *util.Time completionTimestamp *util.Time } dummy := util.Now() curtime := &dummy tests := []handleCancelBuildTest{ { // 0 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseCancelled, exitCode: 0, startTimestamp: nil, completionTimestamp: curtime, }, { // 1 inStatus: buildapi.BuildPhasePending, outStatus: buildapi.BuildPhaseCancelled, podStatus: kapi.PodRunning, exitCode: 0, startTimestamp: nil, completionTimestamp: curtime, }, { // 2 inStatus: buildapi.BuildPhaseRunning, outStatus: buildapi.BuildPhaseCancelled, podStatus: kapi.PodRunning, exitCode: 0, startTimestamp: nil, completionTimestamp: curtime, }, { // 3 inStatus: buildapi.BuildPhaseComplete, outStatus: buildapi.BuildPhaseComplete, podStatus: kapi.PodSucceeded, exitCode: 0, startTimestamp: nil, completionTimestamp: nil, }, { // 4 inStatus: buildapi.BuildPhaseFailed, outStatus: buildapi.BuildPhaseFailed, podStatus: kapi.PodFailed, exitCode: 1, startTimestamp: nil, completionTimestamp: nil, }, { // 5 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseNew, podStatus: kapi.PodFailed, exitCode: 1, podManager: &errPodManager{}, startTimestamp: nil, completionTimestamp: nil, }, { // 6 inStatus: buildapi.BuildPhaseNew, outStatus: buildapi.BuildPhaseNew, podStatus: kapi.PodFailed, exitCode: 1, buildUpdater: &errBuildUpdater{}, startTimestamp: nil, completionTimestamp: nil, }, { // 7 inStatus: buildapi.BuildPhaseCancelled, outStatus: buildapi.BuildPhaseCancelled, exitCode: 0, startTimestamp: nil, completionTimestamp: nil, }, } for i, tc := range tests { build := mockBuild(tc.inStatus, buildapi.BuildOutput{}) ctrl := mockBuildController() if tc.buildUpdater != nil { ctrl.BuildUpdater = tc.buildUpdater } if tc.podManager != nil { ctrl.PodManager = tc.podManager } err := ctrl.CancelBuild(build) if tc.podManager != nil && reflect.TypeOf(tc.podManager).Elem().Name() == "errPodManager" { if err == nil { t.Errorf("(%d) Expected error, got none", i) } } if tc.buildUpdater != nil && reflect.TypeOf(tc.buildUpdater).Elem().Name() == "errBuildUpdater" { if err == nil { t.Errorf("(%d) Expected error, got none", i) } // can't check tc.outStatus because the local build object does get updated // in this test (but would not be updated in etcd) continue } if tc.startTimestamp == nil && build.Status.StartTimestamp != nil { t.Errorf("(%d) Expected nil start timestamp, got %v!", i, build.Status.StartTimestamp) } if tc.startTimestamp != nil && build.Status.StartTimestamp == nil { t.Errorf("(%d) nil start timestamp!", i) } if tc.startTimestamp != nil && !tc.startTimestamp.Before(*build.Status.StartTimestamp) && tc.startTimestamp.Time != build.Status.StartTimestamp.Time { t.Errorf("(%d) Expected build start timestamp %v to be equal to or later than %v!", i, build.Status.StartTimestamp, tc.startTimestamp) } if tc.completionTimestamp == nil && build.Status.CompletionTimestamp != nil { t.Errorf("(%d) Expected nil completion timestamp, got %v!", i, build.Status.CompletionTimestamp) } if tc.completionTimestamp != nil && build.Status.CompletionTimestamp == nil { t.Errorf("(%d) nil start timestamp!", i) } if tc.completionTimestamp != nil && !tc.completionTimestamp.Before(*build.Status.CompletionTimestamp) && tc.completionTimestamp.Time != build.Status.CompletionTimestamp.Time { t.Errorf("(%d) Expected build completion timestamp %v to be equal to or later than %v!", i, build.Status.CompletionTimestamp, tc.completionTimestamp) } if build.Status.Phase != tc.outStatus { t.Errorf("(%d) Expected %s, got %s!", i, tc.outStatus, build.Status.Phase) } } } type customPodManager struct { CreatePodFunc func(namespace string, pod *kapi.Pod) (*kapi.Pod, error) DeletePodFunc func(namespace string, pod *kapi.Pod) error GetPodFunc func(namespace, name string) (*kapi.Pod, error) } func (c *customPodManager) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) { return c.CreatePodFunc(namespace, pod) } func (c *customPodManager) DeletePod(namespace string, pod *kapi.Pod) error { return c.DeletePodFunc(namespace, pod) } func (c *customPodManager) GetPod(namespace, name string) (*kapi.Pod, error) { return c.GetPodFunc(namespace, name) } func TestHandleHandleBuildDeletionOK(t *testing.T) { deleteWasCalled := false build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, names string) (*kapi.Pod, error) { return &kapi.Pod{ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{buildapi.BuildLabel: build.Name}}}, nil }, DeletePodFunc: func(namespace string, pod *kapi.Pod) error { deleteWasCalled = true return nil }, }} err := ctrl.HandleBuildDeletion(build) if err != nil { t.Errorf("Unexpected error %v", err) } if !deleteWasCalled { t.Error("DeletePod was not called when it should!") } } func TestHandleHandleBuildDeletionOKDeprecatedLabel(t *testing.T) { deleteWasCalled := false build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, names string) (*kapi.Pod, error) { return &kapi.Pod{ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{buildapi.DeprecatedBuildLabel: build.Name}}}, nil }, DeletePodFunc: func(namespace string, pod *kapi.Pod) error { deleteWasCalled = true return nil }, }} err := ctrl.HandleBuildDeletion(build) if err != nil { t.Errorf("Unexpected error %v", err) } if !deleteWasCalled { t.Error("DeletePod was not called when it should!") } } func TestHandleHandleBuildDeletionFailGetPod(t *testing.T) { build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, name string) (*kapi.Pod, error) { return nil, errors.New("random") }, }} err := ctrl.HandleBuildDeletion(build) if err == nil { t.Error("Expected random error got none!") } } func TestHandleHandleBuildDeletionGetPodNotFound(t *testing.T) { deleteWasCalled := false build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, name string) (*kapi.Pod, error) { return nil, kerrors.NewNotFound("Pod", name) }, DeletePodFunc: func(namespace string, pod *kapi.Pod) error { deleteWasCalled = true return nil }, }} err := ctrl.HandleBuildDeletion(build) if err != nil { t.Errorf("Unexpected error, %v", err) } if deleteWasCalled { t.Error("DeletePod was called when it should not!") } } func TestHandleHandleBuildDeletionMismatchedLabels(t *testing.T) { deleteWasCalled := false build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, names string) (*kapi.Pod, error) { return &kapi.Pod{}, nil }, DeletePodFunc: func(namespace string, pod *kapi.Pod) error { deleteWasCalled = true return nil }, }} err := ctrl.HandleBuildDeletion(build) if err != nil { t.Errorf("Unexpected error %v", err) } if deleteWasCalled { t.Error("DeletePod was called when it should not!") } } func TestHandleHandleBuildDeletionDeletePodError(t *testing.T) { build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := BuildDeleteController{&customPodManager{ GetPodFunc: func(namespace, names string) (*kapi.Pod, error) { return &kapi.Pod{ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{buildapi.BuildLabel: build.Name}}}, nil }, DeletePodFunc: func(namespace string, pod *kapi.Pod) error { return errors.New("random") }, }} err := ctrl.HandleBuildDeletion(build) if err == nil { t.Error("Expected random error got none!") } } type customBuildUpdater struct { UpdateFunc func(namespace string, build *buildapi.Build) error } func (c *customBuildUpdater) Update(namespace string, build *buildapi.Build) error { return c.UpdateFunc(namespace, build) } func mockBuildPodDeleteController(build *buildapi.Build, buildUpdater *customBuildUpdater, err error) *BuildPodDeleteController { return &BuildPodDeleteController{ BuildStore: buildtest.FakeBuildStore{Build: build, Err: err}, BuildUpdater: buildUpdater, } } func TestHandleBuildPodDeletionOK(t *testing.T) { updateWasCalled := false // only not finished build (buildutil.IsBuildComplete) should be handled build := mockBuild(buildapi.BuildPhaseRunning, buildapi.BuildOutput{}) ctrl := mockBuildPodDeleteController(build, &customBuildUpdater{ UpdateFunc: func(namespace string, build *buildapi.Build) error { updateWasCalled = true return nil }, }, nil) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err != nil { t.Errorf("Unexpected error %v", err) } if !updateWasCalled { t.Error("UpdateBuild was not called when it should!") } } func TestHandleBuildPodDeletionOKFinishedBuild(t *testing.T) { updateWasCalled := false // finished build buildutil.IsBuildComplete should not be handled build := mockBuild(buildapi.BuildPhaseComplete, buildapi.BuildOutput{}) ctrl := mockBuildPodDeleteController(build, &customBuildUpdater{ UpdateFunc: func(namespace string, build *buildapi.Build) error { updateWasCalled = true return nil }, }, nil) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err != nil { t.Errorf("Unexpected error %v", err) } if updateWasCalled { t.Error("UpdateBuild was called when it should not!") } } func TestHandleBuildPodDeletionOKErroneousBuild(t *testing.T) { updateWasCalled := false // erroneous builds should not be handled build := mockBuild(buildapi.BuildPhaseError, buildapi.BuildOutput{}) ctrl := mockBuildPodDeleteController(build, &customBuildUpdater{ UpdateFunc: func(namespace string, build *buildapi.Build) error { updateWasCalled = true return nil }, }, nil) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err != nil { t.Errorf("Unexpected error %v", err) } if updateWasCalled { t.Error("UpdateBuild was called when it should not!") } } func TestHandleBuildPodDeletionBuildGetError(t *testing.T) { ctrl := mockBuildPodDeleteController(nil, &customBuildUpdater{}, errors.New("random")) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err == nil { t.Error("Expected random error, but got none!") } } func TestHandleBuildPodDeletionBuildNotExists(t *testing.T) { updateWasCalled := false ctrl := mockBuildPodDeleteController(nil, &customBuildUpdater{ UpdateFunc: func(namespace string, build *buildapi.Build) error { updateWasCalled = true return nil }, }, nil) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err != nil { t.Errorf("Unexpected error %v", err) } if updateWasCalled { t.Error("UpdateBuild was called when it should not!") } } func TestHandleBuildPodDeletionBuildUpdateError(t *testing.T) { build := mockBuild(buildapi.BuildPhaseRunning, buildapi.BuildOutput{}) ctrl := mockBuildPodDeleteController(build, &customBuildUpdater{ UpdateFunc: func(namespace string, build *buildapi.Build) error { return errors.New("random") }, }, nil) pod := mockPod(kapi.PodSucceeded, 0) err := ctrl.HandleBuildPodDeletion(pod) if err == nil { t.Error("Expected random error, but got none!") } }