deb-heat/heat/tests/test_random_string.py
Steve Baker 5c56779d12 A resource to generate random strings
A common pattern for templates is to define parameters
for passwords and specify the password on stack create.
For complex templates this can impact template maintainability
and makes stack creation more complicated.

In addition, a password parameter leaves the temptation of
using the same password (or worse, the parameter default) on
each stack launch which raises the security risk.

This resource allows secrets to be generated in the template and
propagated to compute resources as needed. Often the user will
never want to see the generated string, but if they do they
can put it in a stack output.

This resource has been requested by the tripleo project
who run trunk heat, so it will be of immediate benefit to them.

Implements blueprint random-string-resource

Change-Id: I044fc7717133fc7a3520dca774300024925ff2aa
2013-10-10 08:08:22 +13:00

115 lines
3.7 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from heat.common import exception
from heat.common import template_format
from heat.engine import parser
from heat.engine.resources.random_string import RandomString
from heat.tests.common import HeatTestCase
from heat.tests import utils
import testscenarios
from testtools.matchers import MatchesRegex
from testtools.matchers import HasLength
load_tests = testscenarios.load_tests_apply_scenarios
class TestRandomString(HeatTestCase):
template_random_string = '''
HeatTemplateFormatVersion: '2012-12-12'
Resources:
secret1:
Type: OS::Heat::RandomString
secret2:
Type: OS::Heat::RandomString
Properties:
length: 10
secret3:
Type: OS::Heat::RandomString
Properties:
length: 100
sequence: octdigits
'''
def setUp(self):
super(HeatTestCase, self).setUp()
utils.setup_dummy_db()
self.ctx = utils.dummy_context()
def create_stack(self, template):
t = template_format.parse(template)
self.stack = self.parse_stack(t)
self.assertEqual(None, self.stack.create())
return self.stack
def parse_stack(self, t):
stack_name = 'test_stack'
tmpl = parser.Template(t)
stack = parser.Stack(utils.dummy_context(), stack_name, tmpl)
stack.validate()
stack.store()
return stack
def test_random_string(self):
stack = self.create_stack(self.template_random_string)
secret1 = stack['secret1']
random_string = secret1.FnGetAtt('value')
self.assertThat(random_string, MatchesRegex('[a-zA-Z0-9]{32}'))
self.assertRaises(exception.InvalidTemplateAttribute,
secret1.FnGetAtt, 'foo')
secret2 = stack['secret2']
random_string = secret2.FnGetAtt('value')
self.assertThat(random_string, MatchesRegex('[a-zA-Z0-9]{10}'))
secret3 = stack['secret3']
random_string = secret3.FnGetAtt('value')
self.assertThat(random_string, MatchesRegex('[0-7]{100}'))
class TestGenerateRandomString(HeatTestCase):
scenarios = [
('lettersdigits', dict(
length=1, seq='lettersdigits', pattern='[a-zA-Z0-9]')),
('letters', dict(
length=10, seq='letters', pattern='[a-zA-Z]')),
('lowercase', dict(
length=100, seq='lowercase', pattern='[a-z]')),
('uppercase', dict(
length=50, seq='uppercase', pattern='[A-Z]')),
('digits', dict(
length=512, seq='digits', pattern='[0-9]')),
('hexdigits', dict(
length=16, seq='hexdigits', pattern='[A-F0-9]')),
('octdigits', dict(
length=32, seq='octdigits', pattern='[0-7]'))
]
def test_generate_random_string(self):
# run each test multiple times to confirm random generator
# doesn't generate a matching pattern by chance
for i in range(1, 32):
sequence = RandomString._sequences[self.seq]
r = RandomString._generate_random_string(sequence, self.length)
self.assertThat(r, HasLength(self.length))
regex = '%s{%s}' % (self.pattern, self.length)
self.assertThat(r, MatchesRegex(regex))