 8eb188457d
			
		
	
	8eb188457d
	
	
	
		
			
			We use an empty dict as config when deployment is created without a config. This fixes 'build_derived_config_params' to accept both SoftwareConfig object and empty dict. Change-Id: Ib9b49e72c117aedc15966c11dafa43eca9e57dac Closes-Bug: #1494672
		
			
				
	
	
		
			360 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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
 | |
| import six
 | |
| import swiftclient.client
 | |
| import testscenarios
 | |
| import testtools
 | |
| 
 | |
| from heatclient.common import deployment_utils
 | |
| from heatclient import exc
 | |
| from heatclient.v1 import software_configs
 | |
| from testtools import matchers
 | |
| 
 | |
| 
 | |
| load_tests = testscenarios.load_tests_apply_scenarios
 | |
| 
 | |
| 
 | |
| def mock_sc(group=None, config=None, options=None,
 | |
|             inputs=None, outputs=None):
 | |
|     return software_configs.SoftwareConfig(None, {
 | |
|         'group': group,
 | |
|         'config': config,
 | |
|         'options': options or {},
 | |
|         'inputs': inputs or [],
 | |
|         'outputs': outputs or [],
 | |
|     }, True)
 | |
| 
 | |
| 
 | |
| class DerivedConfigTest(testtools.TestCase):
 | |
| 
 | |
