Esempio n. 1
0
    def test_user_argument(self):
        # User is the same as logged user, no sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = False
        kwargs['user'] = LOGGED_USER_USERNAME
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'ls -la')

        # User is different, sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = False
        kwargs['user'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'sudo -E -u mauser -- bash -c \'ls -la\'')

        # sudo is used, it doesn't matter what user is specified since the
        # command should run as root
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = True
        kwargs['user'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'sudo -E -- bash -c \'ls -la\'')
    def test_user_argument(self):
        # User is the same as logged user, no sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = False
        kwargs['user'] = LOGGED_USER_USERNAME
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'ls -la')

        # User is different, sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = False
        kwargs['user'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'sudo -E -H -u mauser -- bash -c \'ls -la\'')

        # sudo with password
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = False
        kwargs['sudo_password'] = '******'
        kwargs['user'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()

        expected_command = 'sudo -S -E -H -u mauser -- bash -c \'ls -la\''
        self.assertEqual(command, expected_command)

        # sudo is used, it doesn't matter what user is specified since the
        # command should run as root
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = True
        kwargs['user'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, 'sudo -E -- bash -c \'ls -la\'')

        # sudo with passwd
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs['sudo'] = True
        kwargs['user'] = '******'
        kwargs['sudo_password'] = '******'
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()

        expected_command = 'sudo -S -E -- bash -c \'ls -la\''
        self.assertEqual(command, expected_command)
Esempio n. 3
0
    def run(self, action_parameters):
        if self.entry_point:
            raise ValueError(
                'entry_point is only valid for local-shell-script runner')

        command = self.runner_parameters.get(RUNNER_COMMAND, None)
        action = ShellCommandAction(name=self.action_name,
                                    action_exec_id=str(self.liveaction_id),
                                    command=command,
                                    user=self._user,
                                    env_vars=self._env,
                                    sudo=self._sudo,
                                    timeout=self._timeout,
                                    sudo_password=self._sudo_password)

        return self._run(action=action)
Esempio n. 4
0
    def run(self, action_parameters):
        env_vars = self._env

        if not self.entry_point:
            script_action = False
            command = self.runner_parameters.get(RUNNER_COMMAND, None)
            action = ShellCommandAction(name=self.action_name,
                                        action_exec_id=str(self.liveaction_id),
                                        command=command,
                                        user=self._user,
                                        env_vars=env_vars,
                                        sudo=self._sudo,
                                        timeout=self._timeout,
                                        sudo_password=self._sudo_password)
        else:
            script_action = True
            script_local_path_abs = self.entry_point
            positional_args, named_args = self._get_script_args(action_parameters)
            named_args = self._transform_named_args(named_args)

            action = ShellScriptAction(name=self.action_name,
                                       action_exec_id=str(self.liveaction_id),
                                       script_local_path_abs=script_local_path_abs,
                                       named_args=named_args,
                                       positional_args=positional_args,
                                       user=self._user,
                                       env_vars=env_vars,
                                       sudo=self._sudo,
                                       timeout=self._timeout,
                                       cwd=self._cwd,
                                       sudo_password=self._sudo_password)

        args = action.get_full_command_string()
        sanitized_args = action.get_sanitized_full_command_string()

        # For consistency with the old Fabric based runner, make sure the file is executable
        if script_action:
            args = 'chmod +x %s ; %s' % (script_local_path_abs, args)
            sanitized_args = 'chmod +x %s ; %s' % (script_local_path_abs, sanitized_args)

        env = os.environ.copy()

        # Include user provided env vars (if any)
        env.update(env_vars)

        # Include common st2 env vars
        st2_env_vars = self._get_common_action_env_variables()
        env.update(st2_env_vars)

        LOG.info('Executing action via LocalRunner: %s', self.runner_id)
        LOG.info('[Action info] name: %s, Id: %s, command: %s, user: %s, sudo: %s' %
                 (action.name, action.action_exec_id, sanitized_args, action.user, action.sudo))

        stdout = StringIO()
        stderr = StringIO()

        store_execution_stdout_line = functools.partial(store_execution_output_data,
                                                        output_type='stdout')
        store_execution_stderr_line = functools.partial(store_execution_output_data,
                                                        output_type='stderr')

        read_and_store_stdout = make_read_and_store_stream_func(execution_db=self.execution,
            action_db=self.action, store_data_func=store_execution_stdout_line)
        read_and_store_stderr = make_read_and_store_stream_func(execution_db=self.execution,
            action_db=self.action, store_data_func=store_execution_stderr_line)

        # If sudo password is provided, pass it to the subprocess via stdin>
        # Note: We don't need to explicitly escape the argument because we pass command as a list
        # to subprocess.Popen and all the arguments are escaped by the function.
        if self._sudo_password:
            LOG.debug('Supplying sudo password via stdin')
            echo_process = subprocess.Popen(['echo', self._sudo_password + '\n'],
                                            stdout=subprocess.PIPE)
            stdin = echo_process.stdout
        else:
            stdin = None

        # Make sure os.setsid is called on each spawned process so that all processes
        # are in the same group.

        # Process is started as sudo -u {{system_user}} -- bash -c {{command}}. Introduction of the
        # bash means that multiple independent processes are spawned without them being
        # children of the process we have access to and this requires use of pkill.
        # Ideally os.killpg should have done the trick but for some reason that failed.
        # Note: pkill will set the returncode to 143 so we don't need to explicitly set
        # it to some non-zero value.
        exit_code, stdout, stderr, timed_out = shell.run_command(cmd=args,
                                                                 stdin=stdin,
                                                                 stdout=subprocess.PIPE,
                                                                 stderr=subprocess.PIPE,
                                                                 shell=True,
                                                                 cwd=self._cwd,
                                                                 env=env,
                                                                 timeout=self._timeout,
                                                                 preexec_func=os.setsid,
                                                                 kill_func=kill_process,
                                                           read_stdout_func=read_and_store_stdout,
                                                           read_stderr_func=read_and_store_stderr,
                                                           read_stdout_buffer=stdout,
                                                           read_stderr_buffer=stderr)

        error = None

        if timed_out:
            error = 'Action failed to complete in %s seconds' % (self._timeout)
            exit_code = -1 * exit_code_constants.SIGKILL_EXIT_CODE

        # Detect if user provided an invalid sudo password or sudo is not configured for that user
        if self._sudo_password:
            if re.search('sudo: \d+ incorrect password attempts', stderr):
                match = re.search('\[sudo\] password for (.+?)\:', stderr)

                if match:
                    username = match.groups()[0]
                else:
                    username = '******'

                error = ('Invalid sudo password provided or sudo is not configured for this user '
                        '(%s)' % (username))
                exit_code = -1

        succeeded = (exit_code == exit_code_constants.SUCCESS_EXIT_CODE)

        result = {
            'failed': not succeeded,
            'succeeded': succeeded,
            'return_code': exit_code,
            'stdout': strip_shell_chars(stdout),
            'stderr': strip_shell_chars(stderr)
        }

        if error:
            result['error'] = error

        status = PROC_EXIT_CODE_TO_LIVEACTION_STATUS_MAP.get(
            str(exit_code),
            action_constants.LIVEACTION_STATUS_FAILED
        )

        return (status, jsonify.json_loads(result, LocalShellRunner.KEYS_TO_TRANSFORM), None)
Esempio n. 5
0
    def test_user_argument(self):
        # User is the same as logged user, no sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs["sudo"] = False
        kwargs["user"] = LOGGED_USER_USERNAME
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, "ls -la")

        # User is different, sudo should be used
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs["sudo"] = False
        kwargs["user"] = "******"
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, "sudo -E -H -u mauser -- bash -c 'ls -la'")

        # sudo with password
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs["sudo"] = False
        kwargs["sudo_password"] = "******"
        kwargs["user"] = "******"
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()

        expected_command = "sudo -S -E -H -u mauser -- bash -c 'ls -la'"
        self.assertEqual(command, expected_command)

        # sudo is used, it doesn't matter what user is specified since the
        # command should run as root
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs["sudo"] = True
        kwargs["user"] = "******"
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()
        self.assertEqual(command, "sudo -E -- bash -c 'ls -la'")

        # sudo with passwd
        kwargs = copy.deepcopy(self._base_kwargs)
        kwargs["sudo"] = True
        kwargs["user"] = "******"
        kwargs["sudo_password"] = "******"
        action = ShellCommandAction(**kwargs)
        command = action.get_full_command_string()

        expected_command = "sudo -S -E -- bash -c 'ls -la'"
        self.assertEqual(command, expected_command)
