Resource type implementations for structured software config
StructuredConfig takes configuration as a Map property, which results in config that is stored and returned as a parsed dict. This will be used for defining both cfn-init and tripleo software configuration inline in a template as strucured yaml. StructuredSoftwareDeployment will transform the config to substitute {"get_input": "<input_name>"} with the current input values and storing the result in the ephemeral derived config. partial blueprint hot-software-config Change-Id: I8029f469dfc2a77453abdab46c1a69ba317fc7ea
This commit is contained in:
parent
700e0c7eba
commit
4b7e9b8366
105
heat/engine/resources/software_config/structured_config.py
Normal file
105
heat/engine/resources/software_config/structured_config.py
Normal file
@ -0,0 +1,105 @@
|
||||
# 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.
|
||||
|
||||
import collections
|
||||
import functools
|
||||
|
||||
from heat.engine import properties
|
||||
from heat.engine.resources.software_config import software_config as sc
|
||||
from heat.engine.resources.software_config import software_deployment as sd
|
||||
|
||||
PROPERTIES = (
|
||||
INPUT_KEY
|
||||
) = (
|
||||
'input_key'
|
||||
)
|
||||
|
||||
DEFAULT_INPUT_KEY = 'get_input'
|
||||
|
||||
|
||||
class StructuredConfig(sc.SoftwareConfig):
|
||||
|
||||
properties_schema = {
|
||||
sc.SoftwareConfig.GROUP: sc.SoftwareConfig.properties_schema[
|
||||
sc.SoftwareConfig.GROUP],
|
||||
sc.SoftwareConfig.OPTIONS: sc.SoftwareConfig.properties_schema[
|
||||
sc.SoftwareConfig.OPTIONS],
|
||||
sc.SoftwareConfig.INPUTS: sc.SoftwareConfig.properties_schema[
|
||||
sc.SoftwareConfig.INPUTS],
|
||||
sc.SoftwareConfig.OUTPUTS: sc.SoftwareConfig.properties_schema[
|
||||
sc.SoftwareConfig.OUTPUTS],
|
||||
sc.SoftwareConfig.CONFIG: properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
_('Map representing the configuration data structure which will '
|
||||
'be serialized to the chosen format.')
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class StructuredDeployment(sd.SoftwareDeployment):
|
||||
|
||||
_sd_ps = sd.SoftwareDeployment.properties_schema
|
||||
|
||||
properties_schema = {
|
||||
sd.SoftwareDeployment.CONFIG: _sd_ps[
|
||||
sd.SoftwareDeployment.CONFIG],
|
||||
sd.SoftwareDeployment.SERVER: _sd_ps[
|
||||
sd.SoftwareDeployment.SERVER],
|
||||
sd.SoftwareDeployment.INPUT_VALUES: _sd_ps[
|
||||
sd.SoftwareDeployment.INPUT_VALUES],
|
||||
sd.SoftwareDeployment.DEPLOY_ACTIONS: _sd_ps[
|
||||
sd.SoftwareDeployment.DEPLOY_ACTIONS],
|
||||
sd.SoftwareDeployment.SIGNAL_TRANSPORT: _sd_ps[
|
||||
sd.SoftwareDeployment.SIGNAL_TRANSPORT],
|
||||
INPUT_KEY: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of key to use for substituting inputs during deployment'),
|
||||
default=DEFAULT_INPUT_KEY,
|
||||
)
|
||||
}
|
||||
|
||||
def _build_derived_config(self, action, source,
|
||||
derived_inputs, derived_options):
|
||||
cfg = source.get(sc.SoftwareConfig.CONFIG)
|
||||
input_key = self.properties.get(INPUT_KEY)
|
||||
|
||||
inputs = dict((i['name'], i['value']) for i in derived_inputs)
|
||||
|
||||
return self.parse(inputs, input_key, cfg)
|
||||
|
||||
@staticmethod
|
||||
def parse(inputs, input_key, snippet):
|
||||
parse = functools.partial(
|
||||
StructuredDeployment.parse, inputs, input_key)
|
||||
|
||||
if isinstance(snippet, collections.Mapping):
|
||||
if len(snippet) == 1:
|
||||
fn_name, args = next(snippet.iteritems())
|
||||
if fn_name == input_key:
|
||||
if isinstance(args, basestring):
|
||||
return inputs.get(args)
|
||||
return dict((k, parse(v)) for k, v in snippet.iteritems())
|
||||
elif (not isinstance(snippet, basestring) and
|
||||
isinstance(snippet, collections.Iterable)):
|
||||
return [parse(v) for v in snippet]
|
||||
else:
|
||||
return snippet
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Heat::StructuredConfig': StructuredConfig,
|
||||
'OS::Heat::StructuredDeployment': StructuredDeployment,
|
||||
}
|
196
heat/tests/test_structured_config.py
Normal file
196
heat/tests/test_structured_config.py
Normal file
@ -0,0 +1,196 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from heat.engine import parser
|
||||
from heat.engine import template
|
||||
import heat.engine.resources.software_config.structured_config as sc
|
||||
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class StructuredConfigTestJSON(HeatTestCase):
|
||||
|
||||
template = {
|
||||
'Resources': {
|
||||
'config_mysql': {
|
||||
'Type': 'OS::Heat::StructuredConfig',
|
||||
'Properties': {'config': {'foo': 'bar'}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stored_config = {'foo': 'bar'}
|
||||
|
||||
def setUp(self):
|
||||
super(StructuredConfigTestJSON, self).setUp()
|
||||
utils.setup_dummy_db()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.properties = {
|
||||
'config': {'foo': 'bar'}
|
||||
}
|
||||
self.stack = parser.Stack(
|
||||
self.ctx, 'software_config_test_stack',
|
||||
template.Template(self.template))
|
||||
self.config = self.stack['config_mysql']
|
||||
heat = mock.MagicMock()
|
||||
self.config.heat = heat
|
||||
self.software_configs = heat.return_value.software_configs
|
||||
|
||||
def test_resource_mapping(self):
|
||||
mapping = sc.resource_mapping()
|
||||
self.assertEqual(2, len(mapping))
|
||||
self.assertEqual(sc.StructuredConfig,
|
||||
mapping['OS::Heat::StructuredConfig'])
|
||||
self.assertEqual(sc.StructuredDeployment,
|
||||
mapping['OS::Heat::StructuredDeployment'])
|
||||
self.assertIsInstance(self.config, sc.StructuredConfig)
|
||||
|
||||
def test_handle_create(self):
|
||||
stc = mock.MagicMock()
|
||||
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
|
||||
stc.id = config_id
|
||||
self.software_configs.create.return_value = stc
|
||||
self.config.handle_create()
|
||||
self.assertEqual(config_id, self.config.resource_id)
|
||||
kwargs = self.software_configs.create.call_args[1]
|
||||
self.assertEqual(self.stored_config, kwargs['config'])
|
||||
|
||||
|
||||
class StructuredDeploymentDerivedTest(HeatTestCase):
|
||||
|
||||
template = {
|
||||
'Resources': {
|
||||
'deploy_mysql': {
|
||||
'Type': 'OS::Heat::StructuredDeployment'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(StructuredDeploymentDerivedTest, self).setUp()
|
||||
utils.setup_dummy_db()
|
||||
self.ctx = utils.dummy_context()
|
||||
props = {
|
||||
'input_values': {'bar': 'baz'},
|
||||
}
|
||||
self.template['Resources']['deploy_mysql']['Properties'] = props
|
||||
self.stack = parser.Stack(
|
||||
self.ctx, 'software_deploly_test_stack',
|
||||
template.Template(self.template))
|
||||
self.deployment = self.stack['deploy_mysql']
|
||||
heat = mock.MagicMock()
|
||||
self.deployments = heat.return_value.software_deployments
|
||||
|
||||
def test_build_derived_config(self):
|
||||
source = {
|
||||
'config': {"foo": {"get_input": "bar"}}
|
||||
}
|
||||
inputs = [{'name': 'bar', 'value': 'baz'}]
|
||||
result = self.deployment._build_derived_config(
|
||||
'CREATE', source, inputs, {})
|
||||
self.assertEqual({"foo": "baz"}, result)
|
||||
|
||||
|
||||
class StructuredDeploymentParseTest(HeatTestCase):
|
||||
|
||||
scenarios = [
|
||||
(
|
||||
'no_functions',
|
||||
dict(input_key='get_input',
|
||||
inputs={},
|
||||
config={'foo': 'bar'},
|
||||
result={'foo': 'bar'}),
|
||||
),
|
||||
(
|
||||
'none_inputs',
|
||||
dict(input_key='get_input',
|
||||
inputs=None,
|
||||
config={'foo': 'bar'},
|
||||
result={'foo': 'bar'}),
|
||||
),
|
||||
(
|
||||
'none_config',
|
||||
dict(input_key='get_input',
|
||||
inputs=None,
|
||||
config=None,
|
||||
result=None),
|
||||
),
|
||||
(
|
||||
'empty_config',
|
||||
dict(input_key='get_input',
|
||||
inputs=None,
|
||||
config='',
|
||||
result=''),
|
||||
),
|
||||
(
|
||||
'simple',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'foo': {'get_input': 'bar'}},
|
||||
result={'foo': 'baa'}),
|
||||
),
|
||||
(
|
||||
'multi_key',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'foo': {'get_input': 'bar', 'other': 'thing'}},
|
||||
result={'foo': {'get_input': 'bar', 'other': 'thing'}}),
|
||||
),
|
||||
(
|
||||
'list_arg',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'foo': {'get_input': ['bar', 'baz']}},
|
||||
result={'foo': {'get_input': ['bar', 'baz']}}),
|
||||
),
|
||||
(
|
||||
'missing_input',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'foo': {'get_input': 'barr'}},
|
||||
result={'foo': None}),
|
||||
),
|
||||
(
|
||||
'deep',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'foo': {'foo': {'get_input': 'bar'}}},
|
||||
result={'foo': {'foo': 'baa'}}),
|
||||
),
|
||||
(
|
||||
'shallow',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa'},
|
||||
config={'get_input': 'bar'},
|
||||
result='baa'),
|
||||
),
|
||||
(
|
||||
'list',
|
||||
dict(input_key='get_input',
|
||||
inputs={'bar': 'baa', 'bar2': 'baz', 'bar3': 'bink'},
|
||||
config={'foo': [
|
||||
{'get_input': 'bar'},
|
||||
{'get_input': 'bar2'},
|
||||
{'get_input': 'bar3'}]},
|
||||
result={'foo': ['baa', 'baz', 'bink']}),
|
||||
)
|
||||
]
|
||||
|
||||
def test_parse(self):
|
||||
parse = sc.StructuredDeployment.parse
|
||||
self.assertEqual(
|
||||
self.result,
|
||||
parse(self.inputs, self.input_key, self.config))
|
Loading…
Reference in New Issue
Block a user