Skip to content

Commit

Permalink
set run environment variables feature, use_sudo feature
Browse files Browse the repository at this point in the history
  • Loading branch information
a1fred committed Dec 8, 2021
1 parent 575d5e1 commit 79b1baa
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 37 deletions.
23 changes: 17 additions & 6 deletions carnival/contrib/steps/apt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ def get_validators(self) -> typing.List[validators.StepValidatorBase]:
]

def run(self, c: Connection) -> None:
c.run("DEBIAN_FRONTEND=noninteractive sudo apt-get update", hide=True)
c.run(
"apt-get update",
hide=True,
env={"DEBIAN_FRONTEND": "noninteractive"},
)
print(f"{S.BRIGHT}apt packages list{S.RESET_ALL}: {F.YELLOW}updated{F.RESET}")


Expand All @@ -37,7 +41,12 @@ def get_validators(self) -> typing.List[validators.StepValidatorBase]:

def run(self, c: Connection) -> typing.List[str]:
versions = []
result = c.run(f"DEBIAN_FRONTEND=noninteractive apt-cache madison {self.pkgname}", hide=True, warn=True)
result = c.run(
f"apt-cache madison {self.pkgname}",
env={"DEBIAN_FRONTEND": "noninteractive"},
hide=True,
warn=True
)
if result.ok is False:
return []

Expand Down Expand Up @@ -67,7 +76,8 @@ def run(self, c: Connection) -> typing.Optional[str]:
:return: Версия пакета если установлен, `None` если пакет не установлен
"""
result = c.run(
f"DEBIAN_FRONTEND=noninteractive dpkg -l {self.pkgname} | grep '{self.pkgname}'",
f"dpkg -l {self.pkgname} | grep '{self.pkgname}'",
env={"DEBIAN_FRONTEND": "noninteractive"},
hide=True,
warn=True,
)
Expand Down Expand Up @@ -135,7 +145,7 @@ def run(self, c: Connection) -> None:
if self.update:
Update().run(c)

c.run(f"DEBIAN_FRONTEND=noninteractive sudo apt-get install -y {pkgname}")
c.run(f"apt-get install -y {pkgname}", env={"DEBIAN_FRONTEND": "noninteractive"})


class Install(Step):
Expand Down Expand Up @@ -214,7 +224,7 @@ def run(self, c: Connection) -> bool:
return False

if self.update:
c.run("DEBIAN_FRONTEND=noninteractive sudo apt-get update", hide=True)
c.run("apt-get update", hide=True, env={"DEBIAN_FRONTEND": "noninteractive"})

for pkg in self.pkg_names:
Install(pkgname=pkg, update=False).run(c=c)
Expand Down Expand Up @@ -245,6 +255,7 @@ def run(self, c: Connection) -> None:
for pkg in self.pkg_names:
if IsPackageInstalled(pkg).run(c):
c.run(
f"DEBIAN_FRONTEND=noninteractive sudo apt-get remove --auto-remove -y {' '.join(self.pkg_names)}",
f"apt-get remove --auto-remove -y {' '.join(self.pkg_names)}",
env={"DEBIAN_FRONTEND": "noninteractive"},
)
print(f"{S.BRIGHT}{pkg}{S.RESET_ALL}: {F.YELLOW}removed{F.RESET}")
4 changes: 2 additions & 2 deletions carnival/contrib/steps/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def run(self, c: Connection) -> None:
return

link = f"https://github.com/docker/compose/releases/download/{self.version}/docker-compose-`uname -s`-`uname -m`" # noqa:501
c.run(f"sudo curl -sL {link} -o {self.dest}")
c.run(f"sudo chmod a+x {self.dest}")
c.run(f"curl -sL {link} -o {self.dest}")
c.run(f"chmod a+x {self.dest}")
print(f"{S.BRIGHT}docker-compose{S.RESET_ALL}: {F.GREEN}already installed{F.RESET}")


Expand Down
12 changes: 6 additions & 6 deletions carnival/contrib/steps/systemd.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class DaemonReload(Step):
"""

def run(self, c: Connection) -> None:
c.run("sudo systemctl --system daemon-reload")
c.run("systemctl --system daemon-reload")


class Start(Step):
Expand All @@ -27,7 +27,7 @@ def run(self, c: Connection) -> None:
if self.reload_daemon:
DaemonReload().run(c=c)

c.run(f"sudo systemctl start {self.service_name}")
c.run(f"systemctl start {self.service_name}")


