github.com/newrelic/go-agent@v3.26.0+incompatible/internal/utilization/aws.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package utilization
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  )
    12  
    13  const (
    14  	awsHostname     = "169.254.169.254"
    15  	awsEndpointPath = "/2016-09-02/dynamic/instance-identity/document"
    16  	awsEndpoint     = "http://" + awsHostname + awsEndpointPath
    17  )
    18  
    19  type aws struct {
    20  	InstanceID       string `json:"instanceId,omitempty"`
    21  	InstanceType     string `json:"instanceType,omitempty"`
    22  	AvailabilityZone string `json:"availabilityZone,omitempty"`
    23  }
    24  
    25  func gatherAWS(util *Data, client *http.Client) error {
    26  	aws, err := getAWS(client)
    27  	if err != nil {
    28  		// Only return the error here if it is unexpected to prevent
    29  		// warning customers who aren't running AWS about a timeout.
    30  		if _, ok := err.(unexpectedAWSErr); ok {
    31  			return err
    32  		}
    33  		return nil
    34  	}
    35  	util.Vendors.AWS = aws
    36  
    37  	return nil
    38  }
    39  
    40  type unexpectedAWSErr struct{ e error }
    41  
    42  func (e unexpectedAWSErr) Error() string {
    43  	return fmt.Sprintf("unexpected AWS error: %v", e.e)
    44  }
    45  
    46  func getAWS(client *http.Client) (*aws, error) {
    47  	response, err := client.Get(awsEndpoint)
    48  	if err != nil {
    49  		// No unexpectedAWSErr here: A timeout is usually going to
    50  		// happen.
    51  		return nil, err
    52  	}
    53  	defer response.Body.Close()
    54  
    55  	if response.StatusCode != 200 {
    56  		return nil, unexpectedAWSErr{e: fmt.Errorf("response code %d", response.StatusCode)}
    57  	}
    58  
    59  	data, err := ioutil.ReadAll(response.Body)
    60  	if err != nil {
    61  		return nil, unexpectedAWSErr{e: err}
    62  	}
    63  	a := &aws{}
    64  	if err := json.Unmarshal(data, a); err != nil {
    65  		return nil, unexpectedAWSErr{e: err}
    66  	}
    67  
    68  	if err := a.validate(); err != nil {
    69  		return nil, unexpectedAWSErr{e: err}
    70  	}
    71  
    72  	return a, nil
    73  }
    74  
    75  func (a *aws) validate() (err error) {
    76  	a.InstanceID, err = normalizeValue(a.InstanceID)
    77  	if err != nil {
    78  		return fmt.Errorf("invalid instance ID: %v", err)
    79  	}
    80  
    81  	a.InstanceType, err = normalizeValue(a.InstanceType)
    82  	if err != nil {
    83  		return fmt.Errorf("invalid instance type: %v", err)
    84  	}
    85  
    86  	a.AvailabilityZone, err = normalizeValue(a.AvailabilityZone)
    87  	if err != nil {
    88  		return fmt.Errorf("invalid availability zone: %v", err)
    89  	}
    90  
    91  	return
    92  }