Esempio n. 6
0
    def run(self, action_parameters):
        LOG.debug('    action_parameters = %s', action_parameters)

        env_vars = self._env

        if not self.entry_point:
            script_action = False
            command = self.runner_parameters.get(RUNNER_COMMAND, None)
            action = ShellCommandAction(name=self.action_name,
                                        action_exec_id=str(self.liveaction_id),
                                        command=command,
                                        user=self._user,
                                        env_vars=env_vars,
                                        sudo=self._sudo,
                                        timeout=self._timeout)
        else:
            script_action = True
            script_local_path_abs = self.entry_point
            positional_args, named_args = self._get_script_args(action_parameters)
            named_args = self._transform_named_args(named_args)

            action = ShellScriptAction(name=self.action_name,
                                       action_exec_id=str(self.liveaction_id),
                                       script_local_path_abs=script_local_path_abs,
                                       named_args=named_args,
                                       positional_args=positional_args,
                                       user=self._user,
                                       env_vars=env_vars,
                                       sudo=self._sudo,
                                       timeout=self._timeout,
                                       cwd=self._cwd)

        args = action.get_full_command_string()

        # For consistency with the old Fabric based runner, make sure the file is executable
        if script_action:
            args = 'chmod +x %s ; %s' % (script_local_path_abs, args)

        env = os.environ.copy()

        # Include user provided env vars (if any)
        env.update(env_vars)

        LOG.info('Executing action via LocalRunner: %s', self.runner_id)
        LOG.info('[Action info] name: %s, Id: %s, command: %s, user: %s, sudo: %s' %
                 (action.name, action.action_exec_id, args, action.user, action.sudo))

        # Make sure os.setsid is called on each spawned process so that all processes
        # are in the same group.
        process = subprocess.Popen(args=args, stdin=None, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE, shell=True, cwd=self._cwd,
                                   env=env, preexec_fn=os.setsid)

        error_holder = {}

        def on_timeout_expired(timeout):
            try:
                process.wait(timeout=self._timeout)
            except subprocess.TimeoutExpired:
                # Set the error prior to kill the process else the error is not picked up due
                # to eventlet scheduling.
                error_holder['error'] = 'Action failed to complete in %s seconds' % (self._timeout)
                # Action has timed out, kill the process and propagate the error. The process
                # is started as sudo -u {{system_user}} -- bash -c {{command}}. Introduction of the
                # bash means that multiple independent processes are spawned without them being
                # children of the process we have access to and this requires use of pkill.
                # Ideally os.killpg should have done the trick but for some reason that failed.
                # Note: pkill will set the returncode to 143 so we don't need to explicitly set
                # it to some non-zero value.
                try:
                    killcommand = shlex.split('sudo pkill -TERM -s %s' % process.pid)
                    subprocess.call(killcommand)
                except:
                    LOG.exception('Unable to pkill.')

        timeout_expiry = eventlet.spawn(on_timeout_expired, self._timeout)

        stdout, stderr = process.communicate()
        timeout_expiry.cancel()
        error = error_holder.get('error', None)
        exit_code = process.returncode
        succeeded = (exit_code == 0)

        result = {
            'failed': not succeeded,
            'succeeded': succeeded,
            'return_code': exit_code,
            'stdout': stdout,
            'stderr': stderr
        }

        if error:
            result['error'] = error

        status = LIVEACTION_STATUS_SUCCEEDED if exit_code == 0 else LIVEACTION_STATUS_FAILED
        self._log_action_completion(logger=LOG, result=result, status=status, exit_code=exit_code)
        return (status, jsonify.json_loads(result, LocalShellRunner.KEYS_TO_TRANSFORM), None)
