Merge "Stop using Hidden property for RandomString"

This commit is contained in:
Jenkins 2016-03-02 02:27:14 +00:00 committed by Gerrit Code Review
commit 6de91b1bf1
2 changed files with 75 additions and 67 deletions

View File

@ -124,7 +124,11 @@ class RandomString(resource.Resource):
] ]
) )
} }
) ),
# add defaults for backward compatibility
default=[{CHARACTER_CLASSES_CLASS: 'lettersdigits',
CHARACTER_CLASSES_MIN: 1}]
), ),
CHARACTER_SEQUENCES: properties.Schema( CHARACTER_SEQUENCES: properties.Schema(
properties.Schema.LIST, properties.Schema.LIST,
@ -196,11 +200,6 @@ class RandomString(resource.Resource):
) )
] ]
@staticmethod
def _deprecated_random_string(sequence, length):
rand = random.SystemRandom()
return ''.join(rand.choice(sequence) for x in six.moves.xrange(length))
def _generate_random_string(self, char_sequences, char_classes, length): def _generate_random_string(self, char_sequences, char_classes, length):
random_string = "" random_string = ""
@ -256,18 +255,9 @@ class RandomString(resource.Resource):
def validate(self): def validate(self):
super(RandomString, self).validate() super(RandomString, self).validate()
sequence = self.properties[self.SEQUENCE]
char_sequences = self.properties[self.CHARACTER_SEQUENCES] char_sequences = self.properties[self.CHARACTER_SEQUENCES]
char_classes = self.properties[self.CHARACTER_CLASSES] char_classes = self.properties[self.CHARACTER_CLASSES]
if sequence and (char_sequences or char_classes):
msg = (_("Cannot use deprecated '%(seq)s' property along with "
"'%(char_seqs)s' or '%(char_classes)s' properties")
% {'seq': self.SEQUENCE,
'char_seqs': self.CHARACTER_SEQUENCES,
'char_classes': self.CHARACTER_CLASSES})
raise exception.StackValidationFailed(message=msg)
def char_min(char_dicts, min_prop): def char_min(char_dicts, min_prop):
if char_dicts: if char_dicts:
return sum(char_dict[min_prop] for char_dict in char_dicts) return sum(char_dict[min_prop] for char_dict in char_dicts)
@ -286,18 +276,9 @@ class RandomString(resource.Resource):
char_classes = self.properties[self.CHARACTER_CLASSES] char_classes = self.properties[self.CHARACTER_CLASSES]
length = self.properties[self.LENGTH] length = self.properties[self.LENGTH]
if char_sequences or char_classes: random_string = self._generate_random_string(char_sequences,
random_string = self._generate_random_string(char_sequences, char_classes,
char_classes, length)
length)
else:
sequence = self.properties[self.SEQUENCE]
if not sequence: # Deprecated property not provided, use a default
sequence = "lettersdigits"
char_seq = self._sequences[sequence]
random_string = self._deprecated_random_string(char_seq, length)
self.data_set('value', random_string, redact=True) self.data_set('value', random_string, redact=True)
self.resource_id_set(self.physical_resource_name()) self.resource_id_set(self.physical_resource_name())

View File

