Skip to content

Instantly share code, notes, and snippets.

@jedipunkz
Last active December 28, 2024 17:24
Show Gist options
  • Save jedipunkz/ff3e0a3ba1aeee3f2d619f84c852f4c3 to your computer and use it in GitHub Desktop.
Save jedipunkz/ff3e0a3ba1aeee3f2d619f84c852f4c3 to your computer and use it in GitHub Desktop.

Revisions

  1. jedipunkz renamed this gist Jan 3, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. jedipunkz created this gist Jan 3, 2023.
    163 changes: 163 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,163 @@
    package main

    import (
    "context"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "os"
    )

    const (
    ecsMetadataUriEnvV4 = "ECS_CONTAINER_METADATA_URI_V4"
    )

    type Client struct {
    HTTPClient *http.Client
    endpoint string
    }

    type TaskMetadata struct {
    Cluster string `json:"Cluster"`
    TaskARN string `json:"TaskARN"`
    Family string `json:"Family"`
    Revision string `json:"Revision"`
    DesiredStatus string `json:"DesiredStatus"`
    KnownStatus string `json:"KnownStatus"`
    AvailabilityZone string `json:"AvailabilityZone"`
    LaunchType string `json:"LaunchType"`
    Containers []struct {
    DockerID string `json:"DockerId"`
    Name string `json:"Name"`
    DockerName string `json:"DockerName"`
    Image string `json:"Image"`
    ImageID string `json:"ImageID"`
    Labels map[string]string `json:"Labels"`
    DesiredStatus string `json:"DesiredStatus"`
    KnownStatus string `json:"KnownStatus"`
    Type string `json:"Type"`
    ContainerARN string `json:"ContainerARN"`
    } `json:"Containers"`
    }

    type StatsMetadata struct {
    CPUStats struct {
    CPUUsage struct {
    TotalUsage int `json:"total_usage"`
    PerCPUUsage []int `json:"percpu_usage"`
    UsageInKernelmode int `json:"usage_in_kernelmode"`
    UsageInUsermode int `json:"usage_in_usermode"`
    } `json:"cpu_usage"`
    SystemCPUUsage int `json:"system_cpu_usage"`
    OnlineCPUs int `json:"online_cpus"`
    ThrottlingData struct {
    Periods int `json:"periods"`
    ThrottledPeriods int `json:"throttled_periods"`
    ThrottledTime int `json:"throttled_time"`
    } `json:"throttling_data"`
    } `json:"cpu_stats"`
    PreCPUStats struct {
    CPUUsage struct {
    TotalUsage int `json:"total_usage"`
    PerCPUUsage []int `json:"percpu_usage"`
    UsageInKernelmode int `json:"usage_in_kernelmode"`
    UsageInUsermode int `json:"usage_in_usermode"`
    } `json:"cpu_usage"`
    SystemCPUUsage int `json:"system_cpu_usage"`
    OnlineCPUs int `json:"online_cpus"`
    ThrottlingData struct {
    Periods int `json:"periods"`
    ThrottledPeriods int `json:"throttled_periods"`
    ThrottledTime int `json:"throttled_time"`
    } `json:"throttling_data"`
    } `json:"precpu_stats"`
    }

    // NewClient retrurns a new ECS client and endpoint
    func NewClient(endpoint string) *Client {
    return &Client{
    HTTPClient: &http.Client{},
    endpoint: endpoint,
    }
    }

    // NewClientToMetadataEndpoint returns a new ECS client and endpoint
    func NewClientToMetadataEndpoint() (*Client, error) {
    const endpointURI = "ECS_CONTAINER_METADATA_URI_V4"
    endpoint := os.Getenv(endpointURI)
    if endpoint == "" {
    return nil, fmt.Errorf("environment variable %s not set", endpointURI)
    }

    _, err := url.Parse(endpoint)
    if err != nil {
    return nil, fmt.Errorf("invalid endpoint: %s", err)
    }

    return NewClient(endpoint), nil
    }

    func (c *Client) request(ctx context.Context, uri string, out interface{}) error {
    req, err := http.NewRequest("GET", uri, nil)
    if err != nil {
    return err
    }
    req = req.WithContext(ctx)
    resp, err := c.HTTPClient.Do(req)
    if err != nil {
    return err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
    return err
    }
    return json.Unmarshal(body, out)
    }

    func (c *Client) RetriveTaskMetadata(ctx context.Context) (TaskMetadata, error) {
    var output TaskMetadata
    err := c.request(ctx, c.endpoint+"/task", &output)
    return output, err
    }

    func (c *Client) RetriveStatsMetadata(ctx context.Context) (map[string]StatsMetadata, error) {
    output := make(map[string]StatsMetadata)
    err := c.request(ctx, c.endpoint+"/task/stats", &output)
    return output, err
    }

    func main() {
    ctx := context.Background()
    client, err := NewClientToMetadataEndpoint()
    if err != nil {
    log.Printf("error creating client: %s", err)
    }

    taskMetadata, err := client.RetriveTaskMetadata(ctx)
    if err != nil {
    log.Printf("error retrieving task metadata: %s", err)
    }

    statsMetadata, err := client.RetriveStatsMetadata(ctx)
    if err != nil {
    log.Printf("error retrieving task stats metadata: %s", err)
    }

    for _, container := range taskMetadata.Containers {
    s := statsMetadata[container.DockerID]
    if &s == nil {
    log.Printf("Could not find stats for container %s", container.DockerID)
    continue
    }

    log.Printf("Total CPU Usage: %d", s.CPUStats.CPUUsage.TotalUsage)
    log.Printf("CPU Usage: %f", (float64(s.CPUStats.CPUUsage.TotalUsage)-float64(s.PreCPUStats.CPUUsage.TotalUsage))/
    (float64(s.CPUStats.SystemCPUUsage)-float64(s.PreCPUStats.SystemCPUUsage))*
    float64(s.CPUStats.OnlineCPUs)*100)
    }
    }