github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/builder.go (about)

     1  package builder
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/BurntSushi/toml"
    18  	"github.com/buildpacks/imgutil"
    19  	"github.com/pkg/errors"
    20  
    21  	"github.com/YousefHaggyHeroku/pack/builder"
    22  	"github.com/YousefHaggyHeroku/pack/internal/archive"
    23  	"github.com/YousefHaggyHeroku/pack/internal/dist"
    24  	"github.com/YousefHaggyHeroku/pack/internal/layer"
    25  	"github.com/YousefHaggyHeroku/pack/internal/stack"
    26  	"github.com/YousefHaggyHeroku/pack/internal/style"
    27  	"github.com/YousefHaggyHeroku/pack/logging"
    28  )
    29  
    30  const (
    31  	packName = "Pack CLI"
    32  
    33  	cnbDir = "/cnb"
    34  
    35  	orderPath          = "/cnb/order.toml"
    36  	stackPath          = "/cnb/stack.toml"
    37  	platformDir        = "/platform"
    38  	lifecycleDir       = "/cnb/lifecycle"
    39  	compatLifecycleDir = "/lifecycle"
    40  	workspaceDir       = "/workspace"
    41  	layersDir          = "/layers"
    42  
    43  	metadataLabel = "io.buildpacks.builder.metadata"
    44  	stackLabel    = "io.buildpacks.stack.id"
    45  
    46  	EnvUID = "CNB_USER_ID"
    47  	EnvGID = "CNB_GROUP_ID"
    48  )
    49  
    50  // Builder represents a pack builder, used to build images
    51  type Builder struct {
    52  	baseImageName        string
    53  	image                imgutil.Image
    54  	layerWriterFactory   archive.TarWriterFactory
    55  	lifecycle            Lifecycle
    56  	lifecycleDescriptor  LifecycleDescriptor
    57  	additionalBuildpacks []dist.Buildpack
    58  	metadata             Metadata
    59  	mixins               []string
    60  	env                  map[string]string
    61  	uid, gid             int
    62  	StackID              string
    63  	replaceOrder         bool
    64  	order                dist.Order
    65  }
    66  
    67  type orderTOML struct {
    68  	Order dist.Order `toml:"order"`
    69  }
    70  
    71  // FromImage constructs a builder from a builder image
    72  func FromImage(img imgutil.Image) (*Builder, error) {
    73  	var metadata Metadata
    74  	if ok, err := dist.GetLabel(img, metadataLabel, &metadata); err != nil {
    75  		return nil, errors.Wrapf(err, "getting label %s", metadataLabel)
    76  	} else if !ok {
    77  		return nil, fmt.Errorf("builder %s missing label %s -- try recreating builder", style.Symbol(img.Name()), style.Symbol(metadataLabel))
    78  	}
    79  	return constructBuilder(img, "", metadata)
    80  }
    81  
    82  // New constructs a new builder from a base image
    83  func New(baseImage imgutil.Image, name string) (*Builder, error) {
    84  	var metadata Metadata
    85  	if _, err := dist.GetLabel(baseImage, metadataLabel, &metadata); err != nil {
    86  		return nil, errors.Wrapf(err, "getting label %s", metadataLabel)
    87  	}
    88  	return constructBuilder(baseImage, name, metadata)
    89  }
    90  
    91  func constructBuilder(img imgutil.Image, newName string, metadata Metadata) (*Builder, error) {
    92  	imageOS, err := img.OS()
    93  	if err != nil {
    94  		return nil, errors.Wrap(err, "getting image OS")
    95  	}
    96  	layerWriterFactory, err := layer.NewWriterFactory(imageOS)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	bldr := &Builder{
   102  		baseImageName:       img.Name(),
   103  		image:               img,
   104  		layerWriterFactory:  layerWriterFactory,
   105  		metadata:            metadata,
   106  		lifecycleDescriptor: constructLifecycleDescriptor(metadata),
   107  		env:                 map[string]string{},
   108  	}
   109  
   110  	if err := addImgLabelsToBuildr(bldr); err != nil {
   111  		return nil, errors.Wrap(err, "adding image labels to builder")
   112  	}
   113  
   114  	if newName != "" && img.Name() != newName {
   115  		img.Rename(newName)
   116  	}
   117  
   118  	return bldr, nil
   119  }
   120  
   121  func constructLifecycleDescriptor(metadata Metadata) LifecycleDescriptor {
   122  	return CompatDescriptor(LifecycleDescriptor{
   123  		Info: LifecycleInfo{
   124  			Version: metadata.Lifecycle.Version,
   125  		},
   126  		API:  metadata.Lifecycle.API,
   127  		APIs: metadata.Lifecycle.APIs,
   128  	})
   129  }
   130  
   131  func addImgLabelsToBuildr(bldr *Builder) error {
   132  	var err error
   133  	bldr.uid, bldr.gid, err = userAndGroupIDs(bldr.image)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	bldr.StackID, err = bldr.image.Label(stackLabel)
   139  	if err != nil {
   140  		return errors.Wrapf(err, "get label %s from image %s", style.Symbol(stackLabel), style.Symbol(bldr.image.Name()))
   141  	}
   142  	if bldr.StackID == "" {
   143  		return fmt.Errorf("image %s missing label %s", style.Symbol(bldr.image.Name()), style.Symbol(stackLabel))
   144  	}
   145  
   146  	if _, err = dist.GetLabel(bldr.image, stack.MixinsLabel, &bldr.mixins); err != nil {
   147  		return errors.Wrapf(err, "getting label %s", stack.MixinsLabel)
   148  	}
   149  
   150  	if _, err = dist.GetLabel(bldr.image, OrderLabel, &bldr.order); err != nil {
   151  		return errors.Wrapf(err, "getting label %s", OrderLabel)
   152  	}
   153  
   154  	return nil
   155  }
   156  
   157  // Getters
   158  
   159  // Description returns the builder description
   160  func (b *Builder) Description() string {
   161  	return b.metadata.Description
   162  }
   163  
   164  // LifecycleDescriptor returns the LifecycleDescriptor
   165  func (b *Builder) LifecycleDescriptor() LifecycleDescriptor {
   166  	return b.lifecycleDescriptor
   167  }
   168  
   169  // Buildpacks returns the buildpack list
   170  func (b *Builder) Buildpacks() []dist.BuildpackInfo {
   171  	return b.metadata.Buildpacks
   172  }
   173  
   174  // CreatedBy returns metadata around the creation of the builder
   175  func (b *Builder) CreatedBy() CreatorMetadata {
   176  	return b.metadata.CreatedBy
   177  }
   178  
   179  // Order returns the order
   180  func (b *Builder) Order() dist.Order {
   181  	return b.order
   182  }
   183  
   184  // Name returns the name of the builder
   185  func (b *Builder) Name() string {
   186  	return b.image.Name()
   187  }
   188  
   189  // Image returns the base image
   190  func (b *Builder) Image() imgutil.Image {
   191  	return b.image
   192  }
   193  
   194  // Stack returns the stack metadata
   195  func (b *Builder) Stack() StackMetadata {
   196  	return b.metadata.Stack
   197  }
   198  
   199  // Mixins returns the mixins of the builder
   200  func (b *Builder) Mixins() []string {
   201  	return b.mixins
   202  }
   203  
   204  // UID returns the UID of the builder
   205  func (b *Builder) UID() int {
   206  	return b.uid
   207  }
   208  
   209  // GID returns the GID of the builder
   210  func (b *Builder) GID() int {
   211  	return b.gid
   212  }
   213  
   214  // Setters
   215  
   216  // AddBuildpack adds a buildpack to the builder
   217  func (b *Builder) AddBuildpack(bp dist.Buildpack) {
   218  	b.additionalBuildpacks = append(b.additionalBuildpacks, bp)
   219  	b.metadata.Buildpacks = append(b.metadata.Buildpacks, bp.Descriptor().Info)
   220  }
   221  
   222  // SetLifecycle sets the lifecycle of the builder
   223  func (b *Builder) SetLifecycle(lifecycle Lifecycle) {
   224  	b.lifecycle = lifecycle
   225  	b.lifecycleDescriptor = lifecycle.Descriptor()
   226  }
   227  
   228  // SetEnv sets an environment variable to a value
   229  func (b *Builder) SetEnv(env map[string]string) {
   230  	b.env = env
   231  }
   232  
   233  // SetOrder sets the order of the builder
   234  func (b *Builder) SetOrder(order dist.Order) {
   235  	b.order = order
   236  	b.replaceOrder = true
   237  }
   238  
   239  // SetDescription sets the description of the builder
   240  func (b *Builder) SetDescription(description string) {
   241  	b.metadata.Description = description
   242  }
   243  
   244  // SetStack sets the stack of the builder
   245  func (b *Builder) SetStack(stackConfig builder.StackConfig) {
   246  	b.metadata.Stack = StackMetadata{
   247  		RunImage: RunImageMetadata{
   248  			Image:   stackConfig.RunImage,
   249  			Mirrors: stackConfig.RunImageMirrors,
   250  		},
   251  	}
   252  }
   253  
   254  // Save saves the builder
   255  func (b *Builder) Save(logger logging.Logger, creatorMetadata CreatorMetadata) error {
   256  	logger.Debugf("Creating builder with the following buildpacks:")
   257  	for _, bpInfo := range b.metadata.Buildpacks {
   258  		logger.Debugf("-> %s", style.Symbol(bpInfo.FullName()))
   259  	}
   260  
   261  	resolvedOrder, err := processOrder(b.metadata.Buildpacks, b.order)
   262  	if err != nil {
   263  		return errors.Wrap(err, "processing order")
   264  	}
   265  
   266  	tmpDir, err := ioutil.TempDir("", "create-builder-scratch")
   267  	if err != nil {
   268  		return err
   269  	}
   270  	defer os.RemoveAll(tmpDir)
   271  
   272  	dirsTar, err := b.defaultDirsLayer(tmpDir)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	if err := b.image.AddLayer(dirsTar); err != nil {
   277  		return errors.Wrap(err, "adding default dirs layer")
   278  	}
   279  
   280  	if b.lifecycle != nil {
   281  		lifecycleDescriptor := b.lifecycle.Descriptor()
   282  		b.metadata.Lifecycle.LifecycleInfo = lifecycleDescriptor.Info
   283  		b.metadata.Lifecycle.API = lifecycleDescriptor.API
   284  		b.metadata.Lifecycle.APIs = lifecycleDescriptor.APIs
   285  		lifecycleTar, err := b.lifecycleLayer(tmpDir)
   286  		if err != nil {
   287  			return err
   288  		}
   289  		if err := b.image.AddLayer(lifecycleTar); err != nil {
   290  			return errors.Wrap(err, "adding lifecycle layer")
   291  		}
   292  	}
   293  
   294  	if err := validateBuildpacks(b.StackID, b.Mixins(), b.LifecycleDescriptor(), b.Buildpacks(), b.additionalBuildpacks); err != nil {
   295  		return errors.Wrap(err, "validating buildpacks")
   296  	}
   297  
   298  	bpLayers := dist.BuildpackLayers{}
   299  	if _, err := dist.GetLabel(b.image, dist.BuildpackLayersLabel, &bpLayers); err != nil {
   300  		return errors.Wrapf(err, "getting label %s", dist.BuildpackLayersLabel)
   301  	}
   302  
   303  	for _, bp := range b.additionalBuildpacks {
   304  		bpLayerTar, err := dist.BuildpackToLayerTar(tmpDir, bp)
   305  		if err != nil {
   306  			return err
   307  		}
   308  
   309  		if err := b.image.AddLayer(bpLayerTar); err != nil {
   310  			return errors.Wrapf(err,
   311  				"adding layer tar for buildpack %s",
   312  				style.Symbol(bp.Descriptor().Info.FullName()),
   313  			)
   314  		}
   315  
   316  		diffID, err := dist.LayerDiffID(bpLayerTar)
   317  		if err != nil {
   318  			return errors.Wrapf(err,
   319  				"getting content hashes for buildpack %s",
   320  				style.Symbol(bp.Descriptor().Info.FullName()),
   321  			)
   322  		}
   323  
   324  		bpInfo := bp.Descriptor().Info
   325  		if _, ok := bpLayers[bpInfo.ID][bpInfo.Version]; ok {
   326  			logger.Debugf(
   327  				"buildpack %s already exists on builder and will be overwritten",
   328  				style.Symbol(bpInfo.FullName()),
   329  			)
   330  		}
   331  
   332  		dist.AddBuildpackToLayersMD(bpLayers, bp.Descriptor(), diffID.String())
   333  	}
   334  
   335  	if err := dist.SetLabel(b.image, dist.BuildpackLayersLabel, bpLayers); err != nil {
   336  		return err
   337  	}
   338  
   339  	if b.replaceOrder {
   340  		orderTar, err := b.orderLayer(resolvedOrder, tmpDir)
   341  		if err != nil {
   342  			return err
   343  		}
   344  		if err := b.image.AddLayer(orderTar); err != nil {
   345  			return errors.Wrap(err, "adding order.tar layer")
   346  		}
   347  
   348  		if err := dist.SetLabel(b.image, OrderLabel, b.order); err != nil {
   349  			return err
   350  		}
   351  	}
   352  
   353  	stackTar, err := b.stackLayer(tmpDir)
   354  	if err != nil {
   355  		return err
   356  	}
   357  	if err := b.image.AddLayer(stackTar); err != nil {
   358  		return errors.Wrap(err, "adding stack.tar layer")
   359  	}
   360  
   361  	envTar, err := b.envLayer(tmpDir, b.env)
   362  	if err != nil {
   363  		return err
   364  	}
   365  	if err := b.image.AddLayer(envTar); err != nil {
   366  		return errors.Wrap(err, "adding env layer")
   367  	}
   368  
   369  	if creatorMetadata.Name == "" {
   370  		creatorMetadata.Name = packName
   371  	}
   372  
   373  	b.metadata.CreatedBy = creatorMetadata
   374  
   375  	if err := dist.SetLabel(b.image, metadataLabel, b.metadata); err != nil {
   376  		return err
   377  	}
   378  
   379  	if err := dist.SetLabel(b.image, stack.MixinsLabel, b.mixins); err != nil {
   380  		return err
   381  	}
   382  
   383  	if err := b.image.SetWorkingDir(layersDir); err != nil {
   384  		return errors.Wrap(err, "failed to set working dir")
   385  	}
   386  
   387  	return b.image.Save()
   388  }
   389  
   390  // Helpers
   391  
   392  func processOrder(buildpacks []dist.BuildpackInfo, order dist.Order) (dist.Order, error) {
   393  	resolvedOrder := dist.Order{}
   394  
   395  	for gi, g := range order {
   396  		resolvedOrder = append(resolvedOrder, dist.OrderEntry{})
   397  
   398  		for _, bpRef := range g.Group {
   399  			var matchingBps []dist.BuildpackInfo
   400  			for _, bp := range buildpacks {
   401  				if bpRef.ID == bp.ID {
   402  					matchingBps = append(matchingBps, bp)
   403  				}
   404  			}
   405  
   406  			if len(matchingBps) == 0 {
   407  				return dist.Order{}, fmt.Errorf("no versions of buildpack %s were found on the builder", style.Symbol(bpRef.ID))
   408  			}
   409  
   410  			if bpRef.Version == "" {
   411  				if len(uniqueVersions(matchingBps)) > 1 {
   412  					return dist.Order{}, fmt.Errorf("unable to resolve version: multiple versions of %s - must specify an explicit version", style.Symbol(bpRef.ID))
   413  				}
   414  
   415  				bpRef.Version = matchingBps[0].Version
   416  			}
   417  
   418  			if !hasBuildpackWithVersion(matchingBps, bpRef.Version) {
   419  				return dist.Order{}, fmt.Errorf("buildpack %s with version %s was not found on the builder", style.Symbol(bpRef.ID), style.Symbol(bpRef.Version))
   420  			}
   421  
   422  			resolvedOrder[gi].Group = append(resolvedOrder[gi].Group, bpRef)
   423  		}
   424  	}
   425  
   426  	return resolvedOrder, nil
   427  }
   428  
   429  func hasBuildpackWithVersion(bps []dist.BuildpackInfo, version string) bool {
   430  	for _, bp := range bps {
   431  		if bp.Version == version {
   432  			return true
   433  		}
   434  	}
   435  	return false
   436  }
   437  
   438  func validateBuildpacks(stackID string, mixins []string, lifecycleDescriptor LifecycleDescriptor, allBuildpacks []dist.BuildpackInfo, bpsToValidate []dist.Buildpack) error {
   439  	bpLookup := map[string]interface{}{}
   440  
   441  	for _, bp := range allBuildpacks {
   442  		bpLookup[bp.FullName()] = nil
   443  	}
   444  
   445  	for _, bp := range bpsToValidate {
   446  		bpd := bp.Descriptor()
   447  
   448  		// TODO: Warn when Buildpack API is deprecated - https://github.com/YousefHaggyHeroku/pack/issues/788
   449  		compatible := false
   450  		for _, version := range append(lifecycleDescriptor.APIs.Buildpack.Supported, lifecycleDescriptor.APIs.Buildpack.Deprecated...) {
   451  			compatible = version.Compare(bpd.API) == 0
   452  			if compatible {
   453  				break
   454  			}
   455  		}
   456  
   457  		if !compatible {
   458  			return fmt.Errorf(
   459  				"buildpack %s (Buildpack API %s) is incompatible with lifecycle %s (Buildpack API(s) %s)",
   460  				style.Symbol(bpd.Info.FullName()),
   461  				bpd.API.String(),
   462  				style.Symbol(lifecycleDescriptor.Info.Version.String()),
   463  				strings.Join(lifecycleDescriptor.APIs.Buildpack.Supported.AsStrings(), ", "),
   464  			)
   465  		}
   466  
   467  		if len(bpd.Stacks) >= 1 { // standard buildpack
   468  			if err := bpd.EnsureStackSupport(stackID, mixins, false); err != nil {
   469  				return err
   470  			}
   471  		} else { // order buildpack
   472  			for _, g := range bpd.Order {
   473  				for _, r := range g.Group {
   474  					if _, ok := bpLookup[r.FullName()]; !ok {
   475  						return fmt.Errorf(
   476  							"buildpack %s not found on the builder",
   477  							style.Symbol(r.FullName()),
   478  						)
   479  					}
   480  				}
   481  			}
   482  		}
   483  	}
   484  
   485  	return nil
   486  }
   487  
   488  func userAndGroupIDs(img imgutil.Image) (int, int, error) {
   489  	sUID, err := img.Env(EnvUID)
   490  	if err != nil {
   491  		return 0, 0, errors.Wrap(err, "reading builder env variables")
   492  	} else if sUID == "" {
   493  		return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(EnvUID))
   494  	}
   495  
   496  	sGID, err := img.Env(EnvGID)
   497  	if err != nil {
   498  		return 0, 0, errors.Wrap(err, "reading builder env variables")
   499  	} else if sGID == "" {
   500  		return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(EnvGID))
   501  	}
   502  
   503  	var uid, gid int
   504  	uid, err = strconv.Atoi(sUID)
   505  	if err != nil {
   506  		return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(EnvUID), style.Symbol(sUID))
   507  	}
   508  
   509  	gid, err = strconv.Atoi(sGID)
   510  	if err != nil {
   511  		return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(EnvGID), style.Symbol(sGID))
   512  	}
   513  
   514  	return uid, gid, nil
   515  }
   516  
   517  func uniqueVersions(buildpacks []dist.BuildpackInfo) []string {
   518  	results := []string{}
   519  	set := map[string]interface{}{}
   520  	for _, bpInfo := range buildpacks {
   521  		_, ok := set[bpInfo.Version]
   522  		if !ok {
   523  			results = append(results, bpInfo.Version)
   524  			set[bpInfo.Version] = true
   525  		}
   526  	}
   527  	return results
   528  }
   529  
   530  func (b *Builder) defaultDirsLayer(dest string) (string, error) {
   531  	fh, err := os.Create(filepath.Join(dest, "dirs.tar"))
   532  	if err != nil {
   533  		return "", err
   534  	}
   535  	defer fh.Close()
   536  
   537  	lw := b.layerWriterFactory.NewWriter(fh)
   538  	defer lw.Close()
   539  
   540  	ts := archive.NormalizedDateTime
   541  
   542  	for _, path := range []string{workspaceDir, layersDir} {
   543  		if err := lw.WriteHeader(b.packOwnedDir(path, ts)); err != nil {
   544  			return "", errors.Wrapf(err, "creating %s dir in layer", style.Symbol(path))
   545  		}
   546  	}
   547  
   548  	// can't use filepath.Join(), to ensure Windows doesn't transform it to Windows join
   549  	for _, path := range []string{cnbDir, dist.BuildpacksDir, platformDir, platformDir + "/env"} {
   550  		if err := lw.WriteHeader(b.rootOwnedDir(path, ts)); err != nil {
   551  			return "", errors.Wrapf(err, "creating %s dir in layer", style.Symbol(path))
   552  		}
   553  	}
   554  
   555  	return fh.Name(), nil
   556  }
   557  
   558  func (b *Builder) packOwnedDir(path string, time time.Time) *tar.Header {
   559  	return &tar.Header{
   560  		Typeflag: tar.TypeDir,
   561  		Name:     path,
   562  		Mode:     0755,
   563  		ModTime:  time,
   564  		Uid:      b.uid,
   565  		Gid:      b.gid,
   566  	}
   567  }
   568  
   569  func (b *Builder) rootOwnedDir(path string, time time.Time) *tar.Header {
   570  	return &tar.Header{
   571  		Typeflag: tar.TypeDir,
   572  		Name:     path,
   573  		Mode:     0755,
   574  		ModTime:  time,
   575  	}
   576  }
   577  
   578  func (b *Builder) lifecycleLayer(dest string) (string, error) {
   579  	fh, err := os.Create(filepath.Join(dest, "lifecycle.tar"))
   580  	if err != nil {
   581  		return "", err
   582  	}
   583  	defer fh.Close()
   584  
   585  	lw := b.layerWriterFactory.NewWriter(fh)
   586  	defer lw.Close()
   587  
   588  	if err := lw.WriteHeader(&tar.Header{
   589  		Typeflag: tar.TypeDir,
   590  		Name:     lifecycleDir,
   591  		Mode:     0755,
   592  		ModTime:  archive.NormalizedDateTime,
   593  	}); err != nil {
   594  		return "", err
   595  	}
   596  
   597  	err = b.embedLifecycleTar(lw)
   598  	if err != nil {
   599  		return "", errors.Wrap(err, "embedding lifecycle tar")
   600  	}
   601  
   602  	if err := lw.WriteHeader(&tar.Header{
   603  		Name:     compatLifecycleDir,
   604  		Linkname: lifecycleDir,
   605  		Typeflag: tar.TypeSymlink,
   606  		Mode:     0644,
   607  		ModTime:  archive.NormalizedDateTime,
   608  	}); err != nil {
   609  		return "", errors.Wrapf(err, "creating %s symlink", style.Symbol(compatLifecycleDir))
   610  	}
   611  
   612  	return fh.Name(), nil
   613  }
   614  
   615  func (b *Builder) embedLifecycleTar(tw archive.TarWriter) error {
   616  	var regex = regexp.MustCompile(`^[^/]+/([^/]+)$`)
   617  
   618  	lr, err := b.lifecycle.Open()
   619  	if err != nil {
   620  		return errors.Wrap(err, "failed to open lifecycle")
   621  	}
   622  	defer lr.Close()
   623  	tr := tar.NewReader(lr)
   624  	for {
   625  		header, err := tr.Next()
   626  		if err == io.EOF {
   627  			break
   628  		}
   629  		if err != nil {
   630  			return errors.Wrap(err, "failed to get next tar entry")
   631  		}
   632  
   633  		pathMatches := regex.FindStringSubmatch(path.Clean(header.Name))
   634  		if pathMatches != nil {
   635  			binaryName := pathMatches[1]
   636  
   637  			header.Name = lifecycleDir + "/" + binaryName
   638  			err = tw.WriteHeader(header)
   639  			if err != nil {
   640  				return errors.Wrapf(err, "failed to write header for '%s'", header.Name)
   641  			}
   642  
   643  			buf, err := ioutil.ReadAll(tr)
   644  			if err != nil {
   645  				return errors.Wrapf(err, "failed to read contents of '%s'", header.Name)
   646  			}
   647  
   648  			_, err = tw.Write(buf)
   649  			if err != nil {
   650  				return errors.Wrapf(err, "failed to write contents to '%s'", header.Name)
   651  			}
   652  		}
   653  	}
   654  
   655  	return nil
   656  }
   657  
   658  func (b *Builder) orderLayer(order dist.Order, dest string) (string, error) {
   659  	contents, err := orderFileContents(order)
   660  	if err != nil {
   661  		return "", err
   662  	}
   663  
   664  	layerTar := filepath.Join(dest, "order.tar")
   665  	err = layer.CreateSingleFileTar(layerTar, orderPath, contents, b.layerWriterFactory)
   666  	if err != nil {
   667  		return "", errors.Wrapf(err, "failed to create order.toml layer tar")
   668  	}
   669  
   670  	return layerTar, nil
   671  }
   672  
   673  func orderFileContents(order dist.Order) (string, error) {
   674  	buf := &bytes.Buffer{}
   675  
   676  	tomlData := orderTOML{Order: order}
   677  	if err := toml.NewEncoder(buf).Encode(tomlData); err != nil {
   678  		return "", errors.Wrapf(err, "failed to marshal order.toml")
   679  	}
   680  	return buf.String(), nil
   681  }
   682  
   683  func (b *Builder) stackLayer(dest string) (string, error) {
   684  	buf := &bytes.Buffer{}
   685  	err := toml.NewEncoder(buf).Encode(b.metadata.Stack)
   686  	if err != nil {
   687  		return "", errors.Wrapf(err, "failed to marshal stack.toml")
   688  	}
   689  
   690  	layerTar := filepath.Join(dest, "stack.tar")
   691  	err = layer.CreateSingleFileTar(layerTar, stackPath, buf.String(), b.layerWriterFactory)
   692  	if err != nil {
   693  		return "", errors.Wrapf(err, "failed to create stack.toml layer tar")
   694  	}
   695  
   696  	return layerTar, nil
   697  }
   698  
   699  func (b *Builder) envLayer(dest string, env map[string]string) (string, error) {
   700  	fh, err := os.Create(filepath.Join(dest, "env.tar"))
   701  	if err != nil {
   702  		return "", err
   703  	}
   704  	defer fh.Close()
   705  
   706  	lw := b.layerWriterFactory.NewWriter(fh)
   707  	defer lw.Close()
   708  
   709  	for k, v := range env {
   710  		if err := lw.WriteHeader(&tar.Header{
   711  			Name:    path.Join(platformDir, "env", k),
   712  			Size:    int64(len(v)),
   713  			Mode:    0644,
   714  			ModTime: archive.NormalizedDateTime,
   715  		}); err != nil {
   716  			return "", err
   717  		}
   718  		if _, err := lw.Write([]byte(v)); err != nil {
   719  			return "", err
   720  		}
   721  	}
   722  
   723  	return fh.Name(), nil
   724  }