Skip to content

Commit a6b0f06

Browse files
Add support for azureauth when batch api is enabled (open-telemetry#40886)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Azure auth was only enabled when batch api was set to false. This issue fixes that. <!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes open-telemetry#40872. <!--Describe what testing was performed and which tests were added.--> #### Testing Unit test was added. <!--Describe the documentation added.--> #### Documentation README updated with a note on versions. <!--Please delete paragraphs that you did not use before submitting.--> --------- Co-authored-by: Braydon Kains <[email protected]>
1 parent 0561223 commit a6b0f06

File tree

8 files changed

+211
-253
lines changed

8 files changed

+211
-253
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: azuremonitorreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for azureauth when batch api is enabled.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [40872]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

receiver/azuremonitorreceiver/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The following settings are required:
2525
The following settings are optional:
2626

2727
- `auth.authenticator`: Specifies the component ID to use to authenticate requests to Azure API. Use [azureauth extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/azureauthextension).
28-
- `credentials` (Deprecated [v0.126.0]: use `auth` instead)(default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`, `managed_identity`, `default_credentials`.
28+
- `credentials` (Deprecated since [v0.129.0]: use `auth` instead)(default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`, `managed_identity`, `default_credentials`.
2929
- `resource_groups` (default = none): Filter metrics for specific resource groups, not setting a value will scrape metrics for all resources in the subscription.
3030
- `services` (default = none): Filter metrics for specific services, not setting a value will scrape metrics for all services integrated with Azure Monitor.
3131
- `metrics` (default = none): Filter metrics by name and aggregations. Not setting a value will scrape all metrics and their aggregations.

receiver/azuremonitorreceiver/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ type Config struct {
263263
// useless.
264264
Authentication *AuthConfig `mapstructure:"auth"`
265265

266-
// Credentials is deprecated.
266+
// Deprecated: Credentials is deprecated.
267267
Credentials string `mapstructure:"credentials"`
268268
ClientID string `mapstructure:"client_id"`
269269
ClientSecret string `mapstructure:"client_secret"`
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package azuremonitorreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azuremonitorreceiver"
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
10+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
11+
"go.opentelemetry.io/collector/component"
12+
"go.uber.org/zap"
13+
)
14+
15+
func loadTokenProvider(host component.Host, idAuth component.ID) (azcore.TokenCredential, error) {
16+
authExtension, ok := host.GetExtensions()[idAuth]
17+
if !ok {
18+
return nil, fmt.Errorf("unknown azureauth extension %q", idAuth.String())
19+
}
20+
credential, ok := authExtension.(azcore.TokenCredential)
21+
if !ok {
22+
return nil, fmt.Errorf("extension %q does not implement azcore.TokenCredential", idAuth.String())
23+
}
24+
return credential, nil
25+
}
26+
27+
func loadCredentials(logger *zap.Logger, cfg *Config, host component.Host) (azcore.TokenCredential, error) {
28+
var cred azcore.TokenCredential
29+
var err error
30+
31+
if cfg.Authentication != nil {
32+
logger.Info("'auth.authenticator' will be used to get the token credential")
33+
if cred, err = loadTokenProvider(host, cfg.Authentication.AuthenticatorID); err != nil {
34+
return nil, err
35+
}
36+
return cred, nil
37+
}
38+
39+
logger.Warn("'credentials' is deprecated, use 'auth.authenticator' instead")
40+
switch cfg.Credentials {
41+
case defaultCredentials:
42+
if cred, err = azidentity.NewDefaultAzureCredential(nil); err != nil {
43+
return nil, err
44+
}
45+
case servicePrincipal:
46+
if cred, err = azidentity.NewClientSecretCredential(cfg.TenantID, cfg.ClientID, cfg.ClientSecret, nil); err != nil {
47+
return nil, err
48+
}
49+
case workloadIdentity:
50+
options := &azidentity.WorkloadIdentityCredentialOptions{
51+
ClientID: cfg.ClientID,
52+
TokenFilePath: cfg.FederatedTokenFile,
53+
TenantID: cfg.TenantID,
54+
}
55+
if cred, err = azidentity.NewWorkloadIdentityCredential(options); err != nil {
56+
return nil, err
57+
}
58+
case managedIdentity:
59+
var options *azidentity.ManagedIdentityCredentialOptions
60+
if cfg.ClientID != "" {
61+
options = &azidentity.ManagedIdentityCredentialOptions{
62+
ID: azidentity.ClientID(cfg.ClientID),
63+
}
64+
}
65+
if cred, err = azidentity.NewManagedIdentityCredential(options); err != nil {
66+
return nil, err
67+
}
68+
default:
69+
return nil, fmt.Errorf("unknown credentials %v", cfg.Credentials)
70+
}
71+
return cred, nil
72+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package azuremonitorreceiver
5+
6+
import (
7+
"testing"
8+
9+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
10+
"github.com/stretchr/testify/require"
11+
"go.opentelemetry.io/collector/component"
12+
"go.opentelemetry.io/collector/component/componenttest"
13+
"go.uber.org/zap"
14+
)
15+
16+
func TestLoadCredentials(t *testing.T) {
17+
tests := []struct {
18+
name string
19+
testFunc func(*testing.T)
20+
}{
21+
{
22+
name: "default_credentials",
23+
testFunc: func(t *testing.T) {
24+
cfg := &Config{
25+
Credentials: defaultCredentials,
26+
}
27+
cred, err := loadCredentials(zap.NewNop(), cfg, componenttest.NewNopHost())
28+
require.NoError(t, err)
29+
require.NotNil(t, cred)
30+
require.IsType(t, &azidentity.DefaultAzureCredential{}, cred)
31+
},
32+
},
33+
{
34+
name: "service_principal",
35+
testFunc: func(t *testing.T) {
36+
cfg := &Config{
37+
Credentials: servicePrincipal,
38+
TenantID: "test",
39+
ClientID: "test",
40+
ClientSecret: "test",
41+
}
42+
cred, err := loadCredentials(zap.NewNop(), cfg, componenttest.NewNopHost())
43+
require.NoError(t, err)
44+
require.NotNil(t, cred)
45+
require.IsType(t, &azidentity.ClientSecretCredential{}, cred)
46+
},
47+
},
48+
{
49+
name: "workload_identity",
50+
testFunc: func(t *testing.T) {
51+
cfg := &Config{
52+
Credentials: workloadIdentity,
53+
ClientID: "test",
54+
FederatedTokenFile: "test",
55+
TenantID: "test",
56+
}
57+
cred, err := loadCredentials(zap.NewNop(), cfg, componenttest.NewNopHost())
58+
require.NoError(t, err)
59+
require.NotNil(t, cred)
60+
require.IsType(t, &azidentity.WorkloadIdentityCredential{}, cred)
61+
},
62+
},
63+
{
64+
name: "managed_identity",
65+
testFunc: func(t *testing.T) {
66+
cfg := &Config{
67+
Credentials: managedIdentity,
68+
ClientID: "test",
69+
}
70+
cred, err := loadCredentials(zap.NewNop(), cfg, componenttest.NewNopHost())
71+
require.NoError(t, err)
72+
require.NotNil(t, cred)
73+
require.IsType(t, &azidentity.ManagedIdentityCredential{}, cred)
74+
},
75+
},
76+
{
77+
name: "auth",
78+
testFunc: func(t *testing.T) {
79+
cfg := &Config{
80+
Authentication: &AuthConfig{AuthenticatorID: component.MustNewID("test")},
81+
}
82+
_, err := loadCredentials(zap.NewNop(), cfg, componenttest.NewNopHost())
83+
require.ErrorContains(t, err, `unknown azureauth extension "test"`)
84+
},
85+
},
86+
}
87+
for _, tt := range tests {
88+
t.Run(tt.name, tt.testFunc)
89+
}
90+
}

receiver/azuremonitorreceiver/scraper.go

Lines changed: 10 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
1616
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
17-
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
1817
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor"
1918
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2"
2019
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
@@ -100,16 +99,12 @@ func (*timeWrapper) Now() time.Time {
10099

101100
func newScraper(conf *Config, settings receiver.Settings) *azureScraper {
102101
return &azureScraper{
103-
cfg: conf,
104-
settings: settings.TelemetrySettings,
105-
mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings),
106-
azDefaultCredentialsFunc: azidentity.NewDefaultAzureCredential,
107-
azIDCredentialsFunc: azidentity.NewClientSecretCredential,
108-
azIDWorkloadFunc: azidentity.NewWorkloadIdentityCredential,
109-
azManagedIdentityFunc: azidentity.NewManagedIdentityCredential,
110-
mutex: &sync.Mutex{},
111-
time: &timeWrapper{},
112-
clientOptionsResolver: newClientOptionsResolver(conf.Cloud),
102+
cfg: conf,
103+
settings: settings.TelemetrySettings,
104+
mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings),
105+
mutex: &sync.Mutex{},
106+
time: &timeWrapper{},
107+
clientOptionsResolver: newClientOptionsResolver(conf.Cloud),
113108
}
114109
}
115110

@@ -121,21 +116,17 @@ type azureScraper struct {
121116
// resources on which we'll collect metrics. Stored by resource id and subscription id.
122117
resources map[string]map[string]*azureResource
123118
// subscriptions on which we'll look up resources. Stored by subscription id.
124-
subscriptions map[string]*azureSubscription
125-
subscriptionsUpdated time.Time
126-
mb *metadata.MetricsBuilder
127-
azDefaultCredentialsFunc func(options *azidentity.DefaultAzureCredentialOptions) (*azidentity.DefaultAzureCredential, error)
128-
azIDCredentialsFunc func(string, string, string, *azidentity.ClientSecretCredentialOptions) (*azidentity.ClientSecretCredential, error)
129-
azIDWorkloadFunc func(options *azidentity.WorkloadIdentityCredentialOptions) (*azidentity.WorkloadIdentityCredential, error)
130-
azManagedIdentityFunc func(options *azidentity.ManagedIdentityCredentialOptions) (*azidentity.ManagedIdentityCredential, error)
119+
subscriptions map[string]*azureSubscription
120+
subscriptionsUpdated time.Time
121+
mb *metadata.MetricsBuilder
131122

132123
mutex *sync.Mutex
133124
time timeNowIface
134125
clientOptionsResolver ClientOptionsResolver
135126
}
136127

137128
func (s *azureScraper) start(_ context.Context, host component.Host) (err error) {
138-
if err = s.loadCredentials(host); err != nil {
129+
if s.cred, err = loadCredentials(s.settings.Logger, s.cfg, host); err != nil {
139130
return err
140131
}
141132

@@ -158,57 +149,6 @@ func (s *azureScraper) unloadSubscription(id string) {
158149
delete(s.subscriptions, id)
159150
}
160151

161-
func loadTokenProvider(host component.Host, idAuth component.ID) (azcore.TokenCredential, error) {
162-
authExtension, ok := host.GetExtensions()[idAuth]
163-
if !ok {
164-
return nil, fmt.Errorf("unknown azureauth extension %q", idAuth.String())
165-
}
166-
credential, ok := authExtension.(azcore.TokenCredential)
167-
if !ok {
168-
return nil, fmt.Errorf("extension %q does not implement azcore.TokenCredential", idAuth.String())
169-
}
170-
return credential, nil
171-
}
172-
173-
func (s *azureScraper) loadCredentials(host component.Host) (err error) {
174-
if s.cfg.Authentication != nil {
175-
s.settings.Logger.Info("'auth.authenticator' will be used to get the token credential")
176-
if s.cred, err = loadTokenProvider(host, s.cfg.Authentication.AuthenticatorID); err != nil {
177-
return err
178-
}
179-
return nil
180-
}
181-
182-
s.settings.Logger.Warn("'credentials' is deprecated, use 'auth.authenticator' instead")
183-
switch s.cfg.Credentials {
184-
case defaultCredentials:
185-
if s.cred, err = s.azDefaultCredentialsFunc(nil); err != nil {
186-
return err
187-
}
188-
case servicePrincipal:
189-
if s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil); err != nil {
190-
return err
191-
}
192-
case workloadIdentity:
193-
if s.cred, err = s.azIDWorkloadFunc(nil); err != nil {
194-
return err
195-
}
196-
case managedIdentity:
197-
var options *azidentity.ManagedIdentityCredentialOptions
198-
if s.cfg.ClientID != "" {
199-
options = &azidentity.ManagedIdentityCredentialOptions{
200-
ID: azidentity.ClientID(s.cfg.ClientID),
201-
}
202-
}
203-
if s.cred, err = s.azManagedIdentityFunc(options); err != nil {
204-
return err
205-
}
206-
default:
207-
return fmt.Errorf("unknown credentials %v", s.cfg.Credentials)
208-
}
209-
return nil
210-
}
211-
212152
func (s *azureScraper) scrape(ctx context.Context) (pmetric.Metrics, error) {
213153
s.getSubscriptions(ctx)
214154

0 commit comments

Comments
 (0)