|     scenarios = [
 | |
|         ('defaults', dict(
 | |
|             action='UPDATE',
 | |
|             source=mock_sc(),
 | |
|             name='s1',
 | |
|             input_values=None,
 | |
|             server_id='1234',
 | |
|             signal_transport='NO_SIGNAL',
 | |
|             signal_id=None,
 | |
|             result={
 | |
|                 'config': '',
 | |
|                 'group': 'Heat::Ungrouped',
 | |
|                 'inputs': [{
 | |
|                     'description': 'ID of the server being deployed to',
 | |
|                     'name': 'deploy_server_id',
 | |
|                     'type': 'String',
 | |
|                     'value': '1234'
 | |
|                 }, {
 | |
|                     'description': 'Name of the current action '
 | |
|                     'being deployed',
 | |
|                     'name': 'deploy_action',
 | |
|                     'type': 'String',
 | |
|                     'value': 'UPDATE'
 | |
|                 }, {
 | |
|                     'description': 'How the server should signal to '
 | |
|                     'heat with the deployment output values.',
 | |
|                     'name': 'deploy_signal_transport',
 | |
|                     'type': 'String',
 | |
|                     'value': 'NO_SIGNAL'}],
 | |
|                 'name': 's1',
 | |
|                 'options': {},
 | |
|                 'outputs': []})),
 | |
|         ('defaults_empty', dict(
 | |
|             action='UPDATE',
 | |
|             source={},
 | |
|             name='s1',
 | |
|             input_values=None,
 | |
|             server_id='1234',
 | |
|             signal_transport='NO_SIGNAL',
 | |
|             signal_id=None,
 | |
|             result={
 | |
|                 'config': '',
 | |
|                 'group': 'Heat::Ungrouped',
 | |
|                 'inputs': [{
 | |
|                     'description': 'ID of the server being deployed to',
 | |
|                     'name': 'deploy_server_id',
 | |
|                     'type': 'String',
 | |
|                     'value': '1234'
 | |
|                 }, {
 | |
|                     'description': 'Name of the current action '
 | |
|                     'being deployed',
 | |
|                     'name': 'deploy_action',
 | |
|                     'type': 'String',
 | |
|                     'value': 'UPDATE'
 | |
|                 }, {
 | |
|                     'description': 'How the server should signal to '
 | |
|                     'heat with the deployment output values.',
 | |
|                     'name': 'deploy_signal_transport',
 | |
|                     'type': 'String',
 | |
|                     'value': 'NO_SIGNAL'}],
 | |
|                 'name': 's1',
 | |
|                 'options': {},
 | |
|                 'outputs': []})),
 | |
| 
 | |
|         ('config_values', dict(
 | |
|             action='UPDATE',
 | |
|             source=mock_sc(
 | |
|                 group='puppet',
 | |
|                 config='do the foo',
 | |
|                 inputs=[
 | |
|                     {'name': 'one', 'default': '1'},
 | |
|                     {'name': 'two'}],
 | |
|                 options={'option1': 'value'},
 | |
|                 outputs=[
 | |
|                     {'name': 'output1'},
 | |
|                     {'name': 'output2'}],
 | |
|             ),
 | |
|             name='s2',
 | |
|             input_values={'one': 'foo', 'two': 'bar', 'three': 'baz'},
 | |
|             server_id='1234',
 | |
|             signal_transport='NO_SIGNAL',
 | |
|             signal_id=None,
 | |
|             result={
 | |
|                 'config': 'do the foo',
 | |
|                 'group': 'puppet',
 | |
|                 'inputs': [{
 | |
|                     'name': 'one',
 | |
|                     'default': '1',
 | |
|                     'value': 'foo'
 | |
|                 }, {
 | |
|                     'name': 'two',
 | |
|                     'value': 'bar'
 | |
|                 }, {
 | |
|                     'name': 'three',
 | |
|                     'type': 'String',
 | |
|                     'value': 'baz'
 | |
|                 }, {
 | |
|                     'description': 'ID of the server being deployed to',
 | |
|                     'name': 'deploy_server_id',
 | |
|                     'type': 'String',
 | |
|                     'value': '1234'
 | |
|                 }, {
 | |
|                     'description': 'Name of the current action '
 | |
|                     'being deployed',
 | |
|                     'name': 'deploy_action',
 | |
|                     'type': 'String',
 | |
|                     'value': 'UPDATE'
 | |
|                 }, {
 | |
|                     'description': 'How the server should signal to '
 | |
|                     'heat with the deployment output values.',
 | |
|                     'name': 'deploy_signal_transport',
 | |
|                     'type': 'String',
 | |
|                     'value': 'NO_SIGNAL'
 | |
|                 }],
 | |
|                 'name': 's2',
 | |
|                 'options': {'option1': 'value'},
 | |
|                 'outputs': [
 | |
|                     {'name': 'output1'},
 | |
|                     {'name': 'output2'}]})),
 | |
|         ('temp_url', dict(
 | |
|             action='UPDATE',
 | |
|             source=mock_sc(),
 | |
|             name='s1',
 | |
|             input_values=None,
 | |
|             server_id='1234',
 | |
|             signal_transport='TEMP_URL_SIGNAL',
 | |
|             signal_id='http://192.0.2.1:8080/foo',
 | |
|             result={
 | |
|                 'config': '',
 | |
|                 'group': 'Heat::Ungrouped',
 | |
|                 'inputs': [{
 | |
|                     'description': 'ID of the server being deployed to',
 | |
|                     'name': 'deploy_server_id',
 | |
|                     'type': 'String',
 | |
|                     'value': '1234'
 | |
|                 }, {
 | |
|                     'description': 'Name of the current action '
 | |
|                     'being deployed',
 | |
|                     'name': 'deploy_action',
 | |
|                     'type': 'String',
 | |
|                     'value': 'UPDATE'
 | |
|                 }, {
 | |
|                     'description': 'How the server should signal to '
 | |
|                     'heat with the deployment output values.',
 | |
|                     'name': 'deploy_signal_transport',
 | |
|                     'type': 'String',
 | |
|                     'value': 'TEMP_URL_SIGNAL'
 | |
|                 }, {
 | |
|                     'description': 'ID of signal to use for signaling '
 | |
|                     'output values',
 | |
|                     'name': 'deploy_signal_id',
 | |
|                     'type': 'String',
 | |
|                     'value': 'http://192.0.2.1:8080/foo'
 | |
|                 }, {
 | |
|                     'description': 'HTTP verb to use for signaling '
 | |
|                     'output values',
 | |
|                     'name': 'deploy_signal_verb',
 | |
|                     'type': 'String',
 | |
|                     'value': 'PUT'}],
 | |
|                 'name': 's1',
 | |
|                 'options': {},
 | |
|                 'outputs': []})),
 | |
|         ('unsupported', dict(
 | |
|             action='UPDATE',
 | |
|             source=mock_sc(),
 | |
|             name='s1',
 | |
|             input_values=None,
 | |
|             server_id='1234',
 | |
|             signal_transport='ASDF',
 | |
|             signal_id=None,
 | |
|             result_error=exc.CommandError,
 | |
|             result_error_msg='Unsupported signal transport ASDF',
 | |
|             result=None)),
 | |
|     ]
 | |
