def test_put_blueprint_archive_from_url(self):
        port = 53230
        blueprint_id = 'new_blueprint_id'

        archive_path = self.archive_mock_blueprint(
            archive_func=archiving.make_tarbz2file)
        archive_filename = os.path.basename(archive_path)
        archive_dir = os.path.dirname(archive_path)

        archive_url = 'http://localhost:{0}/{1}'.format(
            port, archive_filename)

        fs = FileServer(archive_dir, False, port)
        fs.start()
        try:
            self.wait_for_url(archive_url)

            blueprint_id = self.client.blueprints.publish_archive(
                archive_url,
                blueprint_id).id
            # verifying blueprint exists
            result = self.client.blueprints.get(blueprint_id)
            self.assertEqual(blueprint_id, result.id)
        finally:
            fs.stop()
    def test_put_blueprint_archive_from_url(self):
        port = 53230
        blueprint_id = 'new_blueprint_id'

        archive_path = self.archive_mock_blueprint(
            archive_func=archiving.make_tarbz2file)
        archive_filename = os.path.basename(archive_path)
        archive_dir = os.path.dirname(archive_path)

        archive_url = 'http://localhost:{0}/{1}'.format(
            port, archive_filename)

        fs = FileServer(archive_dir, False, port)
        fs.start()
        try:
            self.wait_for_url(archive_url)

            blueprint_id = self.client.blueprints.publish_archive(
                archive_url,
                blueprint_id).id
            # verifying blueprint exists
            result = self.client.blueprints.get(blueprint_id)
            self.assertEqual(blueprint_id, result.id)
        finally:
            fs.stop()
Beispiel #3
0
 def test_publish_archive_blueprint_main_file_name(self):
     port = 53230
     blueprint_id = 'publish_archive_blueprint_main_file_name'
     main_file_name = 'blueprint_with_workflows.yaml'
     archive_path = self.archive_mock_blueprint()
     archive_filename = os.path.basename(archive_path)
     archive_dir = os.path.dirname(archive_path)
     fs = FileServer(archive_dir, False, port)
     fs.start()
     try:
         archive_url = 'http://localhost:{0}/{1}'.format(
             port, archive_filename)
         self.wait_for_url(archive_url)
         response = self.client.blueprints.publish_archive(
             archive_url, blueprint_id, main_file_name)
     finally:
         fs.stop()
     self.assertEqual(blueprint_id, response.id)
     self.assertEqual(main_file_name, response.main_file_name)
 def test_publish_archive_blueprint_main_file_name(self):
     port = 53230
     blueprint_id = 'publish_archive_blueprint_main_file_name'
     main_file_name = 'blueprint_with_workflows.yaml'
     archive_path = self.archive_mock_blueprint()
     archive_filename = os.path.basename(archive_path)
     archive_dir = os.path.dirname(archive_path)
     fs = FileServer(archive_dir, False, port)
     fs.start()
     try:
         archive_url = 'http://localhost:{0}/{1}'.format(
             port, archive_filename)
         self.wait_for_url(archive_url)
         response = self.client.blueprints.publish_archive(archive_url,
                                                           blueprint_id,
                                                           main_file_name)
     finally:
         fs.stop()
     self.assertEqual(blueprint_id, response.id)
     self.assertEqual(main_file_name, response.main_file_name)
