func init() { Describe("renderedJobApplier", func() { var ( jobsBc *fakebc.FakeBundleCollection jobSupervisor *fakejobsuper.FakeJobSupervisor packageApplierProvider *fakepa.FakePackageApplierProvider blobstore *fakeblob.FakeBlobstore compressor *fakecmd.FakeCompressor fs *fakesys.FakeFileSystem applier JobApplier ) BeforeEach(func() { jobsBc = fakebc.NewFakeBundleCollection() jobSupervisor = fakejobsuper.NewFakeJobSupervisor() packageApplierProvider = fakepa.NewFakePackageApplierProvider() blobstore = fakeblob.NewFakeBlobstore() fs = fakesys.NewFakeFileSystem() compressor = fakecmd.NewFakeCompressor() logger := boshlog.NewLogger(boshlog.LevelNone) applier = NewRenderedJobApplier( jobsBc, jobSupervisor, packageApplierProvider, blobstore, compressor, fs, logger, ) }) Describe("Prepare & Apply", func() { var ( job models.Job bundle *fakebc.FakeBundle ) BeforeEach(func() { job, bundle = buildJob(jobsBc) }) ItInstallsJob := func(act func() error) { It("returns error when installing job fails", func() { bundle.InstallError = errors.New("fake-install-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-install-error")) }) It("downloads and later cleans up downloaded job template blob", func() { blobstore.GetFileName = "/fake-blobstore-file-name" err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("fake-blobstore-id")) Expect(blobstore.GetFingerprints[0]).To(Equal("fake-blob-sha1")) // downloaded file is cleaned up Expect(blobstore.CleanUpFileName).To(Equal("/fake-blobstore-file-name")) }) It("returns error when downloading job template blob fails", func() { blobstore.GetError = errors.New("fake-get-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-error")) }) It("decompresses job template blob to tmp path and later cleans it up", func() { fs.TempDirDir = "/fake-tmp-dir" blobstore.GetFileName = "/fake-blobstore-file-name" var tmpDirExistsBeforeInstall bool bundle.InstallCallBack = func() { tmpDirExistsBeforeInstall = true } err := act() Expect(err).ToNot(HaveOccurred()) Expect(compressor.DecompressFileToDirTarballPaths[0]).To(Equal("/fake-blobstore-file-name")) Expect(compressor.DecompressFileToDirDirs[0]).To(Equal("/fake-tmp-dir")) // tmp dir exists before bundle install Expect(tmpDirExistsBeforeInstall).To(BeTrue()) // tmp dir is cleaned up after install Expect(fs.FileExists(fs.TempDirDir)).To(BeFalse()) }) It("returns error when temporary directory creation fails", func() { fs.TempDirError = errors.New("fake-filesystem-tempdir-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-filesystem-tempdir-error")) }) It("returns error when decompressing job template fails", func() { compressor.DecompressFileToDirErr = errors.New("fake-decompress-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-decompress-error")) }) It("returns error when getting the list of bin files fails", func() { fs.GlobErr = errors.New("fake-glob-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-glob-error")) }) It("returns error when changing permissions on bin files fails", func() { fs.TempDirDir = "/fake-tmp-dir" fs.SetGlob("/fake-tmp-dir/fake-path-in-archive/bin/*", []string{ "/fake-tmp-dir/fake-path-in-archive/bin/test", }) fs.ChmodErr = errors.New("fake-chmod-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-chmod-error")) }) It("installs bundle from decompressed tmp path of a job template", func() { fs.TempDirDir = "/fake-tmp-dir" var installedBeforeDecompression bool compressor.DecompressFileToDirCallBack = func() { installedBeforeDecompression = bundle.Installed } err := act() Expect(err).ToNot(HaveOccurred()) // bundle installation did not happen before decompression Expect(installedBeforeDecompression).To(BeFalse()) // make sure that bundle install happened after decompression Expect(bundle.InstallSourcePath).To(Equal("/fake-tmp-dir/fake-path-in-archive")) }) It("sets executable bit for files in bin", func() { fs.TempDirDir = "/fake-tmp-dir" compressor.DecompressFileToDirCallBack = func() { fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/bin/test1", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/bin/test2", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/config/test", []byte{}) } fs.SetGlob("/fake-tmp-dir/fake-path-in-archive/bin/*", []string{ "/fake-tmp-dir/fake-path-in-archive/bin/test1", "/fake-tmp-dir/fake-path-in-archive/bin/test2", }) var binTest1Stats, binTest2Stats, configTestStats *fakesys.FakeFileStats bundle.InstallCallBack = func() { binTest1Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/bin/test1") binTest2Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/bin/test2") configTestStats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/config/test") } err := act() Expect(err).ToNot(HaveOccurred()) // bin files are executable Expect(int(binTest1Stats.FileMode)).To(Equal(0755)) Expect(int(binTest2Stats.FileMode)).To(Equal(0755)) // non-bin files are not made executable Expect(int(configTestStats.FileMode)).ToNot(Equal(0755)) }) } ItUpdatesPackages := func(act func() error) { var packageApplier *fakepa.FakePackageApplier BeforeEach(func() { packageApplier = fakepa.NewFakePackageApplier() packageApplierProvider.JobSpecificPackageAppliers[job.Name] = packageApplier }) It("applies each package that job depends on and then cleans up packages", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(packageApplier.ActionsCalled).To(Equal([]string{"Apply", "Apply", "KeepOnly"})) Expect(len(packageApplier.AppliedPackages)).To(Equal(2)) // present Expect(packageApplier.AppliedPackages).To(Equal(job.Packages)) }) It("returns error when applying package that job depends on fails", func() { packageApplier.ApplyError = errors.New("fake-apply-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-apply-err")) }) It("keeps only currently required packages but does not completely uninstall them", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(len(packageApplier.KeptOnlyPackages)).To(Equal(2)) // present Expect(packageApplier.KeptOnlyPackages).To(Equal(job.Packages)) }) It("returns error when keeping only currently required packages fails", func() { packageApplier.KeepOnlyErr = errors.New("fake-keep-only-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-keep-only-err")) }) } Describe("Prepare", func() { act := func() error { return applier.Prepare(job) } It("return an error if getting file bundle fails", func() { jobsBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for installed path fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when job is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{})) // no Install }) It("does not download the job template", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when job is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs job (but does not enable)", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install"})) }) ItInstallsJob(act) }) }) Describe("Apply", func() { act := func() error { return applier.Apply(job) } It("return an error if getting file bundle fails", func() { jobsBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for installed path fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when job is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install but only enables job", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Enable"})) // no Install }) It("returns error when job enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) It("does not download the job template", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) ItUpdatesPackages(act) }) Context("when job is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs and enables job", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install", "Enable"})) }) It("returns error when job enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) ItInstallsJob(act) ItUpdatesPackages(act) }) }) }) Describe("Configure", func() { It("adds job to the job supervisor", func() { job, bundle := buildJob(jobsBc) fs := fakesys.NewFakeFileSystem() fs.WriteFileString("/path/to/job/monit", "some conf") fs.SetGlob("/path/to/job/*.monit", []string{"/path/to/job/subjob.monit"}) bundle.GetDirPath = "/path/to/job" bundle.GetDirFs = fs err := applier.Configure(job, 0) Expect(err).ToNot(HaveOccurred()) Expect(len(jobSupervisor.AddJobArgs)).To(Equal(2)) Expect(jobSupervisor.AddJobArgs[0]).To(Equal(fakejobsuper.AddJobArgs{ Name: job.Name, Index: 0, ConfigPath: "/path/to/job/monit", })) Expect(jobSupervisor.AddJobArgs[1]).To(Equal(fakejobsuper.AddJobArgs{ Name: job.Name + "_subjob", Index: 0, ConfigPath: "/path/to/job/subjob.monit", })) }) It("does not require monit script", func() { job, bundle := buildJob(jobsBc) fs := fakesys.NewFakeFileSystem() bundle.GetDirFs = fs err := applier.Configure(job, 0) Expect(err).ToNot(HaveOccurred()) Expect(len(jobSupervisor.AddJobArgs)).To(Equal(0)) }) }) Describe("KeepOnly", func() { It("first disables and then uninstalls jobs that are not in keeponly list", func() { _, bundle1 := buildJob(jobsBc) job2, bundle2 := buildJob(jobsBc) _, bundle3 := buildJob(jobsBc) job4, bundle4 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Job{job4, job2}) Expect(err).ToNot(HaveOccurred()) Expect(bundle1.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle2.ActionsCalled).To(Equal([]string{})) Expect(bundle3.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle4.ActionsCalled).To(Equal([]string{})) }) It("returns error when bundle collection fails to return list of installed bundles", func() { jobsBc.ListErr = errors.New("fake-bc-list-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-list-error")) }) It("returns error when bundle collection cannot retrieve bundle for keep-only job", func() { job1, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} jobsBc.GetErr = errors.New("fake-bc-get-error") err := applier.KeepOnly([]models.Job{job1}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-get-error")) }) It("returns error when at least one bundle cannot be disabled", func() { _, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.DisableErr = errors.New("fake-bc-disable-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-disable-error")) }) It("returns error when at least one bundle cannot be uninstalled", func() { _, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.UninstallErr = errors.New("fake-bc-uninstall-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-uninstall-error")) }) }) }) }
Expect(err).ToNot(HaveOccurred()) defer file.Close() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { io.Copy(w, file) Expect(r.Method).To(Equal("GET")) Expect(r.URL.Path).To(Equal("/_status2")) Expect(r.URL.Query().Get("format")).To(Equal("xml")) }) ts := httptest.NewServer(handler) defer ts.Close() logger := boshlog.NewLogger(boshlog.LevelNone) client := NewHTTPClient( ts.Listener.Addr().String(), "fake-user", "fake-pass", http.DefaultClient, 1*time.Millisecond, logger, ) status, err := client.Status() Expect(err).ToNot(HaveOccurred()) expectedServices := []Service{ Service{Monitored: true, Status: "running"}, Service{Monitored: false, Status: "unknown"},
func init() { Describe("concreteManagerProvider", func() { Describe("NewManager", func() { It("returns manager with tasks.json as its tasks path", func() { logger := boshlog.NewLogger(boshlog.LevelNone) fs := fakesys.NewFakeFileSystem() taskInfo := boshtask.TaskInfo{ TaskID: "fake-task-id", Method: "fake-method", Payload: []byte("fake-payload"), } manager := boshtask.NewManagerProvider().NewManager(logger, fs, "/dir/path") err := manager.AddTaskInfo(taskInfo) Expect(err).ToNot(HaveOccurred()) // Check expected file location with another manager otherManager := boshtask.NewManager(logger, fs, "/dir/path/tasks.json") taskInfos, err := otherManager.GetTaskInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(Equal([]boshtask.TaskInfo{taskInfo})) }) }) }) Describe("concreteManager", func() { var ( logger boshlog.Logger fs *fakesys.FakeFileSystem manager boshtask.Manager ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) fs = fakesys.NewFakeFileSystem() manager = boshtask.NewManager(logger, fs, "/dir/path") }) Describe("GetTaskInfos", func() { It("can load multiple tasks", func() { err := manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) // Make sure we are not getting cached copy of taskInfos reloadedManager := boshtask.NewManager(logger, fs, "/dir/path") taskInfos, err := reloadedManager.GetTaskInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(Equal([]boshtask.TaskInfo{ boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }, boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("succeeds when there is no tasks (file is not present)", func() { taskInfos, err := manager.GetTaskInfos() Expect(err).ToNot(HaveOccurred()) Expect(len(taskInfos)).To(Equal(0)) }) It("returns an error when failing to load tasks from the file that exists", func() { err := manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) fs.ReadFileError = errors.New("fake-read-error") _, err = manager.GetTaskInfos() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-read-error")) }) }) Describe("AddTaskInfo", func() { It("can add multiple tasks", func() { err := manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) content, err := fs.ReadFile("/dir/path") Expect(err).ToNot(HaveOccurred()) var decodedMap map[string]boshtask.TaskInfo err = json.Unmarshal(content, &decodedMap) Expect(err).ToNot(HaveOccurred()) Expect(decodedMap).To(Equal(map[string]boshtask.TaskInfo{ "fake-task-id-1": boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }, "fake-task-id-2": boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("returns an error when failing to save task", func() { fs.WriteToFileError = errors.New("fake-write-error") err := manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id", Method: "fake-method", Payload: []byte("fake-payload"), }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-write-error")) }) }) Describe("RemoveTaskInfo", func() { BeforeEach(func() { err := manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddTaskInfo(boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) }) It("removes the task", func() { err := manager.RemoveTaskInfo("fake-task-id-1") Expect(err).ToNot(HaveOccurred()) content, err := fs.ReadFile("/dir/path") Expect(err).ToNot(HaveOccurred()) var decodedMap map[string]boshtask.TaskInfo err = json.Unmarshal(content, &decodedMap) Expect(err).ToNot(HaveOccurred()) Expect(decodedMap).To(Equal(map[string]boshtask.TaskInfo{ "fake-task-id-2": boshtask.TaskInfo{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("does not return error when removing task that does not exist", func() { err := manager.RemoveTaskInfo("fake-unknown-task-id") Expect(err).ToNot(HaveOccurred()) }) It("returns an error when failing to remove task", func() { fs.WriteToFileError = errors.New("fake-write-error") err := manager.RemoveTaskInfo("fake-task-id") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-write-error")) }) }) }) }
func init() { Describe("concretePackageApplier", func() { var ( packagesBc *fakebc.FakeBundleCollection blobstore *fakeblob.FakeBlobstore compressor *fakecmd.FakeCompressor fs *fakesys.FakeFileSystem logger boshlog.Logger applier PackageApplier ) BeforeEach(func() { packagesBc = fakebc.NewFakeBundleCollection() blobstore = fakeblob.NewFakeBlobstore() compressor = fakecmd.NewFakeCompressor() fs = fakesys.NewFakeFileSystem() logger = boshlog.NewLogger(boshlog.LevelNone) applier = NewConcretePackageApplier(packagesBc, true, blobstore, compressor, fs, logger) }) Describe("Prepare & Apply", func() { var ( pkg models.Package bundle *fakebc.FakeBundle ) BeforeEach(func() { pkg, bundle = buildPkg(packagesBc) }) ItInstallsPkg := func(act func() error) { It("returns error when installing package fails", func() { bundle.InstallError = errors.New("fake-install-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-install-error")) }) It("downloads and later cleans up downloaded package blob", func() { blobstore.GetFileName = "/fake-blobstore-file-name" err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("fake-blobstore-id")) Expect(blobstore.GetFingerprints[0]).To(Equal("fake-blob-sha1")) // downloaded file is cleaned up Expect(blobstore.CleanUpFileName).To(Equal("/fake-blobstore-file-name")) }) It("returns error when downloading package blob fails", func() { blobstore.GetError = errors.New("fake-get-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-error")) }) It("decompresses package blob to tmp path and later cleans it up", func() { fs.TempDirDir = "/fake-tmp-dir" blobstore.GetFileName = "/fake-blobstore-file-name" var tmpDirExistsBeforeInstall bool bundle.InstallCallBack = func() { tmpDirExistsBeforeInstall = true } err := act() Expect(err).ToNot(HaveOccurred()) Expect(compressor.DecompressFileToDirTarballPaths[0]).To(Equal("/fake-blobstore-file-name")) Expect(compressor.DecompressFileToDirDirs[0]).To(Equal("/fake-tmp-dir")) // tmp dir exists before bundle install Expect(tmpDirExistsBeforeInstall).To(BeTrue()) // tmp dir is cleaned up after install Expect(fs.FileExists(fs.TempDirDir)).To(BeFalse()) }) It("returns error when temporary directory creation fails", func() { fs.TempDirError = errors.New("fake-filesystem-tempdir-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-filesystem-tempdir-error")) }) It("returns error when decompressing package blob fails", func() { compressor.DecompressFileToDirErr = errors.New("fake-decompress-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-decompress-error")) }) It("installs bundle from decompressed tmp path of a package blob", func() { fs.TempDirDir = "/fake-tmp-dir" var installedBeforeDecompression bool compressor.DecompressFileToDirCallBack = func() { installedBeforeDecompression = bundle.Installed } err := act() Expect(err).ToNot(HaveOccurred()) // bundle installation did not happen before decompression Expect(installedBeforeDecompression).To(BeFalse()) // make sure that bundle install happened after decompression Expect(bundle.InstallSourcePath).To(Equal("/fake-tmp-dir")) }) } Describe("Prepare", func() { act := func() error { return applier.Prepare(pkg) } It("return an error if getting file bundle fails", func() { packagesBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for package installation fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when package is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{})) // no Install }) It("does not download the package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when package is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs package (but does not enable it)", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install"})) }) ItInstallsPkg(act) }) }) Describe("Apply", func() { act := func() error { return applier.Apply(pkg) } It("return an error if getting file bundle fails", func() { packagesBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for package installation fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when package is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install but only enables package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Enable"})) // no Install }) It("returns error when package enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) It("does not download the package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when package is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs and enables package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install", "Enable"})) }) It("returns error when package enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) ItInstallsPkg(act) }) }) }) Describe("KeepOnly", func() { ItReturnsErrors := func() { It("returns error when bundle collection fails to return list of installed bundles", func() { packagesBc.ListErr = errors.New("fake-bc-list-error") err := applier.KeepOnly([]models.Package{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-list-error")) }) It("returns error when bundle collection cannot retrieve bundle for keep-only package", func() { pkg1, bundle1 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} packagesBc.GetErr = errors.New("fake-bc-get-error") err := applier.KeepOnly([]models.Package{pkg1}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-get-error")) }) It("returns error when at least one bundle cannot be disabled", func() { _, bundle1 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.DisableErr = errors.New("fake-bc-disable-error") err := applier.KeepOnly([]models.Package{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-disable-error")) }) } Context("when operating on packages as a package owner", func() { BeforeEach(func() { applier = NewConcretePackageApplier(packagesBc, true, blobstore, compressor, fs, logger) }) It("first disables and then uninstalls packages that are not in keeponly list", func() { _, bundle1 := buildPkg(packagesBc) pkg2, bundle2 := buildPkg(packagesBc) _, bundle3 := buildPkg(packagesBc) pkg4, bundle4 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Package{pkg4, pkg2}) Expect(err).ToNot(HaveOccurred()) Expect(bundle1.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle2.ActionsCalled).To(Equal([]string{})) Expect(bundle3.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle4.ActionsCalled).To(Equal([]string{})) }) ItReturnsErrors() It("returns error when at least one bundle cannot be uninstalled", func() { _, bundle1 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.UninstallErr = errors.New("fake-bc-uninstall-error") err := applier.KeepOnly([]models.Package{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-uninstall-error")) }) }) Context("when operating on packages not as a package owner", func() { BeforeEach(func() { applier = NewConcretePackageApplier(packagesBc, false, blobstore, compressor, fs, logger) }) It("disables and but does not uninstall packages that are not in keeponly list", func() { _, bundle1 := buildPkg(packagesBc) pkg2, bundle2 := buildPkg(packagesBc) _, bundle3 := buildPkg(packagesBc) pkg4, bundle4 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Package{pkg4, pkg2}) Expect(err).ToNot(HaveOccurred()) Expect(bundle1.ActionsCalled).To(Equal([]string{"Disable"})) // no Uninstall Expect(bundle2.ActionsCalled).To(Equal([]string{})) Expect(bundle3.ActionsCalled).To(Equal([]string{"Disable"})) // no Uninstall Expect(bundle4.ActionsCalled).To(Equal([]string{})) }) ItReturnsErrors() }) }) }) }
func init() { Describe("concreteBuilder", func() { var ( settingsService *fakesettings.FakeSettingsService builder Builder ) BeforeEach(func() { logger := boshlog.NewLogger(boshlog.LevelNone) settingsService = &fakesettings.FakeSettingsService{} builder = NewBuilder(settingsService, logger) }) Describe("Build", func() { It("builds alert with id, severity and other monit related info", func() { builtAlert, err := builder.Build(buildMonitAlert()) Expect(err).ToNot(HaveOccurred()) Expect(builtAlert).To(Equal(Alert{ ID: "some random id", Severity: SeverityAlert, Title: "nats - does not exist - restart", Summary: "process is not running", CreatedAt: 1306076861, })) }) It("sets the severity based on event", func() { alerts := map[string]SeverityLevel{ "action done": SeverityIgnored, "Action done": SeverityIgnored, "action Done": SeverityIgnored, } for event, expectedSeverity := range alerts { inputAlert := buildMonitAlert() inputAlert.Event = event builtAlert, _ := builder.Build(inputAlert) Expect(builtAlert.Severity).To(Equal(expectedSeverity)) } }) It("sets default severity to critical", func() { inputAlert := buildMonitAlert() inputAlert.Event = "some unknown event" builtAlert, _ := builder.Build(inputAlert) Expect(builtAlert.Severity).To(Equal(SeverityCritical)) }) It("sets created at", func() { inputAlert := buildMonitAlert() inputAlert.Date = "Thu, 02 May 2013 20:07:41 +0500" builtAlert, _ := builder.Build(inputAlert) Expect(int(builtAlert.CreatedAt)).To(Equal(int(1367507261))) }) It("defaults created at to now on parse error", func() { inputAlert := buildMonitAlert() inputAlert.Date = "Thu, 02 May 2013 20:07:0" builtAlert, _ := builder.Build(inputAlert) createdAt := time.Unix(builtAlert.CreatedAt, 0) assert.WithinDuration(GinkgoT(), time.Now(), createdAt, 1*time.Second) }) It("sets the title with ips", func() { inputAlert := buildMonitAlert() settingsService.Settings.Networks = boshsettings.Networks{ "fake-net1": boshsettings.Network{IP: "192.168.0.1"}, "fake-net2": boshsettings.Network{IP: "10.0.0.1"}, } builtAlert, _ := builder.Build(inputAlert) Expect(builtAlert.Title).To(Equal("nats (10.0.0.1, 192.168.0.1) - does not exist - restart")) }) }) }) }
func createSfdiskPartitionerForTests(runner *fakesys.FakeCmdRunner) (partitioner Partitioner) { logger := boshlog.NewLogger(boshlog.LevelNone) partitioner = NewSfdiskPartitioner(logger, runner) return }
func init() { Describe("provider", func() { var ( platform *fakeplatform.FakePlatform client *fakemonit.FakeMonitClient logger boshlog.Logger dirProvider boshdir.DirectoriesProvider jobFailuresServerPort int handler *fakembus.FakeHandler provider Provider ) BeforeEach(func() { platform = fakeplatform.NewFakePlatform() client = fakemonit.NewFakeMonitClient() logger = boshlog.NewLogger(boshlog.LevelNone) dirProvider = boshdir.NewDirectoriesProvider("/fake-base-dir") jobFailuresServerPort = 2825 handler = &fakembus.FakeHandler{} provider = NewProvider( platform, client, logger, dirProvider, handler, ) }) It("provides a monit job supervisor", func() { actualSupervisor, err := provider.Get("monit") Expect(err).ToNot(HaveOccurred()) expectedSupervisor := NewMonitJobSupervisor( platform.Fs, platform.Runner, client, logger, dirProvider, jobFailuresServerPort, MonitReloadOptions{ MaxTries: 3, MaxCheckTries: 6, DelayBetweenCheckTries: 5 * time.Second, }, ) Expect(actualSupervisor).To(Equal(expectedSupervisor)) }) It("provides a dummy job supervisor", func() { actualSupervisor, err := provider.Get("dummy") Expect(err).ToNot(HaveOccurred()) expectedSupervisor := NewDummyJobSupervisor() Expect(actualSupervisor).To(Equal(expectedSupervisor)) }) It("provides a dummy nats job supervisor", func() { actualSupervisor, err := provider.Get("dummy-nats") Expect(err).NotTo(HaveOccurred()) expectedSupervisor := NewDummyNatsJobSupervisor(handler) Expect(actualSupervisor).To(Equal(expectedSupervisor)) }) It("returns an error when the supervisor is not found", func() { _, err := provider.Get("does-not-exist") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("does-not-exist could not be found")) }) }) }
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 == TaskStateRunning { 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(TaskStateDone)) 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(TaskStateFailed)) 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 != TaskStateDone { 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(TaskStateRunning)) task.TaskFunc() Expect(runFuncCalled).To(BeTrue()) task.CancelFunc(task) Expect(cancelFuncCalled).To(BeTrue()) task.TaskEndFunc(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(TaskStateRunning)) task.TaskFunc() Expect(runFuncCalled).To(BeTrue()) task.CancelFunc(task) Expect(cancelFuncCalled).To(BeTrue()) task.TaskEndFunc(task) Expect(endFuncCalled).To(BeTrue()) }) }) }) }
func init() { Describe("execCmdRunner", func() { var ( runner CmdRunner ) BeforeEach(func() { runner = NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) }) Describe("RunComplexCommand", func() { It("run complex command with working directory", func() { cmd := Command{ Name: "ls", Args: []string{"-l"}, WorkingDir: "..", } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(ContainSubstring("README.md")) Expect(stdout).To(ContainSubstring("total")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("run complex command with env", func() { cmd := Command{ Name: "env", Env: map[string]string{ "FOO": "BAR", }, } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(ContainSubstring("FOO=BAR")) Expect(stdout).To(ContainSubstring("PATH=")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("prints stdout/stderr to provided I/O object", func() { fs := fakesys.NewFakeFileSystem() stdoutFile, err := fs.OpenFile("/fake-stdout-path", os.O_RDWR, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) stderrFile, err := fs.OpenFile("/fake-stderr-path", os.O_RDWR, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) cmd := Command{ Name: "bash", Args: []string{"-c", "echo fake-out >&1; echo fake-err >&2"}, Stdout: stdoutFile, Stderr: stderrFile, } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) stdoutContents := make([]byte, 1024) _, err = stdoutFile.Read(stdoutContents) Expect(err).ToNot(HaveOccurred()) Expect(string(stdoutContents)).To(ContainSubstring("fake-out")) stderrContents := make([]byte, 1024) _, err = stderrFile.Read(stderrContents) Expect(err).ToNot(HaveOccurred()) Expect(string(stderrContents)).To(ContainSubstring("fake-err")) }) }) Describe("RunComplexCommandAsync", func() { It("populates stdout and stderr", func() { cmd := Command{Name: "ls"} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.ExitStatus).To(Equal(0)) }) It("populates stdout and stderr", func() { cmd := Command{Name: "bash", Args: []string{"-c", "echo stdout >&1; echo stderr >&2"}} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(Equal("stdout\n")) Expect(result.Stderr).To(Equal("stderr\n")) }) It("returns error and sets status to exit status of comamnd if it command exits with non-0 status", func() { cmd := Command{Name: "bash", Args: []string{"-c", "exit 10"}} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).To(HaveOccurred()) Expect(result.ExitStatus).To(Equal(10)) }) It("allows setting custom env variable in addition to inheriting process env variables", func() { cmd := Command{ Name: "env", Env: map[string]string{ "FOO": "BAR", }, } process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(ContainSubstring("FOO=BAR")) Expect(result.Stdout).To(ContainSubstring("PATH=")) }) It("changes working dir", func() { cmd := Command{Name: "bash", Args: []string{"-c", "echo $PWD"}, WorkingDir: "/tmp"} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(ContainSubstring("/tmp")) }) }) Describe("RunCommand", func() { It("run command", func() { stdout, stderr, status, err := runner.RunCommand("echo", "Hello World!") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(Equal("Hello World!\n")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("run command with error output", func() { stdout, stderr, status, err := runner.RunCommand("bash", "-c", "echo error-output >&2") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(ContainSubstring("error-output")) Expect(status).To(Equal(0)) }) It("run command with non-0 exit status", func() { stdout, stderr, status, err := runner.RunCommand("bash", "-c", "exit 14") Expect(err).To(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(14)) }) It("run command with error", func() { stdout, stderr, status, err := runner.RunCommand("false") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Running command: 'false', stdout: '', stderr: '': exit status 1")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(1)) }) It("run command with error with args", func() { stdout, stderr, status, err := runner.RunCommand("false", "second arg") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Running command: 'false second arg', stdout: '', stderr: '': exit status 1")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(1)) }) It("run command with cmd not found", func() { stdout, stderr, status, err := runner.RunCommand("something that does not exist") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not found")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(-1)) }) }) Describe("CommandExists", func() { It("run command with input", func() { stdout, stderr, status, err := runner.RunCommandWithInput("foo\nbar\nbaz", "grep", "ba") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(Equal("bar\nbaz\n")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) }) Describe("CommandExists", func() { It("command exists", func() { Expect(runner.CommandExists("env")).To(BeTrue()) Expect(runner.CommandExists("absolutely-does-not-exist-ever-please-unicorns")).To(BeFalse()) }) }) }) Describe("execProcess", func() { var ( runner CmdRunner ) BeforeEach(func() { runner = NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) }) Describe("TerminateNicely", func() { var ( buildDir string ) hasProcessesFromBuildDir := func() (bool, string) { // Make sure to show all processes on the system output, err := exec.Command("ps", "-A", "-o", "pid,args").Output() Expect(err).ToNot(HaveOccurred()) // Cannot check for PID existence directly because // PID could have been recycled by the OS; make sure it's not the same process for _, line := range strings.Split(strings.TrimSpace(string(output)), "\n") { if strings.Contains(line, buildDir) { return true, line } } return false, "" } expectProcessesToNotExist := func() { exists, ps := hasProcessesFromBuildDir() Expect(exists).To(BeFalse(), "Expected following process to not exist %s", ps) } BeforeEach(func() { var ( err error ) buildDir, err = ioutil.TempDir("", "TerminateNicely") Expect(err).ToNot(HaveOccurred()) exesToCompile := []string{ "exe_exits", "child_ignore_term", "child_term", "parent_ignore_term", "parent_term", } for _, exe := range exesToCompile { dst := filepath.Join(buildDir, exe) src := filepath.Join("exec_cmd_runner_fixtures", exe+".go") err := exec.Command("go", "build", "-o", dst, src).Run() Expect(err).ToNot(HaveOccurred()) } }) AfterEach(func() { os.RemoveAll(buildDir) }) Context("when parent and child terminate after receiving SIGTERM", func() { It("sends term signal to the whole group and returns with exit status that parent exited", func() { cmd := Command{Name: filepath.Join(buildDir, "parent_term")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to start and output pids time.Sleep(2 * time.Second) waitCh := process.Wait() err = process.TerminateNicely(1 * time.Minute) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).To(HaveOccurred()) // Parent exit code is returned // bash adds 128 to signal status as exit code Expect(result.ExitStatus).To(Equal(13)) // Term signal was sent to all processes in the group Expect(result.Stdout).To(ContainSubstring("Parent received SIGTERM")) Expect(result.Stdout).To(ContainSubstring("Child received SIGTERM")) // All processes are gone expectProcessesToNotExist() }) }) Context("when parent and child do not exit after receiving SIGTERM in small amount of time", func() { It("sends kill signal to the whole group and returns with ? exit status", func() { cmd := Command{Name: filepath.Join(buildDir, "parent_ignore_term")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to start and output pids time.Sleep(2 * time.Second) waitCh := process.Wait() err = process.TerminateNicely(2 * time.Second) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).To(HaveOccurred()) // Parent exit code is returned Expect(result.ExitStatus).To(Equal(128 + 9)) // Term signal was sent to all processes in the group before kill Expect(result.Stdout).To(ContainSubstring("Parent received SIGTERM")) Expect(result.Stdout).To(ContainSubstring("Child received SIGTERM")) // Parent and child are killed expectProcessesToNotExist() }) }) Context("when parent and child already exited before calling TerminateNicely", func() { It("returns without an error since all processes are gone", func() { cmd := Command{Name: filepath.Join(buildDir, "exe_exits")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to exit for i := 0; i < 20; i++ { if exists, _ := hasProcessesFromBuildDir(); !exists { break } if i == 19 { Fail("Expected process did not exit fast enough") } time.Sleep(500 * time.Millisecond) } waitCh := process.Wait() err = process.TerminateNicely(2 * time.Second) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(Equal("")) Expect(result.Stderr).To(Equal("")) Expect(result.ExitStatus).To(Equal(0)) }) }) }) }) Describe("ExecError", func() { Describe("Error", func() { It("returns error message with full stdout and full stderr to aid debugging", func() { execErr := NewExecError("fake-cmd", "fake-stdout", "fake-stderr") expectedMsg := "Running command: 'fake-cmd', stdout: 'fake-stdout', stderr: 'fake-stderr'" Expect(execErr.Error()).To(Equal(expectedMsg)) }) }) Describe("ShortError", func() { buildLines := func(start, stop int, suffix string) string { var result []string for i := start; i <= stop; i++ { result = append(result, fmt.Sprintf("%d %s", i, suffix)) } return strings.Join(result, "\n") } Context("when stdout and stderr contains more than 100 lines", func() { It("returns error message with truncated stdout and stderr to 100 lines", func() { fullStdout101 := buildLines(1, 101, "stdout") truncatedStdout100 := buildLines(2, 101, "stdout") fullStderr101 := buildLines(1, 101, "stderr") truncatedStderr100 := buildLines(2, 101, "stderr") execErr := NewExecError("fake-cmd", fullStdout101, fullStderr101) expectedMsg := fmt.Sprintf( "Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", truncatedStdout100, truncatedStderr100, ) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) Context("when stdout and stderr contains exactly 100 lines", func() { It("returns error message with full lines", func() { stdout100 := buildLines(1, 100, "stdout") stderr100 := buildLines(1, 100, "stderr") execErr := NewExecError("fake-cmd", stdout100, stderr100) expectedMsg := fmt.Sprintf("Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", stdout100, stderr100) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) Context("when stdout and stderr contains less than 100 lines", func() { It("returns error message with full lines", func() { stdout99 := buildLines(1, 99, "stdout") stderr99 := buildLines(1, 99, "stderr") execErr := NewExecError("fake-cmd", stdout99, stderr99) expectedMsg := fmt.Sprintf("Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", stdout99, stderr99) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) }) }) }
func init() { Describe("Testing with Ginkgo", func() { It("home dir", func() { osFs, _ := createOsFs() homeDir, err := osFs.HomeDir("root") Expect(err).ToNot(HaveOccurred()) assert.Contains(GinkgoT(), homeDir, "/root") }) It("mkdir all", func() { osFs, _ := createOsFs() tmpPath := os.TempDir() testPath := filepath.Join(tmpPath, "MkdirAllTestDir", "bar", "baz") defer os.RemoveAll(filepath.Join(tmpPath, "MkdirAllTestDir")) _, err := os.Stat(testPath) Expect(err).To(HaveOccurred()) Expect(os.IsNotExist(err)).To(BeTrue()) fileMode := os.FileMode(0700) err = osFs.MkdirAll(testPath, fileMode) Expect(err).ToNot(HaveOccurred()) stat, err := os.Stat(testPath) Expect(err).ToNot(HaveOccurred()) Expect(stat.IsDir()).To(BeTrue()) Expect(stat.Mode().Perm()).To(Equal(fileMode)) err = osFs.MkdirAll(testPath, fileMode) Expect(err).ToNot(HaveOccurred()) }) It("chown", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ChownTestDir") err := os.Mkdir(testPath, os.FileMode(0700)) Expect(err).ToNot(HaveOccurred()) defer os.RemoveAll(testPath) err = osFs.Chown(testPath, "root") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not permitted")) }) It("chmod", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ChmodTestDir") _, err := os.Create(testPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(testPath) os.Chmod(testPath, os.FileMode(0666)) err = osFs.Chmod(testPath, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) fileStat, err := os.Stat(testPath) Expect(err).ToNot(HaveOccurred()) Expect(fileStat.Mode()).To(Equal(os.FileMode(0644))) }) It("opens file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "OpenFileTestFile") file, err := osFs.OpenFile(testPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(0644)) defer os.Remove(testPath) Expect(err).ToNot(HaveOccurred()) file.Write([]byte("testing new file")) file.Close() createdFile, err := os.Open(testPath) Expect(err).ToNot(HaveOccurred()) defer createdFile.Close() Expect(readFile(createdFile)).To(Equal("testing new file")) }) Context("the file already exists and is not write only", func() { It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDir", "ConvergeFileContentsTestFile") _, err := os.Stat(testPath) Expect(err).To(HaveOccurred()) written, err := osFs.ConvergeFileContents(testPath, []byte("initial write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeTrue()) defer os.Remove(testPath) file, err := os.Open(testPath) Expect(err).ToNot(HaveOccurred()) defer file.Close() Expect(readFile(file)).To(Equal("initial write")) written, err = osFs.ConvergeFileContents(testPath, []byte("second write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeTrue()) file.Close() file, err = os.Open(testPath) Expect(err).ToNot(HaveOccurred()) Expect(readFile(file)).To(Equal("second write")) file.Close() file, err = os.Open(testPath) written, err = osFs.ConvergeFileContents(testPath, []byte("second write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeFalse()) Expect(readFile(file)).To(Equal("second write")) }) }) Context("the file already exists and is write only", func() { It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDir", "ConvergeFileContentsTestFile") _, err := os.OpenFile(testPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0200)) Expect(err).ToNot(HaveOccurred()) defer os.Remove(testPath) err = osFs.WriteFile(testPath, []byte("test")) Expect(err).ToNot(HaveOccurred()) }) }) Context("the file parent fir does not exist", func() { BeforeEach(func() { err := os.RemoveAll(filepath.Join(os.TempDir(), "subDirNew")) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { err := os.RemoveAll(filepath.Join(os.TempDir(), "subDirNew")) Expect(err).ToNot(HaveOccurred()) }) It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDirNew", "ConvergeFileContentsTestFile") err := osFs.WriteFile(testPath, []byte("test")) Expect(err).ToNot(HaveOccurred()) }) }) It("read file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ReadFileTestFile") osFs.WriteFileString(testPath, "some contents") defer os.Remove(testPath) content, err := osFs.ReadFile(testPath) Expect(err).ToNot(HaveOccurred()) Expect("some contents").To(Equal(string(content))) }) It("file exists", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "FileExistsTestFile") Expect(osFs.FileExists(testPath)).To(BeFalse()) osFs.WriteFileString(testPath, "initial write") defer os.Remove(testPath) Expect(osFs.FileExists(testPath)).To(BeTrue()) }) It("rename", func() { osFs, _ := createOsFs() tempDir := os.TempDir() oldPath := filepath.Join(tempDir, "old") oldFilePath := filepath.Join(oldPath, "test.txt") newPath := filepath.Join(tempDir, "new") os.Mkdir(oldPath, os.ModePerm) _, err := os.Create(oldFilePath) Expect(err).ToNot(HaveOccurred()) err = osFs.Rename(oldPath, newPath) Expect(err).ToNot(HaveOccurred()) Expect(osFs.FileExists(newPath)).To(BeTrue()) newFilePath := filepath.Join(newPath, "test.txt") Expect(osFs.FileExists(newFilePath)).To(BeTrue()) }) It("symlink", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestFile") containingDir := filepath.Join(os.TempDir(), "SubDir") os.Remove(containingDir) symlinkPath := filepath.Join(containingDir, "SymlinkTestSymlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.Symlink(filePath, symlinkPath) defer os.Remove(containingDir) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("symlink when link already exists and links to the intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.Symlink(filePath, symlinkPath) defer os.Remove(symlinkPath) firstSymlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) err = osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) secondSymlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(firstSymlinkStats.ModTime()).To(Equal(secondSymlinkStats.ModTime())) }) It("symlink when link already exists and does not link to the intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") otherFilePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1OtherFile") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.WriteFileString(otherFilePath, "other content") defer os.Remove(otherFilePath) err := osFs.Symlink(otherFilePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) err = osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(symlinkPath) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("symlink when a file exists at intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.WriteFileString(symlinkPath, "some other content") defer os.Remove(symlinkPath) err := osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(symlinkPath) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("read link", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestFile") containingDir := filepath.Join(os.TempDir(), "SubDir") os.Remove(containingDir) symlinkPath := filepath.Join(containingDir, "SymlinkTestSymlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) err := osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(containingDir) actualFilePath, err := osFs.ReadLink(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(actualFilePath).To(Equal(filePath)) }) It("temp file", func() { osFs, _ := createOsFs() file1, err := osFs.TempFile("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), file1) defer os.Remove(file1.Name()) file2, err := osFs.TempFile("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), file2) defer os.Remove(file2.Name()) assert.NotEqual(GinkgoT(), file1.Name(), file2.Name()) }) It("temp dir", func() { osFs, _ := createOsFs() path1, err := osFs.TempDir("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), path1) defer os.Remove(path1) path2, err := osFs.TempDir("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), path2) defer os.Remove(path2) assert.NotEqual(GinkgoT(), path1, path2) }) Describe("CopyFile", func() { It("copies file", func() { osFs, _ := createOsFs() srcPath := "../Fixtures/test_copy_dir_entries/foo.txt" dstFile, err := osFs.TempFile("CopyFileTestFile") Expect(err).ToNot(HaveOccurred()) defer os.Remove(dstFile.Name()) err = osFs.CopyFile(srcPath, dstFile.Name()) fooContent, err := osFs.ReadFileString(dstFile.Name()) Expect(err).ToNot(HaveOccurred()) Expect(fooContent).To(Equal("foo\n")) }) It("does not leak file descriptors", func() { osFs, _ := createOsFs() srcFile, err := osFs.TempFile("srcPath") Expect(err).ToNot(HaveOccurred()) err = srcFile.Close() Expect(err).ToNot(HaveOccurred()) dstFile, err := osFs.TempFile("dstPath") Expect(err).ToNot(HaveOccurred()) err = dstFile.Close() Expect(err).ToNot(HaveOccurred()) err = osFs.CopyFile(srcFile.Name(), dstFile.Name()) Expect(err).ToNot(HaveOccurred()) runner := NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) stdout, _, _, err := runner.RunCommand("lsof") Expect(err).ToNot(HaveOccurred()) for _, line := range strings.Split(stdout, "\n") { if strings.Contains(line, srcFile.Name()) { Fail(fmt.Sprintf("CopyFile did not close: srcFile: %s", srcFile.Name())) } if strings.Contains(line, dstFile.Name()) { Fail(fmt.Sprintf("CopyFile did not close: dstFile: %s", dstFile.Name())) } } os.Remove(srcFile.Name()) os.Remove(dstFile.Name()) }) }) It("remove all", func() { osFs, _ := createOsFs() dstFile, err := osFs.TempFile("CopyFileTestFile") Expect(err).ToNot(HaveOccurred()) defer os.Remove(dstFile.Name()) err = osFs.RemoveAll(dstFile.Name()) Expect(err).ToNot(HaveOccurred()) _, err = os.Stat(dstFile.Name()) Expect(os.IsNotExist(err)).To(BeTrue()) }) }) }
func createOsFs() (fs FileSystem, runner CmdRunner) { logger := boshlog.NewLogger(boshlog.LevelNone) fs = NewOsFileSystem(logger) return }