Skip to content

Commit

Permalink
Use the correct default for aws_auth.region for the AWS PRW exporter
Browse files Browse the repository at this point in the history
"aps" is currently the only value that is acceptable for this field. We should have never provided it as a configuration setting but we can't remove and break the existing users. This change is setting it to "aps" by default.

A follow up change will be provided to automatically parse the region from the workspace remote write endpoint.
  • Loading branch information
rakyll committed Apr 22, 2021
1 parent 007b900 commit 458aab5
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 64 deletions.
7 changes: 4 additions & 3 deletions exporter/awsprometheusremotewriteexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ exporters:
max_interval: 60s
max_elapsed_time: 10m
endpoint: "https://aps-workspaces.us-east-1.amazonaws.com/workspaces/ws-XXX/api/v1/remote_write"
aws_auth:
region: "us-east-1" # need to match workspace region
role_arn: "arn:aws:iam::123456789012:role/aws-service-role/access"
ca_file: "/var/lib/mycert.pem"
write_buffer_size: 524288
headers:
X-Scope-OrgID: 234
aws_auth:
region: "us-east-1" # need to match workspace region
service: "aps"
role_arn: "arn:aws:iam::123456789012:role/aws-service-role/access"
external_labels:
key1: value1
key2: value2
Expand Down
40 changes: 15 additions & 25 deletions exporter/awsprometheusremotewriteexporter/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package awsprometheusremotewriteexporter provides a Prometheus Remote Write Exporter with AWS Sigv4 authentication
package awsprometheusremotewriteexporter

