その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Spotless を sbt から利用するための plugin: sbt-spotless をリリースしました

github.com

Spotlesssbt から利用するためのプラグインであるところの sbt-spotless をリリースしました.

Spotless は意欲的に開発されている Java (JDK 言語) のプロジェクト向けのコードフォーマッターで,実に様々なフォーマットに対応しています *1
Spotless 自体がフォーマッターそのものを実装しているわけではなく,対象が Java であれば Eclipse JDT あるいは google-java-formatter,Scala であれば scalafmt というふうに既存のフォーマッターを動的に取ってきて *2 いい感じに適用するという動きをするので,「フォーマッター」そのものというよりは「フォーマッターハブ」と表現したほうが適切かもしれません.

この Spotless は maven plugin と gradle plugin がそれぞれオフィシャルにサポートされているのですが,sbt から利用する plugin がどうにも見当たらなかったので今回はそれを書いたという次第です.気合でsbt 0.13 系と 1.3 系の両方をサポートしています *3


使い方は至って簡単.この plugin は Maven Central にすでにリリースされているので,お手元の plugins.sbt

addSbtPlugin("net.moznion.sbt" % "sbt-spotless" % "0.1.2")

と書いて,build.sbt に設定を書く (例えば以下のように) とすぐにご利用いただけます.

import net.moznion.sbt.spotless.config._

lazy val root = (project in file("."))
  .settings(
    name := "Example",
    spotlessJava := JavaConfig(
      googleJavaFormat = GoogleJavaFormatConfig("1.7")
    ),
  )

そして sbt spotlessCheck を実行すると設定に応じてコードフォーマットのチェックが走りますし,sbt spotlessApply を走らせるとコードフォーマッターが実際にファイルに対して適用されます.

なお,sbt 1.3 系限定ですが,

lazy val root = (project in file("."))
  .settings(
    name := "Example",
    spotless := SpotlessConfig(
      checkOnCompile = true,
      // applyOnCompile = true,
    ),
  )

というふうに spotless.checkOnCompile もしくは spotless.checkOnApplytrue として設定をすると,sbt プロジェクトのコンパイル時にチェックあるいはフォーマットの適用を自動的に走らせることも可能です.

その他の詳細な設定については以下のドキュメントをご参照ください.
Configurations · moznion/sbt-spotless Wiki · GitHub


現状,sbt-spotless は Java, Scala, Kotlin, Groovy, C++ そして SQL のフォーマットに対応しています *4.Spotless 本体はそれよりも多くのフォーマットの種類を対応しているので,もし sbt-spotless でも対応すべきフォーマットがあれば issue や pull-request などでお知らせいただけると助かります.


以上です.ぜひお試しください!

*1:とにかくあらゆるものをフォーマットしようとする気概が見える: > Spotless can format <java | kotlin | scala | sql | groovy | javascript | flow | typeScript | css | scss | less | jsx | vue | graphql | json | yaml | markdown | license headers | anything> using <gradle | maven | anything>

*2:この動的に取ってくる部分が鬼門でいくつかknown issueがある…… https://github.com/moznion/sbt-spotless#known-issues

*3:世の中 0.13 が案外現存している……

*4:https://github.com/moznion/sbt-spotless#supported-formatters

zabbix-internal-checks-exporterをリリースしました

github.com

表題の通りリリースいたしました.

これはなに

Zabbix internal checks と呼ばれる Zabbix プロセスの状態を表現するメトリクスを定期的に収集し,Prometheus のメトリクスフォーマット (i.e. OpenMetrics) で export する Prometheus exporter です.愉快ですね.皆さんも愉快に思っていただけると嬉しいです.

どんなときに使うの

生きていると色々ありますね.




以上です,よろしくお願いします.最近の prometheus/client_golang はめちゃめちゃ便利で良いですね.

jackson-databind で int の取りうる値を超えた場合の挙動

Javaの話題です.

jackson-databind を使って JSON のデシリアライズを行っていて,数値を int にマッピングしている場合,その値が int (32bit) の取りうる値を超えた時の挙動が「バージョンによって異なって」います.
以下に挙げる挙動は 2.9.3 から 2.9.4 へのアップグレードで変更されています.


jackson-databind-2.9.3 を利用している場合,値が int の範囲を超過するとその値を int にキャストしたもの *1 にデシリアライズされます.

コード:


一方,jackson-databind-2.9.4 で同様のシチュエーションになった時にどうなるかというと,このバージョンからは例外が上げられることになります.

コード:

新たに導入された _convertNumberToInt というメソッドの内部で値が int の取りうる範囲内かどうかを判断し,範囲内でない場合には reportOverflowInt で例外を送出するようになっています.


この変更がどこで入ったのかというと,どうやらこのコミットのようです: Fix #1729 · FasterXML/jackson-databind@6a1152c · GitHub

このコミットログに記されている #1729 がどのようなレポートかと言うと

github.com

「int が範囲外だった時,従来の実装では意図しない値になってしまうので明示的になるように例外を上げてほしい.jackson-core の ParserBase では int が範囲外の際に例外を上げている」という内容のようです.
Author の方も「挙動に一貫性を持たせるためにも,範囲外のときには例外を上げるようにしたほうがよい」と返答をしており,これについては賛成するところです (暗黙的な wraparound は脆弱性の元にもなり得るので).


さて一方で uint32 の範囲でおさまる値 (uint32 の範囲内だと値を負に rewind しても一意になる) を取り扱っているときに,従来の挙動を意識せず利用している場合,予期せぬクラッシュが起こります.
手っ取り早く直すためには int ではなく long として取り扱うというのが良いでしょうが,既存データとの兼ね合いなどの色々な事情によってそうもゆかないこともあるでしょう.そのような際にはカスタムシリアライザ・デシリアライザを書いて乗り切ることになると思います.

