Skip to content

Commit

Permalink
Add Snowflake exporter RSA key-pair authentication (#890)
Browse files Browse the repository at this point in the history
* add rsa config options, docs

* update exporter, tests

* reword auth requirement info

* field doc tweaks

* add var replace notes

* fix placeholder values

* rebase main, fix conflicts

* style example config replacements

* move require

* tidy

* revert Changelog formatting

* remove *
  • Loading branch information
Caleb-Hurshman authored Jun 21, 2024
1 parent 67a6239 commit d616a70
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 58 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ v1.2.0-rc.0

- Added `scrape_protocols` option to `prometheus.scrape`, which allows to
control the preferred order of scrape protocols. (@thampiotr)

- Add support for configuring CPU profile's duration scraped by `pyroscope.scrape`. (@hainenber)

- `prometheus.exporter.snowflake`: Add support for RSA key-pair authentication. (@Caleb-Hurshman)

- Improved filesystem error handling when working with `loki.source.file` and `local.file_match`,
which removes some false-positive error log messages on Windows (@thampiotr)

Expand Down Expand Up @@ -188,6 +190,7 @@ v1.1.0
### Enhancements

- Update `prometheus.exporter.kafka` with the following functionalities (@wildum):

* GSSAPI config
* enable/disable PA_FX_FAST
* set a TLS server name
Expand Down Expand Up @@ -269,6 +272,7 @@ v1.1.0
Modern container runtimes allow binding to unprivileged ports as non-root. (@BlackDex)

- Upgrading from OpenTelemetry v0.96.0 to v0.99.0.

- `otelcol.processor.batch`: Prevent starting unnecessary goroutines.
https://github.com/open-telemetry/opentelemetry-collector/issues/9739
- `otelcol.exporter.otlp`: Checks for port in the config validation for the otlpexporter.
Expand Down
69 changes: 51 additions & 18 deletions docs/sources/reference/components/prometheus.exporter.snowflake.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,60 @@ The `prometheus.exporter.snowflake` component embeds

## Usage

### Password Authentication

```alloy
prometheus.exporter.snowflake "LABEL" {
account_name = <ACCOUNT_NAME>
username = <USERNAME>
password = <PASSWORD>
warehouse = <WAREHOUSE>
}
```

Replace the following:

- _`<ACCOUNT_NAME>`_: The Snowflake account name you are collecting metrics from.
- _`<USERNAME>`_: The username used to query metrics.
- _`<PASSWORD>`_: The password for the user used to query metrics.
- _`<WAREHOUSE>`_: The virtual warehouse to use when querying metrics.

### RSA Authentication

```alloy
prometheus.exporter.snowflake "LABEL" {
account_name = ACCOUNT_NAME
username = USERNAME
password = PASSWORD
warehouse = WAREHOUSE
account_name = <ACCOUNT_NAME>
username = <USERNAME>
private_key_path = <PRIVATE_KEY_PATH>
private_key_password = <PRIVATE_KEY_PASSWORD>
warehouse = <WAREHOUSE>
}
```

Replace the following:

- _`<ACCOUNT_NAME>`_: The Snowflake account name you are collecting metrics from.
- _`<USERNAME>`_: The username used to query metrics.
- _`<PRIVATE_KEY_PATH>`_: The path to the user's RSA private key file.
- _`<PRIVATE_KEY_PASSWORD>`_: The password for the user's RSA private key.
- _`<WAREHOUSE>`_: The virtual warehouse to use when querying metrics.

## Arguments

The following arguments can be used to configure the exporter's behavior.
Omitted fields take their default values.

| Name | Type | Description | Default | Required |
| -------------- | -------- | ----------------------------------------------------- | ---------------- | -------- |
| `account_name` | `string` | The account to collect metrics for. | | yes |
| `username` | `string` | The username for the user used when querying metrics. | | yes |
| `password` | `secret` | The password for the user used when querying metrics. | | yes |
| `role` | `string` | The role to use when querying metrics. | `"ACCOUNTADMIN"` | no |
| `warehouse` | `string` | The warehouse to use when querying metrics. | | yes |
One of `password` or `private_key_path` must be specified to authenticate.
Users with an encrypted private key will also need to provide a `private_key_password`.

| Name | Type | Description | Default | Required |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------- | ---------------- | -------- |
| `account_name` | `string` | The account to collect metrics from. | | yes |
| `username` | `string` | The username for the user used when querying metrics. | | yes |
| `password` | `secret` | The password for the user used when querying metrics (required for password authentication). | | no |
| `private_key_path` | `secret` | The path to the user's RSA private key file (required for RSA key-pair authentication). | | no |
| `private_key_password` | `secret` | The password for the user's RSA private key (required for encrypted RSA key-pair authentication). | | no |
| `role` | `string` | The role to use when querying metrics. | `"ACCOUNTADMIN"` | no |
| `warehouse` | `string` | The warehouse to use when querying metrics. | | yes |

## Blocks

Expand Down Expand Up @@ -79,21 +112,21 @@ prometheus.scrape "demo" {
prometheus.remote_write "demo" {
endpoint {
url = PROMETHEUS_REMOTE_WRITE_URL
url = <PROMETHEUS_REMOTE_WRITE_URL>
basic_auth {
username = USERNAME
password = PASSWORD
username = <USERNAME>
password = <PASSWORD>
}
}
}
```

Replace the following:

- `PROMETHEUS_REMOTE_WRITE_URL`: The URL of the Prometheus remote_write-compatible server to send metrics to.
- `USERNAME`: The username to use for authentication to the remote_write API.
- `PASSWORD`: The password to use for authentication to the remote_write API.
- _`<PROMETHEUS_REMOTE_WRITE_URL>`_: The URL of the Prometheus remote_write-compatible server to send metrics to.
- _`<USERNAME>`_: The username to use for authentication to the remote_write API.
- _`<PASSWORD>`_: The password to use for authentication to the remote_write API.

[scrape]: ../prometheus.scrape/

Expand Down
24 changes: 14 additions & 10 deletions internal/component/prometheus/exporter/snowflake/snowflake.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ var DefaultArguments = Arguments{

// Arguments controls the snowflake exporter.
type Arguments struct {
AccountName string `alloy:"account_name,attr"`
Username string `alloy:"username,attr"`
Password alloytypes.Secret `alloy:"password,attr"`
Role string `alloy:"role,attr,optional"`
Warehouse string `alloy:"warehouse,attr"`
AccountName string `alloy:"account_name,attr"`
Username string `alloy:"username,attr"`
Password alloytypes.Secret `alloy:"password,attr,optional"`
PrivateKeyPath string `alloy:"private_key_path,attr,optional"`
PrivateKeyPassword alloytypes.Secret `alloy:"private_key_password,attr,optional"`
Role string `alloy:"role,attr,optional"`
Warehouse string `alloy:"warehouse,attr"`
}

// SetToDefault implements syntax.Defaulter.
Expand All @@ -47,10 +49,12 @@ func (a *Arguments) SetToDefault() {

func (a *Arguments) Convert() *snowflake_exporter.Config {
return &snowflake_exporter.Config{
AccountName: a.AccountName,
Username: a.Username,
Password: config_util.Secret(a.Password),
Role: a.Role,
Warehouse: a.Warehouse,
AccountName: a.AccountName,
Username: a.Username,
Password: config_util.Secret(a.Password),
PrivateKeyPath: a.PrivateKeyPath,
PrivateKeyPassword: config_util.Secret(a.PrivateKeyPassword),
Role: a.Role,
Warehouse: a.Warehouse,
}
}
46 changes: 27 additions & 19 deletions internal/component/prometheus/exporter/snowflake/snowflake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,40 @@ import (

func TestAlloyUnmarshal(t *testing.T) {
alloyConfig := `
account_name = "some_account"
username = "some_user"
password = "some_password"
role = "some_role"
warehouse = "some_warehouse"
account_name = "some_account"
username = "some_user"
password = "some_password"
private_key_path = "/some/path/rsa_key.p8"
private_key_password = "some_password"
role = "some_role"
warehouse = "some_warehouse"
`

var args Arguments
err := syntax.Unmarshal([]byte(alloyConfig), &args)
require.NoError(t, err)

expected := Arguments{
AccountName: "some_account",
Username: "some_user",
Password: alloytypes.Secret("some_password"),
Role: "some_role",
Warehouse: "some_warehouse",
AccountName: "some_account",
Username: "some_user",
Password: alloytypes.Secret("some_password"),
PrivateKeyPath: "/some/path/rsa_key.p8",
PrivateKeyPassword: alloytypes.Secret("some_password"),
Role: "some_role",
Warehouse: "some_warehouse",
}

require.Equal(t, expected, args)
}

func TestConvert(t *testing.T) {
alloyConfig := `
account_name = "some_account"
username = "some_user"
password = "some_password"
warehouse = "some_warehouse"
account_name = "some_account"
username = "some_user"
password = "some_password"
private_key_path = "/some/path/rsa_key.p8"
private_key_password = "some_password"
warehouse = "some_warehouse"
`
var args Arguments
err := syntax.Unmarshal([]byte(alloyConfig), &args)
Expand All @@ -48,11 +54,13 @@ func TestConvert(t *testing.T) {
res := args.Convert()

expected := snowflake_exporter.Config{
AccountName: "some_account",
Username: "some_user",
Password: config_util.Secret("some_password"),
Role: DefaultArguments.Role,
Warehouse: "some_warehouse",
AccountName: "some_account",
Username: "some_user",
Password: config_util.Secret("some_password"),
PrivateKeyPath: "/some/path/rsa_key.p8",
PrivateKeyPassword: config_util.Secret("some_password"),
Role: DefaultArguments.Role,
Warehouse: "some_warehouse",
}
require.Equal(t, expected, *res)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@ var DefaultConfig = Config{

// Config is the configuration for the snowflake integration
type Config struct {
AccountName string `yaml:"account_name,omitempty"`
Username string `yaml:"username,omitempty"`
Password config_util.Secret `yaml:"password,omitempty"`
Role string `yaml:"role,omitempty"`
Warehouse string `yaml:"warehouse,omitempty"`
AccountName string `yaml:"account_name,omitempty"`
Username string `yaml:"username,omitempty"`
Password config_util.Secret `yaml:"password,omitempty"`
PrivateKeyPath string `yaml:"private_key_path,omitempty"`
PrivateKeyPassword config_util.Secret `yaml:"private_key_password,omitempty"`
Role string `yaml:"role,omitempty"`
Warehouse string `yaml:"warehouse,omitempty"`
}

func (c *Config) exporterConfig() *collector.Config {
return &collector.Config{
AccountName: c.AccountName,
Username: c.Username,
Password: string(c.Password),
Role: c.Role,
Warehouse: c.Warehouse,
AccountName: c.AccountName,
Username: c.Username,
Password: string(c.Password),
PrivateKeyPath: c.PrivateKeyPath,
PrivateKeyPassword: string(c.PrivateKeyPassword),
Role: c.Role,
Warehouse: c.Warehouse,
}
}

Expand Down

0 comments on commit d616a70

Please sign in to comment.