diff --git a/docs/sources/reference/components/database_observability/database_observability.mysql.md b/docs/sources/reference/components/database_observability/database_observability.mysql.md index 46aad7b7fed..1aadce499d0 100644 --- a/docs/sources/reference/components/database_observability/database_observability.mysql.md +++ b/docs/sources/reference/components/database_observability/database_observability.mysql.md @@ -55,6 +55,7 @@ You can use the following blocks with `database_observability.mysql`: |--------------------------------------|---------------------------------------------------|----------| | [`cloud_provider`][cloud_provider] | Provide Cloud Provider information. | no | | `cloud_provider` > [`aws`][aws] | Provide AWS database host information. | no | +| `cloud_provider` > [`azure`][azure] | Provide Azure database host information. | no | | [`setup_consumers`][setup_consumers] | Configure the `setup_consumers` collector. | no | | [`setup_actors`][setup_actors] | Configure the `setup_actors` collector. | no | | [`query_details`][query_details] | Configure the queries collector. | no | @@ -69,6 +70,7 @@ For example, `cloud_provider` > `aws` refers to a `aws` block defined inside an [cloud_provider]: #cloud_provider [aws]: #aws +[azure]: #azure [setup_consumers]: #setup_consumers [query_details]: #query_details [schema_details]: #schema_details @@ -94,6 +96,16 @@ The `aws` block supplies the [ARN](https://docs.aws.amazon.com/IAM/latest/UserGu |-------|----------|---------------------------------------------------------|---------|----------| | `arn` | `string` | The ARN associated with the database under observation. | | yes | +### `azure` + +The `azure` block supplies the identifying information for the database being monitored. + +| Name | Type | Description | Default | Required | +|-------------------|----------|------------------------------------------------------|---------|----------| +| `subscription_id` | `string` | The Subscription ID for your Azure account. | | yes | +| `resource_group` | `string` | The Resource Group that holds the database resource. | | yes | +| `server_name` | `string` | The database server name. | | no | + ### `setup_consumers` | Name | Type | Description | Default | Required | diff --git a/docs/sources/reference/components/database_observability/database_observability.postgres.md b/docs/sources/reference/components/database_observability/database_observability.postgres.md index 4d46112095b..ed45544a439 100644 --- a/docs/sources/reference/components/database_observability/database_observability.postgres.md +++ b/docs/sources/reference/components/database_observability/database_observability.postgres.md @@ -51,6 +51,7 @@ You can use the following blocks with `database_observability.postgres`: |------------------------------------|---------------------------------------------------|----------| | [`cloud_provider`][cloud_provider] | Provide Cloud Provider information. | no | | `cloud_provider` > [`aws`][aws] | Provide AWS database host information. | no | +| `cloud_provider` > [`azure`][azure] | Provide Azure database host information. | no | | [`query_details`][query_details] | Configure the queries collector. | no | | [`query_samples`][query_samples] | Configure the query samples collector. | no | | [`schema_details`][schema_details] | Configure the schema and table details collector. | no | @@ -62,6 +63,7 @@ For example, `cloud_provider` > `aws` refers to a `aws` block defined inside an [cloud_provider]: #cloud_provider [aws]: #aws +[azure]: #azure [query_details]: #query_details [query_samples]: #query_samples [schema_details]: #schema_details @@ -84,6 +86,16 @@ The `aws` block supplies the [ARN](https://docs.aws.amazon.com/IAM/latest/UserGu |-------|----------|---------------------------------------------------------|---------|----------| | `arn` | `string` | The ARN associated with the database under observation. | | yes | +### `azure` + +The `azure` block supplies the identifying information for the database being monitored. + +| Name | Type | Description | Default | Required | +|-------------------|----------|------------------------------------------------------|---------|----------| +| `subscription_id` | `string` | The Subscription ID for your Azure account. | | yes | +| `resource_group` | `string` | The Resource Group that holds the database resource. | | yes | +| `server_name` | `string` | The database server name. | | no | + ### `query_details` | Name | Type | Description | Default | Required | diff --git a/internal/component/database_observability/cloud_provider.go b/internal/component/database_observability/cloud_provider.go index 60b162d09ab..1767d95e208 100644 --- a/internal/component/database_observability/cloud_provider.go +++ b/internal/component/database_observability/cloud_provider.go @@ -12,6 +12,9 @@ type CloudProvider struct { type AWSCloudProviderInfo struct { ARN arn.ARN } + type AzureCloudProviderInfo struct { - Resource string + SubscriptionID string + ResourceGroup string + ServerName string } diff --git a/internal/component/database_observability/mysql/cloud_provider.go b/internal/component/database_observability/mysql/cloud_provider.go index e5a0925eaea..f2de8834588 100644 --- a/internal/component/database_observability/mysql/cloud_provider.go +++ b/internal/component/database_observability/mysql/cloud_provider.go @@ -27,6 +27,13 @@ func populateCloudProviderFromConfig(config *CloudProvider) (*database_observabi ARN: arn, } } + if config.Azure != nil { + cloudProvider.Azure = &database_observability.AzureCloudProviderInfo{ + SubscriptionID: config.Azure.SubscriptionID, + ResourceGroup: config.Azure.ResourceGroup, + ServerName: config.Azure.ServerName, + } + } return &cloudProvider, nil } @@ -53,7 +60,7 @@ func populateCloudProviderFromDSN(dsn string) (*database_observability.CloudProv } else if strings.HasSuffix(host, "mysql.database.azure.com") { if matches := azureRegex.FindStringSubmatch(host); len(matches) >= 2 { cloudProvider.Azure = &database_observability.AzureCloudProviderInfo{ - Resource: matches[1], + ServerName: matches[1], } } } diff --git a/internal/component/database_observability/mysql/cloud_provider_test.go b/internal/component/database_observability/mysql/cloud_provider_test.go index ef4414123b1..842205172b5 100644 --- a/internal/component/database_observability/mysql/cloud_provider_test.go +++ b/internal/component/database_observability/mysql/cloud_provider_test.go @@ -29,7 +29,7 @@ func TestPopulateCloudProvider(t *testing.T) { assert.Equal(t, &database_observability.CloudProvider{ Azure: &database_observability.AzureCloudProviderInfo{ - Resource: "products-db", + ServerName: "products-db", }, }, got) }) diff --git a/internal/component/database_observability/mysql/collector/connection_info.go b/internal/component/database_observability/mysql/collector/connection_info.go index 0e7f286e003..60e6b5e8188 100644 --- a/internal/component/database_observability/mysql/collector/connection_info.go +++ b/internal/component/database_observability/mysql/collector/connection_info.go @@ -81,7 +81,9 @@ func (c *ConnectionInfo) Start(ctx context.Context) error { } if c.CloudProvider.Azure != nil { providerName = "azure" - dbInstanceIdentifier = c.CloudProvider.Azure.Resource + dbInstanceIdentifier = c.CloudProvider.Azure.ServerName + providerRegion = c.CloudProvider.Azure.ResourceGroup + providerAccount = c.CloudProvider.Azure.SubscriptionID } } else { cfg, err := mysql.ParseDSN(c.DSN) diff --git a/internal/component/database_observability/mysql/collector/connection_info_test.go b/internal/component/database_observability/mysql/collector/connection_info_test.go index 48b2573f515..a3b91ec9958 100644 --- a/internal/component/database_observability/mysql/collector/connection_info_test.go +++ b/internal/component/database_observability/mysql/collector/connection_info_test.go @@ -60,13 +60,15 @@ func TestConnectionInfo(t *testing.T) { { name: "Azure with cloud provider info supplied", dsn: "user:pass@tcp(products-db.mysql.database.azure.com:3306)/schema", - engineVersion: "15.4", + engineVersion: "8.0.32", cloudProvider: &database_observability.CloudProvider{ Azure: &database_observability.AzureCloudProviderInfo{ - Resource: "products-db", + ServerName: "products-db", + SubscriptionID: "sub-12345-abcde", + ResourceGroup: "my-resource-group", }, }, - expectedMetrics: fmt.Sprintf(baseExpectedMetrics, "products-db", "mysql", "15.4", "unknown", "azure", "unknown"), + expectedMetrics: fmt.Sprintf(baseExpectedMetrics, "products-db", "mysql", "8.0.32", "sub-12345-abcde", "azure", "my-resource-group"), }, { name: "Azure flexibleservers dsn", diff --git a/internal/component/database_observability/mysql/component.go b/internal/component/database_observability/mysql/component.go index 0bbb96f4045..9ec6af057be 100644 --- a/internal/component/database_observability/mysql/component.go +++ b/internal/component/database_observability/mysql/component.go @@ -74,13 +74,20 @@ type Arguments struct { } type CloudProvider struct { - AWS *AWSCloudProviderInfo `alloy:"aws,block,optional"` + AWS *AWSCloudProviderInfo `alloy:"aws,block,optional"` + Azure *AzureCloudProviderInfo `alloy:"azure,block,optional"` } type AWSCloudProviderInfo struct { ARN string `alloy:"arn,attr"` } +type AzureCloudProviderInfo struct { + SubscriptionID string `alloy:"subscription_id,attr"` + ResourceGroup string `alloy:"resource_group,attr"` + ServerName string `alloy:"server_name,attr,optional"` +} + type QueryTablesArguments struct { CollectInterval time.Duration `alloy:"collect_interval,attr,optional"` } diff --git a/internal/component/database_observability/mysql/component_test.go b/internal/component/database_observability/mysql/component_test.go index bc711fe5eb3..0e14b3d9e32 100644 --- a/internal/component/database_observability/mysql/component_test.go +++ b/internal/component/database_observability/mysql/component_test.go @@ -92,6 +92,56 @@ func Test_parseCloudProvider(t *testing.T) { assert.Equal(t, "arn:aws:rds:some-region:some-account:db:some-db-instance", args.CloudProvider.AWS.ARN) }) + + t.Run("parse azure cloud provider block with all fields", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "" + forward_to = [] + targets = [] + cloud_provider { + azure { + subscription_id = "sub-12345-abcde" + resource_group = "my-resource-group" + server_name = "my-mysql-server" + } + } + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + require.NotNil(t, args.CloudProvider) + require.NotNil(t, args.CloudProvider.Azure) + assert.Equal(t, "sub-12345-abcde", args.CloudProvider.Azure.SubscriptionID) + assert.Equal(t, "my-resource-group", args.CloudProvider.Azure.ResourceGroup) + assert.Equal(t, "my-mysql-server", args.CloudProvider.Azure.ServerName) + }) + + t.Run("parse azure cloud provider block without optional server_name", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "" + forward_to = [] + targets = [] + cloud_provider { + azure { + subscription_id = "sub-12345-abcde" + resource_group = "my-resource-group" + } + } + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + require.NotNil(t, args.CloudProvider) + require.NotNil(t, args.CloudProvider.Azure) + assert.Equal(t, "sub-12345-abcde", args.CloudProvider.Azure.SubscriptionID) + assert.Equal(t, "my-resource-group", args.CloudProvider.Azure.ResourceGroup) + assert.Empty(t, args.CloudProvider.Azure.ServerName) + }) + t.Run("empty cloud provider block", func(t *testing.T) { exampleDBO11yAlloyConfig := ` data_source_name = "" diff --git a/internal/component/database_observability/postgres/cloud_provider.go b/internal/component/database_observability/postgres/cloud_provider.go index a32a78c5433..c2abbbaa672 100644 --- a/internal/component/database_observability/postgres/cloud_provider.go +++ b/internal/component/database_observability/postgres/cloud_provider.go @@ -26,6 +26,13 @@ func populateCloudProviderFromConfig(config *CloudProvider) (*database_observabi ARN: arn, } } + if config.Azure != nil { + cloudProvider.Azure = &database_observability.AzureCloudProviderInfo{ + SubscriptionID: config.Azure.SubscriptionID, + ResourceGroup: config.Azure.ResourceGroup, + ServerName: config.Azure.ServerName, + } + } return &cloudProvider, nil } @@ -51,7 +58,7 @@ func populateCloudProviderFromDSN(dsn string) (*database_observability.CloudProv matches := azureRegex.FindStringSubmatch(host) if len(matches) > 1 { cloudProvider.Azure = &database_observability.AzureCloudProviderInfo{ - Resource: matches[1], + ServerName: matches[1], } } } diff --git a/internal/component/database_observability/postgres/cloud_provider_test.go b/internal/component/database_observability/postgres/cloud_provider_test.go index 365fb0b5421..acdc79460f0 100644 --- a/internal/component/database_observability/postgres/cloud_provider_test.go +++ b/internal/component/database_observability/postgres/cloud_provider_test.go @@ -29,7 +29,7 @@ func TestPopulateCloudProvider(t *testing.T) { assert.Equal(t, &database_observability.CloudProvider{ Azure: &database_observability.AzureCloudProviderInfo{ - Resource: "products-db", + ServerName: "products-db", }, }, got) }) diff --git a/internal/component/database_observability/postgres/collector/connection_info.go b/internal/component/database_observability/postgres/collector/connection_info.go index f23b078b6bc..7b4996dd306 100644 --- a/internal/component/database_observability/postgres/collector/connection_info.go +++ b/internal/component/database_observability/postgres/collector/connection_info.go @@ -82,7 +82,9 @@ func (c *ConnectionInfo) Start(ctx context.Context) error { } if c.CloudProvider.Azure != nil { providerName = "azure" - dbInstanceIdentifier = c.CloudProvider.Azure.Resource + dbInstanceIdentifier = c.CloudProvider.Azure.ServerName + providerRegion = c.CloudProvider.Azure.ResourceGroup + providerAccount = c.CloudProvider.Azure.SubscriptionID } } else { parts, err := ParseURL(c.DSN) diff --git a/internal/component/database_observability/postgres/collector/connection_info_test.go b/internal/component/database_observability/postgres/collector/connection_info_test.go index 8d7c269d819..4cd76a02608 100644 --- a/internal/component/database_observability/postgres/collector/connection_info_test.go +++ b/internal/component/database_observability/postgres/collector/connection_info_test.go @@ -65,10 +65,12 @@ func TestConnectionInfo(t *testing.T) { engineVersion: "15.4", cloudProvider: &database_observability.CloudProvider{ Azure: &database_observability.AzureCloudProviderInfo{ - Resource: "products-db", + ServerName: "products-db", + SubscriptionID: "sub-12345-abcde", + ResourceGroup: "my-resource-group", }, }, - expectedMetrics: fmt.Sprintf(baseExpectedMetrics, "products-db", "postgres", "15.4", "unknown", "azure", "unknown"), + expectedMetrics: fmt.Sprintf(baseExpectedMetrics, "products-db", "postgres", "15.4", "sub-12345-abcde", "azure", "my-resource-group"), }, { name: "Azure flexibleservers dsn", diff --git a/internal/component/database_observability/postgres/component.go b/internal/component/database_observability/postgres/component.go index 98a343161ac..843e9bc99c8 100644 --- a/internal/component/database_observability/postgres/component.go +++ b/internal/component/database_observability/postgres/component.go @@ -75,13 +75,20 @@ type Arguments struct { } type CloudProvider struct { - AWS *AWSCloudProviderInfo `alloy:"aws,block,optional"` + AWS *AWSCloudProviderInfo `alloy:"aws,block,optional"` + Azure *AzureCloudProviderInfo `alloy:"azure,block,optional"` } type AWSCloudProviderInfo struct { ARN string `alloy:"arn,attr"` } +type AzureCloudProviderInfo struct { + SubscriptionID string `alloy:"subscription_id,attr"` + ResourceGroup string `alloy:"resource_group,attr"` + ServerName string `alloy:"server_name,attr,optional"` +} + type QuerySampleArguments struct { CollectInterval time.Duration `alloy:"collect_interval,attr,optional"` DisableQueryRedaction bool `alloy:"disable_query_redaction,attr,optional"` diff --git a/internal/component/database_observability/postgres/component_test.go b/internal/component/database_observability/postgres/component_test.go index 039bd075272..0432820cee2 100644 --- a/internal/component/database_observability/postgres/component_test.go +++ b/internal/component/database_observability/postgres/component_test.go @@ -404,3 +404,89 @@ func TestPostgres_schema_details_cache_configuration_is_parsed_from_config(t *te assert.Equal(t, 5*time.Minute, args.SchemaDetailsArguments.CacheTTL) }) } + +func Test_parseCloudProvider(t *testing.T) { + t.Run("parse aws cloud provider block", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "postgres://db" + forward_to = [] + targets = [] + cloud_provider { + aws { + arn = "arn:aws:rds:some-region:some-account:db:some-db-instance" + } + } + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + require.NotNil(t, args.CloudProvider) + require.NotNil(t, args.CloudProvider.AWS) + assert.Equal(t, "arn:aws:rds:some-region:some-account:db:some-db-instance", args.CloudProvider.AWS.ARN) + }) + + t.Run("parse azure cloud provider block with all fields", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "postgres://db" + forward_to = [] + targets = [] + cloud_provider { + azure { + subscription_id = "sub-12345-abcde" + resource_group = "my-resource-group" + server_name = "my-postgres-server" + } + } + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + require.NotNil(t, args.CloudProvider) + require.NotNil(t, args.CloudProvider.Azure) + assert.Equal(t, "sub-12345-abcde", args.CloudProvider.Azure.SubscriptionID) + assert.Equal(t, "my-resource-group", args.CloudProvider.Azure.ResourceGroup) + assert.Equal(t, "my-postgres-server", args.CloudProvider.Azure.ServerName) + }) + + t.Run("parse azure cloud provider block without optional server_name", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "postgres://db" + forward_to = [] + targets = [] + cloud_provider { + azure { + subscription_id = "sub-12345-abcde" + resource_group = "my-resource-group" + } + } + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + require.NotNil(t, args.CloudProvider) + require.NotNil(t, args.CloudProvider.Azure) + assert.Equal(t, "sub-12345-abcde", args.CloudProvider.Azure.SubscriptionID) + assert.Equal(t, "my-resource-group", args.CloudProvider.Azure.ResourceGroup) + assert.Empty(t, args.CloudProvider.Azure.ServerName) + }) + + t.Run("empty cloud provider block", func(t *testing.T) { + exampleDBO11yAlloyConfig := ` + data_source_name = "postgres://db" + forward_to = [] + targets = [] + ` + + var args Arguments + err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args) + require.NoError(t, err) + + assert.Nil(t, args.CloudProvider) + }) +} diff --git a/internal/component/database_observability/relabeling_test.go b/internal/component/database_observability/relabeling_test.go index 5852232bac7..87ed464dfe4 100644 --- a/internal/component/database_observability/relabeling_test.go +++ b/internal/component/database_observability/relabeling_test.go @@ -49,7 +49,7 @@ func Test_GetRelabelingRules(t *testing.T) { t.Run("return relabeling rules with Azure config", func(t *testing.T) { rr := GetRelabelingRules("some-server-id", &CloudProvider{ Azure: &AzureCloudProviderInfo{ - Resource: "some-resource", + ServerName: "some-resource", }, })