-
Notifications
You must be signed in to change notification settings - Fork 37
/
ecs_metadata.go
165 lines (136 loc) · 5.45 KB
/
ecs_metadata.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
// (c) Copyright IBM Corp. 2021
// (c) Copyright Instana Inc. 2020
package aws
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/instana/go-sensor/docker"
)
// ContainerLimits represents the resource limits specified at the task level
type ContainerLimits struct {
CPU int `json:"CPU"`
Memory int `json:"Memory"`
}
// ContainerLabels represents AWS container labels
type ContainerLabels struct {
Cluster string `json:"com.amazonaws.ecs.cluster"`
TaskARN string `json:"com.amazonaws.ecs.task-arn"`
TaskDefinition string `json:"com.amazonaws.ecs.task-definition-family"`
TaskDefinitionVersion string `json:"com.amazonaws.ecs.task-definition-version"`
}
// ContainerNetwork represents AWS container network configuration
type ContainerNetwork struct {
Mode string `json:"NetworkMode"`
IPv4Addresses []string `json:"IPv4Addresses"`
}
// ECSContainerMetadata represents the ECS container metadata as described in
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html#metadata-file-format
type ECSContainerMetadata struct {
DockerID string `json:"DockerId"`
Name string `json:"Name"`
DockerName string `json:"DockerName"`
Image string `json:"Image"`
ImageID string `json:"ImageID"`
DesiredStatus string `json:"DesiredStatus"`
KnownStatus string `json:"KnownStatus"`
Limits ContainerLimits `json:"Limits"`
CreatedAt time.Time `json:"CreatedAt"`
StartedAt time.Time `json:"StartedAt"`
Type string `json:"Type"`
Networks []ContainerNetwork `json:"Networks"`
ContainerLabels `json:"Labels"`
}
// ECSTaskMetadata represents the ECS task metadata as described in
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v3.html#task-metadata-endpoint-v3-response
type ECSTaskMetadata struct {
TaskARN string `json:"TaskARN"`
AvailabilityZone string `json:"AvailabilityZone,omitempty"` // only available starting from ECS platform v1.4
Family string `json:"Family"`
Revision string `json:"Revision"`
DesiredStatus string `json:"DesiredStatus"`
KnownStatus string `json:"KnownStatus"`
Containers []ECSContainerMetadata `json:"Containers"`
PullStartedAt time.Time `json:"PullStartedAt"`
PullStoppedAt time.Time `json:"PullStoppedAt"`
}
// ECSMetadataProvider retireves ECS service metadata from the ECS_CONTAINER_METADATA_URI endpoint
type ECSMetadataProvider struct {
Endpoint string
client *http.Client
}
// NewECSMetadataProvider initializes a new ECSMetadataClient with given endpoint and HTTP client.
// If there is no HTTP client provided, the provider will use http.DefaultClient
func NewECSMetadataProvider(endpoint string, c *http.Client) *ECSMetadataProvider {
if c == nil {
c = http.DefaultClient
}
return &ECSMetadataProvider{
Endpoint: endpoint,
client: c,
}
}
// ContainerMetadata returns ECS metadata for current container
func (c *ECSMetadataProvider) ContainerMetadata(ctx context.Context) (ECSContainerMetadata, error) {
var data ECSContainerMetadata
req, err := http.NewRequest(http.MethodGet, c.Endpoint, nil)
if err != nil {
return data, fmt.Errorf("failed to prepare request: %s", err)
}
body, err := c.executeRequest(req.WithContext(ctx))
if err != nil {
return data, fmt.Errorf("failed to fetch container metadata: %s", err)
}
defer body.Close()
if err := json.NewDecoder(body).Decode(&data); err != nil {
return data, fmt.Errorf("malformed container metadata response: %s", err)
}
return data, nil
}
// TaskMetadata returns ECS metadata for current task
func (c *ECSMetadataProvider) TaskMetadata(ctx context.Context) (ECSTaskMetadata, error) {
var data ECSTaskMetadata
req, err := http.NewRequest(http.MethodGet, c.Endpoint+"/task", nil)
if err != nil {
return data, fmt.Errorf("failed to prepare request: %s", err)
}
body, err := c.executeRequest(req.WithContext(ctx))
if err != nil {
return data, fmt.Errorf("failed to fetch task metadata: %s", err)
}
defer body.Close()
if err := json.NewDecoder(body).Decode(&data); err != nil {
return data, fmt.Errorf("malformed task metadata response: %s", err)
}
return data, nil
}
// TaskStats returns Docker stats for current ECS task
func (c *ECSMetadataProvider) TaskStats(ctx context.Context) (map[string]docker.ContainerStats, error) {
var data map[string]docker.ContainerStats
req, err := http.NewRequest(http.MethodGet, c.Endpoint+"/task/stats", nil)
if err != nil {
return data, fmt.Errorf("failed to prepare request: %s", err)
}
body, err := c.executeRequest(req.WithContext(ctx))
if err != nil {
return data, fmt.Errorf("failed to fetch task metadata: %s", err)
}
defer body.Close()
if err := json.NewDecoder(body).Decode(&data); err != nil {
return data, fmt.Errorf("malformed task stats response: %s", err)
}
return data, nil
}
func (c *ECSMetadataProvider) executeRequest(req *http.Request) (io.ReadCloser, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to execute request: %s", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("the endpoint responded with %s", resp.Status)
}
return resp.Body, nil
}