})
	})

	Describe("Alert", func() {

		itAdaptsMessage := func(msgContent, expectedTitle string) {
			sshMsg := boshsyslog.Msg{Content: msgContent}
			sshAdapter := NewSSHAdapter(
				sshMsg,
				settingsService,
				uuidGenerator,
				timeService,
				logger,
			)

			uuidGenerator.GeneratedUUID = "fake-uuid"

			builtAlert, err := sshAdapter.Alert()
			Expect(err).ToNot(HaveOccurred())
			Expect(builtAlert.Title).To(Equal(expectedTitle))
			Expect(builtAlert.ID).To(Equal("fake-uuid"))
			Expect(builtAlert.Severity).To(Equal(SeverityWarning))
			Expect(builtAlert.Summary).To(Equal(msgContent))
			Expect(builtAlert.CreatedAt).To(Equal(timeService.Now().Unix()))
		}

		It("Returns logout when the user disconnects", func() {
			itAdaptsMessage("Received disconnect from 9.9.9.9: 11: disconnected by user", "SSH Logout")
		})

		It("Returns login when login is successful (publickey)", func() {
			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())
		})
	})

	Describe("Create", func() {
		It("creates the local blob", func() {
			fs.WriteFileString("/fake-file.txt", "fake-file-contents")

			uuidGen.GeneratedUUID = "some-uuid"

			blobID, fingerprint, err := blobstore.Create("/fake-file.txt")
			Expect(err).ToNot(HaveOccurred())
			Expect(blobID).To(Equal("some-uuid"))
			Expect(fingerprint).To(BeEmpty())

			dirStats := fs.GetFileTestStat(fakeBlobstorePath)
			Expect(dirStats).ToNot(BeNil())
			Expect(dirStats.FileMode).To(Equal(os.FileMode(0770)))

			writtenFileStats := fs.GetFileTestStat(fakeBlobstorePath + "/some-uuid")
			Expect(writtenFileStats).ToNot(BeNil())
			Expect("fake-file-contents").To(Equal(writtenFileStats.StringContents()))
		})
func init() {
	Describe("asyncTaskService", func() {
		var (
			uuidGen *fakeuuid.FakeGenerator
			service Service
		)

		BeforeEach(func() {
			uuidGen = &fakeuuid.FakeGenerator{}
			service = NewAsyncTaskService(uuidGen, boshlog.NewLogger(boshlog.LevelNone))
		})

		Describe("StartTask", func() {
			startAndWaitForTaskCompletion := func(task Task) Task {
				service.StartTask(task)
				for task.State == StateRunning {
					time.Sleep(time.Nanosecond)
					task, _ = service.FindTaskWithID(task.ID)
				}
				return task
			}

			It("sets return value on a successful task", func() {
				runFunc := func() (interface{}, error) { return 123, nil }

				task, err := service.CreateTask(runFunc, nil, nil)
				Expect(err).ToNot(HaveOccurred())

				task = startAndWaitForTaskCompletion(task)
				Expect(task.State).To(BeEquivalentTo(StateDone))
				Expect(task.Value).To(Equal(123))
				Expect(task.Error).To(BeNil())
			})

			It("sets task error on a failing task", func() {
				err := errors.New("fake-error")
				runFunc := func() (interface{}, error) { return nil, err }

				task, createErr := service.CreateTask(runFunc, nil, nil)
				Expect(createErr).ToNot(HaveOccurred())

				task = startAndWaitForTaskCompletion(task)
				Expect(task.State).To(BeEquivalentTo(StateFailed))
				Expect(task.Value).To(BeNil())
				Expect(task.Error).To(Equal(err))
			})

			Describe("CreateTask", func() {
				It("can run task created with CreateTask which does not have end func", func() {
					ranFunc := false
					runFunc := func() (interface{}, error) { ranFunc = true; return nil, nil }

					task, err := service.CreateTask(runFunc, nil, nil)
					Expect(err).ToNot(HaveOccurred())

					startAndWaitForTaskCompletion(task)
					Expect(ranFunc).To(BeTrue())
				})

				It("can run task created with CreateTask which has end func", func() {
					ranFunc := false
					runFunc := func() (interface{}, error) { ranFunc = true; return nil, nil }

					ranEndFunc := false
					endFunc := func(Task) { ranEndFunc = true }

					task, err := service.CreateTask(runFunc, nil, endFunc)
					Expect(err).ToNot(HaveOccurred())

					startAndWaitForTaskCompletion(task)
					Expect(ranFunc).To(BeTrue())
					Expect(ranEndFunc).To(BeTrue())
				})

				It("returns an error if generate uuid fails", func() {
					uuidGen.GenerateError = errors.New("fake-generate-uuid-error")
					_, err := service.CreateTask(nil, nil, nil)
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("fake-generate-uuid-error"))
				})
			})

			Describe("CreateTaskWithID", func() {
				It("can run task created with CreateTaskWithID which does not have end func", func() {
					ranFunc := false
					runFunc := func() (interface{}, error) { ranFunc = true; return nil, nil }

					task := service.CreateTaskWithID("fake-task-id", runFunc, nil, nil)

					startAndWaitForTaskCompletion(task)
					Expect(ranFunc).To(BeTrue())
				})

				It("can run task created with CreateTaskWithID which has end func", func() {
					ranFunc := false
					runFunc := func() (interface{}, error) { ranFunc = true; return nil, nil }

					ranEndFunc := false
					endFunc := func(Task) { ranEndFunc = true }

					task := service.CreateTaskWithID("fake-task-id", runFunc, nil, endFunc)

					startAndWaitForTaskCompletion(task)
					Expect(ranFunc).To(BeTrue())
					Expect(ranEndFunc).To(BeTrue())
				})
			})

			It("can process many tasks simultaneously", func() {
				taskFunc := func() (interface{}, error) {
					time.Sleep(10 * time.Millisecond)
					return nil, nil
				}

				ids := []string{}
				for id := 1; id < 200; id++ {
					idStr := fmt.Sprintf("%d", id)
					uuidGen.GeneratedUUID = idStr
					ids = append(ids, idStr)

					task, err := service.CreateTask(taskFunc, nil, nil)
					Expect(err).ToNot(HaveOccurred())
					go service.StartTask(task)
				}

				for {
					allDone := true
					for _, id := range ids {
						task, _ := service.FindTaskWithID(id)
						if task.State != StateDone {
							allDone = false
							break
						}
					}

					if allDone {
						break
					}
					time.Sleep(200 * time.Millisecond)
				}
			})
		})

		Describe("CreateTask", func() {
			It("creates a task with auto-assigned id", func() {
				uuidGen.GeneratedUUID = "fake-uuid"

				runFuncCalled := false
				runFunc := func() (interface{}, error) {
					runFuncCalled = true
					return nil, nil
				}

				cancelFuncCalled := false
				cancelFunc := func(_ Task) error {
					cancelFuncCalled = true
					return nil
				}

				endFuncCalled := false
				endFunc := func(_ Task) {
					endFuncCalled = true
				}

				task, err := service.CreateTask(runFunc, cancelFunc, endFunc)
				Expect(err).ToNot(HaveOccurred())
				Expect(task.ID).To(Equal("fake-uuid"))
				Expect(task.State).To(Equal(StateRunning))

				task.Func()
				Expect(runFuncCalled).To(BeTrue())

				task.CancelFunc(task)
				Expect(cancelFuncCalled).To(BeTrue())

				task.EndFunc(task)
				Expect(endFuncCalled).To(BeTrue())
			})
		})

		Describe("CreateTaskWithID", func() {
			It("creates a task with given id", func() {
				runFuncCalled := false
				runFunc := func() (interface{}, error) {
					runFuncCalled = true
					return nil, nil
				}

				cancelFuncCalled := false
				cancelFunc := func(_ Task) error {
					cancelFuncCalled = true
					return nil
				}

				endFuncCalled := false
				endFunc := func(_ Task) {
					endFuncCalled = true
				}

				task := service.CreateTaskWithID("fake-task-id", runFunc, cancelFunc, endFunc)
				Expect(task.ID).To(Equal("fake-task-id"))
				Expect(task.State).To(Equal(StateRunning))

				task.Func()
				Expect(runFuncCalled).To(BeTrue())

				task.CancelFunc(task)
				Expect(cancelFuncCalled).To(BeTrue())

				task.EndFunc(task)
				Expect(endFuncCalled).To(BeTrue())
			})
		})
	})
}
Beispiel #4
0
func init() {
	Describe("Agent", func() {
		var (
			logger           boshlog.Logger
			handler          *fakembus.FakeHandler
			platform         *fakeplatform.FakePlatform
			actionDispatcher *fakeagent.FakeActionDispatcher
			jobSupervisor    *fakejobsuper.FakeJobSupervisor
			specService      *fakeas.FakeV1Service
			syslogServer     *fakesyslog.FakeServer
			settingsService  *fakesettings.FakeSettingsService
			uuidGenerator    *fakeuuid.FakeGenerator
			timeService      *fakeclock.FakeClock
			agent            Agent
		)

		BeforeEach(func() {
			logger = boshlog.NewLogger(boshlog.LevelNone)
			handler = &fakembus.FakeHandler{}
			platform = fakeplatform.NewFakePlatform()
			actionDispatcher = &fakeagent.FakeActionDispatcher{}
			jobSupervisor = fakejobsuper.NewFakeJobSupervisor()
			specService = fakeas.NewFakeV1Service()
			syslogServer = &fakesyslog.FakeServer{}
			settingsService = &fakesettings.FakeSettingsService{}
			uuidGenerator = &fakeuuid.FakeGenerator{}
			timeService = fakeclock.NewFakeClock(time.Now())
			agent = New(
				logger,
				handler,
				platform,
				actionDispatcher,
				jobSupervisor,
				specService,
				syslogServer,
				5*time.Millisecond,
				settingsService,
				uuidGenerator,
				timeService,
			)
		})

		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 := 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,
						jobSupervisor,
						specService,
						syslogServer,
						5*time.Hour,
						settingsService,
						uuidGenerator,
						timeService,
					)

					// Immediately exit after sending initial heartbeat
					handler.SendErr = errors.New("stop")

					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("stop"))

					Expect(handler.SendInputs()).To(Equal([]fakembus.SendInput{
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
					}))
				})

				It("sends periodic heartbeats", func() {
					sentRequests := 0
					handler.SendCallback = func(_ fakembus.SendInput) {
						sentRequests++
						if sentRequests == 3 {
							handler.SendErr = errors.New("stop")
						}
					}

					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("stop"))

					Expect(handler.SendInputs()).To(Equal([]fakembus.SendInput{
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: 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 monitoring alerts to health manager", func() {
				handler.KeepOnRunning()

				monitAlert := boshalert.MonitAlert{
					ID:          "fake-monit-alert",
					Service:     "fake-service",
					Event:       "fake-event",
					Action:      "fake-action",
					Date:        "Sun, 22 May 2011 20:07:41 +0500",
					Description: "fake-description",
				}
				jobSupervisor.JobFailureAlert = &monitAlert

				// Fail the first time handler.Send is called for an alert (ignore heartbeats)
				handler.SendCallback = func(input fakembus.SendInput) {
					if input.Topic == boshhandler.Alert {
						handler.SendErr = errors.New("stop")
					}
				}

				err := agent.Run()
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("stop"))

				expectedAlert := boshalert.Alert{
					ID:        "fake-monit-alert",
					Severity:  boshalert.SeverityDefault,
					Title:     "fake-service - fake-event - fake-action",
					Summary:   "fake-description",
					CreatedAt: int64(1306076861),
				}

				Expect(handler.SendInputs()).To(ContainElement(fakembus.SendInput{
					Target:  boshhandler.HealthMonitor,
					Topic:   boshhandler.Alert,
					Message: expectedAlert,
				}))
			})

			It("sends ssh alerts to health manager", func() {
				handler.KeepOnRunning()

				syslogMsg := boshsyslog.Msg{Content: "disconnected by user"}
				syslogServer.StartFirstSyslogMsg = &syslogMsg

				uuidGenerator.GeneratedUUID = "fake-uuid"

				// Fail the first time handler.Send is called for an alert (ignore heartbeats)
				handler.SendCallback = func(input fakembus.SendInput) {
					if input.Topic == boshhandler.Alert {
						handler.SendErr = errors.New("stop")
					}
				}

				err := agent.Run()
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("stop"))

				expectedAlert := boshalert.Alert{
					ID:        "fake-uuid",
					Severity:  boshalert.SeverityWarning,
					Title:     "SSH Logout",
					Summary:   "disconnected by user",
					CreatedAt: timeService.Now().Unix(),
				}

				Expect(handler.SendInputs()).To(ContainElement(fakembus.SendInput{
					Target:  boshhandler.HealthMonitor,
					Topic:   boshhandler.Alert,
					Message: expectedAlert,
				}))
			})
		})
	})
}