Beispiel #1
0
 def test_deployments_delete(self):
     self.client.deployments.delete = MagicMock()
     self.client.executions.list = MagicMock(
         side_effect=CloudifyClientError(
             '`Deployment` with ID `my-dep` was not found')
     )
     self.invoke('cfy deployments delete my-dep')
Beispiel #2
0
    def do_request(self, method, url, *args, **kwargs):
        kwargs.setdefault('timeout', self.default_timeout_sec)

        copied_data = None
        if isinstance(kwargs.get('data'), types.GeneratorType):
            copied_data = itertools.tee(kwargs.pop('data'), self.retries)

        errors = {}
        for retry in range(self.retries):
            manager_to_try = next(self.hosts)
            self.host = manager_to_try
            if copied_data is not None:
                kwargs['data'] = copied_data[retry]

            try:
                return super(ClusterHTTPClient,
                             self).do_request(method, url, *args, **kwargs)
            except (requests.exceptions.ConnectionError) as error:
                self.logger.debug('Connection error when trying to connect to '
                                  'manager {0}'.format(error))
                errors[manager_to_try] = error
                continue
            except CloudifyClientError as e:
                errors[manager_to_try] = e.status_code
                if e.response.status_code == 404 and \
                        self._is_fileserver_download(e.response):
                    continue
                else:
                    raise

        raise CloudifyClientError('HTTP Client error: {0} {1} ({2})'.format(
            method.__name__.upper(), url,
            ', '.join('{0}: {1}'.format(host, e)
                      for host, e in errors.items())))
    def test_post_execute_client_error(self):
        # Tests that execute client error ignored

        test_name = 'test_post_execute_client_error'
        _ctx = self.get_mock_ctx(test_name, node_type=DEP_TYPE)
        current_ctx.set(_ctx)
        _ctx.instance.runtime_properties['deployment'] = dict()

        cfy_mock_client = MockCloudifyRestClient()

        cfy_mock_client.deployments.outputs.get = \
            mock.MagicMock(side_effect=CloudifyClientError('Mistake'))

        poll_with_timeout_test = \
            'cloudify_deployment_proxy.DeploymentProxyBase.' \
            'verify_execution_successful'

        with mock.patch(
            'cloudify_deployment_proxy.CloudifyClient'
        ) as mock_local_client:
            mock_local_client.return_value = cfy_mock_client

            with mock.patch(poll_with_timeout_test) as poll:
                poll.return_value = False
                self.assertRaises(NonRecoverableError,
                                  execute_start,
                                  operation='execute_workflow',
                                  deployment_id=test_name,
                                  workflow_id='install',
                                  client={'host': 'localhost'},
                                  timeout=.001)
        del _ctx
Beispiel #4
0
    def test_ExecutionRunner_get_runtime_properties(self):
        _client = Mock()
        exec_inst = execution.ExecutionRunner(Mock(), _client)

        # success
        _client.executions.get = Mock(return_value={'status': 'terminated'})
        self.assertTrue(exec_inst._check_execution_status('abc-bcd'))

        # failed
        _client.executions.get = Mock(return_value={'status': 'failed'})
        with self.assertRaises(RuntimeError):
            exec_inst._check_execution_status('abc-bcd')

        # unknow
        _client.executions.get = Mock(return_value={'status': 'unknow'})
        fake_time_values = [480, 240, 120, 60, 0]

        def fake_time(*_):
            return fake_time_values.pop()

        with patch('time.time', fake_time):
            self.assertFalse(exec_inst._check_execution_status('abc-bcd'))

        # no status
        _client.executions.get = Mock(return_value={'status': None})
        with self.assertRaises(RuntimeError):
            self.assertFalse(exec_inst._check_execution_status('abc-bcd'))

        # exeption in communication
        _client.executions.get = Mock(side_effect=CloudifyClientError('abc'))
        with self.assertRaises(RuntimeError):
            self.assertFalse(exec_inst._check_execution_status('abc-bcd'))
    def test_client_missing_ok(self, mock_cfy):
        l_call = mock_cfy.executions.get
        l_call.side_effect = [CloudifyClientError("test", status_code=404)]

        tasks.wait_for_execution("e_id", True, "c_id")

        l_call.assert_has_calls([mock.call("e_id")])
    def test_invalid_blueprint_upload(self, mock_cfy):
        b = Blueprint.objects.create()
        self.wd.write((str(b.id), "blueprint.yaml"), b"test: pair")
        c = Container.objects.create(blueprint=b)
        call = mock_cfy.blueprints.publish_archive
        call.side_effect = CloudifyClientError("test")

        with self.assertRaises(CloudifyClientError):
            tasks.upload_blueprint(c.cfy_id)
    def test_workflows_get_nonexistent_deployment(self):

        expected_message = ("Deployment 'nonexistent-dep' "
                            "not found on management server")

        self.client.deployments.get = MagicMock(
            side_effect=CloudifyClientError(expected_message))
        self._assert_ex("cfy workflows get -w wf -d nonexistent-dep -v",
                        expected_message)
