forked from aws/aws-sam-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenv.go
188 lines (146 loc) · 5.08 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
package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/awslabs/goformation/cloudformation"
)
/**
The Environment Variable Saga..
There are two types of environment variables
- Lambda runtime variables starting with "AWS_" and passed to every function
- Custom environment variables defined in SAM template for each function
Custom environment variables defined in the SAM template can contain hard-coded
values or fetch from a stack parameter or use Intrinsics to generate a value at
at stack creation time like ARNs. It can get complicated to support all cases
Instead we will parse only hard-coded values from the template. For the rest,
users can supply values through the Shell's environment or the env-var override
CLI argument. If a value is provided through more than one method, the method
with higher priority will win.
Priority (Highest to lowest)
Env-Var CLI argument
Shell's Environment
Hard-coded values from template
This priority also applies to AWS_* system variables
*/
func getEnvironmentVariables(logicalID string, function *cloudformation.AWSServerlessFunction, overrideFile string, profile string) map[string]string {
env := getEnvDefaults(function, profile)
osenv := getEnvFromOS()
overrides := getEnvOverrides(logicalID, overrideFile)
if function.Environment != nil {
for name, value := range function.Environment.Variables {
// hard-coded values, lowest priority
if stringedValue, ok := toStringMaybe(value); ok {
// Get only hard-coded values from the template
env[name] = stringedValue
}
// Shell's environment, second priority
if value, ok := osenv[name]; ok {
env[name] = value
}
// EnvVars overrides provided by customer, highest priority
if len(overrides) > 0 {
if value, ok := overrides[name]; ok {
env[name] = value
}
}
}
}
return env
}
func getEnvDefaults(function *cloudformation.AWSServerlessFunction, profile string) map[string]string {
creds := getSessionOrDefaultCreds(profile)
// Variables available in Lambda execution environment for all functions (AWS_* variables)
env := map[string]string{
"AWS_SAM_LOCAL": "true",
"AWS_REGION": creds["region"],
"AWS_DEFAULT_REGION": creds["region"],
"AWS_ACCESS_KEY_ID": creds["key"],
"AWS_SECRET_ACCESS_KEY": creds["secret"],
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE": strconv.Itoa(int(function.MemorySize)),
"AWS_LAMBDA_FUNCTION_TIMEOUT": strconv.Itoa(int(function.Timeout)),
"AWS_LAMBDA_FUNCTION_HANDLER": function.Handler,
// "AWS_ACCOUNT_ID=",
// "AWS_LAMBDA_EVENT_BODY=",
// "AWS_REGION=",
// "AWS_LAMBDA_FUNCTION_NAME=",
// "AWS_LAMBDA_FUNCTION_VERSION=",
}
if token, ok := creds["sessiontoken"]; ok && token != "" {
env["AWS_SESSION_TOKEN"] = token
}
return env
}
func getEnvOverrides(logicalID string, filename string) map[string]string {
if len(filename) > 0 {
data, err := ioutil.ReadFile(filename)
if err != nil {
log.Printf("Could not read environment overrides from %s: %s\n", filename, err)
return map[string]string{}
}
// This is a JSON of structure {FunctionName: {key:value}, FunctionName: {key:value}}
overrides := map[string]map[string]string{}
if err = json.Unmarshal(data, &overrides); err != nil {
log.Printf("Invalid environment override file %s: %s\n", filename, err)
return map[string]string{}
}
return overrides[logicalID]
}
return map[string]string{}
}
func getSessionOrDefaultCreds(profile string) map[string]string {
region := "us-east-1"
key := "defaultkey"
secret := "defaultsecret"
result := map[string]string{
"region": region,
"key": key,
"secret": secret,
}
opts := session.Options{}
opts.Profile = profile
// Obtain AWS credentials and pass them through to the container runtime via env variables
if sess, err := session.NewSessionWithOptions(opts); err == nil {
creds, err := sess.Config.Credentials.Get()
if err != nil {
log.Printf("WARNING: No AWS credentials found. Missing credentials may lead to slow startup times as detailed in https://github.com/awslabs/aws-sam-local/issues/134")
} else {
if *sess.Config.Region != "" {
result["region"] = *sess.Config.Region
}
result["key"] = creds.AccessKeyID
result["secret"] = creds.SecretAccessKey
if creds.SessionToken != "" {
result["sessiontoken"] = creds.SessionToken
}
}
}
return result
}
func getEnvFromOS() map[string]string {
result := map[string]string{}
for _, value := range os.Environ() {
keyVal := strings.Split(value, "=")
result[keyVal[0]] = keyVal[1]
}
return result
}
// Converts the input to string if it is a primitive type, Otherwise returns nil
func toStringMaybe(value interface{}) (string, bool) {
switch value.(type) {
case string:
return value.(string), true
case int:
return strconv.Itoa(value.(int)), true
case float32, float64:
return strconv.FormatFloat(value.(float64), 'f', -1, 64), true
case bool:
return strconv.FormatBool(value.(bool)), true
default:
return "", false
}
}