github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/buildpack/package.go (about) 1 package buildpack 2 3 import ( 4 "io" 5 "strings" 6 "sync" 7 8 "github.com/pkg/errors" 9 10 "github.com/buildpacks/pack/internal/style" 11 "github.com/buildpacks/pack/pkg/dist" 12 ) 13 14 type Package interface { 15 Label(name string) (value string, err error) 16 GetLayer(diffID string) (io.ReadCloser, error) 17 } 18 19 type syncPkg struct { 20 mu sync.Mutex 21 pkg Package 22 } 23 24 func (s *syncPkg) Label(name string) (value string, err error) { 25 s.mu.Lock() 26 defer s.mu.Unlock() 27 return s.pkg.Label(name) 28 } 29 30 func (s *syncPkg) GetLayer(diffID string) (io.ReadCloser, error) { 31 s.mu.Lock() 32 defer s.mu.Unlock() 33 return s.pkg.GetLayer(diffID) 34 } 35 36 // extractBuildpacks when provided a flattened buildpack package containing N buildpacks, 37 // will return N modules: 1 module with a single tar containing ALL N buildpacks, and N-1 modules with empty tar files. 38 func extractBuildpacks(pkg Package) (mainBP BuildModule, depBPs []BuildModule, err error) { 39 pkg = &syncPkg{pkg: pkg} 40 md := &Metadata{} 41 if found, err := dist.GetLabel(pkg, MetadataLabel, md); err != nil { 42 return nil, nil, err 43 } else if !found { 44 return nil, nil, errors.Errorf( 45 "could not find label %s", 46 style.Symbol(MetadataLabel), 47 ) 48 } 49 50 pkgLayers := dist.ModuleLayers{} 51 ok, err := dist.GetLabel(pkg, dist.BuildpackLayersLabel, &pkgLayers) 52 if err != nil { 53 return nil, nil, err 54 } 55 56 if !ok { 57 return nil, nil, errors.Errorf( 58 "could not find label %s", 59 style.Symbol(dist.BuildpackLayersLabel), 60 ) 61 } 62 63 // Example `dist.ModuleLayers{}`: 64 // 65 //{ 66 // "samples/hello-moon": { 67 // "0.0.1": { 68 // "api": "0.2", 69 // "stacks": [ 70 // { 71 // "id": "*" 72 // } 73 // ], 74 // "layerDiffID": "sha256:37ab46923c181aa5fb27c9a23479a38aec2679237f35a0ea4115e5ae81a17bba", 75 // "homepage": "https://github.com/buildpacks/samples/tree/main/buildpacks/hello-moon", 76 // "name": "Hello Moon Buildpack" 77 // } 78 // } 79 //} 80 81 // If the package is a flattened buildpack, the first buildpack in the package returns all the tar content, 82 // and subsequent buildpacks return an empty tar. 83 var processedDiffIDs = make(map[string]bool) 84 for bpID, v := range pkgLayers { 85 for bpVersion, bpInfo := range v { 86 desc := dist.BuildpackDescriptor{ 87 WithAPI: bpInfo.API, 88 WithInfo: dist.ModuleInfo{ 89 ID: bpID, 90 Version: bpVersion, 91 Homepage: bpInfo.Homepage, 92 Name: bpInfo.Name, 93 }, 94 WithStacks: bpInfo.Stacks, 95 WithTargets: bpInfo.Targets, 96 WithOrder: bpInfo.Order, 97 } 98 99 diffID := bpInfo.LayerDiffID // Allow use in closure 100 101 var openerFunc func() (io.ReadCloser, error) 102 if _, ok := processedDiffIDs[diffID]; ok { 103 // We already processed a layer with this diffID, so the module must be flattened; 104 // return an empty reader to avoid multiple tars with the same content. 105 openerFunc = func() (io.ReadCloser, error) { 106 return io.NopCloser(strings.NewReader("")), nil 107 } 108 } else { 109 openerFunc = func() (io.ReadCloser, error) { 110 rc, err := pkg.GetLayer(diffID) 111 if err != nil { 112 return nil, errors.Wrapf(err, 113 "extracting buildpack %s layer (diffID %s)", 114 style.Symbol(desc.Info().FullName()), 115 style.Symbol(diffID), 116 ) 117 } 118 return rc, nil 119 } 120 processedDiffIDs[diffID] = true 121 } 122 123 b := &openerBlob{ 124 opener: openerFunc, 125 } 126 127 if desc.Info().Match(md.ModuleInfo) { // Current module is the order buildpack of the package 128 mainBP = FromBlob(&desc, b) 129 } else { 130 depBPs = append(depBPs, FromBlob(&desc, b)) 131 } 132 } 133 } 134 135 return mainBP, depBPs, nil 136 } 137 138 func extractExtensions(pkg Package) (mainExt BuildModule, err error) { 139 pkg = &syncPkg{pkg: pkg} 140 md := &Metadata{} 141 if found, err := dist.GetLabel(pkg, MetadataLabel, md); err != nil { 142 return nil, err 143 } else if !found { 144 return nil, errors.Errorf( 145 "could not find label %s", 146 style.Symbol(MetadataLabel), 147 ) 148 } 149 150 pkgLayers := dist.ModuleLayers{} 151 ok, err := dist.GetLabel(pkg, dist.ExtensionLayersLabel, &pkgLayers) 152 if err != nil { 153 return nil, err 154 } 155 156 if !ok { 157 return nil, errors.Errorf( 158 "could not find label %s", 159 style.Symbol(dist.ExtensionLayersLabel), 160 ) 161 } 162 for extID, v := range pkgLayers { 163 for extVersion, extInfo := range v { 164 desc := dist.ExtensionDescriptor{ 165 WithAPI: extInfo.API, 166 WithInfo: dist.ModuleInfo{ 167 ID: extID, 168 Version: extVersion, 169 Homepage: extInfo.Homepage, 170 Name: extInfo.Name, 171 }, 172 } 173 174 diffID := extInfo.LayerDiffID // Allow use in closure 175 b := &openerBlob{ 176 opener: func() (io.ReadCloser, error) { 177 rc, err := pkg.GetLayer(diffID) 178 if err != nil { 179 return nil, errors.Wrapf(err, 180 "extracting extension %s layer (diffID %s)", 181 style.Symbol(desc.Info().FullName()), 182 style.Symbol(diffID), 183 ) 184 } 185 return rc, nil 186 }, 187 } 188 189 mainExt = FromBlob(&desc, b) 190 } 191 } 192 return mainExt, nil 193 } 194 195 type openerBlob struct { 196 opener func() (io.ReadCloser, error) 197 } 198 199 func (b *openerBlob) Open() (io.ReadCloser, error) { 200 return b.opener() 201 }