-
Notifications
You must be signed in to change notification settings - Fork 612
/
common.go
364 lines (307 loc) · 12.6 KB
/
common.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
package config
import (
"encoding/json"
"fmt"
"io"
"os"
"runtime"
"strings"
"github.com/cihub/seelog"
"github.com/aws/aws-sdk-go/aws/endpoints"
godocker "github.com/fsouza/go-dockerclient"
"github.com/pkg/errors"
)
const (
// AgentImageName is the name of the Docker image containing the Agent
AgentImageName = "amazon/amazon-ecs-agent:latest"
// AgentContainerName is the name of the Agent container started by this program
AgentContainerName = "ecs-agent"
// AgentLogFile is the name of the log file used by the Agent
AgentLogFile = "ecs-agent.log"
UnixSocketPrefix = "unix://"
// Used to mount /proc for agent container
ProcFS = "/proc"
// DefaultAgentVersion is the version of the agent that will be
// fetched if required. This should look like v1.2.3 or an
// 8-character sha, as is downloadable from S3.
DefaultAgentVersion = "v1.87.0"
// AgentPartitionBucketName is the name of the paritional s3 bucket that stores the agent
AgentPartitionBucketName = "amazon-ecs-agent"
// DefaultRegionName is the default region to fall back if the user's region is not a region containing
// the agent bucket
DefaultRegionName = endpoints.UsEast1RegionID
// dockerJSONLogMaxSize is the maximum allowed size of the
// individual backing json log files for the managed container.
dockerJSONLogMaxSize = "16m"
// dockerJSONLogMaxSizeEnvVar is the environment variable that may
// be used to override the default value of dockerJSONLogMaxSize
// used for managed containers.
dockerJSONLogMaxSizeEnvVar = "ECS_INIT_DOCKER_LOG_FILE_SIZE"
// dockerJSONLogMaxFiles is the maximum rotated number of backing
// json log files on disk managed by docker for the managed
// container.
dockerJSONLogMaxFiles = "4"
// dockerJSONLogMaxSizeEnvVar is the environment variable that may
// be used to override the default value used of
// dockerJSONLogMaxFiles for managed containers.
dockerJSONLogMaxFilesEnvVar = "ECS_INIT_DOCKER_LOG_FILE_NUM"
// agentLogDriverEnvVar is the environment variable that may be used
// to set a log driver for the agent container
agentLogDriverEnvVar = "ECS_LOG_DRIVER"
// agentLogOptionsEnvVar is the environment variable that may be used to specify options
// for the log driver set in agentLogDriverEnvVar
agentLogOptionsEnvVar = "ECS_LOG_OPTS"
// defaultLogDriver is the logging driver that will be used if one is not explicitly
// set in agentLogDriverEnvVar
defaultLogDriver = "json-file"
// GPUSupportEnvVar indicates that the AMI has support for GPU
GPUSupportEnvVar = "ECS_ENABLE_GPU_SUPPORT"
// DockerHostEnvVar is the environment variable that specifies the location of the Docker daemon socket.
DockerHostEnvVar = "DOCKER_HOST"
// ExternalEnvVar is the environment variable for specifying whether we are running in external (non-EC2) environment.
ExternalEnvVar = "ECS_EXTERNAL"
// DefaultRegionEnvVar is the environment variable for specifying the default AWS region to use.
DefaultRegionEnvVar = "AWS_DEFAULT_REGION"
// ECSGMSASupportEnvVar indicates that the gMSA is supported
ECSGMSASupportEnvVar = "ECS_GMSA_SUPPORTED"
// CredentialsFetcherHostEnvVar is the environment variable that specifies the location of the credentials-fetcher daemon socket.
CredentialsFetcherHostEnvVar = "CREDENTIALS_FETCHER_HOST"
// this socket is exposed by credentials-fetcher (daemon for gMSA support on Linux)
// defaultCredentialsFetcherSocketPath is set to /var/credentials-fetcher/socket/credentials_fetcher.sock
// in case path is not passed in the env variable
DefaultCredentialsFetcherSocketPath = "/var/credentials-fetcher/socket/credentials_fetcher.sock"
// ECSAgentAppArmorProfileNameEnvVar specifies the AppArmor profile name to use. Only applies
// on AppArmor-enabled platforms (such as Ubuntu and Debian).
ECSAgentAppArmorProfileNameEnvVar = "ECS_AGENT_APPARMOR_PROFILE"
ECSAgentAppArmorDefaultProfileName = "ecs-agent-default"
)
// partitionBucketRegion provides the "partitional" bucket region
// suitable for downloading agent from.
var partitionBucketRegion = map[string]string{
endpoints.AwsPartitionID: endpoints.UsEast1RegionID,
endpoints.AwsCnPartitionID: endpoints.CnNorth1RegionID,
endpoints.AwsUsGovPartitionID: endpoints.UsGovWest1RegionID,
endpoints.AwsIsoPartitionID: endpoints.UsIsoEast1RegionID,
endpoints.AwsIsoBPartitionID: endpoints.UsIsobEast1RegionID,
}
// goarch is an injectable GOARCH runtime string. This controls the
// formatting of configuration for supported architectures.
var goarch string = runtime.GOARCH
// validDrivers is the set of all supported Docker logging drivers that
// can be used as the log driver for the Agent container
var validDrivers = map[string]struct{}{
"awslogs": {},
"fluentd": {},
"gelf": {},
"json-file": {},
"journald": {},
"logentries": {},
"syslog": {},
"splunk": {},
}
// GetAgentPartitionBucketRegion returns the s3 bucket region where ECS Agent artifact is located
func GetAgentPartitionBucketRegion(region string) (string, error) {
regionPartition, ok := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region)
if !ok {
return "", errors.Errorf("could not resolve partition ID for region %q", region)
}
bucketRegion, ok := partitionBucketRegion[regionPartition.ID()]
if !ok {
return "", errors.Errorf("no bucket available for partition ID %q", regionPartition.ID())
}
return bucketRegion, nil
}
// AgentConfigDirectory returns the location on disk for configuration
func AgentConfigDirectory() string {
return directoryPrefix + "/etc/ecs"
}
// AgentConfigFile returns the location of a file of environment variables passed to the Agent
func AgentConfigFile() string {
return AgentConfigDirectory() + "/ecs.config"
}
// AgentJSONConfigFile returns the location of a file containing configuration expressed in JSON
func AgentJSONConfigFile() string {
return AgentConfigDirectory() + "/ecs.config.json"
}
// LogDirectory returns the location on disk where logs should be placed
func LogDirectory() string {
return directoryPrefix + "/var/log/ecs"
}
func InitLogFile() string {
return LogDirectory() + "/ecs-init.log"
}
// AgentDataDirectory returns the location on disk where state should be saved
func AgentDataDirectory() string {
return directoryPrefix + "/var/lib/ecs/data"
}
// CacheDirectory returns the location on disk where Agent images should be cached
func CacheDirectory() string {
return directoryPrefix + "/var/cache/ecs"
}
// CacheState returns the location on disk where cache state is stored
func CacheState() string {
return CacheDirectory() + "/state"
}
// AgentTarball returns the location on disk of the cached Agent image
func AgentTarball() string {
return CacheDirectory() + "/ecs-agent.tar"
}
// AgentRemoteTarballKey is the remote filename of the Agent image, used for populating the cache
func AgentRemoteTarballKey() (string, error) {
name, err := agentArtifactName(DefaultAgentVersion, goarch)
if err != nil {
return "", errors.Wrap(err, "no artifact available")
}
return fmt.Sprintf("%s.tar", name), nil
}
// AgentRemoteTarballMD5Key is the remote file of a md5sum used to verify the integrity of the AgentRemoteTarball
func AgentRemoteTarballMD5Key() (string, error) {
tarballKey, err := AgentRemoteTarballKey()
if err != nil {
return "", err
}
return tarballKey + ".md5", nil
}
// DesiredImageLocatorFile returns the location on disk of a well-known file describing an Agent image to load
func DesiredImageLocatorFile() string {
return CacheDirectory() + "/desired-image"
}
// DockerUnixSocket returns the docker socket endpoint and whether it's read from DockerHostEnvVar
func DockerUnixSocket() (string, bool) {
if dockerHost := os.Getenv(DockerHostEnvVar); strings.HasPrefix(dockerHost, UnixSocketPrefix) {
return strings.TrimPrefix(dockerHost, UnixSocketPrefix), true
}
// return /var/run instead of /var/run/docker.sock, in case the /var/run/docker.sock is deleted and recreated
// outside the container, eg: Docker daemon restart
return "/var/run", false
}
// credentialsFetcherUnixSocketHostPath returns the credentials fetcher daemon socket endpoint and whether it reads from CredentialsFetcherEnvVar
func credentialsFetcherUnixSocket() string {
if credentialsFetcherHost := os.Getenv(CredentialsFetcherHostEnvVar); strings.HasPrefix(credentialsFetcherHost, UnixSocketPrefix) {
return strings.TrimPrefix(credentialsFetcherHost, UnixSocketPrefix)
}
return DefaultCredentialsFetcherSocketPath
}
// HostCredentialsFetcherPath() returns the daemon socket location if it is available
func HostCredentialsFetcherPath() (string, bool) {
if credentialsFetcherHost := credentialsFetcherUnixSocket(); len(credentialsFetcherHost) > 0 {
return credentialsFetcherHost, true
}
return "", false
}
// CgroupMountpoint returns the cgroup mountpoint for the system
func CgroupMountpoint() string {
return cgroupMountpoint
}
// MountDirectoryEBS returns the location on disk where EBS volumes will be mounted
func MountDirectoryEBS() string {
return directoryPrefix + "/mnt/ecs/ebs"
}
// HostCertsDirPath() returns the CA store path on the host
func HostCertsDirPath() string {
if _, err := os.Stat(hostCertsDirPath); err != nil {
return ""
}
return hostCertsDirPath
}
// HostPKIDirPath() returns the CA store path on the host
func HostPKIDirPath() string {
if _, err := os.Stat(hostPKIDirPath); err != nil {
return ""
}
return hostPKIDirPath
}
// AgentDockerLogDriverConfiguration returns a LogConfig object
// suitable for used with the managed container.
func AgentDockerLogDriverConfiguration() godocker.LogConfig {
driver := defaultLogDriver
options := parseLogOptions()
if envDriver := os.Getenv(agentLogDriverEnvVar); envDriver != "" {
if _, ok := validDrivers[envDriver]; ok {
driver = envDriver
} else {
seelog.Warnf("Input value for \"ECS_LOG_DRIVER\" is not a supported log driver, overriding to %s and using default log options", defaultLogDriver)
options = nil
}
}
if driver == defaultLogDriver && options == nil {
maxSize := dockerJSONLogMaxSize
if fromEnv := os.Getenv(dockerJSONLogMaxSizeEnvVar); fromEnv != "" {
maxSize = fromEnv
}
maxFiles := dockerJSONLogMaxFiles
if fromEnv := os.Getenv(dockerJSONLogMaxFilesEnvVar); fromEnv != "" {
maxFiles = fromEnv
}
options = map[string]string{
"max-size": maxSize,
"max-file": maxFiles,
}
}
return godocker.LogConfig{
Type: driver,
Config: options,
}
}
func parseLogOptions() map[string]string {
opts := os.Getenv(agentLogOptionsEnvVar)
logOptsDecoder := json.NewDecoder(strings.NewReader(opts))
var logOptions map[string]string
err := logOptsDecoder.Decode(&logOptions)
// blank string is not a warning
if err != io.EOF && err != nil {
seelog.Warnf("Invalid format for \"ECS_LOG_OPTS\", expected a json object with string key value. error: %v", err)
}
return logOptions
}
// InstanceConfigDirectory returns the location on disk for custom instance configuration
func InstanceConfigDirectory() string {
return directoryPrefix + "/var/lib/ecs"
}
// InstanceConfigFile returns the location of a file of custom environment variables
func InstanceConfigFile() string {
return InstanceConfigDirectory() + "/ecs.config"
}
// RunPrivileged returns if agent should be invoked with '--privileged'. This is not
// recommended and may be removed in future versions of amazon-ecs-init.
func RunPrivileged() bool {
envVar := os.Getenv("ECS_AGENT_RUN_PRIVILEGED")
return envVar == "true"
}
// RunningInExternal returns whether we are running in external (non-EC2) environment.
func RunningInExternal() bool {
envVar := os.Getenv(ExternalEnvVar)
return envVar == "true"
}
// ECSAgentApparmorProfileName returns the name of the AppArmor profile to use.
func ECSAgentAppArmorProfileName() string {
envVar := os.Getenv(ECSAgentAppArmorProfileNameEnvVar)
if len(strings.TrimSpace(envVar)) == 0 {
return ECSAgentAppArmorDefaultProfileName
}
return envVar
}
func agentArtifactName(version string, arch string) (string, error) {
var interpose string
switch arch {
case "amd64":
interpose = ""
case "arm64":
interpose = "-" + arch
default:
return "", errors.Errorf("unknown architecture %q", arch)
}
return fmt.Sprintf("ecs-agent%s-%s", interpose, version), nil
}