1package cmd_test
2
3import (
4	. "github.com/cloudfoundry/bosh-cli/cmd"
5
6	. "github.com/cloudfoundry/bosh-cli/release/resource"
7
8	. "github.com/onsi/ginkgo"
9	. "github.com/onsi/gomega"
10
11	boshrel "github.com/cloudfoundry/bosh-cli/release"
12	boshjob "github.com/cloudfoundry/bosh-cli/release/job"
13	boshpkg "github.com/cloudfoundry/bosh-cli/release/pkg"
14	boshtbl "github.com/cloudfoundry/bosh-cli/ui/table"
15
16	fakecrypto "github.com/cloudfoundry/bosh-cli/crypto/fakes"
17	fakerel "github.com/cloudfoundry/bosh-cli/release/releasefakes"
18	fakeui "github.com/cloudfoundry/bosh-cli/ui/fakes"
19	fakefu "github.com/cloudfoundry/bosh-utils/fileutil/fakes"
20	fakes2 "github.com/cloudfoundry/bosh-utils/system/fakes"
21
22	"github.com/cloudfoundry/bosh-cli/crypto/fakes"
23	"github.com/cloudfoundry/bosh-cli/release/license"
24	"github.com/cloudfoundry/bosh-utils/errors"
25)
26
27var _ = Describe("RedigestRelease", func() {
28
29	var (
30		releaseReader                *fakerel.FakeReader
31		ui                           *fakeui.FakeUI
32		fmv                          *fakefu.FakeMover
33		releaseWriter                *fakerel.FakeWriter
34		command                      RedigestReleaseCmd
35		args                         RedigestReleaseArgs
36		fakeDigestCalculator         *fakes.FakeDigestCalculator
37		releaseWriterTempDestination string
38		fs                           *fakes2.FakeFileSystem
39	)
40
41	BeforeEach(func() {
42		releaseReader = &fakerel.FakeReader{}
43		releaseWriter = &fakerel.FakeWriter{}
44		ui = &fakeui.FakeUI{}
45		fmv = &fakefu.FakeMover{}
46		fs = fakes2.NewFakeFileSystem()
47
48		fakeDigestCalculator = fakes.NewFakeDigestCalculator()
49		command = NewRedigestReleaseCmd(releaseReader, releaseWriter, fakeDigestCalculator, fmv, fs, ui)
50	})
51	var fakeSha128Release *fakerel.FakeRelease
52
53	job1ResourcePath := "/job-resource-1-path"
54	pkg1ResourcePath := "/pkg-resource-1-path"
55	compiledPackage1ResourcePath := "/compiled-pkg-resource-path"
56	licenseResourcePath := "/license-resource-path"
57	fileContentSha1 := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
58
59	createFakeFileWithKnownSha1 := func() *fakes2.FakeFile {
60		return &fakes2.FakeFile{
61			Contents: []byte("hello world"),
62		}
63	}
64
65	BeforeEach(func() {
66		args = RedigestReleaseArgs{
67			Path:        "/some/release_128.tgz",
68			Destination: FileArg{ExpandedPath: "/some/release_256.tgz"},
69		}
70
71		fs.RegisterOpenFile(job1ResourcePath, createFakeFileWithKnownSha1())
72		fs.RegisterOpenFile(pkg1ResourcePath, createFakeFileWithKnownSha1())
73		fs.RegisterOpenFile(compiledPackage1ResourcePath, createFakeFileWithKnownSha1())
74		fs.RegisterOpenFile(licenseResourcePath, createFakeFileWithKnownSha1())
75
76		fakeSha128Release = &fakerel.FakeRelease{}
77		jobSha128 := boshjob.NewJob(NewResourceWithBuiltArchive("job-resource-1", "job-sha128-fp", job1ResourcePath, fileContentSha1))
78		packageSha128 := boshpkg.NewPackage(NewResourceWithBuiltArchive("pkg-resource-1", "pkg-sha128-fp", pkg1ResourcePath, fileContentSha1), nil)
79		compiledPackageSha128 := boshpkg.NewCompiledPackageWithArchive("compiledpkg-resource-1", "compiledpkg-sha128-fp", "1", compiledPackage1ResourcePath, fileContentSha1, nil)
80
81		fakeSha128Release.JobsReturns([]*boshjob.Job{jobSha128})
82		fakeSha128Release.PackagesReturns([]*boshpkg.Package{packageSha128})
83		fakeSha128Release.LicenseReturns(license.NewLicense(NewResourceWithBuiltArchive("license-resource-path", "lic-sha128-fp", licenseResourcePath, fileContentSha1)))
84		fakeSha128Release.CompiledPackagesReturns([]*boshpkg.CompiledPackage{compiledPackageSha128})
85
86		fakeSha128Release.CopyWithStub = func(jobs []*boshjob.Job, pkgs []*boshpkg.Package, lic *license.License, compiledPackages []*boshpkg.CompiledPackage) boshrel.Release {
87			fakeSha256Release := &fakerel.FakeRelease{}
88			fakeSha256Release.NameReturns("custom-name")
89			fakeSha256Release.VersionReturns("custom-ver")
90			fakeSha256Release.CommitHashWithMarkReturns("commit")
91			fakeSha256Release.JobsReturns(jobs)
92			fakeSha256Release.PackagesReturns(pkgs)
93			fakeSha256Release.LicenseReturns(lic)
94			fakeSha256Release.CompiledPackagesReturns(compiledPackages)
95			return fakeSha256Release
96		}
97
98		fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
99			job1ResourcePath:             {DigestStr: "sha256:jobsha256"},
100			pkg1ResourcePath:             {DigestStr: "sha256:pkgsha256"},
101			licenseResourcePath:          {DigestStr: "sha256:licsha256"},
102			compiledPackage1ResourcePath: {DigestStr: "sha256:compiledpkgsha256"},
103		})
104
105		releaseReader.ReadReturns(fakeSha128Release, nil)
106		releaseWriterTempDestination = "/some/temp/release_256.tgz"
107		releaseWriter.WriteReturns(releaseWriterTempDestination, nil)
108	})
109
110	Context("Given a valid sha128 release tar", func() {
111		It("Should convert it to a sha256 release tar", func() {
112			err := command.Run(args)
113			Expect(err).ToNot(HaveOccurred())
114
115			Expect(releaseReader.ReadCallCount()).ToNot(Equal(0))
116
117			readPathArg := releaseReader.ReadArgsForCall(0)
118			Expect(readPathArg).To(Equal("/some/release_128.tgz"))
119
120			Expect(releaseWriter.WriteCallCount()).To(Equal(1))
121			sha2ifyRelease, _ := releaseWriter.WriteArgsForCall(0)
122
123			Expect(sha2ifyRelease).NotTo(BeNil())
124
125			Expect(sha2ifyRelease.License()).ToNot(BeNil())
126			Expect(sha2ifyRelease.License().ArchiveDigest()).To(Equal("sha256:licsha256"))
127
128			Expect(sha2ifyRelease.Jobs()).To(HaveLen(1))
129			Expect(sha2ifyRelease.Jobs()[0].ArchiveDigest()).To(Equal("sha256:jobsha256"))
130
131			Expect(sha2ifyRelease.Packages()).To(HaveLen(1))
132			Expect(sha2ifyRelease.Packages()[0].ArchiveDigest()).To(Equal("sha256:pkgsha256"))
133
134			Expect(sha2ifyRelease.CompiledPackages()).To(HaveLen(1))
135			Expect(sha2ifyRelease.CompiledPackages()[0].ArchiveDigest()).To(Equal("sha256:compiledpkgsha256"))
136
137			Expect(fmv.MoveCallCount()).To(Equal(1))
138
139			src, dst := fmv.MoveArgsForCall(0)
140			Expect(src).To(Equal(releaseWriterTempDestination))
141			Expect(dst).To(Equal(args.Destination.ExpandedPath))
142
143			Expect(ui.Tables[0]).To(Equal(boshtbl.Table{
144				Header: []boshtbl.Header{
145					boshtbl.NewHeader("Name"),
146					boshtbl.NewHeader("Version"),
147					boshtbl.NewHeader("Commit Hash"),
148					boshtbl.NewHeader("Archive"),
149				},
150				Rows: [][]boshtbl.Value{
151					{
152						boshtbl.NewValueString("custom-name"),
153						boshtbl.NewValueString("custom-ver"),
154						boshtbl.NewValueString("commit"),
155						boshtbl.NewValueString("/some/release_256.tgz"),
156					},
157				},
158				Transpose: true,
159			}))
160		})
161
162		Context("when unable to write the sha256 tarball", func() {
163			BeforeEach(func() {
164				releaseWriter.WriteReturns("", errors.Error("disaster"))
165			})
166
167			It("should return an error", func() {
168				err := command.Run(args)
169				Expect(err).To(HaveOccurred())
170				Expect(err.Error()).To(ContainSubstring("disaster"))
171			})
172		})
173
174		Context("when rehashing a licence fails", func() {
175			BeforeEach(func() {
176				fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
177					job1ResourcePath:             {DigestStr: "sha256:jobsha256"},
178					pkg1ResourcePath:             {DigestStr: "sha256:pkgsha256"},
179					compiledPackage1ResourcePath: {DigestStr: "sha256:compiledpkgsha256"},
180					licenseResourcePath:          {Err: errors.Error("Unknown algorithm")},
181				})
182			})
183
184			It("should return an error", func() {
185				err := command.Run(args)
186				Expect(err).To(HaveOccurred())
187				Expect(err.Error()).To(ContainSubstring("Unknown algorithm"))
188			})
189		})
190
191		Context("when rehashing compiled packages fails", func() {
192			BeforeEach(func() {
193				fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
194					job1ResourcePath:             {DigestStr: "sha256:jobsha256"},
195					pkg1ResourcePath:             {DigestStr: "sha256:pkgsha256"},
196					compiledPackage1ResourcePath: {Err: errors.Error("Unknown algorithm")},
197					licenseResourcePath:          {DigestStr: "sha256:licsha256"},
198				})
199			})
200
201			It("should return an error", func() {
202				err := command.Run(args)
203				Expect(err).To(HaveOccurred())
204				Expect(err.Error()).To(ContainSubstring("Unknown algorithm"))
205			})
206		})
207
208		Context("when rehashing packages fails", func() {
209			BeforeEach(func() {
210				fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
211					job1ResourcePath:             {DigestStr: "sha256:jobsha256"},
212					pkg1ResourcePath:             {Err: errors.Error("Unknown algorithm")},
213					compiledPackage1ResourcePath: {DigestStr: "sha256:compiledpkgsha256"},
214					licenseResourcePath:          {DigestStr: "sha256:licsha256"},
215				})
216			})
217
218			It("should return an error", func() {
219				err := command.Run(args)
220				Expect(err).To(HaveOccurred())
221				Expect(err.Error()).To(ContainSubstring("Unknown algorithm"))
222			})
223		})
224
225		Context("when rehashing jobs fails", func() {
226			BeforeEach(func() {
227				fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
228					job1ResourcePath:             {Err: errors.Error("Unknown algorithm")},
229					pkg1ResourcePath:             {DigestStr: "sha256:pkgsha256"},
230					compiledPackage1ResourcePath: {DigestStr: "sha256:compiledpkgsha256"},
231					licenseResourcePath:          {DigestStr: "sha256:licsha256"},
232				})
233			})
234
235			It("should return an error", func() {
236				err := command.Run(args)
237				Expect(err).To(HaveOccurred())
238				Expect(err.Error()).To(ContainSubstring("Unknown algorithm"))
239			})
240		})
241
242		Context("when no licence is provided", func() {
243			BeforeEach(func() {
244				fakeSha128Release.LicenseReturns(nil)
245				fakeDigestCalculator.SetCalculateBehavior(map[string]fakecrypto.CalculateInput{
246					job1ResourcePath:             {DigestStr: "sha256:jobsha256"},
247					pkg1ResourcePath:             {DigestStr: "sha256:pkgsha256"},
248					compiledPackage1ResourcePath: {DigestStr: "sha256:compiledpkgsha256"},
249				})
250			})
251
252			It("should not return an error", func() {
253				err := command.Run(args)
254				Expect(err).ToNot(HaveOccurred())
255			})
256		})
257
258		Context("When unable to move sha2fyied release to destination", func() {
259			BeforeEach(func() {
260				fmv.MoveReturns(errors.Error("disaster"))
261			})
262
263			It("Should return an error", func() {
264				err := command.Run(args)
265				Expect(err).To(HaveOccurred())
266				Expect(err.Error()).To(ContainSubstring("disaster"))
267			})
268		})
269	})
270
271	Context("Given an invalid sha128 release tar", func() {
272		Context("Given a job that does not verify", func() {
273			BeforeEach(func() {
274				fs.RegisterOpenFile(job1ResourcePath, &fakes2.FakeFile{
275					Contents: []byte("content that does not match expected sha1"),
276				})
277			})
278
279			It("should return an error", func() {
280				err := command.Run(args)
281				Expect(err).To(HaveOccurred())
282				Expect(err.Error()).To(ContainSubstring("Expected stream to have digest"))
283
284			})
285		})
286
287		Context("Given a package that does not verify", func() {
288			BeforeEach(func() {
289				fs.RegisterOpenFile(pkg1ResourcePath, &fakes2.FakeFile{
290					Contents: []byte("content that does not match expected sha1"),
291				})
292			})
293
294			It("should return an error", func() {
295				err := command.Run(args)
296				Expect(err).To(HaveOccurred())
297				Expect(err.Error()).To(ContainSubstring("Expected stream to have digest"))
298
299			})
300		})
301
302		Context("Given a compiled package that does not verify", func() {
303			BeforeEach(func() {
304				fs.RegisterOpenFile(compiledPackage1ResourcePath, &fakes2.FakeFile{
305					Contents: []byte("content that does not match expected sha1"),
306				})
307			})
308
309			It("should return an error", func() {
310				err := command.Run(args)
311				Expect(err).To(HaveOccurred())
312				Expect(err.Error()).To(ContainSubstring("Expected stream to have digest"))
313
314			})
315		})
316
317		Context("Given a license that does not verify", func() {
318			BeforeEach(func() {
319				fs.RegisterOpenFile(licenseResourcePath, &fakes2.FakeFile{
320					Contents: []byte("content that does not match expected sha1"),
321				})
322			})
323
324			It("should return an error", func() {
325				err := command.Run(args)
326				Expect(err).To(HaveOccurred())
327				Expect(err.Error()).To(ContainSubstring("Expected stream to have digest"))
328
329			})
330		})
331	})
332
333	Context("Given a bad file path", func() {
334		BeforeEach(func() {
335			args = RedigestReleaseArgs{
336				Path:        "/some/release_128.tgz",
337				Destination: FileArg{ExpandedPath: "/some/release_256.tgz"},
338			}
339
340			releaseReader.ReadReturns(nil, errors.Error("disaster"))
341		})
342
343		It("Should return an error", func() {
344			err := command.Run(args)
345			Expect(err).To(HaveOccurred())
346			Expect(err.Error()).To(ContainSubstring("disaster"))
347		})
348	})
349})
350