Skip to content

Commit 528082d

Browse files
authored
Merge pull request #247 from /issues/245-246-lowercase-namespace
Fix namespace casing and `inconsistencies_detected` retries
2 parents a564a63 + 28d2747 commit 528082d

File tree

8 files changed

+30
-24
lines changed

8 files changed

+30
-24
lines changed

agent/src/cgcloud/agent/cli.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,11 @@ def main( ):
3232
required=True,
3333
help='Optional prefix for naming EC2 resource like instances, images, '
3434
'volumes, etc. Use this option to create a separate namespace in '
35-
'order to avoid collisions, e.g. when running tests. The default '
36-
'represents the root namespace. The value of the environment '
37-
'variable CGCLOUD_NAMESPACE, if that variable is present, overrides '
38-
'the default. The string __me__ anywhere in the namespace will be '
39-
'replaced by the name of the IAM user whose credentials are used to '
40-
'issue requests to AWS.' )
35+
'order to avoid collisions, e.g. when running tests. The value of '
36+
'the environment variable CGCLOUD_NAMESPACE, if that variable is '
37+
'present, overrides the default. The string __me__ anywhere in the '
38+
'namespace will be replaced by the name of the IAM user whose '
39+
'credentials are used to issue requests to AWS.' )
4140
default_zone = os.environ.get( 'CGCLOUD_ZONE', None )
4241
group.add_argument( '--zone', '-z', metavar='AVAILABILITY_ZONE',
4342
default=default_zone,

core/src/cgcloud/core/box.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from paramiko.client import MissingHostKeyPolicy
3232

3333
from cgcloud.core.project import project_artifacts
34-
from cgcloud.lib import aws_d64
34+
from cgcloud.lib import aws_d32
3535
from cgcloud.lib.context import Context
3636
from cgcloud.lib.ec2 import (ec2_instance_types,
3737
wait_instances_running,
@@ -1402,7 +1402,7 @@ def _hash_iam_role_name( self, iam_role_name ):
14021402
# name to the IAM role name. Prependi the prefix here and in _get_iam_ec2_role to be
14031403
# backwards-compatible PassRole statements generated by older versions of CGCloud.
14041404
return '-'.join( [ self.iam_role_name_prefix,
1405-
aws_d64.encode( hashlib.sha1( iam_role_name ).digest( )[ 0:8 ] ) ] )
1405+
aws_d32.encode( hashlib.sha1( iam_role_name ).digest( )[ 0:8 ] ) ] )
14061406

14071407
iam_role_name_prefix = 'cgcloud'
14081408

core/src/cgcloud/core/commands.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ def __init__( self, application, **kwargs ):
5959
self.option( '--namespace', '-n', metavar='PREFIX', default=self.default_namespace,
6060
help=heredoc( """Optional prefix for naming EC2 resource like instances,
6161
images, volumes, etc. Use this option to create a separate namespace in
62-
order to avoid collisions, e.g. when running tests. The value of the
62+
order to avoid collisions, e.g. when running tests. A namespace begins with
63+
a slash, followed by zero or more names, each name followed by a slash. Note
64+
that this implies that the namespace begins and ends with a slash. Each name
65+
must begin with a a digit or lowercase letter followed by zero or more
66+
digits, lowercase letters, periods, underscores or dashes. The value of the
6367
environment variable CGCLOUD_NAMESPACE, if that variable is present,
6468
overrides the default. The string __me__ anywhere in the namespace will be
6569
replaced by the name of the IAM user whose credentials are used to issue

lib/src/cgcloud/lib/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from bd2k.util.d64 import D64
1+
from bd2k.util.d32 import standard as d32
22

3-
aws_d64 = D64( '.-' ) # hopefully the dot is supported for all AWS resource names
3+
aws_d32 = d32
44

5-
test_namespace_suffix_length = 11
5+
test_namespace_suffix_length = 13

lib/src/cgcloud/lib/context.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Context( object ):
3434
"""
3535
availability_zone_re = re.compile( r'^([a-z]{2}-[a-z]+-[1-9][0-9]*)([a-z])$' )
3636

37-
name_prefix_re = re.compile( r'^(/([0-9a-zA-Z.-][_0-9a-zA-Z.-]*))*' )
37+
name_prefix_re = re.compile( r'^(/([0-9a-z][0-9a-z._-]*))*' )
3838
name_re = re.compile( name_prefix_re.pattern + '/?$' )
3939
namespace_re = re.compile( name_prefix_re.pattern + '/$' )
4040

@@ -115,7 +115,7 @@ def __init__( self, availability_zone, namespace ):
115115
ValueError: 'ascii' codec can't encode characters in position 2-3: ordinal not in range(128)
116116
117117
>>> import string
118-
>>> component = string.ascii_letters + string.digits + '-_.'
118+
>>> component = string.ascii_lowercase + string.digits + '-_.'
119119
>>> namespace = '/' + component + '/'
120120
>>> Context('us-west-1b', namespace=namespace).namespace == namespace
121121
True
@@ -642,6 +642,7 @@ def resolve_me( self, s, drop_hostname=True ):
642642
'http://boto.readthedocs.org/en/latest/boto_config_tut.html' )
643643
if drop_hostname:
644644
me = self.drop_hostname( me )
645+
me = me.lower() # namespaces must be lower case
645646
return s.replace( placeholder, me )
646647
else:
647648
return s

lib/src/cgcloud/lib/ec2.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from boto.ec2.ec2object import TaggedEC2Object
1010
from boto.ec2.instance import Instance
1111
from boto.ec2.spotinstancerequest import SpotInstanceRequest
12-
from boto.exception import EC2ResponseError
12+
from boto.exception import EC2ResponseError, BotoServerError
1313

1414
from cgcloud.lib.util import UserError
1515

@@ -26,7 +26,7 @@ def not_found( e ):
2626

2727
def retry_ec2( retry_after=a_short_time, retry_for=10 * a_short_time, retry_while=not_found ):
2828
t = retry_after
29-
return retry( delays=(t,t,t*2,t*4), timeout=retry_for, predicate=retry_while )
29+
return retry( delays=(t, t, t * 2, t * 4), timeout=retry_for, predicate=retry_while )
3030

3131

3232
class EC2VolumeHelper( object ):
@@ -343,11 +343,12 @@ def spot_request_not_found( e ):
343343

344344

345345
def create_spot_instances( ec2, price, image_id, spec,
346-
num_instances=1, timeout=None, tentative=False, tags=None):
346+
num_instances=1, timeout=None, tentative=False, tags=None ):
347347
"""
348348
:rtype: Iterator[list[Instance]]
349349
"""
350-
def spotRequestNotFound(e):
350+
351+
def spotRequestNotFound( e ):
351352
return e.error_code == "InvalidSpotInstanceRequestID.NotFound"
352353

