Skip to content

Commit

Permalink
Merge pull request #87 from Clever/ecs-container-metadata-INFRANG-1116
Browse files Browse the repository at this point in the history
Use ECS container metadata rather than cgroup hack
  • Loading branch information
Sayan- authored Mar 5, 2020
2 parents 45827fc + 3fb1602 commit 0ec68cb
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 107 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.8
0.3.9
141 changes: 35 additions & 106 deletions cmd/sfncli/ecs.go
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
}

0 comments on commit 0ec68cb

Please sign in to comment.