Beispiel #8
0
    def test_workflows_get_nonexistent_deployment(self):

        expected_message = \
            "Deployment 'nonexistent-dep' not found on manager server"

        self.client.deployments.get = MagicMock(
            side_effect=CloudifyClientError(expected_message))
        self.invoke('cfy workflows get wf -d nonexistent-dep -v',
                    err_str_segment=expected_message,
                    exception=CloudifyClientError)
    def test_invalid(self, mock_cfy):
        b = Blueprint.objects.create()
        c = Container.objects.create(blueprint=b)
        call = mock_cfy.blueprints.delete
        call.side_effect = CloudifyClientError("test")

        with self.assertRaises(CloudifyClientError):
            tasks.delete_blueprint(c.cfy_id)

        call.assert_called_once_with(b.cfy_id)
    def test_fail(self, mock_cfy):
        b = Blueprint.objects.create()
        c = Container.objects.create(blueprint=b)
        call = mock_cfy.executions.start
        call.side_effect = CloudifyClientError("test")

        with self.assertRaises(CloudifyClientError):
            tasks.uninstall_blueprint(c.cfy_id)

        call.assert_called_once_with(b.cfy_id, "uninstall")
    def test_client_missing(self, mock_cfy):
        l_call = mock_cfy.executions.get
        l_call.side_effect = [
            mock.Mock(status="started"),
            CloudifyClientError("test", status_code=404)
        ]

        with self.assertRaises(CloudifyClientError):
            tasks.wait_for_execution("e_id", False, "c_id")

        l_call.assert_has_calls([mock.call("e_id")] * 2)
Beispiel #12
0
    def _prepare_put_request(self,
                             archive_location,
                             application_file_name,
                             visibility,
                             progress_callback,
                             async_upload,
                             labels=None,
                             created_at=None,
                             owner=None,
                             state=None,
                             skip_execution=False):
        query_params = {
            'visibility': visibility,
            'async_upload': async_upload,
            'skip_execution': skip_execution
        }
        if application_file_name is not None:
            query_params['application_file_name'] = \
                urlquote(application_file_name)
        if labels is not None:
            labels_params = []
            for label in labels:
                if (not isinstance(label, dict)) or len(label) != 1:
                    raise CloudifyClientError(
                        'Labels must be a list of 1-entry dictionaries: '
                        '[{<key1>: <value1>}, {<key2>: [<value2>, <value3>]}, '
                        '...]')

                [(key, value)] = label.items()
                value = value.replace('=', '\\=').replace(',', '\\,')
                labels_params.append('{0}={1}'.format(key, value))
            query_params['labels'] = ','.join(labels_params)
        if created_at:
            query_params['created_at'] = created_at
        if owner:
            query_params['owner'] = owner
        if state:
            query_params['state'] = state

        # For a Windows path (e.g. "C:\aaa\bbb.zip") scheme is the
        # drive letter and therefore the 2nd condition is present
        if urlparse(archive_location).scheme and \
                not os.path.exists(archive_location):
            # archive location is URL
            query_params['blueprint_archive_url'] = archive_location
            data = None
        else:
            # archive location is a system path
            data = bytes_stream_utils.request_data_file_stream(
                archive_location,
                progress_callback=progress_callback,
                client=self.api)

        return query_params, data
    def test_fail(self, mock_cfy):
        b = Blueprint.objects.create()
        c = Container.objects.create(blueprint=b)
        c_call = mock_cfy.deployments.delete
        c_call.side_effect = CloudifyClientError("test")
        l_call = mock_cfy.executions.list

        with self.assertRaises(CloudifyClientError):
            tasks.delete_deployment(c.cfy_id)

        c_call.assert_called_once_with(b.cfy_id)
        l_call.assert_not_called()
    def test_deployments_execute_nonexistent_operation(self):
        # verifying that the CLI allows for arbitrary operation names,
        # while also ensuring correct error-handling of nonexistent
        # operations

        expected_error = "operation nonexistent-operation doesn't exist"

        self.client.executions.start = MagicMock(
            side_effect=CloudifyClientError(expected_error))

        command = 'cfy executions start ' \
                  '-w nonexistent-operation -d a-deployment-id'
        self._assert_ex(command, expected_error)
 def test_execute_failing_to_fetch_capabilities(self):
     with mock.patch('cloudify.manager.get_rest_client') as mock_client:
         mock_client.return_value = self.cfy_mock_client
         self.cfy_mock_client.deployments.capabilities.get =\
             mock.MagicMock(side_effect=CloudifyClientError(
                            'Failing to get capabilities'))
         poll_with_timeout_test = \
             'cloudify_types.component.polling.poll_with_timeout'
         with mock.patch(poll_with_timeout_test) as poll:
             poll.return_value = True
             self.assertRaises(NonRecoverableError,
                               execute_start,
                               deployment_id='dep_name',
                               workflow_id='install')
    def test_happy_path(self, mock_cfy):
        b = Blueprint.objects.create()
        c = Container.objects.create(blueprint=b)
        c_call = mock_cfy.deployments.delete
        l_call = mock_cfy.executions.list
        l_call.side_effect = CloudifyClientError("test", status_code=404)

        result = tasks.delete_deployment(c.cfy_id)

        b.refresh_from_db()
        c_call.assert_called_once_with(b.cfy_id)
        l_call.assert_called_once_with(
            b.cfy_id, workflow_id="delete_deployment_environment")
        self.assertEqual(b.state, Blueprint.State.deleting_deployment)
        self.assertIsNone(result)
    def test_successful_upload_existing_blueprint(self):
        with mock.patch('cloudify.manager.get_rest_client') as mock_client:
            self.cfy_mock_client.blueprints._upload = (mock.MagicMock(
                side_effect=CloudifyClientError('already exists')))
            mock_client.return_value = self.cfy_mock_client

            blueprint_params = dict()
            blueprint_params['blueprint'] = {}
            blueprint_params['blueprint']['id'] = 'blu_name'
            blueprint_params['blueprint']['blueprint_archive'] = self.archive
            self.resource_config['resource_config'] = blueprint_params

            output = upload_blueprint(operation='upload_blueprint',
                                      **self.resource_config)
            self.assertTrue(output)
