github.com/YousefHaggyHeroku/pack@v1.5.5/internal/dist/buildpack_test.go (about) 1 package dist_test 2 3 import ( 4 "errors" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "testing" 10 "time" 11 12 "github.com/heroku/color" 13 "github.com/sclevine/spec" 14 "github.com/sclevine/spec/report" 15 16 "github.com/YousefHaggyHeroku/pack/internal/archive" 17 "github.com/YousefHaggyHeroku/pack/internal/dist" 18 h "github.com/YousefHaggyHeroku/pack/testhelpers" 19 ) 20 21 func TestBuildpack(t *testing.T) { 22 color.Disable(true) 23 defer color.Disable(false) 24 spec.Run(t, "buildpack", testBuildpack, spec.Parallel(), spec.Report(report.Terminal{})) 25 } 26 27 func testBuildpack(t *testing.T, when spec.G, it spec.S) { 28 var writeBlobToFile = func(bp dist.Buildpack) string { 29 t.Helper() 30 31 bpReader, err := bp.Open() 32 h.AssertNil(t, err) 33 34 tmpDir, err := ioutil.TempDir("", "") 35 h.AssertNil(t, err) 36 37 p := filepath.Join(tmpDir, "bp.tar") 38 bpWriter, err := os.Create(p) 39 h.AssertNil(t, err) 40 41 _, err = io.Copy(bpWriter, bpReader) 42 h.AssertNil(t, err) 43 44 err = bpReader.Close() 45 h.AssertNil(t, err) 46 47 return p 48 } 49 50 when("#BuildpackFromRootBlob", func() { 51 it("parses the descriptor file", func() { 52 bp, err := dist.BuildpackFromRootBlob( 53 &readerBlob{ 54 openFn: func() io.ReadCloser { 55 tarBuilder := archive.TarBuilder{} 56 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 57 api = "0.3" 58 59 [buildpack] 60 id = "bp.one" 61 version = "1.2.3" 62 homepage = "http://geocities.com/cool-bp" 63 64 [[stacks]] 65 id = "some.stack.id" 66 `)) 67 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 68 }, 69 }, 70 archive.DefaultTarWriterFactory(), 71 ) 72 h.AssertNil(t, err) 73 74 h.AssertEq(t, bp.Descriptor().API.String(), "0.3") 75 h.AssertEq(t, bp.Descriptor().Info.ID, "bp.one") 76 h.AssertEq(t, bp.Descriptor().Info.Version, "1.2.3") 77 h.AssertEq(t, bp.Descriptor().Info.Homepage, "http://geocities.com/cool-bp") 78 h.AssertEq(t, bp.Descriptor().Stacks[0].ID, "some.stack.id") 79 }) 80 81 it("translates blob to distribution format", func() { 82 bp, err := dist.BuildpackFromRootBlob( 83 &readerBlob{ 84 openFn: func() io.ReadCloser { 85 tarBuilder := archive.TarBuilder{} 86 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 87 api = "0.3" 88 89 [buildpack] 90 id = "bp.one" 91 version = "1.2.3" 92 93 [[stacks]] 94 id = "some.stack.id" 95 `)) 96 97 tarBuilder.AddDir("bin", 0700, time.Now()) 98 tarBuilder.AddFile("bin/detect", 0700, time.Now(), []byte("detect-contents")) 99 tarBuilder.AddFile("bin/build", 0700, time.Now(), []byte("build-contents")) 100 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 101 }, 102 }, 103 archive.DefaultTarWriterFactory(), 104 ) 105 h.AssertNil(t, err) 106 107 tarPath := writeBlobToFile(bp) 108 defer os.Remove(tarPath) 109 110 h.AssertOnTarEntry(t, tarPath, 111 "/cnb/buildpacks/bp.one", 112 h.IsDirectory(), 113 h.HasFileMode(0755), 114 h.HasModTime(archive.NormalizedDateTime), 115 ) 116 117 h.AssertOnTarEntry(t, tarPath, 118 "/cnb/buildpacks/bp.one/1.2.3", 119 h.IsDirectory(), 120 h.HasFileMode(0755), 121 h.HasModTime(archive.NormalizedDateTime), 122 ) 123 124 h.AssertOnTarEntry(t, tarPath, 125 "/cnb/buildpacks/bp.one/1.2.3/bin", 126 h.IsDirectory(), 127 h.HasFileMode(0755), 128 h.HasModTime(archive.NormalizedDateTime), 129 ) 130 131 h.AssertOnTarEntry(t, tarPath, 132 "/cnb/buildpacks/bp.one/1.2.3/bin/detect", 133 h.HasFileMode(0755), 134 h.HasModTime(archive.NormalizedDateTime), 135 h.ContentEquals("detect-contents"), 136 ) 137 138 h.AssertOnTarEntry(t, tarPath, 139 "/cnb/buildpacks/bp.one/1.2.3/bin/build", 140 h.HasFileMode(0755), 141 h.HasModTime(archive.NormalizedDateTime), 142 h.ContentEquals("build-contents"), 143 ) 144 }) 145 146 it("surfaces errors encountered while reading blob", func() { 147 realBlob := &readerBlob{ 148 openFn: func() io.ReadCloser { 149 tarBuilder := archive.TarBuilder{} 150 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 151 api = "0.3" 152 153 [buildpack] 154 id = "bp.one" 155 version = "1.2.3" 156 157 [[stacks]] 158 id = "some.stack.id" 159 `)) 160 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 161 }, 162 } 163 164 bp, err := dist.BuildpackFromRootBlob( 165 &errorBlob{ 166 realBlob: realBlob, 167 }, 168 archive.DefaultTarWriterFactory(), 169 ) 170 h.AssertNil(t, err) 171 172 bpReader, err := bp.Open() 173 h.AssertNil(t, err) 174 175 _, err = io.Copy(ioutil.Discard, bpReader) 176 h.AssertError(t, err, "error from errBlob") 177 }) 178 179 when("calculating permissions", func() { 180 bpTOMLData := ` 181 api = "0.3" 182 183 [buildpack] 184 id = "bp.one" 185 version = "1.2.3" 186 187 [[stacks]] 188 id = "some.stack.id" 189 ` 190 191 when("no exec bits set", func() { 192 it("sets to 0755 if directory", func() { 193 bp, err := dist.BuildpackFromRootBlob( 194 &readerBlob{ 195 openFn: func() io.ReadCloser { 196 tarBuilder := archive.TarBuilder{} 197 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(bpTOMLData)) 198 tarBuilder.AddDir("some-dir", 0600, time.Now()) 199 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 200 }, 201 }, 202 archive.DefaultTarWriterFactory(), 203 ) 204 h.AssertNil(t, err) 205 206 tarPath := writeBlobToFile(bp) 207 defer os.Remove(tarPath) 208 209 h.AssertOnTarEntry(t, tarPath, 210 "/cnb/buildpacks/bp.one/1.2.3/some-dir", 211 h.HasFileMode(0755), 212 ) 213 }) 214 }) 215 216 when("no exec bits set", func() { 217 it("sets to 0755 if 'bin/detect' or 'bin/build'", func() { 218 bp, err := dist.BuildpackFromRootBlob( 219 &readerBlob{ 220 openFn: func() io.ReadCloser { 221 tarBuilder := archive.TarBuilder{} 222 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(bpTOMLData)) 223 tarBuilder.AddFile("bin/detect", 0600, time.Now(), []byte("detect-contents")) 224 tarBuilder.AddFile("bin/build", 0600, time.Now(), []byte("build-contents")) 225 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 226 }, 227 }, 228 archive.DefaultTarWriterFactory(), 229 ) 230 h.AssertNil(t, err) 231 232 tarPath := writeBlobToFile(bp) 233 defer os.Remove(tarPath) 234 235 h.AssertOnTarEntry(t, tarPath, 236 "/cnb/buildpacks/bp.one/1.2.3/bin/detect", 237 h.HasFileMode(0755), 238 ) 239 240 h.AssertOnTarEntry(t, tarPath, 241 "/cnb/buildpacks/bp.one/1.2.3/bin/build", 242 h.HasFileMode(0755), 243 ) 244 }) 245 }) 246 247 when("not directory, 'bin/detect', or 'bin/build'", func() { 248 it("sets to 0755 if ANY exec bit is set", func() { 249 bp, err := dist.BuildpackFromRootBlob( 250 &readerBlob{ 251 openFn: func() io.ReadCloser { 252 tarBuilder := archive.TarBuilder{} 253 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(bpTOMLData)) 254 tarBuilder.AddFile("some-file", 0700, time.Now(), []byte("some-data")) 255 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 256 }, 257 }, 258 archive.DefaultTarWriterFactory(), 259 ) 260 h.AssertNil(t, err) 261 262 tarPath := writeBlobToFile(bp) 263 defer os.Remove(tarPath) 264 265 h.AssertOnTarEntry(t, tarPath, 266 "/cnb/buildpacks/bp.one/1.2.3/some-file", 267 h.HasFileMode(0755), 268 ) 269 }) 270 }) 271 272 when("not directory, 'bin/detect', or 'bin/build'", func() { 273 it("sets to 0644 if NO exec bits set", func() { 274 bp, err := dist.BuildpackFromRootBlob( 275 &readerBlob{ 276 openFn: func() io.ReadCloser { 277 tarBuilder := archive.TarBuilder{} 278 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(bpTOMLData)) 279 tarBuilder.AddFile("some-file", 0600, time.Now(), []byte("some-data")) 280 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 281 }, 282 }, 283 archive.DefaultTarWriterFactory(), 284 ) 285 h.AssertNil(t, err) 286 287 tarPath := writeBlobToFile(bp) 288 defer os.Remove(tarPath) 289 290 h.AssertOnTarEntry(t, tarPath, 291 "/cnb/buildpacks/bp.one/1.2.3/some-file", 292 h.HasFileMode(0644), 293 ) 294 }) 295 }) 296 }) 297 298 when("there is no descriptor file", func() { 299 it("returns error", func() { 300 _, err := dist.BuildpackFromRootBlob( 301 &readerBlob{ 302 openFn: func() io.ReadCloser { 303 tarBuilder := archive.TarBuilder{} 304 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 305 }, 306 }, 307 archive.DefaultTarWriterFactory(), 308 ) 309 h.AssertError(t, err, "could not find entry path 'buildpack.toml'") 310 }) 311 }) 312 313 when("there is no api field", func() { 314 it("assumes an api version", func() { 315 bp, err := dist.BuildpackFromRootBlob( 316 &readerBlob{ 317 openFn: func() io.ReadCloser { 318 tarBuilder := archive.TarBuilder{} 319 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 320 [buildpack] 321 id = "bp.one" 322 version = "1.2.3" 323 324 [[stacks]] 325 id = "some.stack.id"`)) 326 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 327 }, 328 }, 329 archive.DefaultTarWriterFactory(), 330 ) 331 h.AssertNil(t, err) 332 h.AssertEq(t, bp.Descriptor().API.String(), "0.1") 333 }) 334 }) 335 336 when("there is no id", func() { 337 it("returns error", func() { 338 _, err := dist.BuildpackFromRootBlob( 339 &readerBlob{ 340 openFn: func() io.ReadCloser { 341 tarBuilder := archive.TarBuilder{} 342 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 343 [buildpack] 344 id = "" 345 version = "1.2.3" 346 347 [[stacks]] 348 id = "some.stack.id"`)) 349 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 350 }, 351 }, 352 archive.DefaultTarWriterFactory(), 353 ) 354 h.AssertError(t, err, "'buildpack.id' is required") 355 }) 356 }) 357 358 when("there is no version", func() { 359 it("returns error", func() { 360 _, err := dist.BuildpackFromRootBlob( 361 &readerBlob{ 362 openFn: func() io.ReadCloser { 363 tarBuilder := archive.TarBuilder{} 364 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 365 [buildpack] 366 id = "bp.one" 367 version = "" 368 369 [[stacks]] 370 id = "some.stack.id"`)) 371 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 372 }, 373 }, 374 archive.DefaultTarWriterFactory(), 375 ) 376 h.AssertError(t, err, "'buildpack.version' is required") 377 }) 378 }) 379 380 when("both stacks and order are present", func() { 381 it("returns error", func() { 382 _, err := dist.BuildpackFromRootBlob( 383 &readerBlob{ 384 openFn: func() io.ReadCloser { 385 tarBuilder := archive.TarBuilder{} 386 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 387 [buildpack] 388 id = "bp.one" 389 version = "1.2.3" 390 391 [[stacks]] 392 id = "some.stack.id" 393 394 [[order]] 395 [[order.group]] 396 id = "bp.nested" 397 version = "bp.nested.version" 398 `)) 399 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 400 }, 401 }, 402 archive.DefaultTarWriterFactory(), 403 ) 404 h.AssertError(t, err, "cannot have both 'stacks' and an 'order' defined") 405 }) 406 }) 407 408 when("missing stacks and order", func() { 409 it("returns error", func() { 410 _, err := dist.BuildpackFromRootBlob( 411 &readerBlob{ 412 openFn: func() io.ReadCloser { 413 tarBuilder := archive.TarBuilder{} 414 tarBuilder.AddFile("buildpack.toml", 0700, time.Now(), []byte(` 415 [buildpack] 416 id = "bp.one" 417 version = "1.2.3" 418 `)) 419 return tarBuilder.Reader(archive.DefaultTarWriterFactory()) 420 }, 421 }, 422 archive.DefaultTarWriterFactory(), 423 ) 424 h.AssertError(t, err, "must have either 'stacks' or an 'order' defined") 425 }) 426 }) 427 }) 428 429 when("#Match", func() { 430 it("compares, using only the id and version", func() { 431 other := dist.BuildpackInfo{ 432 ID: "same", 433 Version: "1.2.3", 434 Homepage: "something else", 435 } 436 437 self := dist.BuildpackInfo{ 438 ID: "same", 439 Version: "1.2.3", 440 } 441 442 match := self.Match(other) 443 444 h.AssertEq(t, match, true) 445 446 self.ID = "different" 447 match = self.Match(other) 448 449 h.AssertEq(t, match, false) 450 }) 451 }) 452 } 453 454 type errorBlob struct { 455 notFirst bool 456 realBlob dist.Blob 457 } 458 459 func (e *errorBlob) Open() (io.ReadCloser, error) { 460 if !e.notFirst { 461 e.notFirst = true 462 return e.realBlob.Open() 463 } 464 return nil, errors.New("error from errBlob") 465 } 466 467 type readerBlob struct { 468 openFn func() io.ReadCloser 469 } 470 471 func (r *readerBlob) Open() (io.ReadCloser, error) { 472 return r.openFn(), nil 473 }