353354
for attempt in retry_ec2( retry_for=a_long_time,
@@ -357,9 +358,9 @@ def spotRequestNotFound(e):
357358

358359
if tags is not None:
359360
for requestID in (request.id for request in requests):
360-
for attempt in retry_ec2(retry_while=spotRequestNotFound):
361+
for attempt in retry_ec2( retry_while=spotRequestNotFound ):
361362
with attempt:
362-
ec2.create_tags([requestID], tags)
363+
ec2.create_tags( [ requestID ], tags )
363364

364365
num_active, num_other = 0, 0
365366
# noinspection PyUnboundLocalVariable,PyTypeChecker
@@ -391,6 +392,7 @@ def spotRequestNotFound(e):
391392

392393

393394
def inconsistencies_detected( e ):
395+
if not isinstance( e, BotoServerError ): return False
394396
if e.code == 'InvalidGroup.NotFound': return True
395397
m = e.error_message.lower( )
396398
return 'invalid iam instance profile' in m or 'no associated iam roles' in m

lib/src/cgcloud/lib/test/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from boto.utils import get_instance_metadata
77

8-
from cgcloud.lib import aws_d64, test_namespace_suffix_length
8+
from cgcloud.lib import aws_d32, test_namespace_suffix_length
99
from cgcloud.lib.context import Context
1010
from cgcloud.lib.ec2 import running_on_ec2
1111

@@ -26,11 +26,11 @@ def setUpClass( cls ):
2626
if running_on_ec2( ):
2727
os.environ.setdefault( 'CGCLOUD_ZONE',
2828
get_instance_metadata( )[ 'placement' ][ 'availability-zone' ] )
29-
# Using the d64 of a binary string that starts with a 4-byte, big-endian time stamp
29+
# Using the d32 of a binary string that starts with a 4-byte, big-endian time stamp
3030
# yields compact names whose lexicographical sorting is consistent with the historical
3131
# order. We add the process ID so we can run tests concurrently in child processes using
3232
# the pytest-xdist plugin.
33-
suffix = aws_d64.encode( pack( '>II', int( time.time( ) ), os.getpid( ) ) )
33+
suffix = aws_d32.encode( pack( '>II', int( time.time( ) ), os.getpid( ) ) )
3434
assert len( suffix ) == test_namespace_suffix_length
3535
cls.__namespace = '/test/%s/' % suffix
3636
os.environ.setdefault( 'CGCLOUD_NAMESPACE', cls.__namespace )

version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cgcloud_version = '1.6.0a1'
2-
bd2k_python_lib_dep = 'bd2k-python-lib>=1.14a1.dev35'
2+
bd2k_python_lib_dep = 'bd2k-python-lib>=1.14a1.dev37'
33
boto_dep = 'boto==2.38.0'
44
fabric_dep = 'Fabric==1.10.3'
55
s3am_dep = 's3am==2.0a1.dev105'

0 commit comments

Comments
 (0)