diff --git a/.chloggen/azuremonitor-workload-identity.yaml b/.chloggen/azuremonitor-workload-identity.yaml new file mode 100755 index 000000000000..f4e5365d8665 --- /dev/null +++ b/.chloggen/azuremonitor-workload-identity.yaml @@ -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 authenticating using AD workload identity + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [24451] + +# (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: diff --git a/receiver/azuremonitorreceiver/README.md b/receiver/azuremonitorreceiver/README.md index 39003cbf666f..9e585663ae7c 100644 --- a/receiver/azuremonitorreceiver/README.md +++ b/receiver/azuremonitorreceiver/README.md @@ -18,11 +18,9 @@ This receiver scrapes Azure Monitor API for resources metrics. The following settings are required: - `subscription_id` -- `tenant_id` -- `client_id` -- `client_secret` The following settings are optional: +- `auth` (default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`. - `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. @@ -30,8 +28,19 @@ The following settings are optional: - `maximum_number_of_metrics_in_a_call` (default = 20): Maximum number of metrics to fetch in per API call, current limit in Azure is 20 (as of 03/27/2023). - `initial_delay` (default = `1s`): defines how long this receiver waits before starting. -### Example Configuration +Authenticating using service principal requires following additional settings: +- `tenant_id` +- `client_id` +- `client_secret` + +Authenticating using workload identities requires following additional settings: +- `tenant_id` +- `client_id` +- `federate_token_file` + +### Example Configurations +Using Service Principal for authentication: ```yaml receivers: azuremonitor: @@ -49,6 +58,17 @@ receivers: initial_delay: 1s ``` +Using Azure Workload Identity for authentication: +```yaml +receivers: + azuremonitor: + subscription_id: "${subscription_id}" + auth: "workload_identity" + tenant_id: "${env:AZURE_TENANT_ID}" + client_id: "${env:AZURE_CLIENT_ID}" + federated_token_file: "${env:AZURE_FEDERATED_TOKEN_FILE}" +``` + ## 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. diff --git a/receiver/azuremonitorreceiver/config.go b/receiver/azuremonitorreceiver/config.go index a0baa7a8d6b6..cc6746f8370f 100644 --- a/receiver/azuremonitorreceiver/config.go +++ b/receiver/azuremonitorreceiver/config.go @@ -5,6 +5,7 @@ package azuremonitorreceiver // import "github.com/open-telemetry/opentelemetry- import ( "errors" + "fmt" "go.opentelemetry.io/collector/receiver/scraperhelper" "go.uber.org/multierr" @@ -18,6 +19,7 @@ var ( errMissingSubscriptionID = errors.New(`SubscriptionID" is not specified in config`) errMissingClientID = errors.New(`ClientID" is not specified in config`) errMissingClientSecret = errors.New(`ClientSecret" is not specified in config`) + errMissingFedTokenFile = errors.New(`FederatedTokenFile is not specified in config`) monitorServices = []string{ "Microsoft.EventGrid/eventSubscriptions", @@ -224,9 +226,11 @@ type Config struct { scraperhelper.ScraperControllerSettings `mapstructure:",squash"` MetricsBuilderConfig metadata.MetricsBuilderConfig `mapstructure:",squash"` SubscriptionID string `mapstructure:"subscription_id"` + Authentication string `mapstructure:"auth"` TenantID string `mapstructure:"tenant_id"` ClientID string `mapstructure:"client_id"` ClientSecret string `mapstructure:"client_secret"` + FederatedTokenFile string `mapstructure:"federated_token_file"` ResourceGroups []string `mapstructure:"resource_groups"` Services []string `mapstructure:"services"` CacheResources float64 `mapstructure:"cache_resources"` @@ -235,22 +239,44 @@ type Config struct { AppendTagsAsAttributes bool `mapstructure:"append_tags_as_attributes"` } +const ( + servicePrincipal = "service_principal" + workloadIdentity = "workload_identity" +) + // Validate validates the configuration by checking for missing or invalid fields func (c Config) Validate() (err error) { - if c.TenantID == "" { - err = multierr.Append(err, errMissingTenantID) - } - if c.SubscriptionID == "" { err = multierr.Append(err, errMissingSubscriptionID) } - if c.ClientID == "" { - err = multierr.Append(err, errMissingClientID) - } + switch c.Authentication { + case servicePrincipal: + if c.TenantID == "" { + err = multierr.Append(err, errMissingTenantID) + } + + if c.ClientID == "" { + err = multierr.Append(err, errMissingClientID) + } + + if c.ClientSecret == "" { + err = multierr.Append(err, errMissingClientSecret) + } + case workloadIdentity: + if c.TenantID == "" { + err = multierr.Append(err, errMissingTenantID) + } + + if c.ClientID == "" { + err = multierr.Append(err, errMissingClientID) + } - if c.ClientSecret == "" { - err = multierr.Append(err, errMissingClientSecret) + if c.FederatedTokenFile == "" { + err = multierr.Append(err, errMissingFedTokenFile) + } + default: + return fmt.Errorf("authentication %v is not supported. supported authentications include [%v,%v]", c.Authentication, servicePrincipal, workloadIdentity) } return diff --git a/receiver/azuremonitorreceiver/factory.go b/receiver/azuremonitorreceiver/factory.go index 6634a3c1f8f1..9aa4206030bb 100644 --- a/receiver/azuremonitorreceiver/factory.go +++ b/receiver/azuremonitorreceiver/factory.go @@ -41,6 +41,7 @@ func createDefaultConfig() component.Config { CacheResourcesDefinitions: 24 * 60 * 60, MaximumNumberOfMetricsInACall: 20, Services: monitorServices, + Authentication: servicePrincipal, } } diff --git a/receiver/azuremonitorreceiver/factory_test.go b/receiver/azuremonitorreceiver/factory_test.go index 7cf64d769199..edcd86493dac 100644 --- a/receiver/azuremonitorreceiver/factory_test.go +++ b/receiver/azuremonitorreceiver/factory_test.go @@ -44,6 +44,7 @@ func TestNewFactory(t *testing.T) { CacheResources: 24 * 60 * 60, CacheResourcesDefinitions: 24 * 60 * 60, MaximumNumberOfMetricsInACall: 20, + Authentication: servicePrincipal, } require.Equal(t, expectedCfg, factory.CreateDefaultConfig()) diff --git a/receiver/azuremonitorreceiver/go.mod b/receiver/azuremonitorreceiver/go.mod index 447849468488..0790b96aaad2 100644 --- a/receiver/azuremonitorreceiver/go.mod +++ b/receiver/azuremonitorreceiver/go.mod @@ -3,8 +3,8 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azurem go 1.20 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.8.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 github.com/google/go-cmp v0.5.9 @@ -21,14 +21,14 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/uuid v1.3.1 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -41,7 +41,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.86.0 // indirect - github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.86.0 // indirect diff --git a/receiver/azuremonitorreceiver/go.sum b/receiver/azuremonitorreceiver/go.sum index 84467697f698..fc403e39ea0a 100644 --- a/receiver/azuremonitorreceiver/go.sum +++ b/receiver/azuremonitorreceiver/go.sum @@ -1,18 +1,18 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 h1:gVXuXcWd1i4C2Ruxe321aU+IKGaStvGB/S90PUPB/W8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1/go.mod h1:DffdKW9RFqa5VgmsjUOsS7UE7eiA5iAvYUs63bhKQ0M= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 h1:T8quHYlUGyb/oqtSTwqlCr1ilJHrDv+ZtpSfo+hm1BU= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.8.0 h1:dKxKBzh+XIEoYNmx/c8HeiwghuRExXf61WmVotWESeA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.8.0/go.mod h1:kzRLpzzlw6eBUXE7eBw3oqfmKR/kxaHOk4+h9sAe6Yo= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= -github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 h1:oPdPEZFSbl7oSPEAIPMPBMUmiL+mqgzBJwM/9qYcwNg= -github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -51,7 +51,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -83,8 +83,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -231,8 +231,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -408,6 +408,7 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/receiver/azuremonitorreceiver/scraper.go b/receiver/azuremonitorreceiver/scraper.go index afa0deaf4337..887702057bf1 100644 --- a/receiver/azuremonitorreceiver/scraper.go +++ b/receiver/azuremonitorreceiver/scraper.go @@ -83,6 +83,7 @@ func newScraper(conf *Config, settings receiver.CreateSettings) *azureScraper { settings: settings.TelemetrySettings, mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings), azIDCredentialsFunc: azidentity.NewClientSecretCredential, + azIDWorkloadFunc: azidentity.NewWorkloadIdentityCredential, armClientFunc: armresources.NewClient, armMonitorDefinitionsClientFunc: armmonitor.NewMetricDefinitionsClient, armMonitorMetricsClientFunc: armmonitor.NewMetricsClient, @@ -103,6 +104,7 @@ type azureScraper struct { resourcesUpdated time.Time mb *metadata.MetricsBuilder azIDCredentialsFunc func(string, string, string, *azidentity.ClientSecretCredentialOptions) (*azidentity.ClientSecretCredential, error) + azIDWorkloadFunc func(options *azidentity.WorkloadIdentityCredentialOptions) (*azidentity.WorkloadIdentityCredential, error) armClientFunc func(string, azcore.TokenCredential, *arm.ClientOptions) (*armresources.Client, error) armMonitorDefinitionsClientFunc func(azcore.TokenCredential, *arm.ClientOptions) (*armmonitor.MetricDefinitionsClient, error) armMonitorMetricsClientFunc func(azcore.TokenCredential, *arm.ClientOptions) (*armmonitor.MetricsClient, error) @@ -139,8 +141,7 @@ func (s *azureScraper) GetMetricsValuesClient() MetricsValuesClient { } func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) { - s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil) - if err != nil { + if err = s.loadCredentials(); err != nil { return err } @@ -153,6 +154,22 @@ func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) { return } +func (s *azureScraper) loadCredentials() (err error) { + switch s.cfg.Authentication { + case servicePrincipal: + if s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil); err != nil { + return err + } + case workloadIdentity: + if s.cred, err = s.azIDWorkloadFunc(nil); err != nil { + return err + } + default: + return fmt.Errorf("unknown authentication %v", s.cfg.Authentication) + } + return nil +} + func (s *azureScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { s.getResources(ctx) diff --git a/receiver/azuremonitorreceiver/scraper_test.go b/receiver/azuremonitorreceiver/scraper_test.go index 12e8043340b1..3a88e6932a05 100644 --- a/receiver/azuremonitorreceiver/scraper_test.go +++ b/receiver/azuremonitorreceiver/scraper_test.go @@ -17,7 +17,6 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/receiver/receivertest" @@ -38,6 +37,10 @@ func azIDCredentialsFuncMock(string, string, string, *azidentity.ClientSecretCre return &azidentity.ClientSecretCredential{}, nil } +func azIDWorkloadFuncMock(*azidentity.WorkloadIdentityCredentialOptions) (*azidentity.WorkloadIdentityCredential, error) { + return &azidentity.WorkloadIdentityCredential{}, nil +} + func armClientFuncMock(string, azcore.TokenCredential, *arm.ClientOptions) (*armresources.Client, error) { return &armresources.Client{}, nil } @@ -51,49 +54,92 @@ func armMonitorMetricsClientFuncMock(azcore.TokenCredential, *arm.ClientOptions) } func TestAzureScraperStart(t *testing.T) { - type fields struct { - cfg *Config - } - type args struct { - ctx context.Context - host component.Host - } cfg := createDefaultConfig().(*Config) tests := []struct { - name string - fields fields - args args - wantErr bool + name string + testFunc func(*testing.T) }{ // TODO: Add test cases. { - name: "1st", - fields: fields{ - cfg: cfg, + name: "default", + testFunc: func(t *testing.T) { + s := &azureScraper{ + cfg: cfg, + azIDCredentialsFunc: azIDCredentialsFuncMock, + azIDWorkloadFunc: azIDWorkloadFuncMock, + 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.ClientSecretCredential{}, s.cred) }, - args: args{ - ctx: context.Background(), - host: componenttest.NewNopHost(), + }, + { + name: "service_principal", + testFunc: func(t *testing.T) { + customCfg := &Config{ + ScraperControllerSettings: cfg.ScraperControllerSettings, + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + CacheResources: 24 * 60 * 60, + CacheResourcesDefinitions: 24 * 60 * 60, + MaximumNumberOfMetricsInACall: 20, + Services: monitorServices, + Authentication: servicePrincipal, + } + s := &azureScraper{ + cfg: customCfg, + azIDCredentialsFunc: azIDCredentialsFuncMock, + azIDWorkloadFunc: azIDWorkloadFuncMock, + 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.ClientSecretCredential{}, s.cred) + }, + }, + { + name: "workload_identity", + testFunc: func(t *testing.T) { + customCfg := &Config{ + ScraperControllerSettings: cfg.ScraperControllerSettings, + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + CacheResources: 24 * 60 * 60, + CacheResourcesDefinitions: 24 * 60 * 60, + MaximumNumberOfMetricsInACall: 20, + Services: monitorServices, + Authentication: workloadIdentity, + } + s := &azureScraper{ + cfg: customCfg, + azIDCredentialsFunc: azIDCredentialsFuncMock, + azIDWorkloadFunc: azIDWorkloadFuncMock, + 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.WorkloadIdentityCredential{}, s.cred) }, - wantErr: false, }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &azureScraper{ - cfg: tt.fields.cfg, - azIDCredentialsFunc: azIDCredentialsFuncMock, - armClientFunc: armClientFuncMock, - armMonitorDefinitionsClientFunc: armMonitorDefinitionsClientFuncMock, - armMonitorMetricsClientFunc: armMonitorMetricsClientFuncMock, - } - - if err := s.start(tt.args.ctx, tt.args.host); (err != nil) != tt.wantErr { - t.Errorf("azureScraper.start() error = %v, wantErr %v", err, tt.wantErr) - } - }) + t.Run(tt.name, tt.testFunc) } }