Русский
Русский
English
Статистика
Реклама

Оболочки

Консольный менеджер сертификатов для JKSPCKS12

05.02.2021 14:16:53 | Автор: admin

Привет Хабр!

С момента популяризации https, работа с сертификатами теперь есть практически в каждом проекте.

habr-certificatehabr-certificate

Сертификаты, которые любой может сгенерировать сам;
Сертификаты которые выдает местный СА в компании, доверие к которому есть только внутри компании;
Бесплатный публичный let's encrypt
Платный сертификат от крупных центров авторизации с дополнительными услугами, типа проверки компании.

Сертификаты для tls/ssl, сертификаты для авторизации, сертификаты для two-say-tls/ssl - даже в пределах одного небольшого проекта с десятком микросервисов и несколькими тестовыми окружениями - сертификатов набирается уйма.

При работе с множеством сертификатов, их удобно хранить не отдельными файлами, а складывать в хранилища (keystore). Самые популярные keystore форматы на сегодня - JKS (java keystore, который считается legacy с выходом 9-й джавы) и PKCS12. Последний вышел уже около 20 лет назад, и несмотря на некоторую критику, является одним из самых надежных, открытых и популярных форматов, который в скором будущем должен уже полностью вытеснить JKS.

Я много работал в проектах, где основным языком разработки был java, поэтому при работе с keystore основной инструмент для меня это консольная утилита keytool, которая поставляется с JDK. И для большинства популярных действий у меня конечно есть микро-шпаргалка:

Посмотреть список сертификатов:

keytool -list -v -keystore "файл.jks" -storepass "пароль"|grep -P '(Alias name:|Entry type:|Serial number:|Owner:|Valid from:)'

Скопировать сертификат из одного keystore в другой keystore:

keytool -importkeystore -srckeystore "откуда.jks" -srcstorepass "пароль" -destkeystore "куда.jks" -deststorepass "пароль" -srcalias "имя сертификата" [ -destalias "новое имя" ]

Импортировать сертификат в keystore из PEM файла:

keytool -import -file "откуда.pem" -keystore "куда.jks" -storepass "пароль" -noprompt -alias "имя_сертификата"

Экспортировать сертификат из keystore в файл:

keytool -exportcert -v -alias "alias" -keystore "keystore.jks" -storepass "storepassword" -rfc -file "exportedcert.cer"

Но однажды мне пришлось поработать в проекте, где сертификатов оказалось мягко говоря много. Плюс несколько сотен виртуалок и контейнеров, где все сертификаты обновлялись вручную. Перед тем? как подойти к вопросу автоматизации, было необходимо все это разобрать и причесать. Но долгое время руки просто не доходили - с одной стороны были вещи более приоритетные а сертификаты в общем напрягали нечасто. С другой стороны, опыта разгребания такого объема у меня раньше не было, и навскидку казалось, что в лоб задача решается слишком уныло и кропотливо, браться за нее по своей инициативе не хотелось - задача чисто техническая, никакие менеджеры и представители бизнеса такое перед нашей командой не ставили. Но где-то в подсознании желание все это перебрать и привести в порядок крутилось. Изредка я почитывал статьи, просматривал сертификат эксплореры, но все они были под графическую оболочку Windows, а политика безопасности не позволяла ставить в компании неодобренный софт на десктопы.

Возможно, я бы собрался силой воли, помучился бы недельку, сделал бы все стандартными однострочниками+notepad+excel, но тут внезапно у меня случился больничный отпуск, а когда температура меня попустила, а на работу еще можно было не выходить, я случайно вспомнил, что считаю себя спецом по bash.

И пусть я не хватаю лавры автора PIUPIU), но тем не менее встречайте:

Консольный двух-панельный keystore менеджер на Linux shell.

Сперва я написал рабочий вариант на bash, но в какой-то момент подумал и переписал все башизмы в пользу POSIX совместимости. Код стал выглядеть более громоздко, зато скрипт проверенно работает в bash/ksh/dash без проблем.
Если я что-то пропустил - напишите в комментариях, ну или форкайте на здоровье ;)

Также используется sed и grep, по поводу последнего - я не очень уверен, насколько POSIX-совместимо grep -P. Но если что, там несложно переписать на sed.

Некоторое сожаление: привычка работы с java вынудила меня всю работу с хранилищами выполнять именно через keystore, который должен быть доступен в PATH (а может быть стоило разобраться и сделать все через openssl?).

Но давайте к делу. Что умеет менеджер вкратце можно наглядно увидеть на скриншотах.

В одно-панельном режиме:

В двух-панельном режиме:

Для поклонников панельных менеджеров (NC, VC, MC, FAR и др), функциональные клавиши и навигация должны быть интуитивно понятны.

Весьма важным плюсом я считаю, что jks_mgr.sh - это просто шелл скрипт, одним файлом размером 20 килобайт (update: уже 30+ кб) (update2: уже 35+ кб).

Никаких обсфукаций, код максимально простой - проверить на отсутствие закладок может в принципе любой джуниор - то есть такой скрипт можно использовать в любом проекте, не нарушая никаких требований по безопасности, требований по лицензионности, лоялен к производительности, установка не нужна - лишь бы на целевой машине был доступен шелл, keytool, sed и grep.

Что было самое интересное во время разработки:

Автоматическая подстройка панелей под высоту/ширину экрана.

Так как экран у меня перерисовывается почти с каждым нажатием, то менять ширину экрана (например если сидеть через графический ssh клиент) можно в произвольное время - после первого же нажатия любой клавиши экран перерисуется и все адаптируется. Пару вечеров ушло на опции отображения/скрытия столбцов - нужно было высчитать и отладить подстройку для одно-панельного и двух-панельного режима, плюс заголовки и текст считается разными формулами. Вот кусочек для определения насколько надо обрезать Certificate Alias, если экран слишком узкий и затем пример вывода заголовка:

WindowWidth="$(tput cols)"if [ -n "$RFILE" ]; then # two-panel    used=24    [ -n "$SHOW_TYPE" ] && used=$(( $used+34 ))    localWidth=$(( ( $WindowWidth - $used ) / 2 - 1 ))    if [ $localWidth -ne $aliasWidth ]; then        aliasWidth=$localWidth        [ $aliasWidth -lt 1 ] && aliasWidth=1        clear    fi..................................headerWidth=$(( $aliasWidth + 5 ))[ -n "$SHOW_TYPE" ] && headerWidth=$(( $headerWidth + 17 ))printf " store: ${blue}%-$(( $headerWidth ))s${rst}" "$LFILE"printf "| store: ${blue}%-$(( $headerWidth -1 ))s${rst}\n" "$RFILE"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s |" "Alias"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s\n" "Alias"

Определение нажатия функциональных клавиш.

Некоторые специальные keypress могут иметь длину до 4 символов, поэтому просто через read было сделать непросто - он не позволяет вариативно менять длину читаемой строки, но после поиска в гугле и экспериментов c read в bash/dash, это выглядит так:

# Special keypress could take up to 4 charactersread -rsN1 keypressif [ "$keypress" == "$escape_char" ]; then    read -sn1 -t 0.01 k1    read -sn1 -t 0.01 k2    read -sn1 -t 0.01 k3    read -sn1 -t 0.01 k4    keypress=${k1}${k2}${k3}${k4}    unset k1 k2 k3 k4fi

Теперь можно сравнивать $keypress с комбинациями типа '[A' (стрелка вверх), '[B' (стрелка вниз), '[13~' (F3) и так далее. Я не совсем уверен, что мой вариант будет работать везде идеально - поэтому на всякий случай почти все хоткеи продублированы обычными буквами.

В упрощенном виде я подобную навигацию уже копирую в другие свои велосипеды - в некоторых случаях это гораздо удобнее, чем стандартный select благодаря наглядности и возможности делать хоткеи.

На текущий момент все самые часто используемые мной функции уже воплощены, и я взял передышку, да и собственно сам скрипт я начал писать чтобы получить еще немного практики с tput и навигацией, но немного увлекся.

Я догадываюсь, что можно заметно ускорить навигацию - производительность в git-bash ужасна - мне быстрее скопировать keystore на ближайшую виртуалку, поковырять там менеджером и скопировать исправленный keystore назад. Но нужно будет переписывать много вычислений, делать независимую пагинацию для каждой панели, а на Linux все работает отлично, поэтому в текущем варианте скрипт меня устраивает и дойдут ли руки до переделки - не уверен.

В конце нужно написать какой-то умный итог и заключение ну он простой:

Хочешь получить от практики удовольствие - придумай задачу, результат которой будет полезен для тебя.

Хочешь получить от практики двойное удовольствие - придумай задачу, результат которой будет полезен и для тебя и для кого-нибудь еще.

Сейчас моим скриптом пользуется небольшой круг людей в компании, и то, что я, не разработчик, смог написать нечто полезное и востребованное, меня очень радует.

