Казалось бы, на эту тему все говорено-переговорено, но с завидным постоянством наблюдаю как даже опытные коллеги регулярно создают логические бомбы.
Логическая бомба – это код, который при наступлении некоторых условий, которые можно заранее просчитать и предвидеть, производит некоторые деструктивные действия. И здесь трудно сослаться на случайность или недосмотр.
Почему же так происходит? На наш взгляд – это классическое «и так сойдет» и тот же самый русский авось. Т.е. надежда на то, что обстоятельства, приводящие к срабатыванию логической бомбы, не произойдут или находятся под контролем.
Рассмотрим классический пример, очистка устаревших данных, например, бекапов:
find -type f -mtime +90 -exec rm -rf {} \;Что делает эта команда? Тупо ищет файлы старше 90 дней и удаляет их. И логических бомб тут сразу несколько.
1️⃣ Самая очевидная. Если у нас по какой-либо причине перестанут создаваться резервные копии, то через 90 дней у нас не останется ни одной. И тут ошибочно полагаться на мониторинг или иные средства контроля, так как сбой может произойти в любой части схемы.
Сначала сломался мониторинг, потом перестали создаваться копии – вот и все, привет горячий.
2️⃣ Далее. Команда работает без привязки к конкретному расположению. Если кто-то случайно переместит скрипт, то он подметет вообще случайные данные. Поэтому никаких относительных путей в подобных конструкциях быть не должно. Только абсолютные.
3️⃣ Теперь – самое неочевидное –
find, который не рекомендуется использовать в скриптах в силу ряда особенностей. В частности, если нам попадется файл с пробелами в имени, то строка будет разбита на две, что приведет к неожиданным последствиям. Чаще всего удаление не произойдет, но может случиться разное.Поэтому постараемся сразу учесть эти моменты. Удалять всегда нужно привязываясь не ко времени или еще каким-то внешним параметрам, а по количеству остающихся копий, в этом случае даже если у вас перестанут создаваться копии, то последние всегда останутся. Во многих случаях это гораздо лучше ситуации, когда копии нет вообще.
Т.е. оставляем не копии за последние 90 дней, а 90 последних копий (или более, если в день создается более одной копии).
Жестко привязываемся к путям и учитываем возможность появления пробелов в имени файла:
find /mnt/backup/ -type f -printf '%T@ "%p"\n' | \
sort -n | \
head -n -90 | \
awk '{print $2}' | \
xargs -I {} rm {}
Конструкция стала сложнее, мы, как и прежде ищем файлы, но сразу указываем директорию поиска, а список выводим построчно, в каждой строке размещая временную метку файла и его имя, взятое в кавычки.
Сортируем от самых старых к самым новым и отбрасываем последние 90 записей. Затем для каждой строки получаем второй аргумент (имя файла в кавычках) и передаем его на удаление.
Однозначно стало лучше, но не совсем. Что будет, если в эту папку попадут другие файлы? Они также будут удалены, при этом мы снова легко можем остаться без бекапов. Что будет если кто-то случайно закинет туда 100 новых файлов?
Правильно – из них останется только 90 самых последних и ни одного бекапа, ну или самый последний. Это видно на берегу? Видно. Значит это очередная логическая бомба и надо от нее избавляться.
Допустим, наши бекапы имеют имя
base1-YYYY-MM-DD.dump, поэтому добавляем в строку поиска новое условие: find /mnt/backup/increment/ -type f -name "base1*.dump" -printf '%T@ "%p"\n'
Или даже
find... -name "base1-*-*-*.dump"...
Чем более строгое совпадение в шаблоне – тем безопаснее, но во всем нужно видеть меру и вовремя остановиться, потому что если увлечься, то можно погрязнуть в проверках для совсем уже маловероятных условий или событий.
Да, и не забывайте логировать все что делают подобные скрипты, чтобы в случае какой-либо нештатной ситуации вы хотя бы могли выполнить работу над ошибками.