| 
 | |
|     def test_build_derived_config_params(self):
 | |
|         try:
 | |
|             self.assertEqual(
 | |
|                 self.result,
 | |
|                 deployment_utils.build_derived_config_params(
 | |
|                     action=self.action,
 | |
|                     source=self.source,
 | |
|                     name=self.name,
 | |
|                     input_values=self.input_values,
 | |
|                     server_id=self.server_id,
 | |
|                     signal_transport=self.signal_transport,
 | |
|                     signal_id=self.signal_id))
 | |
|         except Exception as e:
 | |
|             if not self.result_error:
 | |
|                 raise e
 | |
|             self.assertIsInstance(e, self.result_error)
 | |
|             self.assertEqual(self.result_error_msg, six.text_type(e))
 | |
| 
 | |
| 
 | |
| class TempURLSignalTest(testtools.TestCase):
 | |
| 
 | |
|     @mock.patch.object(swiftclient.client, 'Connection')
 | |
|     def test_create_swift_client(self, sc_conn):
 | |
|         auth = mock.MagicMock()
 | |
|         auth.get_token.return_value = '1234'
 | |
|         auth.get_endpoint.return_value = 'http://192.0.2.1:8080'
 | |
| 
 | |
|         session = mock.MagicMock()
 | |
| 
 | |
|         args = mock.MagicMock()
 | |
|         args.os_region_name = 'Region1'
 | |
|         args.os_project_name = 'project'
 | |
|         args.os_username = 'user'
 | |
|         args.os_cacert = None
 | |
|         args.insecure = True
 | |
| 
 | |
|         sc_conn.return_value = mock.MagicMock()
 | |
| 
 | |
|         sc = deployment_utils.create_swift_client(auth, session, args)
 | |
| 
 | |
|         self.assertEqual(sc_conn.return_value, sc)
 | |
| 
 | |
|         self.assertEqual(
 | |
|             mock.call(session),
 | |
|             auth.get_token.call_args)
 | |
| 
 | |
|         self.assertEqual(
 | |
|             mock.call(
 | |
|                 session,
 | |
|                 service_type='object-store',
 | |
|                 region_name='Region1'),
 | |
|             auth.get_endpoint.call_args)
 | |
| 
 | |
|         self.assertEqual(
 | |
|             mock.call(
 | |
|                 cacert=None,
 | |
|                 insecure=True,
 | |
|                 key=None,
 | |
|                 tenant_name='project',
 | |
|                 preauthtoken='1234',
 | |
|                 authurl=None,
 | |
|                 user='user',
 | |
|                 preauthurl='http://192.0.2.1:8080',
 | |
|                 auth_version='2.0'),
 | |
|             sc_conn.call_args)
 | |
| 
 | |
|     def test_create_temp_url(self):
 | |
|         swift_client = mock.MagicMock()
 | |
|         swift_client.url = ("http://fake-host.com:8080/v1/AUTH_demo")
 | |
|         swift_client.head_account = mock.Mock(return_value={
 | |
|             'x-account-meta-temp-url-key': '123456'})
 | |
|         swift_client.post_account = mock.Mock()
 | |
| 
 | |
