1package integration_test
2
3import (
4	"fmt"
5	"path/filepath"
6	"regexp"
7	"strings"
8
9	. "github.com/onsi/ginkgo"
10	. "github.com/onsi/gomega"
11
12	. "github.com/cloudfoundry/bosh-cli/cmd"
13
14	boshlog "github.com/cloudfoundry/bosh-utils/logger"
15	boshsys "github.com/cloudfoundry/bosh-utils/system"
16	"github.com/cloudfoundry/bosh-utils/uuid"
17
18	"os"
19
20	boshrel "github.com/cloudfoundry/bosh-cli/release"
21	boshrelman "github.com/cloudfoundry/bosh-cli/release/manifest"
22	boshui "github.com/cloudfoundry/bosh-cli/ui"
23	fakeui "github.com/cloudfoundry/bosh-cli/ui/fakes"
24)
25
26var _ = Describe("create-release command", func() {
27	var (
28		ui         *fakeui.FakeUI
29		fs         boshsys.FileSystem
30		deps       BasicDeps
31		cmdFactory Factory
32	)
33
34	BeforeEach(func() {
35		ui = &fakeui.FakeUI{}
36		logger := boshlog.NewLogger(boshlog.LevelNone)
37		confUI := boshui.NewWrappingConfUI(ui, logger)
38
39		fs = boshsys.NewOsFileSystem(logger)
40		deps = NewBasicDepsWithFS(confUI, fs, logger)
41		cmdFactory = NewFactory(deps)
42	})
43
44	execCmd := func(args []string) {
45		cmd, err := cmdFactory.New(args)
46		Expect(err).ToNot(HaveOccurred())
47
48		err = cmd.Execute()
49		Expect(err).ToNot(HaveOccurred())
50	}
51
52	removeSHA256s := func(contents string) string {
53		matchSHA256s := regexp.MustCompile("sha1: sha256:[a-z0-9]{64}\n")
54		return matchSHA256s.ReplaceAllString(contents, "sha1: replaced\n")
55	}
56
57	expectSha256Checksums := func(filePath string) {
58		contents, err := fs.ReadFileString(filePath)
59		Expect(err).ToNot(HaveOccurred())
60		Expect(contents).To(MatchRegexp("sha1: sha256:.*"))
61	}
62
63	It("can iterate on a basic release", func() {
64		suffix, err := uuid.NewGenerator().Generate()
65		Expect(err).ToNot(HaveOccurred())
66
67		// containing the release in a directory that is a symlink
68		// to ensure we can work inside symlinks (i.e. macOS /tmp)
69		containerDir := filepath.Join("/", "tmp", suffix)
70		symlinkedContainerDir := fmt.Sprintf("%s-symlinked", containerDir)
71		fs.MkdirAll(containerDir, 0755)
72		fs.Symlink(containerDir, symlinkedContainerDir)
73		tmpDir := filepath.Join(symlinkedContainerDir, "release")
74
75		defer func() {
76			fs.RemoveAll(containerDir)
77			fs.RemoveAll(symlinkedContainerDir)
78		}()
79
80		relName := filepath.Base(tmpDir)
81
82		{
83			execCmd([]string{"init-release", "--dir", tmpDir})
84			Expect(fs.FileExists(filepath.Join(tmpDir, "config"))).To(BeTrue())
85			Expect(fs.FileExists(filepath.Join(tmpDir, "jobs"))).To(BeTrue())
86			Expect(fs.FileExists(filepath.Join(tmpDir, "packages"))).To(BeTrue())
87			Expect(fs.FileExists(filepath.Join(tmpDir, "src"))).To(BeTrue())
88		}
89
90		execCmd([]string{"generate-job", "job1", "--dir", tmpDir})
91		execCmd([]string{"generate-package", "pkg1", "--dir", tmpDir})
92		execCmd([]string{"generate-package", "pkg2", "--dir", tmpDir})
93
94		err = fs.WriteFileString(filepath.Join(tmpDir, "LICENSE"), "LICENSE")
95		Expect(err).ToNot(HaveOccurred())
96
97		{ // pkg1 depends on pkg2 for compilation
98			pkg1SpecPath := filepath.Join(tmpDir, "packages", "pkg1", "spec")
99
100			contents, err := fs.ReadFileString(pkg1SpecPath)
101			Expect(err).ToNot(HaveOccurred())
102
103			err = fs.WriteFileString(pkg1SpecPath, strings.Replace(contents, "dependencies: []", "dependencies: [pkg2]", -1))
104			Expect(err).ToNot(HaveOccurred())
105		}
106
107		{ // job1 depends on both packages
108			jobSpecPath := filepath.Join(tmpDir, "jobs", "job1", "spec")
109
110			contents, err := fs.ReadFileString(jobSpecPath)
111			Expect(err).ToNot(HaveOccurred())
112
113			err = fs.WriteFileString(jobSpecPath, strings.Replace(contents, "packages: []", "packages: [pkg1, pkg2]", -1))
114			Expect(err).ToNot(HaveOccurred())
115		}
116
117		{ // Make empty release
118			execCmd([]string{"create-release", "--dir", tmpDir})
119
120			contents, err := fs.ReadFileString(filepath.Join(tmpDir, "dev_releases", relName, relName+"-0+dev.1.yml"))
121			Expect(err).ToNot(HaveOccurred())
122
123			Expect(removeSHA256s(contents)).To(Equal(
124				"name: " + relName + `
125version: 0+dev.1
126commit_hash: non-git
127uncommitted_changes: false
128jobs:
129- name: job1
130  version: f54520d6563c438bf0bc5bb674777db171b78d848a057a3faec0e9b572c3a76c
131  fingerprint: f54520d6563c438bf0bc5bb674777db171b78d848a057a3faec0e9b572c3a76c
132  sha1: replaced
133  packages:
134  - pkg1
135  - pkg2
136packages:
137- name: pkg1
138  version: 08441a1962e8141645edb0f2ddb91330454f2f1a3954d7f27fa256eb5e7b4ed6
139  fingerprint: 08441a1962e8141645edb0f2ddb91330454f2f1a3954d7f27fa256eb5e7b4ed6
140  sha1: replaced
141  dependencies:
142  - pkg2
143- name: pkg2
144  version: 34581dd0d93735e444a32450e3ae3951258c936479b45e08f1fa074740c7e392
145  fingerprint: 34581dd0d93735e444a32450e3ae3951258c936479b45e08f1fa074740c7e392
146  sha1: replaced
147  dependencies: []
148license:
149  version: 42a33a7295936a632c8f54e70f2553975ee38a476d6aae93f3676e68c9db2f86
150  fingerprint: 42a33a7295936a632c8f54e70f2553975ee38a476d6aae93f3676e68c9db2f86
151  sha1: replaced
152`))
153		}
154
155		{ // Add a bit of content
156			err := fs.WriteFileString(filepath.Join(tmpDir, "src", "in-src"), "in-src")
157			Expect(err).ToNot(HaveOccurred())
158
159			randomFile := filepath.Join(tmpDir, "random-file")
160
161			err = fs.WriteFileString(randomFile, "in-blobs")
162			Expect(err).ToNot(HaveOccurred())
163
164			execCmd([]string{"add-blob", randomFile, "in-blobs", "--dir", tmpDir})
165
166			pkg1SpecPath := filepath.Join(tmpDir, "packages", "pkg1", "spec")
167
168			contents, err := fs.ReadFileString(pkg1SpecPath)
169			Expect(err).ToNot(HaveOccurred())
170
171			err = fs.WriteFileString(pkg1SpecPath, strings.Replace(contents, "files: []", "files:\n- in-src\n- in-blobs", -1))
172			Expect(err).ToNot(HaveOccurred())
173		}
174
175		{ // Make release with some contents
176			execCmd([]string{"create-release", "--dir", tmpDir})
177
178			rel1File := filepath.Join(tmpDir, "dev_releases", relName, relName+"-0+dev.1.yml")
179			rel2File := filepath.Join(tmpDir, "dev_releases", relName, relName+"-0+dev.2.yml")
180
181			contents, err := fs.ReadFileString(rel2File)
182			Expect(err).ToNot(HaveOccurred())
183
184			Expect(removeSHA256s(contents)).To(Equal(
185				"name: " + relName + `
186version: 0+dev.2
187commit_hash: non-git
188uncommitted_changes: false
189jobs:
190- name: job1
191  version: f54520d6563c438bf0bc5bb674777db171b78d848a057a3faec0e9b572c3a76c
192  fingerprint: f54520d6563c438bf0bc5bb674777db171b78d848a057a3faec0e9b572c3a76c
193  sha1: replaced
194  packages:
195  - pkg1
196  - pkg2
197packages:
198- name: pkg1
199  version: 00ebebd8dd5a533a91f9de34b0cf708772fca87ada7e37e63bec00ece2e0634c
200  fingerprint: 00ebebd8dd5a533a91f9de34b0cf708772fca87ada7e37e63bec00ece2e0634c
201  sha1: replaced
202  dependencies:
203  - pkg2
204- name: pkg2
205  version: 34581dd0d93735e444a32450e3ae3951258c936479b45e08f1fa074740c7e392
206  fingerprint: 34581dd0d93735e444a32450e3ae3951258c936479b45e08f1fa074740c7e392
207  sha1: replaced
208  dependencies: []
209license:
210  version: 42a33a7295936a632c8f54e70f2553975ee38a476d6aae93f3676e68c9db2f86
211  fingerprint: 42a33a7295936a632c8f54e70f2553975ee38a476d6aae93f3676e68c9db2f86
212  sha1: replaced
213`,
214			))
215
216			man1, err := boshrelman.NewManifestFromPath(rel1File, fs)
217			Expect(err).ToNot(HaveOccurred())
218
219			man2, err := boshrelman.NewManifestFromPath(rel2File, fs)
220			Expect(err).ToNot(HaveOccurred())
221
222			// Explicitly check that pkg1 changed its fingerprint
223			Expect(man1.Packages[0].Name).To(Equal(man2.Packages[0].Name))
224			Expect(man1.Packages[0].Fingerprint).ToNot(Equal(man2.Packages[0].Fingerprint))
225
226			// and pkg2 did not change
227			Expect(man1.Packages[1].Name).To(Equal(man2.Packages[1].Name))
228			Expect(man1.Packages[1].Fingerprint).To(Equal(man2.Packages[1].Fingerprint))
229		}
230
231		{ // check contents of index files when sha2 flag is supplied
232			execCmd([]string{"create-release", "--sha2", "--dir", tmpDir})
233
234			expectSha256Checksums(filepath.Join(tmpDir, "dev_releases", relName, relName+"-0+dev.3.yml"))
235			expectSha256Checksums(filepath.Join(tmpDir, ".dev_builds", "jobs", "job1", "index.yml"))
236			expectSha256Checksums(filepath.Join(tmpDir, ".dev_builds", "packages", "pkg1", "index.yml"))
237			expectSha256Checksums(filepath.Join(tmpDir, ".dev_builds", "license", "index.yml"))
238		}
239
240		{ // Check contents of made release via its tarball
241			tgzFile := filepath.Join(tmpDir, "release-3.tgz")
242
243			execCmd([]string{"create-release", "--dir", tmpDir, "--tarball", tgzFile})
244			relProvider := boshrel.NewProvider(deps.CmdRunner, deps.Compressor, deps.DigestCalculator, deps.FS, deps.Logger)
245			extractingArchiveReader := relProvider.NewExtractingArchiveReader()
246
247			extractingRelease, err := extractingArchiveReader.Read(tgzFile)
248			Expect(err).ToNot(HaveOccurred())
249
250			defer extractingRelease.CleanUp()
251
252			pkg1 := extractingRelease.Packages()[0]
253			Expect(fs.ReadFileString(filepath.Join(pkg1.ExtractedPath(), "in-src"))).To(Equal("in-src"))
254			Expect(fs.ReadFileString(filepath.Join(pkg1.ExtractedPath(), "in-blobs"))).To(Equal("in-blobs"))
255
256			archiveReader := relProvider.NewArchiveReader()
257
258			release, err := archiveReader.Read(tgzFile)
259			Expect(err).ToNot(HaveOccurred())
260
261			defer release.CleanUp()
262
263			job1 := release.Jobs()[0]
264			Expect(job1.PackageNames).To(ConsistOf("pkg1", "pkg2"))
265		}
266
267		{ // Check that tarballs will not overwrite a directory
268			directoryPath := filepath.Join(tmpDir, "tarball-collision-dir")
269			Expect(fs.MkdirAll(directoryPath, os.ModeDir)).To(Succeed())
270			_, err := cmdFactory.New([]string{"create-release", "--dir", tmpDir, "--tarball", directoryPath})
271			Expect(err).To(HaveOccurred())
272			Expect(err.Error()).To(ContainSubstring("Path must not be directory"))
273		}
274
275		{ // removes unknown blobs, keeping known blobs
276			blobPath := filepath.Join(tmpDir, "blobs", "unknown-blob.tgz")
277
278			fs.WriteFileString(blobPath, "i don't belong here")
279
280			execCmd([]string{"create-release", "--dir", tmpDir})
281			Expect(fs.FileExists(blobPath)).To(BeFalse())
282			Expect(fs.FileExists(filepath.Join(tmpDir, "blobs", "in-blobs"))).To(BeTrue())
283		}
284	})
285})
286