func init() { Describe("Agent", func() { var ( agent Agent logger boshlog.Logger handler *fakembus.FakeHandler platform *fakeplatform.FakePlatform actionDispatcher *FakeActionDispatcher alertBuilder *fakealert.FakeAlertBuilder alertSender AlertSender jobSupervisor *fakejobsuper.FakeJobSupervisor specService *fakeas.FakeV1Service ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelDebug) handler = &fakembus.FakeHandler{} platform = fakeplatform.NewFakePlatform() actionDispatcher = &FakeActionDispatcher{} alertBuilder = fakealert.NewFakeAlertBuilder() alertSender = NewAlertSender(handler, alertBuilder) jobSupervisor = fakejobsuper.NewFakeJobSupervisor() specService = fakeas.NewFakeV1Service() agent = New(logger, handler, platform, actionDispatcher, alertSender, jobSupervisor, specService, 5*time.Millisecond) }) Describe("Run", func() { It("lets dispatcher handle requests arriving via handler", func() { err := agent.Run() Expect(err).ToNot(HaveOccurred()) expectedResp := boshhandler.NewValueResponse("pong") actionDispatcher.DispatchResp = expectedResp req := boshhandler.NewRequest("fake-reply", "fake-action", []byte("fake-payload")) resp := handler.RunFunc(req) Expect(actionDispatcher.DispatchReq).To(Equal(req)) Expect(resp).To(Equal(expectedResp)) }) It("resumes persistent actions *before* dispatching new requests", func() { resumedBeforeStartingToDispatch := false handler.RunCallBack = func() { resumedBeforeStartingToDispatch = actionDispatcher.ResumedPreviouslyDispatchedTasks } err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(resumedBeforeStartingToDispatch).To(BeTrue()) }) Context("when heartbeats can be sent", func() { BeforeEach(func() { handler.KeepOnRunning() }) BeforeEach(func() { jobName := "fake-job" jobIndex := 1 specService.Spec = boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{Name: &jobName}, Index: &jobIndex, } jobSupervisor.StatusStatus = "fake-state" platform.FakeVitalsService.GetVitals = boshvitals.Vitals{ Load: []string{"a", "b", "c"}, } }) expectedJobName := "fake-job" expectedJobIndex := 1 expectedHb := boshmbus.Heartbeat{ Job: &expectedJobName, Index: &expectedJobIndex, JobState: "fake-state", Vitals: boshvitals.Vitals{Load: []string{"a", "b", "c"}}, } It("sends initial heartbeat", func() { // Configure periodic heartbeat every 5 hours // so that we are sure that we will not receive it agent = New(logger, handler, platform, actionDispatcher, alertSender, jobSupervisor, specService, 5*time.Hour) // Immediately exit after sending initial heartbeat handler.SendToHealthManagerErr = errors.New("stop") err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("stop")) Expect(handler.HMRequests()).To(Equal([]fakembus.HMRequest{ fakembus.HMRequest{Topic: "heartbeat", Payload: expectedHb}, })) }) It("sends periodic heartbeats", func() { sentRequests := 0 handler.SendToHealthManagerCallBack = func(_ fakembus.HMRequest) { sentRequests++ if sentRequests == 3 { handler.SendToHealthManagerErr = errors.New("stop") } } err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("stop")) Expect(handler.HMRequests()).To(Equal([]fakembus.HMRequest{ fakembus.HMRequest{Topic: "heartbeat", Payload: expectedHb}, fakembus.HMRequest{Topic: "heartbeat", Payload: expectedHb}, fakembus.HMRequest{Topic: "heartbeat", Payload: expectedHb}, })) }) }) Context("when the agent fails to get job spec for a heartbeat", func() { BeforeEach(func() { specService.GetErr = errors.New("fake-spec-service-error") handler.KeepOnRunning() }) It("returns the error", func() { err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-spec-service-error")) }) }) Context("when the agent fails to get vitals for a heartbeat", func() { BeforeEach(func() { platform.FakeVitalsService.GetErr = errors.New("fake-vitals-service-error") handler.KeepOnRunning() }) It("returns the error", func() { err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-vitals-service-error")) }) }) It("sends job failure alerts to health manager", func() { handler.KeepOnRunning() failureAlert := boshalert.MonitAlert{ID: "fake-monit-alert"} jobSupervisor.JobFailureAlert = &failureAlert builtAlert := boshalert.Alert{ID: "fake-built-alert"} alertBuilder.BuildAlert = builtAlert // Immediately exit from Run() after alert is sent handler.SendToHealthManagerCallBack = func(hmRequest fakembus.HMRequest) { if hmRequest.Topic == "alert" { handler.SendToHealthManagerErr = errors.New("stop") } } err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("stop")) Expect(alertBuilder.BuildInput).To(Equal(failureAlert)) // Check for inclusion because heartbeats might have been received Expect(handler.HMRequests()).To(ContainElement( fakembus.HMRequest{Topic: "alert", Payload: builtAlert}, )) }) }) }) }
It("is asynchronous", func() { Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) Describe("Run", func() { Context("when apply spec is successfully retrieved", func() { Context("when current agent has a job spec template", func() { BeforeEach(func() { currentSpec := boshas.V1ApplySpec{} currentSpec.JobSpec.Template = "fake-job-name" specService.Spec = currentSpec }) Context("when errand script exits with non-0 exit code (execution of script is ok)", func() { BeforeEach(func() { cmdRunner.AddProcess("/fake-jobs-dir/fake-job-name/bin/run", &fakesys.FakeProcess{ WaitResult: boshsys.Result{ Stdout: "fake-stdout", Stderr: "fake-stderr", ExitStatus: 0, }, }) }) It("returns errand result without error after running an errand", func() { result, err := action.Run()
func init() { Describe("Agent", func() { var ( agent Agent logger boshlog.Logger handler *fakembus.FakeHandler platform *fakeplatform.FakePlatform actionDispatcher *FakeActionDispatcher alertBuilder *fakealert.FakeAlertBuilder jobSupervisor *fakejobsuper.FakeJobSupervisor specService *fakeas.FakeV1Service ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LEVEL_NONE) handler = &fakembus.FakeHandler{} platform = fakeplatform.NewFakePlatform() actionDispatcher = &FakeActionDispatcher{} alertBuilder = fakealert.NewFakeAlertBuilder() jobSupervisor = fakejobsuper.NewFakeJobSupervisor() specService = fakeas.NewFakeV1Service() agent = New(logger, handler, platform, actionDispatcher, alertBuilder, jobSupervisor, specService, 5*time.Millisecond) }) Describe("Run", func() { It("sets the dispatcher as message handler", func() { actionDispatcher.DispatchResp = boshhandler.NewValueResponse("pong") err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(handler.ReceivedRun).To(BeTrue()) req := boshhandler.NewRequest("fake-reply", "fake-action", []byte("fake-payload")) resp := handler.Func(req) Expect(req).To(Equal(actionDispatcher.DispatchReq)) Expect(actionDispatcher.DispatchResp).To(Equal(resp)) }) It("resumes persistent actions *before* dispatching new requests", func() { resumedBefore := false handler.RunFunc = func() { resumedBefore = actionDispatcher.ResumedPreviouslyDispatchedTasks } err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(resumedBefore).To(BeTrue()) }) Context("when heartbeats can be sent", func() { BeforeEach(func() { jobName := "fake-job" jobIndex := 1 specService.Spec = boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{Name: &jobName}, Index: &jobIndex, } jobSupervisor.StatusStatus = "fake-state" platform.FakeVitalsService.GetVitals = boshvitals.Vitals{ Load: []string{"a", "b", "c"}, } }) expectedJobName := "fake-job" expectedJobIndex := 1 expectedHb := boshmbus.Heartbeat{ Job: &expectedJobName, Index: &expectedJobIndex, JobState: "fake-state", Vitals: boshvitals.Vitals{Load: []string{"a", "b", "c"}}, } It("sends initial heartbeat", func() { err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(handler.InitialHeartbeatSent).To(BeTrue()) Expect(handler.TickHeartbeatsSent).To(BeFalse()) Expect(handler.SendToHealthManagerTopic).To(Equal("heartbeat")) Expect(handler.SendToHealthManagerPayload.(boshmbus.Heartbeat)).To(Equal(expectedHb)) }) It("sends periodic heartbeats", func() { err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(handler.TickHeartbeatsSent).To(BeFalse()) time.Sleep(5 * time.Millisecond) Expect(handler.TickHeartbeatsSent).To(BeTrue()) Expect(handler.SendToHealthManagerTopic).To(Equal("heartbeat")) Expect(handler.SendToHealthManagerPayload.(boshmbus.Heartbeat)).To(Equal(expectedHb)) }) }) Context("when the agent fails to get job spec for a heartbeat", func() { BeforeEach(func() { block := make(chan error) handler.RunFunc = func() { <-block } specService.GetErr = errors.New("fake-spec-service-error") }) It("returns the error", func() { err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-spec-service-error")) }) }) Context("when the agent fails to get vitals for a heartbeat", func() { BeforeEach(func() { block := make(chan error) handler.RunFunc = func() { <-block } platform.FakeVitalsService.GetErr = errors.New("fake-vitals-service-error") }) It("returns the error", func() { err := agent.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-vitals-service-error")) }) }) It("sets the callback for job failures monitoring", func() { builtAlert := boshalert.Alert{Id: "some built alert id"} alertBuilder.BuildAlert = builtAlert err := agent.Run() Expect(err).ToNot(HaveOccurred()) Expect(handler.SendToHealthManagerTopic).ToNot(Equal("alert")) failureAlert := boshalert.MonitAlert{Id: "some random id"} jobSupervisor.OnJobFailure(failureAlert) Expect(failureAlert).To(Equal(alertBuilder.BuildInput)) Expect(handler.SendToHealthManagerTopic).To(Equal("alert")) Expect(builtAlert).To(Equal(handler.SendToHealthManagerPayload)) }) }) }) }
func init() { Describe("ApplyAction", func() { var ( applier *fakeappl.FakeApplier specService *fakeas.FakeV1Service settingsService *fakesettings.FakeSettingsService action ApplyAction ) BeforeEach(func() { applier = fakeappl.NewFakeApplier() specService = fakeas.NewFakeV1Service() settingsService = &fakesettings.FakeSettingsService{} action = NewApply(applier, specService, settingsService) }) It("apply should be asynchronous", func() { Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) Describe("Run", func() { settings := boshsettings.Settings{AgentID: "fake-agent-id"} BeforeEach(func() { settingsService.Settings = settings }) Context("when desired spec has configuration hash", func() { currentApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-current-config-hash"} desiredApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash"} populatedDesiredApplySpec := boshas.V1ApplySpec{ ConfigurationHash: "fake-populated-desired-config-hash", } Context("when current spec can be retrieved", func() { BeforeEach(func() { specService.Spec = currentApplySpec }) It("populates dynamic networks in desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(specService.PopulateDynamicNetworksSpec).To(Equal(desiredApplySpec)) Expect(specService.PopulateDynamicNetworksSettings).To(Equal(settings)) }) Context("when resolving dynamic networks succeeds", func() { BeforeEach(func() { specService.PopulateDynamicNetworksResultSpec = populatedDesiredApplySpec }) It("runs applier with populated desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(applier.Applied).To(BeTrue()) Expect(applier.ApplyCurrentApplySpec).To(Equal(currentApplySpec)) Expect(applier.ApplyDesiredApplySpec).To(Equal(populatedDesiredApplySpec)) }) Context("when applier succeeds applying desired spec", func() { Context("when saving desires spec as current spec succeeds", func() { It("returns 'applied' after setting populated desired spec as current spec", func() { value, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("applied")) Expect(specService.Spec).To(Equal(populatedDesiredApplySpec)) }) }) Context("when saving populated desires spec as current spec fails", func() { It("returns error because agent was not able to remember that is converged to desired spec", func() { specService.SetErr = errors.New("fake-set-error") _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-set-error")) }) }) }) Context("when applier fails applying desired spec", func() { BeforeEach(func() { applier.ApplyError = errors.New("fake-apply-error") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-apply-error")) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when resolving dynamic networks fails", func() { BeforeEach(func() { specService.PopulateDynamicNetworksErr = errors.New("fake-populate-dynamic-networks-err") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err")) }) It("does not apply desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when current spec cannot be retrieved", func() { BeforeEach(func() { specService.Spec = currentApplySpec specService.GetErr = errors.New("fake-get-error") }) It("returns error and does not apply desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-error")) }) It("does not run applier with desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when desired spec does not have a configuration hash", func() { desiredApplySpec := boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{ Template: "fake-job-template", }, } populatedDesiredApplySpec := boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{ Template: "fake-populated-job-template", }, } It("populates dynamic networks in desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(specService.PopulateDynamicNetworksSpec).To(Equal(desiredApplySpec)) Expect(specService.PopulateDynamicNetworksSettings).To(Equal(settings)) }) Context("when resolving dynamic networks succeeds", func() { BeforeEach(func() { specService.PopulateDynamicNetworksResultSpec = populatedDesiredApplySpec }) Context("when saving desires spec as current spec succeeds", func() { It("returns 'applied' after setting desired spec as current spec", func() { value, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("applied")) Expect(specService.Spec).To(Equal(populatedDesiredApplySpec)) }) It("does not try to apply desired spec since it does not have jobs and packages", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) }) Context("when saving desires spec as current spec fails", func() { BeforeEach(func() { specService.SetErr = errors.New("fake-set-error") }) It("returns error because agent was not able to remember that is converged to desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-set-error")) }) It("does not try to apply desired spec since it does not have jobs and packages", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) }) }) Context("when resolving dynamic networks fails", func() { BeforeEach(func() { specService.PopulateDynamicNetworksErr = errors.New("fake-populate-dynamic-networks-err") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err")) }) It("does not apply desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).ToNot(Equal(desiredApplySpec)) }) }) }) }) }) }
func init() { Describe("DrainAction", func() { var ( notifier *fakenotif.FakeNotifier specService *fakeas.FakeV1Service drainScriptProvider *fakedrain.FakeDrainScriptProvider jobSupervisor *fakejobsuper.FakeJobSupervisor action DrainAction ) BeforeEach(func() { notifier = fakenotif.NewFakeNotifier() specService = fakeas.NewFakeV1Service() drainScriptProvider = fakedrain.NewFakeDrainScriptProvider() jobSupervisor = fakejobsuper.NewFakeJobSupervisor() action = NewDrain(notifier, specService, drainScriptProvider, jobSupervisor) }) BeforeEach(func() { drainScriptProvider.NewDrainScriptDrainScript.ExistsBool = true }) It("is asynchronous", func() { Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) Context("when drain update is requested", func() { act := func() (int, error) { return action.Run(DrainTypeUpdate, boshas.V1ApplySpec{}) } Context("when current agent has a job spec template", func() { var currentSpec boshas.V1ApplySpec BeforeEach(func() { currentSpec = boshas.V1ApplySpec{} currentSpec.JobSpec.Template = "foo" specService.Spec = currentSpec }) It("unmonitors services so that drain scripts can kill processes on their own", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(jobSupervisor.Unmonitored).To(BeTrue()) }) Context("when unmonitoring services succeeds", func() { It("does not notify of job shutdown", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(notifier.NotifiedShutdown).To(BeFalse()) }) Context("when new apply spec is provided", func() { newSpec := boshas.V1ApplySpec{ PackageSpecs: map[string]boshas.PackageSpec{ "foo": boshas.PackageSpec{ Name: "foo", Sha1: "foo-sha1-new", }, }, } Context("when drain script exists", func() { It("runs drain script with job_shutdown param", func() { value, err := action.Run(DrainTypeUpdate, newSpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(drainScriptProvider.NewDrainScriptTemplateName).To(Equal("foo")) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeTrue()) params := drainScriptProvider.NewDrainScriptDrainScript.RunParams Expect(params).To(Equal(boshdrain.NewUpdateDrainParams(currentSpec, newSpec))) }) Context("when drain script runs and errs", func() { It("returns error", func() { drainScriptProvider.NewDrainScriptDrainScript.RunError = errors.New("fake-drain-run-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-drain-run-error")) Expect(value).To(Equal(0)) }) }) }) Context("when drain script does not exist", func() { It("returns 0", func() { drainScriptProvider.NewDrainScriptDrainScript.ExistsBool = false value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(0)) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeFalse()) }) }) }) Context("when apply spec is not provided", func() { It("returns error", func() { value, err := action.Run(DrainTypeUpdate) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Drain update requires new spec")) Expect(value).To(Equal(0)) }) }) }) Context("when unmonitoring services fails", func() { It("returns error", func() { jobSupervisor.UnmonitorErr = errors.New("fake-unmonitor-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-unmonitor-error")) Expect(value).To(Equal(0)) }) }) }) Context("when current agent spec does not have a job spec template", func() { It("returns 0 and does not run drain script", func() { specService.Spec = boshas.V1ApplySpec{} value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(0)) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeFalse()) }) }) }) Context("when drain shutdown is requested", func() { act := func() (int, error) { return action.Run(DrainTypeShutdown) } Context("when current agent has a job spec template", func() { var currentSpec boshas.V1ApplySpec BeforeEach(func() { currentSpec = boshas.V1ApplySpec{} currentSpec.JobSpec.Template = "foo" specService.Spec = currentSpec }) It("unmonitors services so that drain scripts can kill processes on their own", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(jobSupervisor.Unmonitored).To(BeTrue()) }) Context("when unmonitoring services succeeds", func() { It("notifies that job is about to shutdown", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(notifier.NotifiedShutdown).To(BeTrue()) }) Context("when job shutdown notification succeeds", func() { Context("when drain script exists", func() { It("runs drain script with job_shutdown param passing no apply spec", func() { value, err := action.Run(DrainTypeShutdown) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(drainScriptProvider.NewDrainScriptTemplateName).To(Equal("foo")) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeTrue()) params := drainScriptProvider.NewDrainScriptDrainScript.RunParams Expect(params).To(Equal(boshdrain.NewShutdownDrainParams(currentSpec, nil))) }) It("runs drain script with job_shutdown param passing in first apply spec", func() { newSpec := boshas.V1ApplySpec{} newSpec.JobSpec.Template = "fake-updated-template" value, err := action.Run(DrainTypeShutdown, newSpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(drainScriptProvider.NewDrainScriptTemplateName).To(Equal("foo")) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeTrue()) params := drainScriptProvider.NewDrainScriptDrainScript.RunParams Expect(params).To(Equal(boshdrain.NewShutdownDrainParams(currentSpec, &newSpec))) }) Context("when drain script runs and errs", func() { It("returns error", func() { drainScriptProvider.NewDrainScriptDrainScript.RunError = errors.New("fake-drain-run-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-drain-run-error")) Expect(value).To(Equal(0)) }) }) }) Context("when drain script does not exist", func() { It("returns 0", func() { drainScriptProvider.NewDrainScriptDrainScript.ExistsBool = false value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(0)) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeFalse()) }) }) }) Context("when job shutdown notification fails", func() { It("returns error if job shutdown notifications errs", func() { notifier.NotifyShutdownErr = errors.New("fake-shutdown-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-shutdown-error")) Expect(value).To(Equal(0)) }) }) }) Context("when unmonitoring services fails", func() { It("returns error", func() { jobSupervisor.UnmonitorErr = errors.New("fake-unmonitor-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-unmonitor-error")) Expect(value).To(Equal(0)) }) }) }) Context("when current agent spec does not have a job spec template", func() { It("returns 0 and does not run drain script", func() { specService.Spec = boshas.V1ApplySpec{} value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(0)) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeFalse()) }) }) }) Context("when drain status is requested", func() { act := func() (int, error) { return action.Run(DrainTypeStatus) } Context("when current agent has a job spec template", func() { var currentSpec boshas.V1ApplySpec BeforeEach(func() { currentSpec = boshas.V1ApplySpec{} currentSpec.JobSpec.Template = "foo" specService.Spec = currentSpec }) It("unmonitors services so that drain scripts can kill processes on their own", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(jobSupervisor.Unmonitored).To(BeTrue()) }) It("does not notify of job shutdown", func() { value, err := act() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(notifier.NotifiedShutdown).To(BeFalse()) }) Context("when unmonitoring services succeeds", func() { Context("when drain script exists", func() { It("runs drain script with job_check_status param passing no apply spec", func() { value, err := action.Run(DrainTypeStatus) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(drainScriptProvider.NewDrainScriptTemplateName).To(Equal("foo")) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeTrue()) params := drainScriptProvider.NewDrainScriptDrainScript.RunParams Expect(params).To(Equal(boshdrain.NewStatusDrainParams(currentSpec, nil))) }) It("runs drain script with job_check_status param passing in first apply spec", func() { newSpec := boshas.V1ApplySpec{} newSpec.JobSpec.Template = "fake-updated-template" value, err := action.Run(DrainTypeStatus, newSpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal(1)) Expect(drainScriptProvider.NewDrainScriptTemplateName).To(Equal("foo")) Expect(drainScriptProvider.NewDrainScriptDrainScript.DidRun).To(BeTrue()) params := drainScriptProvider.NewDrainScriptDrainScript.RunParams Expect(params).To(Equal(boshdrain.NewStatusDrainParams(currentSpec, &newSpec))) }) Context("when drain script runs and errs", func() { It("returns error if drain script errs", func() { drainScriptProvider.NewDrainScriptDrainScript.RunError = errors.New("fake-drain-run-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-drain-run-error")) Expect(value).To(Equal(0)) }) }) }) Context("when drain script does not exist", func() { It("returns error because drain status must be called after starting draining", func() { drainScriptProvider.NewDrainScriptDrainScript.ExistsBool = false value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Check Status on Drain action requires a valid drain script")) Expect(value).To(Equal(0)) }) }) }) Context("when unmonitoring services fails", func() { It("returns error if unmonitoring services errs", func() { jobSupervisor.UnmonitorErr = errors.New("fake-unmonitor-error") value, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-unmonitor-error")) Expect(value).To(Equal(0)) }) }) }) Context("when current agent spec does not have a job spec template", func() { It("returns error because drain status should only be called after starting draining", func() { specService.Spec = boshas.V1ApplySpec{} value, err := action.Run(DrainTypeStatus) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Check Status on Drain action requires job spec")) Expect(value).To(Equal(0)) }) }) }) }) }
It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) Describe("Run", func() { Context("when current spec can be retrieved", func() { Context("when vitals can be retrieved", func() { It("returns state", func() { settings.AgentID = "my-agent-id" settings.VM.Name = "vm-abc-def" jobSupervisor.StatusStatus = "running" specService.Spec = boshas.V1ApplySpec{ Deployment: "fake-deployment", } expectedSpec := GetStateV1ApplySpec{ V1ApplySpec: boshas.V1ApplySpec{ NetworkSpecs: map[string]boshas.NetworkSpec{}, ResourcePoolSpecs: map[string]interface{}{}, PackageSpecs: map[string]boshas.PackageSpec{}, }, 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",