github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/inspect.go (about) 1 package builder 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 pubbldr "github.com/YousefHaggyHeroku/pack/builder" 9 10 "github.com/YousefHaggyHeroku/pack/internal/dist" 11 12 "github.com/YousefHaggyHeroku/pack/config" 13 ) 14 15 type Info struct { 16 Description string 17 StackID string 18 Mixins []string 19 RunImage string 20 RunImageMirrors []string 21 Buildpacks []dist.BuildpackInfo 22 Order pubbldr.DetectionOrder 23 BuildpackLayers dist.BuildpackLayers 24 Lifecycle LifecycleDescriptor 25 CreatedBy CreatorMetadata 26 } 27 28 type Inspectable interface { 29 Label(name string) (string, error) 30 } 31 32 type InspectableFetcher interface { 33 Fetch(ctx context.Context, name string, daemon bool, pullPolicy config.PullPolicy) (Inspectable, error) 34 } 35 36 type LabelManagerFactory interface { 37 BuilderLabelManager(inspectable Inspectable) LabelInspector 38 } 39 40 type LabelInspector interface { 41 Metadata() (Metadata, error) 42 StackID() (string, error) 43 Mixins() ([]string, error) 44 Order() (dist.Order, error) 45 BuildpackLayers() (dist.BuildpackLayers, error) 46 } 47 48 type DetectionCalculator interface { 49 Order(topOrder dist.Order, layers dist.BuildpackLayers, depth int) (pubbldr.DetectionOrder, error) 50 } 51 52 type Inspector struct { 53 imageFetcher InspectableFetcher 54 labelManagerFactory LabelManagerFactory 55 detectionOrderCalculator DetectionCalculator 56 } 57 58 func NewInspector(fetcher InspectableFetcher, factory LabelManagerFactory, calculator DetectionCalculator) *Inspector { 59 return &Inspector{ 60 imageFetcher: fetcher, 61 labelManagerFactory: factory, 62 detectionOrderCalculator: calculator, 63 } 64 } 65 66 func (i *Inspector) Inspect(name string, daemon bool, orderDetectionDepth int) (Info, error) { 67 inspectable, err := i.imageFetcher.Fetch(context.Background(), name, daemon, config.PullNever) 68 if err != nil { 69 return Info{}, fmt.Errorf("fetching builder image: %w", err) 70 } 71 72 labelManager := i.labelManagerFactory.BuilderLabelManager(inspectable) 73 74 metadata, err := labelManager.Metadata() 75 if err != nil { 76 return Info{}, fmt.Errorf("reading image metadata: %w", err) 77 } 78 79 stackID, err := labelManager.StackID() 80 if err != nil { 81 return Info{}, fmt.Errorf("reading image stack id: %w", err) 82 } 83 84 mixins, err := labelManager.Mixins() 85 if err != nil { 86 return Info{}, fmt.Errorf("reading image mixins: %w", err) 87 } 88 89 var commonMixins, buildMixins []string 90 commonMixins = []string{} 91 for _, mixin := range mixins { 92 if strings.HasPrefix(mixin, "build:") { 93 buildMixins = append(buildMixins, mixin) 94 } else { 95 commonMixins = append(commonMixins, mixin) 96 } 97 } 98 99 order, err := labelManager.Order() 100 if err != nil { 101 return Info{}, fmt.Errorf("reading image order: %w", err) 102 } 103 104 layers, err := labelManager.BuildpackLayers() 105 if err != nil { 106 return Info{}, fmt.Errorf("reading image buildpack layers: %w", err) 107 } 108 109 detectionOrder, err := i.detectionOrderCalculator.Order(order, layers, orderDetectionDepth) 110 if err != nil { 111 return Info{}, fmt.Errorf("calculating detection order: %w", err) 112 } 113 114 lifecycle := CompatDescriptor(LifecycleDescriptor{ 115 Info: LifecycleInfo{Version: metadata.Lifecycle.Version}, 116 API: metadata.Lifecycle.API, 117 APIs: metadata.Lifecycle.APIs, 118 }) 119 120 return Info{ 121 Description: metadata.Description, 122 StackID: stackID, 123 Mixins: append(commonMixins, buildMixins...), 124 RunImage: metadata.Stack.RunImage.Image, 125 RunImageMirrors: metadata.Stack.RunImage.Mirrors, 126 Buildpacks: uniqueBuildpacks(metadata.Buildpacks), 127 Order: detectionOrder, 128 BuildpackLayers: layers, 129 Lifecycle: lifecycle, 130 CreatedBy: metadata.CreatedBy, 131 }, nil 132 } 133 134 func uniqueBuildpacks(buildpacks []dist.BuildpackInfo) []dist.BuildpackInfo { 135 foundBuildpacks := map[dist.BuildpackInfo]interface{}{} 136 var uniqueBuildpacks []dist.BuildpackInfo 137 138 for _, bp := range buildpacks { 139 _, ok := foundBuildpacks[bp] 140 if !ok { 141 uniqueBuildpacks = append(uniqueBuildpacks, bp) 142 foundBuildpacks[bp] = true 143 } 144 } 145 146 return uniqueBuildpacks 147 }