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  }