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("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 expectCompile *gomock.Call ) BeforeEach(func() { mockRenderedJobList = mock_template.NewMockRenderedJobList(mockCtrl) mockRenderedJobListArchive = mock_template.NewMockRenderedJobListArchive(mockCtrl) 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", }, }, Templates: []bideplmanifest.ReleaseJobRef{ { Name: "fake-release-job-name", Release: "fake-release-name", }, }, 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 }, } 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() jobProperties := biproperty.Map{ "fake-job-property": "fake-job-property-value", } globalProperties := biproperty.Map{ "fake-job-property": "fake-global-property-value", } mockJobListRenderer.EXPECT().Render(releaseJobs, jobProperties, globalProperties, "fake-deployment-name").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) Expect(err).ToNot(HaveOccurred()) }) It("builds a new instance state with zero-to-many networks", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.NetworkInterfaces()).To(ContainElement(NetworkRef{ Name: "fake-network-name", Interface: biproperty.Map{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, })) Expect(state.NetworkInterfaces()).To(HaveLen(1)) }) 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) 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) 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) 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) 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) 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) Expect(err).ToNot(HaveOccurred()) Expect(state.ToApplySpec()).To(Equal(bias.ApplySpec{ Deployment: "fake-deployment-name", Index: 0, Networks: map[string]biproperty.Map{ "fake-network-name": biproperty.Map{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "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", })) }) }) }
package pkg_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/cloudfoundry/bosh-init/state/pkg" birelpkg "github.com/cloudfoundry/bosh-init/release/pkg" ) var _ = Describe("DependencyResolver", func() { It("supports a single dependency", func() { a := birelpkg.Package{Name: "a"} b := birelpkg.Package{Name: "b"} a.Dependencies = []*birelpkg.Package{&b} deps := ResolveDependencies(&a) Expect(deps).To(Equal([]*birelpkg.Package{&b})) }) It("supports a transitive dependency", func() { a := birelpkg.Package{Name: "a"} b := birelpkg.Package{Name: "b"} a.Dependencies = []*birelpkg.Package{&b} c := birelpkg.Package{Name: "c"} b.Dependencies = []*birelpkg.Package{&c} deps := ResolveDependencies(&a) Expect(deps).To(Equal([]*birelpkg.Package{&c, &b})) })
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)) }) }) }) }
package pkg_test import ( . "github.com/cloudfoundry/bosh-init/internal/github.com/onsi/ginkgo" . "github.com/cloudfoundry/bosh-init/internal/github.com/onsi/gomega" birelpkg "github.com/cloudfoundry/bosh-init/release/pkg" . "github.com/cloudfoundry/bosh-init/state/pkg" ) var _ = Describe("DependencyResolver", func() { It("supports a single dependency", func() { a := birelpkg.Package{Name: "a"} b := birelpkg.Package{Name: "b"} a.Dependencies = []*birelpkg.Package{&b} deps := ResolveDependencies(&a) Expect(deps).To(Equal([]*birelpkg.Package{&b})) }) It("supports a transitive dependency", func() { a := birelpkg.Package{Name: "a"} b := birelpkg.Package{Name: "b"} a.Dependencies = []*birelpkg.Package{&b} c := birelpkg.Package{Name: "c"} b.Dependencies = []*birelpkg.Package{&c} deps := ResolveDependencies(&a) Expect(deps).To(ContainElement(&b)) Expect(deps).To(ContainElement(&c)) Expect(len(deps)).To(Equal(2))
var ( index biindex.Index compiledPackageRepo CompiledPackageRepo fakeFS *fakesys.FakeFileSystem ) BeforeEach(func() { fakeFS = fakesys.NewFakeFileSystem() index = biindex.NewFileIndex("/index_file", fakeFS) compiledPackageRepo = NewCompiledPackageRepo(index) }) Context("Save and Find", func() { var ( record CompiledPackageRecord dependency birelpkg.Package pkg birelpkg.Package ) BeforeEach(func() { record = CompiledPackageRecord{} dependency = birelpkg.Package{ Name: "fake-dependency-package", Fingerprint: "fake-dependency-fingerprint", } pkg = birelpkg.Package{ Name: "fake-package-name", Fingerprint: "fake-package-fingerprint", Dependencies: []*birelpkg.Package{&dependency}, } })
}) It("logs compile stages", func() { _, err := dependencyCompiler.Compile(releaseJobs, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(fakeStage.PerformCalls).To(Equal([]*fakebiui.PerformCall{ {Name: "Compiling package 'fake-release-package-name-1/fake-release-package-fingerprint-1'"}, {Name: "Compiling package 'fake-release-package-name-2/fake-release-package-fingerprint-2'"}, })) }) Context("Graph with circular dependency", func() { var ( package1, package2, package3 *birelpkg.Package ) BeforeEach(func() { package1 = &birelpkg.Package{ Name: "fake-package-name-1", Dependencies: []*birelpkg.Package{}, } package2 = &birelpkg.Package{ Name: "fake-package-name-2", Dependencies: []*birelpkg.Package{package1}, } package3 = &birelpkg.Package{ Name: "fake-package-name-3", Dependencies: []*birelpkg.Package{package2}, }