法人決算の会計処理を終えた

0時に寝て6時に起きた。夜あまり眠れない。

算定基礎届

e-gov電子申請 を使って初めて電子申請してみた。昨年もやろうと挑戦したけど、macos からだと不具合があってエラーになるから断念してた。今年は windows マシンがあるので windows アプリケーションをインストールして問題なく申請できた。うちは社員1人なので csv 取り込みを使わず、手入力で申請した。申請した書類は pdf 出力できるし、申請後に送信したデータは xml で控えとして保持できる。本当に紙でやっていたものを文書データと数値データに置き換えたようなアプリケーションになっている。紙の書類と比べて、アプリケーションがよいところは申請の進捗状況がわかるところ。算定基礎届で問題が発生することは過去にないけど、審査開始、審査終了、手続終了のステータスをアプリケーションから確認できる。それはそれで申請者にとって状況の追跡ができて安心感になる。

振替伝票の使い方

前期は赤字決算だったので中間申告で支払った税金が還付される。中間申告というのは、前年度の納税金額から翌年の税金の半分を納めるという仕組み。前年度の法人税額が20万円を超えると中間申告が必要となる。前年度と同じ法人税が今年度もあるという前提で半分納めるけれど、その納めた金額よりも確定申告のタイミングで実際の納税金額が少ない場合は還付金という形で返ってくる。今回は赤字決算となったものの、それも初めてだったので税務署からの還付金をどう会計処理するのかも初めての機会でよくわからなくて調べながら作業した。

会計システムとして普通に行う処理ではないので freee のドキュメントも断片的にしか説明されていない。基本的な操作の考え方を理解した上で自分がやりたい会計処理に変更しないといけない。

まず中間申告のタイミングで振込した納付金額は「仮払金」として登録される。本来は確定申告のタイミングで確定した納付額に対して「仮払金」を相殺するような会計処理を行う必要がある。freee では取引データの決済欄に「+更新」というボタンがあってそこから仮払金から引き落とすようなデータ登録が可能となる。今回は赤字決算ですでに支払った「仮払金」が還付金として戻ってくるときの会計処理をしなければいけない。その手続きのために使うのが「振替伝票」になる。例として金額を10万円とすると次のような振替伝票を作成する。

  • 借方
    • 勘定科目: 未収入金
    • 税区分: 対象外
    • 金額: 10万円
  • 貸方
    • 勘定科目: 仮払金
    • 税区分: 対象外
    • 金額: 10万円

「仮払金」を相殺するための勘定科目は「未収入金」になる。この「未収入金」を還付金の取引 (税務署から銀行口座に振り込まれた金額) で消し込むことで会計システム上の辻褄があう。一般的に還付金の勘定科目は「雑収入」として扱うらしい。還付金は消費税がかからない取引であるので不課税取引となる。ややこしいのは還付金が振り込まれる際に還付加算金というお金も一緒に振り込みされる場合がある。還付加算金というのは、納め過ぎた税金に対する金利のようなものになる。試しに計算してみると金利が 0.46% になった。ある銀行の定期が 0.002% だったので税務署に税金を納め過ぎるとめちゃくちゃ金利のよい貯金みたいな扱いになる。話しを元に戻すと、還付加算金は課税対象になるので「雑収入」の課税売上として会計処理する。

うちの会社では、振替伝票は決算のタイミングでしか使わない。振替伝票の使い方を忘れていてたまに使うときに右往左往する。

はんなりDAO

はんなりDAOをはじめてみます に参加した。イベントで話した内容は notion で公開されている。

まだ全然、計画段階で段取りの計画も目処もたっていない。dao が良いものかどうか、私はまだよくわかっていないが、実際に自分で試してみることには肯定的である。そして組織の取り組みは実際に複数人いないとあまり実用的ではないことからコミュニティのような、一定以上の信頼のある実際の人間が関わってくれるならそれはそれで実証実験の場としてはおもしろい取り組みになるかもしれない。初回だったので dao とは何かとか、dao や web3 を取り巻く世の中の状況はどうかとか、はんなり dao の目的をどうするかとか、計画段階の雑談が主だった。最初はそんなもんかもしれない。私もスマートコントラクトとか、何が嬉しいのかよくわかってないので dao を運営する中で実際に実装してみる機会があれば、それはそれで学びの機会としてよいかもしれないと考えている。

