Additional code cleanup
This commit is contained in:
parent
78e5f52966
commit
4b8d0ad35d
|
@ -49,7 +49,7 @@ The resourceId used for Azure Monitor metrics.
|
|||
## specified, the plugin will attempt to retrieve the resource ID
|
||||
## of the VM via the instance metadata service (optional if running
|
||||
## on an Azure VM with MSI)
|
||||
#resourceId = "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<vm-name>"
|
||||
#resource_id = "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.Compute/virtualMachines/<vm_name>"
|
||||
## Azure region to publish metrics against. Defaults to eastus.
|
||||
## Leave blank to automatically query the region via MSI.
|
||||
#region = "useast"
|
||||
|
|
|
@ -80,24 +80,14 @@ func (m *msiToken) parseTimes() {
|
|||
}
|
||||
}
|
||||
|
||||
// ExpiresAt is the time at which the token expires
|
||||
func (m *msiToken) ExpiresAt() time.Time {
|
||||
return m.expiresAt
|
||||
}
|
||||
|
||||
// ExpiresInDuration returns the duration until the token expires
|
||||
func (m *msiToken) ExpiresInDuration() time.Duration {
|
||||
func (m *msiToken) expiresInDuration() time.Duration {
|
||||
expiresDuration := m.expiresAt.Sub(time.Now().UTC())
|
||||
return expiresDuration
|
||||
}
|
||||
|
||||
// NotBeforeTime returns the time at which the token becomes valid
|
||||
func (m *msiToken) NotBeforeTime() time.Time {
|
||||
return m.notBefore
|
||||
}
|
||||
|
||||
// GetMsiToken retrieves a managed service identity token from the specified port on the local VM
|
||||
func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToken, error) {
|
||||
func (a *AzureMonitor) getMsiToken(clientID string) (*msiToken, error) {
|
||||
// Acquire an MSI token. Documented at:
|
||||
// https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/how-to-use-vm-token
|
||||
//
|
||||
|
@ -110,13 +100,9 @@ func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToke
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Resource ID defaults to https://management.azure.com
|
||||
if resourceID == "" {
|
||||
resourceID = "https://management.azure.com"
|
||||
}
|
||||
|
||||
msiParameters := url.Values{}
|
||||
msiParameters.Add("resource", resourceID)
|
||||
// Resource ID defaults to https://management.azure.com
|
||||
msiParameters.Add("resource", defaultMSIResource)
|
||||
msiParameters.Add("api-version", "2018-02-01")
|
||||
|
||||
// Client id is optional
|
||||
|
@ -143,7 +129,7 @@ func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToke
|
|||
}
|
||||
|
||||
if resp.StatusCode >= 300 || resp.StatusCode < 200 {
|
||||
return nil, fmt.Errorf("Post Error. HTTP response code:%d message:%s, content: %s",
|
||||
return nil, fmt.Errorf("E! Get Error. %d HTTP response: %s response body: %s",
|
||||
resp.StatusCode, resp.Status, reply)
|
||||
}
|
||||
|
||||
|
@ -156,41 +142,45 @@ func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToke
|
|||
return &token, nil
|
||||
}
|
||||
|
||||
const (
|
||||
vmInstanceMetadataURL = "http://169.254.169.254/metadata/instance?api-version=2017-12-01"
|
||||
msiInstanceMetadataURL = "http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
)
|
||||
|
||||
// GetInstanceMetadata retrieves metadata about the current Azure VM
|
||||
func (a *AzureMonitor) GetInstanceMetadata() (*VirtualMachineMetadata, error) {
|
||||
func (a *AzureMonitor) GetInstanceMetadata() error {
|
||||
req, err := http.NewRequest("GET", vmInstanceMetadataURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating HTTP request")
|
||||
return fmt.Errorf("Error creating HTTP request")
|
||||
}
|
||||
req.Header.Set("Metadata", "true")
|
||||
|
||||
resp, err := a.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
reply, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 300 || resp.StatusCode < 200 {
|
||||
return nil, fmt.Errorf("Post Error. HTTP response code:%d message:%s reply:\n%s",
|
||||
return fmt.Errorf("Post Error. HTTP response code:%d message:%s reply:\n%s",
|
||||
resp.StatusCode, resp.Status, reply)
|
||||
}
|
||||
|
||||
var metadata VirtualMachineMetadata
|
||||
if err := json.Unmarshal(reply, &metadata); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
metadata.AzureResourceID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s",
|
||||
metadata.Compute.SubscriptionID, metadata.Compute.ResourceGroupName, metadata.Compute.Name)
|
||||
|
||||
return &metadata, nil
|
||||
if a.ResourceID == "" {
|
||||
a.ResourceID = fmt.Sprintf(resourceIDTemplate,
|
||||
metadata.Compute.SubscriptionID, metadata.Compute.ResourceGroupName, metadata.Compute.Name)
|
||||
}
|
||||
|
||||
if a.Region == "" {
|
||||
a.Region = metadata.Compute.Location
|
||||
}
|
||||
|
||||
a.url = fmt.Sprintf(urlTemplate, a.Region, a.ResourceID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package azuremonitor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetMetadata(t *testing.T) {
|
||||
azureMetadata := &AzureMonitor{}
|
||||
metadata, err := azureMetadata.GetInstanceMetadata()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, metadata)
|
||||
require.NotEmpty(t, metadata.AzureResourceID)
|
||||
require.NotEmpty(t, metadata.Compute.Location)
|
||||
|
||||
// if err != nil {
|
||||
// t.Logf("could not get metadata: %v\n", err)
|
||||
// } else {
|
||||
// t.Logf("resource id \n%s", metadata.AzureResourceID)
|
||||
// t.Logf("metadata is \n%v", metadata)
|
||||
// }
|
||||
|
||||
//fmt.Printf("metadata is \n%v", metadata)
|
||||
}
|
||||
|
||||
func TestGetTOKEN(t *testing.T) {
|
||||
azureMetadata := &AzureMonitor{}
|
||||
|
||||
resourceID := "https://ingestion.monitor.azure.com/"
|
||||
token, err := azureMetadata.getMsiToken("", resourceID)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token.AccessToken)
|
||||
require.EqualValues(t, token.Resource, resourceID)
|
||||
|
||||
t.Logf("token is %+v\n", token)
|
||||
t.Logf("expiry time is %s\n", token.ExpiresAt().Format(time.RFC3339))
|
||||
t.Logf("expiry duration is %s\n", token.ExpiresInDuration().String())
|
||||
t.Logf("resource is %s\n", token.Resource)
|
||||
|
||||
}
|
|
@ -52,10 +52,11 @@ type aggregate struct {
|
|||
|
||||
const (
|
||||
defaultRegion string = "eastus"
|
||||
|
||||
defaultMSIResource string = "https://monitoring.azure.com/"
|
||||
|
||||
urlTemplate string = "https://%s.monitoring.azure.com%s/metrics"
|
||||
resourceIDTemplate string = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s"
|
||||
vmInstanceMetadataURL string = "http://169.254.169.254/metadata/instance?api-version=2017-12-01"
|
||||
msiInstanceMetadataURL string = "http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
)
|
||||
|
||||
var sampleConfig = `
|
||||
|
@ -63,7 +64,7 @@ var sampleConfig = `
|
|||
## specified, the plugin will attempt to retrieve the resource ID
|
||||
## of the VM via the instance metadata service (optional if running
|
||||
## on an Azure VM with MSI)
|
||||
#resource_id = "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<vm-name>"
|
||||
#resource_id = "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.Compute/virtualMachines/<vm_name>"
|
||||
## Azure region to publish metrics against. Defaults to eastus.
|
||||
## Leave blank to automatically query the region via MSI.
|
||||
#region = "useast"
|
||||
|
@ -110,35 +111,27 @@ func (a *AzureMonitor) Connect() error {
|
|||
if a.AzureSubscriptionID == "" && a.AzureTenantID == "" && a.AzureClientID == "" && a.AzureClientSecret == "" {
|
||||
a.useMsi = true
|
||||
} else if a.AzureSubscriptionID == "" || a.AzureTenantID == "" || a.AzureClientID == "" || a.AzureClientSecret == "" {
|
||||
return fmt.Errorf("Must provide values for azureSubscription, azureTenant, azureClient and azureClientSecret, or leave all blank to default to MSI")
|
||||
return fmt.Errorf("E! Must provide values for azure_subscription, azure_tenant, azure_client and azure_client_secret, or leave all blank to default to MSI")
|
||||
}
|
||||
|
||||
if !a.useMsi {
|
||||
// If using direct AD authentication create the AD access client
|
||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, a.AzureTenantID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not initialize AD client: %s", err)
|
||||
return fmt.Errorf("E! Could not initialize AD client: %s", err)
|
||||
}
|
||||
a.oauthConfig = oauthConfig
|
||||
}
|
||||
|
||||
// Validate the resource identifier
|
||||
metadata, err := a.GetInstanceMetadata()
|
||||
if err != nil {
|
||||
return fmt.Errorf("No resource id specified, and Azure Instance metadata service not available. If not running on an Azure VM, provide a value for resourceId")
|
||||
}
|
||||
a.ResourceID = metadata.AzureResourceID
|
||||
|
||||
if a.Region == "" {
|
||||
a.Region = metadata.Compute.Location
|
||||
// Pull region and resource identifier
|
||||
err := a.GetInstanceMetadata()
|
||||
if err != nil && a.ResourceID == "" && a.Region == "" {
|
||||
return fmt.Errorf("E! No resource id specified, and Azure Instance metadata service not available. If not running on an Azure VM, provide a value for resource_id")
|
||||
}
|
||||
|
||||
a.url := fmt.Sprintf(urlTemplate, a.Region, a.ResourceID)
|
||||
|
||||
// Validate credentials
|
||||
err = a.validateCredentials()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("E! Unable to fetch authentication credentials: %v", err)
|
||||
}
|
||||
|
||||
a.Reset()
|
||||
|
@ -149,8 +142,8 @@ func (a *AzureMonitor) Connect() error {
|
|||
func (a *AzureMonitor) validateCredentials() error {
|
||||
if a.useMsi {
|
||||
// Check expiry on the token
|
||||
if a.msiToken == nil || a.msiToken.ExpiresInDuration() < time.Minute {
|
||||
msiToken, err := a.getMsiToken(a.AzureClientID, defaultMSIResource)
|
||||
if a.msiToken == nil || a.msiToken.expiresInDuration() < time.Minute {
|
||||
msiToken, err := a.getMsiToken(a.AzureClientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -227,7 +220,7 @@ func (a *AzureMonitor) Write(metrics []telegraf.Metric) error {
|
|||
}
|
||||
|
||||
if err := a.validateCredentials(); err != nil {
|
||||
return fmt.Errorf("Error authenticating: %v", err)
|
||||
return fmt.Errorf("E! Unable to fetch authentication credentials: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", a.url, bytes.NewBuffer(body))
|
||||
|
@ -249,7 +242,7 @@ func (a *AzureMonitor) Write(metrics []telegraf.Metric) error {
|
|||
if err != nil {
|
||||
reply = nil
|
||||
}
|
||||
return fmt.Errorf("Post Error. HTTP response code:%d message:%s reply:\n%s",
|
||||
return fmt.Errorf("E! Get Error. %d HTTP response: %s response body: %s",
|
||||
resp.StatusCode, resp.Status, reply)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,23 +7,6 @@ import (
|
|||
"github.com/influxdata/telegraf/metric"
|
||||
)
|
||||
|
||||
// func TestDefaultConnectAndWrite(t *testing.T) {
|
||||
// if testing.Short() {
|
||||
// t.Skip("Skipping integration test in short mode")
|
||||
// }
|
||||
|
||||
// // Test with all defaults (MSI+IMS)
|
||||
// azmon := &AzureMonitor{}
|
||||
|
||||
// // Verify that we can connect to Log Analytics
|
||||
// err := azmon.Connect()
|
||||
// require.NoError(t, err)
|
||||
|
||||
// // Verify that we can write a metric to Log Analytics
|
||||
// err = azmon.Write(testutil.MockMetrics())
|
||||
// require.NoError(t, err)
|
||||
// }
|
||||
|
||||
// MockMetrics returns a mock []telegraf.Metric object for using in unit tests
|
||||
// of telegraf output sinks.
|
||||
func getMockMetrics() []telegraf.Metric {
|
||||
|
@ -55,35 +38,3 @@ func getTestMetric(value interface{}, name ...string) telegraf.Metric {
|
|||
)
|
||||
return pt
|
||||
}
|
||||
|
||||
// func TestPostData(t *testing.T) {
|
||||
// azmon := &AzureMonitor{
|
||||
// Region: "eastus",
|
||||
// }
|
||||
// err := azmon.Connect()
|
||||
|
||||
// metrics := getMockMetrics()
|
||||
// t.Logf("mock metrics are %+v\n", metrics)
|
||||
// // metricsList, err := azmon.add(&metrics[0])
|
||||
// for _, m := range metrics {
|
||||
// azmon.Add(m)
|
||||
// }
|
||||
|
||||
// jsonBytes, err := json.Marshal(azmon.cache)
|
||||
// t.Logf("json content is:\n----------\n%s\n----------\n", string(jsonBytes))
|
||||
|
||||
// req, err := azmon.postData(&jsonBytes)
|
||||
// if err != nil {
|
||||
// // t.Logf("Error publishing metrics %s", err)
|
||||
// t.Logf("url is %+v\n", req.URL)
|
||||
// // t.Logf("failed request is %+v\n", req)
|
||||
|
||||
// // raw, err := httputil.DumpRequestOut(req, true)
|
||||
// // if err != nil {
|
||||
// // t.Logf("Request detail is \n%s\n", string(raw))
|
||||
// // } else {
|
||||
// // t.Logf("could not dump request: %s\n", err)
|
||||
// // }
|
||||
// }
|
||||
// require.NoError(t, err)
|
||||
// }
|
||||
|
|
Loading…
Reference in New Issue