Ansibleのcowsay出力を虹色に輝かせたい

cowsayもきっとゲーミング色に輝いて喋りたい時がある。
2023.05.31

初めに

Ansibleをご利用の多くの方はご存知かと思いますが、Ansibleは実行環境にcowsayがインストールされている場合デフォルトでcowsayによる出力が行われるようになっており、非常に重要なツールの1つとなっております。

とはいえ通常cowsayを利用したいものの、プロフェッショナルな環境であれば時にはそういったジョークめいた出力は許されず無効化しないといけないケースはあるかと思います。

https://docs.ansible.com/ansible/latest/reference_appendices/config.html
ANSIBLE_NOCOWS
Description If you have cowsay installed but want to avoid the ‘cows’ (why????), use this.

永続的に無効化するのであればcowsay自体を削除してしまうのも1つの手ですが、一時的に無効化したい時のためにAnsibleにはそれを無効化するための専用のオプションが用意されています。

出力を抑えたいケースがあり得る一方で、いやそうではないもっと壮大な、たとえばドラゴンのようなキャラクターを出力してもらいたいケースもあるでしょう。

そういった場合に備えAnsibleではANSIBLE_COW_SELECTION環境変数を指定することでcowsayに対して-fオプションを与えたように実行をすることができます。

% ANSIBLE_COW_SELECTION=dragon ansible-playbook playbook.yml
...
 __________________
< PLAY [localhost] >
 ------------------
      \                    / \  //\
       \    |\___/|      /   \//  \\
            /0  0  \__  /    //  | \ \
           /     /  \/_/    //   |  \  \
           @_^_@'/   \/_   //    |   \   \
           //_^_/     \/_ //     |    \    \
        ( //) |        \///      |     \     \
      ( / /) _|_ /   )  //       |      \     _\
    ( // /) '/,_ _ _/  ( ; -.    |    _ _\.-~        .-~~~^-.
  (( / / )) ,-{        _      `-.|.-~-.           .~         `.
 (( // / ))  '/\      /                 ~-. _ .-~      .-~^-.  \
 (( /// ))      `.   {            }                   /      \  \
  (( / ))     .----~-.\        \-'                 .~         \  `. \^-.
             ///.----..>        \             _ -~             `.  ^-`  ^-_
               ///-._ _ _ _ _ _ _}^ - - - - ~                     ~-- ,.-~
                                                                  /.-~

しかしながら自分の気持ちとしてはcowsayは牛が出力されるから"cow"sayであり、違うキャラにより輝いてもらいジョーク度を高めるのは違うのではないかとものがあります。

cowsayは牛のまま輝いてほしいということで、目立つならゲーミングカラー、つまり今回は虹色に輝いてもらいます。

利用ツール

cowsayの呼び出しを差し替える

Ansibleで実行されるcowsayは以下のパスに設置されているファイルを実行する仕様となっております(上位の方が優先度が高い)。

https://github.com/ansible/ansible/blob/stable-2.14/lib/ansible/utils/display.py

b_COW_PATHS = (
    b"/usr/bin/cowsay",
    b"/usr/games/cowsay",
    b"/usr/local/bin/cowsay",  # BSD path for cowsay
    b"/opt/local/bin/cowsay",  # MacPorts path for cowsay
)

上記以外のパスにcowsayが設置されている、もしくは一時的に別のパスに設置されているcowsayを利用したい場合は環境変数ANSIBLE_COW_PATH、もしくはansible.cfgcowpathパラメータにそのパスを指定することで差し替えることができます。

ANSIBLE_COW_PATH
Description
Specify a custom cowsay path or swap in your cowsay implementation of choice
...
Key
cowpath.

今回は上記の仕様に則りAnsible側で読み込ませる先のパスに自作スクリプトを設置してそれを実行させます。

lolcatについて

lolcatコマンドは出力を虹色に変化されるコマンドであり、今回はこれを利用して虹色に出力させます。

今回はAnsibleのオプションの使い勝手が変わらないように引数は全てcowsayに渡るように以下のようなファイルを/opt/local/bin/cowsayを作成します。

cowsay

#!/bin/bash
# % which cowsay
# /opt/homebrew/bin/cowsay
`which cowsay` $@ | lolcat --force

lolcatにはシンプルにパイプで文字列を渡すだけでそれをグラデーションカラーで出力してくれるツールとなります。

Ansible上のcowsayのカラーリングについて

Ansibleでは出力メッセージレベルごとに文字色をを設定できるパラメータが存在します。

コマンドを前述のものに差し替えた場合でもこの設定により色が潰されていないかを懸念していましたが、そもそも牛の出力されるバナー部分の色に対する設定はないようです。

呼び出し元を追ってみるとcowsay経由での出力を行うbanner_cowsay()メソッドには出力色を指定できるcolor引数が存在しますが、呼び出し元を確認すると実態としてcolorに対する値の引き渡しは特に行われていないようです。
https://github.com/ansible/ansible/blob/stable-2.14/lib/ansible/utils/display.py

そのためlolcatにそのままcowsayの出力を渡せばいい感じに色がつくかなと思ったのですが、実際に試してみると色の変更が行われずターミナルのデフォルト文字色が利用されました。

着色部分を追ってみたところ、ANSIエスケープシーケンスを付与して着色が必要となるようです。
https://github.com/ansible/ansible/blob/stable-2.14/lib/ansible/utils/color.py.

lolcat--forceオプションをつけることでANSIエスケープシーケンスを付与する形で出力ができるようなので、今回はこちらで対応を行いました。

実行

通常通り実行するだけで先ほど設置したcowsayスクリプト経由での出力が行われ、虹色の牛がメッセージを読み上げます。

残念ながら縦横幅的に当初イメージしたより少ない色幅になった気がします。
とはいえこの辺りは出力文字数による幅や、出力時の乱数的に視覚的にわかりやすい色の切り替わり部分であるかというのは影響しそうです。

Ansible経由でのcowsay関連のオプションは通常通り渡るので望むのであればドラゴン等別のものを出力させることも可能です。

終わりに

今回はAnsibleのcowsayの実行原理を利用して虹色に輝かせてみました。

フォーマルな出力が求められることは当然ある一方、ジョークめいた出力が許される環境もあるかとあるのではないかと思います。
やりすぎない程度にジョークを詰めていくのは癒しにもなりますし実は大切なのではないかもしれません。

最終的な出力自体はcowsayからの出力をまとめてAnsibleが受け取り、あくまで最終的にAnsibleが出力を行う関係と仕組み上ストリーミングな出力等できないといった制限がつきまとう点は注意しましょう。

実はlolcatにはアニメーション出力が可能なオプションが存在しますが、今回は上記の仕様上断念する形になりました。

一応Ansibleの出力部分に思いっきり手を入れればできなくはなさそうなのでぜひ興味のある方はチャレンジしてみてください。