aragon という dao を作るためのプラットフォームがあって、これを使うと dao そのものはすぐに準備できるらしい。あとは組織運営のルールやスマートコントラクトを実装していくだけみたいな話し。ethereum で動かすと手数料がかかる。しばらくは testnet であーでもないこーでもないみたいなやり取りをしながら dao の運営を学んでいこうみたいな話しをしていた。

m1 chip macbook と cdk/aws-lambda は相性が悪い

0時に寝て6時に起きた。

m1 chip macbook で aws-lambda-python-alpha のデプロイができない

少し前に aws lambda の管理を serverless framework から cdk 移行した 。lambda 関数は python スクリプトで実装されているので @aws-cdk/aws-lambda-python-alpha を使っている。このライブラリでは python distribution を作るときの python インタープリターをローカルのものではなく docker イメージを使って管理しているようにみえる。私の環境 (linux, x86_64) では何の問題もなかったのだけど、同僚が m1 chip macbook を使っていて、そのマシンからだと docker イメージを使ったビルド処理でエラーが発生する。それは既知の問題で次の issue で報告されている。

このワークアラウンドの1つとして Custom Bundling の仕組みがある。任意の Dockerfile を指定することで任意の Docker イメージやプラットフォーム向けにビルド用の python インタープリターを設定できる。そうしたらビルド処理そのものは通るようになったけど、python distribution (python の依存関係を含めたスクリプト群) が asset として生成されない。この現象自体も cdk でよくある issue として報告されていて cdk.out を削除して再実行したら直ったという報告もいくつかあるものの、同僚のマシンではそれでは解決しなかった。

私が m1 chip macbook をもっていないので cdk のコードを修正して push して同僚に git pull して実行してもらうみたいな作業になっている。このデバッグはなかなか大変。

cdk で既存の eks クラスターを管理すべきか

0時に寝て6時に起きた。

cdk から既存の eks クラスターを制御する

1ヶ月ほど前に検証していた cdk による eks クラスターの helm 管理 を再検証した。kubectlRoleArn にどういった権限をもつ iam role を設定したらよいかがよくわからなくて苦労していた。最終的にそれが理解できて helm 管理もできるようになったのでまとめておく。

kubectlRoleArn - the ARN of an IAM role mapped to the system:masters RBAC role. If the cluster you are importing was created using the AWS CDK, the CloudFormation stack has an output that includes an IAM role that can be used. Otherwise, you can create an IAM role and map it to system:masters manually. The trust policy of this role should include the the arn:aws::iam::${accountId}:root principal in order to allow the execution role of the kubectl resource to assume it.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_eks-readme.html#using-existing-clusters

aws-auth の configmap に設定されている system:masters に所属している iam role を調べる。

$ kubectl describe configmap -n kube-system aws-auth

この iam role には sts:AssumeRole 権限を与え、trust relationships に arn:aws:iam::${accountId}:root といった root ユーザーを含める必要がある。この root ユーザーの設定がないと次のような権限エラーが発生する。この権限エラーの修正方法がわからなくて苦労していた。結果的には関係なかった kubectlLambdaRole の設定も必要なんじゃないかと検証していたのが前回の作業の中心だった。

An error occurred (AccessDenied) when calling the AssumeRole operation:
  User: arn:aws:sts::${accountId}:assumed-role/xxx is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::${accountId}:role/myrole
Error: Kubernetes cluster unreachable: Get "https://xxx.gr7.ap-northeast-1.eks.amazonaws.com/version

ようやく cdk で既存の eks クラスターをインポートして helm パッケージを管理できるようになった。とはいえ、cdk/cf の実行時間を測ってみると次のようになった。

  • helm パッケージの新規インストール: 約5分
  • helm パッケージのアンインストール: 約25分

これは cdk が helm パッケージを管理するための lambda 環境を構築/削除するときの時間になる。cdk はアプリケーションの stack から nested stack を作成して、そこに lambda や iam role などをまとめて作成する。一度作成してしまえば、バージョンのアップグレードは30秒ほどで完了した。

この振る舞いを検証した上で、cdk で eks クラスターをインポートする管理はやめようとチームに提案した。正しい設定を作ってしまえば運用は楽になると言える一面もあるが、新規に helm パッケージを追加するときのちょっとした typo や設定ミスなどがあると、1回の試行に30分かかる。私がこの検証に1週間以上のデバッグ時間を割いている理由がそれに相当する。お手伝い先の運用ではテスト/本番環境ともにローカルから接続できる状態なので helm コマンドを直接実行した方が遥かに管理コストや保守コストを下げると言える。cdk を使って嬉しいことは helm コマンドでわかるバージョン情報と設定内容が cdk のコードとして管理されているぐらいでしかない。ドキュメントと helm コマンドで管理する方が現状ではよいだろうと私は結論付けた。同じような理由で eks クラスターも cdk ではなく eksctl コマンドで管理されている。

