-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #87 from Clever/ecs-container-metadata-INFRANG-1116
Use ECS container metadata rather than cgroup hack
- Loading branch information
Showing
2 changed files
with
36 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
0.3.8 | ||
0.3.9 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,133 +1,62 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
"path" | ||
"strings" | ||
"time" | ||
) | ||
|
||
const magicECSTaskARN = "MAGIC_ECS_TASK_ARN" | ||
|
||
func getDockerID() (string, error) { | ||
file, err := os.Open("/proc/self/cgroup") | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
reader := bufio.NewReader(file) | ||
for { | ||
// Example: "2:cpu:/docker/93c562c426414f53582c9830a30bdb54d85642956e18115dd59bc9f435ae5644" | ||
line, err := reader.ReadString('\n') | ||
if err != nil { | ||
break | ||
} | ||
|
||
components := strings.Split(line, ":") | ||
if len(components) == 3 { | ||
return strings.TrimRight(path.Base(components[2]), "\n"), nil | ||
} | ||
} | ||
|
||
return "", fmt.Errorf("Failed to find Docker ID in /proc/self/group") | ||
} | ||
|
||
type ECSAgentMetadata struct { | ||
Cluster string `json:"Cluster"` | ||
} | ||
|
||
type ECSAgentTaskMetadata struct { | ||
Tasks []struct { | ||
ARN string `json:"Arn"` | ||
Containers []struct { | ||
DockerID string `json:"DockerId"` | ||
} `json:"Containers"` | ||
} `json:"Tasks"` | ||
} | ||
|
||
// https://github.com/aws/amazon-ecs-agent/issues/258 | ||
// https://github.com/aws/amazon-ecs-agent/pull/709 | ||
func ecsAgentTaskMetadata() (ECSAgentTaskMetadata, error) { | ||
response, err := http.Get("http://172.17.0.1:51678/v1/tasks") | ||
if err != nil { | ||
return ECSAgentTaskMetadata{}, err | ||
} | ||
defer response.Body.Close() | ||
body, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
return ECSAgentTaskMetadata{}, err | ||
} | ||
|
||
metadata := ECSAgentTaskMetadata{} | ||
err = json.Unmarshal(body, &metadata) | ||
if err != nil { | ||
return ECSAgentTaskMetadata{}, err | ||
} | ||
|
||
return metadata, nil | ||
} | ||
|
||
func ecsAgentMetadata() (ECSAgentMetadata, error) { | ||
response, err := http.Get("http://172.17.0.1:51678/v1/metadata") | ||
if err != nil { | ||
return ECSAgentMetadata{}, err | ||
} | ||
defer response.Body.Close() | ||
body, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
return ECSAgentMetadata{}, err | ||
} | ||
|
||
metadata := ECSAgentMetadata{} | ||
err = json.Unmarshal(body, &metadata) | ||
if err != nil { | ||
return ECSAgentMetadata{}, err | ||
} | ||
|
||
return metadata, nil | ||
// ecsContainerMetadata is a subset of fields in the container metadata file. Doc for reference: | ||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html#metadata-file-format | ||
type ecsContainerMetadata struct { | ||
TaskARN string | ||
MetadataFileStatus string | ||
} | ||
|
||
// expandECSTaskARN uses ECS Container Metadata to magically populate the TaskARN required to | ||
// register with AWS Step Functions. Doc for reference: | ||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html | ||
func expandECSTaskARN(s string) (string, error) { | ||
if !strings.Contains(s, magicECSTaskARN) { | ||
return s, nil | ||
} | ||
|
||
dockerID, err := getDockerID() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
agentMetadata, err := ecsAgentMetadata() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if agentMetadata.Cluster == "" { | ||
return "", fmt.Errorf("Could not find ECS cluster for docker container '%s'", dockerID) | ||
// this is an env var the AWS ECS agent sets for us | ||
filePath, ok := os.LookupEnv("ECS_CONTAINER_METADATA_FILE") | ||
if !ok { | ||
return "", fmt.Errorf("ECS_CONTAINER_METADATA_FILE is not set") | ||
} | ||
|
||
agentTaskMetadata, err := ecsAgentTaskMetadata() | ||
if err != nil { | ||
return "", err | ||
// wait for the file to exist | ||
ticker := time.NewTicker(1 * time.Second) | ||
defer ticker.Stop() | ||
for { | ||
<-ticker.C | ||
if _, err := os.Stat(filePath); err == nil { | ||
break | ||
} | ||
} | ||
|
||
taskARN := "" | ||
for _, task := range agentTaskMetadata.Tasks { | ||
for _, container := range task.Containers { | ||
if strings.HasPrefix(container.DockerID, dockerID) { | ||
taskARN = task.ARN | ||
break | ||
} | ||
// wait until the file data has the TaskARN | ||
var metadata ecsContainerMetadata | ||
for { | ||
b, err := ioutil.ReadFile(filePath) | ||
if err != nil { | ||
return "", err | ||
} | ||
} | ||
if taskARN == "" { | ||
return "", fmt.Errorf("Could not find ECS task for docker container '%s' on cluster '%s'", dockerID, agentMetadata.Cluster) | ||
if err := json.Unmarshal(b, &metadata); err != nil { | ||
return "", err | ||
} | ||
if metadata.TaskARN != "" || metadata.MetadataFileStatus == "READY" { | ||
break | ||
} | ||
<-ticker.C | ||
} | ||
|
||
return strings.Replace(s, magicECSTaskARN, taskARN, 1), nil | ||
return strings.Replace(s, magicECSTaskARN, metadata.TaskARN, 1), nil | ||
} |