|         uuid_pattern = ('[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB]'
 | |
|                         '[a-f0-9]{3}-[a-f0-9]{12}')
 | |
|         url = deployment_utils.create_temp_url(swift_client, 'bar', 60)
 | |
|         self.assertFalse(swift_client.post_account.called)
 | |
|         regexp = ("http://fake-host.com:8080/v1/AUTH_demo/bar-%s"
 | |
|                   "/%s\?temp_url_sig=[0-9a-f]{40}&"
 | |
|                   "temp_url_expires=[0-9]{10}" % (uuid_pattern, uuid_pattern))
 | |
|         self.assertThat(url, matchers.MatchesRegex(regexp))
 | |
| 
 | |
|         timeout = int(url.split('=')[-1])
 | |
|         self.assertTrue(timeout < 2147483647)
 | |
| 
 | |
|     def test_get_temp_url_no_account_key(self):
 | |
|         swift_client = mock.MagicMock()
 | |
|         swift_client.url = ("http://fake-host.com:8080/v1/AUTH_demo")
 | |
|         head_account = {}
 | |
| 
 | |
|         def post_account(data):
 | |
|             head_account.update(data)
 | |
| 
 | |
|         swift_client.head_account = mock.Mock(return_value=head_account)
 | |
|         swift_client.post_account = post_account
 | |
| 
 | |
|         self.assertNotIn('x-account-meta-temp-url-key', head_account)
 | |
|         deployment_utils.create_temp_url(swift_client, 'bar', 60, 'foo')
 | |
|         self.assertIn('x-account-meta-temp-url-key', head_account)
 | |
| 
 | |
|     def test_build_signal_id_no_signal(self):
 | |
|         hc = mock.MagicMock()
 | |
|         args = mock.MagicMock()
 | |
|         args.signal_transport = 'NO_SIGNAL'
 | |
|         self.assertIsNone(deployment_utils.build_signal_id(hc, args))
 | |
| 
 | |
|     def test_build_signal_id_no_client_auth(self):
 | |
|         hc = mock.MagicMock()
 | |
|         args = mock.MagicMock()
 | |
|         args.os_no_client_auth = True
 | |
|         args.signal_transport = 'TEMP_URL_SIGNAL'
 | |
|         e = self.assertRaises(exc.CommandError,
 | |
|                               deployment_utils.build_signal_id, hc, args)
 | |
|         self.assertEqual((
 | |
|             'Cannot use --os-no-client-auth, auth required to create '
 | |
|             'a Swift TempURL.'),
 | |
|             six.text_type(e))
 | |
| 
 | |
|     @mock.patch.object(deployment_utils, 'create_temp_url')
 | |
|     @mock.patch.object(deployment_utils, 'create_swift_client')
 | |
|     def test_build_signal_id(self, csc, ctu):
 | |
|         hc = mock.MagicMock()
 | |
|         args = mock.MagicMock()
 | |
|         args.name = 'foo'
 | |
|         args.timeout = 60
 | |
|         args.os_no_client_auth = False
 | |
|         args.signal_transport = 'TEMP_URL_SIGNAL'
 | |
|         csc.return_value = mock.MagicMock()
 | |
|         temp_url = (
 | |
|             'http://fake-host.com:8080/v1/AUTH_demo/foo/'
 | |
|             'a81a74d5-c395-4269-9670-ddd0824fd696'
 | |
|             '?temp_url_sig=6a68371d602c7a14aaaa9e3b3a63b8b85bd9a503'
 | |
|             '&temp_url_expires=1425270977')
 | |
|         ctu.return_value = temp_url
 | |
| 
 | |
|         self.assertEqual(
 | |
|             temp_url, deployment_utils.build_signal_id(hc, args))
 | |
|         self.assertEqual(
 | |
|             mock.call(hc.http_client.auth, hc.http_client.session, args),
 | |
|             csc.call_args)
 | |
|         self.assertEqual(
 | |
|             mock.call(csc.return_value, 'foo', 60),
 | |
|             ctu.call_args)
 |