P.S. На фоне некоторых проблем с nginx, Wargaming и др., сразу хочу уточнить, что jks_mgr.sh был написан ИСКЛЮЧИТЕЛЬНО в нерабочее время на личном компьютере.

Подробнее..

Blue-Green Deployment на минималках

24.08.2020 22:08:53 | Автор: admin

В этой статье мы с помощью bash, ssh, docker и nginx организуем бесшовную выкладку веб-приложения. Blue-green deployment это техника, позволяющая мгновенно обновлять приложение, не отклоняя ни одного запроса. Она является одной из стратегий zero downtime deployment и лучше всего подходит для приложений с одним инстансом, но возможностью загрузить рядом второй, готовый к работе инстанс.


Допустим, у Вас есть веб-приложение, с которым активно работает множество клиентов, и ему совершенно никак нельзя на пару секунд прилечь. А Вам очень нужно выкатить обновление библиотеки, фикс бага или новую крутую фичу. В обычной ситуации, потребуется остановить приложение, заменить его и снова запустить. В случае докера, можно сначала заменить, потом перезапустить, но всё равно будет период, в котором запросы к приложению не обработаются, ведь обычно приложению требуется некоторое время на первоначальную загрузку. А если оно запустится, но окажется неработоспособным? Вот такая задача, давайте её решать минимальными средствами и максимально элегантно.


DISCLAIMER: Большая часть статьи представлена в экспериментальном формате в виде записи консольной сессии. Надеюсь, это будет не очень сложно воспринимать, и этот код сам себя документирует в достаточном объёме. Для атмосферности, представьте, что это не просто кодсниппеты, а бумага из "железного" телетайпа.



Интересные техники, которые сложно нагуглить просто читая код описаны в начале каждого раздела. Если будет непонятно что-то ещё гуглите и проверяйте в explainshell (благо, он снова работает, в связи с разблокировкой телеграма). Что не гуглится спрашивайте в комментах. С удовольствием дополню соответствующий раздел "Интересные техники".


Приступим.


$ mkdir blue-green-deployment && cd $_

Сервис


Сделаем подопытный сервис и поместим его в контейнер.


Интересные техники


  • cat << EOF > file-name (Here Document + I/O Redirection) способ создать многострочный файл одной командой.
  • wget -qO- URL (explainshell) вывести полученный по HTTP документ в /dev/stdout (аналог curl URL).

Распечатка


Я специально разрываю сниппет, чтобы включить подсветку для Python. В конце будет ещё один такой кусок. Считайте, что в этих местах бумага порвалась и была склеена.

$ cat << EOF > uptimer.py

from http.server import BaseHTTPRequestHandler, HTTPServerfrom time import monotonicapp_version = 1app_name = f'Uptimer v{app_version}.0'loading_seconds = 15 - app_version * 5class Handler(BaseHTTPRequestHandler):    def do_GET(self):        if self.path == '/':            try:                t = monotonic() - server_start                if t < loading_seconds:                    self.send_error(503)                else:                    self.send_response(200)                    self.send_header('Content-Type', 'text/html')                    self.end_headers()                    response = f'<h2>{app_name} is running for {t:3.1f} seconds.</h2>\n'                    self.wfile.write(response.encode('utf-8'))            except Exception:                self.send_error(500)        else:            self.send_error(404)httpd = HTTPServer(('', 8080), Handler)server_start = monotonic()print(f'{app_name} (loads in {loading_seconds} sec.) started.')httpd.serve_forever()

EOF$ cat << EOF > DockerfileFROM python:alpineEXPOSE 8080COPY uptimer.py app.pyCMD [ "python", "-u", "./app.py" ]EOF$ docker build --tag uptimer .Sending build context to Docker daemon  39.42kBStep 1/4 : FROM python:alpine ---> 8ecf5a48c789Step 2/4 : EXPOSE 8080 ---> Using cache ---> cf92d174c9d3Step 3/4 : COPY uptimer.py app.py ---> a7fbb33d6b7eStep 4/4 : CMD [ "python", "-u", "./app.py" ] ---> Running in 1906b4bd9fdfRemoving intermediate container 1906b4bd9fdf ---> c1655b996fe8Successfully built c1655b996fe8Successfully tagged uptimer:latest$ docker run --rm --detach --name uptimer --publish 8080:8080 uptimer8f88c944b8bf78974a5727070a94c76aa0b9bb2b3ecf6324b784e782614b2fbf$ docker psCONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES8f88c944b8bf        uptimer             "python -u ./app.py"   3 seconds ago       Up 5 seconds        0.0.0.0:8080->8080/tcp   uptimer$ docker logs uptimerUptimer v1.0 (loads in 10 sec.) started.$ wget -qSO- http://localhost:8080  HTTP/1.0 503 Service Unavailable  Server: BaseHTTP/0.6 Python/3.8.3  Date: Sat, 22 Aug 2020 19:52:40 GMT  Connection: close  Content-Type: text/html;charset=utf-8  Content-Length: 484$ wget -qSO- http://localhost:8080  HTTP/1.0 200 OK  Server: BaseHTTP/0.6 Python/3.8.3  Date: Sat, 22 Aug 2020 19:52:45 GMT  Content-Type: text/html<h2>Uptimer v1.0 is running for 15.4 seconds.</h2>$ docker rm --force uptimeruptimer

Реверс-прокси


Чтобы наше приложение имело возможность незаметно поменяться, необходимо, чтобы перед ним была ещё какая-то сущность, которая скроет его подмену. Это может быть веб-сервер nginx в режиме реверс-прокси. Реверс-прокси устанавливается между клиентом и приложением. Он принимает запросы от клиентов и перенаправляет их в приложение а ответы приложения направляет клиентам.


Приложение и реверс-прокси можно связать внутри докера с помощью docker network. Таким образом, контейнеру с приложением можно даже не пробрасывать порт в хост-системе, это позволяет максимально изолировать приложение от угроз из внешки.


Если реверс-прокси будет жить на другом хосте, придётся отказаться от docker network и связать приложение с реверс-прокси через сеть хоста, пробросив порт приложения параметром --publish, как при первом запуске и как у реверс-прокси.


Реверс-прокси будем запускать на порту 80, ибо это именно та сущность, которой следует слушать внешку. Если 80-й порт у Вас на тестовом хосте занят, поменяйте параметр --publish 80:80 на --publish ANY_FREE_PORT:80.


Интересные техники



Распечатка


