Exemplo n.º 1
0
// New returns a new logger (that writes to the specified file) & the open file handle
// All log levels >= the specified level are written to the specified file.
// User is responsible for closing the returned file handle, unless an error is returned.
func New(level boshlog.LogLevel, filePath string, fileMode os.FileMode, fs boshsys.FileSystem) (boshlog.Logger, boshsys.File, error) {
	file, err := fs.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, fileMode)
	if err != nil {
		return nil, file, bosherr.WrapErrorf(err, "Failed to open log file '%s'", filePath)
	}

	return boshlog.NewWriterLogger(level, file, file), file, nil
}
Exemplo n.º 2
0
func basicDeps() (boshlog.Logger, boshsys.FileSystem, boshsys.CmdRunner) {
	logger := boshlog.NewWriterLogger(boshlog.LevelDebug, os.Stderr, os.Stderr)

	fs := boshsys.NewOsFileSystem(logger)

	cmdRunner := boshsys.NewExecCmdRunner(logger)

	return logger, fs, cmdRunner
}
Exemplo n.º 3
0
func basicDeps() (boshlog.Logger, boshsys.FileSystem, boshsys.CmdRunner, boshuuid.Generator) {
	logger := boshlog.NewWriterLogger(boshlog.LevelDebug, os.Stderr, os.Stderr)

	fs := boshsys.NewOsFileSystem(logger)

	runner := boshsys.NewExecCmdRunner(logger)

	uuidGen := boshuuid.NewGenerator()

	return logger, fs, runner, uuidGen
}
Exemplo n.º 4
0
		installationManifest      biinstallmanifest.Manifest
		mockJobRenderer           *mock_install.MockJobRenderer
		mockJobResolver           *mock_install.MockJobResolver
		mockPackageCompiler       *mock_install.MockPackageCompiler
		fakeExtractor             *fakeblobextract.FakeExtractor
		mockRegistryServerManager *mock_registry.MockServerManager

		logger boshlog.Logger

		installer    Installer
		target       Target
		installedJob InstalledJob
	)

	BeforeEach(func() {
		logger = boshlog.NewWriterLogger(boshlog.LevelDebug, GinkgoWriter, GinkgoWriter)

		mockJobRenderer = mock_install.NewMockJobRenderer(mockCtrl)
		mockJobResolver = mock_install.NewMockJobResolver(mockCtrl)
		mockPackageCompiler = mock_install.NewMockPackageCompiler(mockCtrl)
		fakeExtractor = &fakeblobextract.FakeExtractor{}
		mockRegistryServerManager = mock_registry.NewMockServerManager(mockCtrl)

		target = NewTarget("fake-installation-path")
		installationManifest = biinstallmanifest.Manifest{
			Name:       "fake-installation-name",
			Properties: biproperty.Map{},
		}
		renderedCPIJob := NewRenderedJobRef("cpi", "fake-release-job-fingerprint", "fake-rendered-job-blobstore-id", "fake-rendered-job-blobstore-id")
		installedJob = NewInstalledJob(renderedCPIJob, "/extracted-release-path/cpi")
	})
