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 }