$ docker network create web-gateway5dba128fb3b255b02ac012ded1906b7b4970b728fb7db3dbbeccc9a77a5dd7bd$ docker run --detach --rm --name uptimer --network web-gateway uptimera1105f1b583dead9415e99864718cc807cc1db1c763870f40ea38bc026e2d67f$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer:8080<h2>Uptimer v1.0 is running for 11.5 seconds.</h2>$ docker run --detach --publish 80:80 --network web-gateway --name reverse-proxy nginx:alpine80695a822c19051260c66bf60605dcb4ea66802c754037704968bc42527bf120$ docker psCONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES80695a822c19        nginx:alpine        "/docker-entrypoint."   27 seconds ago       Up 25 seconds       0.0.0.0:80->80/tcp   reverse-proxya1105f1b583d        uptimer             "python -u ./app.py"     About a minute ago   Up About a minute   8080/tcp             uptimer$ cat << EOF > uptimer.confserver {    listen 80;    location / {        proxy_pass http://uptimer:8080;    }}EOF$ docker cp ./uptimer.conf reverse-proxy:/etc/nginx/conf.d/default.conf$ docker exec reverse-proxy nginx -s reload2020/06/23 20:51:03 [notice] 31#31: signal process started$ wget -qSO- http://localhost  HTTP/1.1 200 OK  Server: nginx/1.19.0  Date: Sat, 22 Aug 2020 19:56:24 GMT  Content-Type: text/html  Transfer-Encoding: chunked  Connection: keep-alive<h2>Uptimer v1.0 is running for 104.1 seconds.</h2>

Бесшовный деплоймент


Выкатим новую версию приложения (с двухкратным бустом startup performance) и попробуем бесшовно её задеплоить.


Интересные техники


  • echo 'my text' | docker exec -i my-container sh -c 'cat > /my-file.txt' Записать текст my text в файл /my-file.txt внутри контейнера my-container.
  • cat > /my-file.txt Записать в файл содержимое стандартного входа /dev/stdin.

Распечатка


$ sed -i "s/app_version = 1/app_version = 2/" uptimer.py$ docker build --tag uptimer .Sending build context to Docker daemon  39.94kBStep 1/4 : FROM python:alpine ---> 8ecf5a48c789Step 2/4 : EXPOSE 8080 ---> Using cache ---> cf92d174c9d3Step 3/4 : COPY uptimer.py app.py ---> 3eca6a51cb2dStep 4/4 : CMD [ "python", "-u", "./app.py" ] ---> Running in 8f13c6d3d9e7Removing intermediate container 8f13c6d3d9e7 ---> 1d56897841ecSuccessfully built 1d56897841ecSuccessfully tagged uptimer:latest$ docker run --detach --rm --name uptimer_BLUE --network web-gateway uptimer96932d4ca97a25b1b42d1b5f0ede993b43f95fac3c064262c5c527e16c119e02$ docker logs uptimer_BLUEUptimer v2.0 (loads in 5 sec.) started.$ docker run --rm --network web-gateway alpine wget -qO- http://uptimer_BLUE:8080<h2>Uptimer v2.0 is running for 23.9 seconds.</h2>$ sed s/uptimer/uptimer_BLUE/ uptimer.conf | docker exec --interactive reverse-proxy sh -c 'cat > /etc/nginx/conf.d/default.conf'$ docker exec reverse-proxy cat /etc/nginx/conf.d/default.confserver {    listen 80;    location / {        proxy_pass http://uptimer_BLUE:8080;    }}$ docker exec reverse-proxy nginx -s reload2020/06/25 21:22:23 [notice] 68#68: signal process started$ wget -qO- http://localhost<h2>Uptimer v2.0 is running for 63.4 seconds.</h2>$ docker rm -f uptimeruptimer$ wget -qO- http://localhost<h2>Uptimer v2.0 is running for 84.8 seconds.</h2>$ docker psCONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                NAMES96932d4ca97a        uptimer             "python -u ./app.py"     About a minute ago   Up About a minute   8080/tcp             uptimer_BLUE80695a822c19        nginx:alpine        "/docker-entrypoint."   8 minutes ago        Up 8 minutes        0.0.0.0:80->80/tcp   reverse-proxy

На данном этапе образ билдится прямо на сервере, что требует наличия там исходников приложения, а также нагружает сервер лишней работой. Следующим шагом будет выделение сборки образа на отдельную машину (например, в CI-систему) с последующей передачей его на сервер.


Перекачка образов


К сожалению, перекачивать образа с localhost на localhost не имеет смысла, так что этот раздел можно пощупать только имея под рукой два хоста с докером. На минималках это выглядит примерно так:


$ ssh production-server docker image lsREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE$ docker image save uptimer | ssh production-server 'docker image load'Loaded image: uptimer:latest$ ssh production-server docker image lsREPOSITORY          TAG                 IMAGE ID            CREATED             SIZEuptimer             latest              1d56897841ec        5 minutes ago       78.9MB

Команда docker save сохраняет данные образа в .tar архив, то есть он весит примерно в 1.5 раза больше, чем мог бы весить в сжатом виде. Так пожмём же его во имя экономии времени и трафика:


$ docker image save uptimer | gzip | ssh production-server 'zcat | docker image load'Loaded image: uptimer:latest

А ещё, можно наблюдать за процессом перекачки (правда, для этого нужна сторонняя утилита):


$ docker image save uptimer | gzip | pv | ssh production-server 'zcat | docker image load'25,7MiB 0:01:01 [ 425KiB/s] [                   <=>    ]Loaded image: uptimer:latest

Совет: Если Вам для соединения с сервером по SSH требуется куча параметров, возможно вы не используете файл ~/.ssh/config.

Передача образа через docker image save/load это наиболее минималистичный метод, но не единственный. Есть и другие:


  1. Container Registry (стандарт отрасли).
  2. Подключиться к docker daemon сервера с другого хоста:
    1. Переменная среды DOCKER_HOST.
    2. Параметр командной строки -H или --host инструмента docker-compose.
    3. docker context

Второй способ (с тремя вариантами его реализации) хорошо описан в статье How to deploy on remote Docker hosts with docker-compose.


deploy.sh


Теперь соберём всё, что мы делали вручную в один скрипт. Начнём с top-level функции, а потом посмотрим на остальные, используемые в ней.


Интересные техники


  • ${parameter?err_msg} одно из заклинаний bash-магии (aka parameter substitution). Если parameter не задан, вывести err_msg и выйти с кодом 1.
  • docker --log-driver journald по-умолчанию, драйвером логирования докера является текстовый файл без какой-либо ротации. С таким подходом логи быстро забивают весь диск, поэтому для production-окружения необходимо менять драйвер на более умный.

Скрипт деплоймента


deploy() {    local usage_msg="Usage: ${FUNCNAME[0]} image_name"    local image_name=${1?$usage_msg}    ensure-reverse-proxy || return 2    if get-active-slot $image_name    then        local OLD=${image_name}_BLUE        local new_slot=GREEN    else        local OLD=${image_name}_GREEN        local new_slot=BLUE    fi    local NEW=${image_name}_${new_slot}    echo "Deploying '$NEW' in place of '$OLD'..."    docker run \        --detach \        --restart always \        --log-driver journald \        --name $NEW \        --network web-gateway \        $image_name || return 3    echo "Container started. Checking health..."    for i in {1..20}    do        sleep 1        if get-service-status $image_name $new_slot        then            echo "New '$NEW' service seems OK. Switching heads..."            sleep 2  # Ensure service is ready            set-active-slot $image_name $new_slot || return 4            echo "'$NEW' service is live!"            sleep 2  # Ensure all requests were processed            echo "Killing '$OLD'..."            docker rm -f $OLD            docker image prune -f            echo "Deployment successful!"            return 0        fi        echo "New '$NEW' service is not ready yet. Waiting ($i)..."    done    echo "New '$NEW' service did not raise, killing it. Failed to deploy T_T"    docker rm -f $NEW    return 5}

Использованные функции:


  • ensure-reverse-proxy Убеждается, что реверс-прокси работает (полезно для первого деплоя)
  • get-active-slot service_name Определяет какой сейчас слот активен для заданного сервиса (BLUE или GREEN)
  • get-service-status service_name deployment_slot Определяет готов ли сервис к обработке входящих запросов
  • set-active-slot service_name deployment_slot Меняет конфиг nginx в контейнере реверс-прокси

По порядку:


ensure-reverse-proxy() {    is-container-up reverse-proxy && return 0    echo "Deploying reverse-proxy..."    docker network create web-gateway    docker run \        --detach \        --restart always \        --log-driver journald \        --name reverse-proxy \        --network web-gateway \        --publish 80:80 \        nginx:alpine || return 1    docker exec --interactive reverse-proxy sh -c "> /etc/nginx/conf.d/default.conf"    docker exec reverse-proxy nginx -s reload}is-container-up() {    local container=${1?"Usage: ${FUNCNAME[0]} container_name"}    [ -n "$(docker ps -f name=${container} -q)" ]    return $?}get-active-slot() {    local service=${1?"Usage: ${FUNCNAME[0]} service_name"}    if is-container-up ${service}_BLUE && is-container-up ${service}_GREEN; then        echo "Collision detected! Stopping ${service}_GREEN..."        docker rm -f ${service}_GREEN        return 0  # BLUE    fi    if is-container-up ${service}_BLUE && ! is-container-up ${service}_GREEN; then        return 0  # BLUE    fi    if ! is-container-up ${service}_BLUE; then        return 1  # GREEN    fi}get-service-status() {    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"    local service=${1?usage_msg}    local slot=${2?$usage_msg}    case $service in        # Add specific healthcheck paths for your services here        *) local health_check_port_path=":8080/" ;;    esac    local health_check_address="http://personeltest.ru/away/${service}_${slot}${health_check_port_path}"    echo "Requesting '$health_check_address' within the 'web-gateway' docker network:"    docker run --rm --network web-gateway alpine \        wget --timeout=1 --quiet --server-response $health_check_address    return $?}set-active-slot() {    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"    local service=${1?$usage_msg}    local slot=${2?$usage_msg}    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1    get-nginx-config $service $slot | docker exec --interactive reverse-proxy sh -c "cat > /etc/nginx/conf.d/$service.conf"    docker exec reverse-proxy nginx -t || return 2    docker exec reverse-proxy nginx -s reload}

Функция get-active-slot требует небольших пояснений:


Почему она возвращает число, а не выводит строку?

Всё равно в вызывающей функции мы проверяем результат её работы, а проверять exit code средствами bash намного проще, чем строку. К тому же, получить из неё строку очень просто:
get-active-slot service && echo BLUE || echo GREEN.


А трёх условий точно хватает, чтобы различить все состояния?


Даже двух хватит, последнее тут просто для полноты, чтобы не писать else.


Осталась неопределённой только функция, возвращающая конфиги nginx: get-nginx-config service_name deployment_slot. По аналогии с хелсчеком, тут можно задать любой конфиг для любого сервиса. Из интересного только cat <<- EOF, что позволяет убрать все табы в начале. Правда, цена благовидного форматирования смешанные табы с пробелами, что сегодня считается очень дурным тоном. Но bash форсит табы, а в конфиге nginx тоже было бы неплохо иметь нормальное форматирование. Короче, тут смешение табов с пробелами кажется действительно лучшим решением из худших. Однако, в сниппете ниже Вы этого не увидите, так как хабр "делает хорошо", меняя все табы на 4 пробела и делая невалидным EOF. А вот тут заметно.


Чтоб два раза не вставать, сразу расскажу про cat << 'EOF', который ещё встретится далее. Если писать просто cat << EOF, то внутри heredoc производится интерполяция строки (раскрываются переменные ($foo), вызовы команд ($(bar)) и т.д.), а если заключить признак конца документа в одинарные ковычки, то интерполяция отключается и символ $ выводится как есть. То что надо для вставки скрипта внутрь другого скрипта.

get-nginx-config() {    local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot"    local service=${1?$usage_msg}    local slot=${2?$usage_msg}    [ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1    local container_name=${service}_${slot}    case $service in        # Add specific nginx configs for your services here        *) nginx-config-simple-service $container_name:8080 ;;    esac}nginx-config-simple-service() {    local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass"    local proxy_pass=${1?$usage_msg}cat << EOFserver {    listen 80;    location / {        proxy_pass http://$proxy_pass;    }}EOF}

Это и есть весь скрипт. И вот гист с этим скриптом для скачки через wget или curl.


Выполнение параметризированных скриптов на удалённом сервере


Пришло время стучаться на целевой сервер. В этот раз localhost вполне подойдёт:


$ ssh-copy-id localhost/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keyshimura@localhost's password: Number of key(s) added: 1Now try logging into the machine, with:   "ssh 'localhost'"and check to make sure that only the key(s) you wanted were added.

Мы написали скрипт деплоймента, который перекачивает предварительно собранный образ на целевой сервер и бесшовно подменяет контейнер сервиса, но как его выполнить на удалённой машине? У скрипта есть аргументы, так как он универсален и может деплоить сразу несколько сервисов под один реверс-прокси (конфигами nginx можно разрулить по какому url какой будет сервис). Скрипт нельзя хранить на сервере, так как в этом случае мы не сможем его автоматически обновлять (с целью багфиксов и добавления новых сервисоы), да и вообще, стэйт = зло.


Решение 1: Таки хранить скрипт на сервере, но копировать его каждый раз через scp. Затем подключиться по ssh и выполнить скрипт с необходимыми аргументами.


Минусы:


  • Два действия вместо одного
  • Места куда вы копируете может не быть, или не быть к нему доступа, или скрипт может выполняться в момент подмены.
  • Желательно убрать за собой (удалить скрипт).
  • Уже три действия.

Решение 2:


  • В скрипте держать только определения функций и вообще ничего запускать
  • С помощью sed дописывать в конец вызов функции
  • Отправлять всё это прямо в shh через pipe (|)

Плюсы:


  • Truely stateless
  • No boilerplate entities
  • Feeling cool

Вот давайте только без Ansible. Да, всё уже придумано. Да, велосипед. Смотрите, какой простой, элегантный и минималистичный велосипед:


$ cat << 'EOF' > deploy.sh

#!/bin/bashusage_msg="Usage: ${FUNCNAME[0]} ssh_address image_name"ssh_address=${1?$usage_msg}image_name=${2?$usage_msg}echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..."( sed "\$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT'deploy() {    echo "Yay! The '${FUNCNAME[0]}' function is executing on '$(hostname)' with argument '$1'"}END_OF_SCRIPT

EOF$ chmod +x deploy.sh$ ./deploy.sh localhost magic-porridge-potConnecting to localhost...Yay! The 'deploy' function is executing on 'hut' with argument 'magic-porridge-pot'

Однако, мы не можем быть уверены, что на удалённом хосте есть адекватный bash, так что добавим в начало небольшую проверочку (это вместо shellbang):


if [ "$SHELL" != "/bin/bash" ]then    echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'."    exit 1fi

А теперь всё по-настоящему:


$ docker exec reverse-proxy rm /etc/nginx/conf.d/default.conf$ wget -qO deploy.sh https://git.io/JUJq1$ chmod +x deploy.sh$ ./deploy.sh localhost uptimerSending gzipped image 'uptimer' to 'localhost' via ssh...Loaded image: uptimer:latestConnecting to 'localhost' via ssh to seamlessly deploy 'uptimer'...Deploying 'uptimer_GREEN' in place of 'uptimer_BLUE'...06f5bc70e9c4f930e7b1f826ae2ca2f536023cc01e82c2b97b2c84d68048b18aContainer started. Checking health...Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:  HTTP/1.0 503 Service Unavailablewget: server returned error: HTTP/1.0 503 Service UnavailableNew 'uptimer_GREEN' service is not ready yet. Waiting (1)...Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:  HTTP/1.0 503 Service Unavailablewget: server returned error: HTTP/1.0 503 Service UnavailableNew 'uptimer_GREEN' service is not ready yet. Waiting (2)...Requesting 'http://uptimer_GREEN:8080/' within the 'web-gateway' docker network:  HTTP/1.0 200 OK  Server: BaseHTTP/0.6 Python/3.8.3  Date: Sat, 22 Aug 2020 20:15:50 GMT  Content-Type: text/htmlNew 'uptimer_GREEN' service seems OK. Switching heads...nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successful2020/08/22 20:15:54 [notice] 97#97: signal process started'uptimer_GREEN' service is live!Killing 'uptimer_BLUE'...uptimer_BLUETotal reclaimed space: 0BDeployment successful!

Теперь можно открыть http://localhost/ в браузере, запустить деплоймент ещё раз и убедиться, что он проходит бесшовно путём обновления страницы по КД во время выкладки.


Не забываем убираться после работы :3


$ docker rm -f uptimer_GREEN reverse-proxy uptimer_GREENreverse-proxy$ docker network rm web-gateway web-gateway$ cd ..$ rm -r blue-green-deployment
Подробнее..

WinUI 3 Новая эра разработки под Windows

11.03.2021 20:12:02 | Автор: admin

В календаре 8 марта, а я пишу эту статью.
Почему? - Потому, что WinUI 3 - это важно!

Предыстория

Для того, что бы понять, что из себя представляет WinUI 3 и какую глобальную проблему он решает, предлагаю окунуться в историю десктопной разработки под Windows:

История развития десктопной разработки под WindowsИстория развития десктопной разработки под Windows
  • Итак, на дворе 1995 год, и мы начинаем писать наш калькулятор на C++ и Win32. Win32 - самый низкоуровневый системный API, для работы с визуальным интерфейсом. Ниже только Windows Driver Model для драйверов.

  • 2002 год - наш калькулятор уже может быть написан на более высокоуровневой оболочке - WinForms (Windows Forms). Она создаёт обёртку над Win32 для более простого и удобного взаимодействия с системой. А за счёт .NET Framework и CLR, которые так же вышли в 2002 году, мы можем использовать целый букет из различный языков для разработки. В их числе были C#, С++, VB.Net, J# и другие.

  • WPF - наш ответ устаревшим технологиям. В конце 2006 года Microsoft презентуют WPF - обширный API-интерфейс для создания настольных графических программ, имеющих насыщенный дизайн и интерактивность. Новый подход включал в себя совершенно другую модель построения пользовательских приложений, основывающуюся на .NET Framework и DirectX. Вместе с выходом WPF у нас появляются следующие возможности: WEB-подобная модель компоновки, богатая модель рисования, анимации, поддержка аудио и видео, стили и шаблоны, декларативный пользовательский интерфейс, подход MVVM, и язык разметки XAML.

  • 2012 год - выход Windows 8. Очень много споров и дискуссий было на тему свежей ОС. Но самое интересное изменение, которое с ней случилось - выход WinRT или же Windows Runtime. Это именно то, почему у Windows присутствует 2 меню настроек: новое и панель управления. WinRT по существу является API, основанное на технологии COM. COM, в свою очередь, предполагал использование одних и тех же компонентов системы во многих местах одновременно. Например, PrintDialog - теперь единый компонент, к реальному состоянию которого можно получить доступ из любого места системы. Нам теперь не нужно загружать информацию о нём каждый раз при использовании, как это было с Win32.

  • 2016 - Universal Windows Platform или же UWP. Апогей унификации Windows. Продолжающая идею WinRT, UWP активно объединяет системные функции и компоненты под единым программным интерфейсом, который теперь может работать на всех устройствах с Windows 10 без изменений в коде! Но, это касается не только программных функций, но и визуальных компонентов. Так и появляется на свет WinUI 2 - единый визуальный интерфейс для всех приложений на Windows.

Что мы в итоге получаем?

Как результат - у нас есть мощная платформа, с унифицированным программным и визуальным интерфейсом. Наш стандартный калькулятор уже построен как UWP приложение ( Исходный код Windows калькулятора можно найти в открытом доступе на github). Но, что-то всё равно не так

Предпочтения разработчиков по выбору платформы за 2016 годПредпочтения разработчиков по выбору платформы за 2016 год

Опрос от компании Telerik за 2016 год показал, что сообщество разработчиков без явного энтузиазма брались использовать UWP, как основную платформу в своих новых проектах. Что же не так? А всё дело в унификации приложений для Windows. За счёт оборачивания их в различные новые API и Windows Runtime, разработчики потеряли возможность работать со старыми добрыми Win32 напрямую. Они, в свою очередь, предоставляют огромнейшие возможности для тонкой работы с системой. И тот список системных API, которые сейчас доступны из UWP приложений, кажется просто смешным. Ознакомиться с ним более детально можно здесь: Список системных API, доступных через UWP приложение

WinUI 3

Именно проблему недоступности системных API и будет решать новая версия WinUI. WinUI 3 Preview 4 уже находится в пре-релизе, а официальный релиз намечен на март. Но каким образом удастся получить доступ к системным API приложению, которое изначально задумывалось как работающее на WinRT и UWP API?

Всё просто: Необходимо оделить визуальную часть и среду выполнения. Ключом к этому стала платформа .NET:

Из чего появился .NET 5Из чего появился .NET 5

Что такое .NET?

  • Из блока предыстории можно узнать, что в 2002 году вышла Windows-ориентированная платформа для разработки ПО - .NET Framework. Она развивалась в плоть до 2019 года, когда была выпущена последняя версия - 4.8

  • Параллельно, с 2016 года начала развиваться новая модульная платформа для разработки программного обеспечения с открытым исходным кодом - .NET Core. Кроме того, она была кроссплатформенная.

  • И наконец - .NET 5. Это, выпущенная в 2020 году, единая платформа для разработки ПО. Она объединила в себе все преимущества .NET Framework, .NET Core и некоторых похожих платформ.

По заявлениям Microsoft, теперь это единственная платформа, в сторону которой будет развиваться компания.

Планы на развитие и поддержку платформы .NETПланы на развитие и поддержку платформы .NET

И теперь, имея единую программную платформу - .NET 5, компания Microsoft может воплотить идею с использованием унифицированного интерфейса, но при этом оставить возможность использовать удобные API: будь-то UWP API или Win32.

Это и является той золотой срединой, когда мы можем использовать современные, производительные визуальные компоненты вместе с функциональностью системных API.

Перспективы WinUI 3

По заявлениям самих Microsoft, на WinUI 3 и его последующие версии, у компании огромные планы. Для начала, уже в осеннем обновлении Windows мы сможем увидеть переход многих частей операционной системы на WinUI 3.

Xamarin, MAUI and WinUI 3?

MAUI (Multi-platform App UI) - кроссплатформенная библиотека графического интерфейса. Является эволюцией Xamarin.Forms. Для Windows она будет работать именно через компоненты WinUI 3. Больше информации о MAUI: devblog.microsoft, github.

Uno Platform and WinUI 3?

Uno Platform - это кроссплатформенный графический пользовательский интерфейс с открытым исходным кодом, который позволяет запускать код на основе WinUI и универсальной платформы Windows на iOS, macOS, Linux, Android и WebAssembly.

Команда разработчиков Uno Platform с первых дней анонсировала свою нацеленность на WinUI 3. А спустя 12 часов после выпуска WinUI 3 Preview 4, его поддержка уже была добавлена в Uno Platform. Больше информации об Uno Platform: platform.uno, github.

Будущее системных API

Что касается системных API, Microsoft не оставляют идею их унификации. Поэтому, сейчас в активной разработке находится Project Reunion. Это универсальная модульная оболочка, которая должно предоставить быстрый доступ ко всем Win32 и UWP API.

Project Reunion будет очень тесно связан с WinUI 3. И уже сейчас находится в превью версии.

Больше информации о Project Reunion: docs.microsoft.com, github

Подробнее..

Как приручить консоль, или 5 шагов к жизни с командной строкой

25.01.2021 16:17:27 | Автор: admin

Всем привет! Меня зовут Осип, я Android-разработчик в Redmadrobot и я люблю автоматизировать всё, что автоматизируется. В этом мне помогает консоль, поэтому решил поделиться опытом, как настроить командную оболочку так, чтобы в ней было приятно работать и она ежедневно помогала вам решать задачи.

Статья для тех, кто использует Linux или macOS. Если у вас Windows, вы можете использовать WSL (приравнивается к Ubuntu).

Есть задачи, которые проще выполнить в командном интерфейсе, а не в графическом, к примеру:

  • посчитать количество строк кода в проекте,

  • скопировать все файлы с расширением .png из одной папки в другую,

  • постучаться API и посмотреть какой ответ он выдаёт.

По опыту знаю: когда возникает одна из таких задач, разбираться, как это сделать в командной строке, если не используешь её это последнее, чего хочется. Кажется, что нет смысла залезать в неё ради одной задачи. Проще найти плагин или утилиту.

Поверьте, стоит один раз настроить командную оболочку и разобраться, как в ней работать, и рабочее окружение начнёт помогать вам как хорошая IDE.

Обо всём по порядку, начнём с терминологии и пойдем дальше. Если вы уже знакомы с основами, можно переходить к разделу Изучаем арсенал.


Статья только началась, а по тексту уже встречались и командная строка, и командная оболочка. Чем отличаются консоль, терминал, командная оболочка и командная строка?

Если объяснять из центра наружу: командная строка строка, где пользователь пишет команды; командная оболочка программа, которая интерпретирует команды, введённые в командную строку и выводит результат.

Термины консоль и терминал в статье используются как синонимы и обозначают программу для ввода и вывода информации. В терминале запускается командная оболочка.

Подробнее о различиях можно почитать на Ask Ubuntu: What is the difference between Terminal, Console, Shell, and Command Line?

В статье будут встречаться примеры команд. Если по ходу прочтения вы не понимаете, что делает консольная команда, скопируйте её и вставьте в ExplainShell. Благо Роскомнадзор перестал его блокировать после разблокировки Telegram.

Зачем вообще использовать командную строку

Дисклеймер: Я не призываю использовать только командную строку и отказаться от графического интерфейса. Нужно просто учитывать командную строку при выборе инструмента для решения задачи.

Когда хотят рассказать чем же хорош CLI, выделяют разные преимущества перед GUI:

  • Доступность. Командная строка доступна везде. Внутри Android Studio есть вкладка с командной строкой. Можно и вовсе настроить drop-down терминал (ещё его называют quake style), который будет появляться поверх всех приложений по нажатию сочетания клавиш.

  • Многофункциональность. Одна точка доступа к любым утилитам.

  • Простота. Простой командный интерфейс может заменить сложный графический интерфейс. Сделать графический интерфейс который позволит ввести, например, пять параметров может быть довольно нетривиальной задачей, а сделать то же самое с командным интерфейсом просто.

  • Легковесность. Как правило, CLI утилиты используют меньше ресурсов.

Меня как разработчика больше всего впечатляет, как можно комбинировать CLI утилиты. Текст интерфейс общения, который понятен для всех утилит с командным интерфейсом. Утилиты принимают на вход текст и возвращают тоже текст. Это один из принципов Unix, которые сформулировал Дуглас Макилрой в 1978 году:

Пишите программы, которые делают одну вещь и делают её хорошо.


Пишите программы, которые бы работали вместе.


Пишите программы, которые поддерживают текстовые потоки, потому что это универсальный интерфейс.

Программы, которые соблюдают эти принципы, похожи на кирпичики. По отдельности они решают простую задачу, но их комбинации позволяют решать и сложные задачи.

Примеры из жизни

Я задал вопрос коллегам-роботам: Для чего вы чаще всего открываете терминал? Получился такой ТОП-5:

  1. Работа с Git там, где не хватает графического интерфейса.

  2. Установка пакетов и управление зависимостями (подробнее про менеджер пакетов поговорим в разделе Устанавливаем менеджер пакетов).

  3. Работа с SSH.

  4. Проверка API с помощью curl.

  5. Когда нужно грохнуть процесс.

Есть и менее очевидные применения:

  1. Скачать видео из YouTube поможет youtube-dl. Качаете подкаст и нужна только аудио-дорожка? Добавьте к команде флаг --audio. Хотите скачать весь плейлист или даже весь канал? Подставляйте ссылку на канал и готовьте побольше свободного места.

  2. Хотите посмотреть отличия между файлами? Выполните команду diff и укажите пути до файлов, которые надо сравнить.

Шаг 1: Открываем терминал

Не терминал, а эмулятор терминала. (c) Департамент зануд

Обычно в ОС уже есть какой-то терминал, и для начала можно использовать его. Если хочется больше функций и возможностей кастомизации, то есть из чего выбирать. Некоторые терминалы позволяют открывать вкладки, разделять экран и даже рендерить картинки. Можно найти на любой вкус, например, стилизованный под старый ламповый монитор.

cool-retro-terminalcool-retro-terminal

Выбор терминала это тема для отдельной статьи. Кратко: если у вас Linux, начните с этого списка. На macOS популярен iTerm2, но я его не использовал, поэтому не могу ни поругать, ни похвалить.

Для меня важно чтобы и на компьютере с Linux, и на рабочем ноутбуке с macOS был один и тот же терминал с одинаковыми настройками. Я выбирал среди кроссплатформенных и остановился на kitty.

Шаг 2: Устанавливаем менеджер пакетов

Мы оказались в терминале. Первое, с чем нужно разобраться, менеджер пакетов.

Я встречал людей, которые уверены, что, для того чтобы установить программу из консоли, нужно непременно собирать её из исходников и вообще это сложно. Это не так. Программы в собранном виде хранятся в репозиториях. Пакетные менеджеры устанавливают программы из репозиториев и отвечают за их обновление и удаление.

Это даже проще, чем искать надёжный источник, из которого можно скачать программу, и удобнее, чем магазины приложений в macOS или Windows, где зачастую нет нужных программ.

Менеджеры пакетов в Linux

В дистрибутивах Linux менеджер пакетов есть по умолчанию. В Ubuntu, Debian и Mint это apt-get, а в Manjaro и ArchLinux pacman.

Чтобы установить пакет достаточно в терминале написать apt-get install [package]. Если у вас pacman, то pacman -S [package]. Может понадобиться sudo в начале, чтобы выполнить команду с правами root.

Чтобы обновить все пакеты с помощью apt-get введите команду apt-get update && apt-get upgrade. В pacman pacman -Syu.

В pacman много флагов и сложно сразу запомнить нужные. Ещё одно неудобство он не поддерживает установку пакетов из репозитория AUR. Это репозиторий, в который могут загружать пакеты любые пользователи. Исправить минусы помогут утилиты, которые упрощают работу с pacman. Рекомендую попробовать yay.

Менеджеры пакетов в macOS

В macOS придется установить пакетный менеджер. Самые популярные Homebrew и MacPorts. Homebrew активнее поддерживается сообществом, а пакеты в нём обновляются чаще, поэтому лучше использовать его. Для установки скопируйте актуальную команду установки c официального сайта. Эта команда скачает скрипт установки и запустит его.

Может понадобиться установка XCode Command Line Tools. Это базовый набор консольных инструментов clang, git, make и других. Он не зависит от XCode, а называется так, потому что необходим XCode для компиляции.

Теперь, чтобы установить пакет, достаточно в терминале написать brew install [package].

Обновляются все пакеты одной командой brew upgrade. Если brew отказывается работать, напишите brew doctor , и brew подскажет, что с ним не так, а также как это исправить.

Шаг 3: Устанавливаем командную оболочку

Чтобы было проще работать, командная оболочка должна вам помогать. В ней должен быть поиск по введённым ранее командам, а ещё должно работать автодополнение. Здорово, если будут подсказки, которые учитывают, какие команды вы используете чаще других. Не помешает подсветка синтаксиса и ошибок при вводе команд.

Все примеры в статье будут сделаны с использованием командной оболочки fish. Эта командная оболочка проста для освоения и в ней все нужные функции работают из коробки.

Чтобы узнать, какая оболочка используется по умолчанию у вас, выполните команду echo $SHELL. Скорее всего, команда выведет /env/bash или /env/zsh это самые популярные оболочки. Если хотите сравнить возможности bash, zsh и fish, посмотрите эту таблицу.

Установим fish c помощью менеджера пакетов:

# Если pacmansudo pacman -S fish# Если apt-getsudo apt-get install fish# Если brewbrew install fish

В статье будут упоминаться и другие программы, все они устанавливаются таким же образом. Команды установки написаны на страницах программ, чтобы не повторяться, не буду писать их для каждой утилиты.

Fish установлен. Запускаем его командой fish:

osip@homepc ~ % fishWelcome to fish, the friendly interactive shellType `help` for instructions on how to use fishosip@homepc ~>

Теперь можно настроить внешний вид. Команда fish_config откроет в браузере страницу настроек. Здесь можно настроить цветовую схему, приглашение командной строки (prompt), посмотреть список функций, переменные окружения, историю команд и список горячих клавиш. Здесь же можно добавлять, редактировать и удалять аббревиатуры команд. Про аббревиатуры поговорим чуть позже.

Fish по умолчанию

Закройте терминал и откройте его заново. Вы окажетесь не в fish, а в командной оболочке по умолчанию. Есть два варианта, как сделать так, чтобы открывался fish:

  1. Назначить fish командной оболочкой по умолчанию.

    Нужно учитывать, что скрипты инициализации текущей командной оболочки не будут выполняться. Команды и переменные окружения из .bashrc, .bash_profile, .zshrc и т.д, нужно переместить в .config/fish/fish.config , а затем адаптировать под синтаксис fish.

  2. Использовать fish только как интерактивную оболочку.

    Это более безболезненный способ, потому что не нужно мигрировать скрипты и переменные окружения. В конце скрипта инициализации текущей командной оболочки нужно запустить fish. Добавьте строку exec fish в файл .bash_profile, если у вас bash или в .zshrc, если zsh. Эти файлы находятся в корневой директории пользователя.

    На ArchWIki есть более подробное описание этого и еще нескольких способов.

Поиск по истории

Давайте-ка посмотрим, что умеет fish. Если еще не установили, можно попробовать в браузере. Я изменил только цвета и prompt, больше ничего не настраивал.

Когда вы начинаете набирать команду, fish подсказывает команды и аргументы, которые вы использовали раньше. Чтобы применить подсказку нажмите . Подставить одно слово из подсказки Ctrl+.

sample_command это демонстрационная функция. Она принимает любые параметры и ничего не возвращает.

Чтобы ввести предыдущую команду, нажмите клавишу . С помощью клавиш и можно перемещаться по истории команд. Если командная строка пуста, команды будут листаться по очереди. Если в командной строке есть текст, то история будет фильтроваться по нему.

Автодополнение

Начните писать любую команду и нажмите Tab, не дописывая её до конца. Попробуйте с командой git config:

Дополнение работает с командами, подкомандами и аргументами. Вместе с вариантами дополнения выводится описание подкоманд из документации.

Если утилита не поддерживает автодополнение, fish умеет создавать дополнения из документации man. Для этого нужно выполнить команду fish_update_completions.

А что с путями? Например, хотим перейти в папку dev/tools/jarjar/:

Дополнение путей тоже работает на Tab. Для перехода по пути не обязательно писать команду cd в начале. А еще работает дополнение, если написать первую букву имени каждой папки в пути. Если указан несуществующий путь, он подсвечивается красным.

Сложно запомнить все нужные флаги у команд. Хочу вывести дерево файлов, но не помню, как ограничить его глубину и сделать так, чтобы вывод был цветным. Для такого случая есть Shift+Tab дополнение с поиском:

Автодополнение может сработать в самых неожиданных местах, например, так работает автодополнение для команды kill:

Убийство Android Studio на глазах у studentdУбийство Android Studio на глазах у studentd

Wildcards

В fish, как и в bash, есть поддержка wildcards. Wildcards позволяют выполнить команду для нескольких файлов.

Выводим все файлы с расширением .md в текущей папкеВыводим все файлы с расширением .md в текущей папке

* соответствует любой строке
** соответствует любой иерархии папок, то есть рекурсивно заходит во вложенные папки

Применим wildcard, чтобы скопировать все файлы apk после сборки в папку output:

  • cp build/*.apk output/ скопирует все apk из папки build.

  • cp build/**.apk output/ скопирует все apk из папки build и из всех вложенных папок. То, что надо.

Функции, алиасы и аббревиатуры

Большиство команд fish это функции. Можно писать и свои функции. Синтаксис такой:

funcion [название]    [тело функции]end

Если функцию нужно сохранить для будущих сессий, не забудьте выполнить команду funcsave [название] после того как напишете функцию и проверите, что она корректно работает.

Для часто используемых команд можно создать более короткие синонимы алиасы. В fish команда alias создаёт однострочную функцию.

Как выглядит alias?

Флаг --save указывает, что нужно автоматически вызвать команду funcsave после создания алиаса. Таким образом алиасы сохранятся для будущих сессий.

Другой вариант сокращения команд аббревиатуры. Они настраиваются командой abbr или в fish_config во вкладке Abbreviations.

Удобно создавать аббревиатуру одновременно в английской и русской раскладках (см. скриншот). Такая аббревиатура выполнится, даже если вы забыли переключить раскладку клавиатуры.

Аббревиатуры подставляются на лету, когда вы нажимаете Space или Enter. В отличие от алиасов, аббревиатуры не являются функциями.

И па и gf превращается в git fetchИ па и gf превращается в git fetch

Шаг 4: Изучаем арсенал

Командная оболочка есть, теперь нужны команды.

Консольные утилиты могут быть с CLI и TUI. Command Line Interface (CLI) программа принимает команды через командную строку. Так работает большинство утилит. Text User Interface (TUI) интерфейс рисуется псевдографикой и по нему можно кликать мышкой как по GUI.

TUI для SpotifyTUI для Spotify

CLI не нужно бояться. На замену старым утилитам появляются новые с улучшенным UX, ориентированные на человека, а не на скрипты.

Например, многие старые утилиты, которые выводят размер файла, по умолчанию выводят его в байтах. А утилита df вообще выводит свободное место на диске в количестве блоков по 512 байт.

Чтобы выводились понятные человеку размерности, нужно добавить флаг -h (human readable). Цветной вывод удобнее читать, но он тоже по умолчанию обычно отключен и включается добавлением флага, чаще всего -C. В современных же утилитах по умолчанию включен цветной человекопонятный вывод.

Стандартные команды

Чтобы пользоваться командной строкой, нужно знать несколько стандартных команд:

  • cd [сhange directory] команда для навигации по файловой системе. Если запустить её без аргументов, вы окажетесь в домашней папке;

  • cp [copy], mv [move], rm [remove] команды для копирования, перемещения и удаления файлов, соответственно;

  • mkdir [make directory] команда для создания папки;

  • echo выводит строку, которую ей передали.

Если команда долго работает и вы не хотите дожидаться её завершения, прервите её выполнение сочетанием клавиш Ctrl + C.

Помощь: man, help, tldr

Есть несколько способов получить справку по команде.

man выводит полную справку:

  • описание команды,

  • список аргументов и описание каждого из них,

  • какие переменные окружения использует утилита и для чего,

  • известные баги,

  • советы и примеры использования,

  • другая информация, которую посчитал полезной разработчик.

Если ввести man man, вы получите справку по команде man, где всё это подробно описано.

man это утилита с TUI, в ней есть горячие клавиши. Для поиска нажмите /, а для выхода q. / и q стандартные клавиши для поиска и выхода, они работают во многих TUI утилитах. Ещё один стандартная клавиша ?, она открывает справку.

Можно выполнить команду из man для этого нажмите ! и введите команду. Хотите открыть man для другой команды внутри man или сразу попробовать выполнить команду, следуя документации? Легко.

Страницы в man пишут разработчики утилит. Если разработчик не написал справку, man выдаст No manual entry for [command]. Но даже если нет страницы в man можно вывести краткую справку с помощью флага --help. Попробуйте написать man --help.

Для команд fish можно открыть справку в браузере командой help <command>.

Если читать мануалы некогда, то поможет утилита tldr. Она отображает типичные случаи использования команд:

tldr tldrtldr tldr

Объединяем команды

Помните, в самом начале я говорил, что консольные команды как кирпичики и их можно объединять, чтобы решить более сложную задачу? Сейчас мы этим и займёмся.

Чтобы направить вывод одной команды на вход другой, используется оператор |. Он называется pipe, а на русский его переводят как конвейер. Если мы хотим подать вывод команды find_bone на вход команде eat, нужно между этими командами поставить трубу (pipe):

$ find_bone | eat

Синтаксис понятен, но таких команд не существует и запустить их у себя в консоли не получится. Давайте посмотрим на более реальный пример:

$ echo -e "spot\\nhandle\\npick\\natlas" > robots.txt$ cat robots.txt | sortatlashandlepickspot

Оператор | нам уже знаком, но что делает >? Этот оператор направляет вывод команды в файл. После этого командой cat мы достаём содержимое файла и с помощью оператора | отдаём на сортировку.

Современные утилиты

Просмотр списка файлов: ls, tree exa

Для просмотра списка файлов в папке обычно используют стандартную команду ls, а для просмотра иерархии папках tree. Обе эти утилиты можно заменить более дружелюбной утилитой exa.

В exa по умолчанию включен цветной вывод и человекопонятные размеры файлов, есть поддержка широких экранов, отображается статус файлов в Git, если использовать команду в репозитории. Подробнее обо всех фичах читайте на сайте утилиты.

Скриншоты. Сравнение ls, tree и exa.
Сравнение вывода ls и exaСравнение вывода ls и exaСравнение вывода tree и exaСравнение вывода tree и exa

Бонус: В exa можно совместить два режима вывода.

Просмотр запущенных процессов: top htop

top и htop. Обе утилиты выводят список запущенных процессов, но htop делает это гораздо приятнее.

В htop можно сортировать процессы по разным столбцам, нажимая на них. Кнопки в нижнем меню тоже нажимаются. Столбцы и информационные блоки в шапке настраиваются можно убрать лишнее и добавить нужное.

А как выглядит top?

Работа с JSON: jq

jq незаменимая утилита, если вы работаете с JSON. Проще показать на примерах что умеет делать jq.

Валидируем json:

$ echo '{"model": spot}' | jq typeparse error: Invalid numeric literal at line 1, column 15$ echo '{"model": "spot"}' | jq type"object"

Форматируем json:

$ echo '{"model":"spot"," type":"robodog"}' | jq{  "model": "spot",  "type": "robodog"}

Выкусываем из json'а только то, что нужно:

$ set json '[{"model": "spot", "type": "robodog"}, {"model": "atlas", "type": "humanoid"}]'$ echo $json | jq 'map(.model)' --compact-output["spot","atlas"]$ echo $json | jq .[0].model"spot"# А теперь пример посложнее$ echo $json | jq 'map({(.model): .type}) | add'{  "spot": "robodog",  "atlas": "humanoid"}

Это только малая часть возможностей. Все возможности смотрите в доке.

Другие утилиты

Чтобы этот раздел не раздулся, остальные утилиты я спрятал под спойлеры. При первом прочтении их можно пропустить и позже использовать этот раздел как справочник.

Консольный HTTP клиент: curl, wget httpie

httpie делает то же что curl отправляет запросы в сеть. Но посмотрите как отличается синтаксис и оформление вывода в curl и httpie.

На фотографии слева направо: curl и httpieНа фотографии слева направо: curl и httpie
Отображение содержимого файла: cat bat

cat и bat выводят содержимое файла, но bat подсвечивает синтаксис и отображает изменения из гита.

Поиск по содержимому файлов: grep ripgrep

ripgrep более быстрая версия grep. Сравнение скорости работы показывает, что ripgrep быстрее всех :)

Поиск файлов: find fd, fzf

Для поиска файлов можно использовать стандартную утилиту find. Для неё есть альтернатива fd. Она работает быстрее, поддерживает цветной вывод, по умолчанию игнорирует скрытые файлы и файлы из .gitignore. Посмотрите на гифку, которая демонстрирует работу fd.

Ещё одна утилита для поиска fzf [fuzzy finder]. Это утилита с TUI для интерактивного поиска файлов с использованием нечёткого поиска по названиям.

Ещё из приятного есть предпросмотр содержимого.

Подсчёт количества строк кода: wc tokei

Стандартная утилита wc [word count] считает количество слов, символов и строк в файлах, но чтобы с помощью неё посчитать количество строк кода в проекте, придётся написать что-то такое:

$ fd -g '*.kt' | xargs wc -l

У такой команды есть сразу несколько недостатков:

  • считаются все строки, включая комментарии и пустые строки,

  • ищутся только файлы с расширением .kt, для поиска других придётся менять команду,

  • сгенерированные файлы и остальные файлы, которые заигнорены в гите, тоже попадут в статистику,

  • такую команду долго писать.

Утилита tokei лишена перечисленных недостатков. Вот пример вывода tokei на одном из наших проектов:

Упс, файлы proguard засчитались в пользу PrologУпс, файлы proguard засчитались в пользу Prolog
Свободное место на диске: du ncdu

Ещё один пример разницы CLI и TUI. В отличие от du, ncdu это TUI. Тут же можно перейти внутрь папки или удалить ненужное нажав d.

Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.
Сравнение файлов: diff delta

Отличная замена старому-доброму diff - delta. Можно использовать режим отображения side-by-side, если больше нравится, включить отображение номеров строк. Даже без дополнительных настроек диффы выглядят приятно:

Измерение времени работы программы: time hyperfine

Не верьте на слово, если я говорю, что одна утилита работает быстрее другой. Лучше проверьте.

Можно измерить время выполнения команды с помощью time (в macOS gtime). Эта утилита не предназначена для бенчмарков нет возможности прогрева, команда выполняется один раз. hyperfine подойдёт лучше, потому что изначально разработан для бенчмарков.

Попробуем замерить время выполнения команды tree:

Вывод команды tree перенаправлен в пустоту (/dev/null), потому что здесь не важен вывод команды, важно только время её выполнения. С hyperfine этого делать не нужно, он сам отбрасывает вывод команды.

Hyperfine умеет отслеживать признаки неправильного бенчмарка. Вот некоторые подсказки:

  • если во время замера есть выбросы, hyperfine посоветует закрыть другие программы, потому что они могут влиять на бенчмарк,

  • если первый запуск команды дольше остальных, hyperfine посоветует сделать прогрев, задав параметр --warmup N. Перед бенчмарком программа выполнится N раз.

Можно сравнивать скорость выполнения нескольких команд. Для этого нужно написать команды друг за другом, заключив их в кавычки:

$ hyperfine 'command_one' 'command_two' 'command_three'

Шаг 5: Сохраняем настройки

Чтобы не настраивать каждый раз все программы заново, нужно где-то сохранять настройки.

Конфиги это файлы. Обычно они хранятся в корневой директории пользователя вместе со скриптами инициализации командной оболочки, например, в папке .config/. Если вы установили fish, то найдёте папку .config/fish/ и в ней файлы с настройками. Самый простой способ сохранить конфиги сохранить их в Git-репозиторий.

Имена файлов и папок с настройками обычно начинаются с точки, поэтому одним словом их называют dotfiles. На момент написания статьи на GitHub опубликовано 138 425 репозиториев с именем dotfiles есть куда подсмотреть.

На странице awesome-dotfiles вы найдёте много информации про dotfiles. Там же есть ссылки на инструменты, которые помогают управлять dotfiles.

Я использую yadm. Мне важна кроссплатформенность, поэтому пригождается его возможность создавать альтернативные версии файлов для разных ОС.

Заключение

В консоли может быть сложно работать, если ее не приручить. Но если выбрать удобную для себя командную оболочку и один раз ее настроить, то она будет постоянно помогать вам.

Чтобы не настраивать рабочее окружение на каждом новом компьютере заново, сохраняйте конфиги в Git-репозиторий. Но не забывайте о безопасности следите, чтобы в репозиторий не попал ключи и пароли.

На замену старым утилитам приходят новые, нацеленные на пользователя, а не на скрипты. Используйте их и ищите новые! Если вам кажется, что утилита слишком сложная, возможно, вы такой не один и кто-то уже написал более простой в использовании аналог.

И самое главное читайте документацию и исследуйте. Порой интересные возможности обнаруживаются у, казалось бы, полностью знакомых утилит.

Если будут вопросы или вам понадобится помощь с освоением консоли, пишите мне в Telegram@osipxd. Ещё я иногда пишу в канал @rareilly заметки про Android и вообще про всё интересное, что нахожу. Спасибо за внимание!

Что ещё почитать

Подробнее..

Из песочницы Ты еще не используешь tmux наполную! (Наверное)

25.08.2020 12:13:42 | Автор: admin
Привет, Хабр! Представляю вашему вниманию перевод статьи "(tmux".

Содержание


  • Что такое tmux
  • Запуск и начало новой сессии
  • Открытие нового окна
  • Переходы между окнами
  • Разделение окон и создание панелей
  • Передвижение между панелями
  • Передвижение между сессиями
  • [Практическое использование] Переадресация отображаемого содержания в tmux
  • [Практическое использование] Отображение названия ветки Git в информационном поле
  • [Практическое использование] Отображение пути текущей операции
  • [Практическое использование] Вызов сниппета
  • Ссылка на настройки .tmux.conf
  • Совместимость версий tmux

Что такое tmux


tmux терминальный мультиплексор. Когда работаешь на машине, использующую SSH, исполняешь увесистую программу, но хочешь без прекращения исполнения перейти к другой задаче и т.д., то tmux может очень пригодиться.

Но возможности tmux на этом на этом не заканчиваются. Есть различные удобные варианты использования tmux, что я и хочу продемонстрировать на примерах ниже.

Базовая идея похожа на моды в Vim. С помощью prefixa (зарезервированные key bindings или привязка клавиш) можно изменить мод, по умолчанию используется привязка ctrl + b.

Запуск и начало новой сессии


Если ваша ОС Ubuntu или Debian, вы можете установить tmux командой `sudo apt-get install tmux`. На MacOS brew install tmux.

Команда для запуска:

$ tmux

Если хотите запустить tmux и сразу дать название новой сессии, используйте эту команду:
$ tmux new -s { Название сессии }

image

Открытие нового окна


Создав сессию в tmux, вы также можете создавать новые окна. Стандартная связка ctrl +b + c. Если вы сохранили свой prefix, можете использовать связку prefix + c. Скорее всего, значение c create, отсюда и такая связка.

image

Переходы между окнами


Можно также свободно переключаться между созданными окнами. Стандартные связки для этого prefix + p (p previous) и prefix + n (n next).

Использовать prefix не всегда удобно, поэтому я для себя кастомизировал связки клавиш для перехода между окнами: shift + rightKey для следующего окна и shift + leftKey для предыдущего.

image

Разделение окон и создание панелей


Разделение окон в tmux похоже на разделение в vim. Вы делите окно на несколько панелей, тем самым в одном окне можно открыть vim, в другом htop, а в третьем выполнять программу.

Без привязки клавиш, команды для разделения окна prefix + split-window -h, prefix + split-window -v.

Так как прописывать это каждый раз не особо хочется, я сделал для себя хоткеи в виде prefix + "-", prefix + "|".

image

Передвижение между панелями


Стандартные команды для передвижения между панелями сложноваты, не очень удобны в обращении, поэтому я и в этом случае назначил свои хоткеи: ctrl + shift + upKey, ctrl + shift + downKey, ctrl + shift + rightKey, ctrl + shift + leftKey.

image

Передвижение между сессиями


С помощью связки клавиш ctrl + t можно вызвать список сессий, а клавишами выбрать нужную сессию.

image

[Практическое использование] Переадресация отображаемого содержания в tmux


По сути, tmux является своего рода виртуальным терминалом, содержащимся в директории /dev/pts/{fooBar}. Для каждой панели создается такая же директория.

Другими словами, если мы определим {fooBar} каждой панели, мы сможем передавать содержимое панели X, разделив, например, на stderr и stdout, на панели Y и Z.

Таким образом можно выводить на разные панели stderr и stdout программы, которые вы пишите, а также использовать терминал как лог операций.

image

[Практическое использование] Отображение названия ветки Git в статус баре


Это лайфхак, который использует возможность чтения Shellscript и Python script из .tmux.conf файла конфигурации tmux.

Статус бар можно настроить следующей командой:

set-option -g status-right '#{host} #(tmux run-shell "tmux_hook --default true --git-path #{pane_current_path}")'

#{host} это, очевидно, имя хоста, а tmux run-shell запускает настраиваемый скрипт, который возвращает value в настройки статус бар.

tmux_hook это файл, который я создал для отображения времени и текущей ветки Git, но он, по сути, может вернуть любую текстовую информацию. Например, если правильно настроить, можно в статус баре отображать даже таймлайн своего твиттера.

image

[Практическое использование] Отображение пути текущей операции


Часто, прыгая по терминалу, хочется понять, в какой ты директории, в каком пути она содержится.

Не хочется для этого лишний раз прописывать pwd, поэтому можно отобразить путь текущей операции в том же месте, где отображается путь до текущей панели.

setw -g pane-border-format '#{pane_tty} | #{pane_current_path}'

image

[Практическое использование] Вызов сниппета


Несмотря на то, что tmux является менеджером сессий с функцией разделения терминала, он может выполнять некоторые функции IDE (круто, не правда ли?). Вы можете забиндить свои хоткеи на специальную настройку send-keys и вызвать их в терминале.

Например, если настроить send-keys как на моем примере ниже, можно просто нажать prefix + 1 и вывести команды import, необходимые для работы с python.

bind -T prefix -n 1 send-keys "import os\nimport sys\n"

image

Ссылка на настройки .tmux.conf


Вот тут

Совместимость версий tmux


Иногда случается, что некоторые настройки tmux не работают в этом случае, его версия может быть либо слишком новой, либо слишком старой.

Все вышеописанное и конфиг, на который я оставил ссылку, для tmux версии 2.8 и выше. Если у вас что либо не работает, проверьте, подходит ли вам данная версия.

Если вы соберетесь самостоятельно что-то отлаживать, то прежде всего стоит обратиться к man tmux самой надежной документации по tmux на данный момент.

Ссылка на оригинал данной статьи тут

Мы будем очень рады, если вы расскажете нам, понравилась ли вам данная статья, понятен ли перевод, была ли она вам полезна?
Подробнее..
Категории: Терминал , Оболочки , Vim , Tmux

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru