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

Support custom endpoint URLs #76

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ The default location of the configuration file used by collectd-cloudwatch plugi
* __credentials_path__ - Used to point to AWS account configuration file
* __region__ - Manual override for [region](http://docs.aws.amazon.com/general/latest/gr/rande.html#cw_region) used to publish metrics
* __host__ - Manual override for EC2 Instance ID and Host information propagated by collectd
* __ec2_endpoint_url__ - Manual override for EC2 endpoint
* __monitoring_endpoint_url__ - Manual override for Monitoring endpoint
* __proxy_server_name__ - Manual override for proxy server name, used by plugin to connect aws cloudwatch at *.amazonaws.com.
* __proxy_server_port__ - Manual override for proxy server port, used by plugin to connect aws cloudwatch at *.amazonaws.com.
* __enable_high_resolution_metrics__ - The storage resolution is for high resolution support
Expand Down
24 changes: 20 additions & 4 deletions src/cloudwatch/modules/client/baserequestbuilder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from ..awsutils import get_aws_timestamp, get_datestamp
from signer import Signer
import re

from querystringbuilder import QuerystringBuilder
from signer import Signer
from ..awsutils import get_aws_timestamp, get_datestamp


class BaseRequestBuilder(object):
"""
Expand All @@ -17,7 +20,8 @@ class BaseRequestBuilder(object):
_ALGORITHM = "AWS4-HMAC-SHA256"
_V4_TERMINATOR = "aws4_request"

def __init__(self, credentials, region, service, action, api_version, enable_high_resolution_metrics=False):
def __init__(self, endpoint, credentials, region, service, action, api_version, enable_high_resolution_metrics=False):
self.endpoint = endpoint
self.credentials = credentials
self.region = region
self.datestamp = None
Expand Down Expand Up @@ -61,4 +65,16 @@ def _get_request_map(self):
}
if self.credentials.token:
canonical_map["X-Amz-Security-Token"] = self.credentials.token
return canonical_map
return canonical_map

def _get_host(self):
""" Returns the endpoint's hostname derived from the endpoint """
match = re.search(r"http://(.*)/", self.endpoint)
if match:
return match.group(1)

match = re.search(r"https://(.*)/", self.endpoint)
if match:
return match.group(1)

raise ValueError("Cannot extract endpoint hostname")
2 changes: 1 addition & 1 deletion src/cloudwatch/modules/client/ec2getclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class EC2GetClient(object):
_TOTAL_RETRIES = 1

def __init__(self, config_helper, connection_timeout=_DEFAULT_CONNECTION_TIMEOUT, response_timeout=_DEFAULT_RESPONSE_TIMEOUT):
self.request_builder = EC2RequestBuilder(config_helper.credentials, config_helper.region)
self.request_builder = EC2RequestBuilder(config_helper.ec2_endpoint, config_helper.credentials, config_helper.region)
self._validate_and_set_endpoint(config_helper.ec2_endpoint)
self.timeout = (connection_timeout, response_timeout)

Expand Down
12 changes: 2 additions & 10 deletions src/cloudwatch/modules/client/ec2requestbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class EC2RequestBuilder(BaseRequestBuilder):
_ACTION = "DescribeTags"
_API_VERSION = "2016-11-15"

def __init__(self, credentials, region):
super(self.__class__, self).__init__(credentials, region, self._SERVICE, self._ACTION, self._API_VERSION)
def __init__(self, ec2_endpoint, credentials, region):
super(self.__class__, self).__init__(ec2_endpoint, credentials, region, self._SERVICE, self._ACTION, self._API_VERSION)

def create_signed_request(self, request_map):
""" Creates a ready to send request with metrics from the metric list passed as parameter """
Expand All @@ -32,11 +32,3 @@ def _create_canonical_querystring(self, request_map):
http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
"""
return self.querystring_builder.build_querystring_from_map(request_map, self._get_request_map())

def _get_host(self):
""" Returns the endpoint's hostname derived from the region """
if self.region == "localhost":
return "localhost"
elif self.region.startswith("cn-"):
return "ec2." + self.region + ".amazonaws.com.cn"
return "ec2." + self.region + ".amazonaws.com"
2 changes: 1 addition & 1 deletion src/cloudwatch/modules/client/putclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class PutClient(object):
_LOG_FILE_MAX_SIZE = 10*1024*1024

def __init__(self, config_helper, connection_timeout=_DEFAULT_CONNECTION_TIMEOUT, response_timeout=_DEFAULT_RESPONSE_TIMEOUT):
self.request_builder = RequestBuilder(config_helper.credentials, config_helper.region, config_helper.enable_high_resolution_metrics)
self.request_builder = RequestBuilder(config_helper.endpoint, config_helper.credentials, config_helper.region, config_helper.enable_high_resolution_metrics)
self._validate_and_set_endpoint(config_helper.endpoint)
self.timeout = (connection_timeout, response_timeout)
self.proxy_server_name = config_helper.proxy_server_name
Expand Down
12 changes: 2 additions & 10 deletions src/cloudwatch/modules/client/requestbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class RequestBuilder(BaseRequestBuilder):
_ACTION = "PutMetricData"
_API_VERSION = "2010-08-01"

def __init__(self, credentials, region, enable_high_resolution_metrics):
super(self.__class__, self).__init__(credentials, region, self._SERVICE, self._ACTION, self._API_VERSION, enable_high_resolution_metrics)
def __init__(self, endpoint, credentials, region, enable_high_resolution_metrics):
super(self.__class__, self).__init__(endpoint, credentials, region, self._SERVICE, self._ACTION, self._API_VERSION, enable_high_resolution_metrics)
self.namespace = ""

def create_signed_request(self, namespace, metric_list):
Expand Down Expand Up @@ -44,11 +44,3 @@ def _get_namespace_request_map(self):
if (self.namespace):
canonical_map["Namespace"] = self.namespace
return canonical_map

def _get_host(self):
""" Returns the endpoint's hostname derived from the region """
if self.region == "localhost":
return "localhost"
elif self.region.startswith("cn-"):
return "monitoring." + self.region + ".amazonaws.com.cn"
return "monitoring." + self.region + ".amazonaws.com"
12 changes: 10 additions & 2 deletions src/cloudwatch/modules/configuration/confighelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ def _load_hostname(self):
" Using host information provided by Collectd.")