Beispiel #5
0
class BaseServerTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(BaseServerTestCase, self).__init__(*args, **kwargs)

    def create_client_with_tenant(self,
                                  username,
                                  password,
                                  tenant=DEFAULT_TENANT_NAME):
        headers = utils.create_auth_header(username=username,
                                           password=password)

        headers[CLOUDIFY_TENANT_HEADER] = tenant
        return self.create_client(headers=headers)

    def create_client(self, headers=None):
        client = CloudifyClient(host='localhost', headers=headers)
        mock_http_client = MockHTTPClient(self.app,
                                          headers=headers,
                                          file_server=self.file_server)
        client._client = mock_http_client
        client.blueprints.api = mock_http_client
        client.deployments.api = mock_http_client
        client.deployments.outputs.api = mock_http_client
        client.deployment_modifications.api = mock_http_client
        client.executions.api = mock_http_client
        client.nodes.api = mock_http_client
        client.node_instances.api = mock_http_client
        client.manager.api = mock_http_client
        client.evaluate.api = mock_http_client
        client.tokens.api = mock_http_client
        client.events.api = mock_http_client
        # only exists in v2 and above
        if CLIENT_API_VERSION != 'v1':
            client.plugins.api = mock_http_client
            client.snapshots.api = mock_http_client

            # only exists in v2.1 and above
            if CLIENT_API_VERSION != 'v2':
                client.maintenance_mode.api = mock_http_client
                client.deployment_updates.api = mock_http_client

                # only exists in v3 and above
                if CLIENT_API_VERSION != 'v2.1':
                    client.tenants.api = mock_http_client
                    client.user_groups.api = mock_http_client
                    client.users.api = mock_http_client
                    client.ldap.api = mock_http_client
                    client.secrets.api = mock_http_client

        return client

    def setUp(self):
        self._create_temp_files_and_folders()
        self._init_file_server()
        self._mock_amqp_manager()

        server_module = self._set_config_path_and_get_server_module()
        self._create_config_and_reset_app(server_module)
        self._handle_flask_app_and_db(server_module)
        self.client = self.create_client()
        self.sm = get_storage_manager()
        self.initialize_provider_context()

    def _mock_amqp_manager(self):
        """ Mock the pyrabbit.Client for all unittests - no RabbitMQ """

        self._amqp_patcher = patch('manager_rest.amqp_manager.Client')
        self.addCleanup(self._amqp_patcher.stop)
        self._amqp_patcher.start()

    def _create_temp_files_and_folders(self):
        self.tmpdir = tempfile.mkdtemp(prefix='fileserver-')
        fd, self.rest_service_log = tempfile.mkstemp(prefix='rest-log-')
        os.close(fd)
        self.maintenance_mode_dir = tempfile.mkdtemp(prefix='maintenance-')
        fd, self.tmp_conf_file = tempfile.mkstemp(prefix='conf-file-')
        os.close(fd)

    def _init_file_server(self):
        self.file_server = FileServer(self.tmpdir)
        self.file_server.start()
        self.addCleanup(self.cleanup)

    def _set_config_path_and_get_server_module(self):
        """Workaround for setting the rest service log path, since it's
        needed when 'server' module is imported.
        right after the import the log path is set normally like the rest
        of the variables (used in the reset_state)
        """

        with open(self.tmp_conf_file, 'w') as f:
            json.dump(
                {
                    'rest_service_log_path': self.rest_service_log,
                    'rest_service_log_file_size_MB': 1,
                    'rest_service_log_files_backup_count': 1,
                    'rest_service_log_level': 'DEBUG'
                }, f)
        os.environ['MANAGER_REST_CONFIG_PATH'] = self.tmp_conf_file
        try:
            from manager_rest import server
        finally:
            del (os.environ['MANAGER_REST_CONFIG_PATH'])
        return server

    def _create_config_and_reset_app(self, server):
        """Create config, and reset Flask app
        :type server: module
        """
        self.server_configuration = self.create_configuration()
        utils.copy_resources(self.server_configuration.file_server_root)
        server.SQL_DIALECT = 'sqlite'
        server.reset_app(self.server_configuration)

    def _handle_flask_app_and_db(self, server):
        """Set up Flask app context, and handle DB related tasks
        :type server: module
        """
        self._set_flask_app_context(server.app)
        self.app = self._get_app(server.app)
        self._handle_default_db_config(server)
        self._setup_anonymous_user(server.app, server.user_datastore)

    def _set_flask_app_context(self, flask_app):
        flask_app_context = flask_app.test_request_context()
        flask_app_context.push()
        self.addCleanup(flask_app_context.pop)

    @staticmethod
    def _handle_default_db_config(server):
        server.db.create_all()
        admin_user = get_admin_user()

        # We're mocking the AMQPManager, as we aren't really using Rabbit here
        default_tenant = create_default_user_tenant_and_roles(
            admin_username=admin_user['username'],
            admin_password=admin_user['password'],
            amqp_manager=MagicMock())
        server.app.config[constants.CURRENT_TENANT_CONFIG] = default_tenant

    @staticmethod
    def _get_app(flask_app):
        """Create a flask.testing FlaskClient

        :param flask_app: Flask app
        :return: Our modified version of Flask's test client
        """
        flask_app.test_client_class = TestClient
        return flask_app.test_client()

    @staticmethod
    def _setup_anonymous_user(flask_app, user_datastore):
        """Change the anonymous user to be admin, in order to have arbitrary
        access to the storage manager (which otherwise requires a valid user)

        :param flask_app: Flask app
        """
        admin_user = user_datastore.get_user(get_admin_user()['username'])
        login_manager = flask_app.extensions['security'].login_manager
        login_manager.anonymous_user = MagicMock(return_value=admin_user)

    def cleanup(self):
        self.quiet_delete(self.rest_service_log)
        self.quiet_delete(self.tmp_conf_file)
        self.quiet_delete_directory(self.maintenance_mode_dir)
        if self.file_server:
            self.file_server.stop()
        self.quiet_delete_directory(self.tmpdir)

    def initialize_provider_context(self):
        provider_context = models.ProviderContext(
            id=constants.PROVIDER_CONTEXT_ID,
            name=self.id(),
            context={'cloudify': {}})
        self.sm.put(provider_context)

    def create_configuration(self):
        test_config = config.Config()
        test_config.test_mode = True
        test_config.postgresql_db_name = ':memory:'
        test_config.postgresql_host = ''
        test_config.postgresql_username = ''
        test_config.postgresql_password = ''
        test_config.file_server_root = self.tmpdir
        test_config.file_server_url = 'http://localhost:{0}'.format(
            self.file_server.port)

        test_config.rest_service_log_level = 'DEBUG'
        test_config.rest_service_log_path = self.rest_service_log
        test_config.rest_service_log_file_size_MB = 100,
        test_config.rest_service_log_files_backup_count = 20
        test_config.maintenance_folder = self.maintenance_mode_dir
        test_config.security_hash_salt = 'hash_salt'
        test_config.security_secret_key = 'secret_key'
        test_config.security_encoding_alphabet = \
            'L7SMZ4XebsuIK8F6aVUBYGQtW0P12Rn'
        test_config.security_encoding_block_size = 24
        test_config.security_encoding_min_length = 5
        return test_config

    def _version_url(self, url):
        # method for versionifying URLs for requests which don't go through
        # the REST client; the version is taken from the REST client regardless
        if not url.startswith('/api/'):
            url = '/api/{0}{1}'.format(CLIENT_API_VERSION, url)

        return url

    def post(self, resource_path, data, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.post(urllib.quote(url),
                               content_type='application/json',
                               data=json.dumps(data),
                               query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def post_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.post(
                urllib.quote(url),
                data=f.read(),
                query_string=build_query_string(query_params))
            result.json = json.loads(result.data)
            return result

    def put_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.put(
                urllib.quote(url),
                data=f.read(),
                query_string=build_query_string(query_params))
            result.json = json.loads(result.data)
            return result

    def put(self, resource_path, data=None, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.put(urllib.quote(url),
                              content_type='application/json',
                              data=json.dumps(data) if data else None,
                              query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def patch(self, resource_path, data):
        url = self._version_url(resource_path)
        result = self.app.patch(urllib.quote(url),
                                content_type='application/json',
                                data=json.dumps(data))
        result.json = json.loads(result.data)
        return result

    def get(self, resource_path, query_params=None, headers=None):
        url = self._version_url(resource_path)
        result = self.app.get(urllib.quote(url),
                              headers=headers,
                              query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def head(self, resource_path):
        url = self._version_url(resource_path)
        result = self.app.head(urllib.quote(url))
        return result

    def delete(self, resource_path, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.delete(urllib.quote(url),
                                 query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def _check_if_resource_on_fileserver(self, folder, container_id,
                                         resource_path):
        url = 'http://localhost:{0}/{1}/{2}/{3}'.format(
            FILE_SERVER_PORT, folder, container_id, resource_path)
        try:
            urllib2.urlopen(url)
            return True
        except urllib2.HTTPError:
            return False

    def check_if_resource_on_fileserver(self, blueprint_id, resource_path):
        return self._check_if_resource_on_fileserver(
            os.path.join(FILE_SERVER_BLUEPRINTS_FOLDER, DEFAULT_TENANT_NAME),
            blueprint_id, resource_path)

    def get_blueprint_path(self, blueprint_dir_name):
        return os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            blueprint_dir_name)

    def archive_mock_blueprint(self,
                               archive_func=archiving.make_targzfile,
                               blueprint_dir='mock_blueprint'):
        archive_path = tempfile.mkstemp()[1]
        source_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                  blueprint_dir)
        archive_func(archive_path, source_dir)
        return archive_path

    def get_mock_blueprint_path(self):
        return os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            'mock_blueprint', 'blueprint.yaml')

    def put_blueprint_args(self,
                           blueprint_file_name=None,
                           blueprint_id='blueprint',
                           archive_func=archiving.make_targzfile,
                           blueprint_dir='mock_blueprint'):

        resource_path = self._version_url('/blueprints/{1}'.format(
            CLIENT_API_VERSION, blueprint_id))

        result = [
            resource_path,
            self.archive_mock_blueprint(archive_func, blueprint_dir),
        ]

        if blueprint_file_name:
            data = {'application_file_name': blueprint_file_name}
        else:
            data = {}

        result.append(data)
        return result

    def put_deployment(self,
                       deployment_id='deployment',
                       blueprint_file_name=None,
                       blueprint_id='blueprint',
                       inputs=None,
                       blueprint_dir='mock_blueprint',
                       skip_plugins_validation=None):
        blueprint_response = self.put_blueprint(blueprint_dir,
                                                blueprint_file_name,
                                                blueprint_id)
        blueprint_id = blueprint_response['id']
        create_deployment_kwargs = {'inputs': inputs}
        if skip_plugins_validation is not None:
            create_deployment_kwargs['skip_plugins_validation'] =\
                skip_plugins_validation
        deployment = self.client.deployments.create(blueprint_id,
                                                    deployment_id,
                                                    **create_deployment_kwargs)
        return blueprint_id, deployment.id, blueprint_response, deployment

    def put_blueprint(self, blueprint_dir, blueprint_file_name, blueprint_id):
        blueprint_response = self.put_file(*self.put_blueprint_args(
            blueprint_file_name, blueprint_id,
            blueprint_dir=blueprint_dir)).json
        if 'error_code' in blueprint_response:
            raise RuntimeError('{}: {}'.format(
                blueprint_response['error_code'],
                blueprint_response['message']))
        return blueprint_response

    def upload_plugin(self, package_name, package_version):
        temp_file_path = self.create_wheel(package_name, package_version)
        response = self.post_file('/plugins', temp_file_path)
        os.remove(temp_file_path)
        return response

    def create_wheel(self, package_name, package_version):
        module_src = '{0}=={1}'.format(package_name, package_version)
        wagon_client = Wagon(module_src)
        return wagon_client.create(
            archive_destination_dir=tempfile.gettempdir(), force=True)

    def wait_for_url(self, url, timeout=5):
        end = time.time() + timeout

        while end >= time.time():
            try:
                status = urllib.urlopen(url).getcode()
                if status == 200:
                    return
            except IOError:
                time.sleep(1)

        raise RuntimeError('Url {0} is not available (waited {1} '
                           'seconds)'.format(url, timeout))

    @staticmethod
    def quiet_delete(file_path):
        try:
            os.remove(file_path)
        except:
            pass

    @staticmethod
    def quiet_delete_directory(file_path):
        shutil.rmtree(file_path, ignore_errors=True)

    def wait_for_deployment_creation(self, client, deployment_id):
        env_creation_execution = None
        deployment_executions = client.executions.list(deployment_id)
        for execution in deployment_executions:
            if execution.workflow_id == 'create_deployment_environment':
                env_creation_execution = execution
                break
        if env_creation_execution:
            self.wait_for_execution(client, env_creation_execution)

    @staticmethod
    def wait_for_execution(client, execution, timeout=900):
        # Poll for execution status until execution ends
        deadline = time.time() + timeout
        while True:
            if time.time() > deadline:
                raise Exception(
                    'execution of operation {0} for deployment {1} timed out'.
                    format(execution.workflow_id, execution.deployment_id))

            execution = client.executions.get(execution.id)
            if execution.status in ExecutionState.END_STATES:
                break
            time.sleep(3)

    def _add_blueprint(self, blueprint_id=None):
        if not blueprint_id:
            unique_str = str(uuid.uuid4())
            blueprint_id = 'blueprint-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        blueprint = models.Blueprint(id=blueprint_id,
                                     created_at=now,
                                     updated_at=now,
                                     description=None,
                                     plan={'name': 'my-bp'},
                                     main_file_name='aaa')
        return self.sm.put(blueprint)

    def _add_deployment(self, blueprint, deployment_id=None):
        if not deployment_id:
            unique_str = str(uuid.uuid4())
            deployment_id = 'deployment-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment = models.Deployment(id=deployment_id,
                                       created_at=now,
                                       updated_at=now,
                                       permalink=None,
                                       description=None,
                                       workflows={},
                                       inputs={},
                                       policy_types={},
                                       policy_triggers={},
                                       groups={},
                                       scaling_groups={},
                                       outputs={})
        deployment.blueprint = blueprint
        return self.sm.put(deployment)

    def _add_execution_with_id(self, execution_id):
        blueprint = self._add_blueprint()
        deployment = self._add_deployment(blueprint.id)
        return self._add_execution(deployment.id, execution_id)

    def _add_execution(self, deployment, execution_id=None):
        if not execution_id:
            unique_str = str(uuid.uuid4())
            execution_id = 'execution-{0}'.format(unique_str)
        execution = models.Execution(
            id=execution_id,
            status=ExecutionState.TERMINATED,
            workflow_id='',
            created_at=utils.get_formatted_timestamp(),
            error='',
            parameters=dict(),
            is_system_workflow=False)
        execution.deployment = deployment
        return self.sm.put(execution)

    def _add_deployment_update(self,
                               deployment,
                               execution,
                               deployment_update_id=None):
        if not deployment_update_id:
            unique_str = str(uuid.uuid4())
            deployment_update_id = 'deployment_update-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment_update = models.DeploymentUpdate(
            deployment_plan={'name': 'my-bp'},
            state='staged',
            id=deployment_update_id,
            deployment_update_nodes=None,
            deployment_update_node_instances=None,
            deployment_update_deployment=None,
            modified_entity_ids=None,
            created_at=now)
        deployment_update.deployment = deployment
        if execution:
            deployment_update.execution = execution
        return self.sm.put(deployment_update)

    def _test_invalid_input(self, func, argument, *args):
        self.assertRaisesRegexp(
            CloudifyClientError,
            'The `{0}` argument contains illegal characters'.format(argument),
            func, *args)
class BaseServerTestCase(unittest.TestCase):
    def create_client_with_tenant(self,
                                  username,
                                  password,
                                  tenant=DEFAULT_TENANT_NAME):
        headers = utils.create_auth_header(username=username,
                                           password=password)

        headers[CLOUDIFY_TENANT_HEADER] = tenant
        return self.create_client(headers=headers)

    def create_client(self, headers=None):
        client = CloudifyClient(host='localhost',
                                headers=headers)
        mock_http_client = MockHTTPClient(self.app,
                                          headers=headers,
                                          file_server=self.file_server)
        client._client = mock_http_client
        client.blueprints.api = mock_http_client
        client.deployments.api = mock_http_client
        client.deployments.outputs.api = mock_http_client
        client.deployment_modifications.api = mock_http_client
        client.executions.api = mock_http_client
        client.nodes.api = mock_http_client
        client.node_instances.api = mock_http_client
        client.manager.api = mock_http_client
        client.evaluate.api = mock_http_client
        client.tokens.api = mock_http_client
        client.events.api = mock_http_client
        # only exists in v2 and above
        if CLIENT_API_VERSION != 'v1':
            client.plugins.api = mock_http_client
            client.snapshots.api = mock_http_client

            # only exists in v2.1 and above
            if CLIENT_API_VERSION != 'v2':
                client.maintenance_mode.api = mock_http_client
                client.deployment_updates.api = mock_http_client

                # only exists in v3 and above
                if CLIENT_API_VERSION != 'v2.1':
                    client.tenants.api = mock_http_client
                    client.user_groups.api = mock_http_client
                    client.users.api = mock_http_client
                    client.ldap.api = mock_http_client
                    client.secrets.api = mock_http_client

                    # only exists in v3.1 and above
                    if CLIENT_API_VERSION != 'v3':
                        client.deployments.capabilities.api = mock_http_client
                        client.agents.api = mock_http_client

        return client

    def setUp(self):
        self._create_temp_files_and_folders()
        self._init_file_server()
        self._mock_amqp_modules()

        server_module = self._set_config_path_and_get_server_module()
        self._create_config_and_reset_app(server_module)
        self._mock_get_encryption_key()
        self._handle_flask_app_and_db(server_module)
        self.client = self.create_client()
        self.sm = get_storage_manager()
        self.initialize_provider_context()
        self._mock_verify_role()

    def _mock_verify_role(self):
        self._original_verify_role = rest_utils.verify_role
        self.addCleanup(self._restore_verify_role)
        rest_utils.verify_role = MagicMock()

    def _restore_verify_role(self):
        rest_utils.verify_role = self._original_verify_role

    def _mock_amqp_modules(self):
        """
        Mock RabbitMQ related modules - AMQP manager and workflow executor -
        that use pika, because we don't have RabbitMQ in the unittests
        """
        amqp_patches = [
            patch('manager_rest.amqp_manager.RabbitMQClient'),
            patch('manager_rest.workflow_executor._execute_task',
                  mock_execute_task),
        ]
        for amqp_patch in amqp_patches:
            self.addCleanup(amqp_patch.stop)
            amqp_patch.start()

    def _mock_get_encryption_key(self):
        """ Mock the _get_encryption_key_patcher function for all unittests """
        self._get_encryption_key_patcher = patch(
            'cloudify.cryptography_utils._get_encryption_key'
        )
        self.addCleanup(self._get_encryption_key_patcher.stop)
        self._get_encryption_key = self._get_encryption_key_patcher.start()
        self._get_encryption_key.return_value = \
            config.instance.security_encryption_key

    def _create_temp_files_and_folders(self):
        self.tmpdir = tempfile.mkdtemp(prefix='fileserver-')
        fd, self.rest_service_log = tempfile.mkstemp(prefix='rest-log-')
        os.close(fd)
        self.maintenance_mode_dir = tempfile.mkdtemp(prefix='maintenance-')
        fd, self.tmp_conf_file = tempfile.mkstemp(prefix='conf-file-')
        os.close(fd)

    def _init_file_server(self):
        self.file_server = FileServer(self.tmpdir)
        self.file_server.start()
        self.addCleanup(self.cleanup)

    def _set_config_path_and_get_server_module(self):
        """Workaround for setting the rest service log path, since it's
        needed when 'server' module is imported.
        right after the import the log path is set normally like the rest
        of the variables (used in the reset_state)
        """

        with open(self.tmp_conf_file, 'w') as f:
            json.dump({'rest_service_log_path': self.rest_service_log,
                       'rest_service_log_file_size_MB': 1,
                       'rest_service_log_files_backup_count': 1,
                       'rest_service_log_level': 'DEBUG'},
                      f)
        os.environ['MANAGER_REST_CONFIG_PATH'] = self.tmp_conf_file
        try:
            from manager_rest import server
        finally:
            del(os.environ['MANAGER_REST_CONFIG_PATH'])
        return server

    def _create_config_and_reset_app(self, server):
        """Create config, and reset Flask app
        :type server: module
        """
        self.server_configuration = self.create_configuration()
        utils.copy_resources(self.server_configuration.file_server_root)
        server.SQL_DIALECT = 'sqlite'
        server.reset_app(self.server_configuration)

    def _handle_flask_app_and_db(self, server):
        """Set up Flask app context, and handle DB related tasks
        :type server: module
        """
        self._set_flask_app_context(server.app)
        self.app = self._get_app(server.app)
        self._handle_default_db_config(server)
        self._setup_anonymous_user(server.app, server.user_datastore)

    def _set_flask_app_context(self, flask_app):
        flask_app_context = flask_app.test_request_context()
        flask_app_context.push()
        self.addCleanup(flask_app_context.pop)

    @staticmethod
    def _handle_default_db_config(server):
        server.db.create_all()
        admin_user = get_admin_user()

        fd, temp_auth_file = tempfile.mkstemp()
        os.close(fd)
        with open(temp_auth_file, 'w') as f:
            yaml.dump(auth_dict, f)

        try:
            # We're mocking the AMQPManager, we aren't really using Rabbit here
            default_tenant = create_default_user_tenant_and_roles(
                admin_username=admin_user['username'],
                admin_password=admin_user['password'],
                amqp_manager=MagicMock(),
                authorization_file_path=temp_auth_file
            )
            default_tenant.rabbitmq_password = encrypt(
                AMQPManager._generate_user_password()
            )
        finally:
            os.remove(temp_auth_file)

        utils.set_current_tenant(default_tenant)

    @staticmethod
    def _get_app(flask_app):
        """Create a flask.testing FlaskClient

        :param flask_app: Flask app
        :return: Our modified version of Flask's test client
        """
        flask_app.test_client_class = TestClient
        return flask_app.test_client()

    @staticmethod
    def _setup_anonymous_user(flask_app, user_datastore):
        """Change the anonymous user to be admin, in order to have arbitrary
        access to the storage manager (which otherwise requires a valid user)

        :param flask_app: Flask app
        """
        admin_user = user_datastore.get_user(get_admin_user()['username'])
        login_manager = flask_app.extensions['security'].login_manager
        login_manager.anonymous_user = MagicMock(return_value=admin_user)

    def cleanup(self):
        self.quiet_delete(self.rest_service_log)
        self.quiet_delete(self.tmp_conf_file)
        self.quiet_delete_directory(self.maintenance_mode_dir)
        if self.file_server:
            self.file_server.stop()
        self.quiet_delete_directory(self.tmpdir)

    def initialize_provider_context(self):
        provider_context = models.ProviderContext(
            id=constants.PROVIDER_CONTEXT_ID,
            name=self.id(),
            context={'cloudify': {}}
        )
        self.sm.put(provider_context)

    def create_configuration(self):
        test_config = config.Config()
        test_config.test_mode = True
        test_config.postgresql_db_name = ':memory:'
        test_config.postgresql_host = ''
        test_config.postgresql_username = ''
        test_config.postgresql_password = ''
        test_config.file_server_root = self.tmpdir
        test_config.file_server_url = 'http://localhost:{0}'.format(
            self.file_server.port)

        test_config.rest_service_log_level = 'DEBUG'
        test_config.rest_service_log_path = self.rest_service_log
        test_config.rest_service_log_file_size_MB = 100,
        test_config.rest_service_log_files_backup_count = 7
        test_config.maintenance_folder = self.maintenance_mode_dir
        test_config.security_hash_salt = 'hash_salt'
        test_config.security_secret_key = 'secret_key'
        test_config.security_encoding_alphabet = \
            'L7SMZ4XebsuIK8F6aVUBYGQtW0P12Rn'
        test_config.security_encoding_block_size = 24
        test_config.security_encoding_min_length = 5
        test_config.authorization_permissions = auth_dict['permissions']
        test_config.security_encryption_key = (
            'lF88UP5SJKluylJIkPDYrw5UMKOgv9w8TikS0Ds8m2UmM'
            'SzFe0qMRa0EcTgHst6LjmF_tZbq_gi_VArepMsrmw=='
        )
        return test_config

    def _version_url(self, url):
        # method for versionifying URLs for requests which don't go through
        # the REST client; the version is taken from the REST client regardless
        if not url.startswith('/api/'):
            url = '/api/{0}{1}'.format(CLIENT_API_VERSION, url)

        return url

    # this method is completely copied from the cli. once caravan sits in a
    # more general package, it should be removed.
    @staticmethod
    def _create_caravan(mappings, dest, name=None):
        tempdir = tempfile.mkdtemp()
        metadata = {}

        for wgn_path, yaml_path in mappings.iteritems():
            plugin_root_dir = os.path.basename(wgn_path).split('.', 1)[0]
            os.mkdir(os.path.join(tempdir, plugin_root_dir))

            dest_wgn_path = os.path.join(plugin_root_dir,
                                         os.path.basename(wgn_path))
            dest_yaml_path = os.path.join(plugin_root_dir,
                                          os.path.basename(yaml_path))

            shutil.copy(wgn_path, os.path.join(tempdir, dest_wgn_path))
            shutil.copy(yaml_path, os.path.join(tempdir, dest_yaml_path))
            metadata[dest_wgn_path] = dest_yaml_path

        with open(os.path.join(tempdir, 'METADATA'), 'w+') as f:
            yaml.dump(metadata, f)

        tar_name = name or 'palace'
        tar_path = os.path.join(dest, '{0}.cvn'.format(tar_name))
        tarfile_ = tarfile.open(tar_path, 'w:gz')
        try:
            tarfile_.add(tempdir, arcname=tar_name)
        finally:
            tarfile_.close()
        return tar_path

    def post(self, resource_path, data, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.post(urllib.quote(url),
                               content_type='application/json',
                               data=json.dumps(data),
                               query_string=build_query_string(query_params))
        return result

    def post_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.post(urllib.quote(url),
                                   data=f.read(),
                                   query_string=build_query_string(
                                       query_params))
            return result

    def put_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.put(urllib.quote(url),
                                  data=f.read(),
                                  query_string=build_query_string(
                                      query_params))
            return result

    def put(self, resource_path, data=None, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.put(urllib.quote(url),
                              content_type='application/json',
                              data=json.dumps(data) if data else None,
                              query_string=build_query_string(query_params))
        return result

    def patch(self, resource_path, data):
        url = self._version_url(resource_path)
        result = self.app.patch(urllib.quote(url),
                                content_type='application/json',
                                data=json.dumps(data))
        return result

    def get(self, resource_path, query_params=None, headers=None):
        url = self._version_url(resource_path)
        result = self.app.get(urllib.quote(url),
                              headers=headers,
                              query_string=build_query_string(query_params))
        return result

    def head(self, resource_path):
        url = self._version_url(resource_path)
        result = self.app.head(urllib.quote(url))
        return result

    def delete(self, resource_path, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.delete(urllib.quote(url),
                                 query_string=build_query_string(query_params))
        return result

    def _check_if_resource_on_fileserver(self,
                                         folder,
                                         container_id,
                                         resource_path):
        url = 'http://localhost:{0}/{1}/{2}/{3}'.format(
            FILE_SERVER_PORT, folder, container_id, resource_path)
        try:
            urllib2.urlopen(url)
            return True
        except urllib2.HTTPError:
            return False

    def check_if_resource_on_fileserver(self, blueprint_id, resource_path):
        return self._check_if_resource_on_fileserver(
            os.path.join(FILE_SERVER_BLUEPRINTS_FOLDER, DEFAULT_TENANT_NAME),
            blueprint_id, resource_path)

    def get_blueprint_path(self, blueprint_dir_name):
        return os.path.join(os.path.dirname(
            os.path.abspath(__file__)), blueprint_dir_name)

    def get_full_path(self, relative_file_path):
        return os.path.join(os.path.dirname(
            os.path.abspath(__file__)), relative_file_path)

    def upload_blueprint(self,
                         client,
                         visibility=VisibilityState.TENANT,
                         blueprint_id='bp_1'):
        bp_path = self.get_blueprint_path('mock_blueprint/blueprint.yaml')
        client.blueprints.upload(path=bp_path,
                                 entity_id=blueprint_id,
                                 visibility=visibility)
        return blueprint_id

    def archive_mock_blueprint(self, archive_func=archiving.make_targzfile,
                               blueprint_dir='mock_blueprint'):
        archive_path = tempfile.mkstemp()[1]
        source_dir = os.path.join(os.path.dirname(
            os.path.abspath(__file__)), blueprint_dir)
        archive_func(archive_path, source_dir)
        return archive_path

    def get_mock_blueprint_path(self):
        return os.path.join(os.path.dirname(
            os.path.abspath(__file__)), 'mock_blueprint', 'blueprint.yaml')

    def put_blueprint_args(self, blueprint_file_name=None,
                           blueprint_id='blueprint',
                           archive_func=archiving.make_targzfile,
                           blueprint_dir='mock_blueprint'):

        resource_path = self._version_url(
            '/blueprints/{1}'.format(CLIENT_API_VERSION, blueprint_id))

        result = [
            resource_path,
            self.archive_mock_blueprint(archive_func, blueprint_dir),
        ]

        if blueprint_file_name:
            data = {'application_file_name': blueprint_file_name}
        else:
            data = {}

        result.append(data)
        return result

    def put_deployment(self,
                       deployment_id='deployment',
                       blueprint_file_name=None,
                       blueprint_id='blueprint',
                       inputs=None,
                       blueprint_dir='mock_blueprint',
                       skip_plugins_validation=None):
        blueprint_response = self.put_blueprint(blueprint_dir,
                                                blueprint_file_name,
                                                blueprint_id)
        blueprint_id = blueprint_response['id']
        create_deployment_kwargs = {'inputs': inputs}
        if skip_plugins_validation is not None:
            create_deployment_kwargs['skip_plugins_validation'] =\
                skip_plugins_validation
        deployment = self.client.deployments.create(blueprint_id,
                                                    deployment_id,
                                                    **create_deployment_kwargs)
        return blueprint_id, deployment.id, blueprint_response, deployment

    def put_blueprint(self, blueprint_dir, blueprint_file_name, blueprint_id):
        blueprint_response = self.put_file(
            *self.put_blueprint_args(blueprint_file_name,
                                     blueprint_id,
                                     blueprint_dir=blueprint_dir)).json
        if 'error_code' in blueprint_response:
            raise RuntimeError(
                '{}: {}'.format(blueprint_response['error_code'],
                                blueprint_response['message']))
        return blueprint_response

    def _create_wagon_and_yaml(self,
                               package_name,
                               package_version,
                               package_yaml_file='mock_blueprint/plugin.yaml'):
        temp_file_path = self.create_wheel(package_name, package_version)
        yaml_path = self.get_full_path(package_yaml_file)
        return temp_file_path, yaml_path

    def upload_plugin(self,
                      package_name,
                      package_version,
                      package_yaml='mock_blueprint/plugin.yaml'):
        wgn_path, yaml_path = self._create_wagon_and_yaml(package_name,
                                                          package_version,
                                                          package_yaml)
        zip_path = self.zip_files([wgn_path, yaml_path])
        response = self.post_file('/plugins', zip_path)
        os.remove(wgn_path)
        return response

    def zip_files(self, files):
        source_folder = tempfile.mkdtemp()
        destination_zip = source_folder + '.zip'
        for path in files:
            shutil.copy(path, source_folder)
        self.zip(source_folder, destination_zip, include_folder=False)
        return destination_zip

    def zip(self, source, destination, include_folder=True):
        with zipfile.ZipFile(destination, 'w') as zip_file:
            for root, _, files in os.walk(source):
                for filename in files:
                    file_path = os.path.join(root, filename)
                    source_dir = os.path.dirname(source) if include_folder \
                        else source
                    zip_file.write(
                        file_path, os.path.relpath(file_path, source_dir))
        return destination

    def create_wheel(self, package_name, package_version):
        module_src = '{0}=={1}'.format(package_name, package_version)
        return wagon.create(
            module_src,
            archive_destination_dir=tempfile.gettempdir(),
            force=True
        )

    def upload_caravan(self, packages):
        mapping = dict(
            self._create_wagon_and_yaml(
                package,
                version_and_yaml[0],
                version_and_yaml[1])
            for package, version_and_yaml in packages.items()
        )

        caravan_path = self._create_caravan(mapping, tempfile.gettempdir())
        response = self.post_file('/plugins', caravan_path)
        os.remove(caravan_path)
        return response

    def wait_for_url(self, url, timeout=5):
        end = time.time() + timeout

        while end >= time.time():
            try:
                status = urllib.urlopen(url).getcode()
                if status == 200:
                    return
            except IOError:
                time.sleep(1)

        raise RuntimeError('Url {0} is not available (waited {1} '
                           'seconds)'.format(url, timeout))

    @staticmethod
    def quiet_delete(file_path):
        try:
            os.remove(file_path)
        except Exception:
            pass

    @staticmethod
    def quiet_delete_directory(file_path):
        shutil.rmtree(file_path, ignore_errors=True)

    def wait_for_deployment_creation(self, client, deployment_id):
        env_creation_execution = None
        deployment_executions = client.executions.list(
            deployment_id=deployment_id
        )
        for execution in deployment_executions:
            if execution.workflow_id == 'create_deployment_environment':
                env_creation_execution = execution
                break
        if env_creation_execution:
            self.wait_for_execution(client, env_creation_execution)

    @staticmethod
    def wait_for_execution(client, execution, timeout=900):
        # Poll for execution status until execution ends
        deadline = time.time() + timeout
        while True:
            if time.time() > deadline:
                raise Exception(
                    'execution of operation {0} for deployment {1} timed out'.
                    format(execution.workflow_id, execution.deployment_id))

            execution = client.executions.get(execution.id)
            if execution.status in ExecutionState.END_STATES:
                break
            time.sleep(3)

    def _add_blueprint(self, blueprint_id=None):
        if not blueprint_id:
            unique_str = str(uuid.uuid4())
            blueprint_id = 'blueprint-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        blueprint = models.Blueprint(id=blueprint_id,
                                     created_at=now,
                                     updated_at=now,
                                     description=None,
                                     plan={'name': 'my-bp'},
                                     main_file_name='aaa')
        return self.sm.put(blueprint)

    def _add_deployment(self, blueprint, deployment_id=None):
        if not deployment_id:
            unique_str = str(uuid.uuid4())
            deployment_id = 'deployment-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment = models.Deployment(id=deployment_id,
                                       created_at=now,
                                       updated_at=now,
                                       permalink=None,
                                       description=None,
                                       workflows={},
                                       inputs={},
                                       policy_types={},
                                       policy_triggers={},
                                       groups={},
                                       scaling_groups={},
                                       outputs={})
        deployment.blueprint = blueprint
        return self.sm.put(deployment)

    def _add_execution_with_id(self, execution_id):
        blueprint = self._add_blueprint()
        deployment = self._add_deployment(blueprint.id)
        return self._add_execution(deployment.id, execution_id)

    def _add_execution(self, deployment, execution_id=None):
        if not execution_id:
            unique_str = str(uuid.uuid4())
            execution_id = 'execution-{0}'.format(unique_str)
        execution = models.Execution(
            id=execution_id,
            status=ExecutionState.TERMINATED,
            workflow_id='',
            created_at=utils.get_formatted_timestamp(),
            error='',
            parameters=dict(),
            is_system_workflow=False)
        execution.deployment = deployment
        return self.sm.put(execution)

    def _add_deployment_update(self, deployment, execution,
                               deployment_update_id=None):
        if not deployment_update_id:
            unique_str = str(uuid.uuid4())
            deployment_update_id = 'deployment_update-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment_update = models.DeploymentUpdate(
            deployment_plan={'name': 'my-bp'},
            state='staged',
            id=deployment_update_id,
            deployment_update_nodes=None,
            deployment_update_node_instances=None,
            deployment_update_deployment=None,
            modified_entity_ids=None,
            created_at=now)
        deployment_update.deployment = deployment
        if execution:
            deployment_update.execution = execution
        return self.sm.put(deployment_update)

    def _test_invalid_input(self, func, argument, *args):
        self.assertRaisesRegexp(
            CloudifyClientError,
            'The `{0}` argument contains illegal characters'.format(argument),
            func,
            *args
        )
Beispiel #7
0
class BaseServerTestCase(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(BaseServerTestCase, self).__init__(*args, **kwargs)

    def create_client(self, headers=None):
        client = CloudifyClient(host='localhost',
                                headers=headers)
        mock_http_client = MockHTTPClient(self.app,
                                          headers=headers,
                                          file_server=self.file_server)
        client._client = mock_http_client
        client.blueprints.api = mock_http_client
        client.deployments.api = mock_http_client
        client.deployments.outputs.api = mock_http_client
        client.deployment_modifications.api = mock_http_client
        client.executions.api = mock_http_client
        client.nodes.api = mock_http_client
        client.node_instances.api = mock_http_client
        client.manager.api = mock_http_client
        client.evaluate.api = mock_http_client
        client.tokens.api = mock_http_client
        client.events.api = mock_http_client
        # only exists in v2 and above
        if CLIENT_API_VERSION != 'v1':
            client.plugins.api = mock_http_client
            client.snapshots.api = mock_http_client

            # only exists in v2.1 and above
            if CLIENT_API_VERSION != 'v2':
                client.maintenance_mode.api = mock_http_client
                client.deployment_updates.api = mock_http_client

        return client

    def setUp(self):
        self.tmpdir = tempfile.mkdtemp(prefix='fileserver-')
        fd, self.rest_service_log = tempfile.mkstemp(prefix='rest-log-')
        os.close(fd)
        fd, self.sqlite_db_file = tempfile.mkstemp(prefix='sqlite-db-')
        os.close(fd)
        self.file_server = FileServer(self.tmpdir)
        self.maintenance_mode_dir = tempfile.mkdtemp(prefix='maintenance-')

        self.addCleanup(self.cleanup)
        self.file_server.start()

        # workaround for setting the rest service log path, since it's
        # needed when 'server' module is imported.
        # right after the import the log path is set normally like the rest
        # of the variables (used in the reset_state)
        fd, self.tmp_conf_file = tempfile.mkstemp(prefix='conf-file-')
        os.close(fd)
        with open(self.tmp_conf_file, 'w') as f:
            json.dump({'rest_service_log_path': self.rest_service_log,
                       'rest_service_log_file_size_MB': 1,
                       'rest_service_log_files_backup_count': 1,
                       'rest_service_log_level': 'DEBUG'},
                      f)
        os.environ['MANAGER_REST_CONFIG_PATH'] = self.tmp_conf_file
        try:
            from manager_rest import server
        finally:
            del(os.environ['MANAGER_REST_CONFIG_PATH'])

        self.server_configuration = self.create_configuration()
        server.SQL_DIALECT = 'sqlite'
        server.reset_app(self.server_configuration)
        utils.copy_resources(config.instance.file_server_root)

        self._flask_app_context = server.app.test_request_context()
        self._flask_app_context.push()
        self.addCleanup(self._flask_app_context.pop)

        self.app = self._get_app(server.app)
        self.client = self.create_client()
        server.db.create_all()
        default_tenant = self._init_default_tenant(server.db, server.app)
        self.sm = get_storage_manager()
        self._add_users_and_roles(server.user_datastore, default_tenant)
        self.initialize_provider_context()

    @staticmethod
    def _init_default_tenant(db, app):
        default_tenant = 'default_tenant'
        t = Tenant(name=default_tenant)
        db.session.add(t)
        db.session.commit()

        app.config['tenant_id'] = t.id
        return t

    @staticmethod
    def _get_app(flask_app):
        """Create a flask.testing FlaskClient

        :param flask_app: Flask app
        """
        flask_app.test_client_class = TestClient
        return flask_app.test_client()

    def _add_users_and_roles(self, user_datastore, default_tenant):
        """Add users and roles for the test

        :param user_datastore: SQLAlchemyDataUserstore
        """
        # Add a fictitious admin user to the user_datastore
        utils.add_users_and_roles_to_userstore(
            user_datastore,
            self._get_users(),
            self._get_roles(),
            default_tenant
        )

        for user in self._get_users():
            user = User.query.filter_by(username=user['username']).first()
            tenant = self.sm.get_tenant_by_name(DEFAULT_TENANT_NAME)
            if tenant not in user.tenants:
                user.tenants.append(tenant)
                self.sm.update_entity(user)

    @staticmethod
    def _get_users():
        return get_admin_user()

    @staticmethod
    def _get_roles():
        return get_admin_role()

    def cleanup(self):
        self.quiet_delete(self.rest_service_log)
        self.quiet_delete(self.sqlite_db_file)
        self.quiet_delete_directory(self.maintenance_mode_dir)
        if self.file_server:
            self.file_server.stop()
        self.quiet_delete_directory(self.tmpdir)

    def initialize_provider_context(self):
        self.sm.put_provider_context(
            {'name': self.id(), 'context': {'cloudify': {}}}
        )

    def create_configuration(self):
        test_config = config.Config()
        test_config.test_mode = True
        test_config.postgresql_db_name = self.sqlite_db_file
        test_config.postgresql_host = ''
        test_config.postgresql_username = ''
        test_config.postgresql_password = ''
        test_config.file_server_root = self.tmpdir
        test_config.file_server_base_uri = 'http://localhost:{0}'.format(
            FILE_SERVER_PORT)
        test_config.file_server_blueprints_folder = \
            FILE_SERVER_BLUEPRINTS_FOLDER
        test_config.file_server_deployments_folder = \
            FILE_SERVER_DEPLOYMENTS_FOLDER
        test_config.file_server_uploaded_blueprints_folder = \
            FILE_SERVER_UPLOADED_BLUEPRINTS_FOLDER
        test_config.file_server_snapshots_folder = \
            FILE_SERVER_SNAPSHOTS_FOLDER
        test_config.file_server_resources_uri = FILE_SERVER_RESOURCES_URI
        test_config.rest_service_log_level = 'DEBUG'
        test_config.rest_service_log_path = self.rest_service_log
        test_config.rest_service_log_file_size_MB = 100,
        test_config.rest_service_log_files_backup_count = 20
        test_config.maintenance_folder = self.maintenance_mode_dir
        return test_config

    def _version_url(self, url):
        # method for versionifying URLs for requests which don't go through
        # the REST client; the version is taken from the REST client regardless
        if CLIENT_API_VERSION not in url:
            url = '/api/{0}{1}'.format(CLIENT_API_VERSION, url)

        return url

    def post(self, resource_path, data, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.post(urllib.quote(url),
                               content_type='application/json',
                               data=json.dumps(data),
                               query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def post_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.post(urllib.quote(url),
                                   data=f.read(),
                                   query_string=build_query_string(
                                       query_params))
            result.json = json.loads(result.data)
            return result

    def put_file(self, resource_path, file_path, query_params=None):
        url = self._version_url(resource_path)
        with open(file_path) as f:
            result = self.app.put(urllib.quote(url),
                                  data=f.read(),
                                  query_string=build_query_string(
                                      query_params))
            result.json = json.loads(result.data)
            return result

    def put(self, resource_path, data=None, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.put(urllib.quote(url),
                              content_type='application/json',
                              data=json.dumps(data) if data else None,
                              query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def patch(self, resource_path, data):
        url = self._version_url(resource_path)
        result = self.app.patch(urllib.quote(url),
                                content_type='application/json',
                                data=json.dumps(data))
        result.json = json.loads(result.data)
        return result

    def get(self, resource_path, query_params=None, headers=None):
        url = self._version_url(resource_path)
        result = self.app.get(urllib.quote(url),
                              headers=headers,
                              query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def head(self, resource_path):
        url = self._version_url(resource_path)
        result = self.app.head(urllib.quote(url))
        return result

    def delete(self, resource_path, query_params=None):
        url = self._version_url(resource_path)
        result = self.app.delete(urllib.quote(url),
                                 query_string=build_query_string(query_params))
        result.json = json.loads(result.data)
        return result

    def _check_if_resource_on_fileserver(self,
                                         folder,
                                         container_id,
                                         resource_path):
        url = 'http://localhost:{0}/{1}/{2}/{3}'.format(
            FILE_SERVER_PORT, folder, container_id, resource_path)
        try:
            urllib2.urlopen(url)
            return True
        except urllib2.HTTPError:
            return False

    def check_if_resource_on_fileserver(self, blueprint_id, resource_path):
        return self._check_if_resource_on_fileserver(
            FILE_SERVER_BLUEPRINTS_FOLDER, blueprint_id, resource_path)

    def get_blueprint_path(self, blueprint_dir_name):
        return os.path.join(os.path.dirname(
            os.path.abspath(__file__)), blueprint_dir_name)

    def archive_mock_blueprint(self, archive_func=archiving.make_targzfile,
                               blueprint_dir='mock_blueprint'):
        archive_path = tempfile.mkstemp()[1]
        source_dir = os.path.join(os.path.dirname(
            os.path.abspath(__file__)), blueprint_dir)
        archive_func(archive_path, source_dir)
        return archive_path

    def get_mock_blueprint_path(self):
        return os.path.join(os.path.dirname(
            os.path.abspath(__file__)), 'mock_blueprint', 'blueprint.yaml')

    def put_blueprint_args(self, blueprint_file_name=None,
                           blueprint_id='blueprint',
                           archive_func=archiving.make_targzfile,
                           blueprint_dir='mock_blueprint'):

        resource_path = self._version_url(
            '/blueprints/{1}'.format(CLIENT_API_VERSION, blueprint_id))

        result = [
            resource_path,
            self.archive_mock_blueprint(archive_func, blueprint_dir),
        ]

        if blueprint_file_name:
            data = {'application_file_name': blueprint_file_name}
        else:
            data = {}

        result.append(data)
        return result

    def put_deployment(self,
                       deployment_id='deployment',
                       blueprint_file_name=None,
                       blueprint_id='blueprint',
                       inputs=None,
                       blueprint_dir='mock_blueprint'):
        blueprint_response = self.put_file(
            *self.put_blueprint_args(blueprint_file_name,
                                     blueprint_id,
                                     blueprint_dir=blueprint_dir)).json

        if 'error_code' in blueprint_response:
            raise RuntimeError(
                '{}: {}'.format(blueprint_response['error_code'],
                                blueprint_response['message']))

        blueprint_id = blueprint_response['id']
        deployment = self.client.deployments.create(blueprint_id,
                                                    deployment_id,
                                                    inputs=inputs)
        return blueprint_id, deployment.id, blueprint_response, deployment

    def upload_plugin(self, package_name, package_version):
        temp_file_path = self.create_wheel(package_name, package_version)
        response = self.post_file('/plugins', temp_file_path)
        os.remove(temp_file_path)
        return response

    def create_wheel(self, package_name, package_version):
        module_src = '{0}=={1}'.format(package_name, package_version)
        wagon_client = Wagon(module_src)
        return wagon_client.create(
            archive_destination_dir=tempfile.gettempdir(), force=True)

    def wait_for_url(self, url, timeout=5):
        end = time.time() + timeout

        while end >= time.time():
            try:
                status = urllib.urlopen(url).getcode()
                if status == 200:
                    return
            except IOError:
                time.sleep(1)

        raise RuntimeError('Url {0} is not available (waited {1} '
                           'seconds)'.format(url, timeout))

    @staticmethod
    def quiet_delete(file_path):
        try:
            os.remove(file_path)
        except:
            pass

    @staticmethod
    def quiet_delete_directory(file_path):
        shutil.rmtree(file_path, ignore_errors=True)

    def wait_for_deployment_creation(self, client, deployment_id):
        env_creation_execution = None
        deployment_executions = client.executions.list(deployment_id)
        for execution in deployment_executions:
            if execution.workflow_id == 'create_deployment_environment':
                env_creation_execution = execution
                break
        if env_creation_execution:
            self.wait_for_execution(client, env_creation_execution)

    @staticmethod
    def wait_for_execution(client, execution, timeout=900):
        # Poll for execution status until execution ends
        deadline = time.time() + timeout
        while True:
            if time.time() > deadline:
                raise Exception(
                    'execution of operation {0} for deployment {1} timed out'.
                    format(execution.workflow_id, execution.deployment_id))

            execution = client.executions.get(execution.id)
            if execution.status in Execution.END_STATES:
                break
            time.sleep(3)

    def _add_blueprint(self, blueprint_id=None):
        if not blueprint_id:
            unique_str = str(uuid.uuid4())
            blueprint_id = 'blueprint-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        blueprint = models.Blueprint(id=blueprint_id,
                                     created_at=now,
                                     updated_at=now,
                                     description=None,
                                     plan={'name': 'my-bp'},
                                     main_file_name='aaa')
        return self.sm.put_blueprint(blueprint)

    def _add_deployment(self, blueprint_id, deployment_id=None):
        if not deployment_id:
            unique_str = str(uuid.uuid4())
            deployment_id = 'deployment-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment = models.Deployment(id=deployment_id,
                                       created_at=now,
                                       updated_at=now,
                                       blueprint_id=blueprint_id,
                                       permalink=None,
                                       description=None,
                                       workflows={},
                                       inputs={},
                                       policy_types={},
                                       policy_triggers={},
                                       groups={},
                                       scaling_groups={},
                                       outputs={})
        return self.sm.put_deployment(deployment)

    def _add_execution_with_id(self, execution_id):
        blueprint = self._add_blueprint()
        deployment = self._add_deployment(blueprint.id)
        return self._add_execution(deployment.id, blueprint.id, execution_id)

    def _add_execution(self, deployment_id, blueprint_id, execution_id=None):
        if not execution_id:
            unique_str = str(uuid.uuid4())
            execution_id = 'execution-{0}'.format(unique_str)
        execution = models.Execution(
            id=execution_id,
            status=models.Execution.TERMINATED,
            deployment_id=deployment_id,
            workflow_id='',
            blueprint_id=blueprint_id,
            created_at=utils.get_formatted_timestamp(),
            error='',
            parameters=dict(),
            is_system_workflow=False)
        return self.sm.put_execution(execution)

    def _add_deployment_update(self, blueprint_id, execution_id,
                               deployment_update_id=None):
        if not deployment_update_id:
            unique_str = str(uuid.uuid4())
            deployment_update_id = 'deployment_update-{0}'.format(unique_str)
        now = utils.get_formatted_timestamp()
        deployment_update = models.DeploymentUpdate(
            deployment_id=blueprint_id,
            deployment_plan={'name': 'my-bp'},
            state='staged',
            id=deployment_update_id,
            deployment_update_nodes=None,
            deployment_update_node_instances=None,
            deployment_update_deployment=None,
            modified_entity_ids=None,
            execution_id=execution_id,
            created_at=now)
        return self.sm.put_deployment_update(deployment_update)