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
|
## specified, the plugin will attempt to retrieve the resource ID
|
||||||
## of the VM via the instance metadata service (optional if running
|
## of the VM via the instance metadata service (optional if running
|
||||||
## on an Azure VM with MSI)
|
## 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.
|
## Azure region to publish metrics against. Defaults to eastus.
|
||||||
## Leave blank to automatically query the region via MSI.
|
## Leave blank to automatically query the region via MSI.
|
||||||
#region = "useast"
|
#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
|
// 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())
|
expiresDuration := m.expiresAt.Sub(time.Now().UTC())
|
||||||
return expiresDuration
|
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
|
// 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:
|
// Acquire an MSI token. Documented at:
|
||||||
// https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/how-to-use-vm-token
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resource ID defaults to https://management.azure.com
|
|
||||||
if resourceID == "" {
|
|
||||||
resourceID = "https://management.azure.com"
|
|
||||||
}
|
|
||||||
|
|
||||||
msiParameters := url.Values{}
|
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")
|
msiParameters.Add("api-version", "2018-02-01")
|
||||||
|
|
||||||
// Client id is optional
|
// Client id is optional
|
||||||
|
@ -143,7 +129,7 @@ func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToke
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= 300 || resp.StatusCode < 200 {
|
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)
|
resp.StatusCode, resp.Status, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,41 +142,45 @@ func (a *AzureMonitor) getMsiToken(clientID string, resourceID string) (*msiToke
|
||||||
return &token, nil
|
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
|
// 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)
|
req, err := http.NewRequest("GET", vmInstanceMetadataURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error creating HTTP request")
|
return fmt.Errorf("Error creating HTTP request")
|
||||||
}
|
}
|
||||||
req.Header.Set("Metadata", "true")
|
req.Header.Set("Metadata", "true")
|
||||||
|
|
||||||
resp, err := a.client.Do(req)
|
resp, err := a.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
reply, err := ioutil.ReadAll(resp.Body)
|
reply, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= 300 || resp.StatusCode < 200 {
|
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)
|
resp.StatusCode, resp.Status, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata VirtualMachineMetadata
|
var metadata VirtualMachineMetadata
|
||||||
if err := json.Unmarshal(reply, &metadata); err != nil {
|
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)
|
|
||||||
|
|
||||||
}
|
|
|
@ -35,7 +35,7 @@ type AzureMonitor struct {
|
||||||
AzureClientSecret string `toml:"azure_client_secret"`
|
AzureClientSecret string `toml:"azure_client_secret"`
|
||||||
StringAsDimension bool `toml:"string_as_dimension"`
|
StringAsDimension bool `toml:"string_as_dimension"`
|
||||||
|
|
||||||
url string
|
url string
|
||||||
msiToken *msiToken
|
msiToken *msiToken
|
||||||
oauthConfig *adal.OAuthConfig
|
oauthConfig *adal.OAuthConfig
|
||||||
adalToken adal.OAuthTokenProvider
|
adalToken adal.OAuthTokenProvider
|
||||||
|
@ -51,11 +51,12 @@ type aggregate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultRegion string = "eastus"
|
defaultRegion string = "eastus"
|
||||||
|
defaultMSIResource string = "https://monitoring.azure.com/"
|
||||||
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"
|
||||||
urlTemplate string = "https://%s.monitoring.azure.com%s/metrics"
|
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 = `
|
var sampleConfig = `
|
||||||
|
@ -63,7 +64,7 @@ var sampleConfig = `
|
||||||
## specified, the plugin will attempt to retrieve the resource ID
|
## specified, the plugin will attempt to retrieve the resource ID
|
||||||
## of the VM via the instance metadata service (optional if running
|
## of the VM via the instance metadata service (optional if running
|
||||||
## on an Azure VM with MSI)
|
## 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.
|
## Azure region to publish metrics against. Defaults to eastus.
|
||||||
## Leave blank to automatically query the region via MSI.
|
## Leave blank to automatically query the region via MSI.
|
||||||
#region = "useast"
|
#region = "useast"
|
||||||
|
@ -110,35 +111,27 @@ func (a *AzureMonitor) Connect() error {
|
||||||
if a.AzureSubscriptionID == "" && a.AzureTenantID == "" && a.AzureClientID == "" && a.AzureClientSecret == "" {
|
if a.AzureSubscriptionID == "" && a.AzureTenantID == "" && a.AzureClientID == "" && a.AzureClientSecret == "" {
|
||||||
a.useMsi = true
|
a.useMsi = true
|
||||||
} else if a.AzureSubscriptionID == "" || a.AzureTenantID == "" || a.AzureClientID == "" || a.AzureClientSecret == "" {
|
} 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 !a.useMsi {
|
||||||
// If using direct AD authentication create the AD access client
|
// If using direct AD authentication create the AD access client
|
||||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, a.AzureTenantID)
|
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, a.AzureTenantID)
|
||||||
if err != nil {
|
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
|
a.oauthConfig = oauthConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the resource identifier
|
// Pull region and resource identifier
|
||||||
metadata, err := a.GetInstanceMetadata()
|
err := a.GetInstanceMetadata()
|
||||||
if err != nil {
|
if err != nil && a.ResourceID == "" && a.Region == "" {
|
||||||
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")
|
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.ResourceID = metadata.AzureResourceID
|
|
||||||
|
|
||||||
if a.Region == "" {
|
|
||||||
a.Region = metadata.Compute.Location
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.url := fmt.Sprintf(urlTemplate, a.Region, a.ResourceID)
|
|
||||||
|
|
||||||
// Validate credentials
|
|
||||||
err = a.validateCredentials()
|
err = a.validateCredentials()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("E! Unable to fetch authentication credentials: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Reset()
|
a.Reset()
|
||||||
|
@ -149,8 +142,8 @@ func (a *AzureMonitor) Connect() error {
|
||||||
func (a *AzureMonitor) validateCredentials() error {
|
func (a *AzureMonitor) validateCredentials() error {
|
||||||
if a.useMsi {
|
if a.useMsi {
|
||||||
// Check expiry on the token
|
// Check expiry on the token
|
||||||
if a.msiToken == nil || a.msiToken.ExpiresInDuration() < time.Minute {
|
if a.msiToken == nil || a.msiToken.expiresInDuration() < time.Minute {
|
||||||
msiToken, err := a.getMsiToken(a.AzureClientID, defaultMSIResource)
|
msiToken, err := a.getMsiToken(a.AzureClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -227,7 +220,7 @@ func (a *AzureMonitor) Write(metrics []telegraf.Metric) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.validateCredentials(); err != nil {
|
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))
|
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 {
|
if err != nil {
|
||||||
reply = 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)
|
resp.StatusCode, resp.Status, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,6 @@ import (
|
||||||
"github.com/influxdata/telegraf/metric"
|
"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
|
// MockMetrics returns a mock []telegraf.Metric object for using in unit tests
|
||||||
// of telegraf output sinks.
|
// of telegraf output sinks.
|
||||||
func getMockMetrics() []telegraf.Metric {
|
func getMockMetrics() []telegraf.Metric {
|
||||||
|
@ -55,35 +38,3 @@ func getTestMetric(value interface{}, name ...string) telegraf.Metric {
|
||||||
)
|
)
|
||||||
return pt
|
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