def _set_ec2_endpoint(self):
""" Creates endpoint from region information """
""" Creates endpoint from region information (if the endpoint not explicitly set via ec2_endpoint_url) """
if self.config_reader.ec2_endpoint_url:
self.ec2_endpoint = self.config_reader.ec2_endpoint_url
return

if self.region is "localhost":
self.ec2_endpoint = "http://" + self.region + "/"
elif self.region.startswith("cn-"):
Expand Down Expand Up @@ -179,7 +183,11 @@ def _load_flush_interval_in_seconds(self):
self._LOGGER.warning("flush_interval_in_seconds in configuration is invalid: " + str(self.config_reader.flush_interval_in_seconds) + " use the default value: " + self.flush_interval_in_seconds)

def _set_endpoint(self):
""" Creates endpoint from region information """
""" Creates endpoint from region information (if the endpoint not explicitly set via monitoring_endpoint_url) """
if self.config_reader.monitoring_endpoint_url:
self.endpoint = self.config_reader.monitoring_endpoint_url
return

if self.region is "localhost":
self.endpoint = "http://" + self.region + "/"
elif self.region.startswith("cn-"):
Expand Down
4 changes: 4 additions & 0 deletions src/cloudwatch/modules/configuration/configreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ConfigReader(object):
PROXY_SERVER_PORT_KEY = "proxy_server_port"
ENABLE_HIGH_DEFINITION_METRICS = "enable_high_resolution_metrics"
FLUSH_INTERVAL_IN_SECONDS = "flush_interval_in_seconds"
EC2_ENDPOINT_URL = "ec2_endpoint_url"
MONITORING_ENDPOINT_URL = "monitoring_endpoint_url"

def __init__(self, config_path):
self.config_path = config_path
Expand Down Expand Up @@ -78,3 +80,5 @@ def _parse_config_file(self):
self.push_asg = self.reader_utils.try_get_boolean(self.PUSH_ASG_KEY, self._PUSH_ASG_DEFAULT_VALUE)
self.push_constant = self.reader_utils.try_get_boolean(self.PUSH_CONSTANT_KEY, self._PUSH_CONSTANT_DEFAULT_VALUE)
self.constant_dimension_value = self.reader_utils.get_string(self.CONSTANT_DIMENSION_KEY)
self.ec2_endpoint_url = self.reader_utils.get_string(self.EC2_ENDPOINT_URL)
self.monitoring_endpoint_url = self.reader_utils.get_string(self.MONITORING_ENDPOINT_URL)
8 changes: 4 additions & 4 deletions test/test_requestbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ def setUp(self):
self.region = "test_region"
self.namespace = "test_namespace"
self.credentials = AWSCredentials("access_key", "secret_key")
self.builder = RequestBuilder(self.credentials, self.region, "10")
self.builder = RequestBuilder("https://monitoring." + self.region + ".amazonaws.com/", self.credentials, self.region, "10")

def test_get_host(self):
self.assertEquals("monitoring." + self.region +".amazonaws.com", self.builder._get_host())

def test_get_host_for_localhost_region(self):
self.builder.region = "localhost"
def test_get_host_for_localhost_endpoint(self):
self.builder.endpoint = "http://localhost/"
self.assertEquals("localhost", self.builder._get_host())

def test_get_signed_headers(self):
Expand Down Expand Up @@ -54,7 +54,7 @@ def test_create_signed_request_generates_all_required_parameters(self):
self.assertTrue("X-Amz-SignedHeaders" in request)

def test_create_signed_request_with_iam_role_has_token_parameter(self):
self.builder = RequestBuilder(AWSCredentials("access_key", "secret_key", "token"), self.region, "10")
self.builder = RequestBuilder("https://localhost/", AWSCredentials("access_key", "secret_key", "token"), self.region, "10")
metric = MetricDataStatistic("test_metric", statistic_values=MetricDataStatistic.Statistics(20))
request = self.builder.create_signed_request(self.namespace, [metric])
self.assertTrue("X-Amz-Security-Token" in request)
Expand Down