Развертывание сервера с Python: от А до Я.
В этом руководстве я покажу, как настроить сервер для запуска веб-приложения без каких-либо других инструментов, кроме Python.
По окончании у вас будет:
- Узнал, какие компоненты конфигурации сервера для веб-развертывания.
- Получил воспроизводимый шаблон Python через GitHub gists.
Фон
В те времена, когда Docker не существовало, я настраивал серверы в облачной среде с помощью кода Python. По сути, это был сценарий / проект Python, который я запускал на своем (локальном) компьютере, и он выполнял команды на (удаленном) сервере.
Я давно не использовал этот проект шахт, но наткнулся на него на прошлой неделе и понял две вещи:
- Сегодня мне не понравилось бы его использовать. Докер намного удобнее.
- Однако понимание этого кода дает отличное представление о том, как настраивать удаленные серверы!
Фактически, причина, по которой я написал это, заключается в том, что даже раньше я настраивал все серверы вручную: вход через ssh, установка необходимых пакетов и т. Д.
Проделав это несколько раз, я решил автоматизировать процедуру . Вот почему я построил проект, который собираюсь вам показать.
Это означает, что код выделяет детали всего, что вы должны делать на сервере, в отличие от использования Docker, где большинство деталей скрыто.
Таким образом, этот пост может быть полезен по двум причинам:
- Если вы не хотите или не можете, используйте Docker.
- Это даст вам довольно хорошее представление о том, что происходит на удаленной машине! Знания всегда имеют значение.
Обзор учебного пособия
Код организован в виде функций, каждая из которых выполняет задачу на сервере, даже если она выполняется на локальном компьютере.
Поэтому, прежде чем смотреть на код, стоит подумать о том, какие шаги вам нужно будет выполнить (вручную), если вы не читали это руководство.
Фактически, ручное развертывание на обычном сервере является обязательным для разработчика . Хотя бы раз в жизни разверните обычную машину у любого облачного провайдера (GCP, AWS, Linode, Digital Ocean, Azure и т. Д.) И выполните полноценное производственное развертывание. Хотя бы один раз!!
Вы многому научитесь, выполняя это вручную. Давайте сначала посмотрим на обзор каждого отдельного шага, а затем снова посмотрим на каждый из них вместе с кодом.
Шаг 1 – Базовая конфигурация машины
Во-первых, вы должны обновить все пакеты. Когда вы создаете новый экземпляр, он обычно запускает немного устаревшую версию операционной системы, поэтому вам необходимо обновить пакеты. Например, в Debian / Ubuntu это делается с помощью apt-get update
.
После этого вам нужно будет установить все пакеты, относящиеся к вашему проекту. В коде этого руководства я установлю множество пакетов, в том числе postgresql-server
позволяющих запускать веб-приложение, использующее PostgreSQL в качестве серверной части.
Затем, поскольку вы хотите запустить приложение Python, вам необходимо настроить Python на сервере.
Большинство операционных систем поставляются с предустановленным двоичным кодом Python по умолчанию. Однако, на мой взгляд, это не очень хорошее решение.
Причина в том, что вы, возможно, разработали свое приложение с Python3.7, но тогда на сервере установлен Python3.9. Все рухнет, и будет трудно понять, почему.
Вот почему в моем коде также есть конкретные инструкции по настройке правильной версии Python в 3 этапа.
- Загрузите исходный код Python.
- Скомпилируйте это.
- Свяжите его как исполняемый файл на машине.
Наконец, как и в случае с большинством приложений Python, вам нужно создать файл virtualenv
только для этого приложения.
Если у вашего приложения есть выделенный сервер, то есть это единственное работающее на нем программное обеспечение, вы можете пропустить эту часть. В любом случае мой код содержит функцию также для установки virtualenvwrapper
и настройки виртуального окружения, предназначенного для этого приложения.
Это конец первого шага.
Шаг 2 – Установите ваше приложение
Чтобы сервер запускал ваше приложение, он должен быть установлен! Если ваше приложение представляет собой исполняемый файл, который можно установить, вы должны загрузить его (через curl
или wget
) или скопировать с локального компьютера на удаленный сервер (через scp
).
Более распространенный случай с приложениями Python, по крайней мере для меня, – это когда для запуска приложения нам нужен исходный код. В этом случае вам необходимо разместить исходный код на удаленном сервере. Есть два распространенных способа сделать это:
- Используя
git clone
, чтобы вы могли взять исходный код из онлайн-репозитория git. - Используя
scp
, чтобы вы скопировали исходный код с локального компьютера на удаленный.
В моем коде я буду использовать вариант 1, поэтому я могу показать вам хороший способ, который я нашел тогда, чтобы написать код, который переключается между ветвями и фиксирует в репозитории (например, если вы хотите выполнить откат).
После этого, если у вас есть requirements.txt
файл, как в большинстве проектов Python, вам нужно будет установить все эти модули Python в файле virtualenv . Я покажу, как это сделать через минуту.
Шаг 3 – Запустите приложение!
Это самый простой этап, особенно если вы используете надежный и готовый к работе веб-сервер. Я выбрал Gunicorn, но есть много альтернатив, которые одинаково просты в использовании.
Это три шага, которые вы должны пройти на высоком уровне. Теперь я хочу вместе с вами взглянуть на них более внимательно, но есть одна недостающая деталь: как мы можем отправлять команды для выполнения на сервер, не выполняя вход вручную через ssh?
Настройка подключения
К счастью для нас, fabric
эту проблему решает пакет Python . Все, что вам нужно, это такая функция, которая создает соединение с сервером:
from os import environ
from fabric import Connection
def create_conn():
# Switch the two lines if you connect via PEM Key
# instead of password.
params = {
#'key_filename': environ['SSH_KEY_PATH']}
'password': environ['REMOTE_PASSWORD']
}
conn = Connection(
host=environ['REMOTE_HOST'],
user=environ['REMOTE_USER'],
connect_kwargs=params,
)
return conn
Объект подключения, возвращаемый этой функцией, будет использоваться во всем остальном коде.
Часть 1 в деталях
В части 1 мы хотим настроить сервер на самом базовом уровне: библиотеки ОС, языки программирования и т. Д.
Если бы мы делали это вручную, мы бы использовали ssh-in, а затем запустили бы несколько ap-get install ...
из оболочки.
Что ж, мы можем сделать то же самое в коде Python, благодаря объекту подключения. Вот код.
def _create_vm(conn):
_install_packages(conn)
_install_python(conn)
_install_venv(conn)
def _install_packages(conn):
conn.sudo('apt-get -y update')
conn.sudo('apt-get -y upgrade')
conn.sudo('apt-get install -y build-essential')
#conn.sudo('apt-get install -y checkinstall')
conn.sudo('apt-get install -y libreadline-gplv2-dev')
conn.sudo('apt-get install -y libncurses-dev')
conn.sudo('apt-get install -y libncursesw5-dev')
conn.sudo('apt-get install -y libssl-dev')
conn.sudo('apt-get install -y libsqlite3-dev')
conn.sudo('apt-get install -y tk-dev')
conn.sudo('apt-get install -y libgdbm-dev')
conn.sudo('apt-get install -y libpq-dev')
conn.sudo('apt-get install -y libc6-dev')
conn.sudo('apt-get install -y libbz2-dev')
conn.sudo('apt-get install -y zlib1g-dev')
conn.sudo('apt-get install -y openssl')
conn.sudo('apt-get install -y libffi-dev')
conn.sudo('apt-get install -y python3-dev')
conn.sudo('apt-get install -y python3-setuptools')
conn.sudo('apt-get install -y uuid-dev')
conn.sudo('apt-get install -y lzma-dev')
conn.sudo('apt-get install -y wget')
conn.sudo('apt-get install -y git')
conn.sudo('apt-get install -y postgresql')
def _install_python(conn):
"""Install python 3.7 in the remote machine."""
res = conn.run('python3 --version')
if '3.7' in res.stdout.strip():
# Python >= 3.7 already exists
return
conn.run('rm -rf /tmp/Python3.7 && mkdir /tmp/Python3.7')
with conn.cd('/tmp/Python3.7'):
conn.run('wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz')
conn.run('tar xvf Python-3.7.0.tar.xz')
with conn.cd ('/tmp/Python3.7/Python-3.7.0'):
conn.run('./configure --enable-optimizations')
conn.run('make')
# see https://github.com/pyinvoke/invoke/issues/459
conn.sudo('bash -c "cd /tmp/Python3.7/Python-3.7.0 && make altinstall"')
def _install_venv(conn):
"""Install virtualenv, virtualenvwrapper."""
res = conn.run('which python3.7')
res = res.stdout.strip()
py_path = res
conn.sudo('apt install -y virtualenvwrapper')
# for a standard Debian distro
venv_sh = '/usr/share/virtualenvwrapper/virtualenvwrapper.sh'
conn.run('echo >> ~/.bashrc') # new line
conn.run(f'echo source {venv_sh} >> ~/.bashrc')
conn.run('echo >> ~/.bashrc') # new line
conn.run('echo export LC_ALL=en_US.UTF-8 >> ~/.bashrc')
conn.run('source ~/.bashrc')
env = environ['VENV_NAME']
with conn.prefix(f'source {venv_sh}'):
conn.run(f'mkvirtualenv -p {py_path} {env}')
В приведенном выше коде реализованы три подэтапа, о которых я говорил ранее:
- Установите все библиотеки, которые нам нужны на уровне ОС (в этом случае есть также
git
иpostgresql-server
среди многих других). - Установите нужную нам версию Python, скомпилировав ее из исходного кода.
- Установить
virtualenvwrapper
. Фактически, вы можете использовать любое управление программным обеспечением virtualenv или ничего, если машина предназначена только для одного приложения Python.
Если вы внимательно посмотрите на код, вы поймете шаблон программирования за одну секунду: каждая задача выполняется путем создания объекта подключения и использования его .run()
метода с аргументом той же команды, которую вы запускали вручную.
Это еще одна причина, по которой я сказал, что очень важно выполнить развертывание вручную хотя бы один раз!
Часть 2 в деталях
Во второй части я сказал, что мы хотим сделать две вещи:
- Получите исходный код приложения на машине.
- Установите все требования Python.
Посмотрим на первый. В следующей функции я просто воспользуюсь некоторыми git
командами, чтобы убедиться, что код извлечен из репозитория на машине.
На первый взгляд функция может показаться немного сложной, но это не так. Я написал общую функцию, которая позволяет вам проверять конкретную ветку по имени или конкретную фиксацию по ее хешу, и поэтому функция выглядит сложной.
Но вы можете упростить это и всегда делать что-то подобное git pull origin master
, и это все равно будет работать!
def _pull_repo(conn, branch=None, commit=None):
if branch and commit:
raise ValueError('Cannot provide both branch name and commit hash')
source = environ['GIT_DIR']
if not branch:
branch = environ['GIT_DEFAULT_BRANCH']
repo = environ['REPO_URL']
if commit:
print('Hash provided. Resetting to that commit.')
conn.run(
f"cd {source} && "
'git stash && '
f'git reset --hard {commit} && '
'git checkout -B tmp_branch'
)
else:
if conn.run(f'test -e {source}/.git', warn=True).ok:
print('Repo already exists.')
else:
print('Repo did not exist. Creating it...')
conn.run(f'git clone {repo} {source}')
conn.run(f'cd {source} && git remote set-url origin {repo}')
print('Checking out the requested branch...')
conn.run(f'cd {source} && git fetch origin && git checkout {branch} && git pull origin {branch}')
current_hash = conn.run(f'cd {source} && git log -n 1 --format=%H', hide='both')
current_hash = current_hash.stdout.strip()
print(f'Checked out {current_hash}')
return current_hash
Вы, наверное, заметили, что некоторые переменные загружаются из environ
. Я доберусь до этого через минуту, но в основном причина в том, что вы можете захотеть сохранить в секрете имя репозитория и учетные данные пользователя, поэтому лучше не использовать их непосредственно в коде. В любом случае, потерпите меня минутку, и эта часть будет ясна.
Вторая часть, установить требования Python намного проще. Единственная уловка здесь заключается в том, что я использую conn.cd()
и conn.prefix()
активирую virtualenv перед установкой требований. В остальном основная команда точно такая же, как если бы вы выполняли ее вручную: pip install -r requirements.tx
def _install_project(conn):
repo_path = environ['GIT_DIR']
venv_name = environ['VENV_NAME']
venv_sh = 'source /usr/share/virtualenvwrapper/virtualenvwrapper.sh'
with conn.cd(repo_path):
with conn.prefix(
f'{venv_sh} && workon {venv_name}'
):
conn.run('pip install --upgrade pip')
conn.run('pip install -r requirements.txt')
# If your project as a `setup.py` then
# install project.
#conn.run('pip install -e .')
Часть 3 в деталях
Часть 3 является самым простым, потому что я использую Gunicorn , как производство веб – сервера и запустить его нужно просто запустить простую строку: gunicorn <app_module_path>
.
def _restart_web(conn):
try:
conn.sudo('pkill gunicorn')
except:
pass # may not be running at all.
repo_path = environ['GIT_DIR']
venv_name = environ['VENV_NAME']
venv_sh = 'source /usr/share/virtualenvwrapper/virtualenvwrapper.sh'
with conn.cd(repo_path):
with conn.prefix(
f'{venv_sh} && workon {venv_name}'
):
conn.run("gunicorn app:app -b 0.0.0.0:8080 -w 3 --daemon")
Хорошо, хорошо … Я делаю еще несколько вещей в коде:
- Сначала я останавливаю процесс,
gunicorn
если он уже запущен. Это вызовет некоторое время простоя в приложении. - Затем я использую некоторые аргументы конфигурации для нового
gunicorn
процесса, чтобы убедиться, что он работает правильно:-b
он привязывает его к нужному мне порту (8080 в этом примере);-w
указывает количество рабочих (процессов);--daemon
запускает его в фоновом режиме, так что вам не нужно держать соединение открытым.
Вот и все! Последнее, на что нам нужно обратить внимание, это как загрузить переменные среды.
Есть масса способов сделать это. В этом проекте я решил создать файл, secret.py
который определяет переменные, используя, os.environ[..] = ..
а затем выполняя их import secret
из основного файла.
Для проверки концепций я искал только готовое к использованию приложение Flask. Я хотел использовать приложение, разработанное НЕ мной, чтобы показать вам, насколько гибкий этот код.
Я нашел образец приложения, опубликованный на GitHub компанией Digital Ocean. Я не имею к ним никакого отношения, но это выглядело хорошо для этого проекта, поэтому вы видите это в secret.py
файле. Вот:
# File secret.py
from os import environ, path
### Connection
environ['REMOTE_HOST'] = '172.104.239.248'
environ['REMOTE_USER'] = '****'
environ['REMOTE_PASSWORD'] = '********+'
#
## Python venv
environ['VENV_NAME'] = 'prod-api'
#
### Git
environ['GIT_DIR'] = '~/app'
environ['GIT_DEFAULT_BRANCH'] = 'main'
environ['REPO_URL'] = 'https://github.com/digitalocean/sample-flask.git'
В основном файле, который вы можете найти в этой сущности , я просто делаю, import secret
и все переменные среды загружаются. Два файла main.py
и secret.py
должны находиться в одном каталоге.
Последний трюк!
Код появился несколько лет назад, но есть еще один трюк, который я реализовал и которым я хочу с вами поделиться.
Мотивация в том, что иногда мне захочется запустить одну из функций, которые мы видели, и только одну. И иногда им могут потребоваться аргументы во вводе (это так _pull_repo
).
Это означает, что я хочу иметь __main__
точку входа, которая запускает только функцию, которую я хочу запустить в этот конкретный момент, а также передает ей любой аргумент, поступающий из командной строки.
Вот что я придумал два года назад.
def main(tasks):
if len(tasks) <= 1:
print('No task name found')
return
i = 1
while i < len(tasks):
try:
fn = getattr(sys.modules[__name__], tasks[i])
except AttributeError:
print(f'Cannot find task {tasks[i]}. Quit.')
return
params = {}
j = i + 1
while j < len(tasks) and '=' in tasks[j]:
k, v = tasks[j].split('=')
params[k] = v
j += 1
i = j
print(f'Function is {fn}')
print(f'args are {params}')
fn(**params)
if __name__ == '__main__':
'''
Run it with
$ python main <task1> <key1-task1>=<value1-task1> <key2-task1>=<value2-task2> <task2> <key1-task2>=<value1-task2>
E.g.
$ python main create_vm
Or
$ python main pull_repo branch=develop
'''
import sys
tasks = sys.argv
main(tasks)
Давай проверим!
Пора протестировать. Заранее хочу сказать, что результат хороший: я был очень рад, что мой код работал отлично, даже если я не использовал его какое-то время!
Вот что я сделал.
Сначала я создал небольшую машину на Линоде. Я не связан с ними, я просто хотел использовать другого провайдера, так как код приложения принадлежит Digital Ocean.
Самая маленькая машина на Linode стоит 5 долларов (у других поставщиков такая же цена), а весь тест длился менее 5 минут, поэтому я потратил всего несколько центов. Я выбрал Debian 10 в качестве ОС.
Затем я скопировал хост, имя пользователя и пароль root из консоли Linode в secret.py
файл.
Затем я загрузил GitHug gist, сохранив то же имя файла azPyDeploy.py
в том же каталоге что и secret.py
.
И, наконец, я выполнил четыре простых команды.
python azPyDepl.py create_vm
python azPyDepl.py pull_repo
python azPyDepl.py install_project
python azPyDepl.py restart_web
Замечательно! Приложение Flask, развернутое исключительно через Python. Я должен сказать, что большое спасибо Fabric
разработчикам.
Затем я отключил сервер (так что IP, который вы видите на рисунке, больше не существует), чтобы не тратить больше $$.
Заключительные комментарии и код
Я знаю, что это не похоже на развертывание производственной среды сегодня. Даже я использую Docker все на время и некоторые более продвинутые системы инфраструктуры , предоставляемой AWS.
Если по какой-то причине вы не можете использовать больше автоматизированных систем, тогда проект из этого руководства может быть вам полезен.
Но в целом главное преимущество этого кода для меня – это ценность процесса обучения . Понимание серверов и того, как все работает за кулисами, было очень важно в моей профессиональной карьере, и я надеюсь, что этот пост пролит некоторый свет и поможет вам тоже!
Вот полный код, если суть не работает.
Сообщите мне, если вы столкнулись с какой-либо проблемой при воспроизведении этого руководства.
# This script needs
# $ pip install fabric
from os import environ
from fabric import Connection
# Create a file `secret.py` in the same directory as this one
# and add in it the credentials to connect to the server and GitHub.
# Here is a template.
#############
# File secret.py
#from os import environ, path
#
### Connection
#environ['REMOTE_HOST'] = '172.104.239.248'
#environ['REMOTE_USER'] = '****'
#environ['REMOTE_PASSWORD'] = '********'
#
## Python venv
#environ['VENV_NAME'] = 'prod-api'
#
### Git
#environ['GIT_DIR'] = '~/app'
#environ['GIT_DEFAULT_BRANCH'] = 'main'
#environ['REPO_URL'] = 'https://github.com/digitalocean/sample-flask.git'
#############
import secret
def create_conn():
# Switch the two lines if you connect via PEM Key
# instead of password.
params = {
#'key_filename': environ['SSH_KEY_PATH']}
'password': environ['REMOTE_PASSWORD']
}
conn = Connection(
host=environ['REMOTE_HOST'],
user=environ['REMOTE_USER'],
connect_kwargs=params,
)
return conn
######################
# Internal Functions #
######################
def _create_vm(conn):
_install_packages(conn)
_install_python(conn)
_install_venv(conn)
def _install_packages(conn):
conn.sudo('apt-get -y update')
conn.sudo('apt-get -y upgrade')
conn.sudo('apt-get install -y build-essential')
#conn.sudo('apt-get install -y checkinstall')
conn.sudo('apt-get install -y libreadline-gplv2-dev')
conn.sudo('apt-get install -y libncurses-dev')
conn.sudo('apt-get install -y libncursesw5-dev')
conn.sudo('apt-get install -y libssl-dev')
conn.sudo('apt-get install -y libsqlite3-dev')
conn.sudo('apt-get install -y tk-dev')
conn.sudo('apt-get install -y libgdbm-dev')
conn.sudo('apt-get install -y libpq-dev')
conn.sudo('apt-get install -y libc6-dev')
conn.sudo('apt-get install -y libbz2-dev')
conn.sudo('apt-get install -y zlib1g-dev')
conn.sudo('apt-get install -y openssl')
conn.sudo('apt-get install -y libffi-dev')
conn.sudo('apt-get install -y python3-dev')
conn.sudo('apt-get install -y python3-setuptools')
conn.sudo('apt-get install -y uuid-dev')
conn.sudo('apt-get install -y lzma-dev')
conn.sudo('apt-get install -y wget')
conn.sudo('apt-get install -y git')
conn.sudo('apt-get install -y postgresql')
def _install_python(conn):
"""Install python 3.7 in the remote machine."""
res = conn.run('python3 --version')
if '3.7' in res.stdout.strip():
# Python >= 3.7 already exists
return
conn.run('rm -rf /tmp/Python3.7 && mkdir /tmp/Python3.7')
with conn.cd('/tmp/Python3.7'):
conn.run('wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz')
conn.run('tar xvf Python-3.7.0.tar.xz')
with conn.cd ('/tmp/Python3.7/Python-3.7.0'):
conn.run('./configure --enable-optimizations')
conn.run('make')
# see https://github.com/pyinvoke/invoke/issues/459
conn.sudo('bash -c "cd /tmp/Python3.7/Python-3.7.0 && make altinstall"')
def _install_venv(conn):
"""Install virtualenv, virtualenvwrapper."""
res = conn.run('which python3.7')
res = res.stdout.strip()
py_path = res
conn.sudo('apt install -y virtualenvwrapper')
# for a standard Debian distro
venv_sh = '/usr/share/virtualenvwrapper/virtualenvwrapper.sh'
conn.run('echo >> ~/.bashrc') # new line
conn.run(f'echo source {venv_sh} >> ~/.bashrc')
conn.run('echo >> ~/.bashrc') # new line
conn.run('echo export LC_ALL=en_US.UTF-8 >> ~/.bashrc')
conn.run('source ~/.bashrc')
env = environ['VENV_NAME']
with conn.prefix(f'source {venv_sh}'):
conn.run(f'mkvirtualenv -p {py_path} {env}')
def _pull_repo(conn, branch=None, commit=None):
if branch and commit:
raise ValueError('Cannot provide both branch name and commit hash')
source = environ['GIT_DIR']
if not branch:
branch = environ['GIT_DEFAULT_BRANCH']
repo = environ['REPO_URL']
if commit:
print('Hash provided. Resetting to that commit.')
conn.run(
f"cd {source} && "
'git stash && '
f'git reset --hard {commit} && '
'git checkout -B tmp_branch'
)
else:
if conn.run(f'test -e {source}/.git', warn=True).ok:
print('Repo already exists.')
# run("cd %s && git pull upstream %s" % (source_dir, branch))
#conn.run(f'cd {source} && git fetch origin {branch}')
#conn.run(f'cd {source} && git reset --hard origin/{branch}')
else:
print('Repo did not exist. Creating it...')
conn.run(f'git clone {repo} {source}')
conn.run(f'cd {source} && git remote set-url origin {repo}')
print('Checking out the requested branch...')
conn.run(f'cd {source} && git fetch origin && git checkout {branch} && git pull origin {branch}')
current_hash = conn.run(f'cd {source} && git log -n 1 --format=%H', hide='both')
current_hash = current_hash.stdout.strip()
print(f'Checked out {current_hash}')
return current_hash
def _install_project(conn):
repo_path = environ['GIT_DIR']
venv_name = environ['VENV_NAME']
venv_sh = 'source /usr/share/virtualenvwrapper/virtualenvwrapper.sh'
with conn.cd(repo_path):
with conn.prefix(
f'{venv_sh} && workon {venv_name}'
):
conn.run('pip install --upgrade pip')
conn.run('pip install -r requirements.txt')
# If your project as a `setup.py` then
# install project.
#conn.run('pip install -e .')
def _restart_web(conn):
try:
conn.sudo('pkill gunicorn')
except:
pass # may not be running at all.
repo_path = environ['GIT_DIR']
venv_name = environ['VENV_NAME']
venv_sh = 'source /usr/share/virtualenvwrapper/virtualenvwrapper.sh'
with conn.cd(repo_path):
with conn.prefix(
f'{venv_sh} && workon {venv_name}'
):
conn.run("gunicorn app:app -b 0.0.0.0:8080 -w 3 --daemon")
#####################################
# Functions used from the __main__ ##
#####################################
def create_vm(**kwargs):
_create_vm(create_conn())
def pull_repo(**kwargs):
conn = create_conn()
_pull_repo(conn, **kwargs)
def install_project(**kwargs):
_install_project(create_conn())
def restart_web(**kwargs):
_restart_web(create_conn())
def main(tasks):
if len(tasks) <= 1:
print('No task name found')
return
i = 1
while i < len(tasks):
try:
fn = getattr(sys.modules[__name__], tasks[i])
except AttributeError:
print(f'Cannot find task {tasks[i]}. Quit.')
return
params = {}
j = i + 1
while j < len(tasks) and '=' in tasks[j]:
k, v = tasks[j].split('=')
params[k] = v
j += 1
i = j
print(f'Function is {fn}')
print(f'args are {params}')
fn(**params)
if __name__ == '__main__':
'''
Run it with
>>python main <task1> <key1-task1>=<value1-task1> <key2-task1>=<value2-task2> <task2> <key1-task2>=<value1-task2>
E.g.
$ python main create_vm
'''
import sys
tasks = sys.argv
main(tasks)