Esempio n. 7
0
    def run(self, action_parameters):
        LOG.debug('    action_parameters = %s', action_parameters)

        env_vars = self._env

        if not self.entry_point:
            script_action = False
            command = self.runner_parameters.get(RUNNER_COMMAND, None)
            action = ShellCommandAction(name=self.action_name,
                                        action_exec_id=str(self.liveaction_id),
                                        command=command,
                                        user=self._user,
                                        env_vars=env_vars,
                                        sudo=self._sudo,
                                        timeout=self._timeout)
        else:
            script_action = True
            script_local_path_abs = self.entry_point
            positional_args, named_args = self._get_script_args(action_parameters)
            named_args = self._transform_named_args(named_args)

            action = ShellScriptAction(name=self.action_name,
                                       action_exec_id=str(self.liveaction_id),
                                       script_local_path_abs=script_local_path_abs,
                                       named_args=named_args,
                                       positional_args=positional_args,
                                       user=self._user,
                                       env_vars=env_vars,
                                       sudo=self._sudo,
                                       timeout=self._timeout,
                                       cwd=self._cwd)

        args = action.get_full_command_string()

        # For consistency with the old Fabric based runner, make sure the file is executable
        if script_action:
            args = 'chmod +x %s ; %s' % (script_local_path_abs, args)

        env = os.environ.copy()

        # Include user provided env vars (if any)
        env.update(env_vars)

        # Make sure os.setsid is called on each spawned process so that all processes
        # are in the same group.
        process = subprocess.Popen(args=args, stdin=None, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE, shell=True, cwd=self._cwd,
                                   env=env, preexec_fn=os.setsid)

        error_holder = {}

        def on_timeout_expired(timeout):
            try:
                process.wait(timeout=self._timeout)
            except subprocess.TimeoutExpired:
                # Set the error prior to kill the process else the error is not picked up due
                # to eventlet scheduling.
                error_holder['error'] = 'Action failed to complete in %s seconds' % (self._timeout)
                # Action has timed out, kill the process and propagate the error. The process
                # is started as sudo -u {{system_user}} -- bash -c {{command}}. Introduction of the
                # bash means that multiple independent processes are spawned without them being
                # children of the process we have access to and this requires use of pkill.
                # Ideally os.killpg should have done the trick but for some reason that failed.
                # Note: pkill will set the returncode to 143 so we don't need to explicitly set
                # it to some non-zero value.
                try:
                    killcommand = shlex.split('sudo pkill -TERM -s %s' % process.pid)
                    subprocess.call(killcommand)
                except:
                    LOG.exception('Unable to pkill.')

        timeout_expiry = eventlet.spawn(on_timeout_expired, self._timeout)

        stdout, stderr = process.communicate()
        timeout_expiry.cancel()
        error = error_holder.get('error', None)
        exit_code = process.returncode
        succeeded = (exit_code == 0)

        result = {
            'failed': not succeeded,
            'succeeded': succeeded,
            'return_code': exit_code,
            'stdout': stdout,
            'stderr': stderr
        }

        if error:
            result['error'] = error

        status = LIVEACTION_STATUS_SUCCEEDED if exit_code == 0 else LIVEACTION_STATUS_FAILED
        return (status, jsonify.json_loads(result, LocalShellRunner.KEYS_TO_TRANSFORM), None)
    def run(self, action_parameters):
        env_vars = self._env

        if not self.entry_point:
            script_action = False
            command = self.runner_parameters.get(RUNNER_COMMAND, None)
            action = ShellCommandAction(name=self.action_name,
                                        action_exec_id=str(self.liveaction_id),
                                        command=command,
                                        user=self._user,
                                        env_vars=env_vars,
                                        sudo=self._sudo,
                                        timeout=self._timeout)
        else:
            script_action = True
            script_local_path_abs = self.entry_point
            positional_args, named_args = self._get_script_args(
                action_parameters)
            named_args = self._transform_named_args(named_args)

            action = ShellScriptAction(
                name=self.action_name,
                action_exec_id=str(self.liveaction_id),
                script_local_path_abs=script_local_path_abs,
                named_args=named_args,
                positional_args=positional_args,
                user=self._user,
                env_vars=env_vars,
                sudo=self._sudo,
                timeout=self._timeout,
                cwd=self._cwd)

        args = action.get_full_command_string()

        # For consistency with the old Fabric based runner, make sure the file is executable
        if script_action:
            args = 'chmod +x %s ; %s' % (script_local_path_abs, args)

        env = os.environ.copy()

        # Include user provided env vars (if any)
        env.update(env_vars)

        # Include common st2 env vars
        st2_env_vars = self._get_common_action_env_variables()
        env.update(st2_env_vars)

        LOG.info('Executing action via LocalRunner: %s', self.runner_id)
        LOG.info(
            '[Action info] name: %s, Id: %s, command: %s, user: %s, sudo: %s' %
            (action.name, action.action_exec_id, args, action.user,
             action.sudo))

        # Make sure os.setsid is called on each spawned process so that all processes
        # are in the same group.

        # Process is started as sudo -u {{system_user}} -- bash -c {{command}}. Introduction of the
        # bash means that multiple independent processes are spawned without them being
        # children of the process we have access to and this requires use of pkill.
        # Ideally os.killpg should have done the trick but for some reason that failed.
        # Note: pkill will set the returncode to 143 so we don't need to explicitly set
        # it to some non-zero value.
        exit_code, stdout, stderr, timed_out = run_command(
            cmd=args,
            stdin=None,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            shell=True,
            cwd=self._cwd,
            env=env,
            timeout=self._timeout,
            preexec_func=os.setsid,
            kill_func=kill_process)

        error = None

        if timed_out:
            error = 'Action failed to complete in %s seconds' % (self._timeout)
            exit_code = -9

        succeeded = (exit_code == 0)

        result = {
            'failed': not succeeded,
            'succeeded': succeeded,
            'return_code': exit_code,
            'stdout': strip_shell_chars(stdout),
            'stderr': strip_shell_chars(stderr)
        }

        if error:
            result['error'] = error

        if exit_code == 0:
            status = LIVEACTION_STATUS_SUCCEEDED
        elif timed_out:
            status = LIVEACTION_STATUS_TIMED_OUT
        else:
            status = LIVEACTION_STATUS_FAILED

        return (status,
                jsonify.json_loads(result,
                                   LocalShellRunner.KEYS_TO_TRANSFORM), None)
