Skip to content

Commit

Permalink
Additional code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
gunnaraasen committed May 24, 2018
1 parent 78e5f52 commit 4b8d0ad
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 153 deletions.
2 changes: 1 addition & 1 deletion plugins/outputs/azuremonitor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
56 changes: 23 additions & 33 deletions plugins/outputs/azuremonitor/azuremetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand All @@ -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
Expand All @@ -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)
}

Expand All @@ -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
}
44 changes: 0 additions & 44 deletions plugins/outputs/azuremonitor/azuremetadata_test.go

This file was deleted.

45 changes: 19 additions & 26 deletions plugins/outputs/azuremonitor/azuremonitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type AzureMonitor struct {
AzureClientSecret string `toml:"azure_client_secret"`
StringAsDimension bool `toml:"string_as_dimension"`

url string
url string
msiToken *msiToken
oauthConfig *adal.OAuthConfig
adalToken adal.OAuthTokenProvider
Expand All @@ -51,19 +51,20 @@ type aggregate struct {
}

const (
defaultRegion string = "eastus"

defaultMSIResource string = "https://monitoring.azure.com/"

urlTemplate string = "https://%s.monitoring.azure.com%s/metrics"
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 = `
## The resource ID against which metric will be logged. If not
## 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"
Expand Down Expand Up @@ -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")
// 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.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()
if err != nil {
return err
return fmt.Errorf("E! Unable to fetch authentication credentials: %v", err)
}

a.Reset()
Expand All @@ -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
}
Expand Down Expand Up @@ -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))
Expand All @@ -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)
}

Expand Down
49 changes: 0 additions & 49 deletions plugins/outputs/azuremonitor/azuremonitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
// }

0 comments on commit 4b8d0ad

Please sign in to comment.