Beispiel #18
0
    def test_get_profile_from_secret_store(self):
        rest_client = Mock()
        rest_client.secrets.get = Mock(return_value={'value': 'some_result'})

        # we have such secret
        self.assertEqual(
            resource_management_plugin._get_profile_from_secret_store(
                rest_client, 'a'),
            'some_result'
        )
        rest_client.secrets.get.assert_called_with(key='a')

        # no sudh secret
        rest_client.secrets.get = Mock(side_effect=CloudifyClientError(''))
        with self.assertRaises(NonRecoverableError):
            resource_management_plugin._get_profile_from_secret_store(
                rest_client, 'b')
    def test_invalid(self, mock_cfy, mock_parse):
        b = Blueprint.objects.create()
        c = Container.objects.create(blueprint=b)
        c_call = mock_cfy.deployments.create
        c_call.side_effect = CloudifyClientError("test")
        l_call = mock_cfy.executions.list
        mock_parse.return_value = {"inputs": {"1": "_1", "3": "_3"}}
        [Input.objects.create(key=str(i), value="v") for i in range(5)]

        with self.assertRaises(CloudifyClientError):
            tasks.create_deployment(c.cfy_id)

        c_call.assert_called_once_with(b.cfy_id,
                                       b.cfy_id,
                                       inputs={
                                           "1": "v",
                                           "3": "v"
                                       })
        l_call.assert_not_called()
Beispiel #20
0
    def do_request(self, *args, **kwargs):
        kwargs.setdefault('timeout', self.default_timeout_sec)

        copied_data = None
        if isinstance(kwargs.get('data'), types.GeneratorType):
            copied_data = itertools.tee(kwargs.pop('data'), self.retries)

        for retry in range(self.retries):
            manager_to_try = next(self.hosts)
            self.host = manager_to_try
            if copied_data is not None:
                kwargs['data'] = copied_data[retry]

            try:
                return super(ClusterHTTPClient,
                             self).do_request(*args, **kwargs)
            except (requests.exceptions.ConnectionError):
                continue

        raise CloudifyClientError('No active node in the cluster!')
