github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/lifecycle.go (about)

     1  package builder
     2  
     3  import (
     4  	"archive/tar"
     5  	"fmt"
     6  	"io"
     7  	"path"
     8  	"regexp"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/YousefHaggyHeroku/pack/internal/archive"
    13  )
    14  
    15  // A snapshot of the latest tested lifecycle version values
    16  const (
    17  	DefaultLifecycleVersion    = "0.9.3"
    18  	DefaultBuildpackAPIVersion = "0.2"
    19  )
    20  
    21  // Blob is an interface to wrap opening blobs
    22  type Blob interface {
    23  	Open() (io.ReadCloser, error)
    24  }
    25  
    26  // Lifecycle is an implementation of the CNB Lifecycle spec
    27  //go:generate mockgen -package testmocks -destination testmocks/mock_lifecycle.go github.com/YousefHaggyHeroku/pack/internal/builder Lifecycle
    28  type Lifecycle interface {
    29  	Blob
    30  	Descriptor() LifecycleDescriptor
    31  }
    32  
    33  type lifecycle struct {
    34  	descriptor LifecycleDescriptor
    35  	Blob
    36  }
    37  
    38  // NewLifecycle creates a Lifecycle from a Blob
    39  func NewLifecycle(blob Blob) (Lifecycle, error) {
    40  	var err error
    41  
    42  	br, err := blob.Open()
    43  	if err != nil {
    44  		return nil, errors.Wrap(err, "open lifecycle blob")
    45  	}
    46  	defer br.Close()
    47  
    48  	_, buf, err := archive.ReadTarEntry(br, "lifecycle.toml")
    49  	if err != nil && errors.Cause(err) == archive.ErrEntryNotExist {
    50  		return nil, err
    51  	} else if err != nil {
    52  		return nil, errors.Wrap(err, "reading lifecycle descriptor")
    53  	}
    54  
    55  	lifecycleDescriptor, err := ParseDescriptor(string(buf))
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	lifecycle := &lifecycle{Blob: blob, descriptor: CompatDescriptor(lifecycleDescriptor)}
    61  
    62  	if err = lifecycle.validateBinaries(); err != nil {
    63  		return nil, errors.Wrap(err, "validating binaries")
    64  	}
    65  
    66  	return lifecycle, nil
    67  }
    68  
    69  // Descriptor returns the LifecycleDescriptor
    70  func (l *lifecycle) Descriptor() LifecycleDescriptor {
    71  	return l.descriptor
    72  }
    73  
    74  func (l *lifecycle) validateBinaries() error {
    75  	rc, err := l.Open()
    76  	if err != nil {
    77  		return errors.Wrap(err, "create lifecycle blob reader")
    78  	}
    79  	defer rc.Close()
    80  	regex := regexp.MustCompile(`^[^/]+/([^/]+)$`)
    81  	headers := map[string]bool{}
    82  	tr := tar.NewReader(rc)
    83  	for {
    84  		header, err := tr.Next()
    85  		if err == io.EOF {
    86  			break
    87  		}
    88  		if err != nil {
    89  			return errors.Wrap(err, "failed to get next tar entry")
    90  		}
    91  
    92  		pathMatches := regex.FindStringSubmatch(path.Clean(header.Name))
    93  		if pathMatches != nil {
    94  			headers[pathMatches[1]] = true
    95  		}
    96  	}
    97  	for _, p := range l.binaries() {
    98  		_, found := headers[p]
    99  		if !found {
   100  			_, found = headers[p+".exe"]
   101  			if !found {
   102  				return fmt.Errorf("did not find '%s' in tar", p)
   103  			}
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  // Binaries returns a list of all binaries contained in the lifecycle.
   110  func (l *lifecycle) binaries() []string {
   111  	binaries := []string{
   112  		"detector",
   113  		"restorer",
   114  		"analyzer",
   115  		"builder",
   116  		"exporter",
   117  		"launcher",
   118  		"creator",
   119  	}
   120  	return binaries
   121  }