import com.fasterxml.jackson.databind.util.StdConverter;

public static class Uint32JacksonSerializer extends StdConverter<Integer, Long> {
    @Override
    public Long convert(Integer n) {
        if (n == null) {
            return 0L; // as you like
        }
        return n.longValue();
    }
}
import com.fasterxml.jackson.databind.util.StdConverter;

public static class Uint32JacksonDeserializer extends StdConverter<Long, Integer> {
    private static final long MAX_UINT32_VALUE = 4294967295L;

    @Override
    public Integer convert(Long n) {
        if (n == null) {
            return 0; // as you like
        }
        if (n > MAX_UINT32_VALUE) {
            throw new IllegalArgumentException("out of the boundary of uint32 value: " + n);
        }
        return n.intValue();
    }
}

このようにカスタム (デ) シリアライザを書いて,

public class JsonClass {
    @JsonSerialize(converter = Uint32JacksonSerializer.class)
    @JsonDeserialize(converter = Uint32JacksonDeserializer.class)
    private Integer foo;
}

などとしてあげると,入れる時・出す時によしなに値を変換して取り扱ってくれるようになります.



しかしこれがパッチバージョンとして入ってくるレベルの変更かというと……大変ですね.まあリリースノートにはしっかり書いてあるのですが……jackson-databind/VERSION-2.x at ae9c91d254954a963cc525e941564c5348181eac · FasterXML/jackson-databind · GitHub

教訓としては「bit長にゆとりを持ってデータ設計をしましょう」「リリースノートをちゃんと読もう」ということです,現場からは以上です.

*1:例えば `2147483648` が来た場合に rewind されて `-2147483648` になる

pcapng ファイルを pcap ファイルに変換する

基本的に Wireshark を使うことで得られるパケットキャプチャファイルは pcapng と呼ばれるフォーマットになっており,これは pcap フォーマットとは異なります.

例えば複数のパケットキャプチャファイルを時系列に従ってマージできるコマンドラインツールである mergecap コマンドは Wireshark が提供しているソフトウェアであるため,これを使って出力される merged なパケットキャプチャファイルはデフォルトでは pcapng フォーマットとなります (ちなみに,これについては mergecap -F pcap というふうに -F オプションでフォーマットを指定すると pcap フォーマットで出力できる).


pcap を前提としたプログラムでこのpcapngファイルを読み込もうとすると往々にしてエラーが起きる (例えば Unknown magic a0d0d0a のようなエラーが上がる) ので,pcapng なファイルを pcap なファイルに変換したくなることがあります.というわけでこうです

tcpdump -r file.pcapng -w file.pcap

こうすることで pcapng ファイルを読み込んで pcap ファイルに変換することができます.簡単ですね.


なおインターネット上を検索してみると tshark を使った方法 (tshark -F pcap -r file.pcapng -w file.pcap) がヒットするのですが,この方法は元のファイルサイズが大きいと Killed という無慈悲なエラーを吐いてコマンドが完遂しないので,特別な理由がない限りは tcpdump を使うのが良いと思います.

AWS CodeBuild で AWS CDK を実行する時に IAM Role に S3 の権限を与えないとハマる

AWS CodeBuild から AWS CDK を呼び出し,いい感じで継続的に構成を更新し続けるパイプラインのようなものを作っておくと何かと便利です.
さてこの時,AWS CodeBuild を実行する IAM Role の権限がそのまま AWS CDK の実行に影響するので,その IAM には

  • CloudFormation Stacks の操作に必要な権限
  • CloudFormation が実際に影響を与える対象の操作 (つまり本当にやりたいこと) に必要な権限

という権限を与えておく必要がありますが,これに加えて

  • CDK の中間生成物をアップロードする S3 Bucket: arn:aws:s3:::cdktoolkit-stagingbucket-* に対する権限
    • s3:*Object
    • s3:ListBucket

も併せて与えないと ❌ YourStack failed: Forbidden: <snip> などというエラーを吐いて死にます.
AWS CDK Forbidden, arn:aws:s3:::cdktoolkit-stagingbucket-* などで検索すると,公式ドキュメントを含めていろいろな情報がヒットするので既知の情報なのでしょう.しかしちょっとハマってしまったのでメモとして記すこととします.

なおタイトルにはこう書いてありますが,これは別に CodeBuild に限った話ではなく,任意の IAM Role で CDK を利用する時には共通して必要です.

シェルスクリプトで空きポートを取ってきたいという時

kazuho さんのこれをシェルスクリプトでやりたいというお話です.

kazuhooku.hatenadiary.org

こんな感じ:

nc -l 0 &
NC_PID=$!
lsof -n -P -p $NC_PID | grep TCP | awk '{print $9}' | awk -F ':' '{print $2}'
kill $NC_PID

nc で ephemeral port を listen して (-l 0),その nc の PID をもとに実際に listen しているポートを引っ張ってくるというシンプルな感じです.

特別な理由がなければ Net::EmptyPort を使えば良い気もします.以上です.

macOS 上で pythonz を使って python をインストールする時に openssl 周りをいい感じにする

macOS 上で pythonz を使って素朴に python をビルドしてインストールすると SSL/TLS 周りでずっこけて基本的に使えないものがインストールされる (そもそも pip とかが動かないので).
brew で openssl を入れている場合は以下のように pythonz install するとうまいこと ssl 周りの native code がコンパイル・リンクされる.

pythonz install --configure="--with-openssl=$(brew --prefix openssl)" 3.7.6

--configure オプションに --with-openssl を渡してやると良い,という感じですね.rbenv などでもこのような対応をしていた記憶がある……