Beispiel #21
0
    def do_request(self, *args, **kwargs):
        # this request can be retried for each manager - if the data is
        # a generator, we need to copy it, so we can send it more than once
        copied_data = None
        if isinstance(kwargs.get('data'), types.GeneratorType):
            copied_data = itertools.tee(kwargs.pop('data'),
                                        len(self._cluster) + 1)

        if kwargs.get('timeout') is None:
            kwargs['timeout'] = self.default_timeout_sec

        if copied_data is not None:
            kwargs['data'] = copied_data[-1]

        manager_host = get_target_manager()
        if manager_host:
            self.host = manager_host
            return self._try_do_request(*args, **kwargs)

        # First try with the main manager ip given when creating the profile
        # with `cfy profiles use`
        self.host = self._profile.manager_ip
        response = self._try_do_request(*args, **kwargs)
        if response:
            return response

        for node_index, node in list(
                enumerate(self._profile.cluster[CloudifyNodeType.MANAGER])):
            if self._profile.manager_ip in [node['host_ip'], node['hostname']]:
                continue
            self._use_node(node)
            if copied_data is not None:
                kwargs['data'] = copied_data[node_index]

            response = self._try_do_request(*args, **kwargs)
            if response:
                return response

        raise CloudifyClientError('All cluster nodes are offline')
Beispiel #22
0
    def do_request(self, *args, **kwargs):
        # this request can be retried for each manager - if the data is
        # a generator, we need to copy it, so we can send it more than once
        copied_data = None
        if isinstance(kwargs.get('data'), types.GeneratorType):
            copied_data = itertools.tee(kwargs.pop('data'), len(self._cluster))

        if kwargs.get('timeout') is None:
            kwargs['timeout'] = self.default_timeout_sec

        for node_index, node in list(enumerate(self._profile.cluster)):
            self._use_node(node)
            if copied_data is not None:
                kwargs['data'] = copied_data[node_index]

            try:
                return super(ClusterHTTPClient,
                             self).do_request(*args, **kwargs)
            except (NotClusterMaster, requests.exceptions.ConnectionError):
                continue

        raise CloudifyClientError('No active node in the cluster!')
    def test_post_execute_client_error(self):
        cfy_mock_client = MockCloudifyRestClient()
        cfy_mock_client.deployments.outputs.get = mock.MagicMock(
            side_effect=CloudifyClientError('Mistake'))

        poll_with_timeout_test = \
            'cloudify_types.component.component.Component.' \
            'verify_execution_successful'

        with mock.patch('cloudify_types.component.component.CloudifyClient'
                        ) as mock_local_client:
            mock_local_client.return_value = cfy_mock_client

            with mock.patch(poll_with_timeout_test) as poll:
                poll.return_value = False
                self.assertRaises(NonRecoverableError,
                                  execute_start,
                                  operation='execute_workflow',
                                  deployment_id='dep_name',
                                  workflow_id='install',
                                  client={'host': 'localhost'},
                                  timeout=MOCK_TIMEOUT)
 def test_delete_secret_Error(self):
     mock_client = mock.MagicMock(side_effect=CloudifyClientError("e"))
     with mock.patch('cloudify.manager.get_rest_client', mock_client):
         self.assertRaises(NonRecoverableError, _delete_secret, 'k')
Beispiel #25
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 testtools

from cloudify.state import current_ctx
from cloudify.mocks import MockCloudifyContext

from cloudify_rest_client.exceptions import CloudifyClientError

REST_CLIENT_EXCEPTION = \
    mock.MagicMock(side_effect=CloudifyClientError('Mistake'))