Exemplo n.º 5
0
func init() {
	Describe("App", func() {
		var (
			baseDir       string
			agentConfPath string
			agentConfJSON string
			app           App
		)

		BeforeEach(func() {
			var err error

			baseDir, err = ioutil.TempDir("", "go-agent-test")
			Expect(err).ToNot(HaveOccurred())

			err = os.Mkdir(filepath.Join(baseDir, "bosh"), os.ModePerm)
			Expect(err).ToNot(HaveOccurred())
		})

		BeforeEach(func() {
			agentConfPath = filepath.Join(baseDir, "bosh", "agent.json")

			agentConfJSON = `{
					"Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } }
				}`

			settingsPath := filepath.Join(baseDir, "bosh", "settings.json")

			settingsJSON := `{
				"agent_id": "my-agent-id",
				"blobstore": {
					"options": {
						"bucket_name": "george",
						"encryption_key": "optional encryption key",
						"access_key_id": "optional access key id",
						"secret_access_key": "optional secret access key"
					},
					"provider": "dummy"
				},
				"disks": {
					"ephemeral": "/dev/sdb",
					"persistent": {
						"vol-xxxxxx": "/dev/sdf"
					},
					"system": "/dev/sda1"
				},
				"env": {
					"bosh": {
						"password": "******"
					}
				},
				"networks": {
					"netA": {
						"default": ["dns", "gateway"],
						"ip": "ww.ww.ww.ww",
						"dns": [
							"xx.xx.xx.xx",
							"yy.yy.yy.yy"
						]
					},
					"netB": {
						"dns": [
							"zz.zz.zz.zz"
						]
					}
				},
				"Mbus": "https://*****:*****@0.0.0.0:6868",
				"ntp": [
					"0.north-america.pool.ntp.org",
					"1.north-america.pool.ntp.org"
				],
				"vm": {
					"name": "vm-abc-def"
				}
			}`

			err := ioutil.WriteFile(settingsPath, []byte(settingsJSON), 0640)
			Expect(err).ToNot(HaveOccurred())
		})

		JustBeforeEach(func() {
			err := ioutil.WriteFile(agentConfPath, []byte(agentConfJSON), 0640)
			Expect(err).ToNot(HaveOccurred())

			logger := boshlog.NewLogger(boshlog.LevelNone)
			fakefs := boshsys.NewOsFileSystem(logger)
			app = New(logger, fakefs)
		})

		AfterEach(func() {
			os.RemoveAll(baseDir)
		})

		It("Sets up device path resolver on platform specific to infrastructure", func() {
			err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
			Expect(err).ToNot(HaveOccurred())

			Expect(app.GetPlatform().GetDevicePathResolver()).To(Equal(devicepathresolver.NewIdentityDevicePathResolver()))
		})

		Context("when DevicePathResolutionType is 'virtio'", func() {
			BeforeEach(func() {
				agentConfJSON = `{
					"Platform": { "Linux": { "DevicePathResolutionType": "virtio" } },
					"Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } }
				}`
			})

			It("uses a VirtioDevicePathResolver", func() {
				err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
				Expect(err).ToNot(HaveOccurred())
				logLevel, err := boshlog.Levelify("DEBUG")
				Expect(err).NotTo(HaveOccurred())

				Expect(app.GetPlatform().GetDevicePathResolver()).To(
					BeAssignableToTypeOf(devicepathresolver.NewVirtioDevicePathResolver(nil, nil, boshlog.NewLogger(logLevel))))
			})
		})

		Context("when DevicePathResolutionType is 'scsi'", func() {
			BeforeEach(func() {
				agentConfJSON = `{
					"Platform": { "Linux": { "DevicePathResolutionType": "scsi" } },
					"Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } }
				}`
			})

			It("uses a VirtioDevicePathResolver", func() {
				err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
				Expect(err).ToNot(HaveOccurred())

				Expect(app.GetPlatform().GetDevicePathResolver()).To(
					BeAssignableToTypeOf(devicepathresolver.NewScsiDevicePathResolver(nil, nil, nil)))
			})
		})

		Context("logging stemcell version and git sha", func() {
			var (
				logger                  boshlog.Logger
				outBuf                  *bytes.Buffer
				fakeFs                  boshsys.FileSystem
				stemcellVersionFilePath string
				stemcellSha1FilePath    string
			)

			JustBeforeEach(func() {
				outBuf = bytes.NewBufferString("")
				errBuf := bytes.NewBufferString("")
				logger = boshlog.NewWriterLogger(boshlog.LevelDebug, outBuf, errBuf)
				fakeFs = fakesys.NewFakeFileSystem()
				dirProvider := boshdirs.NewProvider(baseDir)
				stemcellVersionFilePath = filepath.Join(dirProvider.EtcDir(), "stemcell_version")
				stemcellSha1FilePath = filepath.Join(dirProvider.EtcDir(), "stemcell_git_sha1")
				app = New(logger, fakeFs)
			})

			Context("when stemcell version and sha files are present", func() {
				It("should print out the stemcell version and sha in the logs", func() {
					fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah")
					fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah")
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: sha1-blah)"))
				})
			})

			Context("when stemcell version file is NOT present", func() {
				It("should print out the sha in the logs", func() {
					fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah")
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: sha1-blah)"))
				})
			})

			Context("when sha version file is NOT present", func() {
				It("should print out the stemcell version in the logs", func() {
					fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah")
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: ?)"))
				})
			})

			Context("when stemcell version file is empty", func() {
				It("should print out the sha in the logs", func() {
					fakeFs.WriteFileString(stemcellVersionFilePath, "")
					fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah")
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: sha1-blah)"))
				})
			})

			Context("when sha version file is empty", func() {
				It("should print out the stemcell version in the logs", func() {
					fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah")
					fakeFs.WriteFileString(stemcellSha1FilePath, "")
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: ?)"))
				})
			})

			Context("when stemcell version and sha files are NOT present", func() {
				It("should print unknown version and sha in the logs", func() {
					app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir})
					Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: ?)"))
				})
			})
		})
	})
}
			processConfigPath string
			jobSupervisor     JobSupervisor
			runner            boshsys.CmdRunner
			logOut            *bytes.Buffer
			logErr            *bytes.Buffer
		)

		BeforeEach(func() {
			once.Do(func() { Expect(buildPipeExe()).To(Succeed()) })

			const testExtPath = "testdata/job-service-wrapper"

			logOut = bytes.NewBufferString("")
			logErr = bytes.NewBufferString("")

			logger = boshlog.NewWriterLogger(boshlog.LevelDebug, logOut, logErr)
			fs = boshsys.NewOsFileSystem(logger)

			var err error
			basePath, err = ioutil.TempDir("", "")
			Expect(err).ToNot(HaveOccurred())
			fs.MkdirAll(basePath, 0755)

			binPath := filepath.Join(basePath, "bosh", "bin")
			fs.MkdirAll(binPath, 0755)

			logDir = path.Join(basePath, "sys", "log")
			fs.MkdirAll(binPath, 0755)

			exePath = filepath.Join(binPath, "job-service-wrapper.exe")
	var (
		outBuffer *bytes.Buffer
		errBuffer *bytes.Buffer
		logger    boshlog.Logger

		fakeSHA1Calculator *fakebicrypto.FakeSha1Calculator

		renderedJobList RenderedJobList

		renderedJobListCompressor RenderedJobListCompressor
	)

	BeforeEach(func() {
		outBuffer = bytes.NewBufferString("")
		errBuffer = bytes.NewBufferString("")
		logger = boshlog.NewWriterLogger(boshlog.LevelDebug, outBuffer, errBuffer)

		fakeSHA1Calculator = fakebicrypto.NewFakeSha1Calculator()

		renderedJobList = NewRenderedJobList()
	})

	Describe("Compress", func() {

		Context("with a real fs & compressor", func() {
			var (
				fs         boshsys.FileSystem
				cmdRunner  boshsys.CmdRunner
				compressor boshcmd.Compressor
			)
Exemplo n.º 8
0
package agentlogger_test

import (
	"bytes"
	"os"
	"syscall"

	"github.com/cloudfoundry/bosh-agent/infrastructure/agentlogger"
	"github.com/cloudfoundry/bosh-utils/logger"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Signalable logger debug", func() {
	Describe("when SIGSEGV is recieved", func() {
		It("it dumps all goroutines to stderr", func() {
			errBuf := new(bytes.Buffer)
			outBuf := new(bytes.Buffer)
			signalChannel := make(chan os.Signal, 1)
			writerLogger := logger.NewWriterLogger(logger.LevelError, outBuf, errBuf)
			_, doneChannel := agentlogger.NewSignalableLogger(writerLogger, signalChannel)

			signalChannel <- syscall.SIGSEGV
			<-doneChannel

			Expect(errBuf).To(ContainSubstring("Dumping goroutines"))
			Expect(errBuf).To(MatchRegexp(`goroutine (\d+) \[(syscall|running)\]`))
		})
	})
})
Exemplo n.º 9
0
		logger                     boshlog.Logger

		stage           Stage
		ui              UI
		fakeTimeService *fakeclock.FakeClock

		uiOut, uiErr *bytes.Buffer
	)

	BeforeEach(func() {
		uiOut = bytes.NewBufferString("")
		uiErr = bytes.NewBufferString("")

		logOutBuffer = bytes.NewBufferString("")
		logErrBuffer = bytes.NewBufferString("")
		logger = boshlog.NewWriterLogger(boshlog.LevelDebug, logOutBuffer, logErrBuffer)

		ui = NewWriterUI(uiOut, uiErr, logger)
		fakeTimeService = fakeclock.NewFakeClock(time.Now())

		stage = NewStage(ui, fakeTimeService, logger)
	})

	Describe("Perform", func() {
		It("prints a single-line stage", func() {
			actionsPerformed := []string{}

			err := stage.Perform("Simple stage 1", func() error {
				actionsPerformed = append(actionsPerformed, "1")
				fakeTimeService.Increment(time.Minute)
				return nil
Exemplo n.º 10
0
	})

	It("returns error if server fails to listen", func() {
		listenerProvider = func(protocol, Iaddr string) (net.Listener, error) {
			return nil, errors.New("Fail!")
		}
		server := NewServer(10, listenerProvider, logger)
		err := server.Start(nil)
		Expect(err).To(HaveOccurred())
		Expect(err.Error()).To(ContainSubstring("Fail!"))
	})

	It("logs parsing error to error log if parsing syslog message fails", func() {
		outBuf := bytes.NewBufferString("")
		errBuf := newLockedWriter(bytes.NewBufferString(""))
		logger := boshlog.NewWriterLogger(boshlog.LevelDebug, outBuf, errBuf)
		server = NewServer(serverPort, listenerProvider, logger)

		doneCh := make(chan struct{})
		go server.Start(captureNMsgs(msgs, doneCh, 2))

		conn, err := waitToDial()
		Expect(err).ToNot(HaveOccurred())

		fmt.Fprintf(conn, "<38>Jan  1 00:00:00 localhost sshd[22636]: msg1\n")
		fmt.Fprintf(conn, "invalid-syslog-format\n")
		fmt.Fprintf(conn, "<38>Jan  1 00:00:00 localhost sshd[22636]: msg2\n")

		<-doneCh

		err = server.Stop()
Exemplo n.º 11
0
func init() {
	Describe("natsHandler", func() {
		var (
			settingsService *fakesettings.FakeSettingsService
			client          *fakeyagnats.FakeYagnats
			logger          boshlog.Logger
			handler         boshhandler.Handler
			platform        *fakeplatform.FakePlatform
			loggerOutBuf    *bytes.Buffer
			loggerErrBuf    *bytes.Buffer
		)

		BeforeEach(func() {
			settingsService = &fakesettings.FakeSettingsService{
				Settings: boshsettings.Settings{
					AgentID: "my-agent-id",
					Mbus:    "nats://*****:*****@127.0.0.1:1234",
				},
			}

			loggerOutBuf = bytes.NewBufferString("")
			loggerErrBuf = bytes.NewBufferString("")
			logger = boshlog.NewWriterLogger(boshlog.LevelError, loggerOutBuf, loggerErrBuf)

			client = fakeyagnats.New()
			platform = fakeplatform.NewFakePlatform()
			handler = NewNatsHandler(settingsService, client, logger, platform)
		})

		Describe("Start", func() {
			It("starts", func() {
				var receivedRequest boshhandler.Request

				handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					receivedRequest = req
					return boshhandler.NewValueResponse("expected value")
				})
				defer handler.Stop()

				Expect(client.ConnectedConnectionProvider()).ToNot(BeNil())

				Expect(client.SubscriptionCount()).To(Equal(1))
				subscriptions := client.Subscriptions("agent.my-agent-id")
				Expect(len(subscriptions)).To(Equal(1))

				expectedPayload := []byte(`{"method":"ping","arguments":["foo","bar"], "reply_to": "reply to me!"}`)
				subscription := subscriptions[0]
				subscription.Callback(&yagnats.Message{
					Subject: "agent.my-agent-id",
					Payload: expectedPayload,
				})

				Expect(receivedRequest).To(Equal(boshhandler.Request{
					ReplyTo: "reply to me!",
					Method:  "ping",
					Payload: expectedPayload,
				}))

				Expect(client.PublishedMessageCount()).To(Equal(1))
				messages := client.PublishedMessages("reply to me!")
				Expect(len(messages)).To(Equal(1))
				Expect(messages[0].Payload).To(Equal([]byte(`{"value":"expected value"}`)))
			})

			It("cleans up ip-mac address cache for nats configured with ip address", func() {
				handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					return nil
				})
				defer handler.Stop()

				Expect(platform.CleanedIPMacAddressCache).To(Equal("127.0.0.1"))
				Expect(client.ConnectedConnectionProvider()).ToNot(BeNil())
			})

			It("does not try to clean up ip-mac address cache for nats configured with hostname", func() {
				settingsService.Settings.Mbus = "nats://*****:*****@fake-hostname.com:1234"
				handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					return nil
				})
				defer handler.Stop()

				Expect(platform.CleanedIPMacAddressCache).To(BeEmpty())
				Expect(client.ConnectedConnectionProvider()).ToNot(BeNil())
			})

			It("logs error and proceeds if it fails to clean up ip-mac address cache for nats", func() {
				platform.CleanIPMacAddressCacheErr = errors.New("failed to run")
				handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					return nil
				})
				defer handler.Stop()

				Expect(platform.CleanedIPMacAddressCache).To(Equal("127.0.0.1"))
				Expect(loggerErrBuf).To(ContainSubstring("ERROR - Cleaning ip-mac address cache for: 127.0.0.1"))
				Expect(client.ConnectedConnectionProvider()).ToNot(BeNil())
			})

			It("does not respond if the response is nil", func() {
				err := handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					return nil
				})
				Expect(err).ToNot(HaveOccurred())
				defer handler.Stop()

				subscription := client.Subscriptions("agent.my-agent-id")[0]
				subscription.Callback(&yagnats.Message{
					Subject: "agent.my-agent-id",
					Payload: []byte(`{"method":"ping","arguments":["foo","bar"], "reply_to": "reply to me!"}`),
				})

				Expect(client.PublishedMessageCount()).To(Equal(0))
			})

			It("responds with an error if the response is bigger than 1MB", func() {
				err := handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					// gets inflated by json.Marshal when enveloping
					size := 0

					switch req.Method {
					case "small":
						size = 1024*1024 - 12
					case "big":
						size = 1024 * 1024
					default:
						panic("unknown request size")
					}

					chars := make([]byte, size)
					for i := range chars {
						chars[i] = 'A'
					}
					return boshhandler.NewValueResponse(string(chars))
				})
				Expect(err).ToNot(HaveOccurred())
				defer handler.Stop()

				subscription := client.Subscriptions("agent.my-agent-id")[0]
				subscription.Callback(&yagnats.Message{
					Subject: "agent.my-agent-id",
					Payload: []byte(`{"method":"small","arguments":[], "reply_to": "fake-reply-to"}`),
				})

				subscription.Callback(&yagnats.Message{
					Subject: "agent.my-agent-id",
					Payload: []byte(`{"method":"big","arguments":[], "reply_to": "fake-reply-to"}`),
				})

				Expect(client.PublishedMessageCount()).To(Equal(1))
				messages := client.PublishedMessages("fake-reply-to")
				Expect(len(messages)).To(Equal(2))
				Expect(messages[0].Payload).To(MatchRegexp("value"))
				Expect(messages[1].Payload).To(Equal([]byte(
					`{"exception":{"message":"Response exceeded maximum allowed length"}}`)))
			})

			It("can add additional handler funcs to receive requests", func() {
				var firstHandlerReq, secondHandlerRequest boshhandler.Request

				handler.Start(func(req boshhandler.Request) (resp boshhandler.Response) {
					firstHandlerReq = req
					return boshhandler.NewValueResponse("first-handler-resp")
				})
				defer handler.Stop()

				handler.RegisterAdditionalFunc(func(req boshhandler.Request) (resp boshhandler.Response) {
					secondHandlerRequest = req
					return boshhandler.NewValueResponse("second-handler-resp")
				})

				expectedPayload := []byte(`{"method":"ping","arguments":["foo","bar"], "reply_to": "fake-reply-to"}`)

				subscription := client.Subscriptions("agent.my-agent-id")[0]
				subscription.Callback(&yagnats.Message{
					Subject: "agent.my-agent-id",
					Payload: expectedPayload,
				})

				// Expected requests received by both handlers
				Expect(firstHandlerReq).To(Equal(boshhandler.Request{
					ReplyTo: "fake-reply-to",
					Method:  "ping",
					Payload: expectedPayload,
				}))

				Expect(secondHandlerRequest).To(Equal(boshhandler.Request{
					ReplyTo: "fake-reply-to",
					Method:  "ping",
					Payload: expectedPayload,
				}))

				// Bosh handler responses were sent
				Expect(client.PublishedMessageCount()).To(Equal(1))
				messages := client.PublishedMessages("fake-reply-to")
				Expect(len(messages)).To(Equal(2))
				Expect(messages[0].Payload).To(Equal([]byte(`{"value":"first-handler-resp"}`)))
				Expect(messages[1].Payload).To(Equal([]byte(`{"value":"second-handler-resp"}`)))
			})

			It("has the correct connection info", func() {
				err := handler.Start(func(req boshhandler.Request) (res boshhandler.Response) { return })
				Expect(err).ToNot(HaveOccurred())
				defer handler.Stop()

				Expect(client.ConnectedConnectionProvider()).To(Equal(&yagnats.ConnectionInfo{
					Addr:     "127.0.0.1:1234",
					Username: "******",
					Password: "******",
				}))
			})

			It("does not err when no username and password", func() {
				settingsService.Settings.Mbus = "nats://127.0.0.1:1234"
				handler = NewNatsHandler(settingsService, client, logger, platform)

				err := handler.Start(func(req boshhandler.Request) (res boshhandler.Response) { return })
				Expect(err).ToNot(HaveOccurred())
				defer handler.Stop()
			})

			It("errs when has username without password", func() {
				settingsService.Settings.Mbus = "nats://[email protected]:1234"
				handler = NewNatsHandler(settingsService, client, logger, platform)

				err := handler.Start(func(req boshhandler.Request) (res boshhandler.Response) { return })
				Expect(err).To(HaveOccurred())
				defer handler.Stop()
			})
		})

		Describe("Send", func() {
			It("sends the message over nats to a subject that includes the target and topic", func() {
				errCh := make(chan error, 1)

				payload := map[string]string{"key1": "value1", "keyA": "valueA"}

				go func() {
					errCh <- handler.Send(boshhandler.HealthMonitor, boshhandler.Heartbeat, payload)
				}()

				var err error
				select {
				case err = <-errCh:
				}
				Expect(err).ToNot(HaveOccurred())

				Expect(client.PublishedMessageCount()).To(Equal(1))
				messages := client.PublishedMessages("hm.agent.heartbeat.my-agent-id")
				Expect(messages).To(HaveLen(1))
				Expect(messages[0].Payload).To(Equal(
					[]byte("{\"key1\":\"value1\",\"keyA\":\"valueA\"}"),
				))
			})
		})
	})
}