1週間以上の労力と時間を費やしてやらない方がよいとわかったという、一般的には失敗と呼ばれる作業に終わったわけだけど、eks/cdk の勉強にはなった。

設計のリファクタリング

23時に寝て4時に起きた。その後もだらだらしていて変な寝方した。

型定義のリファクタリング

開発が佳境でサービスイン前なのと機能開発は完了しているのでリスクのある作業やリファクタリングなどを主にやっている。別の開発者が作った機能の型定義が曖昧なところをまとめてリファクタリングしていた。若い開発者だから仕方ないことだけど、ジェネリクスとポリモーフィズムを正しく理解できていない。effective java で言うところの抽象骨格実装という設計手法になる。あと ide のリファクタリング機能を使ってコードを書いているのか?public な api の設計がおかしかった。人間が考えてメソッド分割したようには思えない oop らしからぬ手続き的な api になっていた。後から oop としてジェネリックな型定義に落とし込むのがなかなか難しくて丸1日ぐらいやってた。私がドメイン知識をもっていないのもある。以前にもそのコードの pr をレビューしていてよくないコードだとはわかっていたけど、私はドメイン知識がないためにやりたいことがわからないから最低限の品質でマージした後に私がリファクタリングすればいいかと考えていた。若い開発者にプログラミングの設計やコーディングを教えるのは時間を要するのでお手伝いという立場だとなかなか難しい。だから私が直してしまえばいいやというノリで勝手に直した。

openapi generator の設定

3時に寝て6時半に起きた。昨日は夕方に昼寝したので夜は眠れなかった。

openapi generator の x-implements 機能

外部ベンダーの api client の wrapper を実装していて、api client が扱うリクエストやレスポンスを型 (インターフェース) で抽象化できるとよさそうと思って openapi generator の設定を調べていた。maven-plugin の設定と openapi-generator の設定の2つがあるので両方のドキュメントを確認しないといけない。

そんなに都合よくインターフェースを指定できるような仕組みがなければ、最悪は mustache テンプレートをカスタマイズするしかないかなぁとか考えていた。テンプレートを操作すると、今後の保守コストが上がってしまうのでそのメリット・デメリットを比較して考えないといけない。諦めかけていたときに so でこの issue をみつけた。

ちょうどこの5月末にリリースされたばかりの 6.0.0 に x-implements と指定すれば、任意のインターフェースを implements できる機能が追加された。これはスキーマに対する設定なのでテンプレートをカスタマイズするよりずっと保守コストは小さくて済む。

