diff --git a/.github/workflows/go-build-test.yml b/.github/workflows/go-build-test.yml index c500db1..3f2cd8e 100644 --- a/.github/workflows/go-build-test.yml +++ b/.github/workflows/go-build-test.yml @@ -50,7 +50,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 'stable' + go-version: '1.24' cache: false env: GO111MODULE: on @@ -59,5 +59,5 @@ jobs: uses: golangci/golangci-lint-action@v6 with: skip-cache: true - version: v1.60.1 + version: v1.64.5 args: --exclude ".Log(.*)|format.Set|level.Set" --timeout=2m diff --git a/configuration.go b/configuration.go index 297fba5..005464a 100644 --- a/configuration.go +++ b/configuration.go @@ -35,6 +35,8 @@ var ( defaultDatabaseConfig = &configuration{flag: "default-database", envFlag: "default_database", defaultValue: ""} defaultTableConfig = &configuration{flag: "default-table", envFlag: "default_table", defaultValue: ""} enableSigV4AuthConfig = &configuration{flag: "enable-sigv4-auth", envFlag: "enable_sigv4_auth", defaultValue: "true"} + queryBaseEndpointConfig = &configuration{flag: "query-base-endpoint", envFlag: "query_base_endpoint", defaultValue: ""} + writeBaseEndpointConfig = &configuration{flag: "write-base-endpoint", envFlag: "write_base_endpoint", defaultValue: ""} listenAddrConfig = &configuration{flag: "web.listen-address", envFlag: "", defaultValue: ":9201"} telemetryPathConfig = &configuration{flag: "web.telemetry-path", envFlag: "", defaultValue: "/metrics"} failOnLabelConfig = &configuration{flag: "fail-on-long-label", envFlag: "fail_on_long_label", defaultValue: "false"} diff --git a/main.go b/main.go index 7156c03..c125e95 100644 --- a/main.go +++ b/main.go @@ -103,6 +103,8 @@ type connectionConfig struct { telemetryPath string maxReadRetries int maxWriteRetries int + queryBaseEndpoint string + writeBaseEndpoint string certificate string key string } @@ -122,13 +124,14 @@ func main() { logger := cfg.createLogger() ctx := context.Background() - awsQueryConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxReadRetries) + + awsQueryConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxReadRetries, cfg.queryBaseEndpoint) if err != nil { timestream.LogError(logger, "Failed to build AWS configuration for query", err) os.Exit(1) } - awsWriteConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxWriteRetries) + awsWriteConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxWriteRetries, cfg.writeBaseEndpoint) if err != nil { timestream.LogError(logger, "Failed to build AWS configuration for write", err) os.Exit(1) @@ -185,12 +188,12 @@ func lambdaHandler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyRes return createErrorResponse(errors.NewParseBasicAuthHeaderError().(*errors.ParseBasicAuthHeaderError).Message()) } } - awsQueryConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxReadRetries) + awsQueryConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxReadRetries, cfg.queryBaseEndpoint) if err != nil { timestream.LogError(logger, "Failed to build AWS configuration for query", err) os.Exit(1) } - awsWriteConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxWriteRetries) + awsWriteConfigs, err := cfg.buildAWSConfig(ctx, cfg.maxWriteRetries, cfg.writeBaseEndpoint) if err != nil { timestream.LogError(logger, "Failed to build AWS configuration for write", err) os.Exit(1) @@ -394,6 +397,9 @@ func parseEnvironmentVariables() (*connectionConfig, error) { return nil, errors.NewParseRetriesError(writeRetries, "write") } + cfg.queryBaseEndpoint = getOrDefault(queryBaseEndpointConfig) + cfg.writeBaseEndpoint = getOrDefault(writeBaseEndpointConfig) + cfg.promlogConfig = promlog.Config{Level: &promlog.AllowedLevel{}, Format: &promlog.AllowedFormat{}} cfg.promlogConfig.Level.Set(getOrDefault(promlogLevelConfig)) cfg.promlogConfig.Format.Set(getOrDefault(promlogFormatConfig)) @@ -431,6 +437,10 @@ func parseFlags() *connectionConfig { a.Flag(certificateConfig.flag, "TLS server certificate file.").Default(certificateConfig.defaultValue).StringVar(&cfg.certificate) a.Flag(keyConfig.flag, "TLS server private key file.").Default(keyConfig.defaultValue).StringVar(&cfg.key) a.Flag(enableSigV4AuthConfig.flag, "Whether to enable SigV4 authentication with the API Gateway. Default to 'false'.").Default(enableSigV4AuthConfig.defaultValue).StringVar(&enableSigV4Auth) + a.Flag(queryBaseEndpointConfig.flag, "Override the default Timestream query endpoint (e.g., a VPC Endpoint)."). + Default(queryBaseEndpointConfig.defaultValue).StringVar(&cfg.queryBaseEndpoint) + a.Flag(writeBaseEndpointConfig.flag, "Override the default Timestream write endpoint (e.g., a VPC Endpoint)."). + Default(writeBaseEndpointConfig.defaultValue).StringVar(&cfg.writeBaseEndpoint) flag.AddFlags(a, &cfg.promlogConfig) @@ -439,7 +449,12 @@ func parseFlags() *connectionConfig { os.Exit(1) } - if err := cfg.parseBoolFromStrings(enableLogging, failOnLongMetricLabelName, failOnInvalidSample, enableSigV4Auth); err != nil { + if err := cfg.parseBoolFromStrings( + enableLogging, + failOnLongMetricLabelName, + failOnInvalidSample, + enableSigV4Auth, + ); err != nil { os.Exit(1) } @@ -457,7 +472,7 @@ func parseFlags() *connectionConfig { } // buildAWSConfig builds a aws.Config and return the pointer of the config. -func (cfg *connectionConfig) buildAWSConfig(ctx context.Context, maxRetries int) (aws.Config, error) { +func (cfg *connectionConfig) buildAWSConfig(ctx context.Context, maxRetries int, baseEndpoint string) (aws.Config, error) { awsConfig, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.clientConfig.region), config.WithRetryer(func() aws.Retryer { @@ -469,6 +484,10 @@ func (cfg *connectionConfig) buildAWSConfig(ctx context.Context, maxRetries int) if err != nil { return aws.Config{}, fmt.Errorf("failed to build AWS config: %w", err) } + + if baseEndpoint != "" { + awsConfig.BaseEndpoint = aws.String(baseEndpoint) + } return awsConfig, nil } diff --git a/main_test.go b/main_test.go index 1e239d0..7325910 100644 --- a/main_test.go +++ b/main_test.go @@ -661,16 +661,19 @@ func TestBuildAWSConfig(t *testing.T) { name string maxRetries int expectedMaxAttempts int + baseEndpoint string }{ { name: "read config", maxRetries: 10, expectedMaxAttempts: 10, + baseEndpoint: "", }, { name: "write config", maxRetries: 3, expectedMaxAttempts: 3, + baseEndpoint: "https://ingest-cell1.timestream.us-west-2.amazonaws.com", }, } @@ -685,7 +688,7 @@ func TestBuildAWSConfig(t *testing.T) { maxWriteRetries: test.expectedMaxAttempts, } - actualConfig, err := input.buildAWSConfig(context.Background(), test.maxRetries) + actualConfig, err := input.buildAWSConfig(context.Background(), test.maxRetries, test.baseEndpoint) assert.Nil(t, err) assert.NotNil(t, actualConfig) @@ -700,6 +703,9 @@ func TestBuildAWSConfig(t *testing.T) { if ok { assert.Equal(t, test.expectedMaxAttempts, standardRetryer.MaxAttempts()) } + if test.baseEndpoint != "" { + assert.Equal(t, aws.String(test.baseEndpoint), actualConfig.BaseEndpoint) + } }) } } diff --git a/privatelink/DEVELOPER_README.md b/privatelink/DEVELOPER_README.md new file mode 100644 index 0000000..8bc566d --- /dev/null +++ b/privatelink/DEVELOPER_README.md @@ -0,0 +1,141 @@ +# Timestream Prometheus Connector with AWS PrivateLink + +## Overview + +This guide explains how to set up the Prometheus Connector to ingest data to Amazon Timestream from within an isolated VPC environment using [AWS PrivateLink](https://aws.amazon.com/privatelink/). + +This [serverless application](https://aws.amazon.com/serverless/) consists of the following: +- [Amazon EC2](https://aws.amazon.com/ec2/getting-started/) instance that will host the Prometheus Connector. +- [VPC Endpoints](https://docs.aws.amazon.com/whitepapers/latest/aws-privatelink/what-are-vpc-endpoints.html) for securely communicating with AWS services using PrivateLink. + +This application assumes that the VPC in which the template will be deployed has no internet access and ensures that all communication stays within Amazon's internal network. + +## Prerequisites + +1. A VPC with at least two private subnets and route tables. +2. A Timestream database and table. +3. [Read and write cells](https://docs.aws.amazon.com/timestream/latest/developerguide/architecture.html#cells) for your Timestream account. Amazon routes requests to the write and query endpoints of the cell that your account has been mapped to for a given region. + +To get your assigned cells using `awscli`: + +For read endpoint: +``` +aws timestream-query describe-endpoints --region +``` + +For write endpoint: +``` +aws timestream-write describe-endpoints --region +``` + +Example output for the write endpoint: +``` +{ + "Endpoints": [ + { + "Address": "ingest-cell1.timestream.us-west-2.amazonaws.com", + "CachePeriodInMinutes": 1440 + } + ] +} +``` +Take note of your assigned cells (`ingest-cell1` for the above example) for both read and write endpoints. + + +## Deployment + +From your existing VPC, you will need the following values: +- VPC ID: This is the ID of your existing VPC +- VPC CIDR : This is the CIDR range for your VPC +- Private Subnet IDs: This is where the EC2 instance and VPC endpoints will be deployed +- Private Route Table ID(s): This is how the [S3 Gateway endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html) will resolve requests +- Query and Write cells: These are your assigned endpoint cells for Timestream + + +1. From the `privatelink` directory, run the following command to deploy the SAM template: + +``` +sam deploy --parameter-overrides "VpcId= VpcCidrIp= PrivateSubnetIds=, PrivateRouteTableIds= TimestreamQueryCell= TimestreamWriteCell= --region " +``` + +To view the full set of `sam deploy` options see the [sam deploy documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html). + +2. The deployment will have the following outputs upon completion: + +- `InstanceId`: ID of the EC2 instance + + An example of the output: + +``` +------------------------------------------------------------------------------ +Outputs +------------------------------------------------------------------------------ +Key InstanceId +Description ID of the EC2 instance +Value i-08a5d7e1700c9be5a +------------------------------------------------------------------------------ +``` + +3. Start an AWS SSM session, replacing `INSTANCE_ID` with your EC2 instance ID from deployment. You can install the [plugin here.](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) + +```shell +aws ssm start-session --target i- +``` + +4. Install the Prometheus Connector. + + 1. Create a directory for the connector. + ``` + mkdir ~/connector && cd ~/connector + ``` + + 2. Download the precompiled binary from S3 for your region. [See here](https://github.com/awslabs/amazon-timestream-connector-prometheus/tags) for released versions. + + ```shell + curl -O https://timestreamassets-.s3..amazonaws.com/timestream-prometheus-connector/timestream-prometheus-connector-linux-arm64-.zip + ``` + + 3. Unzip the binary. + + ```shell + unzip timestream-prometheus-connector-linux-arm64-.zip + ``` + + 4. Disable endpoint discovery by setting the `AWS_ENABLE_ENDPOINT_DISCOVERY` environment variable to `false`. This ensures requests from the connector are routed through VPC endpoints. + ``` + export AWS_ENABLE_ENDPOINT_DISCOVERY=false + ``` + +5. Launch Prometheus Connector + +Replace the following variables to configure your Timestream database, region, and assigned cells. + + +- `DEFAULT_DATABASE`: Specifies the default Timestream database for the Prometheus Connector. +- `DEFAULT_TABLE`: Specifies the default table for storing Prometheus metrics. +- `AWS_REGION`: Defines the AWS region. +- `QUERY_CELL`: Defines the query endpoint cell for Timestream. +- `INGEST_CELL`: Defines the ingestion endpoint cell for Timestream. + +Run the Prometheus Connector: + +``` +./bootstrap \ + --default-database= \ + --default-table= \ + --region= \ + --query-base-endpoint=https://.timestream..amazonaws.com \ + --write-base-endpoint=https://.timestream..amazonaws.com +``` + +The connector is now ready to ingest data to Timestream! + +To see an example of how Prometheus can be configured, [see here](https://github.com/awslabs/amazon-timestream-connector-prometheus?tab=readme-ov-file#prometheus-configuration). + +### Cleanup + +Delete the CloudFormation stack. From the `privatelink` directory, run the following command: + +```shell +sam delete --region +``` diff --git a/privatelink/samconfig.toml b/privatelink/samconfig.toml new file mode 100644 index 0000000..1817283 --- /dev/null +++ b/privatelink/samconfig.toml @@ -0,0 +1,10 @@ +version = 0.1 +[default] +[default.deploy] +[default.deploy.parameters] +capabilities = "CAPABILITY_NAMED_IAM" +confirm_changeset = true +profile = "default" +region = "us-west-2" +stack_name = "PrometheusConnectorPrivateLink" +resolve_s3 = true diff --git a/privatelink/template.yml b/privatelink/template.yml new file mode 100644 index 0000000..253793b --- /dev/null +++ b/privatelink/template.yml @@ -0,0 +1,186 @@ +Transform: AWS::Serverless-2016-10-31 + +Metadata: + AWS::ServerlessRepo::Application: + Name: timestream-privatelink-ec2 + Description: "This serverless application deploys an Amazon EC2 instance and a set of VPC endpoints to an existing VPC that uses PrivateLink for communication across AWS services." + Author: "Amazon Timestream" + SpdxLicenseId: "Apache-2.0" + LicenseUrl: "../LICENSE" + ReadmeUrl: "DEVELOPER_README.md" + HomePageUrl: "https://aws.amazon.com/timestream/" + SemanticVersion: "1.3.1" + +Parameters: + VpcId: + Type: AWS::EC2::VPC::Id + Description: The ID of the VPC where resources will be deployed. + + PrivateSubnetIds: + Type: List + Description: ID of private subnets in VPC. + + PrivateRouteTableIds: + Type: List + Description: ID of private route tables in VPC. + + VpcCidrIp: + Description: The CIDR IP range for the VPC. + Type: String + + TimestreamQueryCell: + Description: The query cell for the Timestream service. + Type: String + + TimestreamWriteCell: + Description: The ingest cell for the Timestream service. + Type: String + + InstanceType: + Description: The EC2 instance type. + Type: String + Default: "t4g.micro" + + LatestAmiId: + Description: The EC2 instance AMI ID from Parameter Store + Type: 'AWS::SSM::Parameter::Value' + Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-arm64-gp2' + +Resources: + SSMRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ec2.amazonaws.com + Action: sts:AssumeRole + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore + Policies: + - PolicyName: TimestreamAccessPolicy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: "Allow" + Action: + - "timestream:DescribeEndpoints" + Resource: "*" + - Effect: "Allow" + Action: + - "timestream:ListDatabases" + - "timestream:WriteRecords" + - "timestream:Select" + Resource: !Sub "arn:aws:timestream:${AWS::Region}:${AWS::AccountId}:database/*" + + EC2InstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - Ref: SSMRole + + EC2Instance: + Type: AWS::EC2::Instance + Properties: + InstanceType: !Ref InstanceType + ImageId: !Ref LatestAmiId + IamInstanceProfile: !Ref EC2InstanceProfile + NetworkInterfaces: + - AssociatePublicIpAddress: false + DeviceIndex: 0 + SubnetId: !Select [0, !Ref PrivateSubnetIds] + GroupSet: + - Ref: EC2InstanceSG + + EC2InstanceSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow necessary traffic from within VPC + VpcId: !Ref VpcId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: !Ref VpcCidrIp + + VPCEndpointInstanceSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for VPC Endpoints + VpcId: !Ref VpcId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + SourceSecurityGroupId: !Ref EC2InstanceSG + + VPCEndpointSSM: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm + VpcId: !Ref VpcId + VpcEndpointType: Interface + SubnetIds: !Ref PrivateSubnetIds + SecurityGroupIds: + - Ref: VPCEndpointInstanceSG + PrivateDnsEnabled: true + + VPCEndpointEC2Messages: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages + VpcId: !Ref VpcId + VpcEndpointType: Interface + SubnetIds: !Ref PrivateSubnetIds + SecurityGroupIds: + - Ref: VPCEndpointInstanceSG + PrivateDnsEnabled: true + + VPCEndpointSSMMessages: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages + VpcId: !Ref VpcId + VpcEndpointType: Interface + SubnetIds: !Ref PrivateSubnetIds + SecurityGroupIds: + - Ref: VPCEndpointInstanceSG + PrivateDnsEnabled: true + + VPCEndpointTimestreamWrite: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.timestream.${TimestreamWriteCell} + VpcId: !Ref VpcId + SubnetIds: !Ref PrivateSubnetIds + SecurityGroupIds: + - !Ref VPCEndpointInstanceSG + PrivateDnsEnabled: true + VpcEndpointType: Interface + + VPCEndpointTimestreamQuery: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.timestream.${TimestreamQueryCell} + VpcId: !Ref VpcId + SubnetIds: !Ref PrivateSubnetIds + SecurityGroupIds: + - !Ref VPCEndpointInstanceSG + PrivateDnsEnabled: true + VpcEndpointType: Interface + + S3Endpoint: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 + VpcId: !Ref VpcId + VpcEndpointType: Gateway + RouteTableIds: !Ref PrivateRouteTableIds + +Outputs: + InstanceId: + Description: ID of the EC2 instance + Value: !Ref EC2Instance diff --git a/serverless/LICENSE b/serverless/LICENSE deleted file mode 100644 index 67db858..0000000 --- a/serverless/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/serverless/template.yml b/serverless/template.yml index 91be7f7..e2474be 100644 --- a/serverless/template.yml +++ b/serverless/template.yml @@ -6,7 +6,7 @@ Metadata: Description: "This serverless application deploys an AWS Lambda function and an Amazon API Gateway that listens for Prometheus remote write/read requests and stores metrics to Amazon Timestream. This application allows configurations over the resources being created." Author: "Amazon Timestream" SpdxLicenseId: "Apache-2.0" - LicenseUrl: "LICENSE" + LicenseUrl: "../LICENSE" ReadmeUrl: "DEVELOPER_README.md" HomePageUrl: "https://aws.amazon.com/timestream/" SemanticVersion: "1.3.1" diff --git a/timestream/client.go b/timestream/client.go index 16f60ca..e959132 100644 --- a/timestream/client.go +++ b/timestream/client.go @@ -52,6 +52,9 @@ var addUserAgentMiddleware = middleware.AddUserAgentKey("Prometheus Connector/" // Store the initialization function calls to allow unit tests to mock the creation of real clients. var initWriteClient = func(cfg aws.Config) (TimestreamWriteClient, error) { client := timestreamwrite.NewFromConfig(cfg, func(o *timestreamwrite.Options) { + if cfg.BaseEndpoint != nil { + o.BaseEndpoint = cfg.BaseEndpoint + } o.APIOptions = append(o.APIOptions, addUserAgentMiddleware) }) return client, nil @@ -59,6 +62,9 @@ var initWriteClient = func(cfg aws.Config) (TimestreamWriteClient, error) { var initQueryClient = func(cfg aws.Config) (*timestreamquery.Client, error) { client := timestreamquery.NewFromConfig(cfg, func(o *timestreamquery.Options) { + if cfg.BaseEndpoint != nil { + o.BaseEndpoint = cfg.BaseEndpoint + } o.APIOptions = append(o.APIOptions, addUserAgentMiddleware) }) return client, nil