Esempio n. 9
0
    def run(self, action_parameters):
        env_vars = self._env

        if not self.entry_point:
            script_action = False
            command = self.runner_parameters.get(RUNNER_COMMAND, None)
            action = ShellCommandAction(name=self.action_name,
                                        action_exec_id=str(self.liveaction_id),
                                        command=command,
                                        user=self._user,
                                        env_vars=env_vars,
                                        sudo=self._sudo,
                                        timeout=self._timeout)
        else:
            script_action = True
            script_local_path_abs = self.entry_point
            positional_args, named_args = self._get_script_args(action_parameters)
            named_args = self._transform_named_args(named_args)

            action = ShellScriptAction(name=self.action_name,
                                       action_exec_id=str(self.liveaction_id),
                                       script_local_path_abs=script_local_path_abs,
                                       named_args=named_args,
                                       positional_args=positional_args,
                                       user=self._user,
                                       env_vars=env_vars,
                                       sudo=self._sudo,
                                       timeout=self._timeout,
                                       cwd=self._cwd)

        args = action.get_full_command_string()

        # For consistency with the old Fabric based runner, make sure the file is executable
        if script_action:
            args = 'chmod +x %s ; %s' % (script_local_path_abs, args)

        env = os.environ.copy()

        # Include user provided env vars (if any)
        env.update(env_vars)

        # Include common st2 env vars
        st2_env_vars = self._get_common_action_env_variables()
        env.update(st2_env_vars)

        LOG.info('Executing action via LocalRunner: %s', self.runner_id)
        LOG.info('[Action info] name: %s, Id: %s, command: %s, user: %s, sudo: %s' %
                 (action.name, action.action_exec_id, args, action.user, action.sudo))

        # Make sure os.setsid is called on each spawned process so that all processes
        # are in the same group.

        # Process is started as sudo -u {{system_user}} -- bash -c {{command}}. Introduction of the
        # bash means that multiple independent processes are spawned without them being
        # children of the process we have access to and this requires use of pkill.
        # Ideally os.killpg should have done the trick but for some reason that failed.
        # Note: pkill will set the returncode to 143 so we don't need to explicitly set
        # it to some non-zero value.
        exit_code, stdout, stderr, timed_out = run_command(cmd=args, stdin=None,
                                                           stdout=subprocess.PIPE,
                                                           stderr=subprocess.PIPE,
                                                           shell=True,
                                                           cwd=self._cwd,
                                                           env=env,
                                                           timeout=self._timeout,
                                                           preexec_func=os.setsid,
                                                           kill_func=kill_process)

        error = None

        if timed_out:
            error = 'Action failed to complete in %s seconds' % (self._timeout)
            exit_code = -9

        succeeded = (exit_code == 0)

        result = {
            'failed': not succeeded,
            'succeeded': succeeded,
            'return_code': exit_code,
            'stdout': strip_last_newline_char(stdout),
            'stderr': strip_last_newline_char(stderr)
        }

        if error:
            result['error'] = error

        status = LIVEACTION_STATUS_SUCCEEDED if exit_code == 0 else LIVEACTION_STATUS_FAILED
        return (status, jsonify.json_loads(result, LocalShellRunner.KEYS_TO_TRANSFORM), None)