github.com/buildpack/pack@v0.5.0/dist/layers.go (about)

     1  package dist
     2  
     3  import (
     4  	"archive/tar"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  
    12  	v1 "github.com/google/go-containerregistry/pkg/v1"
    13  	"github.com/google/go-containerregistry/pkg/v1/tarball"
    14  	"github.com/pkg/errors"
    15  
    16  	"github.com/buildpack/pack/internal/archive"
    17  )
    18  
    19  const BuildpacksDir = "/cnb/buildpacks"
    20  
    21  // Output:
    22  //
    23  // layer tar = {ID}.{V}.tar
    24  //
    25  // inside the layer = /cnbs/buildpacks/{ID}/{V}/*
    26  func BuildpackLayer(dest string, uid, gid int, bp Buildpack) (string, error) {
    27  	bpd := bp.Descriptor()
    28  	layerTar := filepath.Join(dest, fmt.Sprintf("%s.%s.tar", bpd.EscapedID(), bpd.Info.Version))
    29  
    30  	fh, err := os.Create(layerTar)
    31  	if err != nil {
    32  		return "", fmt.Errorf("create file for tar: %s", err)
    33  	}
    34  	defer fh.Close()
    35  
    36  	tw := tar.NewWriter(fh)
    37  	defer tw.Close()
    38  
    39  	ts := archive.NormalizedDateTime
    40  
    41  	if err := tw.WriteHeader(&tar.Header{
    42  		Typeflag: tar.TypeDir,
    43  		Name:     path.Join(BuildpacksDir, bpd.EscapedID()),
    44  		Mode:     0755,
    45  		ModTime:  ts,
    46  	}); err != nil {
    47  		return "", err
    48  	}
    49  
    50  	baseTarDir := path.Join(BuildpacksDir, bpd.EscapedID(), bpd.Info.Version)
    51  	if err := tw.WriteHeader(&tar.Header{
    52  		Typeflag: tar.TypeDir,
    53  		Name:     baseTarDir,
    54  		Mode:     0755,
    55  		ModTime:  ts,
    56  	}); err != nil {
    57  		return "", err
    58  	}
    59  
    60  	if err := embedBuildpackTar(tw, uid, gid, bp, baseTarDir); err != nil {
    61  		return "", errors.Wrapf(err, "creating layer tar for buildpack '%s:%s'", bpd.Info.ID, bpd.Info.Version)
    62  	}
    63  
    64  	return layerTar, nil
    65  }
    66  
    67  func embedBuildpackTar(tw *tar.Writer, uid, gid int, bp Buildpack, baseTarDir string) error {
    68  	var (
    69  		err error
    70  	)
    71  
    72  	rc, err := bp.Open()
    73  	if err != nil {
    74  		return errors.Wrap(err, "read buildpack blob")
    75  	}
    76  	defer rc.Close()
    77  
    78  	tr := tar.NewReader(rc)
    79  	for {
    80  		header, err := tr.Next()
    81  		if err == io.EOF {
    82  			break
    83  		}
    84  		if err != nil {
    85  			return errors.Wrap(err, "failed to get next tar entry")
    86  		}
    87  
    88  		header.Name = path.Clean(header.Name)
    89  		if header.Name == "." || header.Name == "/" {
    90  			continue
    91  		}
    92  
    93  		header.Name = path.Clean(path.Join(baseTarDir, header.Name))
    94  		header.Uid = uid
    95  		header.Gid = gid
    96  		err = tw.WriteHeader(header)
    97  		if err != nil {
    98  			return errors.Wrapf(err, "failed to write header for '%s'", header.Name)
    99  		}
   100  
   101  		buf, err := ioutil.ReadAll(tr)
   102  		if err != nil {
   103  			return errors.Wrapf(err, "failed to read contents of '%s'", header.Name)
   104  		}
   105  
   106  		_, err = tw.Write(buf)
   107  		if err != nil {
   108  			return errors.Wrapf(err, "failed to write contents to '%s'", header.Name)
   109  		}
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func LayerDiffID(layerTarPath string) (v1.Hash, error) {
   116  	fh, err := os.Open(layerTarPath)
   117  	if err != nil {
   118  		return v1.Hash{}, errors.Wrap(err, "opening tar file")
   119  	}
   120  	defer fh.Close()
   121  
   122  	layer, err := tarball.LayerFromFile(layerTarPath)
   123  	if err != nil {
   124  		return v1.Hash{}, errors.Wrap(err, "reading layer tar")
   125  	}
   126  
   127  	hash, err := layer.DiffID()
   128  	if err != nil {
   129  		return v1.Hash{}, errors.Wrap(err, "generating diff id")
   130  	}
   131  
   132  	return hash, nil
   133  }