каталог zone-files: содержит файлы зон, которые могут быть отредактированы командой проекта;
каталог zone-backup: при каждом билде — скрипт с помощью Azure CLI сохраняет текущие файлы зон сюда;
каталог scripts: скрипты 🙂
Файлы зон — это обычные txt-файлы в том виде, как их можно увидеть на BIND-сервере, например:
$ cat zone-files/domain.ms.txt
; Exported zone file from Azure DNS
; Resource Group Name: DNS
; Zone name: domain.ms
; Date and time (UTC): Fri Mar 25 2016 11:42:14 GMT+0000
$TTL 3600
$ORIGIN domain.ms.
@ 3600 IN SOA ns1-02.azure-dns.com. msnhst.microsoft.com. (
2016032514
3600
300
2419200
300
)
@ 3600 IN A 104.40.183.236
@ 3600 IN MX 10 mx.domain.net.
@ 172800 IN NS ns1-02.azure-dns.com.
172800 IN NS ns2-02.azure-dns.net.
172800 IN NS ns3-02.azure-dns.org.
172800 IN NS ns4-02.azure-dns.info.
www 3600 IN CNAME domain.ms.
test 3600 IN CNAME domain.ms.
GoCD билд
В GoCD имеется pipeline:
Который состоит из трех шагов:
DNSverify: вызывается azure network dns zone import с опцией --parse-only, и в цикле проверяет все файлы зон из каталога zone-files;
DNSbackup: использует azure network dns zone export для выгрузки все имеющихся на данный момент зон в каталог zone-backup;
DNSupdate: собственно, обновление данных — с помощью azure network dns zone import каждый файл зоны из zone-files загружается в Azure DNS.
Для авторизации используется пользователь и пароль, которые передаются через Secure variables:
А так выглядит один из шагов билда:
Bash скрипт и Azure CLI
Основная часть всей схемы:
#!/usr/bin/env bash
# must be present from CI variables
USER=$AZURE_USER
PASS=$AZURE_PASS
SUB_ID=$SUBSCRIPTION_ID
# группа ресурсов
RES_GR="DNS"
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
# reset all options
# изврат делать это через массив - но выглядит приятнее
declare -A opts
opts=([verify]=0 [backup]=0 [import]=0)
if [ -z $* ]; then
echo "No options found!"
exit 1
fi
# скрипт принимает три опции, по кол-ву шагов в билде
# -v: verify zone-files before import
# -b: backup zones before import
# -i: import zones to Azure DNS
while getopts "vbi" opt; do
case $opt in
v)
opts[verify]=1
;;
b)
opts[backup]=1
;;
i)
opts[import]=1
;;
*)
echo "No reasonable options found!"
exit 0
;;
esac
done
main () {
# main() - используется как "инициализатор" Azure CLI
# которому первым аргументом передается следующая функция для вызова
echo -e "\n[$me] Azure CLI initialization started\n"
# go to HOME and save curr path
pushd $HOME &> /dev/null
# "хак", что бы пропустить установку Azure CLI, если пакет уже установлен
if [ $(node -p "require('azure-cli/package.json').version") ]; then
echo -e "[$me] Azure CLI already installed, skip.\n"
else
npm install azure-cli
fi
export PATH="$PATH":"$HOME/node_modules/azure-cli/bin"
# go back to /var/lib/go-agent-2/pipelines/AzureDNSupdate
popd
if azure login -v -u $USER -p $PASS; then
azure account set -v $SUB_ID
else
exit 1
fi
azure config mode arm
azure provider register --namespace Microsoft.Network
# after initialization - call function specified in first argument
$1
}
dns_import () {
echo -e "\n[$me] Azure DNS zones import started.\n"
if [ -d zone-files ];then
# create zone names from zone file name
for i in $(ls -1 zone-files); do
# получаем имя домена из имени файла, вырезая .txt
zone=$(echo $i | sed 's/.txt//')
# force используется, что бы не спрашивать при перезаписи зоны
azure network dns zone import -v -g $RES_GR -n "$zone" -f zone-files/"$zone".txt --force
done
else
echo -e "\nERROR: can not find zone files directory in $(pwd). Exit.\n"
ls -l
exit 1
fi
}
dns_verify () {
echo -e "\n[$me] Azure DNS zones verify started.\n"
if [ -d zone-files ];then
# create zone names from zone file name
for i in $(ls -1 zone-files); do
zone=$(echo $i | sed 's/.txt//')
echo -e "[$me] Checking $zone..."
if [ -e zone-files/"$zone".txt ]; then
# grep "warn" or "error" from Azure CLI output
# не нашел адекватного способа получить exit code, грепаем warn или error
azure network dns zone import -v -g $RES_GR -n "$zone" --parse-only -f zone-files/"$zone".txt | grep "warn\|error" && {echo -e "\nERROR during parsing zone file $zone.txt. Exit." && exit 1} || echo "$zone check passed"
else
echo -e "\nERROR: can not find zone-files/"$zone".txt file. Exit.\n"
exit 1
fi
done
echo -e "\n[$me] All zone files checked successfully.\n"
else
echo -e "\nERROR: can not find zone files directory in $(pwd). Exit.\n"
ls -l
exit 1
fi
}
dns_backup() {
echo -e "\n[$me] Azure DNS zones backup started.\n"
# получаем список всех доменов, которые сейчас есть на NS
zones=$(azure network dns zone list -g $RES_GR --json | grep "name" | cut -d":" -f 2 | sed 's/"//g' | sed 's/,//' | tr -d " ")
if [ -z $zones ]; then
echo -e "WARNING: zones files not found. Skip.\n"
exit 0
fi
for zone in $zones; do
echo -e "Zone found: $zone.\n"
# и выгружаем их в zone-backup
if azure network dns zone export -q -v -g $RES_GR -n "$zone" -f zone-backup/"$zone".txt; then
echo -e "\n$zone exported to zone-backup/"$zone".txt\n"
else
echo -e "\nERROR during $zone export. Exit.\n"
exit 1
fi
done
git add -A
git commit -m "$(date +%Y%m%d) zones backup"
git push origin
}
# проверяем полученные аргументы, и запускаем те, для которых значение == 1
# первой вызывается main(), которой первым аргументом передается следующая функция
# $act in ${!opts[@]} == "verify" or "backup" or "import"
for act in "${!opts[@]}"; do
# i.e.: if ${oprts[verify]} == 1
if [ "${opts[$act]}" == 1 ]; then
# main() with first argument like "dns_verify()"
main dns_$act
fi
done