Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

awsprometheusremotewrite: Add support for given IAM roles #2675

Merged
merged 7 commits into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions exporter/awsprometheusremotewriteexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ of the AWS SDK for Go.
Note: this exporter imports and uses the [Prometheus remote write exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/prometheusremotewriteexporter)
from upstream, and simply wraps it in Sigv4 authentication logic

Same as the Prometheus remote write exporter, this exporter checks the temporality and the type of each incoming metric
Same as the Prometheus remote write exporter, this exporter checks the temporality and the type of each incoming metric
and only exports the following combination:

- Int64 or Double type with any temporality
- MonotonicInt64, MonotonicDouble, Histogram, or Summary with only Cumulative temporality.

## Configuration
The following settings are required:
- `endpoint`: protocol:host:port to which the exporter is going to send traces or metrics, using the HTTP/HTTPS protocol.
- `endpoint`: protocol:host:port to which the exporter is going to send traces or metrics, using the HTTP/HTTPS protocol.

The following settings can be optionally configured:
- `namespace`: prefix attached to each exported metric name.
Expand All @@ -31,8 +31,8 @@ The following settings can be optionally configured:
- `aws_auth`: specify if each request should be signed with AWS Sig v4. The following settings must be configured:
- `region`: region of the AWS service being exported to.
- `service`: AWS service being exported to.

- `role_arn`: Amazon Resource Name of the role to assume.

#### Examples:

Simplest configuration:
Expand Down
36 changes: 23 additions & 13 deletions exporter/awsprometheusremotewriteexporter/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"errors"
"io/ioutil"
"net/http"
"strconv"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
)
Expand Down Expand Up @@ -70,23 +72,31 @@ func (si *signingRoundTripper) RoundTrip(req *http.Request) (*http.Response, err
}

func newSigningRoundTripper(auth AuthConfig, next http.RoundTripper) (http.RoundTripper, error) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(auth.Region)},
)
if err != nil {
return nil, err
}

if _, err = sess.Config.Credentials.Get(); err != nil {
return nil, err
}

// Get Credentials, either from ./aws or from environmental variables
creds := sess.Config.Credentials

creds := getCredsFromConfig(auth)
return createSigningRoundTripperWithCredentials(auth, creds, next)
}

func getCredsFromConfig(auth AuthConfig) *credentials.Credentials {

// Session Must ensure the Session is valid
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(auth.Region)},
}))

var creds *credentials.Credentials
if auth.RoleArn != "" {
// Get credentials from an assumeRole API call
creds = stscreds.NewCredentials(sess, auth.RoleArn, func(p *stscreds.AssumeRoleProvider) {
p.RoleSessionName = "aws-otel-collector-" + strconv.FormatInt(time.Now().Unix(), 10)
})
} else {
// Get Credentials, either from ./aws or from environmental variables
creds = sess.Config.Credentials
}
return creds
}

func createSigningRoundTripperWithCredentials(auth AuthConfig, creds *credentials.Credentials, next http.RoundTripper) (http.RoundTripper, error) {
if !isValidAuth(auth) {
return next, nil
Expand Down
29 changes: 29 additions & 0 deletions exporter/awsprometheusremotewriteexporter/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/config/configtls"
)
Expand Down Expand Up @@ -62,6 +63,31 @@ func TestRequestSignature(t *testing.T) {
assert.NoError(t, err)
}

func TestGetCredsFromConfig(t *testing.T) {

tests := []struct {
name string
authConfig AuthConfig
}{
{
"success_case_without_role",
AuthConfig{Region: "region", Service: "service"},
},
{
"success_case_with_role",
AuthConfig{Region: "region", Service: "service", RoleArn: "arn:aws:iam::123456789012:role/IAMRole"},
},
}
// run tests
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
creds := getCredsFromConfig(tt.authConfig)
require.NotNil(t, creds)

})
}
}

type ErrorRoundTripper struct{}

func (ert *ErrorRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
Expand All @@ -79,16 +105,19 @@ func TestRoundTrip(t *testing.T) {
name string
rt http.RoundTripper
shouldError bool
authConfig AuthConfig
}{
{
"valid_round_tripper",
defaultRoundTripper,
false,
AuthConfig{Region: "region", Service: "service"},
},
{
"round_tripper_error",
errorRoundTripper,
true,
AuthConfig{Region: "region", Service: "service", RoleArn: "arn:aws:iam::123456789012:role/IAMRole"},
},
}

Expand Down
2 changes: 2 additions & 0 deletions exporter/awsprometheusremotewriteexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ type AuthConfig struct {
Region string `mapstructure:"region"`
// Service is the service name for AWS Sig v4
Service string `mapstructure:"service"`
// Amazon Resource Name (ARN) of a role to assume
RoleArn string `mapstructure:"role_arn"`
}
1 change: 1 addition & 0 deletions exporter/awsprometheusremotewriteexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func TestLoadConfig(t *testing.T) {
AuthConfig: AuthConfig{
Region: "us-west-2",
Service: "service-name",
RoleArn: "arn:aws:iam::123456789012:role/IAMRole",
},
}
// testing function equality is not supported in Go hence these will be ignored for this test
Expand Down
1 change: 1 addition & 0 deletions exporter/awsprometheusremotewriteexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (af *awsFactory) CreateDefaultConfig() configmodels.Exporter {
AuthConfig: AuthConfig{
Region: "",
Service: "",
RoleArn: "",
},
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
receivers:
nop:

processors:
nop:

exporters:
awsprometheusremotewrite:
awsprometheusremotewrite/2:
Expand All @@ -26,6 +26,7 @@ exporters:
aws_auth:
region: "us-west-2"
service: "service-name"
role_arn: "arn:aws:iam::123456789012:role/IAMRole"
external_labels:
key1: value1
key2: value2
Expand All @@ -35,5 +36,3 @@ service:
receivers: [nop]
processors: [nop]
exporters: [awsprometheusremotewrite]