Sunflat のブログ

ソフトウェア開発についての話題が多いかも

Linuxの設定ファイルをSubversionで管理する

サンフラットGAMESは、遠隔のLinuxサーバで管理してるのだが(id:sunflat:20071108)、テストサーバ(ローカルのVMWare上)で更新した設定ファイルを本番サーバへコピーするのが、結構大変。
rootではsshログインできないように設定しているので、scpやsftpでは転送できないし、できれば設定ファイルの履歴も保存しておきたい。

というわけで、Linuxの設定ファイルをSubversion(svn)で管理するスクリプトを作ってみた。


最初は、etcディレクトリ以下をそのままsvnリポジトリと同期できないかなぁと思ったけど、そこらじゅうに.svnディレクトリが出来るのもいやなので、リポジトリとの同期用のディレクトリを別に用意して、

という3段階で同期することにした。


以下のようなファイルリストを書いておくと、その設定ファイルが同期される。

filelist
process_file /etc/httpd/conf/httpd.conf
process_file /etc/httpd/conf/vhosts.conf
process_file /etc/httpd/conf/ssl.conf
process_file /etc/pound.cfg
process_file /etc/postfix/main.cf
process_file /etc/postfix/virtual
exec_when_update postmap /etc/postfix/virtual
process_file /root/tools/*

※ exec_when_update は、直前のファイルが更新したときに実行されるコマンド


まず、/root/configsなどに以下のディレクトリ構成を作成し、そのディレクトリ全体をsubversionリポジトリにcommitしておく。

  • commit_configs
  • update_configs
  • common.sh
  • filelist
  • rootdir/ (同期用ディレクトリ。最初は空)


テストサーバ上で、設定をcommit(リポジトリへ保存)したい場合は cd /root/configs; ./commit_configs を実行する。
※更新されたファイルが表示されるので、良ければ y を押す


本番サーバ上で、設定をupdate(リポジトリから更新)したい場合は cd /root/configs; ./update_configs を実行する。
※元のファイルと上書きするファイルのdiffが表示されるので、良ければ y を押す。元のファイルは.bakとして残る。


以下、スクリプト一覧

commit_configs
#!/bin/bash
# commit to repository

source common.sh

function process_file {
  updated=0
  while [ -n "$1" ]; do
    # skip backup file
    if [ ${1##*.} = "bak" ]; then
      shift
      continue
    fi
    
    # copy to repository
    cp -u --parent $1 $fdir
    shift
  done
}

mode='c'
source filelist
[ -f filelist_commit_only ] && source filelist_commit_only

# commit to svn repository
result=`svn status $fdir`
if [ -z "$result" ]; then
  echo "no update"
  exit
fi
echo "$result"
read -p "Ready? (y/n):" -n 1 inp
echo
[ "$inp" = "y" ] || exit
svn add --force $fdir
svn commit -m "commit configs" $fdir
update_configs
#!/bin/bash
# update from repository

source common.sh

function process_file {
  updated=0
  while [ -n "$1" ]; do
    # update only when a repository file is newer
    if [ ! -f "$1" ]; then
      read -p "create $1 ? (y/n):" -n 1 inp
      echo
      if [ "$inp" = "y" ]; then
        cp "$fdir$1" "$1"
	updated=1
      fi
    elif [ "$fdir$1" -nt "$1" ]; then
      diff -u "$1" "$fdir$1"
      diffret=$?
      if [ $diffret -eq 1 ]; then
        read -p "update $1 ? (y/n):" -n 1 inp
        echo
	if [ "$inp" = "y" ]; then
	  cp -p "$1" "$1.bak"
	  cp "$fdir$1" "$1"
	  updated=1
	fi
      elif [ $diffret -eq 0 ]; then
        # update timestamp to skip doing diff on the next time
	touch -r "$fdir$1" "$1"
      fi
    fi	
    shift
  done
}

mode='u'
svn update $fdir
source filelist
[ -f filelist_update_only ] && source filelist_update_only
common.sh
function exec_when_update {
  if [ $mode = 'u' ] && [ $updated -eq 1 ]; then
    echo exec on update: $*
    $*
  fi
}

fdir='rootdir'

CentOS 4.6上で動かしました。
※ ファイルの上書き等行いますので、万が一使いたい場合は、十分動作確認してからお願いします。