github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/buildpackage/config_reader.go (about)

     1  package buildpackage
     2  
     3  import (
     4  	"path/filepath"
     5  
     6  	"github.com/BurntSushi/toml"
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/buildpacks/pack/internal/config"
    10  	"github.com/buildpacks/pack/internal/style"
    11  	"github.com/buildpacks/pack/pkg/buildpack"
    12  	"github.com/buildpacks/pack/pkg/dist"
    13  )
    14  
    15  const defaultOS = "linux"
    16  
    17  // Config encapsulates the possible configuration options for buildpackage creation.
    18  type Config struct {
    19  	Buildpack    dist.BuildpackURI `toml:"buildpack"`
    20  	Extension    dist.BuildpackURI `toml:"extension"`
    21  	Dependencies []dist.ImageOrURI `toml:"dependencies"`
    22  	Platform     dist.Platform     `toml:"platform"`
    23  }
    24  
    25  func DefaultConfig() Config {
    26  	return Config{
    27  		Buildpack: dist.BuildpackURI{
    28  			URI: ".",
    29  		},
    30  		Platform: dist.Platform{
    31  			OS: defaultOS,
    32  		},
    33  	}
    34  }
    35  
    36  func DefaultExtensionConfig() Config {
    37  	return Config{
    38  		Extension: dist.BuildpackURI{
    39  			URI: ".",
    40  		},
    41  		Platform: dist.Platform{
    42  			OS: defaultOS,
    43  		},
    44  	}
    45  }
    46  
    47  // NewConfigReader returns an instance of ConfigReader. It does not take any parameters.
    48  func NewConfigReader() *ConfigReader {
    49  	return &ConfigReader{}
    50  }
    51  
    52  // ConfigReader implements a Read method for buildpackage configuration which parses and validates buildpackage
    53  // configuration from a toml file.
    54  type ConfigReader struct{}
    55  
    56  // Read reads and validates a buildpackage configuration from the file path provided and returns the
    57  // configuration and any error that occurred during reading or validation.
    58  func (r *ConfigReader) Read(path string) (Config, error) {
    59  	packageConfig := Config{}
    60  
    61  	tomlMetadata, err := toml.DecodeFile(path, &packageConfig)
    62  	if err != nil {
    63  		return packageConfig, errors.Wrap(err, "decoding toml")
    64  	}
    65  
    66  	undecodedKeys := tomlMetadata.Undecoded()
    67  	if len(undecodedKeys) > 0 {
    68  		unknownElementsMsg := config.FormatUndecodedKeys(undecodedKeys)
    69  
    70  		return packageConfig, errors.Errorf("%s in %s",
    71  			unknownElementsMsg,
    72  			style.Symbol(path),
    73  		)
    74  	}
    75  
    76  	if packageConfig.Buildpack.URI == "" && packageConfig.Extension.URI == "" {
    77  		if packageConfig.Buildpack.URI == "" {
    78  			return packageConfig, errors.Errorf("missing %s configuration", style.Symbol("buildpack.uri"))
    79  		}
    80  		return packageConfig, errors.Errorf("missing %s configuration", style.Symbol("extension.uri"))
    81  	}
    82  
    83  	if packageConfig.Platform.OS == "" {
    84  		packageConfig.Platform.OS = defaultOS
    85  	}
    86  
    87  	if packageConfig.Platform.OS != "linux" && packageConfig.Platform.OS != "windows" {
    88  		return packageConfig, errors.Errorf("invalid %s configuration: only [%s, %s] is permitted, found %s",
    89  			style.Symbol("platform.os"), style.Symbol("linux"), style.Symbol("windows"), style.Symbol(packageConfig.Platform.OS))
    90  	}
    91  
    92  	configDir, err := filepath.Abs(filepath.Dir(path))
    93  	if err != nil {
    94  		return packageConfig, err
    95  	}
    96  
    97  	if err := validateURI(packageConfig.Buildpack.URI, configDir); err != nil {
    98  		return packageConfig, err
    99  	}
   100  
   101  	for _, dep := range packageConfig.Dependencies {
   102  		if dep.URI != "" && dep.ImageName != "" {
   103  			return packageConfig, errors.Errorf(
   104  				"dependency configured with both %s and %s",
   105  				style.Symbol("uri"),
   106  				style.Symbol("image"),
   107  			)
   108  		}
   109  
   110  		if dep.URI != "" {
   111  			if err := validateURI(dep.URI, configDir); err != nil {
   112  				return packageConfig, err
   113  			}
   114  		}
   115  	}
   116  
   117  	return packageConfig, nil
   118  }
   119  
   120  func validateURI(uri, relativeBaseDir string) error {
   121  	locatorType, err := buildpack.GetLocatorType(uri, relativeBaseDir, nil)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	if locatorType == buildpack.InvalidLocator {
   127  		return errors.Errorf("invalid locator %s", style.Symbol(uri))
   128  	}
   129  
   130  	return nil
   131  }