Merge "Updates template_validate call to validate parameter_groups."
This commit is contained in:
commit
7100cfa147
|
@ -0,0 +1,68 @@
|
|||
# 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.openstack.common import log as logging
|
||||
from heat.openstack.common.gettextutils import _
|
||||
|
||||
from heat.common.exception import StackValidationFailed
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PARAMETER_GROUPS = 'parameter_groups'
|
||||
PARAMETERS = 'parameters'
|
||||
|
||||
|
||||
class ParameterGroups(object):
|
||||
'''
|
||||
The ParameterGroups specified by the stack's template.
|
||||
'''
|
||||
def __init__(self, tmpl):
|
||||
self.tmpl = tmpl
|
||||
self.parameters = tmpl.parameters(None, {}, validate_value=False)
|
||||
logger.debug(self.tmpl)
|
||||
logger.debug(self.parameters)
|
||||
self.parameter_names = []
|
||||
if self.parameters:
|
||||
self.parameter_names = [param for param in self.parameters]
|
||||
self.parameter_groups = tmpl.get(PARAMETER_GROUPS)
|
||||
|
||||
def validate(self):
|
||||
'''
|
||||
Validate that a parameter belongs to only one Parameter Group
|
||||
and that each parameter name references a valid parameter.
|
||||
'''
|
||||
logger.debug(_('Validating Parameter Groups.'))
|
||||
logger.debug(self.parameter_names)
|
||||
if self.parameter_groups is not None:
|
||||
#Loop through groups and validate parameters
|
||||
grouped_parameters = []
|
||||
for group in self.parameter_groups:
|
||||
parameters = group.get(PARAMETERS)
|
||||
|
||||
if parameters is None:
|
||||
raise StackValidationFailed(message=_(
|
||||
'Parameters must be provided for '
|
||||
'each Parameter Group.'))
|
||||
|
||||
for param in parameters:
|
||||
#Check if param has been added to a previous group
|
||||
if param in grouped_parameters:
|
||||
raise StackValidationFailed(message=_(
|
||||
'The %s parameter must be assigned to one '
|
||||
'Parameter Group only.') % param)
|
||||
else:
|
||||
grouped_parameters.append(param)
|
||||
|
||||
#Check that grouped parameter references a valid Parameter
|
||||
if param not in self.parameter_names:
|
||||
raise StackValidationFailed(message=_(
|
||||
'The Parameter name (%s) does not reference '
|
||||
'an existing parameter.') % param)
|
|
@ -31,6 +31,7 @@ from heat.engine import scheduler
|
|||
from heat.engine import timestamp
|
||||
from heat.engine import update
|
||||
from heat.engine.notification import stack as notification
|
||||
from heat.engine.parameter_groups import ParameterGroups
|
||||
from heat.engine.template import Template
|
||||
from heat.engine.clients import Clients
|
||||
from heat.db import api as db_api
|
||||
|
@ -323,6 +324,10 @@ class Stack(collections.Mapping):
|
|||
'''
|
||||
# TODO(sdake) Should return line number of invalid reference
|
||||
|
||||
# Validate Parameter Groups
|
||||
parameter_groups = ParameterGroups(self.t)
|
||||
parameter_groups.validate()
|
||||
|
||||
# Check duplicate names between parameters and resources
|
||||
dup_names = set(self.parameters.keys()) & set(self.keys())
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ from heat.engine import environment
|
|||
from heat.common import exception
|
||||
from heat.common import identifier
|
||||
from heat.common import heat_keystoneclient as hkc
|
||||
from heat.engine import parameter_groups
|
||||
from heat.engine import parser
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
|
@ -525,11 +526,16 @@ class EngineService(service.Service):
|
|||
tmpl_params = tmpl.parameters(None, {}, validate_value=False)
|
||||
is_real_param = lambda p: p.name not in tmpl_params.PSEUDO_PARAMETERS
|
||||
params = tmpl_params.map(api.format_validate_parameter, is_real_param)
|
||||
param_groups = parameter_groups.ParameterGroups(tmpl)
|
||||
|
||||
result = {
|
||||
'Description': tmpl.get('Description', ''),
|
||||
'Parameters': params,
|
||||
}
|
||||
|
||||
if param_groups.parameter_groups:
|
||||
result['ParameterGroups'] = param_groups.parameter_groups
|
||||
|
||||
return result
|
||||
|
||||
@request_context
|
||||
|
|
|
@ -25,6 +25,7 @@ from heat.engine.resources import instance as instances
|
|||
from heat.engine import service
|
||||
from heat.openstack.common.importutils import try_import
|
||||
from heat.engine import parser
|
||||
from heat.engine.hot import HOTemplate
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import utils
|
||||
|
||||
|
@ -537,6 +538,163 @@ test_template_unique_logical_name = '''
|
|||
}
|
||||
'''
|
||||
|
||||
test_template_duplicate_parameters = '''
|
||||
# This is a hello world HOT template just defining a single compute instance
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
Hello world HOT template that just defines a single compute instance.
|
||||
Contains just base features to verify base HOT support.
|
||||
|
||||
parameter_groups:
|
||||
- label: Server Group
|
||||
description: A group of parameters for the server
|
||||
parameters:
|
||||
- InstanceType
|
||||
- KeyName
|
||||
- ImageId
|
||||
- label: Database Group
|
||||
description: A group of parameters for the database
|
||||
parameters:
|
||||
- db_password
|
||||
- db_port
|
||||
- InstanceType
|
||||
|
||||
parameters:
|
||||
KeyName:
|
||||
type: string
|
||||
description: Name of an existing key pair to use for the instance
|
||||
InstanceType:
|
||||
type: string
|
||||
description: Instance type for the instance to be created
|
||||
default: m1.small
|
||||
constraints:
|
||||
- allowed_values: [m1.tiny, m1.small, m1.large]
|
||||
description: Value must be one of 'm1.tiny', 'm1.small' or 'm1.large'
|
||||
ImageId:
|
||||
type: string
|
||||
description: ID of the image to use for the instance
|
||||
# parameters below are not used in template, but are for verifying parameter
|
||||
# validation support in HOT
|
||||
db_password:
|
||||
type: string
|
||||
description: Database password
|
||||
hidden: true
|
||||
constraints:
|
||||
- length: { min: 6, max: 8 }
|
||||
description: Password length must be between 6 and 8 characters
|
||||
- allowed_pattern: "[a-zA-Z0-9]+"
|
||||
description: Password must consist of characters and numbers only
|
||||
- allowed_pattern: "[A-Z]+[a-zA-Z0-9]*"
|
||||
description: Password must start with an uppercase character
|
||||
db_port:
|
||||
type: number
|
||||
description: Database port number
|
||||
default: 50000
|
||||
constraints:
|
||||
- range: { min: 40000, max: 60000 }
|
||||
description: Port number must be between 40000 and 60000
|
||||
|
||||
resources:
|
||||
my_instance:
|
||||
# Use an AWS resource type since this exists; so why use other name here?
|
||||
type: AWS::EC2::Instance
|
||||
properties:
|
||||
KeyName: { get_param: KeyName }
|
||||
ImageId: { get_param: ImageId }
|
||||
InstanceType: { get_param: InstanceType }
|
||||
|
||||
outputs:
|
||||
instance_ip:
|
||||
description: The IP address of the deployed instance
|
||||
value: { get_attr: [my_instance, PublicIp] }
|
||||
'''
|
||||
|
||||
test_template_invalid_parameter_name = '''
|
||||
# This is a hello world HOT template just defining a single compute instance
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
Hello world HOT template that just defines a single compute instance.
|
||||
Contains just base features to verify base HOT support.
|
||||
|
||||
parameter_groups:
|
||||
- label: Server Group
|
||||
description: A group of parameters for the server
|
||||
parameters:
|
||||
- InstanceType
|
||||
- KeyName
|
||||
- ImageId
|
||||
- label: Database Group
|
||||
description: A group of parameters for the database
|
||||
parameters:
|
||||
- db_password
|
||||
- db_port
|
||||
- SomethingNotHere
|
||||
|
||||
parameters:
|
||||
KeyName:
|
||||
type: string
|
||||
description: Name of an existing key pair to use for the instance
|
||||
InstanceType:
|
||||
type: string
|
||||
description: Instance type for the instance to be created
|
||||
default: m1.small
|
||||
constraints:
|
||||
- allowed_values: [m1.tiny, m1.small, m1.large]
|
||||
description: Value must be one of 'm1.tiny', 'm1.small' or 'm1.large'
|
||||
ImageId:
|
||||
type: string
|
||||
description: ID of the image to use for the instance
|
||||
# parameters below are not used in template, but are for verifying parameter
|
||||
# validation support in HOT
|
||||
db_password:
|
||||
type: string
|
||||
description: Database password
|
||||
hidden: true
|
||||
constraints:
|
||||
- length: { min: 6, max: 8 }
|
||||
description: Password length must be between 6 and 8 characters
|
||||
- allowed_pattern: "[a-zA-Z0-9]+"
|
||||
description: Password must consist of characters and numbers only
|
||||
- allowed_pattern: "[A-Z]+[a-zA-Z0-9]*"
|
||||
description: Password must start with an uppercase character
|
||||
db_port:
|
||||
type: number
|
||||
description: Database port number
|
||||
default: 50000
|
||||
constraints:
|
||||
- range: { min: 40000, max: 60000 }
|
||||
description: Port number must be between 40000 and 60000
|
||||
|
||||
resources:
|
||||
my_instance:
|
||||
# Use an AWS resource type since this exists; so why use other name here?
|
||||
type: AWS::EC2::Instance
|
||||
properties:
|
||||
KeyName: { get_param: KeyName }
|
||||
ImageId: { get_param: ImageId }
|
||||
InstanceType: { get_param: InstanceType }
|
||||
|
||||
outputs:
|
||||
instance_ip:
|
||||
description: The IP address of the deployed instance
|
||||
value: { get_attr: [my_instance, PublicIp] }
|
||||
'''
|
||||
test_template_no_parameters = '''
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
Hello world HOT template that just defines a single compute instance.
|
||||
Contains just base features to verify base HOT support.
|
||||
|
||||
parameter_groups:
|
||||
- label: Server Group
|
||||
description: A group of parameters for the server
|
||||
- label: Database Group
|
||||
description: A group of parameters for the database
|
||||
'''
|
||||
|
||||
|
||||
class validateTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
|
@ -852,3 +1010,43 @@ class validateTest(HeatTestCase):
|
|||
'KeyName': 'test'}))
|
||||
|
||||
self.assertRaises(exception.StackValidationFailed, stack.validate)
|
||||
|
||||
def test_validate_duplicate_parameters_in_group(self):
|
||||
t = template_format.parse(test_template_duplicate_parameters)
|
||||
template = HOTemplate(t)
|
||||
stack = parser.Stack(self.ctx, 'test_stack', template,
|
||||
environment.Environment({
|
||||
'KeyName': 'test',
|
||||
'ImageId': 'sometestid',
|
||||
'db_password': 'Pass123'
|
||||
}))
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
stack.validate)
|
||||
|
||||
self.assertEqual(_('The InstanceType parameter must be assigned to '
|
||||
'one Parameter Group only.'), str(exc))
|
||||
|
||||
def test_validate_invalid_parameter_in_group(self):
|
||||
t = template_format.parse(test_template_invalid_parameter_name)
|
||||
template = HOTemplate(t)
|
||||
stack = parser.Stack(self.ctx, 'test_stack', template,
|
||||
environment.Environment({
|
||||
'KeyName': 'test',
|
||||
'ImageId': 'sometestid',
|
||||
'db_password': 'Pass123'}))
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
stack.validate)
|
||||
|
||||
self.assertEqual(_('The Parameter name (SomethingNotHere) does not '
|
||||
'reference an existing parameter.'), str(exc))
|
||||
|
||||
def test_validate_no_parameters_in_group(self):
|
||||
t = template_format.parse(test_template_no_parameters)
|
||||
template = HOTemplate(t)
|
||||
stack = parser.Stack(self.ctx, 'test_stack', template)
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
stack.validate)
|
||||
|
||||
self.assertEqual(_('Parameters must be provided for each Parameter '
|
||||
'Group.'), str(exc))
|
||||
|
|
Loading…
Reference in New Issue