Esempio n. 1
0
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},
			}