208 lines
5.7 KiB
Go
208 lines
5.7 KiB
Go
package env
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/buildpacks/lifecycle/api"
|
|
)
|
|
|
|
// Env is used to modify and return environment variables
|
|
type Env struct {
|
|
// RootDirMap maps directories in a posix root filesystem to a slice of environment variables that
|
|
RootDirMap map[string][]string
|
|
Vars *Vars
|
|
}
|
|
|
|
// AddRootDir modifies the environment given a root dir. If the root dir contains a directory that matches a key in
|
|
// the Env RooDirMap, the absolute path to the keyed directory will be prepended to all the associated environment variables
|
|
// using the OS path list separator as a delimiter.
|
|
func (p *Env) AddRootDir(dir string) error {
|
|
absDir, err := filepath.Abs(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for dir, vars := range p.RootDirMap {
|
|
childDir := filepath.Join(absDir, dir)
|
|
if _, err := os.Stat(childDir); os.IsNotExist(err) {
|
|
continue
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
for _, key := range vars {
|
|
p.Vars.Set(key, childDir+prefix(p.Vars.Get(key), os.PathListSeparator))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Env) isRootEnv(name string) bool {
|
|
for _, m := range p.RootDirMap {
|
|
for _, k := range m {
|
|
if k == name {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type ActionType string
|
|
|
|
const (
|
|
ActionTypePrepend ActionType = "prepend"
|
|
ActionTypeAppend ActionType = "append"
|
|
ActionTypeOverride ActionType = "override"
|
|
ActionTypeDefault ActionType = "default"
|
|
ActionTypePrependPath ActionType = ""
|
|
)
|
|
|
|
// DefaultActionType returns the default action to perform for an unsuffixed env file as specified for the given
|
|
// buildpack API
|
|
func DefaultActionType(_ *api.Version) ActionType {
|
|
return ActionTypeOverride
|
|
}
|
|
|
|
// AddEnvDir modified the Env given a directory containing env files. For each file in the envDir, if the file has
|
|
// a period delimited suffix, the action matching the given suffix will be performed. If the file has no suffix,
|
|
// the default action will be performed. If the suffix does not match a known type, AddEnvDir will ignore the file.
|
|
func (p *Env) AddEnvDir(envDir string, defaultAction ActionType) error {
|
|
return addEnvDir(p.Vars, envDir, defaultAction)
|
|
}
|
|
|
|
// Set sets the environment variable with the given name to the given value.
|
|
func (p *Env) Set(name, v string) {
|
|
p.Vars.Set(name, v)
|
|
}
|
|
|
|
// WithOverrides returns the environment after applying modifications from the given platform dir and build config
|
|
// If platformDir is non-empty, for each file in the platformDir, if the name of the file does not match an environment variable name in the
|
|
// RootDirMap, the given variable will be set to the contents of the file. If the name does match an environment
|
|
// variable name in the RootDirMap, the contents of the file will be prepended to the environment variable value
|
|
// using the OS path list separator as a delimiter.
|
|
// If baseConfigDir is non-empty, for each file in the envDir, if the file has
|
|
// a period delimited suffix, the action matching the given suffix will be performed. If the file has no suffix,
|
|
// the default action will be performed. If the suffix does not match a known type, AddEnvDir will ignore the file.
|
|
func (p *Env) WithOverrides(platformDir string, baseConfigDir string) (output []string, err error) {
|
|
vars := NewVars(p.Vars.vals, p.Vars.ignoreCase)
|
|
|
|
if platformDir != "" {
|
|
if err := eachEnvFile(filepath.Join(platformDir, "env"), func(k, v string) error {
|
|
if p.isRootEnv(k) {
|
|
vars.Set(k, v+prefix(vars.Get(k), os.PathListSeparator))
|
|
return nil
|
|
}
|
|
vars.Set(k, v)
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if baseConfigDir != "" {
|
|
if err := addEnvDir(vars, filepath.Join(baseConfigDir, "env"), ActionTypeDefault); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return vars.List(), nil
|
|
}
|
|
|
|
func addEnvDir(vars *Vars, envDir string, defaultAction ActionType) error {
|
|
if err := eachEnvFile(envDir, func(k, v string) error {
|
|
parts := strings.SplitN(k, ".", 2)
|
|
name := parts[0]
|
|
var action ActionType
|
|
if len(parts) > 1 {
|
|
action = ActionType(parts[1])
|
|
} else {
|
|
action = defaultAction
|
|
}
|
|
switch action {
|
|
case ActionTypePrepend:
|
|
vars.Set(name, v+prefix(vars.Get(name), delim(envDir, name)...))
|
|
case ActionTypeAppend:
|
|
vars.Set(name, suffix(vars.Get(name), delim(envDir, name)...)+v)
|
|
case ActionTypeOverride:
|
|
vars.Set(name, v)
|
|
case ActionTypeDefault:
|
|
if vars.Get(name) != "" {
|
|
return nil
|
|
}
|
|
vars.Set(name, v)
|
|
case ActionTypePrependPath:
|
|
vars.Set(name, v+prefix(vars.Get(name), delim(envDir, name, os.PathListSeparator)...))
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return errors.Wrapf(err, "apply env files from dir '%s'", envDir)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func prefix(s string, prefix ...byte) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
return string(prefix) + s
|
|
}
|
|
|
|
func suffix(s string, suffix ...byte) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
return s + string(suffix)
|
|
}
|
|
|
|
func delim(dir, name string, def ...byte) []byte {
|
|
value, err := os.ReadFile(filepath.Join(dir, name+".delim"))
|
|
if err != nil {
|
|
return def
|
|
}
|
|
return value
|
|
}
|
|
|
|
func eachEnvFile(dir string, fn func(k, v string) error) error {
|
|
files, err := os.ReadDir(dir)
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range files {
|
|
if f.IsDir() {
|
|
continue
|
|
}
|
|
if f.Type()&os.ModeSymlink != 0 {
|
|
lnFile, err := os.Stat(filepath.Join(dir, f.Name()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if lnFile.IsDir() {
|
|
continue
|
|
}
|
|
}
|
|
value, err := os.ReadFile(filepath.Join(dir, f.Name()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := fn(f.Name(), string(value)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// List returns the environment
|
|
func (p *Env) List() []string {
|
|
return p.Vars.List()
|
|
}
|
|
|
|
// Get returns the value for the given key
|
|
func (p *Env) Get(k string) string {
|
|
return p.Vars.Get(k)
|
|
}
|