class Stop(Step):
Expand All @@ -47,7 +47,7 @@ def run(self, c: Connection) -> None:
if self.reload_daemon:
DaemonReload().run(c=c)

c.run(f"sudo systemctl stop {self.service_name}")
c.run(f"systemctl stop {self.service_name}")


class Restart(Step):
Expand All @@ -62,7 +62,7 @@ def __init__(self, service_name: str) -> None:
self.service_name = service_name

def run(self, c: Connection) -> None:
c.run(f"sudo systemctl restart {self.service_name}")
c.run(f"systemctl restart {self.service_name}")


class Enable(Step):
Expand All @@ -84,7 +84,7 @@ def run(self, c: Connection) -> None:
if self.reload_daemon:
DaemonReload().run(c=c)

c.run(f"sudo systemctl enable {self.service_name}")
c.run(f"systemctl enable {self.service_name}")

if self.start_now:
Start(self.service_name).run(c=c)
Expand All @@ -110,7 +110,7 @@ def run(self, c: Connection) -> None:
if self.reload_daemon:
DaemonReload().run(c=c)

c.run(f"sudo systemctl disable {self.service_name}")
c.run(f"systemctl disable {self.service_name}")

if self.stop_now:
Stop(self.service_name).run(c=c)
42 changes: 30 additions & 12 deletions carnival/hosts/base/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ class Connection:
Хост с которым связан конект
"""

def __init__(self, host: "Host") -> None:
def __init__(self, host: "Host", use_sudo: bool = False) -> None:
"""
Конекст с хостом, все конекты являются контекст-менеджерами
>>> with host.connect() as c:
>>> c.run("ls -1")
:param host: хост с которым связано соединение
:param use_sudo: использовать sudo для выполнения команд
"""
self.host = host
self.use_sudo = use_sudo

def __enter__(self) -> "Connection":
raise NotImplementedError
Expand All @@ -34,33 +37,45 @@ def __exit__(self, *args: typing.Any) -> None:

@abc.abstractmethod
def run_promise(
self,
command: str,
cwd: typing.Optional[str] = None,
timeout: int = 60,
self,
command: str,
use_sudo: bool,
env: typing.Optional[typing.Dict[str, str]] = None,
cwd: typing.Optional[str] = None,
timeout: int = 60,
) -> ResultPromise:
raise NotImplementedError

def run(
self,
command: str,
hide: bool = True,
warn: bool = False,
cwd: typing.Optional[str] = None,
timeout: int = 60,
self,
command: str,
use_sudo: typing.Optional[bool] = None,
env: typing.Optional[typing.Dict[str, str]] = None,
hide: bool = True,
warn: bool = False,
cwd: typing.Optional[str] = None,
timeout: int = 60,
) -> Result:
"""
Запустить команду
:param command: Команда для запуска
:param use_sudo: использовать sudo для выполнения команды, если не задано используется значение `self.use_sudo`
:param env: задать переменные окружения для команды
:param hide: Скрыть вывод команды
:param warn: Вывести stderr
:param cwd: Перейти в папку при выполнении команды
:param timeout: таймаут выполнения команды
"""

if use_sudo is None:
use_sudo = self.use_sudo

result = self.run_promise(
command=command,
env=env,
cwd=cwd,
use_sudo=use_sudo,
timeout=timeout,
).get_result(hide=hide)
result.check_result(warn=warn)
Expand All @@ -69,7 +84,8 @@ def run(
@abc.abstractmethod
def file_stat(self, path: str) -> StatResult:
"""
Получить fstat файла
Получить fstat файла,
не поддерживает `use_sudo`
:param path: путь до файла
"""
Expand All @@ -78,6 +94,7 @@ def file_stat(self, path: str) -> StatResult:
def file_read(self, path: str) -> typing.ContextManager[typing.IO[bytes]]:
"""
Открыть файл на чтение
не поддерживает `use_sudo`
:param path: путь до файла
:return: дескриптор файла
Expand All @@ -87,6 +104,7 @@ def file_read(self, path: str) -> typing.ContextManager[typing.IO[bytes]]:
def file_write(self, path: str) -> typing.ContextManager[typing.IO[bytes]]:
"""
Открыть файл на запись
не поддерживает `use_sudo`
:param path: путь до файла
:return: дескриптор файла
Expand Down
6 changes: 6 additions & 0 deletions carnival/hosts/base/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class Host:
Адрес хоста
"""

def __init__(self, use_sudo: bool = False):
"""
:param use_sudo: использовать sudo для выполнения команд
"""
self.use_sudo = use_sudo