例えば、openapi schema の json で設定すると、コード生成したときにそんな風にインターフェースが付く。

       "SomethingApiResponse": {
+        "x-implements": "com.example.app.MyResponse",
         "title": "SomethingApiResponse",
         "type": "object",
         "properties": {
-public class SomethingApiResponse {
+public class SomethingApiResponse implements com.example.app.MyResponse {

あまりにも意図していた機能をみつけて嬉しくてツィートしてしまった。

法人税の修正申告

1時に寝て7時に起きた。

源泉所得税の納付

ちょうど給料日を過ぎたので所得税徴収高計算書 (納期特例分) の申請を行った。これまでも e-tax で電子申請して振り込みしていたのだけれど、e-tax ソフト (web版) でブラウザから申請していた。今回は windows マシンにインストールされている e-tax ソフトから申請してみることにした。e-tax ソフトは起動時に「追加インストール」という機能があって、申請に必要なモジュールのみをダウンロードしてインストールできるようになっている。20年前で言うところの saas はこうだった。「源泉所得税関係」というモジュールをインストールしないと、所得税徴収高計算書の申請ができない (e-tax ソフトに帳票がインストールされない) 。モジュールを追加インストールすれば、「源泉所得税」という税目から「所得税徴収高計算書 (納期特例分) 」という帳票を選択して、あとは数字を記入して送信するだけ。この申請に電子署名は不要。

法人税の修正申告と欠損金の繰り戻し還付の訂正依頼

国税局の職員さんからの指摘 で提出した書類が誤っていることに気付いた。いくつか訂正箇所を書いておく。

  • 欠損金額とは
    • 正: 税引き後の欠損金額 = 別表1の1の数字をそのまま使えばよい
    • 誤: 税引き前の所得 (税務上は負の所得を欠損金と呼ぶ) を使っていた
  • 法人税と地方法人税の計算は別
    • 欠損金の繰り戻し還付の申請は法人税のみの還付金を算出
    • ↑で求めた還付金に対して(令和元年以降は)10.3%を地方法人税の還付金とする
      • この手続きは不要で別表一に算出した数字を記載すればよい

まずこの申請書の誤りを修正して訂正依頼とする。

さらにこの欠損金の繰り戻し還付の申請の数字を別表1に記載しなければならない。それらが漏れているのと還付申請のためには別表七も提出しないといけない。あと細かい数字の記入漏れの指摘もあった。法人税の確定申告に対して次の5つの書類を修正申告として提出する。

  • 別表一
  • 別表一 次葉
  • 別表四
  • 別表五 (一)
  • 別表七 (一)

税務署の職員さんが訂正する数字を書いてくれていたので、それをみながら e-tax ソフトで数字を修正して紙に印刷してそれを再提出する。おかげで赤字のときの別表書類の書き方もわかった。大半は欠損金の繰り返し還付に関する数字の記入漏れなので今後も赤字の年度があったときにこの内容を踏襲しながら申告すればよい。また1つ行政手続きのノウハウを得ることができた。税務署の職員さんに感謝。

企業サイトの更新

企業サイトを作成してから3年近く経つのでそろそろ初期の頃に書いた内容が現状とあわなくなってきた。あちこち現状とあっていない内容を更新した。本当はデザインを刷新したいと思っている。デザイン刷新のためのチケットを作ったのが2021年7月28日 20:04なのでもうすぐ1年経とうとしている。どんどん時間が過ぎるな。今日のところは、主には 企業情報 の構成を作り直した。それと同時に過去に働いていた会社での業務外発表を除去した。今後は自社の発表のみを掲載する。これには会社としてマーケティング活動をやっていくという意気込みと過去との決別の意味合いもある。トップページに news を5件表示しているところがビルドするタイミングによってそうならないときがあって、実行タイミングによってページングがされたりされなかったりする現象に悩まされている。ワークアラウンドとしては、何回かビルドをやり直せばページングされるときもあるのでそれを待つみたいな、どうしようもないやり方でこの場は凌いでいる。

k8s のアップグレードをやってみた

0時に寝て6時半に起きた。起きてから1時間ほどだらだらしてた。

ストレッチ

今日の開脚幅は開始前161cmで、ストレッチ後163cmだった。先週と同じなので現状維持とも言えるし、よい状態を維持しているとも言えるかもしれない。もう1年以上通っているせいか、なにかポイントが溜まっていて使わないといけないという話しで今日は20分延長でやってくれた。とは言っても、基本的なストレッチ項目が変わるわけではなく、いつもより伸ばす時間や手順が少し増えているぐらいだった気がする。今週はとくに腰の負荷もあまり感じなかったせいか、いつもの右腰の張りもなかったように思う。トレーナーさんに聞くと、暑くなると筋肉は伸びやすくなるので季節要因でストレッチをしたときの伸び具合が変わるのは普通とのこと。調子がよくなってきたのでこのまま好調を維持したい。

eks (k8s) のアップグレード

お手伝い先のお仕事がもうすぐサービスインなのでそれまでにリスクのある作業をやっとこうみたいな状況にある。たまたま eks (k8s) のバージョンを 1.21 から 1.22 にあげようと思い立って、木曜日に提案したら、どんな障害が起きるかわからないので他メンバーがテスト環境を使っていない時間帯で作業した方がよいだろうという話になって土日にやることにした。

何が起きるか分からなくても、土曜日から始めて致命的なトラブルに見舞われても1日もあれば解決できるだろうという見通しで作業を始めた。その見通しも「私がやれば」という前提に成り立っている。良くも悪くも私がやろうと言ったことに反対されることはほとんどないが、それは私が言ったことは一定時間に私がすべてやり切るという信頼に基づいている。本当の意味でできるかどうか分からないことを必要以上に抱え込んでしまうときもあるのでバランス感覚は必要かもしれない。言わばサービス休日出勤だし、なぜ私がやっているかと言うと、システムの運用や保守の展望を考えたら、サービスインの前にインフラのバージョンを上げておく方が将来の保守コストを下げることに繋がるという1点のみに重要性を見い出していて、それをもっとも強く主張しているのが私だからという理由。

結論から言って2時間でアップグレード作業を完了した。1つ手順漏れがあって、アプリケーションの pod がすべてエラーになるというトラブルに見舞われたものの、すぐ手順漏れに気付いて難なく復旧できた。今日はテスト環境のアップグレードをしたわけだけど、また後日、本番向けの作業手順書を作れば、ほぼタウンタイムなしで1時間もあればアップグレード作業を完了できそうな見通しではある。

実際はミスもあったので次の順番でやったわけではないが、おそらくこの手順でやれば正しいはず。

  1. aws cli と eksctl コマンドのインストール
  2. aws のアップグレードドキュメンを読む
  3. cert-manager のアップグレード (1.1.1 から 1.5.4)
  4. aws-load-balancer-controller のアップグレード (2.2.0 から 2.4.2)
  5. k8s control plane のアップグレード (1.21 から 1.22)
  6. (オプション: 不要) autoscaler のアップグレード
  7. (オプション: 不要) gpu サポートノードのアップグレード
  8. vpc cni プラグインのアップグレード (1.7.5 から 1.11.2)
  9. coredns プラグインのアップグレード (1.8.4 から 1.8.7)
  10. kube-proxy のアップグレード (1.21.2 から 1.22.6)
  11. k8s nodegroup のアップグレード (1.21 から 1.22)
    • k8s ノードが存在する nodegroup をアップグレードするとそのインスタンスが再作成されて pod が再デプロイされる

細かい手順は aws のドキュメントの指示に従いながらやったらできた。add-on と self-managed add-on の種別の違いがあったり、helm と k8s manifest の手順が別々だったり、どのバージョンからのアップグレードかで作業手順が異なったりと、ドキュメントをちゃんと読まないと正しい作業手順がわからない。基本的にはドキュメント通りの作業で完了できた。

もくもく会

アップグレード作業を終えてから1時間ほど残っていたので16時から 【三宮.dev & KELab 共催】もくもく会 に参加した。今回は Kobe Engineers Lab さんと共催ということで 120 WORKPLACE KOBE で開催された。Kobe Engineers Lab の主催者の会社が 120 workplace でオフィスを借りているため、会議室を5時間/月まで無料で借りられるという。私も過去に何度か 120 workplace のコワーキングスペースで作業したこともあった。久しぶりに行ってよい場所だとは思う。会議室は初めて入ったけど、10人ぐらいは余裕で作業できる大きなテーブルがあって広くてよかった。終わってからわたなべさんと3時間ほど立ち呑みしてた。

はんなりビジネス

21時から はんなりビジネス #0 に参加した。おがわさんがまた新しいことやるんだなと思って興味本位で参加してみた。現実の課題に対してコミュニティの有志を募ってチームで取り組んでみたら、問題解決能力も身についてプログラミングの知識を活かしてより実践的なスキルが身に着いてよいのではないかといったところから始まった企画らしい。今日は初回だったので参加者でどういう取り組みがよいのかを雑談してた。まだまだこの先どうなるかわからないけど、私はあまりこの手の取り組みには懐疑的かなぁ。自分たちにとってちょうどよい課題レベルの対象をみつけるのは難しいし、誰でも参加できるオープンなビジネスコンテストやアイディアソンが本当に大事な問題を扱っているかも怪しい。現実の課題はお仕事でいくらでもあるので、それをコミュニティでやろうと思うとニッチな何かになるか、価値があるかどうかよりも本人がやりたいかどうかの目的になってしまうような気もする。とはいえ、私自身、ビジネス力はまったくないのでなにかしらやっているうちに価値に気付くこともあるかもしれない。もうしばらく様子をみてみる。

余白談義

0時に寝て6時に起きた。だいぶ復調してきて朝起きれるようになってきた。

歯科検診

3ヶ月ごとの定期検診。前回 は2年ぶりにレントゲンをとったので新しい歯のレントゲン写真をみせていただいた。とくに変わりなく、親知らずにゴミが溜まるスポットがあるから、親知らずを抜いた方がいいのはいいけど、下側の親知らずを抜くのは大変だから痛くなってからでもよいかも?みたいな話しをした。いま3ヶ月ごとに定期検診して親知らずのゴミスポットの掃除をしてもらっているからそれでもしかしたら大丈夫なのかもしれない。あと1箇所だけ銀の詰めものと歯が精密に一致していないところがあって、その隙間にゴミが溜まりやすいとのこと。急がなくてもよいけど、いずれ付け直した方がよいらしい。

隔週の雑談

顧問のはらさんと隔週の打ち合わせ。今日の議題は次の3つ。

  • お手伝い先の契約更新についての話題
  • カフーツさんとワーケーションの話題
  • jjug ccc 2022 spring ふりかえり

先日の記事 でも「余白」という概念が個人的にはまったのであちこちで余白談義をしている。デザインで言うところの余白とは何かをはらさんに聞いてみたところ、次のようなものだという。

  • 空白というコンテンツである
  • 他に使ってはいけない領域である

開発における余裕とかゆとりのことを余白と言ってしまうと、デザインの文脈とは一致しないという話しになった。ワーケーションの文脈では予定調和じゃない時間を指すといとうさんは仰っていた。何かをやるための計画を立てた時間ではない。何もしなくてもよいし、思いつきで何かをしてもよい。先日の記事で、課題管理の文脈でメタ課題の抽出や他人のチケットにコメントしたりするのも余白の1つではないかと私が書いたのはその行動が必須というわけではない。誰かから指示されてやっているわけでもないという行動が、ワーケーションにおける予定調和じゃない行動と共通点があるのではないかと考えたから。

私が開発における余白を余裕やゆとりのように解釈して発言したので余っているものというニュアンスになってしまったけど、その時間は余っているものではなく、明示的に明けておくべき時間のように捉えたらデザインの文脈で言う余白にも近い概念になるかもしれない。開発だと想定外の追加作業や要件漏れなどのために確保しておく時間をバッファと呼んだりもする。例えば、開発の作業時間を1日8時間、1週間(5日間)で40時間と見積もるから余白がなくなってしまうであって、仮に1週間の余白を3時間と先に確保してしまって、37時間を開発の作業時間としてしまえば余白がなくなるということない。その余白時間に業務に関係ない勉強会をやってもいいし、自分の調べたいことに費やしてもいいかもしれない。私が金曜日を非稼働日として業務委託の作業をやらずに雑談していることにも通じる。

その後、余白談義からスクラムの課題や展望の話しなどにも発散して盛り上がった。スクラムには余白がないから。まだまだ私自身、余白の言語化に曖昧なところがあるので今後も意識しながら概念や価値を言語化していくように努めたい。

assertj を使ってみた

0時に寝て6時に起きた。

assertDeepEquals を作った

AssertJ というアサーションライブラリを使って assertDeepEquals を実装した。

junit4 では hamcrest という matcher が使われていて、それが assertDeepEquals 相当の機能を提供していたが、それが junit5 では提供されなくなったので自分で実装するか、アサーションライブラリを別途使う必要がある。

現時点でJUnit5ではHamcrestのMatcherは提供せず、使用者が自由に選択する方針で進んでいます。そうなった場合、標準でサポートされるassertTrueやassertEquelsなどだけでは、ちょっと頼りなく車輪の再発明になりそうなので、候補になりそうなHamcrestとAssertJのよく使いそうなメソッド比較表を作りました。

JUnitのアサーションライブラリHamcrest,AssertJ比較

2.4.2. Third-party Assertion Libraries によると、junit は基本的なアサーション機能を提供し、より強力なアサーションはサードパーティ製の好きなライブラリを使ってくれみたいなことが書いてある。軽く github でソースコード検索しても、みんな自前で作っているんやなということも分かる。

hamcrest はもう保守されていないようにみえるので assertj を使うことにした。assertj の機能を使うと assertDeepEquals を次のように実装できる。直接 assertj を使ってもよいのだけど、assertXxx という名前で使えた方が junit ベースのテストのアサートの統合性があるし、いまお手伝い先では myapp-test のような、テスト向けの共通ライブラリを提供していて、すべてのプロジェクトで既に使っているので assertj の依存関係を追加しなくてもすぐに使えるというぐらいの利便性を提供するだけのユーティリティになる。

public class Assertions {

    public static final void assertDeepEquals(Object expected, Object actual) {
        assertThat(expected).usingRecursiveComparison().isEqualTo(actual);
    }

    public static final void assertDeepEquals(Object expected, Object actual, String... fields) {
        assertThat(expected).usingRecursiveComparison().comparingOnlyFields(fields).isEqualTo(actual);
    }

    public static final void assertDeepEqualsIgnoringFields(Object expected, Object actual, String... fields) {
        assertThat(expected).usingRecursiveComparison().ignoringFields(fields).isEqualTo(actual);
    }
}

k8s の cronjob を検証中

0時に寝て6時に起きた。寝不足を解消して体調が戻ってきた。

k8s の cronjob

バッチ処理を Kubernetes: CronJob で作る。一通り設定して minikube で検証して eks 上でも動くようになった。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: my-app-hourly-job
spec:
  schedule: "5 */1 * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 600
  jobTemplate:
    spec:
      backoffLimit: 0
      template:
        metadata:
          labels:
            app: my-app-hourly
          annotations:
            dapr.io/enabled: "true"
            dapr.io/app-id: "my-app-hourly"
        spec:
          containers:
          - name: my-app-hourly-job
            image: my-app-image
            imagePullPolicy: Always
            env:
            - name: BATCH_ENV
              value: "dev"
            command:
            - "/bin/sh"
            - "/app/scripts/my-app.sh"
            - "param1"
            - "param2"
          restartPolicy: Never

command の設定がわかりにくい。さらに k8s のドキュメントのサンプル設定も誤解を招くような例になっている。どうも実行できるのは1つの cli だけで、複数コマンドを指定できるわけではない。シェルスクリプトを docker イメージに含めて、そこで任意のスクリプトを実装した方がよいだろう。

  • “/bin/sh”
  • “/app/scripts/my-app.sh”
  • “param1”
  • “param2”

この設定は次の cli として実行される。

/bin/sh /app/scripts/my-app.sh param1 param2

How to ensure kubernetes cronjob does not restart on failure によると、バッチ処理が失敗したときに再実行したくないときは次の3つの設定をする。

  • concurrencyPolicy: Forbid
  • backoffLimit: 0
  • restartPolicy: Never

restartPolicy が Never 以外だと、エラーが発生すると永遠に再実行されてしまうので障害時に2次被害を増やしてしまう懸念があったような気がする。

あと、うちの環境は dapr 経由で他の pod サービスと通信しているので dapr を有効にしないと pod 間通信ができない。dapr はデーモンでずっと起動しているからバッチ処理の終了時に daprd も shutdown してやらないといけない。Running Dapr with a Kubernetes Job にその方法が書いてある。daprd を shutdown しないと、pod のステータスが NotReady のままで Completed にならない。

まだまだよくわかってないので Jobs のドキュメントに一通り目を通そうと思っている。

maven で executable jar を作る

4時に寝て7時に起きた。

maven での executable jar の作り方

gradle では作ったことがあったけど、maven では初めてなので要領がわかっていない。

これらの記事を読むと、maven-assembly-plugin を使えばいいのかな?とまずはこのプラグインで検証を始めた。古くからあるプラグインなので実績は十分なのだけど、もうあまり保守されていないのか、他プラグインから jar のマニフェストに書き込んで git のリビジョン番号が連携できてなかったり、通常の jar の生成処理を置き換えられなかったりと、あまり使い勝手のよいものではなかった。あと log4j2 と相性が悪くて意図したように設定ファイルを読み込んで初期化ができない。

main ERROR Error processing element EcsLayout: CLASS_NOT_FOUND
main ERROR Unable to locate plugin type for EcsLayout
main ERROR Unable to locate plugin for EcsLayout
main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.ConsoleAppender for element Console:
  java.lang.NullPointerException: Cannot invoke "org.apache.logging.log4j.core.config.plugins.util.PluginType.getElementName()"
  because "childType" is null java.lang.NullPointerException:
    Cannot invoke "org.apache.logging.log4j.core.config.plugins.util.PluginType.getElementName()" because "childType" is null

この厄介な問題をデバッグするよりも、すでにうまくいくことがわかっている spring-boot-maven-plugin を使った方が簡単そうだったのでそうすることにした。不要な spring boot 関連の jar なども executable jar や docker イメージに含まれてしまうことだけがデメリット。そこだけ目を瞑れば log4j2 の初期化エラーも起きず、正常に動作した。やっぱり最近のアプリケーションで使われているプラグインはちゃんとしてるねみたいな話しにしておく。次の設定だけでうまくいった。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.example.myapp.Main</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>