自作 Splunk app を Python v2/v3 対応にして Splunkbase を更新するまでの道のり

この記事は Splunk Advent Calendar 2019 の記事の一つです。
[他の記事](https://qiita.com/advent-calendar/2019/splunk) もお楽しみくださいね。

今回は、自作の App を [Splunkbase](https://splunkbase.splunk.com/) で公開したは良いけれど、
そろそろ (?!) Python 3 対応をしないとなぁ・・
Splunk も [Python 3 移行するよ！](https://www.splunk.com/en_us/blog/tips-and-tricks/admins-and-developers-we-re-transitioning-to-python-3.html) と言ってたしなぁ・・

という締め切りに追われる Splunk App 開発者向けの内容です。

簡単に自己紹介しますと、
普段のお仕事では、Splunk の構築やトラブルシュートを主に担当しています。
好きな機能は Metrics type インデックスです。

## 記事の背景について、簡単に。

### そもそも Python 3 移行って？

インタプリタ系プログラミング言語の Python は
v2 系と v3 系が長らく使われてきていますが、
開発コミュニティによる v2 系 (v2.7) のメンテナンスが
[2020/1/1 をもって終了](https://www.python.org/dev/peps/pep-0373/)することが、
12 年前 (2008 年) からアナウンスされていました。

さあ、2019 年もいよいよ、あと残すところ[僅か](https://pythonclock.org/)。
重い腰を上げるときが来ました。

> ちなみに、メンテナンスが終了するだけで、
> 2020/1 以降、急に動かなくなるわけでは無いですよ。 念の為。


### そもそも、Python 3 対応で書いておけば良いじゃない。

はい。おっしゃる通りです。

しかしながら、

* 利用しているライブラリがまだ v3 対応していない。
* 改修ポイントがよく分からない (不勉強なだけか・・)。
* 動いてるんだから良いじゃない。

などと言った、理由で先送りされてしまうケースも多いかと思います。

### Splunk の Python 事情。

しかも、Splunk に関しては、ちょっと特殊な事情があります。

Splunk というソフトウェアには、
**Python 実行環境が同梱されています**。
ですので、OS 側に別途 Python をインストールしたりすることなく、
プラグイン (App, Add-on) で利用することができました。

この同梱 Python が、つい先日 (2019/10/22) まで、
Python 2.7 しか選択できない状況でした。

v3 系である Python 3.7 が同梱され始めたのは、Splunk v8.0.0 から。

さぁ、環境を言い訳にできなくなったのです (誤解を生むニュアンスだな)。

ちなみに v8.0.0 には、Python 2.7 と Python 3.7 の
両方の環境が同梱されており、
App, Add-on で利用する機能ごとに
どちらの環境で動かすかを選ぶことができます。
デフォルトでは、従来どおりの v2.7 系で動作します
(ただし、ウェブアプリ機能など一部の例外を除く)。

今後のロードマップとしては、
2020 年の中頃を目処にデフォルトの Python が v2.7 から v3.7 に変更 (v2.7 を選択することは可能)。
2020 年の後半のリリースでは、v2.7 の同梱が廃止され、v3.7 のみになるようです。

#### v3.7 に書き換えるだけ？

移行にあたっての注意点として、
**v2/v3 の互換記法が強く推奨されます**。

えー、v2 は EOL でメンテナンスされなくなるって言ってたじゃん、
というご意見はごもっともなのですが・・

Splunk に馴染みのある方には、聞き覚えがあるかと思いますが、
分散サーチ、という機能があります。

Splunk は、単一のインスタンスで all-in-one で利用することもできますし、
主要な機能ごとにインスタンスを分け、より検索効率や可用性を高めることも可能です。
主な役者としては、

* サーチ (検索) を受付け、結果の表示を行うサーチヘッド (SH)
* データの格納とサーチに基づくデータを返すインデクサ (IDX)
* 新しいデータを取り込みインデクサに転送するフォワーダ (FWD)

に分けられます。
この分散構成を採ったとき、
サーチヘッドで受け付けたサーチ命令を、
複数のインデクサ (peers とも呼ばれます) に、
分散サーチ処理を依頼します。

このとき、サーチヘッド上にある App も、
命令の一部としてインデクサに渡され、インデクサ上で実行されます。

カスタムサーチコマンド等で、
Python を利用した処理を書いてある場合、
サーチヘッドとインデクサの設定 (Python 環境の指定) も
同一なら良いのですが、
サーチヘッドは v3.7 対応済み、
インデクサは v2.7 で動作するという指定の場合、
v3 準拠、v2 非互換の記法があると
インデクサ上の分散処理が失敗してしまう、という
シナリオが待ち受けています。

そんな状況のため、Python v2/v3 両対応の記法が
推奨されています。

## Python v2/v3 の違いって？

だんだん、本題から逸れていきそうなので
Python のバージョンに関連する違いの詳細は端折りますが、
詳しくは他の記事などをご参考ください。

* [Python 2 から Python 3 への移植](https://docs.python.org/ja/3/howto/pyporting.html)
* [Python2からPython3.0での変更点](https://qiita.com/cstoku/items/353fd4b0fd9ed17dc152)
* [Python 2.7.x と 3.x の決定的な違いを例とともに](https://postd.cc/the-key-differences-between-python-2-7-x-and-python-3-x-with-examples/)
* [Python3にすんなり移行できないから 2-3 互換を考えよう](https://qiita.com/ta_ta_ta_miya/items/33481af5f7d1a59d3ccb)

私の経験としては、

  * `print` が文から関数に (`print` の後に丸カッコが必要に)
  * 除算の演算子 (`/`, `//`) の挙動が変更に。
  * 四苦八苦して対応した文字コード対応が、却って誤動作。
  * 通ぶって使っていた `reduce` 等が、`functools` へ移行。
  * `ConfigParser` が `configparser` に。

といったあたり、ハマった憶えがあります。

互換記法への書き換えにあたっては、
six と tox という 2 つをご紹介しておきます。
一言で言うと、six は、関数名などの違いを吸収するためのもの、
tox は、v2/v3 両方の単体テストを行うものです。 

Python v2 と v3 の違いを
ライブラリで吸収しようという取組み
[six](https://six.readthedocs.io/) は、
こちらは、Splunk App 開発向けのライブラリにも同梱されています。

また、Python 2 および Python 3 それぞれの
環境で単体テストを実行するための
[tox](https://tox.readthedocs.io/en/latest/) 
フレームワークも用意されています。
同じコードの単体テストを、
複数のインタプリタ環境 (Python v2 と v3 など) で実行することができます。

文法的に問題ないというチェックまでは、
ある程度、フレームワーク等で機械的に対応できます。

## 自作 Splunk App の書換え。

さて、話のマクラがだいぶ長くなりましたが、そろそろ本題に。

App の互換性チェックには、
[Splunk Platform Upgrade Readiness App](https://splunkbase.splunk.com/app/4698/) も用意されていますが、
Splunk Enterprise 環境が必要だったりするので、
まずは、お手軽に AppInspect の REST API からご紹介。
その後、Splunk Platform Upgrade Readiness App についても後述します。

### 互換性チェックその 1: AppInspect REST API

#### 認証トークンの取得

```none
$ curl -X GET \
       -u {splunk.com のユーザ名} \
       --url "https://api.splunk.com/2.0/rest/login/splunk"
```

パスワードを聞かれるので、
splunk.com のユーザアカウントのパスワードを入力します。

```javascript
{
    "data": {
        "token": "{トークン文字列。以下、$TOKEN で参照}",
        "user": {
            "email": "{登録しているメールアドレス}",
            "groups": [
                "Beta Users"
            ],
            "name": "{登録している名前}",
            "username": "{splunk.com のユーザ名}"
        }
    },
    "msg": "Successfully authenticated user and assigned a token",
    "status": "success",
    "status_code": 200
}
```

ここで応答の中にある、`token` の文字列をこの後の手順で使って確認していきます。
(トークン文字列はだいぶ長いので、シェル変数などに入れておくと良いかもしれません)

以下では、環境変数 `TOKEN` にここで取得したトークンが設定されているものとします。
(`$TOKEN` で参照します) 

#### App 検査依頼の発行

```none
$ curl -X POST
       -H "Authorization: bearer $TOKEN"
       -H "Cache-Control: no-cache"
       -F "app_package=@\"line-alert-for-splunk_100.tgz\""
       -F "included_tags=py3_migration"
       --url "https://appinspect.splunk.com/v1/app/validate"
```


```javascript
{
    "request_id": "a32e91f8-7767-400f-a33f-xxxxxxxxxxxx",
    "message": "Validation request submitted.",
    "links": [
        {
            "rel": "status",
            "href": "/v1/app/validate/status/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx"
        },
        {
            "rel": "report",
            "href": "/v1/app/report/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx"
        },
        {
            "rel": "package",
            "href": "/v1/app/package/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx"
        }
    ]
}
```

応答に含まれるリクエスト ID を結果の問合せに使います。

#### App 検査状況および結果の確認

検査リクエストの進行状況を確認するには、
status エンドポイントに問合せます。

```
$ curl -X GET
       -H "Authorization: bearer $TOKEN"
       -H "Cache-Control: no-cache"
       --url https://appinspect.splunk.com/v1/app/validate/status/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx
```

```javascript
{
    "request_id": "a32e91f8-7767-400f-a33f-xxxxxxxxxxxx",
    "status": "SUCCESS",
    "info": {
        "error": 0,
        "failure": 0,
        "skipped": 0,
        "manual_check": 0,
        "not_applicable": 3,
        "warning": 0,
        "success": 10
    },
    "links": [
        {
            "rel": "self",
            "href": "/v1/app/validate/status/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx"
        },
        {
            "rel": "report",
            "href": "/v1/app/report/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx"
        }
    ]
}
```

status が SUCCESS になっていれば完了しています。

結果の取得には report エンドポイントを使います。

```none
$ curl -X GET
       -H "Authorization: bearer $TOKEN"
       -H "Cache-Control: no-cache"
       --url https://appinspect.splunk.com/v1/app/report/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx
```

無指定では json が返ってきます。
`Content-Type` を適宜指定すると、HTML でも結果を取得できます。

```none
$ curl -X GET
       -H "Authorization: bearer $TOKEN"
       -H "Content-Type: text/html"
       -H "Cache-Control: no-cache"
       --url https://appinspect.splunk.com/v1/app/report/a32e91f8-7767-400f-a33f-xxxxxxxxxxxx
```

![](191215-appinspect-MSTeams-1.png)
![191215-appinspect-MSTeams-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/8db79632-f539-e0b9-df53-fd2ce7ae7ec6.png)


こちらのほうが見やすいですね。
Totals 欄の Failures と Error がゼロならば、ひと安心かと。


### 互換性チェックその 2: Splunk Platform Upgrade Readiness App

次に、Splunk Platform Upgrade Readiness App での、
チェック方法をご紹介します。

本来は、Splunk v7.x から Splunk v8.0 へのアップグレードにあたっての
チェックをする App なのですが、
Python 3 対応のチェックもできるので、便利です。

ただし、実行するには、
アップグレード元となる Splunk Enterprise v7.1, v7.2, v7.3 の
何れかの環境が必要になります。
なお、
**[アップグレード後の Splunk 8 では、動作しません！](https://answers.splunk.com/answers/782504/splunk-platform-upgrade-readiness-app-does-not-loa.html)**

運良く対象となる v7.x 系をお持ちの場合は App をインストールし、
Splunk Web UI の App リストから
Splunk Platform Upgrade Readiness App を開きます。

右上の Run New Scan ボタンを選択し、
Scan Settings のプルダウンから
Scan custom selection of apps を選択します。

![](191215-upgrade_readiness_app-MSTeams-3a.png)
![191215-upgrade_readiness_app-MSTeams-3a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/7a74adb3-f170-03f0-fb62-ad9014014eea.png)


画面右にインストール済みの App 一覧が表示されるので、
リストからチェックしたい App を選択。

![](191215-upgrade_readiness_app-MSTeams-3b.png)
![191215-upgrade_readiness_app-MSTeams-3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/dc4ef889-a1ff-0940-5501-7e4a32d96d29.png)


Scan ボタンを選択します。

しばらく待つと、結果が表示されるはず。
App に含まれるファイル次第ですが、数分〜数十分とけっこうかかりますので、
気長に待つのが吉かと。

完了すると、GUI に Scan completed と表示され、
スキャン結果を見ることができます。

![](191215-upgrade_readiness_app-MSTeams-4.png)
![191215-upgrade_readiness_app-MSTeams-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/c9a203de-3b87-2c6a-0466-40eb4cb6f45a.png)


ちなみに、Upgrade Readiness App の動作ログは
`$SPLUNK_HOME/var/log/upgrade_readiness_app/upgrade_readiness.log` に
出力されます。

```
2019-12-11 16:57:44,671 INFO 140663382782080 - Scan initiated
2019-12-11 16:57:44,671 INFO 140663382782080 - Retrieving key to write progress
2019-12-11 16:57:44,771 INFO 140663382782080 - Found key for existing entry: 5df0a1788f02502eb0569f21
2019-12-11 16:57:44,829 INFO 140663382782080 - Total 1 apps found for user: admin
2019-12-11 16:57:44,905 INFO 140663382782080 - 0 apps out of 1 scanned. Scanning App: Microsoft Teams alert for Splunk
2019-12-11 16:57:59,288 INFO 140084004323456 - Handling a request
2019-12-11 16:57:59,289 INFO 140084004323456 - Executing function, name=get_read_progress

(中略)

2019-12-11 17:19:17,115 INFO 140663382782080 - Deployment scanned successfully for user: admin
```

[公開ドキュメント](https://docs.splunk.com/Documentation/UpgradeReadiness/2.0.0/Use/Use) には、
> Some Splunk apps are too large to scan.
> If you cannot scan a Splunk app,
> follow the app's documentation for updates on Python 3 readiness.
という但し書きも見つかったりするので、失敗にならないよう祈るくらいでしょうか。

じりじりしながら待っていても生産的ではないので、
Splunk App 向けの改修ポイントを眺めておきましょう。
公開ドキュメントの [Act on scan results](https://docs.splunk.com/Documentation/UpgradeReadiness/2.0.0/Use/Use#Act_on_scan_results) を見ると、
次のような項目が記載されています。

* Advanced XML を削除する (Splunk v6.3.0 で非推奨済)。
* Splunk Web レガシーモードを無効化する (Splunk v6.4.0 で非推奨済)。
* `test.py` というファイル名は使わない。
* M2Crypto ライブラリは使わない。
* アプリケーションサーバ機能の Mako テンプレートを Python 3 互換にする。
* アプリケーションサーバ機能の CherryPy エンドポイントを更新する。
* その他の Python ファイルを更新する。

対応できそうな部分は、予め実施しておくのも手ですね。

スキャンが終了すると、結果一覧が表示されます。

![](191215-upgrade_readiness_app-MSTeams-1.png)
![191215-upgrade_readiness_app-MSTeams-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/ee71c79c-6924-6114-4553-d1cc1195e763.png)

Check 7: Python scripts の Status: が **Warning** になっているのが見つかります。

この App は、ちょっと古い [Add-on Builder](https://splunkbase.splunk.com/app/2962/) でひな型作成しているので、
そのファイル群がばっちりひっかかったようです。

なお、Issues リストのそれぞれの項目右側に表示される
See Issues リンクをクリックすると、
修正ポイントが表示されます (これは便利)。

![](191215-upgrade_readiness_app-MSTeams-2.png)
![191215-upgrade_readiness_app-MSTeams-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/af5ecff7-153b-cef2-3250-48a56d2c6172.png)

Upgrade Readiness App のスキャンは、
Python ファイルを独立に見ているようなので (私の想像です)、
v2/v3 を条件分岐させて別ファイルで処理させている場合などでも、
改修候補に上がってくる可能性があるようです。
スキャン結果の数に一瞬ギョッとしますが、
地道にチェックして、必要に応じて改修しましょう。

### (番外編) 自作以外のサードパーティ App の互換性に、問題が見つかってしまったのだけれど・・

Splunkbase で公開されていたものであれば、
更新版が無いか、探してみましょう。

作者やメンテナによってサポートが続いてれば、
改修をお願いするのも手かもしれません。

気骨のある方は、自分で改修するのも一案かもしれませんが、
ライセンス上問題ないか確認の上、他への動作影響なども考慮しつつ、
対応されるのが良いかと思います。
晴れて、対応版が完成したら、
作者にコンタクトして、マージしてもらうのも
オープンソースの醍醐味かもしれませんね。

## Splunkbase へ更新版をアップロード。

いよいよ、更新版をアップロードして再申請しましょう。

[Splunkbase](https://splunkbase.splunk.com) にログインして、
上部メールから My Account - My Profile に移動します。

![](191211-splunkbase-1.png)
![191211-splunkbase-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/90a2cde9-1266-4b15-599b-30a920a005fd.png)

Your Apps のうち、対象 App を選んで、
上部の Administrator Tools メニューから
Manage App を選択。

![](191211-splunkbase-2.png)
![191211-splunkbase-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/f5a6824d-fc43-1369-39a6-bdcc2a33a14f.png)

右上部にある New Version を選択します。

![](191211-splunkbase-3.png)
![191211-splunkbase-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59810/3ca27d5f-42d1-3000-579f-f3e19b940580.png)

Splunkbase Developer Distribution Agreement が表示されるので、
Agree します。

Version: New Release というドロップ領域が表示されるので、
新しい版の App をアップロードします。

Release Note 等を記載できるページに遷移するので、適宜記載します。
Splunk Version Compatibility の欄もあるので、
ついでに 8.0 もチェックしてみたり。

SAVE すれば申請は完了です。
あとは、無事審査が通ることを祈りましょう。

## 最後に。

この記事では、サーチなどの Splunk の使い方や、
便利な Add-on のご紹介、気の利いた構成の活用方法、には触れずに、
Splunk App 開発と公開方法、にスポットライトを当ててみました。

何かしらご参考になる内容があれば幸いです。

もちろん、更新版の App を
Splunk v8 環境で実行して動作確認することも、大事ですね。

Splunk App に限らず、アプリケーションの修正にあたっては、
ビジネスロジック上、差異が無い、という部分まで チェックすることも、求められることがあります。
理想論かもしれませんが、きちんと単体テストを用意し、
tox 等で Python 2 でも Python 3 でも同じ結果が得られることをチェックするのが、
デグレ (regression) を防ぐ手立てではないか、とも思います。

なお、自作 App の新規公開については、
[My First Splunk App on Splunkbase](https://qiita.com/ryumei/items/d8c9b9f024175f547f9a) という記事を書いていますので、ご興味のある方は、ご覧ください。

それでは、Happy Merry Christmas!!