github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/client/inspect_buildpack.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 8 v1 "github.com/opencontainers/image-spec/specs-go/v1" 9 10 "github.com/buildpacks/pack/internal/style" 11 "github.com/buildpacks/pack/pkg/buildpack" 12 "github.com/buildpacks/pack/pkg/dist" 13 "github.com/buildpacks/pack/pkg/image" 14 ) 15 16 type BuildpackInfo struct { 17 BuildpackMetadata buildpack.Metadata 18 Buildpacks []dist.ModuleInfo 19 Order dist.Order 20 BuildpackLayers dist.ModuleLayers 21 Location buildpack.LocatorType 22 } 23 24 type InspectBuildpackOptions struct { 25 BuildpackName string 26 Daemon bool 27 Registry string 28 } 29 30 type ImgWrapper struct { 31 v1.ImageConfig 32 } 33 34 func (iw ImgWrapper) Label(name string) (string, error) { 35 return iw.Labels[name], nil 36 } 37 38 func (c *Client) InspectBuildpack(opts InspectBuildpackOptions) (*BuildpackInfo, error) { 39 locatorType, err := buildpack.GetLocatorType(opts.BuildpackName, "", []dist.ModuleInfo{}) 40 if err != nil { 41 return nil, err 42 } 43 var layersMd dist.ModuleLayers 44 var buildpackMd buildpack.Metadata 45 46 switch locatorType { 47 case buildpack.RegistryLocator: 48 buildpackMd, layersMd, err = metadataFromRegistry(c, opts.BuildpackName, opts.Registry) 49 case buildpack.PackageLocator: 50 buildpackMd, layersMd, err = metadataFromImage(c, opts.BuildpackName, opts.Daemon) 51 case buildpack.URILocator: 52 buildpackMd, layersMd, err = metadataFromArchive(c.downloader, opts.BuildpackName) 53 default: 54 return nil, fmt.Errorf("unable to handle locator %q: for buildpack %q", locatorType, opts.BuildpackName) 55 } 56 if err != nil { 57 return nil, err 58 } 59 60 return &BuildpackInfo{ 61 BuildpackMetadata: buildpackMd, 62 BuildpackLayers: layersMd, 63 Order: extractOrder(buildpackMd), 64 Buildpacks: extractBuildpacks(layersMd), 65 Location: locatorType, 66 }, nil 67 } 68 69 func metadataFromRegistry(client *Client, name, registry string) (buildpackMd buildpack.Metadata, layersMd dist.ModuleLayers, err error) { 70 registryCache, err := getRegistry(client.logger, registry) 71 if err != nil { 72 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("invalid registry %s: %q", registry, err) 73 } 74 75 registryBp, err := registryCache.LocateBuildpack(name) 76 if err != nil { 77 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("unable to find %s in registry: %q", style.Symbol(name), err) 78 } 79 buildpackMd, layersMd, err = metadataFromImage(client, registryBp.Address, false) 80 if err != nil { 81 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("error pulling registry specified image: %s", err) 82 } 83 return buildpackMd, layersMd, nil 84 } 85 86 func metadataFromArchive(downloader BlobDownloader, path string) (buildpackMd buildpack.Metadata, layersMd dist.ModuleLayers, err error) { 87 imgBlob, err := downloader.Download(context.Background(), path) 88 if err != nil { 89 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("unable to download archive: %q", err) 90 } 91 92 config, err := buildpack.ConfigFromOCILayoutBlob(imgBlob) 93 if err != nil { 94 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("unable to fetch config from buildpack blob: %q", err) 95 } 96 wrapper := ImgWrapper{config} 97 98 if _, err := dist.GetLabel(wrapper, dist.BuildpackLayersLabel, &layersMd); err != nil { 99 return buildpack.Metadata{}, dist.ModuleLayers{}, err 100 } 101 102 if _, err := dist.GetLabel(wrapper, buildpack.MetadataLabel, &buildpackMd); err != nil { 103 return buildpack.Metadata{}, dist.ModuleLayers{}, err 104 } 105 return buildpackMd, layersMd, nil 106 } 107 108 func metadataFromImage(client *Client, name string, daemon bool) (buildpackMd buildpack.Metadata, layersMd dist.ModuleLayers, err error) { 109 imageName := buildpack.ParsePackageLocator(name) 110 img, err := client.imageFetcher.Fetch(context.Background(), imageName, image.FetchOptions{Daemon: daemon, PullPolicy: image.PullNever}) 111 if err != nil { 112 return buildpack.Metadata{}, dist.ModuleLayers{}, err 113 } 114 if _, err := dist.GetLabel(img, dist.BuildpackLayersLabel, &layersMd); err != nil { 115 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("unable to get image label %s: %q", dist.BuildpackLayersLabel, err) 116 } 117 118 if _, err := dist.GetLabel(img, buildpack.MetadataLabel, &buildpackMd); err != nil { 119 return buildpack.Metadata{}, dist.ModuleLayers{}, fmt.Errorf("unable to get image label %s: %q", buildpack.MetadataLabel, err) 120 } 121 return buildpackMd, layersMd, nil 122 } 123 124 func extractOrder(buildpackMd buildpack.Metadata) dist.Order { 125 return dist.Order{ 126 { 127 Group: []dist.ModuleRef{ 128 { 129 ModuleInfo: buildpackMd.ModuleInfo, 130 }, 131 }, 132 }, 133 } 134 } 135 136 func extractBuildpacks(layersMd dist.ModuleLayers) []dist.ModuleInfo { 137 result := []dist.ModuleInfo{} 138 buildpackSet := map[*dist.ModuleInfo]bool{} 139 140 for buildpackID, buildpackMap := range layersMd { 141 for version, layerInfo := range buildpackMap { 142 bp := dist.ModuleInfo{ 143 ID: buildpackID, 144 Name: layerInfo.Name, 145 Version: version, 146 Homepage: layerInfo.Homepage, 147 } 148 buildpackSet[&bp] = true 149 } 150 } 151 152 for currentBuildpack := range buildpackSet { 153 result = append(result, *currentBuildpack) 154 } 155 156 sort.Slice(result, func(i int, j int) bool { 157 switch { 158 case result[i].ID < result[j].ID: 159 return true 160 case result[i].ID == result[j].ID: 161 return result[i].Version < result[j].Version 162 default: 163 return false 164 } 165 }) 166 return result 167 }