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 }