func init() { Describe("Testing with Ginkgo", func() { It("mount disk should be asynchronous", func() { settings := &fakesettings.FakeSettingsService{} _, action := buildMountDiskAction(settings) Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { settings := &fakesettings.FakeSettingsService{} _, action := buildMountDiskAction(settings) Expect(action.IsPersistent()).To(BeFalse()) }) It("mount disk", func() { settings := &fakesettings.FakeSettingsService{} settings.Disks.Persistent = map[string]string{"vol-123": "/dev/sdf"} platform, mountDisk := buildMountDiskAction(settings) result, err := mountDisk.Run("vol-123") Expect(err).NotTo(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), result, "{}") Expect(settings.SettingsWereLoaded).To(BeTrue()) Expect(platform.MountPersistentDiskDevicePath).To(Equal("/dev/sdf")) Expect(platform.MountPersistentDiskMountPoint).To(Equal("/foo/store")) }) It("mount disk when store already mounted", func() { settings := &fakesettings.FakeSettingsService{} settings.Disks.Persistent = map[string]string{"vol-123": "/dev/sdf"} platform, mountDisk := buildMountDiskAction(settings) platform.IsMountPointResult = true result, err := mountDisk.Run("vol-123") Expect(err).NotTo(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), result, "{}") Expect(platform.IsMountPointPath).To(Equal("/foo/store")) Expect(platform.MountPersistentDiskDevicePath).To(Equal("/dev/sdf")) Expect(platform.MountPersistentDiskMountPoint).To(Equal("/foo/store_migration_target")) }) It("mount disk when device path not found", func() { settings := &fakesettings.FakeSettingsService{} settings.Disks.Persistent = map[string]string{"vol-123": "/dev/sdf"} _, mountDisk := buildMountDiskAction(settings) _, err := mountDisk.Run("vol-456") Expect(err).To(HaveOccurred()) }) }) }
func init() { Describe("Testing with Ginkgo", func() { It("json with value", func() { resp := NewValueResponse("some value") boshassert.MatchesJSONString(GinkgoT(), resp, `{"value":"some value"}`) }) It("json with exception", func() { resp := NewExceptionResponse("oops!") boshassert.MatchesJSONString(GinkgoT(), resp, `{"exception":{"message":"oops!"}}`) }) }) }
func init() { Describe("Testing with Ginkgo", func() { It("unmount disk should be asynchronous", func() { platform := fakeplatform.NewFakePlatform() action := buildUnmountDiskAction(platform) Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { platform := fakeplatform.NewFakePlatform() action := buildUnmountDiskAction(platform) Expect(action.IsPersistent()).To(BeFalse()) }) It("unmount disk when the disk is mounted", func() { platform := fakeplatform.NewFakePlatform() platform.UnmountPersistentDiskDidUnmount = true unmountDisk := buildUnmountDiskAction(platform) result, err := unmountDisk.Run("vol-123") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), result, `{"message":"Unmounted partition of /dev/sdf"}`) Expect(platform.UnmountPersistentDiskDevicePath).To(Equal("/dev/sdf")) }) It("unmount disk when the disk is not mounted", func() { platform := fakeplatform.NewFakePlatform() platform.UnmountPersistentDiskDidUnmount = false mountDisk := buildUnmountDiskAction(platform) result, err := mountDisk.Run("vol-123") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), result, `{"message":"Partition of /dev/sdf is not mounted"}`) Expect(platform.UnmountPersistentDiskDevicePath).To(Equal("/dev/sdf")) }) It("unmount disk when device path not found", func() { platform := fakeplatform.NewFakePlatform() mountDisk := buildUnmountDiskAction(platform) _, err := mountDisk.Run("vol-456") Expect(err).To(HaveOccurred()) }) }) }
func testLogs(t assert.TestingT, logType string, filters []string, expectedFilters []string) { deps, action := buildLogsAction() deps.copier.FilteredCopyToTempTempDir = "/fake-temp-dir" deps.compressor.CompressFilesInDirTarballPath = "logs_test.go" deps.blobstore.CreateBlobID = "my-blob-id" logs, err := action.Run(logType, filters) assert.NoError(t, err) var expectedPath string switch logType { case "job": expectedPath = filepath.Join("/fake", "dir", "sys", "log") case "agent": expectedPath = filepath.Join("/fake", "dir", "bosh", "log") } assert.Equal(t, expectedPath, deps.copier.FilteredCopyToTempDir) assert.Equal(t, expectedFilters, deps.copier.FilteredCopyToTempFilters) assert.Equal(t, deps.copier.FilteredCopyToTempTempDir, deps.compressor.CompressFilesInDirDir) assert.Equal(t, deps.copier.CleanUpTempDir, deps.compressor.CompressFilesInDirDir) assert.Equal(t, deps.compressor.CompressFilesInDirTarballPath, deps.blobstore.CreateFileName) boshassert.MatchesJSONString(t, logs, `{"blobstore_id":"my-blob-id"}`) }
func init() { Describe("Testing with Ginkgo", func() { It("migrate disk should be asynchronous", func() { _, action := buildMigrateDiskAction() Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { _, action := buildMigrateDiskAction() Expect(action.IsPersistent()).To(BeFalse()) }) It("migrate disk action run", func() { platform, action := buildMigrateDiskAction() value, err := action.Run() Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), value, "{}") Expect(platform.MigratePersistentDiskFromMountPoint).To(Equal("/foo/store")) Expect(platform.MigratePersistentDiskToMountPoint).To(Equal("/foo/store_migration_target")) }) }) }
func init() { Describe("Testing with Ginkgo", func() { var ( logger boshlog.Logger platform *fakeplatform.FakePlatform ) BeforeEach(func() { platform = fakeplatform.NewFakePlatform() logger = boshlog.NewLogger(boshlog.LevelNone) }) It("list disk should be synchronous", func() { settings := &fakesettings.FakeSettingsService{} action := NewListDisk(settings, platform, logger) Expect(action.IsAsynchronous()).To(BeFalse()) }) It("is not persistent", func() { settings := &fakesettings.FakeSettingsService{} action := NewListDisk(settings, platform, logger) Expect(action.IsPersistent()).To(BeFalse()) }) It("list disk run", func() { settings := &fakesettings.FakeSettingsService{ Disks: boshsettings.Disks{ Persistent: map[string]string{ "volume-1": "/dev/sda", "volume-2": "/dev/sdb", "volume-3": "/dev/sdc", }, }, } platform.MountedDevicePaths = []string{"/dev/sdb", "/dev/sdc"} action := NewListDisk(settings, platform, logger) value, err := action.Run() Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), value, `["volume-2","volume-3"]`) }) }) }
It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) It("returns a running task", func() { taskService.StartedTasks["fake-task-id"] = boshtask.Task{ ID: "fake-task-id", State: boshtask.TaskStateRunning, } taskValue, err := action.Run("fake-task-id") Expect(err).ToNot(HaveOccurred()) // Check JSON key casing boshassert.MatchesJSONString(GinkgoT(), taskValue, `{"agent_task_id":"fake-task-id","state":"running"}`) }) It("returns a failed task", func() { taskService.StartedTasks["fake-task-id"] = boshtask.Task{ ID: "fake-task-id", State: boshtask.TaskStateFailed, Error: errors.New("fake-task-error"), } taskValue, err := action.Run("fake-task-id") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Task fake-task-id result: fake-task-error")) Expect(taskValue).To(BeNil()) })
switch logType { case "job": expectedPath = filepath.Join("/fake", "dir", "sys", "log") case "agent": expectedPath = filepath.Join("/fake", "dir", "bosh", "log") } Expect(copier.FilteredCopyToTempDir).To(Equal(expectedPath)) Expect(copier.FilteredCopyToTempFilters).To(Equal(expectedFilters)) Expect(copier.FilteredCopyToTempTempDir).To(Equal(compressor.CompressFilesInDirDir)) Expect(copier.CleanUpTempDir).To(Equal(compressor.CompressFilesInDirDir)) Expect(compressor.CompressFilesInDirTarballPath).To(Equal(blobstore.CreateFileName)) boshassert.MatchesJSONString(GinkgoT(), logs, `{"blobstore_id":"my-blob-id"}`) } It("logs errs if given invalid log type", func() { _, err := action.Run("other-logs", []string{}) Expect(err).To(HaveOccurred()) }) It("agent logs with filters", func() { filters := []string{"**/*.stdout.log", "**/*.stderr.log"} expectedFilters := []string{"**/*.stdout.log", "**/*.stderr.log"} testLogs("agent", filters, expectedFilters) }) It("agent logs without filters", func() { filters := []string{}
func init() { Describe("GetState", func() { It("get state should be synchronous", func() { settings := &fakesettings.FakeSettingsService{} _, _, _, action := buildGetStateAction(settings) Expect(action.IsAsynchronous()).To(BeFalse()) }) It("is not persistent", func() { settings := &fakesettings.FakeSettingsService{} _, _, _, action := buildGetStateAction(settings) Expect(action.IsPersistent()).To(BeFalse()) }) Describe("Run", func() { It("returns state", func() { settings := &fakesettings.FakeSettingsService{} settings.AgentID = "my-agent-id" settings.VM.Name = "vm-abc-def" specService, jobSupervisor, _, action := buildGetStateAction(settings) jobSupervisor.StatusStatus = "running" specService.Spec = boshas.V1ApplySpec{ Deployment: "fake-deployment", } expectedSpec := GetStateV1ApplySpec{ AgentID: "my-agent-id", JobState: "running", BoshProtocol: "1", VM: boshsettings.VM{Name: "vm-abc-def"}, Ntp: boshntp.NTPInfo{ Offset: "0.34958", Timestamp: "12 Oct 17:37:58", }, } expectedSpec.Deployment = "fake-deployment" state, err := action.Run() Expect(err).ToNot(HaveOccurred()) Expect(state.AgentID).To(Equal(expectedSpec.AgentID)) Expect(state.JobState).To(Equal(expectedSpec.JobState)) Expect(state.Deployment).To(Equal(expectedSpec.Deployment)) boshassert.LacksJSONKey(GinkgoT(), state, "vitals") Expect(state).To(Equal(expectedSpec)) }) It("returns state in full format", func() { settings := &fakesettings.FakeSettingsService{} settings.AgentID = "my-agent-id" settings.VM.Name = "vm-abc-def" specService, jobSupervisor, fakeVitals, action := buildGetStateAction(settings) jobSupervisor.StatusStatus = "running" specService.Spec = boshas.V1ApplySpec{ Deployment: "fake-deployment", } expectedVitals := boshvitals.Vitals{ Load: []string{"foo", "bar", "baz"}, } fakeVitals.GetVitals = expectedVitals expectedVM := map[string]interface{}{"name": "vm-abc-def"} state, err := action.Run("full") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), state.AgentID, `"my-agent-id"`) boshassert.MatchesJSONString(GinkgoT(), state.JobState, `"running"`) boshassert.MatchesJSONString(GinkgoT(), state.Deployment, `"fake-deployment"`) Expect(*state.Vitals).To(Equal(expectedVitals)) boshassert.MatchesJSONMap(GinkgoT(), state.VM, expectedVM) }) Context("when current cannot be retrieved", func() { It("without current spec", func() { settings := &fakesettings.FakeSettingsService{} specService, _, _, action := buildGetStateAction(settings) specService.GetErr = errors.New("fake-spec-get-error") _, err := action.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-spec-get-error")) }) }) Context("when vitals cannot be retrieved", func() { It("returns error", func() { settings := &fakesettings.FakeSettingsService{} _, _, fakeVitals, action := buildGetStateAction(settings) fakeVitals.GetErr = errors.New("fake-vitals-get-error") _, err := action.Run("full") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-vitals-get-error")) }) }) }) }) }
Describe("Validate", func() { It("external validate writes config file", func() { options := map[string]interface{}{"fake-key": "fake-value"} blobstore := NewExternalBlobstore("fake-provider", options, fs, runner, uuidGen, configPath) runner.CommandExistsValue = true err := blobstore.Validate() Expect(err).ToNot(HaveOccurred()) s3CliConfig, err := fs.ReadFileString(configPath) Expect(err).ToNot(HaveOccurred()) expectedJSON := map[string]string{"fake-key": "fake-value"} boshassert.MatchesJSONString(GinkgoT(), expectedJSON, s3CliConfig) }) It("external validate errors when command not in path", func() { options := map[string]interface{}{} blobstore := NewExternalBlobstore("fake-provider", options, fs, runner, uuidGen, configPath) err := blobstore.Validate() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not found in PATH")) }) }) Describe("Get", func() { It("external get", func() {
func init() { Describe("GetTask", func() { var ( taskService *faketask.FakeService action GetTaskAction ) BeforeEach(func() { taskService = faketask.NewFakeService() action = NewGetTask(taskService) }) It("is synchronous", func() { Expect(action.IsAsynchronous()).To(BeFalse()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) It("returns a running task", func() { taskService.StartedTasks["fake-task-id"] = boshtask.Task{ ID: "fake-task-id", State: boshtask.TaskStateRunning, } taskValue, err := action.Run("fake-task-id") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), taskValue, `{"agent_task_id":"fake-task-id","state":"running"}`) }) It("returns a failed task", func() { taskService.StartedTasks["fake-task-id"] = boshtask.Task{ ID: "fake-task-id", State: boshtask.TaskStateFailed, Error: errors.New("fake-task-error"), } taskValue, err := action.Run("fake-task-id") Expect(err).To(HaveOccurred()) Expect("fake-task-error").To(Equal(err.Error())) boshassert.MatchesJSONString(GinkgoT(), taskValue, `null`) }) It("returns a successful task", func() { taskService.StartedTasks["fake-task-id"] = boshtask.Task{ ID: "fake-task-id", State: boshtask.TaskStateDone, Value: "some-task-value", } taskValue, err := action.Run("fake-task-id") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), taskValue, `"some-task-value"`) }) It("returns error when task is not found", func() { taskService.StartedTasks = map[string]boshtask.Task{} _, err := action.Run("fake-task-id") Expect(err).To(HaveOccurred()) Expect("Task with id fake-task-id could not be found").To(Equal(err.Error())) }) }) }
jobSupervisor.StatusStatus = "running" specService.Spec = boshas.V1ApplySpec{ Deployment: "fake-deployment", } expectedVitals := boshvitals.Vitals{ Load: []string{"foo", "bar", "baz"}, } vitalsService.GetVitals = expectedVitals expectedVM := map[string]interface{}{"name": "vm-abc-def"} state, err := action.Run("full") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), state.AgentID, `"my-agent-id"`) boshassert.MatchesJSONString(GinkgoT(), state.JobState, `"running"`) boshassert.MatchesJSONString(GinkgoT(), state.Deployment, `"fake-deployment"`) Expect(*state.Vitals).To(Equal(expectedVitals)) boshassert.MatchesJSONMap(GinkgoT(), state.VM, expectedVM) }) Describe("non-populated field formatting", func() { It("returns network as empty hash if not set", func() { specService.Spec = boshas.V1ApplySpec{NetworkSpecs: nil} state, err := action.Run("full") Expect(err).ToNot(HaveOccurred()) boshassert.MatchesJSONString(GinkgoT(), state.NetworkSpecs, `{}`) // Non-empty NetworkSpecs specService.Spec = boshas.V1ApplySpec{
fullMsg string shortMsgs []string } func (e testShortError) Error() string { return e.fullMsg } func (e *testShortError) ShortError() string { msg := e.shortMsgs[0] e.shortMsgs = e.shortMsgs[1:] return msg } var _ = Describe("NewValueResponse", func() { It("can be serialized to JSON", func() { resp := NewValueResponse("fake-value") boshassert.MatchesJSONString(GinkgoT(), resp, `{"value":"fake-value"}`) }) It("shortening does not change the response", func() { resp := NewValueResponse("fake-value") boshassert.MatchesJSONString(GinkgoT(), resp.Shorten(), `{"value":"fake-value"}`) }) }) var _ = Describe("NewExceptionResponse", func() { Context("with error that can be shortened", func() { var err error BeforeEach(func() { err = &testShortError{ fullMsg: "fake-full-msg",
func init() { Describe("Testing with Ginkgo", func() { It("external validate writes config file", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() options := map[string]string{"fake-key": "fake-value"} blobstore := NewExternalBlobstore("fake-provider", options, fs, runner, uuidGen, configPath) runner.CommandExistsValue = true assert.NoError(GinkgoT(), blobstore.Validate()) s3CliConfig, err := fs.ReadFileString(configPath) Expect(err).ToNot(HaveOccurred()) expectedJSON := map[string]string{"fake-key": "fake-value"} boshassert.MatchesJSONString(GinkgoT(), expectedJSON, s3CliConfig) }) It("external validate errors when command not in path", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() options := map[string]string{} blobstore := NewExternalBlobstore("fake-provider", options, fs, runner, uuidGen, configPath) assert.Error(GinkgoT(), blobstore.Validate()) }) It("external get", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() blobstore := NewExternalBlobstore("fake-provider", map[string]string{}, fs, runner, uuidGen, configPath) tempFile, err := fs.TempFile("bosh-blobstore-external-TestGet") Expect(err).ToNot(HaveOccurred()) fs.ReturnTempFile = tempFile defer fs.RemoveAll(tempFile.Name()) fileName, err := blobstore.Get("fake-blob-id", "") Expect(err).ToNot(HaveOccurred()) Expect(1).To(Equal(len(runner.RunCommands))) assert.Equal(GinkgoT(), []string{ "bosh-blobstore-fake-provider", "-c", configPath, "get", "fake-blob-id", tempFile.Name(), }, runner.RunCommands[0]) Expect(fileName).To(Equal(tempFile.Name())) Expect(fs.FileExists(tempFile.Name())).To(BeTrue()) }) It("external get errs when temp file create errs", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() blobstore := NewExternalBlobstore("fake-provider", map[string]string{}, fs, runner, uuidGen, configPath) fs.TempFileError = errors.New("fake-error") fileName, err := blobstore.Get("fake-blob-id", "") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-error")) assert.Empty(GinkgoT(), fileName) }) It("external get errs when external cli errs", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() blobstore := NewExternalBlobstore("fake-provider", map[string]string{}, fs, runner, uuidGen, configPath) tempFile, err := fs.TempFile("bosh-blobstore-external-TestGetErrsWhenExternalCliErrs") Expect(err).ToNot(HaveOccurred()) fs.ReturnTempFile = tempFile defer fs.RemoveAll(tempFile.Name()) expectedCmd := []string{ "bosh-blobstore-fake-provider", "-c", configPath, "get", "fake-blob-id", tempFile.Name(), } runner.AddCmdResult(strings.Join(expectedCmd, " "), fakesys.FakeCmdResult{Error: errors.New("fake-error")}) fileName, err := blobstore.Get("fake-blob-id", "") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-error")) assert.Empty(GinkgoT(), fileName) Expect(fs.FileExists(tempFile.Name())).To(BeFalse()) }) It("external clean up", func() { fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() blobstore := NewExternalBlobstore("fake-provider", map[string]string{}, fs, runner, uuidGen, configPath) file, err := fs.TempFile("bosh-blobstore-external-TestCleanUp") Expect(err).ToNot(HaveOccurred()) fileName := file.Name() defer fs.RemoveAll(fileName) err = blobstore.CleanUp(fileName) Expect(err).ToNot(HaveOccurred()) Expect(fs.FileExists(fileName)).To(BeFalse()) }) It("external create", func() { fileName := "../../../fixtures/some.config" expectedPath, _ := filepath.Abs(fileName) fs, runner, uuidGen, configPath := getExternalBlobstoreDependencies() blobstore := NewExternalBlobstore("fake-provider", map[string]string{}, fs, runner, uuidGen, configPath) uuidGen.GeneratedUuid = "some-uuid" blobID, fingerprint, err := blobstore.Create(fileName) Expect(err).ToNot(HaveOccurred()) Expect(blobID).To(Equal("some-uuid")) assert.Empty(GinkgoT(), fingerprint) Expect(1).To(Equal(len(runner.RunCommands))) assert.Equal(GinkgoT(), []string{ "bosh-blobstore-fake-provider", "-c", configPath, "put", expectedPath, "some-uuid", }, runner.RunCommands[0]) }) }) }
func init() { Describe("actionDispatcher", func() { var ( logger boshlog.Logger taskService *faketask.FakeService taskManager *faketask.FakeManager actionFactory *fakeaction.FakeFactory actionRunner *fakeaction.FakeRunner dispatcher ActionDispatcher ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) taskService = faketask.NewFakeService() taskManager = faketask.NewFakeManager() actionFactory = fakeaction.NewFakeFactory() actionRunner = &fakeaction.FakeRunner{} dispatcher = NewActionDispatcher(logger, taskService, taskManager, actionFactory, actionRunner) }) It("responds with exception when the method is unknown", func() { actionFactory.RegisterActionErr("fake-action", errors.New("fake-create-error")) req := boshhandler.NewRequest("fake-reply", "fake-action", []byte{}) resp := dispatcher.Dispatch(req) boshassert.MatchesJSONString(GinkgoT(), resp, `{"exception":{"message":"unknown message fake-action"}}`) }) Context("when action is synchronous", func() { var ( req boshhandler.Request ) BeforeEach(func() { req = boshhandler.NewRequest("fake-reply", "fake-action", []byte("fake-payload")) actionFactory.RegisterAction("fake-action", &fakeaction.TestAction{Asynchronous: false}) }) It("handles synchronous action", func() { actionRunner.RunValue = "fake-value" resp := dispatcher.Dispatch(req) Expect(req.GetPayload()).To(Equal(actionRunner.RunPayload)) Expect(boshhandler.NewValueResponse("fake-value")).To(Equal(resp)) }) It("handles synchronous action when err", func() { actionRunner.RunErr = errors.New("fake-run-error") resp := dispatcher.Dispatch(req) expectedJSON := fmt.Sprintf("{\"exception\":{\"message\":\"Action Failed %s: fake-run-error\"}}", req.Method) boshassert.MatchesJSONString(GinkgoT(), resp, expectedJSON) }) }) Context("when action is asynchronous", func() { var ( req boshhandler.Request action *fakeaction.TestAction ) BeforeEach(func() { req = boshhandler.NewRequest("fake-reply", "fake-action", []byte("fake-payload")) action = &fakeaction.TestAction{Asynchronous: true} actionFactory.RegisterAction("fake-action", action) }) ItAllowsToCancelTask := func() { It("allows task to be cancelled", func() { dispatcher.Dispatch(req) err := taskService.StartedTasks["fake-generated-task-id"].Cancel() Expect(err).ToNot(HaveOccurred()) Expect(action.Canceled).To(BeTrue()) }) It("returns error from cancelling task if canceling task fails", func() { action.CancelErr = errors.New("fake-cancel-err") dispatcher.Dispatch(req) err := taskService.StartedTasks["fake-generated-task-id"].Cancel() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-cancel-err")) }) } Context("when action is not persistent", func() { BeforeEach(func() { action.Persistent = false }) It("responds with task id and state", func() { resp := dispatcher.Dispatch(req) boshassert.MatchesJSONString(GinkgoT(), resp, `{"value":{"agent_task_id":"fake-generated-task-id","state":"running"}}`) }) It("starts running created task", func() { dispatcher.Dispatch(req) Expect(len(taskService.StartedTasks)).To(Equal(1)) Expect(taskService.StartedTasks["fake-generated-task-id"]).ToNot(BeNil()) }) It("returns create task error", func() { taskService.CreateTaskErr = errors.New("fake-create-task-error") resp := dispatcher.Dispatch(req) respJSON, err := json.Marshal(resp) Expect(err).ToNot(HaveOccurred()) Expect(string(respJSON)).To(ContainSubstring("fake-create-task-error")) }) It("return run value to the task", func() { actionRunner.RunValue = "fake-value" dispatcher.Dispatch(req) value, err := taskService.StartedTasks["fake-generated-task-id"].TaskFunc() Expect(value).To(Equal("fake-value")) Expect(err).ToNot(HaveOccurred()) Expect(actionRunner.RunAction).To(Equal(action)) Expect(string(actionRunner.RunPayload)).To(Equal("fake-payload")) }) It("returns run error to the task", func() { actionRunner.RunErr = errors.New("fake-run-error") dispatcher.Dispatch(req) value, err := taskService.StartedTasks["fake-generated-task-id"].TaskFunc() Expect(value).To(BeNil()) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-run-error")) Expect(actionRunner.RunAction).To(Equal(action)) Expect(string(actionRunner.RunPayload)).To(Equal("fake-payload")) }) ItAllowsToCancelTask() It("does not add task to task manager since it should not be resumed if agent is restarted", func() { dispatcher.Dispatch(req) taskInfos, _ := taskManager.GetTaskInfos() Expect(taskInfos).To(BeEmpty()) }) It("does not do anything after task finishes", func() { dispatcher.Dispatch(req) Expect(taskService.StartedTasks["fake-generated-task-id"].TaskEndFunc).To(BeNil()) }) }) Context("when action is persistent", func() { BeforeEach(func() { action.Persistent = true }) It("responds with task id and state", func() { resp := dispatcher.Dispatch(req) boshassert.MatchesJSONString(GinkgoT(), resp, `{"value":{"agent_task_id":"fake-generated-task-id","state":"running"}}`) }) It("starts running created task", func() { dispatcher.Dispatch(req) Expect(len(taskService.StartedTasks)).To(Equal(1)) Expect(taskService.StartedTasks["fake-generated-task-id"]).ToNot(BeNil()) }) It("returns create task error", func() { taskService.CreateTaskErr = errors.New("fake-create-task-error") resp := dispatcher.Dispatch(req) respJSON, err := json.Marshal(resp) Expect(err).ToNot(HaveOccurred()) Expect(string(respJSON)).To(ContainSubstring("fake-create-task-error")) }) It("return run value to the task", func() { actionRunner.RunValue = "fake-value" dispatcher.Dispatch(req) value, err := taskService.StartedTasks["fake-generated-task-id"].TaskFunc() Expect(value).To(Equal("fake-value")) Expect(err).ToNot(HaveOccurred()) Expect(actionRunner.RunAction).To(Equal(action)) Expect(string(actionRunner.RunPayload)).To(Equal("fake-payload")) }) It("returns run error to the task", func() { actionRunner.RunErr = errors.New("fake-run-error") dispatcher.Dispatch(req) value, err := taskService.StartedTasks["fake-generated-task-id"].TaskFunc() Expect(value).To(BeNil()) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-run-error")) Expect(actionRunner.RunAction).To(Equal(action)) Expect(string(actionRunner.RunPayload)).To(Equal("fake-payload")) }) ItAllowsToCancelTask() It("adds task to task manager before task starts so that it could be resumed if agent is restarted", func() { dispatcher.Dispatch(req) taskInfos, _ := taskManager.GetTaskInfos() Expect(taskInfos).To(Equal([]boshtask.TaskInfo{ boshtask.TaskInfo{ TaskID: "fake-generated-task-id", Method: "fake-action", Payload: []byte("fake-payload"), }, })) }) It("removes task from task manager after task finishes", func() { dispatcher.Dispatch(req) taskService.StartedTasks["fake-generated-task-id"].TaskEndFunc(boshtask.Task{ID: "fake-generated-task-id"}) taskInfos, _ := taskManager.GetTaskInfos() Expect(taskInfos).To(BeEmpty()) }) It("does not start running created task if task manager cannot add task", func() { taskManager.AddTaskInfoErr = errors.New("fake-add-task-info-error") resp := dispatcher.Dispatch(req) boshassert.MatchesJSONString(GinkgoT(), resp, `{"exception":{"message":"Action Failed fake-action: fake-add-task-info-error"}}`) Expect(len(taskService.StartedTasks)).To(Equal(0)) }) }) }) Describe("ResumePreviouslyDispatchedTasks", func() { var firstAction, secondAction *fakeaction.TestAction BeforeEach(func() { err := taskManager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-action-1", Payload: []byte("fake-task-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = taskManager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-action-2", Payload: []byte("fake-task-payload-2"), }) Expect(err).ToNot(HaveOccurred()) firstAction = &fakeaction.TestAction{} secondAction = &fakeaction.TestAction{} }) It("calls resume on each task that was saved in a task manager", func() { actionFactory.RegisterAction("fake-action-1", firstAction) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() Expect(len(taskService.StartedTasks)).To(Equal(2)) { // Check that first task executes first action actionRunner.ResumeValue = "fake-resume-value-1" value, err := taskService.StartedTasks["fake-task-id-1"].TaskFunc() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-resume-value-1")) Expect(actionRunner.ResumeAction).To(Equal(firstAction)) Expect(string(actionRunner.ResumePayload)).To(Equal("fake-task-payload-1")) } { // Check that second task executes second action actionRunner.ResumeValue = "fake-resume-value-2" value, err := taskService.StartedTasks["fake-task-id-2"].TaskFunc() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-resume-value-2")) Expect(actionRunner.ResumeAction).To(Equal(secondAction)) Expect(string(actionRunner.ResumePayload)).To(Equal("fake-task-payload-2")) } }) It("removes tasks from task manager after each task finishes", func() { actionFactory.RegisterAction("fake-action-1", firstAction) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() Expect(len(taskService.StartedTasks)).To(Equal(2)) // Simulate all tasks ending taskService.StartedTasks["fake-task-id-1"].TaskEndFunc(boshtask.Task{ID: "fake-task-id-1"}) taskService.StartedTasks["fake-task-id-2"].TaskEndFunc(boshtask.Task{ID: "fake-task-id-2"}) taskInfos, err := taskManager.GetTaskInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(BeEmpty()) }) It("return resume error to each task", func() { actionFactory.RegisterAction("fake-action-1", firstAction) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() Expect(len(taskService.StartedTasks)).To(Equal(2)) { // Check that first task propagates its resume error actionRunner.ResumeErr = errors.New("fake-resume-error-1") value, err := taskService.StartedTasks["fake-task-id-1"].TaskFunc() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-resume-error-1")) Expect(value).To(BeNil()) Expect(actionRunner.ResumeAction).To(Equal(firstAction)) Expect(string(actionRunner.ResumePayload)).To(Equal("fake-task-payload-1")) } { // Check that second task propagates its resume error actionRunner.ResumeErr = errors.New("fake-resume-error-2") value, err := taskService.StartedTasks["fake-task-id-2"].TaskFunc() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-resume-error-2")) Expect(value).To(BeNil()) Expect(actionRunner.ResumeAction).To(Equal(secondAction)) Expect(string(actionRunner.ResumePayload)).To(Equal("fake-task-payload-2")) } }) It("ignores actions that cannot be created and removes them from task manager", func() { actionFactory.RegisterActionErr("fake-action-1", errors.New("fake-action-error-1")) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() Expect(len(taskService.StartedTasks)).To(Equal(1)) { // Check that first action is removed from task manager taskInfos, err := taskManager.GetTaskInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(Equal([]boshtask.TaskInfo{ boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-action-2", Payload: []byte("fake-task-payload-2"), }, })) } { // Check that second task executes second action taskService.StartedTasks["fake-task-id-2"].TaskFunc() Expect(actionRunner.ResumeAction).To(Equal(secondAction)) Expect(string(actionRunner.ResumePayload)).To(Equal("fake-task-payload-2")) } }) It("allows to cancel after resume", func() { actionFactory.RegisterAction("fake-action-1", firstAction) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() err := taskService.StartedTasks["fake-task-id-1"].Cancel() Expect(err).ToNot(HaveOccurred()) Expect(firstAction.Canceled).To(BeTrue()) Expect(secondAction.Canceled).To(BeFalse()) err = taskService.StartedTasks["fake-task-id-2"].Cancel() Expect(err).ToNot(HaveOccurred()) Expect(secondAction.Canceled).To(BeTrue()) }) It("returns error from cancelling task when canceling resumed task fails", func() { actionFactory.RegisterAction("fake-action-1", firstAction) actionFactory.RegisterAction("fake-action-2", secondAction) dispatcher.ResumePreviouslyDispatchedTasks() firstAction.CancelErr = errors.New("fake-cancel-err-1") secondAction.CancelErr = errors.New("fake-cancel-err-2") err := taskService.StartedTasks["fake-task-id-1"].Cancel() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-cancel-err-1")) err = taskService.StartedTasks["fake-task-id-2"].Cancel() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-cancel-err-2")) }) }) }) }