Skip to content

Commit

Permalink
executer: support run with sudo. (#140)
Browse files Browse the repository at this point in the history
* executer: support run with sudo.

exec = host.executor(sudo=True)

* Update README

* Fix tests

* test to cover sudo

* Fix test

* Fix tests

* Fix tests
  • Loading branch information
myakove authored Feb 3, 2021
1 parent 0827dd7 commit 14ae0c9
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 43 deletions.
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ that means SSH server must be running there already.
host.executor_factory = rrmngmnt.ssh.RemoteExecutorFactory(use_pkey=True)
exec = h.executor()
# Run with sudo
exec = h.executor(sudo=True)
print exec.run_cmd(['echo', 'Hello World'])
Expand Down
8 changes: 6 additions & 2 deletions rrmngmnt/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(self, ip, service_provider=None):
self._package_manager = PackageManagerProxy(self)
self.os = OperatingSystem(self)
self.add() # adding host to inventory
self.sudo = False

def __str__(self):
return "Host(%s)" % self.ip
Expand Down Expand Up @@ -210,7 +211,7 @@ def package_manager(self):
def power_manager(self):
return self.get_power_manager()

def executor(self, user=None, pkey=False):
def executor(self, user=None, pkey=False, sudo=False):
"""
Gives you executor to allowing command execution
Expand All @@ -219,6 +220,9 @@ def executor(self, user=None, pkey=False):
user. when it is None, the default executor user is used,
see set_executor_user method for more info.
"""
if sudo:
self.sudo = True

if user is None:
user = self.executor_user
if pkey:
Expand All @@ -229,7 +233,7 @@ def executor(self, user=None, pkey=False):
ef = copy.copy(ssh.RemoteExecutorFactory)
ef.use_pkey = pkey
return ef(self.ip, user)
return self.executor_factory.build(self, user)
return self.executor_factory.build(self, user, sudo=self.sudo)

def run_command(
self, command, input_=None, tcp_timeout=None, io_timeout=None,
Expand Down
11 changes: 8 additions & 3 deletions rrmngmnt/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ def command(self, cmd):
return RemoteExecutor.Command(cmd, self)

def run_cmd(self, cmd, input_=None, timeout=None):
if self._executor.sudo:
cmd.insert(0, "sudo")

cmd = self.command(cmd)
return cmd.run(input_, timeout)

Expand Down Expand Up @@ -206,18 +209,20 @@ def run(self, input_, timeout=None, get_pty=False):
self.err = normalize_string(err.read())
return self.rc, self.out, self.err

def __init__(self, user, address, use_pkey=False, port=22):
def __init__(self, user, address, use_pkey=False, port=22, sudo=False):
"""
Args:
use_pkey (bool): Use ssh private key in the connection
user (instance of User): User
address (str): Ip / hostname
port (int): Port to connect
sudo (bool): Use sudo to execute command.
"""
super(RemoteExecutor, self).__init__(user)
self.address = address
self.use_pkey = use_pkey
self.port = port
self.sudo = sudo

def session(self, timeout=None):
"""
Expand Down Expand Up @@ -306,6 +311,6 @@ def __init__(self, use_pkey=False, port=22):
self.use_pkey = use_pkey
self.port = port

def build(self, host, user):
def build(self, host, user, sudo=False):
return RemoteExecutor(
user, host.ip, use_pkey=self.use_pkey, port=self.port)
user, host.ip, use_pkey=self.use_pkey, port=self.port, sudo=sudo)
4 changes: 3 additions & 1 deletion tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def session(self, timeout=None):
return FakeExecutor.Session(self, timeout)

def run_cmd(self, cmd, input_=None, tcp_timeout=None, io_timeout=None):
cmd = list(cmd)
with self.session(tcp_timeout) as session:
return session.run_cmd(cmd, input_, io_timeout)

Expand All @@ -126,8 +127,9 @@ def __init__(self, cmd_to_data, files_content):
self.cmd_to_data = cmd_to_data.copy()
self.files_content = files_content

def build(self, host, user):
def build(self, host, user, sudo):
fe = FakeExecutor(user, host.ip)
fe.cmd_to_data = self.cmd_to_data.copy()
fe.files_content = self.files_content
fe.sudo = sudo
return fe
156 changes: 119 additions & 37 deletions tests/test_package_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import pytest

from rrmngmnt import Host, User
from .common import FakeExecutorFactory
import rrmngmnt.package_manager as pm
Expand All @@ -8,19 +10,41 @@
host_executor_factory = Host.executor_factory


def extend_cmd(cmd, *args):
def _extend_cmd(cmd, sudo, *args):
cmd = list(cmd)
if sudo:
cmd.insert(0, "sudo")

cmd.extend(args)
return list2cmdline(cmd)


def join_cmds(*args):
def extend_cmd(cmd, *args):
return _extend_cmd(cmd, False, *args)


def sudo_extend_cmd(cmd, *args):
return _extend_cmd(cmd, True, *args)


def _join_cmd(sudo, *args):
cmd = []
if sudo:
cmd.append("sudo")

for _cmd in args:
cmd += list(_cmd)
return list2cmdline(cmd)


def join_cmds(*args):
return _join_cmd(False, *args)


def sudo_join_cmds(*args):
return _join_cmd(True, *args)


def teardown_module():
Host.executor_factory = host_executor_factory

Expand All @@ -29,6 +53,7 @@ def fake_cmd_data(cmd_to_data, files=None):
Host.executor_factory = FakeExecutorFactory(cmd_to_data, files)


@pytest.mark.parametrize("sudo", [False, True])
class BasePackageManager(object):
__test__ = False

Expand All @@ -43,7 +68,7 @@ class BasePackageManager(object):
'not_installed': 'p-not-installed',
'non_existing': 'p-non-existing',
'list': 'p-installed-1\np-installed-2\np-installed-3\n',
'pattern': 'p-installed-(1|2)'
'pattern': 'p-installed-(1|2)',
}
rc1 = (1, '', '')
rc0 = (0, '', '')
Expand All @@ -63,7 +88,14 @@ def setup_class(cls):
grep_xargs_command,
cls.managers[cls.manager].remove_command_d
)
sudo_remove_pattern_cmd = sudo_join_cmds(
cls.managers[cls.manager].list_command_d,
grep_xargs_command,
cls.managers[cls.manager].remove_command_d
)
cls.data.update({
remove_pattern_cmd: cls.rc0,
sudo_remove_pattern_cmd: cls.rc0,
extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['installed_1']
Expand Down Expand Up @@ -101,16 +133,63 @@ def setup_class(cls):
cls.managers[cls.manager].remove_command_d,
cls.packages['installed_2']
): cls.rc0,
remove_pattern_cmd: cls.rc0,
extend_cmd(
cls.managers[cls.manager].update_command_d,
cls.packages['installed_1']
): cls.rc0,
list2cmdline(
extend_cmd(
cls.managers[cls.manager].update_command_d,
): cls.rc0,
extend_cmd(
cls.managers[cls.manager].list_command_d,
): (0, cls.packages['list'], ''),
# For sudo tests.
sudo_extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['installed_1']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['installed_2']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['installed_3']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['not_installed']
): cls.rc1,
sudo_extend_cmd(
cls.managers[cls.manager].install_command_d,
cls.packages['not_installed']
): cls.rc0,
# for negative install test
sudo_extend_cmd(
cls.managers[cls.manager].exist_command_d,
cls.packages['non_existing']
): cls.rc1,
sudo_extend_cmd(
cls.managers[cls.manager].install_command_d,
cls.packages['non_existing']
): cls.rc1,
sudo_extend_cmd(
cls.managers[cls.manager].remove_command_d,
cls.packages['installed_1']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].remove_command_d,
cls.packages['installed_2']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].update_command_d,
cls.packages['installed_1']
): cls.rc0,
sudo_extend_cmd(
cls.managers[cls.manager].update_command_d,
): cls.rc0,
list2cmdline(
cls.managers[cls.manager].list_command_d
sudo_extend_cmd(
cls.managers[cls.manager].list_command_d,
): (0, cls.packages['list'], ''),
})
fake_cmd_data(cls.data)
Expand All @@ -121,55 +200,58 @@ def set_base_data(cls):
rc = cls.rc1
if manager_ == cls.manager:
rc = cls.rc0
cls.data.update({
list2cmdline(['which', manager_]): rc,
})

def get_host(self, ip='1.1.1.1'):
for val in (['which'], ['sudo', 'which']):
cls.data.update({
list2cmdline(val + [manager_]): rc,
})

def get_host(self, ip='1.1.1.1', sudo=False):
h = Host(ip)
h.add_user(User('root', '11111'))
h.executor(sudo=sudo)
return h

def get_pm(self):
return self.get_host().package_manager
def get_pm(self, sudo=False):
return self.get_host(sudo=sudo).package_manager

def test_info(self):
assert not self.get_pm().info(self.packages['installed_1'])
def test_info(self, sudo):
assert not self.get_pm(sudo).info(self.packages['installed_1'])

def test_info_negative(self):
assert not self.get_pm().info(self.packages['not_installed'])
def test_info_negative(self, sudo):
assert not self.get_pm(sudo).info(self.packages['not_installed'])

def test_exist(self):
assert self.get_pm().exist(self.packages['installed_1'])
def test_exist(self, sudo):
assert self.get_pm(sudo).exist(self.packages['installed_1'])

def test_exist_negative(self):
assert not self.get_pm().exist(self.packages['not_installed'])
def test_exist_negative(self, sudo):
assert not self.get_pm(sudo).exist(self.packages['not_installed'])

def test_install_installed(self):
assert self.get_pm().install(self.packages['installed_1'])
def test_install_installed(self, sudo):
assert self.get_pm(sudo).install(self.packages['installed_1'])

def test_install_new(self):
assert self.get_pm().install(self.packages['not_installed'])
def test_install_new(self, sudo):
assert self.get_pm(sudo).install(self.packages['not_installed'])

def test_install_negative(self):
assert not self.get_pm().install(self.packages['non_existing'])
def test_install_negative(self, sudo):
assert not self.get_pm(sudo).install(self.packages['non_existing'])

def test_remove(self):
assert self.get_pm().remove(self.packages['installed_1'])
def test_remove(self, sudo):
assert self.get_pm(sudo).remove(self.packages['installed_1'])

def test_remove_pattern(self):
def test_remove_pattern(self, sudo):
assert (
self.get_pm().remove(self.packages['pattern'], pattern=True)
self.get_pm(sudo).remove(self.packages['pattern'], pattern=True)
)

def test_update(self):
assert self.get_pm().update([self.packages['installed_1']])
def test_update(self, sudo):
assert self.get_pm(sudo).update([self.packages['installed_1']])

def test_update_all(self):
assert self.get_pm().update()
def test_update_all(self, sudo):
assert self.get_pm(sudo).update()

def test_list(self):
assert self.get_pm().list_() == self.packages['list'].split('\n')
def test_list(self, sudo):
assert self.get_pm(sudo).list_() == self.packages['list'].split('\n')


class TestYumPM(BasePackageManager):
Expand Down

0 comments on commit 14ae0c9

Please sign in to comment.