-
Notifications
You must be signed in to change notification settings - Fork 107
/
env.go
207 lines (185 loc) · 5.7 KB
/
env.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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)
}