summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lindsley <daniel@toastdriven.com>2013-07-24 14:51:18 -0700
committerDaniel Lindsley <daniel@toastdriven.com>2013-07-24 14:51:18 -0700
commit15c650e3af28c7a6b36ad736b0911bcf0ad7fb86 (patch)
tree0034f4670542ecf23005b5a10bc5207af79b41ad
parent3c56d13f56d4db34ea59eb526e221c1e07728c98 (diff)
parent35617d8c1984a507fdefcaeb6fa7a92b00d02d95 (diff)
downloadboto-2.9.9.tar.gz
Merge branch 'release-2.9.9'2.9.9
* release-2.9.9: (39 commits) Bumping version to 2.9.9 Added release notes for v2.9.9. Revert "Added "lookup" and "new_item" to dynamodb2.table, which makes it more" Updated Opsworks for support AMI ID & configuration managers. Add unit tests for #1555 to ensure behavior works as expected Added a try block around json.loads call, and code to look for 403 errors Fix compatibility issues with python 2.5 Set secure connection to elb api as default, as for any other connection Reformat SES unit tests for PEP8 using from clause to import TestCase Changing to unittest2 Changing call to super for setUp pep8 standards Adding unit test for detach_internet_gateway Fix for Issue#1417 add unit tests for InstanceGroup instantation don't require the price to be a string, and then report 'must be specified' if I pass a float. Updated erroneous test. Updated a test to match current autoscale output. Fixed up BotoServerException's use of message Fixed the ability to specify ``logging`` on ``DistributionConfig``. ...
-rw-r--r--README.rst4
-rw-r--r--boto/__init__.py2
-rw-r--r--boto/auth.py5
-rw-r--r--boto/cloudformation/stack.py5
-rw-r--r--boto/cloudfront/distribution.py22
-rw-r--r--boto/cloudsearch/search.py14
-rw-r--r--boto/connection.py23
-rw-r--r--boto/ec2/autoscale/__init__.py3
-rw-r--r--boto/ec2/elb/__init__.py4
-rw-r--r--boto/elasticache/__init__.py3
-rw-r--r--boto/emr/instance_group.py4
-rw-r--r--boto/exception.py18
-rw-r--r--boto/opsworks/layer1.py654
-rw-r--r--boto/rds/__init__.py93
-rw-r--r--boto/rds/dbsubnetgroup.py69
-rw-r--r--boto/s3/key.py27
-rw-r--r--boto/s3/keyfile.py2
-rw-r--r--boto/ses/connection.py3
-rw-r--r--boto/sqs/message.py3
-rw-r--r--docs/source/index.rst1
-rw-r--r--docs/source/releasenotes/v2.9.9.rst50
-rw-r--r--tests/integration/rds/test_db_subnet_group.py92
-rw-r--r--tests/unit/auth/test_sigv4.py8
-rw-r--r--tests/unit/cloudformation/test_connection.py2
-rw-r--r--tests/unit/cloudformation/test_stack.py34
-rw-r--r--tests/unit/cloudfront/test_distribution.py21
-rw-r--r--tests/unit/cloudfront/test_signed_urls.py17
-rw-r--r--tests/unit/cloudsearch/test_search.py38
-rw-r--r--tests/unit/ec2/autoscale/test_group.py53
-rw-r--r--tests/unit/emr/test_instance_group_args.py57
-rw-r--r--tests/unit/ses/__init__.py0
-rw-r--r--tests/unit/ses/test_identity.py82
-rw-r--r--tests/unit/sqs/test_message.py35
-rw-r--r--tests/unit/test_connection.py44
-rw-r--r--tests/unit/test_exception.py48
-rw-r--r--tests/unit/vpc/__init__.py3
-rw-r--r--tests/unit/vpc/test_vpc.py43
37 files changed, 1345 insertions, 241 deletions
diff --git a/README.rst b/README.rst
index cfeebef7..2359c66f 100644
--- a/README.rst
+++ b/README.rst
@@ -1,9 +1,9 @@
####
boto
####
-boto 2.9.8
+boto 2.9.9
-Released: 18-July-2013
+Released: 24-July-2013
.. image:: https://travis-ci.org/boto/boto.png?branch=develop
:target: https://travis-ci.org/boto/boto
diff --git a/boto/__init__.py b/boto/__init__.py
index 20c9000d..d2527786 100644
--- a/boto/__init__.py
+++ b/boto/__init__.py
@@ -36,7 +36,7 @@ import logging.config
import urlparse
from boto.exception import InvalidUriError
-__version__ = '2.9.8'
+__version__ = '2.9.9'
Version = __version__ # for backware compatibility
UserAgent = 'Boto/%s (%s)' % (__version__, sys.platform)
diff --git a/boto/auth.py b/boto/auth.py
index 0aa299f9..02de5e1e 100644
--- a/boto/auth.py
+++ b/boto/auth.py
@@ -385,8 +385,9 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
def canonical_uri(self, http_request):
path = http_request.auth_path
- # Normalize the path.
- normalized = posixpath.normpath(path)
+ # Normalize the path
+ # in windows normpath('/') will be '\\' so we chane it back to '/'
+ normalized = posixpath.normpath(path).replace('\\','/')
# Then urlencode whatever's left.
encoded = urllib.quote(normalized)
if len(path) > 1 and path.endswith('/'):
diff --git a/boto/cloudformation/stack.py b/boto/cloudformation/stack.py
index 5d35e891..2ee78022 100644
--- a/boto/cloudformation/stack.py
+++ b/boto/cloudformation/stack.py
@@ -48,7 +48,10 @@ class Stack(object):
elif name == "Description":
self.description = value
elif name == "DisableRollback":
- self.disable_rollback = bool(value)
+ if str(value).lower() == 'true':
+ self.disable_rollback = True
+ else:
+ self.disable_rollback = False
elif name == 'StackId':
self.stack_id = value
elif name == 'StackName':
diff --git a/boto/cloudfront/distribution.py b/boto/cloudfront/distribution.py
index 78b26240..fb8309f2 100644
--- a/boto/cloudfront/distribution.py
+++ b/boto/cloudfront/distribution.py
@@ -30,7 +30,7 @@ from boto.cloudfront.logging import LoggingInfo
from boto.cloudfront.origin import S3Origin, CustomOrigin
from boto.s3.acl import ACL
-class DistributionConfig:
+class DistributionConfig(object):
def __init__(self, connection=None, origin=None, enabled=False,
caller_reference='', cnames=None, comment='',
@@ -100,7 +100,7 @@ class DistributionConfig:
self.cnames = cnames
self.comment = comment
self.trusted_signers = trusted_signers
- self.logging = None
+ self.logging = logging
self.default_root_object = default_root_object
def to_xml(self):
@@ -214,7 +214,7 @@ class StreamingDistributionConfig(DistributionConfig):
s += '</StreamingDistributionConfig>\n'
return s
-class DistributionSummary:
+class DistributionSummary(object):
def __init__(self, connection=None, domain_name='', id='',
last_modified_time=None, status='', origin=None,
@@ -279,7 +279,7 @@ class StreamingDistributionSummary(DistributionSummary):
def get_distribution(self):
return self.connection.get_streaming_distribution_info(self.id)
-class Distribution:
+class Distribution(object):
def __init__(self, connection=None, config=None, domain_name='',
id='', last_modified_time=None, status=''):
@@ -654,12 +654,14 @@ class Distribution:
raise ValueError("Only specify the private_key_file or the private_key_string not both")
if not private_key_file and not private_key_string:
raise ValueError("You must specify one of private_key_file or private_key_string")
- # If private_key_file is a file, read its contents. Otherwise, open it and then read it
- if isinstance(private_key_file, file):
- private_key_string = private_key_file.read()
- elif private_key_file:
- with open(private_key_file, 'r') as file_handle:
- private_key_string = file_handle.read()
+ # If private_key_file is a file name, open it and read it
+ if private_key_string is None:
+ if isinstance(private_key_file, basestring):
+ with open(private_key_file, 'r') as file_handle:
+ private_key_string = file_handle.read()
+ # Otherwise, treat it like a file
+ else:
+ private_key_string = private_key_file.read()
# Sign it!
private_key = rsa.PrivateKey.load_pkcs1(private_key_string)
diff --git a/boto/cloudsearch/search.py b/boto/cloudsearch/search.py
index 69a1981e..cc217ad9 100644
--- a/boto/cloudsearch/search.py
+++ b/boto/cloudsearch/search.py
@@ -289,7 +289,19 @@ class SearchConnection(object):
params = query.to_params()
r = requests.get(url, params=params)
- data = json.loads(r.content)
+ try:
+ data = json.loads(r.content)
+ except json.JSONDecodeError,e:
+ if r.status_code == 403:
+ msg = ''
+ import re
+ g = re.search('<html><body><h1>403 Forbidden</h1>([^<]+)<', r.content)
+ try:
+ msg = ': %s' % (g.groups()[0].strip())
+ except AttributeError:
+ pass
+ raise SearchServiceException('Authentication error from Amazon%s' % msg)
+ raise SearchServiceException("Got non-json response from Amazon")
data['query'] = query
data['search_service'] = self
diff --git a/boto/connection.py b/boto/connection.py
index 375a9ca7..a66dba8b 100644
--- a/boto/connection.py
+++ b/boto/connection.py
@@ -673,6 +673,8 @@ class AWSAuthConnection(object):
print "http_proxy environment variable does not specify " \
"a port, using default"
self.proxy_port = self.port
+
+ self.no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '')
self.use_proxy = (self.proxy != None)
def get_http_connection(self, host, is_secure):
@@ -682,8 +684,25 @@ class AWSAuthConnection(object):
else:
return self.new_http_connection(host, is_secure)
+ def skip_proxy(self, host):
+ if not self.no_proxy:
+ return False
+
+ if self.no_proxy == "*":
+ return True
+
+ hostonly = host
+ hostonly = host.split(':')[0]
+
+ for name in self.no_proxy.split(','):
+ if name and (hostonly.endswith(name) or host.endswith(name)):
+ return True
+
+ return False
+
def new_http_connection(self, host, is_secure):
- if self.use_proxy and not is_secure:
+ if self.use_proxy and not is_secure and \
+ not self.skip_proxy(host):
host = '%s:%d' % (self.proxy, int(self.proxy_port))
if host is None:
host = self.server_name()
@@ -691,7 +710,7 @@ class AWSAuthConnection(object):
boto.log.debug(
'establishing HTTPS connection: host=%s, kwargs=%s',
host, self.http_connection_kwargs)
- if self.use_proxy:
+ if self.use_proxy and not self.skip_proxy(host):
connection = self.proxy_ssl(host, is_secure and 443 or 80)
elif self.https_connection_factory:
connection = self.https_connection_factory(host)
diff --git a/boto/ec2/autoscale/__init__.py b/boto/ec2/autoscale/__init__.py
index 17a89e11..65268f44 100644
--- a/boto/ec2/autoscale/__init__.py
+++ b/boto/ec2/autoscale/__init__.py
@@ -224,8 +224,7 @@ class AutoScaleConnection(AWSQueryConnection):
if launch_config.ramdisk_id:
params['RamdiskId'] = launch_config.ramdisk_id
if launch_config.block_device_mappings:
- self.build_list_params(params, launch_config.block_device_mappings,
- 'BlockDeviceMappings')
+ [x.build_list_params(params) for x in launch_config.block_device_mappings]
if launch_config.security_groups:
self.build_list_params(params, launch_config.security_groups,
'SecurityGroups')
diff --git a/boto/ec2/elb/__init__.py b/boto/ec2/elb/__init__.py
index ed9aaeaa..a190ab79 100644
--- a/boto/ec2/elb/__init__.py
+++ b/boto/ec2/elb/__init__.py
@@ -87,7 +87,7 @@ class ELBConnection(AWSQueryConnection):
'elasticloadbalancing.us-east-1.amazonaws.com')
def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
- is_secure=False, port=None, proxy=None, proxy_port=None,
+ is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, debug=0,
https_connection_factory=None, region=None, path='/',
security_token=None, validate_certs=True):
@@ -616,5 +616,5 @@ class ELBConnection(AWSQueryConnection):
params = {'LoadBalancerName': name}
self.build_list_params(params, subnets,
'Subnets.member.%d')
- return self.get_list('DettachLoadBalancerFromSubnets',
+ return self.get_list('DetachLoadBalancerFromSubnets',
params, None)
diff --git a/boto/elasticache/__init__.py b/boto/elasticache/__init__.py
index fe35d707..acd03777 100644
--- a/boto/elasticache/__init__.py
+++ b/boto/elasticache/__init__.py
@@ -49,6 +49,9 @@ def regions():
RegionInfo(name='ap-southeast-1',
endpoint='elasticache.ap-southeast-1.amazonaws.com',
connection_cls=ElastiCacheConnection),
+ RegionInfo(name='ap-southeast-2',
+ endpoint='elasticache.ap-southeast-2.amazonaws.com',
+ connection_cls=ElastiCacheConnection),
RegionInfo(name='sa-east-1',
endpoint='elasticache.sa-east-1.amazonaws.com',
connection_cls=ElastiCacheConnection),
diff --git a/boto/emr/instance_group.py b/boto/emr/instance_group.py
index be229510..6ab63c5d 100644
--- a/boto/emr/instance_group.py
+++ b/boto/emr/instance_group.py
@@ -27,9 +27,9 @@ class InstanceGroup(object):
self.market = market
self.name = name
if market == 'SPOT':
- if not isinstance(bidprice, basestring):
+ if not bidprice:
raise ValueError('bidprice must be specified if market == SPOT')
- self.bidprice = bidprice
+ self.bidprice = str(bidprice)
def __repr__(self):
if self.market == 'SPOT':
diff --git a/boto/exception.py b/boto/exception.py
index 0c871b37..419aac15 100644
--- a/boto/exception.py
+++ b/boto/exception.py
@@ -76,7 +76,7 @@ class BotoServerError(StandardError):
self.body = body or ''
self.request_id = None
self.error_code = None
- self.error_message = None
+ self._error_message = None
self.box_usage = None
# Attempt to parse the error response. If body isn't present,
@@ -90,16 +90,22 @@ class BotoServerError(StandardError):
# in exception. But first, save self.body in self.error_message
# because occasionally we get error messages from Eucalyptus
# that are just text strings that we want to preserve.
- self.error_message = self.body
+ self.message = self.body
self.body = None
def __getattr__(self, name):
- if name == 'message':
- return self.error_message
+ if name == 'error_message':
+ return self.message
if name == 'code':
return self.error_code
raise AttributeError
+ def __setattr__(self, name, value):
+ if name == 'error_message':
+ self.message = value
+ else:
+ super(BotoServerError, self).__setattr__(name, value)
+
def __repr__(self):
return '%s: %s %s\n%s' % (self.__class__.__name__,
self.status, self.reason, self.body)
@@ -117,7 +123,7 @@ class BotoServerError(StandardError):
elif name == 'Code':
self.error_code = value
elif name == 'Message':
- self.error_message = value
+ self.message = value
elif name == 'BoxUsage':
self.box_usage = value
return None
@@ -125,7 +131,7 @@ class BotoServerError(StandardError):
def _cleanupParsedProperties(self):
self.request_id = None
self.error_code = None
- self.error_message = None
+ self.message = None
self.box_usage = None
class ConsoleOutput:
diff --git a/boto/opsworks/layer1.py b/boto/opsworks/layer1.py
index 2e8ae436..e3a11cd9 100644
--- a/boto/opsworks/layer1.py
+++ b/boto/opsworks/layer1.py
@@ -31,6 +31,29 @@ from boto.opsworks import exceptions
class OpsWorksConnection(AWSQueryConnection):
"""
AWS OpsWorks
+ Welcome to the AWS OpsWorks API Reference . This guide provides
+ descriptions, syntax, and usage examples about AWS OpsWorks
+ actions and data types, including common parameters and error
+ codes.
+
+ AWS OpsWorks is an application management service that provides an
+ integrated experience for overseeing the complete application
+ lifecycle. For information about this product, go to the `AWS
+ OpsWorks`_ details page.
+
+ **Endpoints**
+
+ AWS OpsWorks supports only one endpoint, opsworks.us-
+ east-1.amazonaws.com (HTTPS), so you must connect to that
+ endpoint. You can then use the API to direct AWS OpsWorks to
+ create stacks in any AWS Region.
+
+ **Chef Version**
+
+ When you call CreateStack, CloneStack, or UpdateStack we recommend
+ you use the `ConfigurationManager` parameter to specify the Chef
+ version, 0.9 or 11.4. The default value is currently 0.9. However,
+ we expect to change the default value to 11.4 in late August 2013.
"""
APIVersion = "2013-02-18"
DefaultRegionName = "us-east-1"
@@ -60,13 +83,21 @@ class OpsWorksConnection(AWSQueryConnection):
def attach_elastic_load_balancer(self, elastic_load_balancer_name,
layer_id):
"""
+ Attaches an Elastic Load Balancing instance to a specified
+ layer.
+ You must create the Elastic Load Balancing instance
+ separately, by using the Elastic Load Balancing console, API,
+ or CLI. For more information, see ` Elastic Load Balancing
+ Developer Guide`_.
:type elastic_load_balancer_name: string
- :param elastic_load_balancer_name:
+ :param elastic_load_balancer_name: The Elastic Load Balancing
+ instance's name.
:type layer_id: string
- :param layer_id:
+ :param layer_id: The ID of the layer that the Elastic Load Balancing
+ instance is to be attached to.
"""
params = {
@@ -80,12 +111,13 @@ class OpsWorksConnection(AWSQueryConnection):
region=None, attributes=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
- custom_json=None, use_custom_cookbooks=None,
- custom_cookbooks_source=None, default_ssh_key_name=None,
- clone_permissions=None, clone_app_ids=None,
- default_root_device_type=None):
+ custom_json=None, configuration_manager=None,
+ use_custom_cookbooks=None, custom_cookbooks_source=None,
+ default_ssh_key_name=None, clone_permissions=None,
+ clone_app_ids=None, default_root_device_type=None):
"""
- Creates a clone of a specified stack.
+ Creates a clone of a specified stack. For more information,
+ see `Clone a Stack`_.
:type source_stack_id: string
:param source_stack_id: The source stack ID.
@@ -95,21 +127,26 @@ class OpsWorksConnection(AWSQueryConnection):
:type region: string
:param region: The cloned stack AWS region, such as "us-east-1". For
- more information about AWS regions, see `Regions and Endpoints`_
+ more information about AWS regions, see `Regions and Endpoints`_.
:type attributes: map
:param attributes: A list of stack attributes and values as key/value
pairs to be added to the cloned stack.
:type service_role_arn: string
- :param service_role_arn: The stack AWS Identity and Access Management
- (IAM) role, which allows OpsWorks to work with AWS resources on
- your behalf. You must set this parameter to the Amazon Resource
- Name (ARN) for an existing IAM role. If you create a stack by using
- the OpsWorks console, it creates the role for you. You can obtain
- an existing stack's IAM ARN programmatically by calling
- DescribePermissions. For more information about IAM ARNs, see
- `Using Identifiers`_.
+ :param service_role_arn:
+ The stack AWS Identity and Access Management (IAM) role, which allows
+ AWS OpsWorks to work with AWS resources on your behalf. You must
+ set this parameter to the Amazon Resource Name (ARN) for an
+ existing IAM role. If you create a stack by using the AWS OpsWorks
+ console, it creates the role for you. You can obtain an existing
+ stack's IAM ARN programmatically by calling DescribePermissions.
+ For more information about IAM ARNs, see `Using Identifiers`_.
+
+ You must set this parameter to a valid service role ARN or the action
+ will fail; there is no default value. You can specify the source
+ stack's service role ARN, if you prefer, but you must do so
+ explicitly.
:type default_instance_profile_arn: string
:param default_instance_profile_arn: The ARN of an IAM profile that is
@@ -118,14 +155,25 @@ class OpsWorksConnection(AWSQueryConnection):
:type default_os: string
:param default_os: The cloned stack default operating system, which
- must be either "Amazon Linux" or "Ubuntu 12.04 LTS".
+ must be set to one of the following.
+
+ + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
+ + Custom AMIs: `Custom`
+
+
+ The default option is `Amazon Linux`. If you set this parameter to
+ `Custom`, you must use the CreateInstance action's AmiId parameter
+ to specify the custom AMI that you want to use. For more
+ information on the standard operating systems, see `Operating
+ Systems`_For more information on how to use custom AMIs with
+ OpsWorks, see `Using Custom AMIs`_.
:type hostname_theme: string
:param hostname_theme: The stack's host name theme, with spaces are
- replaced by underscores. The theme is used to generate hostnames
+ replaced by underscores. The theme is used to generate host names
for the stack's instances. By default, `HostnameTheme` is set to
- Layer_Dependent, which creates hostnames by appending integers to
- the layer's shortname. The other themes are:
+ Layer_Dependent, which creates host names by appending integers to
+ the layer's short name. The other themes are:
+ Baked_Goods
+ Clouds
@@ -140,26 +188,37 @@ class OpsWorksConnection(AWSQueryConnection):
+ Wild_Cats
- To obtain a generated hostname, call `GetHostNameSuggestion`, which
- returns a hostname based on the current theme.
+ To obtain a generated host name, call `GetHostNameSuggestion`, which
+ returns a host name based on the current theme.
:type default_availability_zone: string
:param default_availability_zone: The cloned stack's Availability Zone.
For more information, see `Regions and Endpoints`_.
:type custom_json: string
- :param custom_json:
- A string that contains user-defined, custom JSON. It is used to
- override the corresponding default stack configuration JSON values.
- The string should be in the following format and must escape
- characters such as '"'.:
- `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
+ :param custom_json: A string that contains user-defined, custom JSON.
+ It is used to override the corresponding default stack
+ configuration JSON values. The string should be in the following
+ format and must escape characters such as '"'.: `"{\"key1\":
+ \"value1\", \"key2\": \"value2\",...}"`
+ For more information on custom JSON, see `Use Custom JSON to Modify the
+ Stack Configuration JSON`_
+
+ :type configuration_manager: dict
+ :param configuration_manager: The configuration manager. When you clone
+ a stack we recommend that you use the configuration manager to
+ specify the Chef version, 0.9 or 11.4. The default value is
+ currently 0.9. However, we expect to change the default value to
+ 11.4 in late August 2013.
:type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether to use custom cookbooks.
:type custom_cookbooks_source: dict
- :param custom_cookbooks_source:
+ :param custom_cookbooks_source: Contains the information required to
+ retrieve an app or cookbook from a repository. For more
+ information, see `Creating Apps`_ or `Custom Recipes and
+ Cookbooks`_.
:type default_ssh_key_name: string
:param default_ssh_key_name: A default SSH key for the stack instances.
@@ -174,7 +233,10 @@ class OpsWorksConnection(AWSQueryConnection):
the cloned stack.
:type default_root_device_type: string
- :param default_root_device_type:
+ :param default_root_device_type: The default root device type. This
+ value is used by default for all instances in the cloned stack, but
+ you can override it when you create an instance. For more
+ information, see `Storage for the Root Device`_.
"""
params = {
@@ -197,6 +259,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['DefaultAvailabilityZone'] = default_availability_zone
if custom_json is not None:
params['CustomJson'] = custom_json
+ if configuration_manager is not None:
+ params['ConfigurationManager'] = configuration_manager
if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks
if custom_cookbooks_source is not None:
@@ -216,13 +280,14 @@ class OpsWorksConnection(AWSQueryConnection):
description=None, app_source=None, domains=None,
enable_ssl=None, ssl_configuration=None, attributes=None):
"""
- Creates an app for a specified stack.
+ Creates an app for a specified stack. For more information,
+ see `Creating Apps`_.
:type stack_id: string
:param stack_id: The stack ID.
:type shortname: string
- :param shortname:
+ :param shortname: The app's short name.
:type name: string
:param name: The app name.
@@ -233,7 +298,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type type: string
:param type: The app type. Each supported type is associated with a
particular layer. For example, PHP applications are associated with
- a PHP layer. OpsWorks deploys an application to those instances
+ a PHP layer. AWS OpsWorks deploys an application to those instances
that are members of the corresponding layer.
:type app_source: dict
@@ -241,7 +306,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type domains: list
:param domains: The app virtual host settings, with multiple domains
- separated by commas. For example: `'www.mysite.com, mysite.com'`
+ separated by commas. For example: `'www.example.com, example.com'`
:type enable_ssl: boolean
:param enable_ssl: Whether to enable SSL for the app.
@@ -285,29 +350,35 @@ class OpsWorksConnection(AWSQueryConnection):
+ Stack deployment runs the `deploy` recipes but does not
raise an event.
+
+ For more information, see `Deploying Apps`_ and `Run Stack
+ Commands`_.
+
:type stack_id: string
:param stack_id: The stack ID.
:type app_id: string
- :param app_id: The app ID, for app deployments.
+ :param app_id: The app ID. This parameter is required for app
+ deployments, but not for other deployment commands.
:type instance_ids: list
:param instance_ids: The instance IDs for the deployment targets.
:type command: dict
- :param command: A `DeploymentCommand` object that describes details of
- the operation.
+ :param command: A `DeploymentCommand` object that specifies the
+ deployment command and any associated arguments.
:type comment: string
:param comment: A user-defined comment.
:type custom_json: string
- :param custom_json:
- A string that contains user-defined, custom JSON. It is used to
- override the corresponding default stack configuration JSON values.
- The string should be in the following format and must escape
- characters such as '"'.:
- `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
+ :param custom_json: A string that contains user-defined, custom JSON.
+ It is used to override the corresponding default stack
+ configuration JSON values. The string should be in the following
+ format and must escape characters such as '"'.: `"{\"key1\":
+ \"value1\", \"key2\": \"value2\",...}"`
+ For more information on custom JSON, see `Use Custom JSON to Modify the
+ Stack Configuration JSON`_.
"""
params = {'StackId': stack_id, 'Command': command, }
@@ -324,10 +395,12 @@ class OpsWorksConnection(AWSQueryConnection):
def create_instance(self, stack_id, layer_ids, instance_type,
auto_scaling_type=None, hostname=None, os=None,
- ssh_key_name=None, availability_zone=None,
- architecture=None, root_device_type=None):
+ ami_id=None, ssh_key_name=None,
+ availability_zone=None, architecture=None,
+ root_device_type=None, install_updates_on_boot=None):
"""
- Creates an instance in a specified stack.
+ Creates an instance in a specified stack. For more
+ information, see `Adding an Instance to a Layer`_.
:type stack_id: string
:param stack_id: The stack ID.
@@ -336,26 +409,18 @@ class OpsWorksConnection(AWSQueryConnection):
:param layer_ids: An array that contains the instance layer IDs.
:type instance_type: string
- :param instance_type:
- The instance type, which can be one of the following:
-
-
- + m1.small
- + m1.medium
- + m1.large
- + m1.xlarge
- + c1.medium
- + c1.xlarge
- + m2.xlarge
- + m2.2xlarge
- + m2.4xlarge
+ :param instance_type: The instance type. AWS OpsWorks supports all
+ instance types except Cluster Compute, Cluster GPU, and High Memory
+ Cluster. For more information, see `Instance Families and Types`_.
+ The parameter values that you use to specify the various types are
+ in the API Name column of the Available Instance Types table.
:type auto_scaling_type: string
:param auto_scaling_type:
The instance auto scaling type, which has three possible values:
- + **AlwaysRunning**: A 24x7 instance, which is not affected by auto
+ + **AlwaysRunning**: A 24/7 instance, which is not affected by auto
scaling.
+ **TimeBasedAutoScaling**: A time-based auto scaling instance, which
is started and stopped based on a specified schedule. To specify
@@ -369,7 +434,14 @@ class OpsWorksConnection(AWSQueryConnection):
:param hostname: The instance host name.
:type os: string
- :param os: The instance operating system.
+ :param os: The instance's operating system, which must be either
+ `Amazon Linux` or `Ubuntu 12.04 LTS`.
+
+ :type ami_id: string
+ :param ami_id: A custom AMI ID to be used to create the instance. The
+ AMI should be based on one of the standard AWS OpsWorks APIs:
+ Amazon Linux or Ubuntu 12.04 LTS. For more information, see
+ `Instances`_
:type ssh_key_name: string
:param ssh_key_name: The instance SSH key name.
@@ -379,10 +451,26 @@ class OpsWorksConnection(AWSQueryConnection):
information, see `Regions and Endpoints`_.
:type architecture: string
- :param architecture:
+ :param architecture: The instance architecture. Instance types do not
+ necessarily support both architectures. For a list of the
+ architectures that are supported by the different instance types,
+ see `Instance Families and Types`_.
:type root_device_type: string
- :param root_device_type:
+ :param root_device_type: The instance root device type. For more
+ information, see `Storage for the Root Device`_.
+
+ :type install_updates_on_boot: boolean
+ :param install_updates_on_boot:
+ Whether to install operating system and package updates when the
+ instance boots. The default value is `True`. To control when
+ updates are installed, set this value to `False`. You must then
+ update your instances manually by using CreateDeployment to run the
+ `update_dependencies` stack command or manually running `yum`
+ (Amazon Linux) or `apt-get` (Ubuntu) on the instances.
+
+ We strongly recommend using the default value of `True`, to ensure that
+ your instances have the latest security updates.
"""
params = {
@@ -396,6 +484,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Hostname'] = hostname
if os is not None:
params['Os'] = os
+ if ami_id is not None:
+ params['AmiId'] = ami_id
if ssh_key_name is not None:
params['SshKeyName'] = ssh_key_name
if availability_zone is not None:
@@ -404,6 +494,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Architecture'] = architecture
if root_device_type is not None:
params['RootDeviceType'] = root_device_type
+ if install_updates_on_boot is not None:
+ params['InstallUpdatesOnBoot'] = install_updates_on_boot
return self.make_request(action='CreateInstance',
body=json.dumps(params))
@@ -411,23 +503,45 @@ class OpsWorksConnection(AWSQueryConnection):
custom_instance_profile_arn=None,
custom_security_group_ids=None, packages=None,
volume_configurations=None, enable_auto_healing=None,
- auto_assign_elastic_ips=None, custom_recipes=None):
+ auto_assign_elastic_ips=None, custom_recipes=None,
+ install_updates_on_boot=None):
"""
- Creates a layer.
+ Creates a layer. For more information, see `How to Create a
+ Layer`_.
+
+ You should use **CreateLayer** for non-custom layer types such
+ as PHP App Server only if the stack does not have an existing
+ layer of that type. A stack can have at most one instance of
+ each non-custom layer; if you attempt to create a second
+ instance, **CreateLayer** fails. A stack can have an arbitrary
+ number of custom layers, so you can call **CreateLayer** as
+ many times as you like for that layer type.
:type stack_id: string
:param stack_id: The layer stack ID.
:type type: string
- :param type: The layer type. A stack cannot have more than one layer of
- the same type.
+ :param type:
+ The layer type. A stack cannot have more than one layer of the same
+ type. This parameter must be set to one of the following:
+
+
+ + lb: An HAProxy layer
+ + web: A Static Web Server layer
+ + rails-app: A Rails App Server layer
+ + php-app: A PHP App Server layer
+ + nodejs-app: A Node.js App Server layer
+ + memcached: A Memcached layer
+ + db-master: A MySQL layer
+ + monitoring-master: A Ganglia layer
+ + custom: A custom layer
:type name: string
:param name: The layer name, which is used by the console.
:type shortname: string
- :param shortname: The layer short name, which is used internally by
- OpsWorks and by Chef recipes. The shortname is also used as the
+ :param shortname: The layer short name, which is used internally by AWS
+ OpsWorks and by Chef recipes. The short name is also used as the
name for the directory where your app files are installed. It can
have a maximum of 200 characters, which are limited to the
alphanumeric characters, '-', '_', and '.'.
@@ -465,6 +579,18 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_recipes: A `LayerCustomRecipes` object that specifies the
layer custom recipes.
+ :type install_updates_on_boot: boolean
+ :param install_updates_on_boot:
+ Whether to install operating system and package updates when the
+ instance boots. The default value is `True`. To control when
+ updates are installed, set this value to `False`. You must then
+ update your instances manually by using CreateDeployment to run the
+ `update_dependencies` stack command or manually running `yum`
+ (Amazon Linux) or `apt-get` (Ubuntu) on the instances.
+
+ We strongly recommend using the default value of `True`, to ensure that
+ your instances have the latest security updates.
+
"""
params = {
'StackId': stack_id,
@@ -488,6 +614,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['AutoAssignElasticIps'] = auto_assign_elastic_ips
if custom_recipes is not None:
params['CustomRecipes'] = custom_recipes
+ if install_updates_on_boot is not None:
+ params['InstallUpdatesOnBoot'] = install_updates_on_boot
return self.make_request(action='CreateLayer',
body=json.dumps(params))
@@ -495,11 +623,12 @@ class OpsWorksConnection(AWSQueryConnection):
default_instance_profile_arn, attributes=None,
default_os=None, hostname_theme=None,
default_availability_zone=None, custom_json=None,
- use_custom_cookbooks=None, custom_cookbooks_source=None,
- default_ssh_key_name=None,
+ configuration_manager=None, use_custom_cookbooks=None,
+ custom_cookbooks_source=None, default_ssh_key_name=None,
default_root_device_type=None):
"""
- Creates a new stack.
+ Creates a new stack. For more information, see `Create a New
+ Stack`_.
:type name: string
:param name: The stack name.
@@ -514,7 +643,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type service_role_arn: string
:param service_role_arn: The stack AWS Identity and Access Management
- (IAM) role, which allows OpsWorks to work with AWS resources on
+ (IAM) role, which allows AWS OpsWorks to work with AWS resources on
your behalf. You must set this parameter to the Amazon Resource
Name (ARN) for an existing IAM role. For more information about IAM
ARNs, see `Using Identifiers`_.
@@ -525,15 +654,26 @@ class OpsWorksConnection(AWSQueryConnection):
information about IAM ARNs, see `Using Identifiers`_.
:type default_os: string
- :param default_os: The cloned stack default operating system, which
- must be either "Amazon Linux" or "Ubuntu 12.04 LTS".
+ :param default_os: The stack default operating system, which must be
+ set to one of the following.
+
+ + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
+ + Custom AMIs: `Custom`
+
+
+ The default option is `Amazon Linux`. If you set this parameter to
+ `Custom`, you must use the CreateInstance action's AmiId parameter
+ to specify the custom AMI that you want to use. For more
+ information on the standard operating systems, see `Operating
+ Systems`_For more information on how to use custom AMIs with
+ OpsWorks, see `Using Custom AMIs`_.
:type hostname_theme: string
:param hostname_theme: The stack's host name theme, with spaces are
- replaced by underscores. The theme is used to generate hostnames
+ replaced by underscores. The theme is used to generate host names
for the stack's instances. By default, `HostnameTheme` is set to
- Layer_Dependent, which creates hostnames by appending integers to
- the layer's shortname. The other themes are:
+ Layer_Dependent, which creates host names by appending integers to
+ the layer's short name. The other themes are:
+ Baked_Goods
+ Clouds
@@ -548,33 +688,47 @@ class OpsWorksConnection(AWSQueryConnection):
+ Wild_Cats
- To obtain a generated hostname, call `GetHostNameSuggestion`, which
- returns a hostname based on the current theme.
+ To obtain a generated host name, call `GetHostNameSuggestion`, which
+ returns a host name based on the current theme.
:type default_availability_zone: string
:param default_availability_zone: The stack default Availability Zone.
For more information, see `Regions and Endpoints`_.
:type custom_json: string
- :param custom_json:
- A string that contains user-defined, custom JSON. It is used to
- override the corresponding default stack configuration JSON values.
- The string should be in the following format and must escape
- characters such as '"'.:
- `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
+ :param custom_json: A string that contains user-defined, custom JSON.
+ It is used to override the corresponding default stack
+ configuration JSON values. The string should be in the following
+ format and must escape characters such as '"'.: `"{\"key1\":
+ \"value1\", \"key2\": \"value2\",...}"`
+ For more information on custom JSON, see `Use Custom JSON to Modify the
+ Stack Configuration JSON`_.
+
+ :type configuration_manager: dict
+ :param configuration_manager: The configuration manager. When you
+ create a stack we recommend that you use the configuration manager
+ to specify the Chef version, 0.9 or 11.4. The default value is
+ currently 0.9. However, we expect to change the default value to
+ 11.4 in late August 2013.
:type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether the stack uses custom cookbooks.
:type custom_cookbooks_source: dict
- :param custom_cookbooks_source:
+ :param custom_cookbooks_source: Contains the information required to
+ retrieve an app or cookbook from a repository. For more
+ information, see `Creating Apps`_ or `Custom Recipes and
+ Cookbooks`_.
:type default_ssh_key_name: string
:param default_ssh_key_name: A default SSH key for the stack instances.
You can override this value when you create or update an instance.
:type default_root_device_type: string
- :param default_root_device_type:
+ :param default_root_device_type: The default root device type. This
+ value is used by default for all instances in the cloned stack, but
+ you can override it when you create an instance. For more
+ information, see `Storage for the Root Device`_.
"""
params = {
@@ -593,6 +747,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['DefaultAvailabilityZone'] = default_availability_zone
if custom_json is not None:
params['CustomJson'] = custom_json
+ if configuration_manager is not None:
+ params['ConfigurationManager'] = configuration_manager
if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks
if custom_cookbooks_source is not None:
@@ -607,7 +763,7 @@ class OpsWorksConnection(AWSQueryConnection):
def create_user_profile(self, iam_user_arn, ssh_username=None,
ssh_public_key=None):
"""
- Creates a new user.
+ Creates a new user profile.
:type iam_user_arn: string
:param iam_user_arn: The user's IAM ARN.
@@ -642,7 +798,9 @@ class OpsWorksConnection(AWSQueryConnection):
def delete_instance(self, instance_id, delete_elastic_ip=None,
delete_volumes=None):
"""
- Deletes a specified instance.
+ Deletes a specified instance. You must stop an instance before
+ you can delete it. For more information, see `Deleting
+ Instances`_.
:type instance_id: string
:param instance_id: The instance ID.
@@ -666,8 +824,9 @@ class OpsWorksConnection(AWSQueryConnection):
def delete_layer(self, layer_id):
"""
- Deletes a specified layer. You must first remove all
- associated instances.
+ Deletes a specified layer. You must first stop and then delete
+ all associated instances. For more information, see `How to
+ Delete a Layer`_.
:type layer_id: string
:param layer_id: The layer ID.
@@ -679,8 +838,9 @@ class OpsWorksConnection(AWSQueryConnection):
def delete_stack(self, stack_id):
"""
- Deletes a specified stack. You must first delete all instances
- and layers.
+ Deletes a specified stack. You must first delete all
+ instances, layers, and apps. For more information, see `Shut
+ Down a Stack`_.
:type stack_id: string
:param stack_id: The stack ID.
@@ -692,7 +852,7 @@ class OpsWorksConnection(AWSQueryConnection):
def delete_user_profile(self, iam_user_arn):
"""
- Deletes a user.
+ Deletes a user profile.
:type iam_user_arn: string
:param iam_user_arn: The user's IAM ARN.
@@ -707,11 +867,14 @@ class OpsWorksConnection(AWSQueryConnection):
Requests a description of a specified set of apps.
:type stack_id: string
- :param stack_id:
- The app stack ID.
+ :param stack_id: The app stack ID. If you use this parameter,
+ `DescribeApps` returns a description of the apps in the specified
+ stack.
:type app_ids: list
- :param app_ids: An array of app IDs for the apps to be described.
+ :param app_ids: An array of app IDs for the apps to be described. If
+ you use this parameter, `DescribeApps` returns a description of the
+ specified apps. Otherwise, it returns a description of every app.
"""
params = {}
@@ -728,13 +891,20 @@ class OpsWorksConnection(AWSQueryConnection):
Describes the results of specified commands.
:type deployment_id: string
- :param deployment_id: The deployment ID.
+ :param deployment_id: The deployment ID. If you include this parameter,
+ `DescribeCommands` returns a description of the commands associated
+ with the specified deployment.
:type instance_id: string
- :param instance_id: The instance ID.
+ :param instance_id: The instance ID. If you include this parameter,
+ `DescribeCommands` returns a description of the commands associated
+ with the specified instance.
:type command_ids: list
- :param command_ids: An array of IDs for the commands to be described.
+ :param command_ids: An array of command IDs. If you include this
+ parameter, `DescribeCommands` returns a description of the
+ specified commands. Otherwise, it returns a description of every
+ command.
"""
params = {}
@@ -753,13 +923,20 @@ class OpsWorksConnection(AWSQueryConnection):
Requests a description of a specified set of deployments.
:type stack_id: string
- :param stack_id: The stack ID.
+ :param stack_id: The stack ID. If you include this parameter,
+ `DescribeDeployments` returns a description of the commands
+ associated with the specified stack.
:type app_id: string
- :param app_id: The app ID.
+ :param app_id: The app ID. If you include this parameter,
+ `DescribeDeployments` returns a description of the commands
+ associated with the specified app.
:type deployment_ids: list
- :param deployment_ids: An array of deployment IDs to be described.
+ :param deployment_ids: An array of deployment IDs to be described. If
+ you include this parameter, `DescribeDeployments` returns a
+ description of the specified deployments. Otherwise, it returns a
+ description of every deployment.
"""
params = {}
@@ -777,10 +954,15 @@ class OpsWorksConnection(AWSQueryConnection):
Describes an instance's `Elastic IP addresses`_.
:type instance_id: string
- :param instance_id: The instance ID.
+ :param instance_id: The instance ID. If you include this parameter,
+ `DescribeElasticIps` returns a description of the Elastic IP
+ addresses associated with the specified instance.
:type ips: list
- :param ips: An array of Elastic IP addresses to be described.
+ :param ips: An array of Elastic IP addresses to be described. If you
+ include this parameter, `DescribeElasticIps` returns a description
+ of the specified Elastic IP addresses. Otherwise, it returns a
+ description of every Elastic IP address.
"""
params = {}
@@ -793,13 +975,15 @@ class OpsWorksConnection(AWSQueryConnection):
def describe_elastic_load_balancers(self, stack_id=None, layer_ids=None):
"""
-
+ Describes a stack's Elastic Load Balancing instances.
:type stack_id: string
- :param stack_id:
+ :param stack_id: A stack ID. The action describes the Elastic Load
+ Balancing instances for the stack.
:type layer_ids: list
- :param layer_ids:
+ :param layer_ids: A list of layer IDs. The action describes the Elastic
+ Load Balancing instances for the specified layers.
"""
params = {}
@@ -817,13 +1001,20 @@ class OpsWorksConnection(AWSQueryConnection):
specified ID or IDs.
:type stack_id: string
- :param stack_id: A stack ID.
+ :param stack_id: A stack ID. If you use this parameter,
+ `DescribeInstances` returns descriptions of the instances
+ associated with the specified stack.
:type layer_id: string
- :param layer_id: A layer ID.
+ :param layer_id: A layer ID. If you use this parameter,
+ `DescribeInstances` returns descriptions of the instances
+ associated with the specified layer.
:type instance_ids: list
- :param instance_ids: An array of instance IDs to be described.
+ :param instance_ids: An array of instance IDs to be described. If you
+ use this parameter, `DescribeInstances` returns a description of
+ the specified instances. Otherwise, it returns a description of
+ every instance.
"""
params = {}
@@ -846,7 +1037,8 @@ class OpsWorksConnection(AWSQueryConnection):
:type layer_ids: list
:param layer_ids: An array of layer IDs that specify the layers to be
- described.
+ described. If you omit this parameter, `DescribeLayers` returns a
+ description of every layer in the specified stack.
"""
params = {'StackId': stack_id, }
@@ -870,8 +1062,7 @@ class OpsWorksConnection(AWSQueryConnection):
def describe_permissions(self, iam_user_arn, stack_id):
"""
- Describes the permissions for a specified stack. You must
- specify at least one of the two request values.
+ Describes the permissions for a specified stack.
:type iam_user_arn: string
:param iam_user_arn: The user's IAM ARN. For more information about IAM
@@ -890,10 +1081,15 @@ class OpsWorksConnection(AWSQueryConnection):
Describe an instance's RAID arrays.
:type instance_id: string
- :param instance_id: The instance ID.
+ :param instance_id: The instance ID. If you use this parameter,
+ `DescribeRaidArrays` returns descriptions of the RAID arrays
+ associated with the specified instance.
:type raid_array_ids: list
- :param raid_array_ids: An array of RAID array IDs to be described.
+ :param raid_array_ids: An array of RAID array IDs. If you use this
+ parameter, `DescribeRaidArrays` returns descriptions of the
+ specified arrays. Otherwise, it returns a description of every
+ array.
"""
params = {}
@@ -907,17 +1103,23 @@ class OpsWorksConnection(AWSQueryConnection):
def describe_service_errors(self, stack_id=None, instance_id=None,
service_error_ids=None):
"""
- Describes OpsWorks service errors.
+ Describes AWS OpsWorks service errors.
:type stack_id: string
- :param stack_id: The stack ID.
+ :param stack_id: The stack ID. If you use this parameter,
+ `DescribeServiceErrors` returns descriptions of the errors
+ associated with the specified stack.
:type instance_id: string
- :param instance_id: The instance ID.
+ :param instance_id: The instance ID. If you use this parameter,
+ `DescribeServiceErrors` returns descriptions of the errors
+ associated with the specified instance.
:type service_error_ids: list
- :param service_error_ids: An array of service error IDs to be
- described.
+ :param service_error_ids: An array of service error IDs. If you use
+ this parameter, `DescribeServiceErrors` returns descriptions of the
+ specified errors. Otherwise, it returns a description of every
+ error.
"""
params = {}
@@ -936,7 +1138,8 @@ class OpsWorksConnection(AWSQueryConnection):
:type stack_ids: list
:param stack_ids: An array of stack IDs that specify the stacks to be
- described.
+ described. If you omit this parameter, `DescribeStacks` returns a
+ description of every stack.
"""
params = {}
@@ -977,13 +1180,19 @@ class OpsWorksConnection(AWSQueryConnection):
Describes an instance's Amazon EBS volumes.
:type instance_id: string
- :param instance_id: The instance ID.
+ :param instance_id: The instance ID. If you use this parameter,
+ `DescribeVolumes` returns descriptions of the volumes associated
+ with the specified instance.
:type raid_array_id: string
- :param raid_array_id: The RAID array ID.
+ :param raid_array_id: The RAID array ID. If you use this parameter,
+ `DescribeVolumes` returns descriptions of the volumes associated
+ with the specified RAID array.
:type volume_ids: list
- :param volume_ids: Am array of volume IDs to be described.
+ :param volume_ids: Am array of volume IDs. If you use this parameter,
+ `DescribeVolumes` returns descriptions of the specified volumes.
+ Otherwise, it returns a description of every volume.
"""
params = {}
@@ -999,13 +1208,16 @@ class OpsWorksConnection(AWSQueryConnection):
def detach_elastic_load_balancer(self, elastic_load_balancer_name,
layer_id):
"""
-
+ Detaches a specified Elastic Load Balancing instance from it's
+ layer.
:type elastic_load_balancer_name: string
- :param elastic_load_balancer_name:
+ :param elastic_load_balancer_name: The Elastic Load Balancing
+ instance's name.
:type layer_id: string
- :param layer_id:
+ :param layer_id: The ID of the layer that the Elastic Load Balancing
+ instance is attached to.
"""
params = {
@@ -1017,8 +1229,8 @@ class OpsWorksConnection(AWSQueryConnection):
def get_hostname_suggestion(self, layer_id):
"""
- Gets a generated hostname for the specified layer, based on
- the current hostname theme.
+ Gets a generated host name for the specified layer, based on
+ the current host name theme.
:type layer_id: string
:param layer_id: The layer ID.
@@ -1030,7 +1242,8 @@ class OpsWorksConnection(AWSQueryConnection):
def reboot_instance(self, instance_id):
"""
- Reboots a specified instance.
+ Reboots a specified instance. For more information, see
+ `Starting, Stopping, and Rebooting Instances`_.
:type instance_id: string
:param instance_id: The instance ID.
@@ -1044,7 +1257,8 @@ class OpsWorksConnection(AWSQueryConnection):
up_scaling=None, down_scaling=None):
"""
Specify the load-based auto scaling configuration for a
- specified layer.
+ specified layer. For more information, see `Managing Load with
+ Time-based and Load-based Instances`_.
To use load-based auto scaling, you must create a set of load-
based auto scaling instances. Load-based auto scaling operates
@@ -1061,13 +1275,13 @@ class OpsWorksConnection(AWSQueryConnection):
:type up_scaling: dict
:param up_scaling: An `AutoScalingThresholds` object with the upscaling
threshold configuration. If the load exceeds these thresholds for a
- specified amount of time, OpsWorks starts a specified number of
+ specified amount of time, AWS OpsWorks starts a specified number of
instances.
:type down_scaling: dict
:param down_scaling: An `AutoScalingThresholds` object with the
downscaling threshold configuration. If the load falls below these
- thresholds for a specified amount of time, OpsWorks stops a
+ thresholds for a specified amount of time, AWS OpsWorks stops a
specified number of instances.
"""
@@ -1084,7 +1298,8 @@ class OpsWorksConnection(AWSQueryConnection):
def set_permission(self, stack_id, iam_user_arn, allow_ssh=None,
allow_sudo=None):
"""
- Specifies a stack's permissions.
+ Specifies a stack's permissions. For more information, see
+ `Security and Permissions`_.
:type stack_id: string
:param stack_id: The stack ID.
@@ -1113,7 +1328,8 @@ class OpsWorksConnection(AWSQueryConnection):
auto_scaling_schedule=None):
"""
Specify the time-based auto scaling configuration for a
- specified instance.
+ specified instance. For more information, see `Managing Load
+ with Time-based and Load-based Instances`_.
:type instance_id: string
:param instance_id: The instance ID.
@@ -1131,7 +1347,8 @@ class OpsWorksConnection(AWSQueryConnection):
def start_instance(self, instance_id):
"""
- Starts a specified instance.
+ Starts a specified instance. For more information, see
+ `Starting, Stopping, and Rebooting Instances`_.
:type instance_id: string
:param instance_id: The instance ID.
@@ -1158,7 +1375,8 @@ class OpsWorksConnection(AWSQueryConnection):
Stops a specified instance. When you stop a standard instance,
the data disappears and must be reinstalled when you restart
the instance. You can stop an Amazon EBS-backed instance
- without losing data.
+ without losing data. For more information, see `Starting,
+ Stopping, and Rebooting Instances`_.
:type instance_id: string
:param instance_id: The instance ID.
@@ -1203,7 +1421,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type domains: list
:param domains: The app's virtual host settings, with multiple domains
- separated by commas. For example: `'www.mysite.com, mysite.com'`
+ separated by commas. For example: `'www.example.com, example.com'`
:type enable_ssl: boolean
:param enable_ssl: Whether SSL is enabled for the app.
@@ -1239,8 +1457,9 @@ class OpsWorksConnection(AWSQueryConnection):
def update_instance(self, instance_id, layer_ids=None,
instance_type=None, auto_scaling_type=None,
- hostname=None, os=None, ssh_key_name=None,
- architecture=None):
+ hostname=None, os=None, ami_id=None,
+ ssh_key_name=None, architecture=None,
+ install_updates_on_boot=None):
"""
Updates a specified instance.
@@ -1251,26 +1470,18 @@ class OpsWorksConnection(AWSQueryConnection):
:param layer_ids: The instance's layer IDs.
:type instance_type: string
- :param instance_type:
- The instance type, which can be one of the following:
-
-
- + m1.small
- + m1.medium
- + m1.large
- + m1.xlarge
- + c1.medium
- + c1.xlarge
- + m2.xlarge
- + m2.2xlarge
- + m2.4xlarge
+ :param instance_type: The instance type. AWS OpsWorks supports all
+ instance types except Cluster Compute, Cluster GPU, and High Memory
+ Cluster. For more information, see `Instance Families and Types`_.
+ The parameter values that you use to specify the various types are
+ in the API Name column of the Available Instance Types table.
:type auto_scaling_type: string
:param auto_scaling_type:
The instance's auto scaling type, which has three possible values:
- + **AlwaysRunning**: A 24x7 instance, which is not affected by auto
+ + **AlwaysRunning**: A 24/7 instance, which is not affected by auto
scaling.
+ **TimeBasedAutoScaling**: A time-based auto scaling instance, which
is started and stopped based on a specified schedule.
@@ -1283,11 +1494,32 @@ class OpsWorksConnection(AWSQueryConnection):
:type os: string
:param os: The instance operating system.
+ :type ami_id: string
+ :param ami_id: A custom AMI ID to be used to create the instance. The
+ AMI should be based on one of the standard AWS OpsWorks APIs:
+ Amazon Linux or Ubuntu 12.04 LTS. For more information, see
+ `Instances`_
+
:type ssh_key_name: string
:param ssh_key_name: The instance SSH key name.
:type architecture: string
- :param architecture:
+ :param architecture: The instance architecture. Instance types do not
+ necessarily support both architectures. For a list of the
+ architectures that are supported by the different instance types,
+ see `Instance Families and Types`_.
+
+ :type install_updates_on_boot: boolean
+ :param install_updates_on_boot:
+ Whether to install operating system and package updates when the
+ instance boots. The default value is `True`. To control when
+ updates are installed, set this value to `False`. You must then
+ update your instances manually by using CreateDeployment to run the
+ `update_dependencies` stack command or manually running `yum`
+ (Amazon Linux) or `apt-get` (Ubuntu) on the instances.
+
+ We strongly recommend using the default value of `True`, to ensure that
+ your instances have the latest security updates.
"""
params = {'InstanceId': instance_id, }
@@ -1301,10 +1533,14 @@ class OpsWorksConnection(AWSQueryConnection):
params['Hostname'] = hostname
if os is not None:
params['Os'] = os
+ if ami_id is not None:
+ params['AmiId'] = ami_id
if ssh_key_name is not None:
params['SshKeyName'] = ssh_key_name
if architecture is not None:
params['Architecture'] = architecture
+ if install_updates_on_boot is not None:
+ params['InstallUpdatesOnBoot'] = install_updates_on_boot
return self.make_request(action='UpdateInstance',
body=json.dumps(params))
@@ -1312,7 +1548,8 @@ class OpsWorksConnection(AWSQueryConnection):
attributes=None, custom_instance_profile_arn=None,
custom_security_group_ids=None, packages=None,
volume_configurations=None, enable_auto_healing=None,
- auto_assign_elastic_ips=None, custom_recipes=None):
+ auto_assign_elastic_ips=None, custom_recipes=None,
+ install_updates_on_boot=None):
"""
Updates a specified layer.
@@ -1323,10 +1560,10 @@ class OpsWorksConnection(AWSQueryConnection):
:param name: The layer name, which is used by the console.
:type shortname: string
- :param shortname: The layer short name, which is used internally by
- OpsWorks, by Chef. The shortname is also used as the name for the
- directory where your app files are installed. It can have a maximum
- of 200 characters and must be in the following format:
+ :param shortname: The layer short name, which is used internally by AWS
+ OpsWorksand by Chef. The short name is also used as the name for
+ the directory where your app files are installed. It can have a
+ maximum of 200 characters and must be in the following format:
/\A[a-z0-9\-\_\.]+\Z/.
:type attributes: map
@@ -1362,6 +1599,18 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_recipes: A `LayerCustomRecipes` object that specifies the
layer's custom recipes.
+ :type install_updates_on_boot: boolean
+ :param install_updates_on_boot:
+ Whether to install operating system and package updates when the
+ instance boots. The default value is `True`. To control when
+ updates are installed, set this value to `False`. You must then
+ update your instances manually by using CreateDeployment to run the
+ `update_dependencies` stack command or manually running `yum`
+ (Amazon Linux) or `apt-get` (Ubuntu) on the instances.
+
+ We strongly recommend using the default value of `True`, to ensure that
+ your instances have the latest security updates.
+
"""
params = {'LayerId': layer_id, }
if name is not None:
@@ -1384,6 +1633,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['AutoAssignElasticIps'] = auto_assign_elastic_ips
if custom_recipes is not None:
params['CustomRecipes'] = custom_recipes
+ if install_updates_on_boot is not None:
+ params['InstallUpdatesOnBoot'] = install_updates_on_boot
return self.make_request(action='UpdateLayer',
body=json.dumps(params))
@@ -1391,8 +1642,9 @@ class OpsWorksConnection(AWSQueryConnection):
service_role_arn=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
- custom_json=None, use_custom_cookbooks=None,
- custom_cookbooks_source=None, default_ssh_key_name=None,
+ custom_json=None, configuration_manager=None,
+ use_custom_cookbooks=None, custom_cookbooks_source=None,
+ default_ssh_key_name=None,
default_root_device_type=None):
"""
Updates a specified stack.
@@ -1408,11 +1660,17 @@ class OpsWorksConnection(AWSQueryConnection):
to the stack attributes bag.
:type service_role_arn: string
- :param service_role_arn: The stack AWS Identity and Access Management
- (IAM) role, which allows OpsWorks to work with AWS resources on
- your behalf. You must set this parameter to the Amazon Resource
- Name (ARN) for an existing IAM role. For more information about IAM
- ARNs, see `Using Identifiers`_.
+ :param service_role_arn:
+ The stack AWS Identity and Access Management (IAM) role, which allows
+ AWS OpsWorks to work with AWS resources on your behalf. You must
+ set this parameter to the Amazon Resource Name (ARN) for an
+ existing IAM role. For more information about IAM ARNs, see `Using
+ Identifiers`_.
+
+ You must set this parameter to a valid service role ARN or the action
+ will fail; there is no default value. You can specify the stack's
+ current service role ARN, if you prefer, but you must do so
+ explicitly.
:type default_instance_profile_arn: string
:param default_instance_profile_arn: The ARN of an IAM profile that is
@@ -1420,15 +1678,26 @@ class OpsWorksConnection(AWSQueryConnection):
information about IAM ARNs, see `Using Identifiers`_.
:type default_os: string
- :param default_os: The cloned stack default operating system, which
- must be either "Amazon Linux" or "Ubuntu 12.04 LTS".
+ :param default_os: The stack default operating system, which must be
+ set to one of the following.
+
+ + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
+ + Custom AMIs: `Custom`
+
+
+ The default option is `Amazon Linux`. If you set this parameter to
+ `Custom`, you must use the CreateInstance action's AmiId parameter
+ to specify the custom AMI that you want to use. For more
+ information on the standard operating systems, see `Operating
+ Systems`_For more information on how to use custom AMIs with
+ OpsWorks, see `Using Custom AMIs`_.
:type hostname_theme: string
:param hostname_theme: The stack's new host name theme, with spaces are
- replaced by underscores. The theme is used to generate hostnames
+ replaced by underscores. The theme is used to generate host names
for the stack's instances. By default, `HostnameTheme` is set to
- Layer_Dependent, which creates hostnames by appending integers to
- the layer's shortname. The other themes are:
+ Layer_Dependent, which creates host names by appending integers to
+ the layer's short name. The other themes are:
+ Baked_Goods
+ Clouds
@@ -1443,33 +1712,46 @@ class OpsWorksConnection(AWSQueryConnection):
+ Wild_Cats
- To obtain a generated hostname, call `GetHostNameSuggestion`, which
- returns a hostname based on the current theme.
+ To obtain a generated host name, call `GetHostNameSuggestion`, which
+ returns a host name based on the current theme.
:type default_availability_zone: string
:param default_availability_zone: The stack new default Availability
Zone. For more information, see `Regions and Endpoints`_.
:type custom_json: string
- :param custom_json:
- A string that contains user-defined, custom JSON. It is used to
- override the corresponding default stack configuration JSON values.
- The string should be in the following format and must escape
- characters such as '"'.:
- `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
+ :param custom_json: A string that contains user-defined, custom JSON.
+ It is used to override the corresponding default stack
+ configuration JSON values. The string should be in the following
+ format and must escape characters such as '"'.: `"{\"key1\":
+ \"value1\", \"key2\": \"value2\",...}"`
+ For more information on custom JSON, see `Use Custom JSON to Modify the
+ Stack Configuration JSON`_.
+
+ :type configuration_manager: dict
+ :param configuration_manager: The configuration manager. When you
+ update a stack you can optionally use the configuration manager to
+ specify the Chef version, 0.9 or 11.4. If you omit this parameter,
+ AWS OpsWorks does not change the Chef version.
:type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether the stack uses custom cookbooks.
:type custom_cookbooks_source: dict
- :param custom_cookbooks_source:
+ :param custom_cookbooks_source: Contains the information required to
+ retrieve an app or cookbook from a repository. For more
+ information, see `Creating Apps`_ or `Custom Recipes and
+ Cookbooks`_.
:type default_ssh_key_name: string
:param default_ssh_key_name: A default SSH key for the stack instances.
You can override this value when you create or update an instance.
:type default_root_device_type: string
- :param default_root_device_type:
+ :param default_root_device_type: The default root device type. This
+ value is used by default for all instances in the cloned stack, but
+ you can override it when you create an instance. For more
+ information, see `Storage for the Root Device`_.
"""
params = {'StackId': stack_id, }
@@ -1489,6 +1771,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['DefaultAvailabilityZone'] = default_availability_zone
if custom_json is not None:
params['CustomJson'] = custom_json
+ if configuration_manager is not None:
+ params['ConfigurationManager'] = configuration_manager
if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks
if custom_cookbooks_source is not None:
@@ -1503,7 +1787,7 @@ class OpsWorksConnection(AWSQueryConnection):
def update_user_profile(self, iam_user_arn, ssh_username=None,
ssh_public_key=None):
"""
- Updates a specified user's SSH name and public key.
+ Updates a specified user profile.
:type iam_user_arn: string
:param iam_user_arn: The user IAM ARN.
diff --git a/boto/rds/__init__.py b/boto/rds/__init__.py
index ec3305cf..42b2d910 100644
--- a/boto/rds/__init__.py
+++ b/boto/rds/__init__.py
@@ -29,6 +29,7 @@ from boto.rds.parametergroup import ParameterGroup
from boto.rds.dbsnapshot import DBSnapshot
from boto.rds.event import Event
from boto.rds.regioninfo import RDSRegionInfo
+from boto.rds.dbsubnetgroup import DBSubnetGroup
def regions():
@@ -1199,6 +1200,98 @@ class RDSConnection(AWSQueryConnection):
params['Marker'] = marker
return self.get_list('DescribeEvents', params, [('Event', Event)])
+ def create_db_subnet_group(self, name, desc, subnet_ids):
+ """
+ Create a new Database Subnet Group.
+
+ :type name: string
+ :param name: The identifier for the db_subnet_group
+
+ :type desc: string
+ :param desc: A description of the db_subnet_group
+
+ :type subnet_ids: list
+ :param subnets: A list of the subnet identifiers to include in the
+ db_subnet_group
+
+ :rtype: :class:`boto.rds.dbsubnetgroup.DBSubnetGroup
+ :return: the created db_subnet_group
+ """
+
+ params = {'DBSubnetGroupName': name,
+ 'DBSubnetGroupDescription': desc}
+ self.build_list_params(params, subnet_ids, 'SubnetIds.member')
+
+ return self.get_object('CreateDBSubnetGroup', params, DBSubnetGroup)
+
+ def delete_db_subnet_group(self, name):
+ """
+ Delete a Database Subnet Group.
+
+ :type name: string
+ :param name: The identifier of the db_subnet_group to delete
+
+ :rtype: :class:`boto.rds.dbsubnetgroup.DBSubnetGroup`
+ :return: The deleted db_subnet_group.
+ """
+
+ params = {'DBSubnetGroupName': name}
+
+ return self.get_object('DeleteDBSubnetGroup', params, DBSubnetGroup)
+
+
+ def get_all_db_subnet_groups(self, name=None, max_records=None, marker=None):
+ """
+ Retrieve all the DBSubnetGroups in your account.
+
+ :type name: str
+ :param name: DBSubnetGroup name If supplied, only information about
+ this DBSubnetGroup will be returned. Otherwise, info
+ about all DBSubnetGroups will be returned.
+
+ :type max_records: int
+ :param max_records: The maximum number of records to be returned.
+ If more results are available, a Token will be
+ returned in the response that can be used to
+ retrieve additional records. Default is 100.
+
+ :type marker: str
+ :param marker: The marker provided by a previous request.
+
+ :rtype: list
+ :return: A list of :class:`boto.rds.dbsubnetgroup.DBSubnetGroup`
+ """
+ params = dict()
+ if name != None:
+ params['DBSubnetGroupName'] = name
+ if max_records != None:
+ params['MaxRecords'] = max_records
+ if marker != None:
+ params['Marker'] = marker
+
+ return self.get_list('DescribeDBSubnetGroups', params, [('DBSubnetGroup',DBSubnetGroup)])
+
+ def modify_db_subnet_group(self, name, description=None, subnet_ids=None):
+ """
+ Modify a parameter group for your account.
+
+ :type name: string
+ :param name: The name of the new parameter group
+
+ :type parameters: list of :class:`boto.rds.parametergroup.Parameter`
+ :param parameters: The new parameters
+
+ :rtype: :class:`boto.rds.parametergroup.ParameterGroup`
+ :return: The newly created ParameterGroup
+ """
+ params = {'DBSubnetGroupName': name}
+ if description != None:
+ params['DBSubnetGroupDescription'] = description
+ if subnet_ids != None:
+ self.build_list_params(params, subnet_ids, 'SubnetIds.member')
+
+ return self.get_object('ModifyDBSubnetGroup', params, DBSubnetGroup)
+
def create_option_group(self, name, engine_name, major_engine_version,
description=None):
"""
diff --git a/boto/rds/dbsubnetgroup.py b/boto/rds/dbsubnetgroup.py
new file mode 100644
index 00000000..4b9fc580
--- /dev/null
+++ b/boto/rds/dbsubnetgroup.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2013 Franc Carter - franc.carter@gmail.com
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish, dis-
+# tribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the fol-
+# lowing conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+"""
+Represents an DBSubnetGroup
+"""
+
+class DBSubnetGroup(object):
+ """
+ Represents an RDS database subnet group
+
+ Properties reference available from the AWS documentation at http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DeleteDBSubnetGroup.html
+
+ :ivar status: The current status of the subnet group. Possibile values are [ active, ? ]. Reference documentation lacks specifics of possibilities
+ :ivar connection: boto.rds.RDSConnection associated with the current object
+ :ivar description: The description of the subnet group
+ :ivar subnet_ids: List of subnet identifiers in the group
+ :ivar name: Name of the subnet group
+ :ivar vpc_id: The ID of the VPC the subnets are inside
+ """
+ def __init__(self, connection=None, name=None, description=None, subnet_ids=None):
+ self.connection = connection
+ self.name = name
+ self.description = description
+ if subnet_ids != None:
+ self.subnet_ids = subnet_ids
+ else:
+ self.subnet_ids = []
+ self.vpc_id = None
+ self.status = None
+
+ def __repr__(self):
+ return 'DBSubnetGroup:%s' % self.name
+
+ def startElement(self, name, attrs, connection):
+ pass
+
+ def endElement(self, name, value, connection):
+ if name == 'SubnetIdentifier':
+ self.subnet_ids.append(value)
+ elif name == 'DBSubnetGroupName':
+ self.name = value
+ elif name == 'DBSubnetGroupDescription':
+ self.description = value
+ elif name == 'VpcId':
+ self.vpc_id = value
+ elif name == 'SubnetGroupStatus':
+ self.status = value
+ else:
+ setattr(self, name, value)
+
diff --git a/boto/s3/key.py b/boto/s3/key.py
index 9c7b4b27..d16352fd 100644
--- a/boto/s3/key.py
+++ b/boto/s3/key.py
@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+from __future__ import with_statement
import errno
import mimetypes
import os
@@ -147,38 +148,38 @@ class Key(object):
provider = self.bucket.connection.provider
return provider
- @property
- def key(self):
+ def _get_key(self):
return self.name
- @key.setter
- def key(self, value):
+ def _set_key(self, value):
self.name = value
- @property
- def md5(self):
+ key = property(_get_key, _set_key);
+
+ def _get_md5(self):
if 'md5' in self.local_hashes and self.local_hashes['md5']:
return binascii.b2a_hex(self.local_hashes['md5'])
- @md5.setter
- def md5(self, value):
+ def _set_md5(self, value):
if value:
self.local_hashes['md5'] = binascii.a2b_hex(value)
elif 'md5' in self.local_hashes:
self.local_hashes.pop('md5', None)
- @property
- def base64md5(self):
+ md5 = property(_get_md5, _set_md5);
+
+ def _get_base64md5(self):
if 'md5' in self.local_hashes and self.local_hashes['md5']:
return binascii.b2a_base64(self.local_hashes['md5']).rstrip('\n')
- @base64md5.setter
- def base64md5(self, value):
+ def _set_base64md5(self, value):
if value:
self.local_hashes['md5'] = binascii.a2b_base64(value)
elif 'md5' in self.local_hashes:
del self.local_hashes['md5']
+ base64md5 = property(_get_base64md5, _set_base64md5);
+
def get_md5_from_hexdigest(self, md5_hexdigest):
"""
A utility function to create the 2-tuple (md5hexdigest, base64md5)
@@ -1464,7 +1465,7 @@ class Key(object):
if i == cb_count or cb_count == -1:
cb(data_len, cb_size)
i = 0
- except IOError as e:
+ except IOError, e:
if e.errno == errno.ENOSPC:
raise StorageDataError('Out of space for destination file '
'%s' % fp.name)
diff --git a/boto/s3/keyfile.py b/boto/s3/keyfile.py
index 4245413d..84858a2b 100644
--- a/boto/s3/keyfile.py
+++ b/boto/s3/keyfile.py
@@ -75,7 +75,7 @@ class KeyFile():
raise IOError('Invalid whence param (%d) passed to seek' % whence)
try:
self.key.open_read(headers={"Range": "bytes=%d-" % pos})
- except StorageResponseError as e:
+ except StorageResponseError, e:
# 416 Invalid Range means that the given starting byte was past the end
# of file. We catch this because the Python file interface allows silently
# seeking past the end of the file.
diff --git a/boto/ses/connection.py b/boto/ses/connection.py
index b5bd157b..8652042d 100644
--- a/boto/ses/connection.py
+++ b/boto/ses/connection.py
@@ -103,7 +103,8 @@ class SESConnection(AWSAuthConnection):
body = response.read()
if response.status == 200:
list_markers = ('VerifiedEmailAddresses', 'Identities',
- 'VerificationAttributes', 'SendDataPoints')
+ 'DkimTokens', 'VerificationAttributes',
+ 'SendDataPoints')
item_markers = ('member', 'item', 'entry')
e = boto.jsonresponse.Element(list_marker=list_markers,
diff --git a/boto/sqs/message.py b/boto/sqs/message.py
index 43efee38..f0666e56 100644
--- a/boto/sqs/message.py
+++ b/boto/sqs/message.py
@@ -199,6 +199,9 @@ class MHMessage(Message):
s = s + '%s: %s\n' % (item[0], item[1])
return s
+ def __contains__(self, key):
+ return key in self._body
+
def __getitem__(self, key):
if key in self._body:
return self._body[key]
diff --git a/docs/source/index.rst b/docs/source/index.rst
index f720cf1b..2e1370f7 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -111,6 +111,7 @@ Release Notes
.. toctree::
:titlesonly:
+ releasenotes/v2.9.9
releasenotes/v2.9.8
releasenotes/v2.9.7
releasenotes/v2.9.6
diff --git a/docs/source/releasenotes/v2.9.9.rst b/docs/source/releasenotes/v2.9.9.rst
new file mode 100644
index 00000000..e9a0bfd2
--- /dev/null
+++ b/docs/source/releasenotes/v2.9.9.rst
@@ -0,0 +1,50 @@
+boto v2.9.9
+===========
+
+:date: 2013/07/24
+
+This release updates Opsworks to add AMI & Chef 11 support, DBSubnetGroup
+support in RDS & many other bugfixes.
+
+
+Features
+--------
+
+* Added AMI, configuration manager & Chef 11 support to Opsworks.
+ (:sha:`55725fc`).
+* Added ``in`` support for SQS messages. (:issue:`1593`, :sha:`e5fe1ed`)
+* Added support for the ``ap-southeast-2`` region in Elasticache.
+ (:issue:`1607`, :sha:`9986b61`)
+* Added support for block device mappings in ELB. (:issue:`1343`, :issue:`753`,
+ :issue:`1357`, :sha:`974a23a`)
+* Added support for DBSubnetGroup in RDS. (:issue:`1500`, :sha:`01eef87`,
+ :sha:`45c60a0`, :sha:`c4c859e`)
+
+
+Bugfixes
+--------
+
+* Fixed the canonicalization of paths on Windows. (:issue:`1609`,
+ :sha:`a1fa98c`)
+* Fixed how ``BotoServerException`` uses ``message``. (:issue:`1353`,
+ :sha:`b944f4b`)
+* Fixed ``DisableRollback`` always being ``True`` in a CloudFormation ``Stack``.
+ (:issue:`1379`, :sha:`32b3150`)
+* Changed EMR instance groups to no longer require a string price (can now be
+ a ``Decimal``). (:issue:`1396`, :sha:`dfc39ff`)
+* Altered ``Distribution._sign_string`` to accept any file-like object as well
+ within CloudFront. (:issue:`1349`, :sha:`8df6c14`)
+* Fixed the ``detach_lb_from_subnets`` call within ELB. (:issue:`1417`,
+ :issue:`1418` :sha:`4a397bd`, :sha:`c11d72b`, :sha:`9e595b5`, :sha:`634469d`,
+ :sha:`586dd54`)
+* Altered boto to obey ``no_proxy`` environment variables. (:issue:`1600`,
+ :issue:`1603`, :sha:`aaef5a9`)
+* Fixed ELB connections to use HTTPS by default. (:issue:`1587`, :sha:`fe158c4`)
+* Updated S3 to be Python 2.5 compatible again. (:issue:`1598`, :sha:`066009f`)
+* All calls within SES will now return *all* DKIMTokens, instead of just one.
+ (:issue:`1550`, :issue:`1610`, :sha:`1a079da`, :sha:`1e82f85`, :sha:`5c8b6b8`)
+* Fixed the ``logging`` parameter within ``DistributionConfig`` in CloudFront
+ to respect whatever is provided to the constructor. (:issue:`1457`,
+ :sha:`e76180d`)
+* Fixed CloudSearch to no longer raise an error if a non-JSON response is received.
+ (:issue:`1555`, :issue:`1614`, :sha:`5e2c292`, :sha:`6510e1f`)
diff --git a/tests/integration/rds/test_db_subnet_group.py b/tests/integration/rds/test_db_subnet_group.py
new file mode 100644
index 00000000..52d63739
--- /dev/null
+++ b/tests/integration/rds/test_db_subnet_group.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2013 Franc Carter franc.carter@gmail.com
+# All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish, dis-
+# tribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the fol-
+# lowing conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+"""
+Check that db_subnet_groups behave sanely
+"""
+
+import time
+import unittest
+import boto.rds
+from boto.vpc import VPCConnection
+from boto.rds import RDSConnection
+
+def _is_ok(subnet_group, vpc_id, description, subnets):
+ if subnet_group.vpc_id != vpc_id:
+ print 'vpc_id is ',subnet_group.vpc_id, 'but should be ', vpc_id
+ return 0
+ if subnet_group.description != description:
+ print "description is '"+subnet_group.description+"' but should be '"+description+"'"
+ return 0
+ if set(subnet_group.subnet_ids) != set(subnets):
+ subnets_are = ','.join(subnet_group.subnet_ids)
+ should_be = ','.join(subnets)
+ print "subnets are "+subnets_are+" but should be "+should_be
+ return 0
+ return 1
+
+
+class DbSubnetGroupTest(unittest.TestCase):
+ rds = True
+
+ def test_db_subnet_group(self):
+ vpc_api = VPCConnection()
+ rds_api = RDSConnection()
+ vpc = vpc_api.create_vpc('10.0.0.0/16')
+
+ az_list = vpc_api.get_all_zones(filters={'state':'available'})
+ subnet = list()
+ n = 0;
+ for az in az_list:
+ try:
+ subnet.append(vpc_api.create_subnet(vpc.id, '10.0.'+str(n)+'.0/24',availability_zone=az.name))
+ n = n+1
+ except:
+ pass
+
+ grp_name = 'db_subnet_group'+str(int(time.time()))
+ subnet_group = rds_api.create_db_subnet_group(grp_name, grp_name, [subnet[0].id,subnet[1].id])
+ if not _is_ok(subnet_group, vpc.id, grp_name, [subnet[0].id,subnet[1].id]):
+ raise Exception("create_db_subnet_group returned bad values")
+
+ rds_api.modify_db_subnet_group(grp_name, description='new description')
+ subnet_grps = rds_api.get_all_db_subnet_groups(name=grp_name)
+ if not _is_ok(subnet_grps[0], vpc.id, 'new description', [subnet[0].id,subnet[1].id]):
+ raise Exception("modifying the subnet group desciption returned bad values")
+
+ rds_api.modify_db_subnet_group(grp_name, subnet_ids=[subnet[1].id,subnet[2].id])
+ subnet_grps = rds_api.get_all_db_subnet_groups(name=grp_name)
+ if not _is_ok(subnet_grps[0], vpc.id, 'new description', [subnet[1].id,subnet[2].id]):
+ raise Exception("modifying the subnet group subnets returned bad values")
+
+ rds_api.delete_db_subnet_group(subnet_group.name)
+ try:
+ rds_api.get_all_db_subnet_groups(name=grp_name)
+ raise Exception(subnet_group.name+" still accessible after delete_db_subnet_group")
+ except:
+ pass
+
+ while n > 0:
+ n = n-1
+ vpc_api.delete_subnet(subnet[n].id)
+ vpc_api.delete_vpc(vpc.id)
+
diff --git a/tests/unit/auth/test_sigv4.py b/tests/unit/auth/test_sigv4.py
index 2de6d724..64a72ab3 100644
--- a/tests/unit/auth/test_sigv4.py
+++ b/tests/unit/auth/test_sigv4.py
@@ -91,6 +91,14 @@ class TestSigV4Handler(unittest.TestCase):
# There should not be two-slashes.
self.assertEqual(canonical_uri, '/')
+ # Make sure Windows-style slashes are converted properly
+ request = HTTPRequest(
+ 'GET', 'https', 'glacier.us-east-1.amazonaws.com', 443,
+ '\\x\\x.html', None, {},
+ {'x-amz-glacier-version': '2012-06-01'}, '')
+ canonical_uri = auth.canonical_uri(request)
+ self.assertEqual(canonical_uri, '/x/x.html')
+
def test_headers_to_sign(self):
auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com',
Mock(), self.provider)
diff --git a/tests/unit/cloudformation/test_connection.py b/tests/unit/cloudformation/test_connection.py
index 6890f152..7b1fbc59 100644
--- a/tests/unit/cloudformation/test_connection.py
+++ b/tests/unit/cloudformation/test_connection.py
@@ -421,7 +421,7 @@ class TestCloudFormationDescribeStacks(CloudFormationConnectionBase):
self.assertEqual(stack.creation_time,
datetime(2012, 5, 16, 22, 55, 31))
self.assertEqual(stack.description, 'My Description')
- self.assertEqual(stack.disable_rollback, True)
+ self.assertEqual(stack.disable_rollback, False)
self.assertEqual(stack.stack_id, 'arn:aws:cfn:us-east-1:1:stack')
self.assertEqual(stack.stack_status, 'CREATE_COMPLETE')
self.assertEqual(stack.stack_name, 'MyStack')
diff --git a/tests/unit/cloudformation/test_stack.py b/tests/unit/cloudformation/test_stack.py
index 0f39184e..f42fee2a 100644
--- a/tests/unit/cloudformation/test_stack.py
+++ b/tests/unit/cloudformation/test_stack.py
@@ -218,5 +218,39 @@ class TestStackParse(unittest.TestCase):
datetime.datetime(2011, 6, 21, 20, 25, 57, 875643)
)
+ def test_disable_rollback_false(self):
+ # SAMPLE_XML defines DisableRollback=="false"
+ rs = boto.resultset.ResultSet([('member', boto.cloudformation.stack.Stack)])
+ h = boto.handler.XmlHandler(rs, None)
+ xml.sax.parseString(SAMPLE_XML, h)
+ disable_rollback = rs[0].disable_rollback
+ self.assertFalse(disable_rollback)
+
+ def test_disable_rollback_false_upper(self):
+ # Should also handle "False"
+ rs = boto.resultset.ResultSet([('member', boto.cloudformation.stack.Stack)])
+ h = boto.handler.XmlHandler(rs, None)
+ sample_xml_upper = SAMPLE_XML.replace('false', 'False')
+ xml.sax.parseString(sample_xml_upper, h)
+ disable_rollback = rs[0].disable_rollback
+ self.assertFalse(disable_rollback)
+
+ def test_disable_rollback_true(self):
+ rs = boto.resultset.ResultSet([('member', boto.cloudformation.stack.Stack)])
+ h = boto.handler.XmlHandler(rs, None)
+ sample_xml_upper = SAMPLE_XML.replace('false', 'true')
+ xml.sax.parseString(sample_xml_upper, h)
+ disable_rollback = rs[0].disable_rollback
+ self.assertTrue(disable_rollback)
+
+ def test_disable_rollback_true_upper(self):
+ rs = boto.resultset.ResultSet([('member', boto.cloudformation.stack.Stack)])
+ h = boto.handler.XmlHandler(rs, None)
+ sample_xml_upper = SAMPLE_XML.replace('false', 'True')
+ xml.sax.parseString(sample_xml_upper, h)
+ disable_rollback = rs[0].disable_rollback
+ self.assertTrue(disable_rollback)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/cloudfront/test_distribution.py b/tests/unit/cloudfront/test_distribution.py
new file mode 100644
index 00000000..38e27106
--- /dev/null
+++ b/tests/unit/cloudfront/test_distribution.py
@@ -0,0 +1,21 @@
+import unittest
+
+from boto.cloudfront.distribution import DistributionConfig
+from boto.cloudfront.logging import LoggingInfo
+
+
+class CloudfrontDistributionTest(unittest.TestCase):
+ cloudfront = True
+
+ def setUp(self):
+ self.dist = DistributionConfig()
+
+ def test_logging(self):
+ # Default.
+ self.assertEqual(self.dist.logging, None)
+
+ # Override.
+ lo = LoggingInfo(bucket='whatever', prefix='override_')
+ dist = DistributionConfig(logging=lo)
+ self.assertEqual(dist.logging.bucket, 'whatever')
+ self.assertEqual(dist.logging.prefix, 'override_')
diff --git a/tests/unit/cloudfront/test_signed_urls.py b/tests/unit/cloudfront/test_signed_urls.py
index e1a19f06..30d9035e 100644
--- a/tests/unit/cloudfront/test_signed_urls.py
+++ b/tests/unit/cloudfront/test_signed_urls.py
@@ -4,6 +4,7 @@ try:
import simplejson as json
except ImportError:
import json
+from cStringIO import StringIO
from textwrap import dedent
from boto.cloudfront.distribution import Distribution
@@ -132,6 +133,22 @@ class CloudfrontSignedUrlsTest(unittest.TestCase):
encoded_sig = self.dist._url_base64_encode(sig)
self.assertEqual(expected, encoded_sig)
+ def test_sign_canned_policy_pk_file_like(self):
+ """
+ Test signing the canned policy from amazon's cloudfront documentation
+ with a file-like object (not a subclass of 'file' type)
+ """
+ expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
+ "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
+ "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
+ "t9w2EOwi6sIIqrg_")
+ pk_file = StringIO()
+ pk_file.write(self.pk_str)
+ pk_file.seek(0)
+ sig = self.dist._sign_string(self.canned_policy, private_key_file=pk_file)
+ encoded_sig = self.dist._url_base64_encode(sig)
+ self.assertEqual(expected, encoded_sig)
+
def test_sign_canned_policy_unicode(self):
"""
Test signing the canned policy from amazon's cloudfront documentation.
diff --git a/tests/unit/cloudsearch/test_search.py b/tests/unit/cloudsearch/test_search.py
index 7cadf659..990e4637 100644
--- a/tests/unit/cloudsearch/test_search.py
+++ b/tests/unit/cloudsearch/test_search.py
@@ -6,7 +6,7 @@ from httpretty import HTTPretty
import urlparse
import json
-from boto.cloudsearch.search import SearchConnection
+from boto.cloudsearch.search import SearchConnection, SearchServiceException
HOSTNAME = "search-demo-userdomain.us-east-1.cloudsearch.amazonaws.com"
FULL_URL = 'http://%s/2011-02-01/search' % HOSTNAME
@@ -45,6 +45,8 @@ class CloudSearchSearchBaseTest(unittest.TestCase):
},
]
+ content_type = "text/xml"
+ response_status = 200
def get_args(self, requestline):
(_, request, _) = requestline.split(" ")
@@ -54,9 +56,15 @@ class CloudSearchSearchBaseTest(unittest.TestCase):
def setUp(self):
HTTPretty.enable()
+ body = self.response
+
+ if not isinstance(body, basestring):
+ body = json.dumps(body)
+
HTTPretty.register_uri(HTTPretty.GET, FULL_URL,
- body=json.dumps(self.response),
- content_type="text/xml")
+ body=body,
+ content_type=self.content_type,
+ status=self.response_status)
def tearDown(self):
HTTPretty.disable()
@@ -355,3 +363,27 @@ class CloudSearchSearchFacetTest(CloudSearchSearchBaseTest):
self.assertTrue('tags' not in results.facets)
self.assertEqual(results.facets['animals'], {u'lions': u'1', u'fish': u'2'})
+
+
+class CloudSearchNonJsonTest(CloudSearchSearchBaseTest):
+ response = '<html><body><h1>500 Internal Server Error</h1></body></html>'
+ response_status = 500
+ content_type = 'text/xml'
+
+ def test_response(self):
+ search = SearchConnection(endpoint=HOSTNAME)
+
+ with self.assertRaises(SearchServiceException):
+ search.search(q='Test')
+
+
+class CloudSearchUnauthorizedTest(CloudSearchSearchBaseTest):
+ response = '<html><body><h1>403 Forbidden</h1>foo bar baz</body></html>'
+ response_status = 403
+ content_type = 'text/html'
+
+ def test_response(self):
+ search = SearchConnection(endpoint=HOSTNAME)
+
+ with self.assertRaisesRegexp(SearchServiceException, 'foo bar baz'):
+ search.search(q='Test')
diff --git a/tests/unit/ec2/autoscale/test_group.py b/tests/unit/ec2/autoscale/test_group.py
index 28941545..e1e942f0 100644
--- a/tests/unit/ec2/autoscale/test_group.py
+++ b/tests/unit/ec2/autoscale/test_group.py
@@ -29,6 +29,9 @@ from tests.unit import AWSMockServiceTestCase
from boto.ec2.autoscale import AutoScaleConnection
from boto.ec2.autoscale.group import AutoScalingGroup
+from boto.ec2.blockdevicemapping import EBSBlockDeviceType, BlockDeviceMapping
+
+from boto.ec2.autoscale import launchconfig
class TestAutoScaleGroup(AWSMockServiceTestCase):
connection_class = AutoScaleConnection
@@ -195,6 +198,56 @@ class TestDescribeTerminationPolicies(AWSMockServiceTestCase):
['ClosestToNextInstanceHour', 'Default',
'NewestInstance', 'OldestInstance', 'OldestLaunchConfiguration'])
+class TestLaunchConfiguration(AWSMockServiceTestCase):
+ connection_class = AutoScaleConnection
+
+ def default_body(self):
+ # This is a dummy response
+ return """
+ <DescribeLaunchConfigurationsResponse>
+ </DescribeLaunchConfigurationsResponse>
+ """
+
+ def test_launch_config(self):
+ # This unit test is based on #753 and #1343
+ self.set_http_response(status_code=200)
+ dev_sdf = EBSBlockDeviceType(snapshot_id='snap-12345')
+ dev_sdg = EBSBlockDeviceType(snapshot_id='snap-12346')
+
+ bdm = BlockDeviceMapping()
+ bdm['/dev/sdf'] = dev_sdf
+ bdm['/dev/sdg'] = dev_sdg
+
+ lc = launchconfig.LaunchConfiguration(
+ connection=self.service_connection,
+ name='launch_config',
+ image_id='123456',
+ instance_type = 'm1.large',
+ security_groups = ['group1', 'group2'],
+ spot_price='price',
+ block_device_mappings = [bdm]
+ )
+
+ response = self.service_connection.create_launch_configuration(lc)
+
+ self.assert_request_parameters({
+ 'Action': 'CreateLaunchConfiguration',
+ 'BlockDeviceMapping.1.DeviceName': '/dev/sdf',
+ 'BlockDeviceMapping.1.Ebs.DeleteOnTermination': 'false',
+ 'BlockDeviceMapping.1.Ebs.SnapshotId': 'snap-12345',
+ 'BlockDeviceMapping.2.DeviceName': '/dev/sdg',
+ 'BlockDeviceMapping.2.Ebs.DeleteOnTermination': 'false',
+ 'BlockDeviceMapping.2.Ebs.SnapshotId': 'snap-12346',
+ 'EbsOptimized': 'false',
+ 'LaunchConfigurationName': 'launch_config',
+ 'ImageId': '123456',
+ 'InstanceMonitoring.Enabled': 'false',
+ 'InstanceType': 'm1.large',
+ 'SecurityGroups.member.1': 'group1',
+ 'SecurityGroups.member.2': 'group2',
+ 'SpotPrice': 'price',
+ }, ignore_params_values=['Version'])
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/emr/test_instance_group_args.py b/tests/unit/emr/test_instance_group_args.py
new file mode 100644
index 00000000..d5aab790
--- /dev/null
+++ b/tests/unit/emr/test_instance_group_args.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# Author: Charlie Schluting <charlie@schluting.com>
+#
+# Test to ensure initalization of InstanceGroup object emits appropriate errors
+# if bidprice is not specified, but allows float, int, Decimal.
+
+import unittest
+from decimal import Decimal
+
+from boto.emr.instance_group import InstanceGroup
+
+
+class TestInstanceGroupArgs(unittest.TestCase):
+
+ def test_bidprice_missing_spot(self):
+ """
+ Test InstanceGroup init raises ValueError when market==spot and
+ bidprice is not specified.
+ """
+ with self.assertRaisesRegexp(ValueError, 'bidprice must be specified'):
+ InstanceGroup(1, 'MASTER', 'm1.small',
+ 'SPOT', 'master')
+
+ def test_bidprice_missing_ondemand(self):
+ """
+ Test InstanceGroup init accepts a missing bidprice arg, when market is
+ ON_DEMAND.
+ """
+ instance_group = InstanceGroup(1, 'MASTER', 'm1.small',
+ 'ON_DEMAND', 'master')
+
+ def test_bidprice_Decimal(self):
+ """
+ Test InstanceGroup init works with bidprice type = Decimal.
+ """
+ instance_group = InstanceGroup(1, 'MASTER', 'm1.small',
+ 'SPOT', 'master', bidprice=Decimal(1.10))
+ self.assertEquals('1.10', instance_group.bidprice[:4])
+
+ def test_bidprice_float(self):
+ """
+ Test InstanceGroup init works with bidprice type = float.
+ """
+ instance_group = InstanceGroup(1, 'MASTER', 'm1.small',
+ 'SPOT', 'master', bidprice=1.1)
+ self.assertEquals('1.1', instance_group.bidprice)
+
+ def test_bidprice_string(self):
+ """
+ Test InstanceGroup init works with bidprice type = string.
+ """
+ instance_group = InstanceGroup(1, 'MASTER', 'm1.small',
+ 'SPOT', 'master', bidprice='1.1')
+ self.assertEquals('1.1', instance_group.bidprice)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/unit/ses/__init__.py b/tests/unit/ses/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/unit/ses/__init__.py
diff --git a/tests/unit/ses/test_identity.py b/tests/unit/ses/test_identity.py
new file mode 100644
index 00000000..6735e4a8
--- /dev/null
+++ b/tests/unit/ses/test_identity.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish, dis-
+# tribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the fol-
+# lowing conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#
+from tests.unit import unittest
+from tests.unit import AWSMockServiceTestCase
+
+from boto.jsonresponse import ListElement
+from boto.ses.connection import SESConnection
+
+
+class TestSESIdentity(AWSMockServiceTestCase):
+ connection_class = SESConnection
+
+ def setUp(self):
+ super(TestSESIdentity, self).setUp()
+
+ def default_body(self):
+ return """<GetIdentityDkimAttributesResponse \
+xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
+ <GetIdentityDkimAttributesResult>
+ <DkimAttributes>
+ <entry>
+ <key>amazon.com</key>
+ <value>
+ <DkimEnabled>true</DkimEnabled>
+ <DkimVerificationStatus>Success</DkimVerificationStatus>
+ <DkimTokens>
+ <member>vvjuipp74whm76gqoni7qmwwn4w4qusjiainivf6f</member>
+ <member>3frqe7jn4obpuxjpwpolz6ipb3k5nvt2nhjpik2oy</member>
+ <member>wrqplteh7oodxnad7hsl4mixg2uavzneazxv5sxi2</member>
+ </DkimTokens>
+ </value>
+ </entry>
+ </DkimAttributes>
+ </GetIdentityDkimAttributesResult>
+ <ResponseMetadata>
+ <RequestId>bb5a105d-c468-11e1-82eb-dff885ccc06a</RequestId>
+ </ResponseMetadata>
+</GetIdentityDkimAttributesResponse>"""
+
+ def test_ses_get_identity_dkim_list(self):
+ self.set_http_response(status_code=200)
+
+ response = self.service_connection\
+ .get_identity_dkim_attributes(['test@amazon.com'])
+
+ response = response['GetIdentityDkimAttributesResponse']
+ result = response['GetIdentityDkimAttributesResult']
+ attributes = result['DkimAttributes']['entry']['value']
+ tokens = attributes['DkimTokens']
+
+ self.assertEqual(ListElement, type(tokens))
+ self.assertEqual(3, len(tokens))
+ self.assertEqual('vvjuipp74whm76gqoni7qmwwn4w4qusjiainivf6f',
+ tokens[0])
+ self.assertEqual('3frqe7jn4obpuxjpwpolz6ipb3k5nvt2nhjpik2oy',
+ tokens[1])
+ self.assertEqual('wrqplteh7oodxnad7hsl4mixg2uavzneazxv5sxi2',
+ tokens[2])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/unit/sqs/test_message.py b/tests/unit/sqs/test_message.py
new file mode 100644
index 00000000..44cca9c1
--- /dev/null
+++ b/tests/unit/sqs/test_message.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish, dis-
+# tribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the fol-
+# lowing conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#
+from tests.unit import unittest
+
+from boto.sqs.message import MHMessage
+
+
+class TestMHMessage(unittest.TestCase):
+ def test_contains(self):
+ msg = MHMessage()
+ msg.update({'hello': 'world'})
+ self.assertTrue('hello' in msg)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py
index d71587fc..35dd7259 100644
--- a/tests/unit/test_connection.py
+++ b/tests/unit/test_connection.py
@@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
+import os
import urlparse
from tests.unit import unittest
from httpretty import HTTPretty
@@ -134,6 +135,49 @@ class TestAWSQueryConnectionSimple(TestAWSQueryConnection):
self.assertEqual(conn.host, 'mockservice.cc-zone-1.amazonaws.com')
+ def test_query_connection_noproxy(self):
+ HTTPretty.register_uri(HTTPretty.POST,
+ 'https://%s/' % self.region.endpoint,
+ json.dumps({'test': 'secure'}),
+ content_type='application/json')
+
+ os.environ['no_proxy'] = self.region.endpoint
+
+ conn = self.region.connect(aws_access_key_id='access_key',
+ aws_secret_access_key='secret',
+ proxy="NON_EXISTENT_HOSTNAME",
+ proxy_port="3128")
+
+ resp = conn.make_request('myCmd',
+ {'par1': 'foo', 'par2': 'baz'},
+ "/",
+ "POST")
+ del os.environ['no_proxy']
+ args = urlparse.parse_qs(HTTPretty.last_request.body)
+ self.assertEqual(args['AWSAccessKeyId'], ['access_key'])
+
+ def test_query_connection_noproxy_nosecure(self):
+ HTTPretty.register_uri(HTTPretty.POST,
+ 'https://%s/' % self.region.endpoint,
+ json.dumps({'test': 'insecure'}),
+ content_type='application/json')
+
+ os.environ['no_proxy'] = self.region.endpoint
+
+ conn = self.region.connect(aws_access_key_id='access_key',
+ aws_secret_access_key='secret',
+ proxy="NON_EXISTENT_HOSTNAME",
+ proxy_port="3128",
+ is_secure = False)
+
+ resp = conn.make_request('myCmd',
+ {'par1': 'foo', 'par2': 'baz'},
+ "/",
+ "POST")
+ del os.environ['no_proxy']
+ args = urlparse.parse_qs(HTTPretty.last_request.body)
+ self.assertEqual(args['AWSAccessKeyId'], ['access_key'])
+
def test_single_command(self):
HTTPretty.register_uri(HTTPretty.POST,
'https://%s/' % self.region.endpoint,
diff --git a/tests/unit/test_exception.py b/tests/unit/test_exception.py
index 684ca0ce..a14f0dca 100644
--- a/tests/unit/test_exception.py
+++ b/tests/unit/test_exception.py
@@ -1,6 +1,6 @@
from tests.unit import unittest
-from boto.exception import BotoServerError
+from boto.exception import BotoServerError, S3CreateError, JSONResponseError
from httpretty import HTTPretty, httprettified
@@ -23,8 +23,9 @@ class TestBotoServerError(unittest.TestCase):
<RequestId>093f80d0-4473-11e1-9234-edce8ec08e2d</RequestId>
</ErrorResponse>"""
bse = BotoServerError('400', 'Bad Request', body=xml)
-
+
self.assertEqual(bse.error_message, 'Cannot find Load Balancer webapp-balancer2')
+ self.assertEqual(bse.error_message, bse.message)
self.assertEqual(bse.request_id, '093f80d0-4473-11e1-9234-edce8ec08e2d')
self.assertEqual(bse.error_code, 'LoadBalancerNotFound')
self.assertEqual(bse.status, '400')
@@ -44,10 +45,11 @@ class TestBotoServerError(unittest.TestCase):
<RequestID>e73bb2bb-63e3-9cdc-f220-6332de66dbbe</RequestID>
</Response>"""
bse = BotoServerError('403', 'Forbidden', body=xml)
- self.assertEqual(bse.error_message,
+ self.assertEqual(bse.error_message,
'Session does not have permission to perform (sdb:CreateDomain) on '
'resource (arn:aws:sdb:us-east-1:xxxxxxx:domain/test_domain). '
'Contact account owner.')
+ self.assertEqual(bse.error_message, bse.message)
self.assertEqual(bse.box_usage, '0.0055590278')
self.assertEqual(bse.error_code, 'AuthorizationFailure')
self.assertEqual(bse.status, '403')
@@ -65,6 +67,45 @@ class TestBotoServerError(unittest.TestCase):
bse = BotoServerError('403', 'Forbidden', body=xml)
self.assertEqual([], HTTPretty.latest_requests)
+ def test_message_storage_create_error(self):
+ # This test value comes from https://answers.launchpad.net/duplicity/+question/150801
+ xml = """<?xml version="1.0" encoding="UTF-8"?>
+<Error>
+ <Code>BucketAlreadyOwnedByYou</Code>
+ <Message>Your previous request to create the named bucket succeeded and you already own it.</Message>
+ <BucketName>cmsbk</BucketName>
+ <RequestId>FF8B86A32CC3FE4F</RequestId>
+ <HostId>6ENGL3DT9f0n7Tkv4qdKIs/uBNCMMA6QUFapw265WmodFDluP57esOOkecp55qhh</HostId>
+</Error>
+"""
+ s3ce = S3CreateError('409', 'Conflict', body=xml)
+
+ self.assertEqual(s3ce.bucket, 'cmsbk')
+ self.assertEqual(s3ce.error_code, 'BucketAlreadyOwnedByYou')
+ self.assertEqual(s3ce.status, '409')
+ self.assertEqual(s3ce.reason, 'Conflict')
+ self.assertEqual(s3ce.error_message,
+ 'Your previous request to create the named bucket succeeded '
+ 'and you already own it.')
+ self.assertEqual(s3ce.error_message, s3ce.message)
+ self.assertEqual(s3ce.request_id, 'FF8B86A32CC3FE4F')
+
+ def test_message_json_response_error(self):
+ # This test comes from https://forums.aws.amazon.com/thread.jspa?messageID=374936
+ body = {
+ '__type': 'com.amazon.coral.validate#ValidationException',
+ 'message': 'The attempted filter operation is not supported '
+ 'for the provided filter argument count'}
+
+ jre = JSONResponseError('400', 'Bad Request', body=body)
+
+ self.assertEqual(jre.status, '400')
+ self.assertEqual(jre.reason, 'Bad Request')
+ self.assertEqual(jre.error_message, body['message'])
+ self.assertEqual(jre.error_message, jre.message)
+ self.assertEqual(jre.code, 'ValidationException')
+ self.assertEqual(jre.code, jre.error_code)
+
def test_message_not_xml(self):
body = 'This is not XML'
@@ -76,3 +117,4 @@ class TestBotoServerError(unittest.TestCase):
bse = BotoServerError('400', 'Bad Request', body=body)
self.assertEqual(bse.code, bse.error_code)
+ self.assertEqual(bse.message, bse.error_message)
diff --git a/tests/unit/vpc/__init__.py b/tests/unit/vpc/__init__.py
index e69de29b..c7856c5b 100644
--- a/tests/unit/vpc/__init__.py
+++ b/tests/unit/vpc/__init__.py
@@ -0,0 +1,3 @@
+"""
+Test package for VPC
+"""
diff --git a/tests/unit/vpc/test_vpc.py b/tests/unit/vpc/test_vpc.py
index 499d1582..b8e69dc9 100644
--- a/tests/unit/vpc/test_vpc.py
+++ b/tests/unit/vpc/test_vpc.py
@@ -4,6 +4,7 @@ from tests.unit import AWSMockServiceTestCase
from boto.vpc import VPCConnection
+
DESCRIBE_VPCS = r'''<?xml version="1.0" encoding="UTF-8"?>
<DescribeVpcsResponse xmlns="http://ec2.amazonaws.com/doc/2013-02-01/">
<requestId>623040d1-b51c-40bc-8080-93486f38d03d</requestId>
@@ -22,10 +23,10 @@ DESCRIBE_VPCS = r'''<?xml version="1.0" encoding="UTF-8"?>
class TestDescriveVPCs(AWSMockServiceTestCase):
connection_class = VPCConnection
-
+
def default_body(self):
return DESCRIBE_VPCS
-
+
def test_get_vpcs(self):
self.set_http_response(status_code=200)
@@ -36,5 +37,41 @@ class TestDescriveVPCs(AWSMockServiceTestCase):
self.assertFalse(vpc.is_default)
self.assertEqual(vpc.instance_tenancy,'default')
+
+class TestVPCConnection(unittest.TestCase):
+ """
+ Test class for `boto.vpc.VPCConnection`
+ """
+
+ def setUp(self):
+ """
+ Setup method to initialize vpc_connection objectq
+ """
+ super(TestVPCConnection, self).setUp()
+ self.vpc_connection = VPCConnection(
+ aws_access_key_id='aws_access_key_id',
+ aws_secret_access_key='aws_secret_access_key')
+
+ def test_detach_internet_gateway(self):
+ """
+ Tests detach_internet_gateway with all valid parameters
+ """
+ internet_gateway_id = 'mock_gateway_id'
+ vpc_id = 'mock_vpc_id'
+
+ def get_status(status, params):
+ if status == "DetachInternetGateway" and \
+ params["InternetGatewayId"] == internet_gateway_id and \
+ params["VpcId"] == vpc_id:
+ return True
+ else:
+ return False
+
+ self.vpc_connection.get_status = get_status
+ status = self.vpc_connection.detach_internet_gateway(
+ internet_gateway_id, vpc_id)
+ self.assertEquals(True, status)
+
+
if __name__ == '__main__':
- unittest.main() \ No newline at end of file
+ unittest.main()