@property
def ip(self) -> str:
# Maybe self.addr is ip?
Expand Down
4 changes: 4 additions & 0 deletions carnival/hosts/local/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ def __exit__(self, *args: typing.Any) -> None:
def run_promise(
self,
command: str,
use_sudo: bool,
env: typing.Optional[typing.Dict[str, str]] = None,
cwd: typing.Optional[str] = None,
timeout: int = 60,
) -> LocalResultPromise:
return LocalResultPromise(
command=command,
cwd=cwd,
env=env,
use_sudo=use_sudo,
timeout=timeout,
)

Expand Down
24 changes: 19 additions & 5 deletions carnival/hosts/local/result_promise.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typing
import os
from subprocess import Popen, PIPE

from carnival.hosts.base.result_promise import ResultPromise
Expand All @@ -7,12 +8,25 @@

class LocalResultPromise(ResultPromise):
def __init__(
self,
command: str,
timeout: int,
cwd: typing.Optional[str]
self,
command: str,
timeout: int,
cwd: typing.Optional[str],
use_sudo: bool,
env: typing.Optional[typing.Dict[str, str]] = None,
):
self.proc = Popen(command, shell=True, stderr=PIPE, stdin=PIPE, stdout=PIPE, cwd=cwd)
proc_env = os.environ.copy()
if env is not None:
proc_env.update(env)

if use_sudo is True:
command = f"sudo -n -- sh -c '{command}'"

self.proc = Popen(
command, shell=True,
stderr=PIPE, stdin=PIPE, stdout=PIPE, cwd=cwd,
env=proc_env,
)
self.command = command
assert self.proc.stdout is not None
assert self.proc.stderr is not None
Expand Down
4 changes: 4 additions & 0 deletions carnival/hosts/ssh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(
self,
addr: str,
port: int = SSH_PORT,
use_sudo: bool = False,

user: typing.Optional[str] = None,
password: typing.Optional[str] = None,
Expand All @@ -38,6 +39,8 @@ def __init__(
:param gateway: Gateway
:param connect_timeout: SSH таймаут соединения
"""
super(SshHost, self).__init__(use_sudo=use_sudo)

if ":" in addr:
raise ValueError("Please set port in 'ssh_port' arg")
if "@" in addr:
Expand Down Expand Up @@ -70,4 +73,5 @@ def connect(self) -> SshConnection:
return SshConnection(
host=self,
conf=self.connect_config,
use_sudo=self.use_sudo,
)
15 changes: 10 additions & 5 deletions carnival/hosts/ssh/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ def __init__(
self,
host: "SshHost",
conf: HostnameConfig,
use_sudo: bool = False,
) -> None:
super().__init__(host)
super().__init__(host=host, use_sudo=use_sudo)
self.host: "SshHost" = host
self.conf = conf
self.conn: typing.Optional[SSHClient] = None
Expand All @@ -40,17 +41,21 @@ def _ensure_connection(self) -> None:
self.conn = self.conf.connect()

def run_promise(
self,
command: str,
cwd: typing.Optional[str] = None,
timeout: int = 60,
self,
command: str,
use_sudo: bool,
env: typing.Optional[typing.Dict[str, str]] = None,
cwd: typing.Optional[str] = None,
timeout: int = 60,
) -> ResultPromise:
self._ensure_connection()
assert self.conn is not None, "Connection is not opened"
return SshResultPromise(
conn=self.conn,
command=command,
env=env,
cwd=cwd,
use_sudo=use_sudo,
timeout=timeout,
)

Expand Down
15 changes: 14 additions & 1 deletion carnival/hosts/ssh/result_promise.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,30 @@


class SshResultPromise(ResultPromise):
def __init__(self, conn: SSHClient, command: str, cwd: typing.Optional[str], timeout: int):
def __init__(
self,
conn: SSHClient,
command: str,
cwd: typing.Optional[str],
timeout: int,
use_sudo: bool,
env: typing.Optional[typing.Dict[str, str]] = None,
):
self.command = command
self.timeout = timeout
self.conn = conn

if cwd is not None:
command = f"cd {cwd}; {command}"

if use_sudo is True:
command = f"sudo -n -- sh -c '{command}'"

# https://stackoverflow.com/questions/39429680/python-paramiko-redirecting-stderr-is-affected-by-get-pty-true
_, stdout, stderr = self.conn.exec_command(
command,
timeout=timeout,
environment=env,
get_pty=True, # Combines stdout and stderr, we dont want it
)
self.stdout_channel = stdout.channel
Expand Down

0 comments on commit 79b1baa

Please sign in to comment.