@ -19,7 +19,6 @@ from testtools import matchers
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
from heat.engine.resources.openstack.heat import random_string as rs
from heat.engine import stack as parser from heat.engine import stack as parser
from heat.engine import template from heat.engine import template
from heat.tests import common from heat.tests import common
@ -38,11 +37,6 @@ Resources:
Properties: Properties:
length: 10 length: 10
secret3: secret3:
Type: OS::Heat::RandomString
Properties:
length: 100
sequence: octdigits
secret4:
Type: OS::Heat::RandomString Type: OS::Heat::RandomString
Properties: Properties:
length: 32 length: 32
@ -60,7 +54,7 @@ Resources:
min: 2 min: 2
- sequence: '@' - sequence: '@'
min: 5 min: 5
secret5: secret4:
Type: OS::Heat::RandomString Type: OS::Heat::RandomString
Properties: Properties:
length: 25 length: 25
@ -71,7 +65,7 @@ Resources:
min: 1 min: 1
- class: lowercase - class: lowercase
min: 20 min: 20
secret6: secret5:
Type: OS::Heat::RandomString Type: OS::Heat::RandomString
Properties: Properties:
length: 10 length: 10
@ -101,59 +95,74 @@ Resources:
stack.store() stack.store()
return stack return stack
def assert_min(self, pattern, string, minimum):
self.assertTrue(len(re.findall(pattern, string)) >= minimum)
def test_random_string(self): def test_random_string(self):
stack = self.create_stack(self.template_random_string) stack = self.create_stack(self.template_random_string)
secret1 = stack['secret1'] secret1 = stack['secret1']
def assert_min(pattern, string, minimum):
self.assertTrue(len(re.findall(pattern, string)) >= minimum)
random_string = secret1.FnGetAtt('value') random_string = secret1.FnGetAtt('value')
assert_min('[a-zA-Z0-9]', random_string, 32) self.assert_min('[a-zA-Z0-9]', random_string, 32)
self.assertRaises(exception.InvalidTemplateAttribute, self.assertRaises(exception.InvalidTemplateAttribute,
secret1.FnGetAtt, 'foo') secret1.FnGetAtt, 'foo')
self.assertEqual(secret1.FnGetRefId(), random_string) self.assertEqual(secret1.FnGetRefId(), random_string)
secret2 = stack['secret2'] secret2 = stack['secret2']
random_string = secret2.FnGetAtt('value') random_string = secret2.FnGetAtt('value')
assert_min('[a-zA-Z0-9]', random_string, 10) self.assert_min('[a-zA-Z0-9]', random_string, 10)
self.assertEqual(secret2.FnGetRefId(), random_string) self.assertEqual(secret2.FnGetRefId(), random_string)
secret3 = stack['secret3'] secret3 = stack['secret3']
random_string = secret3.FnGetAtt('value') random_string = secret3.FnGetAtt('value')
assert_min('[0-7]', random_string, 100) self.assertEqual(32, len(random_string))
self.assert_min('[0-9]', random_string, 1)
self.assert_min('[A-Z]', random_string, 1)
self.assert_min('[a-z]', random_string, 20)
self.assert_min('[(),\[\]{}]', random_string, 1)
self.assert_min('[$_]', random_string, 2)
self.assert_min('@', random_string, 5)
self.assertEqual(secret3.FnGetRefId(), random_string) self.assertEqual(secret3.FnGetRefId(), random_string)
secret4 = stack['secret4'] secret4 = stack['secret4']
random_string = secret4.FnGetAtt('value') random_string = secret4.FnGetAtt('value')
self.assertEqual(32, len(random_string)) self.assertEqual(25, len(random_string))
assert_min('[0-9]', random_string, 1) self.assert_min('[0-9]', random_string, 1)
assert_min('[A-Z]', random_string, 1) self.assert_min('[A-Z]', random_string, 1)
assert_min('[a-z]', random_string, 20) self.assert_min('[a-z]', random_string, 20)
assert_min('[(),\[\]{}]', random_string, 1)
assert_min('[$_]', random_string, 2)
assert_min('@', random_string, 5)
self.assertEqual(secret4.FnGetRefId(), random_string) self.assertEqual(secret4.FnGetRefId(), random_string)
secret5 = stack['secret5'] secret5 = stack['secret5']
random_string = secret5.FnGetAtt('value') random_string = secret5.FnGetAtt('value')
self.assertEqual(25, len(random_string)) self.assertEqual(10, len(random_string))
assert_min('[0-9]', random_string, 1) self.assert_min('[(),\[\]{}]', random_string, 1)
assert_min('[A-Z]', random_string, 1) self.assert_min('[$_]', random_string, 2)
assert_min('[a-z]', random_string, 20) self.assert_min('@', random_string, 5)
self.assertEqual(secret5.FnGetRefId(), random_string) self.assertEqual(secret5.FnGetRefId(), random_string)
secret6 = stack['secret6']
random_string = secret6.FnGetAtt('value')
self.assertEqual(10, len(random_string))
assert_min('[(),\[\]{}]', random_string, 1)
assert_min('[$_]', random_string, 2)
assert_min('@', random_string, 5)
self.assertEqual(secret6.FnGetRefId(), random_string)
# Prove the name is returned before create sets the ID # Prove the name is returned before create sets the ID
secret6.resource_id = None secret5.resource_id = None
self.assertEqual('secret6', secret6.FnGetRefId()) self.assertEqual('secret5', secret5.FnGetRefId())
def test_hidden_sequence_property(self):
hidden_prop_templ = '''
HeatTemplateFormatVersion: '2012-12-12'
Resources:
secret:
Type: OS::Heat::RandomString
Properties:
length: 100
sequence: octdigits
'''
stack = self.create_stack(hidden_prop_templ)
secret = stack['secret']
random_string = secret.FnGetAtt('value')
self.assert_min('[0-7]', random_string, 100)
self.assertEqual(secret.FnGetRefId(), random_string)
# check, that property was translated according to the TranslationRule
self.assertIsNone(secret.properties['sequence'])
expected = [{'class': u'octdigits', 'min': 1}]
self.assertEqual(expected, secret.properties['character_classes'])
def test_random_string_refid_convergence_cache_data(self): def test_random_string_refid_convergence_cache_data(self):
t = template_format.parse(self.template_random_string) t = template_format.parse(self.template_random_string)
@ -220,7 +229,6 @@ Resources:
class TestGenerateRandomString(common.HeatTestCase): class TestGenerateRandomString(common.HeatTestCase):
scenarios = [ scenarios = [
('lettersdigits', dict( ('lettersdigits', dict(
length=1, seq='lettersdigits', pattern='[a-zA-Z0-9]')), length=1, seq='lettersdigits', pattern='[a-zA-Z0-9]')),
@ -238,13 +246,32 @@ class TestGenerateRandomString(common.HeatTestCase):
length=32, seq='octdigits', pattern='[0-7]')) length=32, seq='octdigits', pattern='[0-7]'))
] ]
def test_generate_random_string(self): template_rs = '''
HeatTemplateFormatVersion: '2012-12-12'
Resources:
secret:
Type: OS::Heat::RandomString
'''
def parse_stack(self, t):
stack_name = 'test_stack'
tmpl = template.Template(t)
stack = parser.Stack(utils.dummy_context(), stack_name, tmpl)
stack.validate()
stack.store()
return stack
# test was saved to test backward compatibility with old behavior
def test_generate_random_string_backward_compatiable(self):
stack = self.parse_stack(template_format.parse(self.template_rs))
secret = stack['secret']
char_classes = secret.properties['character_classes']
for char_cl in char_classes:
char_cl['class'] = self.seq
# run each test multiple times to confirm random generator # run each test multiple times to confirm random generator
# doesn't generate a matching pattern by chance # doesn't generate a matching pattern by chance
for i in range(1, 32): for i in range(1, 32):
sequence = rs.RandomString._sequences[self.seq] r = secret._generate_random_string(None, char_classes, self.length)
r = rs.RandomString._deprecated_random_string(sequence,
self.length)
self.assertThat(r, matchers.HasLength(self.length)) self.assertThat(r, matchers.HasLength(self.length))
regex = '%s{%s}' % (self.pattern, self.length) regex = '%s{%s}' % (self.pattern, self.length)