From 95ac80a0d5e30c068795a7f08fadc8800df73b33 Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Mon, 17 Jun 2013 14:39:19 +1000 Subject: [PATCH] API support for Environments Support "Environment" section in the create/update body. We the roll up the parameters and the environment and send that as json using the rpc "params" argument. So no changes to the rpc layer are needed. The engine knows how to deal with both formats in the Environments class. blueprint envirnonments Change-Id: I6d6085d5c052a5f9115ecd46891b00d2fad4d1db --- .../src/wadls/heat-api/src/heat-api-1.0.wadl | 12 ++++ heat/api/openstack/v1/stacks.py | 34 +++++++++-- heat/tests/test_api_openstack_v1.py | 59 +++++++++++++++---- 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/doc/docbkx/api-ref/src/wadls/heat-api/src/heat-api-1.0.wadl b/doc/docbkx/api-ref/src/wadls/heat-api/src/heat-api-1.0.wadl index 146ed7a7c4..be9340fee5 100644 --- a/doc/docbkx/api-ref/src/wadls/heat-api/src/heat-api-1.0.wadl +++ b/doc/docbkx/api-ref/src/wadls/heat-api/src/heat-api-1.0.wadl @@ -142,6 +142,12 @@

+ +

+ A JSON envionment for the stack. +

+ +

User-defined parameter names to pass to the template. @@ -226,6 +232,12 @@

+ +

+ A JSON envionment for the stack. +

+ +

User-defined parameter names to pass to the template. diff --git a/heat/api/openstack/v1/stacks.py b/heat/api/openstack/v1/stacks.py index 9dcec53eb1..6e7f897def 100644 --- a/heat/api/openstack/v1/stacks.py +++ b/heat/api/openstack/v1/stacks.py @@ -44,11 +44,13 @@ class InstantiationData(object): PARAM_TEMPLATE, PARAM_TEMPLATE_URL, PARAM_USER_PARAMS, + PARAM_ENVIRONMENT, ) = ( 'stack_name', 'template', 'template_url', 'parameters', + 'environment', ) def __init__(self, data): @@ -99,11 +101,33 @@ class InstantiationData(object): return self.format_parse(template_data, 'Template') - def user_params(self): + def environment(self): """ - Get the user-supplied parameters for the stack in JSON format. + Get the user-supplied environment for the stack in YAML format. + If the user supplied Parameters then merge these into the + environment global options. """ - return self.data.get(self.PARAM_USER_PARAMS, {}) + env = {} + if self.PARAM_ENVIRONMENT in self.data: + env_data = self.data[self.PARAM_ENVIRONMENT] + if isinstance(env_data, dict): + env = env_data + else: + env = self.format_parse(env_data, + 'Environment', + add_template_sections=False) + + for field in env: + if field not in ('parameters', 'resource_registry'): + reason = _("%s not in valid in the environment") % field + raise exc.HTTPBadRequest(reason) + + if self.PARAM_USER_PARAMS not in env: + env[self.PARAM_USER_PARAMS] = {} + + parameters = self.data.get(self.PARAM_USER_PARAMS, {}) + env[self.PARAM_USER_PARAMS].update(parameters) + return env def args(self): """ @@ -180,7 +204,7 @@ class StackController(object): result = self.engine.create_stack(req.context, data.stack_name(), data.template(), - data.user_params(), + data.environment(), data.args()) except rpc_common.RemoteError as ex: return util.remote_error(ex) @@ -255,7 +279,7 @@ class StackController(object): res = self.engine.update_stack(req.context, identity, data.template(), - data.user_params(), + data.environment(), data.args()) except rpc_common.RemoteError as ex: return util.remote_error(ex) diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index b1e35b033a..4c4694c2ca 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -114,21 +114,54 @@ blarg: wibble data = stacks.InstantiationData(body) self.assertRaises(webob.exc.HTTPBadRequest, data.template) - def test_user_params(self): + def test_parameters(self): params = {'foo': 'bar', 'blarg': 'wibble'} body = {'parameters': params} data = stacks.InstantiationData(body) - self.assertEqual(data.user_params(), params) + self.assertEqual(data.environment(), body) - def test_user_params_missing(self): - params = {'foo': 'bar', 'blarg': 'wibble'} - body = {'not the parameters': params} + def test_environment_only_params(self): + env = {'parameters': {'foo': 'bar', 'blarg': 'wibble'}} + body = {'environment': env} data = stacks.InstantiationData(body) - self.assertEqual(data.user_params(), {}) + self.assertEqual(data.environment(), env) + + def test_environment_and_parameters(self): + body = {'parameters': {'foo': 'bar'}, + 'environment': {'parameters': {'blarg': 'wibble'}}} + expect = {'parameters': {'blarg': 'wibble', + 'foo': 'bar'}} + data = stacks.InstantiationData(body) + self.assertEqual(data.environment(), expect) + + def test_parameters_override_environment(self): + # This tests that the cli parameters will override + # any parameters in the environment. + body = {'parameters': {'foo': 'bar', + 'tester': 'Yes'}, + 'environment': {'parameters': {'blarg': 'wibble', + 'tester': 'fail'}}} + expect = {'parameters': {'blarg': 'wibble', + 'foo': 'bar', + 'tester': 'Yes'}} + data = stacks.InstantiationData(body) + self.assertEqual(data.environment(), expect) + + def test_environment_bad_format(self): + body = {'environment': {'somethingnotsupported': {'blarg': 'wibble'}}} + data = stacks.InstantiationData(body) + self.assertRaises(webob.exc.HTTPBadRequest, data.environment) + + def test_environment_missing(self): + env = {'foo': 'bar', 'blarg': 'wibble'} + body = {'not the environment': env} + data = stacks.InstantiationData(body) + self.assertEqual(data.environment(), {'parameters': {}}) def test_args(self): body = { 'parameters': {}, + 'environment': {}, 'stack_name': 'foo', 'template': {}, 'template_url': 'http://example.com/', @@ -324,7 +357,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'create_stack', 'args': {'stack_name': identity.stack_name, 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndReturn(dict(identity)) @@ -358,7 +391,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'create_stack', 'args': {'stack_name': stack_name, 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndRaise(rpc_common.RemoteError("AttributeError")) @@ -367,7 +400,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'create_stack', 'args': {'stack_name': stack_name, 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndRaise(rpc_common.RemoteError("UnknownUserParameter")) @@ -401,7 +434,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'create_stack', 'args': {'stack_name': stack_name, 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndRaise(rpc_common.RemoteError("StackExists")) @@ -430,7 +463,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'create_stack', 'args': {'stack_name': stack_name, 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndRaise(rpc_common.RemoteError( @@ -719,7 +752,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'update_stack', 'args': {'stack_identity': dict(identity), 'template': template, - 'params': parameters, + 'params': {'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndReturn(dict(identity)) @@ -751,7 +784,7 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'method': 'update_stack', 'args': {'stack_identity': dict(identity), 'template': template, - 'params': parameters, + 'params': {u'parameters': parameters}, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, None).AndRaise(rpc_common.RemoteError("StackNotFound"))