Skip to content

Instantly share code, notes, and snippets.

@pecigonzalo
Forked from IngmarStein/ECSDynamicHostPort.go
Created August 10, 2017 16:05
Show Gist options
  • Save pecigonzalo/7ae8be959875a45b2832655c850d9a0a to your computer and use it in GitHub Desktop.
Save pecigonzalo/7ae8be959875a45b2832655c850d9a0a to your computer and use it in GitHub Desktop.
Get dynamically mapped host port from within ECS container
package main
import (
"encoding/json"
"io/ioutil"
"log"
"net/http"
"os"
"bufio"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecs"
"path"
"strings"
)
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"`
}
func check(err error) {
if err != nil {
log.Panic(err)
}
}
// See
// 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
}
func getDockerId() (string, error) {
file, err := os.Open("/proc/self/cgroup")
if err != nil {
fmt.Println("Could not open /proc/self/cgroup, using hostname instead as container id")
return os.Hostname()
}
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")
}
func main() {
metasession := session.Must(session.NewSession())
ec2metadataSvc := ec2metadata.New(metasession)
region, err := ec2metadataSvc.Region()
check(err)
sess, err := session.NewSession(aws.NewConfig().WithRegion(region))
check(err)
// Create the AWS clients
ecsSvc := ecs.New(sess)
// find our task in the ECS agent metadata
dockerId, err := getDockerId()
check(err)
agentMetadata, err := ecsAgentMetadata()
check(err)
if agentMetadata.Cluster == "" {
log.Panic(fmt.Errorf("Could not find ECS cluster for docker container '%s'", dockerId))
}
agentTaskMetadata, err := ecsAgentTaskMetadata()
check(err)
taskArn := ""
for _, task := range agentTaskMetadata.Tasks {
for _, container := range task.Containers {
if strings.HasPrefix(container.DockerId, dockerId) {
taskArn = task.Arn
break
}
}
}
if taskArn == "" {
log.Panic(fmt.Errorf("Could not find ECS task for docker container '%s' on cluster '%s'", dockerId, agentMetadata.Cluster))
}
tasks, err := ecsSvc.DescribeTasks(&ecs.DescribeTasksInput{
Cluster: aws.String(agentMetadata.Cluster),
Tasks: []*string{aws.String(taskArn)},
})
check(err)
for _, task := range tasks.Tasks {
for _, container := range task.Containers {
for _, binding := range container.NetworkBindings {
if binding.ContainerPort != nil && binding.HostPort != nil {
log.Println("Found host port: ", *binding.HostPort)
return
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment