_, _, err := compiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) Expect(compressor.CompressFilesInDirDir).To(Equal(installPath)) Expect(compressor.CleanUpTarballPath).To(Equal(compiledPackageTarballPath)) }) It("moves the compressed package to a blobstore", func() { _, _, err := compiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) Expect(blobstore.CreateFileNames).To(Equal([]string{compiledPackageTarballPath})) }) It("stores the compiled package blobID and fingerprint into the compile package repo", func() { expectSave.Times(1) _, _, err := compiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) }) It("returns the repo record", func() { record, _, err := compiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) Expect(record).To(Equal(bistatepkg.CompiledPackageRecord{ BlobID: "fake-blob-id", BlobSHA1: "fake-fingerprint", })) })
&fakeui.PerformCall{ Name: "installing CPI", Stage: fakeui.NewFakeStage(), }, )) }) It("cleans up the installation afterwards", func() { cpiInstaller := release.CpiInstaller{ InstallerFactory: mockInstallerFactory, } cleanupCalled := false expectInstall.Return(installation, nil) expectCleanup.Times(1).Do(func(_ biinstallation.Installation) { cleanupCalled = true }) err := cpiInstaller.WithInstalledCpiRelease(installationManifest, target, installStage, func(installation biinstallation.Installation) error { Expect(cleanupCalled).To(BeFalse()) return nil }) Expect(err).ToNot(HaveOccurred()) }) It("creates a stage for the cleanup", func() { cpiInstaller := release.CpiInstaller{ InstallerFactory: mockInstallerFactory, } expectInstall.Return(installation, nil) err := cpiInstaller.WithInstalledCpiRelease(installationManifest, target, installStage, func(installation biinstallation.Installation) error {
func rootDesc() { var mockCtrl *gomock.Controller BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) AfterEach(func() { mockCtrl.Finish() }) Describe("Run", func() { var ( command bicmd.Cmd fakeFs *fakesys.FakeFileSystem stdOut *gbytes.Buffer stdErr *gbytes.Buffer userInterface biui.UI sha1Calculator crypto.SHA1Calculator manifestSHA1 string mockDeployer *mock_deployment.MockDeployer mockInstaller *mock_install.MockInstaller mockInstallerFactory *mock_install.MockInstallerFactory mockReleaseExtractor *mock_release.MockExtractor releaseManager birel.Manager mockRegistryServerManager *mock_registry.MockServerManager mockRegistryServer *mock_registry.MockServer mockAgentClient *mock_agentclient.MockAgentClient mockAgentClientFactory *mock_httpagent.MockAgentClientFactory mockCloudFactory *mock_cloud.MockFactory fakeCPIRelease *fakebirel.FakeRelease logger boshlog.Logger mockBlobstoreFactory *mock_blobstore.MockFactory mockBlobstore *mock_blobstore.MockBlobstore mockVMManagerFactory *mock_vm.MockManagerFactory fakeVMManager *fakebivm.FakeManager fakeStemcellExtractor *fakebistemcell.FakeExtractor mockStemcellManager *mock_stemcell.MockManager fakeStemcellManagerFactory *fakebistemcell.FakeManagerFactory fakeReleaseSetParser *fakebirelsetmanifest.FakeParser fakeInstallationParser *fakebiinstallmanifest.FakeParser fakeDeploymentParser *fakebideplmanifest.FakeParser mockLegacyDeploymentStateMigrator *mock_config.MockLegacyDeploymentStateMigrator setupDeploymentStateService biconfig.DeploymentStateService fakeDeploymentValidator *fakebideplval.FakeValidator directorID = "generated-director-uuid" fakeUUIDGenerator *fakeuuid.FakeGenerator configUUIDGenerator *fakeuuid.FakeGenerator fakeStage *fakebiui.FakeStage deploymentManifestPath string deploymentStatePath string cpiReleaseTarballPath string stemcellTarballPath string extractedStemcell bistemcell.ExtractedStemcell expectDeploy *gomock.Call mbusURL = "http://*****:*****@fake-mbus-endpoint" releaseSetManifest birelsetmanifest.Manifest boshDeploymentManifest bideplmanifest.Manifest installationManifest biinstallmanifest.Manifest cloud bicloud.Cloud cloudStemcell bistemcell.CloudStemcell expectLegacyMigrate *gomock.Call expectStemcellUpload *gomock.Call expectStemcellDeleteUnused *gomock.Call expectCPIReleaseExtract *gomock.Call expectInstall *gomock.Call expectNewCloud *gomock.Call ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) stdOut = gbytes.NewBuffer() stdErr = gbytes.NewBuffer() userInterface = biui.NewWriterUI(stdOut, stdErr, logger) fakeFs = fakesys.NewFakeFileSystem() fakeFs.EnableStrictTempRootBehavior() deploymentManifestPath = "/path/to/manifest.yml" deploymentStatePath = "/path/to/manifest-state.json" fakeFs.RegisterOpenFile(deploymentManifestPath, &fakesys.FakeFile{ Stats: &fakesys.FakeFileStats{FileType: fakesys.FakeFileTypeFile}, }) fakeFs.WriteFileString(deploymentManifestPath, "") mockDeployer = mock_deployment.NewMockDeployer(mockCtrl) mockInstaller = mock_install.NewMockInstaller(mockCtrl) mockInstallerFactory = mock_install.NewMockInstallerFactory(mockCtrl) mockReleaseExtractor = mock_release.NewMockExtractor(mockCtrl) releaseManager = birel.NewManager(logger) mockRegistryServerManager = mock_registry.NewMockServerManager(mockCtrl) mockRegistryServer = mock_registry.NewMockServer(mockCtrl) mockAgentClientFactory = mock_httpagent.NewMockAgentClientFactory(mockCtrl) mockAgentClient = mock_agentclient.NewMockAgentClient(mockCtrl) mockAgentClientFactory.EXPECT().NewAgentClient(gomock.Any(), gomock.Any()).Return(mockAgentClient).AnyTimes() mockCloudFactory = mock_cloud.NewMockFactory(mockCtrl) mockBlobstoreFactory = mock_blobstore.NewMockFactory(mockCtrl) mockBlobstore = mock_blobstore.NewMockBlobstore(mockCtrl) mockBlobstoreFactory.EXPECT().Create(mbusURL, gomock.Any()).Return(mockBlobstore, nil).AnyTimes() mockVMManagerFactory = mock_vm.NewMockManagerFactory(mockCtrl) fakeVMManager = fakebivm.NewFakeManager() mockVMManagerFactory.EXPECT().NewManager(gomock.Any(), mockAgentClient).Return(fakeVMManager).AnyTimes() fakeStemcellExtractor = fakebistemcell.NewFakeExtractor() mockStemcellManager = mock_stemcell.NewMockManager(mockCtrl) fakeStemcellManagerFactory = fakebistemcell.NewFakeManagerFactory() fakeReleaseSetParser = fakebirelsetmanifest.NewFakeParser() fakeInstallationParser = fakebiinstallmanifest.NewFakeParser() fakeDeploymentParser = fakebideplmanifest.NewFakeParser() mockLegacyDeploymentStateMigrator = mock_config.NewMockLegacyDeploymentStateMigrator(mockCtrl) configUUIDGenerator = &fakeuuid.FakeGenerator{} configUUIDGenerator.GeneratedUUID = directorID setupDeploymentStateService = biconfig.NewFileSystemDeploymentStateService(fakeFs, configUUIDGenerator, logger, biconfig.DeploymentStatePath(deploymentManifestPath)) fakeDeploymentValidator = fakebideplval.NewFakeValidator() fakeStage = fakebiui.NewFakeStage() sha1Calculator = crypto.NewSha1Calculator(fakeFs) fakeUUIDGenerator = &fakeuuid.FakeGenerator{} var err error manifestSHA1, err = sha1Calculator.Calculate(deploymentManifestPath) Expect(err).ToNot(HaveOccurred()) cpiReleaseTarballPath = "/release/tarball/path" stemcellTarballPath = "/stemcell/tarball/path" extractedStemcell = bistemcell.NewExtractedStemcell( bistemcell.Manifest{ ImagePath: "/stemcell/image/path", Name: "fake-stemcell-name", Version: "fake-stemcell-version", SHA1: "fake-stemcell-sha1", CloudProperties: biproperty.Map{}, OS: "ubuntu-trusty", }, "fake-extracted-path", fakeFs, ) // create input files fakeFs.WriteFileString(cpiReleaseTarballPath, "") fakeFs.WriteFileString(stemcellTarballPath, "") // deployment exists fakeFs.WriteFileString(deploymentManifestPath, "") // deployment is valid fakeDeploymentValidator.SetValidateBehavior([]fakebideplval.ValidateOutput{ {Err: nil}, }) fakeDeploymentValidator.SetValidateReleaseJobsBehavior([]fakebideplval.ValidateReleaseJobsOutput{ {Err: nil}, }) // stemcell exists fakeFs.WriteFile(stemcellTarballPath, []byte{}) releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, }, } // parsed CPI deployment manifest installationManifest = biinstallmanifest.Manifest{ Template: biinstallmanifest.ReleaseJobRef{ Name: "fake-cpi-release-job-name", Release: "fake-cpi-release-name", }, Mbus: mbusURL, } // parsed BOSH deployment manifest boshDeploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-job-name", }, }, ResourcePools: []bideplmanifest.ResourcePool{ { Stemcell: bideplmanifest.StemcellRef{ URL: "file://" + stemcellTarballPath, }, }, }, } fakeDeploymentParser.ParseManifest = boshDeploymentManifest // parsed/extracted CPI release fakeCPIRelease = fakebirel.NewFakeRelease() fakeCPIRelease.ReleaseName = "fake-cpi-release-name" fakeCPIRelease.ReleaseVersion = "1.0" fakeCPIRelease.ReleaseIsCompiled = false fakeCPIRelease.ReleaseJobs = []bireljob.Job{ { Name: "fake-cpi-release-job-name", Templates: map[string]string{ "templates/cpi.erb": "bin/cpi", }, }, } cloud = bicloud.NewCloud(fakebicloud.NewFakeCPICmdRunner(), "fake-director-id", logger) cloudStemcell = fakebistemcell.NewFakeCloudStemcell("fake-stemcell-cid", "fake-stemcell-name", "fake-stemcell-version") }) JustBeforeEach(func() { doGet := func(deploymentManifestPath string) (bicmd.DeploymentPreparer, error) { deploymentStateService := biconfig.NewFileSystemDeploymentStateService(fakeFs, configUUIDGenerator, logger, biconfig.DeploymentStatePath(deploymentManifestPath)) deploymentRepo := biconfig.NewDeploymentRepo(deploymentStateService) releaseRepo := biconfig.NewReleaseRepo(deploymentStateService, fakeUUIDGenerator) stemcellRepo := biconfig.NewStemcellRepo(deploymentStateService, fakeUUIDGenerator) deploymentRecord := deployment.NewRecord(deploymentRepo, releaseRepo, stemcellRepo, sha1Calculator) fakeHTTPClient := fakebihttpclient.NewFakeHTTPClient() tarballCache := bitarball.NewCache("fake-base-path", fakeFs, logger) tarballProvider := bitarball.NewProvider(tarballCache, fakeFs, fakeHTTPClient, sha1Calculator, 1, 0, logger) cpiInstaller := bicpirel.CpiInstaller{ ReleaseManager: releaseManager, InstallerFactory: mockInstallerFactory, Validator: bicpirel.NewValidator(), } releaseFetcher := birel.NewFetcher(tarballProvider, mockReleaseExtractor, releaseManager) stemcellFetcher := bistemcell.Fetcher{ TarballProvider: tarballProvider, StemcellExtractor: fakeStemcellExtractor, } releaseSetAndInstallationManifestParser := bicmd.ReleaseSetAndInstallationManifestParser{ ReleaseSetParser: fakeReleaseSetParser, InstallationParser: fakeInstallationParser, } deploymentManifestParser := bicmd.DeploymentManifestParser{ DeploymentParser: fakeDeploymentParser, DeploymentValidator: fakeDeploymentValidator, ReleaseManager: releaseManager, } fakeInstallationUUIDGenerator := &fakeuuid.FakeGenerator{} fakeInstallationUUIDGenerator.GeneratedUUID = "fake-installation-id" targetProvider := biinstall.NewTargetProvider( deploymentStateService, fakeInstallationUUIDGenerator, filepath.Join("fake-install-dir"), ) tempRootConfigurator := bicmd.NewTempRootConfigurator(fakeFs) return bicmd.NewDeploymentPreparer( userInterface, logger, "deployCmd", deploymentStateService, mockLegacyDeploymentStateMigrator, releaseManager, deploymentRecord, mockCloudFactory, fakeStemcellManagerFactory, mockAgentClientFactory, mockVMManagerFactory, mockBlobstoreFactory, mockDeployer, deploymentManifestPath, cpiInstaller, releaseFetcher, stemcellFetcher, releaseSetAndInstallationManifestParser, deploymentManifestParser, tempRootConfigurator, targetProvider, ), nil } command = bicmd.NewDeployCmd(userInterface, fakeFs, logger, doGet) expectLegacyMigrate = mockLegacyDeploymentStateMigrator.EXPECT().MigrateIfExists("/path/to/bosh-deployments.yml").AnyTimes() fakeStemcellExtractor.SetExtractBehavior(stemcellTarballPath, extractedStemcell, nil) fakeStemcellManagerFactory.SetNewManagerBehavior(cloud, mockStemcellManager) expectStemcellUpload = mockStemcellManager.EXPECT().Upload(extractedStemcell, fakeStage).Return(cloudStemcell, nil).AnyTimes() expectStemcellDeleteUnused = mockStemcellManager.EXPECT().DeleteUnused(fakeStage).AnyTimes() fakeReleaseSetParser.ParseManifest = releaseSetManifest fakeDeploymentParser.ParseManifest = boshDeploymentManifest fakeInstallationParser.ParseManifest = installationManifest installationPath := filepath.Join("fake-install-dir", "fake-installation-id") target := biinstall.NewTarget(installationPath) installedJob := biinstall.NewInstalledJob( biinstall.RenderedJobRef{ Name: "fake-cpi-release-job-name", }, filepath.Join(target.JobsPath(), "fake-cpi-release-job-name"), ) mockInstallerFactory.EXPECT().NewInstaller(target).Return(mockInstaller).AnyTimes() installation := biinstall.NewInstallation(target, installedJob, installationManifest, mockRegistryServerManager) expectInstall = mockInstaller.EXPECT().Install(installationManifest, gomock.Any()).Do(func(_ interface{}, stage biui.Stage) { Expect(fakeStage.SubStages).To(ContainElement(stage)) }).Return(installation, nil).AnyTimes() mockInstaller.EXPECT().Cleanup(installation).AnyTimes() mockDeployment := mock_deployment.NewMockDeployment(mockCtrl) expectDeploy = mockDeployer.EXPECT().Deploy( cloud, boshDeploymentManifest, cloudStemcell, installationManifest.Registry, fakeVMManager, mockBlobstore, gomock.Any(), ).Do(func(_, _, _, _, _, _ interface{}, stage biui.Stage) { Expect(fakeStage.SubStages).To(ContainElement(stage)) }).Return(mockDeployment, nil).AnyTimes() expectCPIReleaseExtract = mockReleaseExtractor.EXPECT().Extract(cpiReleaseTarballPath).Return(fakeCPIRelease, nil).AnyTimes() expectNewCloud = mockCloudFactory.EXPECT().NewCloud(installation, directorID).Return(cloud, nil).AnyTimes() }) It("prints the deployment manifest and state file", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("Deployment manifest: '/path/to/manifest.yml'")) Expect(stdOut).To(gbytes.Say("Deployment state: '/path/to/manifest-state.json'")) }) It("does not migrate the legacy bosh-deployments.yml if manifest-state.json exists", func() { err := fakeFs.WriteFileString(deploymentStatePath, "{}") Expect(err).ToNot(HaveOccurred()) expectLegacyMigrate.Times(0) err = command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("migrates the legacy bosh-deployments.yml if manifest-state.json does not exist", func() { err := fakeFs.RemoveAll(deploymentStatePath) Expect(err).ToNot(HaveOccurred()) expectLegacyMigrate.Return(true, nil).Times(1) err = command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) Expect(stdOut).To(gbytes.Say("Deployment manifest: '/path/to/manifest.yml'")) Expect(stdOut).To(gbytes.Say("Deployment state: '/path/to/manifest-state.json'")) Expect(stdOut).To(gbytes.Say("Migrated legacy deployments file: '/path/to/bosh-deployments.yml'")) }) It("sets the temp root", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeFs.TempRootPath).To(Equal("fake-install-dir/fake-installation-id/tmp")) }) Context("when setting the temp root fails", func() { It("returns an error", func() { fakeFs.ChangeTempRootErr = errors.New("fake ChangeTempRootErr") err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Setting temp root: fake ChangeTempRootErr")) }) }) It("parses the installation manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("parses the deployment manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("validates bosh deployment manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentValidator.ValidateInputs).To(Equal([]fakebideplval.ValidateInput{ {Manifest: boshDeploymentManifest, ReleaseSetManifest: releaseSetManifest}, })) }) It("validates jobs in manifest refer to job in releases", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentValidator.ValidateReleaseJobsInputs).To(Equal([]fakebideplval.ValidateReleaseJobsInput{ {Manifest: boshDeploymentManifest, ReleaseManager: releaseManager}, })) }) It("logs validating stages", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[0]).To(Equal(&fakebiui.PerformCall{ Name: "validating", Stage: &fakebiui.FakeStage{ PerformCalls: []*fakebiui.PerformCall{ {Name: "Validating release 'fake-cpi-release-name'"}, {Name: "Validating cpi release"}, {Name: "Validating deployment manifest"}, {Name: "Validating stemcell"}, }, }, })) }) It("extracts CPI release tarball", func() { expectCPIReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("installs the CPI locally", func() { expectInstall.Times(1) expectNewCloud.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("adds a new 'installing CPI' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[1]).To(Equal(&fakebiui.PerformCall{ Name: "installing CPI", Stage: &fakebiui.FakeStage{}, // mock installer doesn't add sub-stages })) }) It("adds a new 'Starting registry' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[2]).To(Equal(&fakebiui.PerformCall{ Name: "Starting registry", })) }) Context("when the registry is configured", func() { BeforeEach(func() { installationManifest.Registry = biinstallmanifest.Registry{ Username: "******", Password: "******", Host: "fake-host", Port: 123, } }) It("starts & stops the registry", func() { mockRegistryServerManager.EXPECT().Start("fake-username", "fake-password", "fake-host", 123).Return(mockRegistryServer, nil) mockRegistryServer.EXPECT().Stop() err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) }) It("deletes the extracted CPI release", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeCPIRelease.DeleteCalled).To(BeTrue()) }) It("extracts the stemcell", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStemcellExtractor.ExtractInputs).To(Equal([]fakebistemcell.ExtractInput{ {TarballPath: stemcellTarballPath}, })) }) It("uploads the stemcell", func() { expectStemcellUpload.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) }) It("adds a new 'deploying' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[3]).To(Equal(&fakebiui.PerformCall{ Name: "deploying", Stage: &fakebiui.FakeStage{}, // mock deployer doesn't add sub-stages })) }) It("deploys", func() { expectDeploy.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("updates the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{ { ID: "fake-uuid-0", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, })) }) It("deletes unused stemcells", func() { expectStemcellDeleteUnused.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) Context("when deployment has not changed", func() { JustBeforeEach(func() { previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"my-release-id-1"}, Releases: []biconfig.ReleaseRecord{{ ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }}, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("skips deploy", func() { expectDeploy.Times(0) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("No deployment, stemcell or release changes. Skipping deploy.")) }) }) Context("when parsing the cpi deployment manifest fails", func() { BeforeEach(func() { fakeDeploymentParser.ParseErr = bosherr.Error("fake-parse-error") }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Parsing deployment manifest")) Expect(err.Error()).To(ContainSubstring("fake-parse-error")) Expect(fakeDeploymentParser.ParsePath).To(Equal(deploymentManifestPath)) }) }) Context("when the cpi release does not contain a 'cpi' job", func() { BeforeEach(func() { fakeCPIRelease.ReleaseJobs = []bireljob.Job{ { Name: "not-cpi", }, } }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Invalid CPI release 'fake-cpi-release-name': CPI release must contain specified job 'fake-cpi-release-job-name'")) }) }) Context("when multiple releases are given", func() { var ( otherReleaseTarballPath string fakeOtherRelease *fakebirel.FakeRelease expectOtherReleaseExtract *gomock.Call ) BeforeEach(func() { otherReleaseTarballPath = "/path/to/other-release.tgz" fakeFs.WriteFileString(otherReleaseTarballPath, "") fakeOtherRelease = fakebirel.New("other-release", "1234") fakeOtherRelease.ReleaseJobs = []bireljob.Job{{Name: "not-cpi"}} expectOtherReleaseExtract = mockReleaseExtractor.EXPECT().Extract( otherReleaseTarballPath, ).Return(fakeOtherRelease, nil).AnyTimes() releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, { Name: "other-release", URL: "file://" + otherReleaseTarballPath, }, }, } }) It("extracts all the release tarballs", func() { expectCPIReleaseExtract.Times(1) expectOtherReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("installs the CPI release locally", func() { expectInstall.Times(1) expectNewCloud.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("updates the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{ { ID: "fake-uuid-0", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "fake-uuid-1", Name: fakeOtherRelease.Name(), Version: fakeOtherRelease.Version(), }, })) }) Context("when one of the releases in the deployment has changed", func() { JustBeforeEach(func() { olderReleaseVersion := "1233" Expect(fakeOtherRelease.Version()).ToNot(Equal(olderReleaseVersion)) previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"existing-release-id-1", "existing-release-id-2"}, Releases: []biconfig.ReleaseRecord{ { ID: "existing-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "existing-release-id-2", Name: fakeOtherRelease.Name(), Version: olderReleaseVersion, }, }, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("updates the deployment record, clearing out unused releases", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) keys := []string{} ids := []string{} for _, releaseRecord := range deploymentState.Releases { keys = append(keys, fmt.Sprintf("%s-%s", releaseRecord.Name, releaseRecord.Version)) ids = append(ids, releaseRecord.ID) } Expect(deploymentState.CurrentReleaseIDs).To(ConsistOf(ids)) Expect(keys).To(ConsistOf([]string{ fmt.Sprintf("%s-%s", fakeCPIRelease.Name(), fakeCPIRelease.Version()), fmt.Sprintf("%s-%s", fakeOtherRelease.Name(), fakeOtherRelease.Version()), })) }) }) Context("when the deployment has not changed", func() { JustBeforeEach(func() { previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"my-release-id-1", "my-release-id-2"}, Releases: []biconfig.ReleaseRecord{ { ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "my-release-id-2", Name: fakeOtherRelease.Name(), Version: fakeOtherRelease.Version(), }, }, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("skips deploy", func() { expectDeploy.Times(0) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("No deployment, stemcell or release changes. Skipping deploy.")) }) }) }) Context("when release name does not match the name in release tarball", func() { BeforeEach(func() { releaseSetManifest.Releases = []birelmanifest.ReleaseRef{ { Name: "fake-other-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, } }) It("returns an error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Release name 'fake-other-cpi-release-name' does not match the name in release tarball 'fake-cpi-release-name'")) }) }) Context("When the stemcell tarball does not exist", func() { JustBeforeEach(func() { fakeStemcellExtractor.SetExtractBehavior(stemcellTarballPath, extractedStemcell, errors.New("no-stemcell-there")) }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("no-stemcell-there")) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[3] Expect(performCall.Name).To(Equal("Validating stemcell")) Expect(performCall.Error.Error()).To(ContainSubstring("no-stemcell-there")) }) }) Context("when release file does not exist", func() { BeforeEach(func() { mockReleaseExtractor.EXPECT().Extract(cpiReleaseTarballPath).Return(nil, errors.New("not there")) }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not there")) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[0] Expect(performCall.Name).To(Equal("Validating release 'fake-cpi-release-name'")) Expect(performCall.Error.Error()).To(ContainSubstring("not there")) }) }) Context("when the deployment state file does not exist", func() { BeforeEach(func() { fakeFs.RemoveAll(deploymentStatePath) }) It("creates a deployment state", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.DirectorID).To(Equal(directorID)) }) }) It("returns err when the deployment manifest does not exist", func() { fakeFs.RemoveAll(deploymentManifestPath) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Deployment manifest does not exist at '/path/to/manifest.yml'")) Expect(stdErr).To(gbytes.Say("Deployment '/path/to/manifest.yml' does not exist")) }) Context("when the deployment manifest is invalid", func() { BeforeEach(func() { fakeDeploymentValidator.SetValidateBehavior([]fakebideplval.ValidateOutput{ {Err: bosherr.Error("fake-deployment-validation-error")}, }) }) It("returns err", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-deployment-validation-error")) }) It("logs the failed event log", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[2] Expect(performCall.Name).To(Equal("Validating deployment manifest")) Expect(performCall.Error.Error()).To(Equal("Validating deployment manifest: fake-deployment-validation-error")) }) }) Context("when validating jobs fails", func() { BeforeEach(func() { fakeDeploymentValidator.SetValidateReleaseJobsBehavior([]fakebideplval.ValidateReleaseJobsOutput{ {Err: bosherr.Error("fake-jobs-validation-error")}, }) }) It("returns err", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-jobs-validation-error")) }) It("logs the failed event log", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[2] Expect(performCall.Name).To(Equal("Validating deployment manifest")) Expect(performCall.Error.Error()).To(Equal("Validating deployment jobs refer to jobs in release: fake-jobs-validation-error")) }) }) It("returns err when number of arguments is not equal 1", func() { err := command.Run(fakeStage, []string{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Invalid usage")) err = command.Run(fakeStage, []string{"1", "2"}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Invalid usage")) }) Context("when uploading stemcell fails", func() { JustBeforeEach(func() { expectStemcellUpload.Return(nil, bosherr.Error("fake-upload-error")) }) It("returns an error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-upload-error")) }) }) Context("when deploy fails", func() { BeforeEach(func() { mockDeployer.EXPECT().Deploy( cloud, boshDeploymentManifest, cloudStemcell, installationManifest.Registry, fakeVMManager, mockBlobstore, gomock.Any(), ).Return(nil, errors.New("fake-deploy-error")).AnyTimes() previousDeploymentState := biconfig.DeploymentState{ CurrentReleaseIDs: []string{"my-release-id-1"}, Releases: []biconfig.ReleaseRecord{{ ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }}, CurrentManifestSHA1: "fake-manifest-sha", } setupDeploymentStateService.Save(previousDeploymentState) }) It("clears the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-deploy-error")) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal("")) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{})) Expect(deploymentState.CurrentReleaseIDs).To(Equal([]string{})) }) }) Context("when compiled releases are being used", func() { var ( otherReleaseTarballPath string fakeOtherRelease *fakebirel.FakeRelease expectOtherReleaseExtract *gomock.Call ) BeforeEach(func() { otherReleaseTarballPath = "/path/to/other-release.tgz" fakeFs.WriteFileString(otherReleaseTarballPath, "") fakeOtherRelease = fakebirel.New("other-release", "1234") fakeOtherRelease.ReleaseIsCompiled = true fakeOtherRelease.ReleaseJobs = []bireljob.Job{{Name: "not-cpi"}} fakeOtherRelease.ReleasePackages = []*bipkg.Package{ { Stemcell: "ubuntu-trusty/fake-stemcell-version", }, } expectOtherReleaseExtract = mockReleaseExtractor.EXPECT().Extract( otherReleaseTarballPath, ).Return(fakeOtherRelease, nil).AnyTimes() releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, { Name: "other-release", URL: "file://" + otherReleaseTarballPath, }, }, } boshDeploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-job-name", Templates: []bideplmanifest.ReleaseJobRef{ { Release: "other-release", }, }, }, }, ResourcePools: []bideplmanifest.ResourcePool{ { Stemcell: bideplmanifest.StemcellRef{ URL: "file://" + stemcellTarballPath, }, }, }, } }) It("extracts the compiled release tarball", func() { expectOtherReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("parse compiled releases correctly", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) }) It("returns error if compiled package stemcell does not match the deployment stemcell", func() { fakeOtherRelease.ReleasePackages = []*bipkg.Package{ { Stemcell: "ubuntu-trusty/wrong-version", }, } err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("OS/Version mismatch between deployment stemcell and compiled package stemcell for release 'other-release'")) }) It("returns error if CPI release is compiled", func() { fakeCPIRelease.ReleaseIsCompiled = true err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("CPI is not allowed to be a compiled release. The provided CPI release 'fake-cpi-release-name' is compiled")) }) }) }) }
compiledPackageRecord1 := bistatepkg.CompiledPackageRecord{ BlobID: "fake-compiled-package-blobstore-id-1", BlobSHA1: "fake-compiled-package-sha1-1", } expectCompilePkg1 = mockPackageCompiler.EXPECT().Compile(releasePackage1).Return(compiledPackageRecord1, false, nil).AnyTimes() compiledPackageRecord2 := bistatepkg.CompiledPackageRecord{ BlobID: "fake-compiled-package-blobstore-id-2", BlobSHA1: "fake-compiled-package-sha1-2", } expectCompilePkg2 = mockPackageCompiler.EXPECT().Compile(releasePackage2).Return(compiledPackageRecord2, false, nil).AnyTimes() }) It("compiles all the job dependencies (packages) such that no package is compiled before its dependencies", func() { gomock.InOrder( expectCompilePkg1.Times(1), expectCompilePkg2.Times(1), ) _, err := dependencyCompiler.Compile(releaseJobs, fakeStage) Expect(err).ToNot(HaveOccurred()) }) It("returns references to the compiled packages", func() { compiledPackageRefs, err := dependencyCompiler.Compile(releaseJobs, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(compiledPackageRefs).To(Equal([]CompiledPackageRef{ { Name: "fake-release-package-name-1", Version: "fake-release-package-fingerprint-1",
applySpec = bias.ApplySpec{ Deployment: "fake-deployment-name", } }) JustBeforeEach(func() { fakeAgentState := agentclient.AgentState{JobState: "testing"} fakeVM.GetStateResult = fakeAgentState expectStateBuild = mockStateBuilder.EXPECT().Build(jobName, jobIndex, deploymentManifest, fakeStage, fakeAgentState).Return(mockState, nil).AnyTimes() expectStateBuildInitialState = mockStateBuilder.EXPECT().BuildInitialState(jobName, jobIndex, deploymentManifest).Return(mockState, nil).AnyTimes() mockState.EXPECT().ToApplySpec().Return(applySpec).AnyTimes() }) It("builds a new instance state", func() { expectStateBuild.Times(1) expectStateBuildInitialState.Times(1) err := instance.UpdateJobs(deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) }) It("tells agent to stop jobs, apply a new spec (with new rendered jobs templates), and start jobs", func() { err := instance.UpdateJobs(deploymentManifest, fakeStage) Expect(err).NotTo(HaveOccurred()) Expect(fakeVM.StopCalled).To(Equal(1)) Expect(fakeVM.ApplyInputs).To(Equal([]fakebivm.ApplyInput{ {ApplySpec: applySpec}, {ApplySpec: applySpec}, }))
Expect(err.Error()).To(Equal("Setting temp root: fake ChangeTempRootErr")) }) }) It("sets the temp root", func() { expectDeleteAndCleanup(true) err := newDeploymentDeleter().DeleteDeployment(fakeStage) Expect(err).NotTo(HaveOccurred()) Expect(fs.TempRootPath).To(Equal("fake-install-dir/fake-installation-id/tmp")) }) It("extracts & install CPI release tarball", func() { expectDeleteAndCleanup(true) gomock.InOrder( expectCPIExtractRelease.Times(1), expectCPIInstall.Times(1), expectNewCloud.Times(1), ) err := newDeploymentDeleter().DeleteDeployment(fakeStage) Expect(err).NotTo(HaveOccurred()) }) It("deletes the extracted CPI release", func() { expectDeleteAndCleanup(true) err := newDeploymentDeleter().DeleteDeployment(fakeStage) Expect(err).NotTo(HaveOccurred()) Expect(fs.FileExists("fake-cpi-extracted-dir")).To(BeFalse()) })
Context("the function fails", func() { It("stops the registry and returns the error", func() { stopCall.After(spyCall) err := newInstalation().WithRunningRegistry(logger, fakeStage, func() error { spy.Record() return errors.New("blarg!") }) Expect(err).To(HaveOccurred()) }) }) Context("when starting registry fails", func() { It("returns an error and doesn't call the function", func() { startCall.Return(mockRegistryServer, errors.New("registry-start-error")) spyCall.Times(0) stopCall.Times(0) err := newInstalation().WithRunningRegistry(logger, fakeStage, func() error { spy.Record() return nil }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Starting registry: registry-start-error")) }) }) Context("when stopping registry fails", func() { Context("when the function fails", func() { It("logs a warning and returns the function error", func() {
func describeBuilder() { var mockCtrl *gomock.Controller BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) AfterEach(func() { mockCtrl.Finish() }) var ( logger boshlog.Logger mockReleaseJobResolver *mock_deployment_release.MockJobResolver mockDependencyCompiler *mock_state_job.MockDependencyCompiler mockJobListRenderer *mock_template.MockJobListRenderer mockCompressor *mock_template.MockRenderedJobListCompressor mockBlobstore *mock_blobstore.MockBlobstore stateBuilder Builder ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) mockReleaseJobResolver = mock_deployment_release.NewMockJobResolver(mockCtrl) mockDependencyCompiler = mock_state_job.NewMockDependencyCompiler(mockCtrl) mockJobListRenderer = mock_template.NewMockJobListRenderer(mockCtrl) mockCompressor = mock_template.NewMockRenderedJobListCompressor(mockCtrl) mockBlobstore = mock_blobstore.NewMockBlobstore(mockCtrl) }) Describe("BuildInitialState", func() { var ( jobName string instanceID int deploymentManifest bideplmanifest.Manifest ) BeforeEach(func() { jobName = "fake-deployment-job-name" instanceID = 0 deploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-deployment-job-name", Networks: []bideplmanifest.JobNetwork{ { Name: "fake-network-name", StaticIPs: []string{"1.2.3.4"}, }, }, Templates: []bideplmanifest.ReleaseJobRef{ { Name: "fake-release-job-name", Release: "fake-release-name", Properties: &biproperty.Map{ "fake-template-property": "fake-template-property-value", }, }, }, Properties: biproperty.Map{ "fake-job-property": "fake-job-property-value", }, }, }, Networks: []bideplmanifest.Network{ { Name: "fake-network-name", Type: "fake-network-type", CloudProperties: biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, Properties: biproperty.Map{ "fake-job-property": "fake-global-property-value", //overridden by job property value }, } stateBuilder = NewBuilder( mockReleaseJobResolver, mockDependencyCompiler, mockJobListRenderer, mockCompressor, mockBlobstore, logger, ) }) It("generates an initial apply spec", func() { state, err := stateBuilder.BuildInitialState(jobName, instanceID, deploymentManifest) Expect(err).ToNot(HaveOccurred()) Expect(state.ToApplySpec()).To(Equal(bias.ApplySpec{ Deployment: "fake-deployment-name", Index: 0, Job: bias.Job{ Name: "fake-deployment-job-name", Templates: []bias.Blob{}, }, Packages: map[string]bias.Blob{}, Networks: map[string]interface{}{ "fake-network-name": map[string]interface{}{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "ip": "1.2.3.4", "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, })) }) }) Describe("Build", func() { var ( mockRenderedJobList *mock_template.MockRenderedJobList mockRenderedJobListArchive *mock_template.MockRenderedJobListArchive jobName string instanceID int deploymentManifest bideplmanifest.Manifest fakeStage *fakebiui.FakeStage releasePackageLibyaml *birelpkg.Package releasePackageRuby *birelpkg.Package releasePackageCPI *birelpkg.Package agentState biac.AgentState expectedIP string expectCompile *gomock.Call ) BeforeEach(func() { mockRenderedJobList = mock_template.NewMockRenderedJobList(mockCtrl) mockRenderedJobListArchive = mock_template.NewMockRenderedJobListArchive(mockCtrl) jobName = "fake-deployment-job-name" instanceID = 0 expectedIP = "1.2.3.4" deploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-deployment-job-name", Networks: []bideplmanifest.JobNetwork{ { Name: "fake-network-name", StaticIPs: []string{"1.2.3.4"}, }, }, Templates: []bideplmanifest.ReleaseJobRef{ { Name: "fake-release-job-name", Release: "fake-release-name", Properties: &biproperty.Map{ "fake-template-property": "fake-template-property-value", }, }, }, Properties: biproperty.Map{ "fake-job-property": "fake-job-property-value", }, }, }, Networks: []bideplmanifest.Network{ { Name: "fake-network-name", Type: "fake-network-type", CloudProperties: biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, Properties: biproperty.Map{ "fake-job-property": "fake-global-property-value", //overridden by job property value }, } agentState = biac.AgentState{ NetworkSpecs: map[string]biac.NetworkSpec{ "fake-network-name": biac.NetworkSpec{ IP: "1.2.3.5", }, }, } fakeStage = fakebiui.NewFakeStage() stateBuilder = NewBuilder( mockReleaseJobResolver, mockDependencyCompiler, mockJobListRenderer, mockCompressor, mockBlobstore, logger, ) releasePackageLibyaml = &birelpkg.Package{ Name: "libyaml", Fingerprint: "fake-package-source-fingerprint-libyaml", SHA1: "fake-package-source-sha1-libyaml", Dependencies: []*birelpkg.Package{}, ArchivePath: "fake-package-archive-path-libyaml", // only required by compiler... } releasePackageRuby = &birelpkg.Package{ Name: "ruby", Fingerprint: "fake-package-source-fingerprint-ruby", SHA1: "fake-package-source-sha1-ruby", Dependencies: []*birelpkg.Package{releasePackageLibyaml}, ArchivePath: "fake-package-archive-path-ruby", // only required by compiler... } releasePackageCPI = &birelpkg.Package{ Name: "cpi", Fingerprint: "fake-package-source-fingerprint-cpi", SHA1: "fake-package-source-sha1-cpi", Dependencies: []*birelpkg.Package{releasePackageRuby}, ArchivePath: "fake-package-archive-path-cpi", // only required by compiler... } }) JustBeforeEach(func() { releaseJob := bireljob.Job{ Name: "fake-release-job-name", Fingerprint: "fake-release-job-source-fingerprint", Packages: []*birelpkg.Package{releasePackageCPI, releasePackageRuby}, } mockReleaseJobResolver.EXPECT().Resolve("fake-release-job-name", "fake-release-name").Return(releaseJob, nil) releaseJobs := []bireljob.Job{releaseJob} compiledPackageRefs := []bistatejob.CompiledPackageRef{ { Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", SHA1: "fake-package-compiled-archive-sha1-libyaml", }, { Name: "ruby", Version: "fake-package-source-fingerprint-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", SHA1: "fake-package-compiled-archive-sha1-ruby", }, { Name: "cpi", Version: "fake-package-source-fingerprint-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", SHA1: "fake-package-compiled-archive-sha1-cpi", }, } expectCompile = mockDependencyCompiler.EXPECT().Compile(releaseJobs, fakeStage).Return(compiledPackageRefs, nil).AnyTimes() releaseJobProperties := map[string]*biproperty.Map{ "fake-release-job-name": &biproperty.Map{ "fake-template-property": "fake-template-property-value", }, } jobProperties := biproperty.Map{ "fake-job-property": "fake-job-property-value", } globalProperties := biproperty.Map{ "fake-job-property": "fake-global-property-value", } mockJobListRenderer.EXPECT().Render(releaseJobs, releaseJobProperties, jobProperties, globalProperties, "fake-deployment-name", expectedIP).Return(mockRenderedJobList, nil) mockRenderedJobList.EXPECT().DeleteSilently() mockCompressor.EXPECT().Compress(mockRenderedJobList).Return(mockRenderedJobListArchive, nil) mockRenderedJobListArchive.EXPECT().DeleteSilently() mockRenderedJobListArchive.EXPECT().Path().Return("fake-rendered-job-list-archive-path") mockRenderedJobListArchive.EXPECT().SHA1().Return("fake-rendered-job-list-archive-sha1") mockRenderedJobListArchive.EXPECT().Fingerprint().Return("fake-rendered-job-list-fingerprint") mockBlobstore.EXPECT().Add("fake-rendered-job-list-archive-path").Return("fake-rendered-job-list-archive-blob-id", nil) }) It("compiles the dependencies of the jobs", func() { expectCompile.Times(1) _, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) }) It("builds a new instance state with zero-to-many networks", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.NetworkInterfaces()).To(ContainElement(NetworkRef{ Name: "fake-network-name", Interface: map[string]interface{}{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "ip": "1.2.3.4", "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, })) Expect(state.NetworkInterfaces()).To(HaveLen(1)) }) Context("dynamic network without IP address", func() { Context("single network", func() { BeforeEach(func() { deploymentManifest.Jobs[0].Networks[0].StaticIPs = nil deploymentManifest.Networks[0].Type = "dynamic" expectedIP = "1.2.3.5" }) It("should not fail", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.NetworkInterfaces()).To(ContainElement(NetworkRef{ Name: "fake-network-name", Interface: map[string]interface{}{ "type": "dynamic", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, })) Expect(state.NetworkInterfaces()).To(HaveLen(1)) }) }) Context("multiple networks", func() { BeforeEach(func() { expectedIP = "1.2.3.6" deploymentManifest.Networks = append( deploymentManifest.Networks, bideplmanifest.Network{ Name: "fake-dynamic-network-name", Type: "dynamic", CloudProperties: biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, ) deploymentManifest.Jobs[0].Networks = append( deploymentManifest.Jobs[0].Networks, bideplmanifest.JobNetwork{ Name: "fake-dynamic-network-name", Defaults: []bideplmanifest.NetworkDefault{bideplmanifest.NetworkDefaultDNS, bideplmanifest.NetworkDefaultGateway}, }, ) agentState.NetworkSpecs["fake-dynamic-network-name"] = biac.NetworkSpec{ IP: "1.2.3.6", } }) It("should not fail", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.NetworkInterfaces()).To(ContainElement(NetworkRef{ Name: "fake-dynamic-network-name", Interface: map[string]interface{}{ "type": "dynamic", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, })) Expect(state.NetworkInterfaces()).To(HaveLen(2)) }) }) }) It("builds a new instance state with zero-to-many rendered jobs from one or more releases", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.RenderedJobs()).To(ContainElement(JobRef{ Name: "fake-release-job-name", Version: "fake-release-job-source-fingerprint", })) // multiple jobs are rendered in a single archive Expect(state.RenderedJobListArchive()).To(Equal(BlobRef{ BlobstoreID: "fake-rendered-job-list-archive-blob-id", SHA1: "fake-rendered-job-list-archive-sha1", })) Expect(state.RenderedJobs()).To(HaveLen(1)) }) It("prints ui stages for compiling packages and rendering job templates", func() { _, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(fakeStage.PerformCalls).To(Equal([]*fakebiui.PerformCall{ // compile stages not produced by mockDependencyCompiler {Name: "Rendering job templates"}, })) }) It("builds a new instance state with the compiled packages required by the release jobs", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) }) It("builds a new instance state that includes transitively dependent compiled packages", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, })) Expect(state.CompiledPackages()).To(HaveLen(3)) }) Context("when multiple packages have the same dependency", func() { BeforeEach(func() { releasePackageRuby.Dependencies = append(releasePackageRuby.Dependencies, releasePackageLibyaml) }) It("does not recompile dependant packages", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, })) Expect(state.CompiledPackages()).To(HaveLen(3)) }) }) It("builds an instance state that can be converted to an ApplySpec", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage, agentState) Expect(err).ToNot(HaveOccurred()) Expect(state.ToApplySpec()).To(Equal(bias.ApplySpec{ Deployment: "fake-deployment-name", Index: 0, Networks: map[string]interface{}{ "fake-network-name": map[string]interface{}{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "ip": "1.2.3.4", "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, Job: bias.Job{ Name: "fake-deployment-job-name", Templates: []bias.Blob{ { Name: "fake-release-job-name", Version: "fake-release-job-source-fingerprint", }, }, }, Packages: map[string]bias.Blob{ "cpi": bias.Blob{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, "ruby": bias.Blob{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, "libyaml": bias.Blob{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, }, RenderedTemplatesArchive: bias.RenderedTemplatesArchiveSpec{ BlobstoreID: "fake-rendered-job-list-archive-blob-id", SHA1: "fake-rendered-job-list-archive-sha1", }, ConfigurationHash: "fake-rendered-job-list-fingerprint", })) }) }) }
func describeRemotePackageCompiler() { var mockCtrl *gomock.Controller BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) AfterEach(func() { mockCtrl.Finish() }) var ( packageRepo bistatepkg.CompiledPackageRepo pkgDependency *birelpkg.Package pkg *birelpkg.Package mockBlobstore *mock_blobstore.MockBlobstore mockAgentClient *mock_agentclient.MockAgentClient archivePath = "fake-archive-path" remotePackageCompiler bistatepkg.Compiler compiledPackages map[bistatepkg.CompiledPackageRecord]*birelpkg.Package expectBlobstoreAdd *gomock.Call expectAgentCompile *gomock.Call ) BeforeEach(func() { mockBlobstore = mock_blobstore.NewMockBlobstore(mockCtrl) mockAgentClient = mock_agentclient.NewMockAgentClient(mockCtrl) index := biindex.NewInMemoryIndex() packageRepo = bistatepkg.NewCompiledPackageRepo(index) remotePackageCompiler = NewRemotePackageCompiler(mockBlobstore, mockAgentClient, packageRepo) pkgDependency = &birelpkg.Package{ Name: "fake-package-name-dep", Fingerprint: "fake-package-fingerprint-dep", } pkg = &birelpkg.Package{ Name: "fake-package-name", Fingerprint: "fake-package-fingerprint", SHA1: "fake-source-package-sha1", ArchivePath: archivePath, Dependencies: []*birelpkg.Package{pkgDependency}, } depRecord1 := bistatepkg.CompiledPackageRecord{ BlobID: "fake-compiled-package-blob-id-dep", BlobSHA1: "fake-compiled-package-sha1-dep", } compiledPackages = map[bistatepkg.CompiledPackageRecord]*birelpkg.Package{ depRecord1: pkgDependency, } }) JustBeforeEach(func() { // add compiled packages to the repo for record, dependency := range compiledPackages { err := packageRepo.Save(*dependency, record) Expect(err).ToNot(HaveOccurred()) } packageSource := biagentclient.BlobRef{ Name: "fake-package-name", Version: "fake-package-fingerprint", BlobstoreID: "fake-source-package-blob-id", SHA1: "fake-source-package-sha1", } packageDependencies := []biagentclient.BlobRef{ { Name: "fake-package-name-dep", Version: "fake-package-fingerprint-dep", BlobstoreID: "fake-compiled-package-blob-id-dep", SHA1: "fake-compiled-package-sha1-dep", }, } compiledPackageRef := biagentclient.BlobRef{ Name: "fake-package-name", Version: "fake-package-version", BlobstoreID: "fake-compiled-package-blob-id", SHA1: "fake-compiled-package-sha1", } expectBlobstoreAdd = mockBlobstore.EXPECT().Add(archivePath).Return("fake-source-package-blob-id", nil).AnyTimes() expectAgentCompile = mockAgentClient.EXPECT().CompilePackage(packageSource, packageDependencies).Return(compiledPackageRef, nil).AnyTimes() }) Describe("Compile", func() { It("uploads the package archive to the blobstore and then compiles the package with the agent", func() { gomock.InOrder( expectBlobstoreAdd.Times(1), expectAgentCompile.Times(1), ) compiledPackageRecord, _, err := remotePackageCompiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) Expect(compiledPackageRecord).To(Equal(bistatepkg.CompiledPackageRecord{ BlobID: "fake-compiled-package-blob-id", BlobSHA1: "fake-compiled-package-sha1", })) }) It("saves the compiled package ref in the package repo", func() { compiledPackageRecord, _, err := remotePackageCompiler.Compile(pkg) Expect(err).ToNot(HaveOccurred()) record, found, err := packageRepo.Find(*pkg) Expect(err).ToNot(HaveOccurred()) Expect(found).To(BeTrue()) Expect(record).To(Equal(compiledPackageRecord)) }) Context("when the dependencies are not in the repo", func() { BeforeEach(func() { compiledPackages = map[bistatepkg.CompiledPackageRecord]*birelpkg.Package{} }) It("returns an error", func() { _, _, err := remotePackageCompiler.Compile(pkg) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Remote compilation failure: Package 'fake-package-name/fake-package-fingerprint' requires package 'fake-package-name-dep/fake-package-fingerprint-dep', but it has not been compiled")) }) }) Context("when package belongs to a compiled release", func() { BeforeEach(func() { pkg.Stemcell = "ubuntu/fake" }) AfterEach(func() { pkg.Stemcell = "" }) It("should skip compilation", func() { compiledPackageRecord, isAlreadyCompiled, err := remotePackageCompiler.Compile(pkg) expectAgentCompile.Times(0) Expect(err).ToNot(HaveOccurred()) Expect(isAlreadyCompiled).To(Equal(true)) Expect(compiledPackageRecord.BlobID).To(Equal("fake-source-package-blob-id")) Expect(compiledPackageRecord.BlobSHA1).To(Equal(pkg.SHA1)) }) }) }) }
Context("when no current instances, disks, or stemcells exist", func() { It("returns not found", func() { _, found, err := deploymentManager.FindCurrent() Expect(err).ToNot(HaveOccurred()) Expect(found).To(BeFalse()) }) }) Context("when current instances exist", func() { BeforeEach(func() { instance := mock_instance.NewMockInstance(mockCtrl) expectedInstances = append(expectedInstances, instance) }) It("returns a deployment that wraps the current instances, disks, & stemcells", func() { expectNewDeployment.Times(1) deployment, found, err := deploymentManager.FindCurrent() Expect(err).ToNot(HaveOccurred()) Expect(found).To(BeTrue()) Expect(deployment).To(Equal(mockDeployment)) }) }) Context("when current disk exist", func() { BeforeEach(func() { disk := mock_disk.NewMockDisk(mockCtrl) expectedDisks = append(expectedDisks, disk) }) It("returns a deployment that wraps the current instances, disks, & stemcells", func() {