def run_script(script_path,
               fabric_env=None,
               process=None,
               use_sudo=False,
               hide_output=None,
               **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(), 'ctx-sh')
    local_ctx_py_path = os.path.join(
        os.path.dirname(cloudify.ctx_wrappers.__file__), 'ctx-py.py')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))

    env = process.get('env', {})
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    with fabric_api.settings(
            _hide_output(groups=hide_output),
            **_fabric_env(fabric_env, warn_only=False)):
        # Determine the basedir. In order of precedence:

        # * 'base_dir' process input
        # * ${CFY_EXEC_TEMP}/cloudify-ctx on the remote machine, if
        #   CFY_EXEC_TEMP is defined
        # * <python default tempdir>/cloudify-ctx

        base_dir = process.get('base_dir')

        if not base_dir:
            #  "CFY_EXEC_TEMPDIR_ENVVAR" doesn't exist in 3.3.1, so
            # to remain backward compatible...
            if hasattr(utils, 'CFY_EXEC_TEMPDIR_ENVVAR'):
                base_dir = fabric_api.run(
                    '( [[ -n "${0}" ]] && echo -n ${0} ) || '
                    'echo -n $(dirname $(mktemp -u))'.format(
                        utils.CFY_EXEC_TEMPDIR_ENVVAR))
            else:
                base_dir = fabric_api.run('echo -n $(dirname $(mktemp -u))')

        if not base_dir:
            raise NonRecoverableError('Could not conclude temporary directory')

        base_dir = posixpath.join(base_dir, DEFAULT_BASE_SUBDIR)

        ctx.logger.debug('base_dir set to: {0}'.format(base_dir))

        remote_ctx_dir = base_dir
        remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
        remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
        remote_ctx_py_path = '{0}/cloudify.py'.format(remote_ctx_dir)
        remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
        remote_work_dir = '{0}/work'.format(remote_ctx_dir)
        remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                      remote_path_suffix)
        remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                              remote_path_suffix)

        cwd = process.get('cwd', remote_work_dir)

        command = remote_script_path
        if command_prefix:
            command = '{0} {1}'.format(command_prefix, command)
        if args:
            command = ' '.join([command] + args)

        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)
            fabric_api.put(proxy_client_path, remote_ctx_path)
            fabric_api.put(local_ctx_py_path, remote_ctx_py_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value
        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path,
                template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined and
               isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env['PYTHONPATH'] = '{0}:$PYTHONPATH'.format(remote_ctx_dir)
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        fabric_api.put(local_script_path, remote_script_path)
        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)
        try:
            with fabric_context.cd(cwd):
                local_port = proxy.port
                with tunnel.remote(local_port=local_port) as remote_port:
                    env[CTX_SOCKET_URL] = proxy.socket_url.replace(
                        str(local_port), str(remote_port))
                    env['LOCAL_{0}'.format(CTX_SOCKET_URL)] = proxy.socket_url
                    for key, value in env.iteritems():
                        env_script.write('export {0}={1}\n'.format(key, value))
                    fabric_api.put(env_script, remote_env_script_path)
                    # invoke sys.exc_clear() because handle_script_result
                    # invokes sys.exc_info()
                    sys.exc_clear()
                    try:
                        command = 'source {0} && {1}'.format(
                            remote_env_script_path, command)
                        run = fabric_api.sudo if use_sudo else fabric_api.run
                        run(command)
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(),
                                     'ctx-sh')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):

            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))

            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)

            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value
        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path,
                template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined and
               isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        # invoke sys.exc_clear() because handle_script_result
        # invokes sys.exc_info()
        sys.exc_clear()
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    try:
                        fabric_api.run('source {0} && {1}'.format(
                            remote_env_script_path, command))
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()
Beispiel #3
0
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_ctx_sh_path = os.path.join(_get_bin_dir(), 'ctx-sh')
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_ctx_sh_path = '{0}/ctx-sh'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        # the remote host must have ctx and any related files before
        # running any fabric scripts
        if not fabric_files.exists(remote_ctx_path):

            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))

            # this file has to be present before using ctx
            fabric_api.put(local_ctx_sh_path, remote_ctx_sh_path)

            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        actual_ctx.is_script_exception_defined = ScriptException is not None

        def abort_operation(message=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx
            return actual_ctx._return_value

        def retry_operation(message=None, retry_after=None):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx.operation.retry(message=message,
                                       retry_after=retry_after)
            if actual_ctx.is_script_exception_defined:
                actual_ctx._return_value = ScriptException(message, retry=True)
            else:
                actual_ctx._return_value = UNSUPPORTED_SCRIPT_FEATURE_ERROR
                raise actual_ctx._return_value
            return actual_ctx._return_value

        actual_ctx.abort_operation = abort_operation
        actual_ctx.retry_operation = retry_operation

        def returns(_value):
            if actual_ctx._return_value is not None:
                actual_ctx._return_value = ILLEGAL_CTX_OPERATION_ERROR
                raise actual_ctx._return_value
            actual_ctx._return_value = _value

        actual_ctx.returns = returns

        actual_ctx._return_value = None
        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path, template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir, os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        def handle_script_result(script_result):
            if (actual_ctx.is_script_exception_defined
                    and isinstance(script_result, ScriptException)):
                if script_result.retry:
                    return script_result
                else:
                    raise NonRecoverableError(str(script_result))
            # this happens when more than 1 ctx operation is invoked or
            # the plugin runs an unsupported feature on older Cloudify
            elif isinstance(script_result, RuntimeError):
                raise NonRecoverableError(str(script_result))
            # determine if this code runs during exception handling
            current_exception = sys.exc_info()[1]
            if current_exception:
                raise
            else:
                return script_result

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        # invoke sys.exc_clear() because handle_script_result
        # invokes sys.exc_info()
        sys.exc_clear()
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    try:
                        fabric_api.run('source {0} && {1}'.format(
                            remote_env_script_path, command))
                    except FabricTaskError:
                        return handle_script_result(actual_ctx._return_value)
            return handle_script_result(actual_ctx._return_value)
        finally:
            proxy.close()
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        if not fabric_files.exists(remote_ctx_path):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        def returns(_value):
            actual_ctx._return_value = _value
        actual_ctx._return_value = None
        actual_ctx.returns = returns

        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path,
                template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir,
                    os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    fabric_api.run('source {0} && {1}'.format(
                        remote_env_script_path, command))
            return actual_ctx._return_value
        finally:
            proxy.close()
def run_script(script_path, fabric_env=None, process=None, **kwargs):

    if not process:
        process = {}
    process = _create_process_config(process, kwargs)
    base_dir = process.get('base_dir', DEFAULT_BASE_DIR)
    ctx_server_port = process.get('ctx_server_port')

    proxy_client_path = proxy_client.__file__
    if proxy_client_path.endswith('.pyc'):
        proxy_client_path = proxy_client_path[:-1]
    local_script_path = get_script(ctx.download_resource, script_path)
    base_script_path = os.path.basename(local_script_path)
    remote_ctx_dir = base_dir
    remote_ctx_path = '{0}/ctx'.format(remote_ctx_dir)
    remote_scripts_dir = '{0}/scripts'.format(remote_ctx_dir)
    remote_work_dir = '{0}/work'.format(remote_ctx_dir)
    remote_path_suffix = '{0}-{1}'.format(base_script_path,
                                          utils.id_generator(size=8))
    remote_env_script_path = '{0}/env-{1}'.format(remote_scripts_dir,
                                                  remote_path_suffix)
    remote_script_path = '{0}/{1}'.format(remote_scripts_dir,
                                          remote_path_suffix)

    env = process.get('env', {})
    cwd = process.get('cwd', remote_work_dir)
    args = process.get('args')
    command_prefix = process.get('command_prefix')

    command = remote_script_path
    if command_prefix:
        command = '{0} {1}'.format(command_prefix, command)
    if args:
        command = ' '.join([command] + args)

    with fabric_api.settings(**_fabric_env(fabric_env, warn_only=False)):
        if not fabric_files.exists(remote_ctx_path):
            # there may be race conditions with other operations that
            # may be running in parallel, so we pass -p to make sure
            # we get 0 exit code if the directory already exists
            fabric_api.run('mkdir -p {0}'.format(remote_scripts_dir))
            fabric_api.run('mkdir -p {0}'.format(remote_work_dir))
            fabric_api.put(proxy_client_path, remote_ctx_path)

        actual_ctx = ctx._get_current_object()

        def returns(_value):
            actual_ctx._return_value = _value

        actual_ctx._return_value = None
        actual_ctx.returns = returns

        original_download_resource = actual_ctx.download_resource

        def download_resource(resource_path, target_path=None):
            local_target_path = original_download_resource(resource_path)
            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource = download_resource

        original_download_resource_and_render = \
            actual_ctx.download_resource_and_render

        def download_resource_and_render(resource_path,
                                         target_path=None,
                                         template_variables=None):
            local_target_path = original_download_resource_and_render(
                resource_path, template_variables=template_variables)

            return fabric_put_in_remote_path(local_target_path, target_path)

        actual_ctx.download_resource_and_render = download_resource_and_render

        def fabric_put_in_remote_path(local_target_path, target_path):
            if target_path:
                remote_target_path = target_path
            else:
                remote_target_path = '{0}/{1}'.format(
                    remote_work_dir, os.path.basename(local_target_path))
            fabric_api.put(local_target_path, remote_target_path)
            return remote_target_path

        proxy = proxy_server.HTTPCtxProxy(actual_ctx, port=ctx_server_port)

        env_script = StringIO()
        env['PATH'] = '{0}:$PATH'.format(remote_ctx_dir)
        env[CTX_SOCKET_URL] = proxy.socket_url
        for key, value in env.iteritems():
            env_script.write('export {0}={1}\n'.format(key, value))
        env_script.write('chmod +x {0}\n'.format(remote_script_path))
        env_script.write('chmod +x {0}\n'.format(remote_ctx_path))
        try:
            fabric_api.put(local_script_path, remote_script_path)
            fabric_api.put(env_script, remote_env_script_path)
            with fabric_context.cd(cwd):
                with tunnel.remote(proxy.port):
                    fabric_api.run('source {0} && {1}'.format(
                        remote_env_script_path, command))
            return actual_ctx._return_value
        finally:
            proxy.close()