github.com/newrelic/go-agent@v3.26.0+incompatible/internal/utilization/azure.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  	azureHostname     = "169.254.169.254"
    15  	azureEndpointPath = "/metadata/instance/compute?api-version=2017-03-01"
    16  	azureEndpoint     = "http://" + azureHostname + azureEndpointPath
    17  )
    18  
    19  type azure struct {
    20  	Location string `json:"location,omitempty"`
    21  	Name     string `json:"name,omitempty"`
    22  	VMID     string `json:"vmId,omitempty"`
    23  	VMSize   string `json:"vmSize,omitempty"`
    24  }
    25  
    26  func gatherAzure(util *Data, client *http.Client) error {
    27  	az, err := getAzure(client)
    28  	if err != nil {
    29  		// Only return the error here if it is unexpected to prevent
    30  		// warning customers who aren't running Azure about a timeout.
    31  		if _, ok := err.(unexpectedAzureErr); ok {
    32  			return err
    33  		}
    34  		return nil
    35  	}
    36  	util.Vendors.Azure = az
    37  
    38  	return nil
    39  }
    40  
    41  type unexpectedAzureErr struct{ e error }
    42  
    43  func (e unexpectedAzureErr) Error() string {
    44  	return fmt.Sprintf("unexpected Azure error: %v", e.e)
    45  }
    46  
    47  func getAzure(client *http.Client) (*azure, error) {
    48  	req, err := http.NewRequest("GET", azureEndpoint, nil)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	req.Header.Add("Metadata", "true")
    53  
    54  	response, err := client.Do(req)
    55  	if err != nil {
    56  		// No unexpectedAzureErr here: a timeout isusually going to
    57  		// happen.
    58  		return nil, err
    59  	}
    60  	defer response.Body.Close()
    61  
    62  	if response.StatusCode != 200 {
    63  		return nil, unexpectedAzureErr{e: fmt.Errorf("response code %d", response.StatusCode)}
    64  	}
    65  
    66  	data, err := ioutil.ReadAll(response.Body)
    67  	if err != nil {
    68  		return nil, unexpectedAzureErr{e: err}
    69  	}
    70  
    71  	az := &azure{}
    72  	if err := json.Unmarshal(data, az); err != nil {
    73  		return nil, unexpectedAzureErr{e: err}
    74  	}
    75  
    76  	if err := az.validate(); err != nil {
    77  		return nil, unexpectedAzureErr{e: err}
    78  	}
    79  
    80  	return az, nil
    81  }
    82  
    83  func (az *azure) validate() (err error) {
    84  	az.Location, err = normalizeValue(az.Location)
    85  	if err != nil {
    86  		return fmt.Errorf("Invalid location: %v", err)
    87  	}
    88  
    89  	az.Name, err = normalizeValue(az.Name)
    90  	if err != nil {
    91  		return fmt.Errorf("Invalid name: %v", err)
    92  	}
    93  
    94  	az.VMID, err = normalizeValue(az.VMID)
    95  	if err != nil {
    96  		return fmt.Errorf("Invalid VM ID: %v", err)
    97  	}
    98  
    99  	az.VMSize, err = normalizeValue(az.VMSize)
   100  	if err != nil {
   101  		return fmt.Errorf("Invalid VM size: %v", err)
   102  	}
   103  
   104  	return
   105  }