import (
Expand All @@ -30,40 +29,37 @@ import (
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
)

// signingRoundTripper is a Custom RoundTripper that performs AWS Sig V4
const defaultAMPSigV4Service = "aps"

// signingRoundTripper is a Custom RoundTripper that performs AWS Sig V4.
type signingRoundTripper struct {
transport http.RoundTripper
signer *v4.Signer
region string
service string
}

// RoundTrip signs each outgoing request
func (si *signingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
reqBody, err := req.GetBody()
if err != nil {
return nil, err
}

// Get the body
content, err := ioutil.ReadAll(reqBody)
reqBody.Close()
if err != nil {
return nil, err
}

body := bytes.NewReader(content)

// Clone request to ensure thread safety
// Clone request to ensure thread safety.
req2 := cloneRequest(req)

// Sign the request
_, err = si.signer.Sign(req2, body, si.service, si.region, time.Now())
if err != nil {
return nil, err
}

// Send the request to Prometheus Remote Write Backend
// Send the request to Prometheus Remote Write Backend.
resp, err := si.transport.RoundTrip(req2)
if err != nil {
return nil, err
Expand All @@ -73,14 +69,20 @@ func (si *signingRoundTripper) RoundTrip(req *http.Request) (*http.Response, err
}

func newSigningRoundTripper(auth AuthConfig, next http.RoundTripper) (http.RoundTripper, error) {
if auth.Region == "" {
// TODO(jbd): Automatically parse the region from the workspace.
return next, nil
}
if auth.Service == "" {
auth.Service = defaultAMPSigV4Service
}

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

func getCredsFromConfig(auth AuthConfig) *credentials.Credentials {

// Session Must ensure the Session is valid
// TODO: Don't panic, handle the error from NewSessionWithOptions.
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(auth.Region)},
}))
Expand All @@ -98,32 +100,20 @@ func getCredsFromConfig(auth AuthConfig) *credentials.Credentials {
return creds
}

func createSigningRoundTripperWithCredentials(auth AuthConfig, creds *credentials.Credentials, next http.RoundTripper) (http.RoundTripper, error) {
if !isValidAuth(auth) {
return next, nil
}

func newSigningRoundTripperWithCredentials(auth AuthConfig, creds *credentials.Credentials, next http.RoundTripper) (http.RoundTripper, error) {
if creds == nil {
return nil, errors.New("no AWS credentials exist")
}

signer := v4.NewSigner(creds)

rt := signingRoundTripper{
transport: next,
signer: signer,
region: auth.Region,
service: auth.Service,
}

// return a RoundTripper
return &rt, nil
}

func isValidAuth(params AuthConfig) bool {
return params.Region != "" && params.Service != ""
}

func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
Expand Down
19 changes: 8 additions & 11 deletions exporter/awsprometheusremotewriteexporter/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package awsprometheusremotewriteexporter provides a Prometheus Remote Write Exporter with AWS Sigv4 authentication
package awsprometheusremotewriteexporter

import (
Expand Down Expand Up @@ -55,7 +54,7 @@ func TestRequestSignature(t *testing.T) {
WriteBufferSize: 0,
Timeout: 0,
CustomRoundTripper: func(next http.RoundTripper) (http.RoundTripper, error) {
return createSigningRoundTripperWithCredentials(authConfig, awsCreds, next)
return newSigningRoundTripperWithCredentials(authConfig, awsCreds, next)
},
}
client, _ := setting.ToClient()
Expand Down Expand Up @@ -100,7 +99,7 @@ func TestLeakingBody(t *testing.T) {
WriteBufferSize: 0,
Timeout: 0,
CustomRoundTripper: func(next http.RoundTripper) (http.RoundTripper, error) {
return createSigningRoundTripperWithCredentials(authConfig, awsCreds, next)
return newSigningRoundTripperWithCredentials(authConfig, awsCreds, next)
},
}
client, _ := setting.ToClient()
Expand Down Expand Up @@ -184,7 +183,7 @@ func TestRoundTrip(t *testing.T) {
defer server.Close()
serverURL, _ := url.Parse(server.URL)
authConfig := AuthConfig{Region: "region", Service: "service"}
rt, err := createSigningRoundTripperWithCredentials(authConfig, awsCreds, tt.rt)
rt, err := newSigningRoundTripperWithCredentials(authConfig, awsCreds, tt.rt)
assert.NoError(t, err)
req, err := http.NewRequest("POST", serverURL.String(), strings.NewReader(""))
assert.NoError(t, err)
Expand All @@ -201,7 +200,6 @@ func TestRoundTrip(t *testing.T) {
}

func TestCreateSigningRoundTripperWithCredentials(t *testing.T) {

defaultRoundTripper := (http.RoundTripper)(http.DefaultTransport.(*http.Transport).Clone())

// Some form of AWS credentials must be set up for tests to succeed
Expand Down Expand Up @@ -240,10 +238,9 @@ func TestCreateSigningRoundTripperWithCredentials(t *testing.T) {
true,
},
}
// run tests
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rtp, err := createSigningRoundTripperWithCredentials(tt.authConfig, tt.creds, tt.roundTripper)
rtp, err := newSigningRoundTripperWithCredentials(tt.authConfig, tt.creds, tt.roundTripper)
if tt.returnError {
assert.Error(t, err)
return
Expand All @@ -253,8 +250,6 @@ func TestCreateSigningRoundTripperWithCredentials(t *testing.T) {
sRtp := rtp.(*signingRoundTripper)
assert.Equal(t, sRtp.transport, tt.roundTripper)
assert.Equal(t, tt.authConfig.Service, sRtp.service)
} else {
assert.Equal(t, rtp, tt.roundTripper)
}
})
}
Expand Down Expand Up @@ -294,7 +289,9 @@ func TestCloneRequest(t *testing.T) {
}

func fetchMockCredentials() *credentials.Credentials {
return credentials.NewStaticCredentials("MOCK_AWS_ACCESS_KEY",
return credentials.NewStaticCredentials(
"MOCK_AWS_ACCESS_KEY",
"MOCK_AWS_SECRET_ACCESS_KEY",
"MOCK_TOKEN")
"MOCK_TOKEN",
)
}
13 changes: 7 additions & 6 deletions exporter/awsprometheusremotewriteexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package awsprometheusremotewriteexporter provides a Prometheus Remote Write Exporter with AWS Sigv4 authentication
package awsprometheusremotewriteexporter

import (
Expand All @@ -21,19 +20,21 @@ import (

// Config defines configuration for Remote Write exporter.
type Config struct {
// Config represents the Prometheus Remote Write Exporter configuration
// Config represents the Prometheus Remote Write Exporter configuration.
prw.Config `mapstructure:",squash"`

// AuthConfig represents the AWS Sig V4 configuration options
// AuthConfig represents the AWS SigV4 configuration options.
AuthConfig AuthConfig `mapstructure:"aws_auth"`
}

// AuthConfig defines AWS authentication configurations for SigningRoundTripper
type AuthConfig struct {
// Region is the AWS region for AWS Sig v4.
// Region is the AWS region for AWS SigV4.
Region string `mapstructure:"region"`
// Service is the service name for AWS Sig v4

// Service is the AWS service for AWS SigV4, this is by default "aps".
Service string `mapstructure:"service"`
// Amazon Resource Name (ARN) of a role to assume

// Amazon Resource Name (ARN) of a role to assume. Optional.
RoleArn string `mapstructure:"role_arn"`
}
3 changes: 1 addition & 2 deletions exporter/awsprometheusremotewriteexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package awsprometheusremotewriteexporter provides a Prometheus Remote Write Exporter with AWS Sigv4 authentication
package awsprometheusremotewriteexporter

import (
Expand All @@ -31,7 +30,7 @@ import (
prw "go.opentelemetry.io/collector/exporter/prometheusremotewriteexporter"
)

// TestLoadConfig checks whether yaml configuration can be loaded correctly
// TestLoadConfig checks whether yaml configuration can be loaded correctly.
func TestLoadConfig(t *testing.T) {
factories, err := componenttest.NopFactories()
assert.NoError(t, err)
Expand Down
12 changes: 1 addition & 11 deletions exporter/awsprometheusremotewriteexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package awsprometheusremotewriteexporter

import (
"context"
"errors"
"net/http"

"go.opentelemetry.io/collector/component"
Expand Down Expand Up @@ -50,25 +49,16 @@ func (af *awsFactory) CreateDefaultConfig() config.Exporter {
Config: *af.ExporterFactory.CreateDefaultConfig().(*prw.Config),
AuthConfig: AuthConfig{
Region: "",
Service: "",
Service: defaultAMPSigV4Service,
RoleArn: "",
},
}

cfg.TypeVal = typeStr
cfg.NameVal = typeStr

cfg.HTTPClientSettings.CustomRoundTripper = func(next http.RoundTripper) (http.RoundTripper, error) {
if !isAuthConfigValid(cfg.AuthConfig) {
return nil, errors.New("invalid authentication configuration")
}

return newSigningRoundTripper(cfg.AuthConfig, next)
}

return cfg
}

func isAuthConfigValid(params AuthConfig) bool {
return !(params.Region != "" && params.Service == "" || params.Region == "" && params.Service != "")
}
6 changes: 0 additions & 6 deletions exporter/awsprometheusremotewriteexporter/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package awsprometheusremotewriteexporter provides a Prometheus Remote Write Exporter with AWS Sigv4 authentication
package awsprometheusremotewriteexporter

import (
Expand Down Expand Up @@ -87,11 +86,6 @@ func TestCreateMetricsExporter(t *testing.T) {
component.ExporterCreateParams{Logger: zap.NewNop()},
false,
},
{"invalid_auth_case",
invalidConfigWithAuth,
component.ExporterCreateParams{Logger: zap.NewNop()},
true,
},
{"invalid_config_case",
invalidConfig,
component.ExporterCreateParams{Logger: zap.NewNop()},
Expand Down

0 comments on commit 458aab5

Please sign in to comment.