Skip to content

Commit

Permalink
Merge branch 'main' into rashmi/hash-fix-2
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole authored Jul 10, 2024
2 parents 3037589 + 1b4f045 commit b95cc82
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 18 deletions.
20 changes: 20 additions & 0 deletions .chloggen/azuremonitor-additional-authentication.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: receiver/azuremonitorreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add support for Managed Identity and Default Credential auth

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [31268, 33584]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
30 changes: 27 additions & 3 deletions receiver/azuremonitorreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The following settings are required:

The following settings are optional:

- `auth` (default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`.
- `auth` (default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`, `managed_identity`, `default_credentials`.
- `resource_groups` (default = none): Filter metrics for specific resource groups, not setting a value will scrape metrics for all resources in the subscription.
- `services` (default = none): Filter metrics for specific services, not setting a value will scrape metrics for all services integrated with Azure Monitor.
- `cache_resources` (default = 86400): List of resources will be cached for the provided amount of time in seconds.
Expand All @@ -43,9 +43,13 @@ Authenticating using workload identities requires following additional settings:
- `client_id`
- `federate_token_file`

Authenticating using managed identities has the following optional settings:

- `client_id`

### Example Configurations

Using Service Principal for authentication:
Using [Service Principal](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#service-principal-with-a-secret) for authentication:

```yaml
receivers:
Expand All @@ -65,7 +69,7 @@ receivers:
initial_delay: 1s
```
Using Azure Workload Identity for authentication:
Using [Azure Workload Identity](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#option-2-use-workload-identity) for authentication:
```yaml
receivers:
Expand All @@ -77,6 +81,26 @@ receivers:
federated_token_file: "${env:AZURE_FEDERATED_TOKEN_FILE}"
```
Using [Managed Identity](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#option-3-use-a-managed-identity) for authentication:
```yaml
receivers:
azuremonitor:
subscription_id: "${subscription_id}"
auth: "managed_identity"
client_id: "${env:AZURE_CLIENT_ID}"
```
Using [Environment Variables](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#option-1-define-environment-variables) for authentication:
```yaml
receivers:
azuremonitor:
subscription_id: "${subscription_id}"
auth: "default_credentials"
```
## Metrics
Details about the metrics scraped by this receiver can be found in [Supported metrics with Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/metrics-supported). This receiver adds the prefix "azure_" to all scraped metrics.
11 changes: 8 additions & 3 deletions receiver/azuremonitorreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,10 @@ type Config struct {
}

const (
servicePrincipal = "service_principal"
workloadIdentity = "workload_identity"
defaultCredentials = "default_credentials"
servicePrincipal = "service_principal"
workloadIdentity = "workload_identity"
managedIdentity = "managed_identity"
)

// Validate validates the configuration by checking for missing or invalid fields
Expand Down Expand Up @@ -282,8 +284,11 @@ func (c Config) Validate() (err error) {
if c.FederatedTokenFile == "" {
err = multierr.Append(err, errMissingFedTokenFile)
}

case managedIdentity:
case defaultCredentials:
default:
return fmt.Errorf("authentication %v is not supported. supported authentications include [%v,%v]", c.Authentication, servicePrincipal, workloadIdentity)
return fmt.Errorf("authentication %v is not supported. supported authentications include [%v,%v,%v,%v]", c.Authentication, servicePrincipal, workloadIdentity, managedIdentity, defaultCredentials)
}

if c.Cloud != azureCloud && c.Cloud != azureGovernmentCloud {
Expand Down
51 changes: 39 additions & 12 deletions receiver/azuremonitorreceiver/scraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ func newScraper(conf *Config, settings receiver.Settings) *azureScraper {
cfg: conf,
settings: settings.TelemetrySettings,
mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings),
azDefaultCredentialsFunc: azidentity.NewDefaultAzureCredential,
azIDCredentialsFunc: azidentity.NewClientSecretCredential,
azIDWorkloadFunc: azidentity.NewWorkloadIdentityCredential,
azManagedIdentityFunc: azidentity.NewManagedIdentityCredential,
armClientFunc: armresources.NewClient,
armMonitorDefinitionsClientFunc: armmonitor.NewMetricDefinitionsClient,
armMonitorMetricsClientFunc: armmonitor.NewMetricsClient,
Expand All @@ -104,8 +106,10 @@ type azureScraper struct {
resources map[string]*azureResource
resourcesUpdated time.Time
mb *metadata.MetricsBuilder
azDefaultCredentialsFunc func(options *azidentity.DefaultAzureCredentialOptions) (*azidentity.DefaultAzureCredential, error)
azIDCredentialsFunc func(string, string, string, *azidentity.ClientSecretCredentialOptions) (*azidentity.ClientSecretCredential, error)
azIDWorkloadFunc func(options *azidentity.WorkloadIdentityCredentialOptions) (*azidentity.WorkloadIdentityCredential, error)
azManagedIdentityFunc func(options *azidentity.ManagedIdentityCredentialOptions) (*azidentity.ManagedIdentityCredential, error)
armClientOptions *arm.ClientOptions
armClientFunc func(string, azcore.TokenCredential, *arm.ClientOptions) (*armresources.Client, error)
armMonitorDefinitionsClientFunc func(string, azcore.TokenCredential, *arm.ClientOptions) (*armmonitor.MetricDefinitionsClient, error)
Expand Down Expand Up @@ -134,18 +138,18 @@ func (s *azureScraper) getArmClientOptions() *arm.ClientOptions {
return &options
}

func (s *azureScraper) getArmClient() armClient {
client, _ := s.armClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client
func (s *azureScraper) getArmClient() (armClient, error) {
client, err := s.armClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client, err
}

type metricsDefinitionsClientInterface interface {
NewListPager(resourceURI string, options *armmonitor.MetricDefinitionsClientListOptions) *runtime.Pager[armmonitor.MetricDefinitionsClientListResponse]
}

func (s *azureScraper) getMetricsDefinitionsClient() metricsDefinitionsClientInterface {
client, _ := s.armMonitorDefinitionsClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client
func (s *azureScraper) getMetricsDefinitionsClient() (metricsDefinitionsClientInterface, error) {
client, err := s.armMonitorDefinitionsClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client, err
}

type metricsValuesClient interface {
Expand All @@ -154,9 +158,9 @@ type metricsValuesClient interface {
)
}

func (s *azureScraper) GetMetricsValuesClient() metricsValuesClient {
client, _ := s.armMonitorMetricsClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client
func (s *azureScraper) GetMetricsValuesClient() (metricsValuesClient, error) {
client, err := s.armMonitorMetricsClientFunc(s.cfg.SubscriptionID, s.cred, s.armClientOptions)
return client, err
}

func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) {
Expand All @@ -165,9 +169,18 @@ func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) {
}

s.armClientOptions = s.getArmClientOptions()
s.clientResources = s.getArmClient()
s.clientMetricsDefinitions = s.getMetricsDefinitionsClient()
s.clientMetricsValues = s.GetMetricsValuesClient()
s.clientResources, err = s.getArmClient()
if err != nil {
return err
}
s.clientMetricsDefinitions, err = s.getMetricsDefinitionsClient()
if err != nil {
return err
}
s.clientMetricsValues, err = s.GetMetricsValuesClient()
if err != nil {
return err
}

s.resources = map[string]*azureResource{}

Expand All @@ -176,6 +189,10 @@ func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) {

func (s *azureScraper) loadCredentials() (err error) {
switch s.cfg.Authentication {
case defaultCredentials:
if s.cred, err = s.azDefaultCredentialsFunc(nil); err != nil {
return err
}
case servicePrincipal:
if s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil); err != nil {
return err
Expand All @@ -184,6 +201,16 @@ func (s *azureScraper) loadCredentials() (err error) {
if s.cred, err = s.azIDWorkloadFunc(nil); err != nil {
return err
}
case managedIdentity:
var options *azidentity.ManagedIdentityCredentialOptions
if s.cfg.ClientID != "" {
options = &azidentity.ManagedIdentityCredentialOptions{
ID: azidentity.ClientID(s.cfg.ClientID),
}
}
if s.cred, err = s.azManagedIdentityFunc(options); err != nil {
return err
}
default:
return fmt.Errorf("unknown authentication %v", s.cfg.Authentication)
}
Expand Down
64 changes: 64 additions & 0 deletions receiver/azuremonitorreceiver/scraper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ func azIDWorkloadFuncMock(*azidentity.WorkloadIdentityCredentialOptions) (*azide
return &azidentity.WorkloadIdentityCredential{}, nil
}

func azManagedIdentityFuncMock(*azidentity.ManagedIdentityCredentialOptions) (*azidentity.ManagedIdentityCredential, error) {
return &azidentity.ManagedIdentityCredential{}, nil
}

func azDefaultCredentialsFuncMock(*azidentity.DefaultAzureCredentialOptions) (*azidentity.DefaultAzureCredential, error) {
return &azidentity.DefaultAzureCredential{}, nil
}

func armClientFuncMock(string, azcore.TokenCredential, *arm.ClientOptions) (*armresources.Client, error) {
return &armresources.Client{}, nil
}
Expand Down Expand Up @@ -137,6 +145,62 @@ func TestAzureScraperStart(t *testing.T) {
require.IsType(t, &azidentity.WorkloadIdentityCredential{}, s.cred)
},
},
{
name: "managed_identity",
testFunc: func(t *testing.T) {
customCfg := &Config{
ControllerConfig: cfg.ControllerConfig,
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
CacheResources: 24 * 60 * 60,
CacheResourcesDefinitions: 24 * 60 * 60,
MaximumNumberOfMetricsInACall: 20,
Services: monitorServices,
Authentication: managedIdentity,
}
s := &azureScraper{
cfg: customCfg,
azIDCredentialsFunc: azIDCredentialsFuncMock,
azManagedIdentityFunc: azManagedIdentityFuncMock,
armClientFunc: armClientFuncMock,
armMonitorDefinitionsClientFunc: armMonitorDefinitionsClientFuncMock,
armMonitorMetricsClientFunc: armMonitorMetricsClientFuncMock,
}

if err := s.start(context.Background(), componenttest.NewNopHost()); err != nil {
t.Errorf("azureScraper.start() error = %v", err)
}
require.NotNil(t, s.cred)
require.IsType(t, &azidentity.ManagedIdentityCredential{}, s.cred)
},
},
{
name: "default_credentials",
testFunc: func(t *testing.T) {
customCfg := &Config{
ControllerConfig: cfg.ControllerConfig,
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
CacheResources: 24 * 60 * 60,
CacheResourcesDefinitions: 24 * 60 * 60,
MaximumNumberOfMetricsInACall: 20,
Services: monitorServices,
Authentication: defaultCredentials,
}
s := &azureScraper{
cfg: customCfg,
azIDCredentialsFunc: azIDCredentialsFuncMock,
azDefaultCredentialsFunc: azDefaultCredentialsFuncMock,
armClientFunc: armClientFuncMock,
armMonitorDefinitionsClientFunc: armMonitorDefinitionsClientFuncMock,
armMonitorMetricsClientFunc: armMonitorMetricsClientFuncMock,
}

if err := s.start(context.Background(), componenttest.NewNopHost()); err != nil {
t.Errorf("azureScraper.start() error = %v", err)
}
require.NotNil(t, s.cred)
require.IsType(t, &azidentity.DefaultAzureCredential{}, s.cred)
},
},
}
for _, tt := range tests {
t.Run(tt.name, tt.testFunc)
Expand Down

0 comments on commit b95cc82

Please sign in to comment.