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  }