Tomcatの設定ファイル差分比較スクリプト

###はじめに
Tomcatの仕様やコンフィグを調査し、新バージョンとの差異を比較するShellscriptを作成しました。

開発担当でない場合、[Tomcatのchangelog](https://tomcat.apache.org/tomcat-8.5-doc/changelog.html)を見ても何がアプリの仕様に影響を及ぼすか分からないことがあるため、検証前の準備や調査は重要です。
OSSはセキュリティ修正などで頻繁に更新されるため、脆弱性対応で単純にアップデートすると大事故につながる可能性もあります。

また、以下のような情報不足を補う効果も期待できます。
・実は環境によって使っているコンフィグやライブラリが異なっている
・実は独自に追加したファイルが無いと動かない
・実はドキュメントにある情報が古い
・実はetc...

###内容
bin/conf/libの差分を比較し、ディレクトリ内に差分のログファイルを出力します。
以下のようにdiffのオプションを駆使すると良い感じに差分がでますが、目視で変更箇所を把握すると漏れる可能性もありますので、grepしたログも出力しています。
`diff --side-by-side --strip-trailing-cr -Bw `

本例では、現行の何らかのTomcatバージョン8.5.xと、8.5.50で比較します。
開発環境のTomcatがdevelopで、app1/app2/app3と分かれている時にbin/conf/lib以下のファイルを比較することを想定します。

スクリプトの環境変数を変更することで、比較するバージョンや対象を変更することも可能です。
比較対象のディレクトリ/ファイルは、予め用意しておく必要があります。
ログファイルのディレクトリとログファイルは実行時に初期化・削除されます。

###コード

```bash:tomcat_diff_version.sh
#!/bin/bash

ENV="develop"
TOMCAT_DEFAULT_VERSION="8.5.50"
TOMCAT_DEFAULT="../apache-tomcat-${TOMCAT_DEFAULT_VERSION}"
DIR_LOG_DIFF="diff"

for type in app1 app2 app3 
do
  for dir in bin conf lib
  do
    dir_diff="${ENV}_${type}/${DIR_LOG_DIFF}_${TOMCAT_DEFAULT_VERSION}/${DIR_LOG_DIFF}_${dir}"
    if [ -d ${dir_diff} ]; then
      sudo rm -r ${dir_diff}
      mkdir -p ${dir_diff}
      else if [ ! -d ${dir_diff} ]; then
        mkdir -p ${dir_diff}
      fi
    fi
  done
done

for type in app1 app2 app3 
do
  for dir in bin conf lib
  do
    for file in $(ls ${ENV}_${type}/${dir})
    do
      file_default="${TOMCAT_DEFAULT}/${dir}/${file}"
      file_current="${ENV}_${type}/${dir}/${file}"
      dir_diff="${ENV}_${type}/${DIR_LOG_DIFF}_${TOMCAT_DEFAULT_VERSION}/${DIR_LOG_DIFF}_${dir}"
      file_diff="${ENV}_${type}_${TOMCAT_DEFAULT_VERSION}_${dir}_${file}.txt"
      name="${dir}/${file} <DEFAULT CURRENT>"

      echo ${name} >> ${dir_diff}/${file_diff}
      if [[ ${file} =~ .*\.jar$ ]]; then
        diff --side-by-side --strip-trailing-cr -Bw <( jar -tf ${file_default} ) <( jar -tf ${file_current} ) \
        >> ${dir_diff}/${file_diff}
      else
        diff --side-by-side --strip-trailing-cr -Bw ${file_default} ${file_current} \
        >> ${dir_diff}/${file_diff}
      fi
      echo ${name} >> ${dir_diff}/"grep_${file_diff}"
      echo "grep <" >> ${dir_diff}/"grep_${file_diff}"
      grep "<" ${dir_diff}/${file_diff} | grep -v "${name}" >> ${dir_diff}/"grep_${file_diff}"
      echo "grep >" >> ${dir_diff}/"grep_${file_diff}"
      grep ">" ${dir_diff}/${file_diff} | grep -v "${name}" >> ${dir_diff}/"grep_${file_diff}"
      echo "grep |" >> ${dir_diff}/"grep_${file_diff}"
      grep "|" ${dir_diff}/${file_diff} | grep -v "${name}" >> ${dir_diff}/"grep_${file_diff}"
    done
  done
done

```

###ディレクトリ構造

```
investigation_change_tomcat					
|--	apache-tomcat-8.5.50			
|	|--	bin		
|	|--	conf		
|	`--	lib		
|--	develop			
|	|--	diff_tomcat_config.sh		
|	|--	app1		
|	|	|--	bin	
|	|	|--	conf	
|	|	|--	lib	
|	|	|--	diff_8.5.50
|	|	        |--	diff_bin
|	|	        |--	diff_conf
|	|	        `--	diff_lib
|	|-- app2
|	`--	app3		
|--	production			
|--	test			
`--	other		
```

###実行方法と動作要件

実行方法：
1. ディレクトリ移動 (${HOME}にスクリプト一式を置いた場合）
`cd ${HOME}/investigation_change_tomcat/develop`
2. スクリプト実行
`./diff_tomcat_config.sh`

動作要件：

- jarが実行可能であること (libの比較を出力したい場合)
- investigation_change_tomcatに以下ファイルがあること
  - 導入したいTomcatのデフォルトパッケージ
  - 比較したいTomcatのディレクトリ（環境名/アプリ名/比較対象、例えばdevelop/app1/bin）


###ログフォーマット

ログディレクトリ：
investigation_change_tomcat/＜環境名＞/＜アプリ名＞/diff_＜新バージョン番号＞/diff_＜比較対象 bin|conf|lib＞

- 開発環境アプリとTomcat 8.5.50の例

現行とバージョン8.5.50のapp1のbin比較ログ
`investigation_change_tomcat/develop/app1/diff_8.5.50/diff_bin`

現行と新バージョンのapp1のconfの比較ログ
`investigation_change_tomcat/develop/app1/diff_8.5.50/diff_conf`

現行と新バージョンのapp1のlibの比較ログ
`investigation_change_tomcat/develop/app1/diff_8.5.50/diff_lib`


ログファイル：

side-by-sideの比較結果をそのまま出力したもの
`＜現行アプリ名＞_＜新バージョン番号＞_＜比較対象 bin|conf|lib＞_＜比較対象ファイル＞.txt`
例：
`app1_8.5.50_bin_version.sh.txt`

diff --side-by-sideの出力結果のうち差分だけをgrepしたもの
`grep_＜現行アプリ名＞_＜新バージョン番号＞_＜比較対象 bin|conf|lib＞_＜比較対象ファイル＞.txt`
例：
`grep_app1_8.5.50_bin_version.sh.txt`


###出力ログ例

センターラインに出力された記号によって、以下のような意味があります。

`>`：現行(CURRENT)にのみある要素
`<`：新バージョン(DEFAULT)にのみある要素
`|` ：現行と新バージョンにあるが変数などが異なる要素

 - catalina.shの場合

\#行数が長いため一部抜粋してます。

```
bin/catalina.sh <DEFAULT CURRENT>
#!/bin/sh                                                       #!/bin/sh

# Licensed to the Apache Software Foundation (ASF) under one    # Licensed to the Apache Software Foundation (ASF) under one
# contributor license agreements.  See the NOTICE file distri   # contributor license agreements.  See the NOTICE file distri
# this work for additional information regarding copyright ow   # this work for additional information regarding copyright ow
# The ASF licenses this file to You under the Apache License,   # The ASF licenses this file to You under the Apache License,
# (the "License"); you may not use this file except in compli   # (the "License"); you may not use this file except in compli
# the License.  You may obtain a copy of the License at         # the License.  You may obtain a copy of the License at
#                                                               #
#     http://www.apache.org/licenses/LICENSE-2.0                #     http://www.apache.org/licenses/LICENSE-2.0
#                                                               #
# Unless required by applicable law or agreed to in writing,    # Unless required by applicable law or agreed to in writing,
# distributed under the License is distributed on an "AS IS"    # distributed under the License is distributed on an "AS IS"
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expres   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expres
# See the License for the specific language governing permiss   # See the License for the specific language governing permiss
# limitations under the License.                                # limitations under the License.

# -----------------------------------------------------------   # -----------------------------------------------------------
# Control Script for the CATALINA Server                        # Control Script for the CATALINA Server
#                                                               #
# For supported commands call "catalina.sh help" or see the u <
# the end of this file.                                       <
#                                                             <
```

\#前の結果を`< > |`で単純にgrepしたログは以下です。
\#コメントの追加や、コードがリファクタリングされていることが推察されます。

```
bin/catalina.sh <DEFAULT CURRENT>
grep <
# For supported commands call "catalina.sh help" or see the u <
# the end of this file.                                       <
#                                                             <
#                   This is only supported for Java <= 8.       #                   This is only supported for Java <= 8.
# TODO: Bugzilla 63815                                        <
# This doesn't currently work (and can't be made to work) if  <
# CATALINA_OPTS and/or JAVA_OPTS require quoting. See:        <
# https://bugs.openjdk.java.net/browse/JDK-8234808            <
grep >
  link=`expr "$ls" : '.*-> \(.*\)$'`                              link=`expr "$ls" : '.*-> \(.*\)$'`
  if expr "$link" : '/.*' > /dev/null; then                       if expr "$link" : '/.*' > /dev/null; then
[ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." >/d   [ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." >/d
          ps -p $PID >/dev/null 2>&1                                      ps -p $PID >/dev/null 2>&1
            rm -f "$CATALINA_PID" >/dev/null 2>&1                           rm -f "$CATALINA_PID" >/dev/null 2>&1
                cat /dev/null > "$CATALINA_PID"                                 cat /dev/null > "$CATALINA_PID"
        rm -f "$CATALINA_PID" >/dev/null 2>&1                           rm -f "$CATALINA_PID" >/dev/null 2>&1
      >> "$CATALINA_OUT" 2>&1 "&"                                     >> "$CATALINA_OUT" 2>&1 "&"
      >> "$CATALINA_OUT" 2>&1 "&"                                     >> "$CATALINA_OUT" 2>&1 "&"
    echo $! > "$CATALINA_PID"                                       echo $! > "$CATALINA_PID"
    echo $1 | grep "[^0-9]" >/dev/null 2>&1                         echo $1 | grep "[^0-9]" >/dev/null 2>&1
        kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1                   kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1
      kill -15 `cat "$CATALINA_PID"` >/dev/null 2>&1                  kill -15 `cat "$CATALINA_PID"` >/dev/null 2>&1
        kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1                   kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1
          rm -f "$CATALINA_PID" >/dev/null 2>&1                           rm -f "$CATALINA_PID" >/dev/null 2>&1
              cat /dev/null > "$CATALINA_PID"                                 cat /dev/null > "$CATALINA_PID"
            kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1                   kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1
                rm -f "$CATALINA_PID" >/dev/null 2>&1                           rm -f "$CATALINA_PID" >/dev/null 2>&1
                        cat /dev/null > "$CATALINA_PID"                                 cat /dev/null > "$CATALINA_PID"
grep |
if [ -t 0 ]; then                                             | if [ "`tty`" != "not a tty" ]; then
    UMASK="0027"                                              |     UMASK="0022"
    _NOHUP="nohup"                                            |     _NOHUP=nohup
    eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_ |     eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_
    eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_ |     eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_
    eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGI |     eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGI
    eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGI |     eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGI
    echo $1 | grep "[^0-9]" >/dev/null 2>&1                         echo $1 | grep "[^0-9]" >/dev/null 2>&1
  eval "\"$_RUNJAVA\"" $LOGGING_MANAGER "$JAVA_OPTS" \        |   eval "\"$_RUNJAVA\"" $JAVA_OPTS \
    eval "\"$_RUNJAVA\"" $LOGGING_MANAGER "$JAVA_OPTS" \      |     eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \
```

###まとめ

Tomcatのコンフィグ差分を出力するスクリプトを作成し、調査にかかる時間を短縮することができました。
特に、ドキュメントがなかったり仕様が知らぬ間に変更されるような環境の場合は現行調査が一番です。

差分を出力する際に、たまに出力文字数の関係でログが見切れてしまい、詳細まで把握できてないことが課題として残りました。
その場合はファイルを直接開いて確認していました。

もうちょっといい方法があったら改めて実装してみたいと思います。