635
673
def test_handle_signal_ok_zero(self):
636
674
self._create_stack(self.template)
637
675
self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
642
{'name': 'failed', 'error_output': True}
647
'status': self.deployment.IN_PROGRESS
649
self.rpc_client.show_software_deployment.return_value = sd
650
self.rpc_client.show_software_config.return_value = sc
676
rpcc = self.rpc_client
677
rpcc.signal_software_deployment.return_value = 'deployment succeeded'
653
680
'deploy_status_code': 0
655
682
ret = self.deployment.handle_signal(details)
656
683
self.assertEqual('deployment succeeded', ret)
658
'deployment_id': 'c8a19429-7fde-47ea-a42f-40045488226c',
661
'deploy_status_code': 0,
662
'deploy_stderr': None,
663
'deploy_stdout': None
665
'status': 'COMPLETE',
666
'status_reason': 'Outputs received'},
667
self.rpc_client.update_software_deployment.call_args[1])
684
ca = rpcc.signal_software_deployment.call_args[0]
685
self.assertEqual(self.ctx, ca[0])
686
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', ca[1])
687
self.assertEqual({'foo': 'bar', 'deploy_status_code': 0}, ca[2])
688
self.assertIsNotNone(ca[3])
669
690
def test_handle_signal_ok_str_zero(self):
670
691
self._create_stack(self.template)
671
692
self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
676
{'name': 'failed', 'error_output': True}
681
'status': self.deployment.IN_PROGRESS
683
self.rpc_client.show_software_deployment.return_value = sd
684
self.rpc_client.show_software_config.return_value = sc
693
rpcc = self.rpc_client
694
rpcc.signal_software_deployment.return_value = 'deployment succeeded'
687
697
'deploy_status_code': '0'
689
699
ret = self.deployment.handle_signal(details)
690
700
self.assertEqual('deployment succeeded', ret)
692
'deployment_id': 'c8a19429-7fde-47ea-a42f-40045488226c',
695
'deploy_status_code': '0',
696
'deploy_stderr': None,
697
'deploy_stdout': None
699
'status': 'COMPLETE',
700
'status_reason': 'Outputs received'},
701
self.rpc_client.update_software_deployment.call_args[1])
701
ca = rpcc.signal_software_deployment.call_args[0]
702
self.assertEqual(self.ctx, ca[0])
703
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', ca[1])
704
self.assertEqual({'foo': 'bar', 'deploy_status_code': '0'}, ca[2])
705
self.assertIsNotNone(ca[3])
703
707
def test_handle_signal_failed(self):
704
708
self._create_stack(self.template)
705
709
self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
710
{'name': 'failed', 'error_output': True}
715
'status': self.deployment.IN_PROGRESS
717
self.rpc_client.show_software_deployment.return_value = sd
718
self.rpc_client.show_software_config.return_value = sc
710
rpcc = self.rpc_client
711
rpcc.signal_software_deployment.return_value = 'deployment failed'
719
713
details = {'failed': 'no enough memory found.'}
720
714
ret = self.deployment.handle_signal(details)
721
715
self.assertEqual('deployment failed', ret)
723
'deployment_id': 'c8a19429-7fde-47ea-a42f-40045488226c',
725
'deploy_status_code': None,
726
'deploy_stderr': None,
727
'deploy_stdout': None,
728
'failed': 'no enough memory found.'
731
'status_reason': 'failed : no enough memory found.'},
732
self.rpc_client.update_software_deployment.call_args[1])
716
ca = rpcc.signal_software_deployment.call_args[0]
717
self.assertEqual(self.ctx, ca[0])
718
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', ca[1])
719
self.assertEqual(details, ca[2])
720
self.assertIsNotNone(ca[3])
734
722
# Test bug 1332355, where details contains a translateable message
735
723
details = {'failed': _('need more memory.')}
736
self.deployment.handle_signal(details)
738
'deployment_id': 'c8a19429-7fde-47ea-a42f-40045488226c',
740
'deploy_status_code': None,
741
'deploy_stderr': None,
742
'deploy_stdout': None,
743
'failed': 'need more memory.'
746
'status_reason': 'failed : need more memory.'},
747
self.rpc_client.update_software_deployment.call_args[1])
724
ret = self.deployment.handle_signal(details)
725
self.assertEqual('deployment failed', ret)
726
ca = rpcc.signal_software_deployment.call_args[0]
727
self.assertEqual(self.ctx, ca[0])
728
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', ca[1])
729
self.assertEqual(details, ca[2])
730
self.assertIsNotNone(ca[3])
749
732
def test_handle_status_code_failed(self):
750
733
self._create_stack(self.template)
751
734
self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
755
'status': self.deployment.IN_PROGRESS
757
self.rpc_client.show_software_deployment.return_value = sd
735
rpcc = self.rpc_client
736
rpcc.signal_software_deployment.return_value = 'deployment failed'
759
739
'deploy_stdout': 'A thing happened',
760
740
'deploy_stderr': 'Then it broke',
761
741
'deploy_status_code': -1
763
743
self.deployment.handle_signal(details)
765
'c8a19429-7fde-47ea-a42f-40045488226c',
766
self.rpc_client.show_software_deployment.call_args[0][1])
768
'deployment_id': 'c8a19429-7fde-47ea-a42f-40045488226c',
770
'deploy_stdout': 'A thing happened',
771
'deploy_stderr': 'Then it broke',
772
'deploy_status_code': -1
775
'status_reason': ('deploy_status_code : Deployment exited '
776
'with non-zero status code: -1')},
777
self.rpc_client.update_software_deployment.call_args[1])
744
ca = rpcc.signal_software_deployment.call_args[0]
745
self.assertEqual(self.ctx, ca[0])
746
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', ca[1])
747
self.assertEqual(details, ca[2])
748
self.assertIsNotNone(ca[3])
779
750
def test_handle_signal_not_waiting(self):
780
751
self._create_stack(self.template)
782
'status': self.deployment.COMPLETE
784
self.rpc_client.show_software_deployment.return_value = sd
752
rpcc = self.rpc_client
753
rpcc.signal_software_deployment.return_value = None
786
755
self.assertIsNone(self.deployment.handle_signal(details))
756
ca = rpcc.signal_software_deployment.call_args[0]
757
self.assertEqual(self.ctx, ca[0])
758
self.assertIsNone(ca[1])
759
self.assertIsNone(ca[2])
760
self.assertIsNotNone(ca[3])
788
762
def test_fn_get_att(self):
789
763
self._create_stack(self.template)
828
802
self._create_stack(self.template)
830
804
self.mock_software_config()
805
sd = self.mock_deployment()
806
rsrc = self.stack['deployment_mysql']
808
self.rpc_client.show_software_deployment.return_value = sd
809
self.deployment.resource_id = sd['id']
810
config_id = '0ff2e903-78d7-4cca-829e-233af3dae705'
811
prop_diff = {'config': config_id}
812
props = copy.copy(rsrc.properties.data)
813
props.update(prop_diff)
814
snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props)
816
# by default (no 'actions' property) SoftwareDeployment must only
817
# trigger for CREATE and UPDATE
818
self.assertIsNotNone(self.deployment.handle_create())
819
self.assertIsNotNone(self.deployment.handle_update(
820
json_snippet=snippet, tmpl_diff=None, prop_diff=prop_diff))
821
# ... but it must not trigger for SUSPEND, RESUME and DELETE
822
self.assertIsNone(self.deployment.handle_suspend())
823
self.assertIsNone(self.deployment.handle_resume())
824
self.assertIsNone(self.deployment.handle_delete())
826
def test_handle_action_for_component(self):
827
self._create_stack(self.template)
829
self.mock_software_component()
830
sd = self.mock_deployment()
831
rsrc = self.stack['deployment_mysql']
833
self.rpc_client.show_software_deployment.return_value = sd
834
self.deployment.resource_id = sd['id']
835
config_id = '0ff2e903-78d7-4cca-829e-233af3dae705'
836
prop_diff = {'config': config_id}
837
props = copy.copy(rsrc.properties.data)
838
props.update(prop_diff)
839
snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props)
841
# for a SoftwareComponent, SoftwareDeployment must always trigger
842
self.assertIsNotNone(self.deployment.handle_create())
843
self.assertIsNotNone(self.deployment.handle_update(
844
json_snippet=snippet, tmpl_diff=None, prop_diff=prop_diff))
845
self.assertIsNotNone(self.deployment.handle_suspend())
846
self.assertIsNotNone(self.deployment.handle_resume())
847
self.assertIsNotNone(self.deployment.handle_delete())
849
def test_get_temp_url(self):
852
sc = mock.MagicMock()
854
'heat.engine.clients.os.swift.SwiftClientPlugin._create')
855
scc.return_value = sc
856
sc.head_account.return_value = {
857
'x-account-meta-temp-url-key': 'secrit'
859
sc.url = 'http://192.0.2.1/v1/AUTH_test_tenant_id'
861
self._create_stack(self.template_temp_url_signal)
863
def data_set(key, value, redact=False):
864
dep_data[key] = value
866
self.deployment.data_set = data_set
867
self.deployment.data = mock.Mock(
868
return_value=dep_data)
870
self.deployment.id = 23
871
self.deployment.uuid = str(uuid.uuid4())
872
self.deployment.action = self.deployment.CREATE
873
container = self.deployment.physical_resource_name()
875
temp_url = self.deployment._get_temp_url()
876
temp_url_pattern = re.compile(
877
'^http://192.0.2.1/v1/AUTH_test_tenant_id/'
878
'(software_deployment_test_stack-deployment_mysql-.*)/(.*)'
879
'\\?temp_url_sig=.*&temp_url_expires=\\d*$')
880
self.assertRegexpMatches(temp_url, temp_url_pattern)
881
m = temp_url_pattern.search(temp_url)
882
object_name = m.group(2)
883
self.assertEqual(container, m.group(1))
884
self.assertEqual(dep_data['signal_object_name'], object_name)
886
self.assertEqual(dep_data['signal_temp_url'], temp_url)
888
self.assertEqual(temp_url, self.deployment._get_temp_url())
890
sc.put_container.assert_called_once_with(container)
891
sc.put_object.assert_called_once_with(container, object_name, '')
893
def test_delete_temp_url(self):
894
object_name = str(uuid.uuid4())
896
'signal_object_name': object_name
898
self._create_stack(self.template_temp_url_signal)
900
self.deployment.data_delete = mock.MagicMock()
901
self.deployment.data = mock.Mock(
902
return_value=dep_data)
904
sc = mock.MagicMock()
905
sc.head_container.return_value = {
906
'x-container-object-count': 0
909
'heat.engine.clients.os.swift.SwiftClientPlugin._create')
910
scc.return_value = sc
912
self.deployment.id = 23
913
self.deployment.uuid = str(uuid.uuid4())
914
container = self.deployment.physical_resource_name()
915
self.deployment._delete_temp_url()
916
sc.delete_object.assert_called_once_with(container, object_name)
918
[mock.call('signal_object_name'), mock.call('signal_temp_url')],
919
self.deployment.data_delete.mock_calls)
921
swift_exc = swift.SwiftClientPlugin.exceptions_module
922
sc.delete_object.side_effect = swift_exc.ClientException(
923
'Not found', http_status=404)
924
self.deployment._delete_temp_url()
926
[mock.call('signal_object_name'), mock.call('signal_temp_url'),
927
mock.call('signal_object_name'), mock.call('signal_temp_url')],
928
self.deployment.data_delete.mock_calls)
930
del(dep_data['signal_object_name'])
931
self.deployment.physical_resource_name = mock.Mock()
932
self.deployment._delete_temp_url()
933
self.assertFalse(self.deployment.physical_resource_name.called)
935
def test_handle_action_temp_url(self):
937
self._create_stack(self.template_temp_url_signal)
940
'http://192.0.2.1/v1/AUTH_a/b/c'
941
'?temp_url_sig=ctemp_url_expires=1234')
943
self.deployment.data = mock.Mock(
944
return_value=dep_data)
946
self.mock_software_config()
832
948
for action in ('DELETE', 'SUSPEND', 'RESUME'):
833
949
self.assertIsNone(self.deployment._handle_action(action))
834
950
for action in ('CREATE', 'UPDATE'):
835
951
self.assertIsNotNone(self.deployment._handle_action(action))
837
def test_handle_action_for_component(self):
838
self._create_stack(self.template)
840
self.mock_software_component()
842
for action in ('CREATE', 'UPDATE', 'DELETE', 'SUSPEND', 'RESUME'):
843
self.assertIsNotNone(self.deployment._handle_action(action))
846
954
class SoftwareDeploymentsTest(common.HeatTestCase):