COMPONENT_PROPS = {
    'resource_config': {
        'blueprint': {
            'id': '',
            'blueprint_archive': 'URL',
            'main_file_name': 'blueprint.yaml'
        },
        'deployment': {
            'id': '',
            'inputs': {},
            'outputs': {
                'output1': 'output2'
            }
        }
Beispiel #26
0
 def mock_return(*args, **kwargs):
     del args, kwargs
     raise CloudifyClientError('Mistake')
 def raise_client_error():
     raise CloudifyClientError('this is an IOError')
 def raise_client_error():
     raise CloudifyClientError('CloudifyClientError')
class TestPropertiesUpdateDefaultMergeHandler(unittest.TestCase):
    ERR_CONFLICT = CloudifyClientError('conflict', status_code=409)

    def test_merge_handler_noconflict(self):
        """The merge builtin handler adds properties that are not present.

        If a property was added locally, but isn't in the storage version,
        it can be added.
        """
        instance = NodeInstance('id', 'node_id', {'value': 1})

        def mock_update(instance):
            # we got both properties merged - the locally added one
            # and the server one
            self.assertEqual({
                'othervalue': 1,
                'value': 1
            }, instance.runtime_properties)

        ep = mock.Mock(
            **{
                'get_node_instance.return_value': instance,
                'update_node_instance.side_effect': mock_update
            })

        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['othervalue'] = 1
        ctx.update(conflict_handlers.simple_merge_handler)

        ep.update_node_instance.assert_called_once_with(instance)

    def test_merge_handler_repeated_property(self):
        """Merge handler won't overwrite already existing properties.

        First fetch returns value=1; locally change that to 2 and try to
        update. However server says that's a conflict, and now says value=5.
        Merge handler decides it can't merge and errors out.
        """
        instance = NodeInstance('id', 'node_id', {'value': 1})

        ep = mock.Mock(
            **{
                'get_node_instance.return_value': instance,
                'update_node_instance.side_effect': self.ERR_CONFLICT
            })
        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['value'] = 2

        # in the meantime, server's version changed! value is now 5
        ep.get_node_instance.return_value = NodeInstance(
            'id', 'node_id', {'value': 5})

        try:
            ctx.update(conflict_handlers.simple_merge_handler)
        except ValueError:
            pass
        else:
            self.fail('merge handler should fail to merge repeated properties')

        self.assertEqual(1, len(ep.update_node_instance.mock_calls))

    def test_merge_handler_conflict_resolved(self):
        """Merge handler can resolve conflicts, adding new properties.

        First fetch returns instance without the 'value' property.
        Handler adds the locally-added 'othervalue' and tries updating.
        That's a conflict, because now the server version has the 'value'
        property. Handler refetches, and is able to merge.
        """

        instances = [
            NodeInstance('id', 'node_id'),
            NodeInstance('id', 'node_id', {'value': 1})
        ]

        def mock_update(instance):
            if 'value' not in instance.runtime_properties:
                raise self.ERR_CONFLICT
            self.assertEqual({
                'othervalue': 1,
                'value': 1
            }, instance.runtime_properties)

        ep = mock.Mock(
            **{
                'get_node_instance.side_effect': instances,
                'update_node_instance.side_effect': mock_update
            })

        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['othervalue'] = 1
        # at this point we don't know about the 'value' property yet
        ctx.update(conflict_handlers.simple_merge_handler)

        self.assertEqual(2, len(ep.update_node_instance.mock_calls))
class TestPropertiesUpdate(testtools.TestCase):
    ERR_CONFLICT = CloudifyClientError('conflict', status_code=409)

    def test_update(self):
        """.update() without a handler sends the changed runtime properties."""
        def mock_update(instance):
            self.assertEqual({'foo': 42}, instance.runtime_properties)

        instance = NodeInstance('id', 'node_id')
        ep = mock.Mock(
            **{
                'get_node_instance.return_value': instance,
                'update_node_instance.side_effect': mock_update
            })
        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['foo'] = 42
        ctx.update()

        ep.update_node_instance.assert_called_once_with(instance)

    def test_update_conflict_no_handler(self):
        """Version conflict without a handler function aborts the operation."""
        instance = NodeInstance('id', 'node_id')

        ep = mock.Mock(
            **{
                'get_node_instance.return_value': instance,
                'update_node_instance.side_effect': self.ERR_CONFLICT
            })

        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['foo'] = 42

        try:
            ctx.update()
        except CloudifyClientError as e:
            self.assertEqual(409, e.status_code)
        else:
            self.fail('ctx.update() has hidden the 409 error')

    def test_update_conflict_simple_handler(self):
        """On a conflict, the handler will be called until it succeeds.

        The simple handler function in this test will just increase the
        runtime property value by 1 each call. When the value reaches 5,
        the mock update method will at last allow it to save.
        """
        # each next call of the mock .get_node_instance will return subsequent
        # instances: each time the runtime property is changed
        instances = [
            NodeInstance('id', 'node_id', {'value': i}) for i in range(5)
        ]

        def mock_update(instance):
            if instance.runtime_properties.get('value', 0) < 5:
                raise self.ERR_CONFLICT

        ep = mock.Mock(
            **{
                'get_node_instance.side_effect': instances,
                'update_node_instance.side_effect': mock_update
            })

        ctx = _context_with_endpoint(ep)
        ctx.runtime_properties['value'] = 1

        def _handler(previous, next_props):
            # the "previous" argument is always the props as they were before
            # .update() was called
            self.assertEqual(previous, {'value': 1})
            return {'value': next_props['value'] + 1}

        handler = mock.Mock(side_effect=_handler)  # Mock() for recording calls
        ctx.update(handler)

        self.assertEqual(5, len(handler.mock_calls))
        self.assertEqual(5, len(ep.update_node_instance.mock_calls))