firstOrFailの例外処理でハマった話

Laravel 8においてDBにデータが存在するかどうかをチェックして、存在したらそのデータを、存在しなかったら定数を返却するような処理を書く際に一時間近くハマったので備忘録として残しておく。

環境

  • OS: Ubuntu 20.04 server
  • Laravel 8.26.1
  • PHP 8.0.12

やりたかったこと

  1. 特定の条件に当てはまるデータが存在した場合、firstOrFail()関数を用いてそのデータの特定カラム(foo)の値を返却する。
  2. 条件に当てはまらない場合、スローされた例外をキャッチして定数(-1)を返却する。

これを実現するため、以下のようなコードを作成。

try{
      $user= User::where([
           ['user_id', '=', $userId],
           他の条件…,
      ])
      ->firstOrFail();
      return $user->foo;
}catch(Exception $e){
      return -1;
}

実際にこの処理を行うAPIをブラウザから叩くと、Laravel標準の 「404 | Not Found」ページが表示された。

原因

findOrFailメソッドとfirstOrFailメソッドは、クエリの最初の結果を取得します。ただし、結果が見つからない場合は、Illuminate\Database\Eloquent\ModelNotFoundExceptionを投げます。
(中略)
ModelNotFoundExceptionをキャッチしない場合は、404 HTTPレスポンスをクライアントへ自動的に返送します。

引用: Eloquentの準備 8.x Laravel

どうやらModelNotFoundExceptionがキャッチされない限り、firstOrFailメソッドは例外発生時にApp::abort(404)と同様の動作をするらしい。

解決

きちんとModelNotFoundExceptionをキャッチするようにしたところ、想定していた通りの動作をしてくれた。

use Illuminate\Database\Eloquent\ModelNotFoundException;
...
try{
      $user= User::where([
           ['user_id', '=', $userId],
           他の条件…,
      ])
      ->firstOrFail();
      return $user->foo;
}catch(ModelNotFoundException $e){
      return -1;
}

EC2上のnginxにHTTPSで接続できるようにした話

大学の友人間でLaravelを使ったWebアプリケーションを開発することになり、開発環境としてEC2上にnginxサーバを構築しました。
この環境にHTTPSで接続できるようにしたのでメモ程度に手順を残しておきます。

Certbotのインストール

今回はLet's Encryptを利用してサーバ証明書を発行してもらうので、Certbotを使ってSSL設定を行っていきます。aptからインストールできるのは便利ですね。

$ sudo apt install certbot python3-certbot-nginx

nginxのconfファイル記述

Certbot/etc/nginx/sites-enabled下にあるファイルのserver_nameディレクティブをサーチし、一致したドメインの存在するファイルに自動で設定を適用してくれます。
なので、confファイル内にserver_nameの項目を登録していきます。

server{
    server_name exaple.com www.example.com;
    root /var/www/hogehoge/public;
    location / {
       ...
    }
}

なお、ここですでにlistenディレクティブが定義されている場合、Certbotを実行する際に「同じポートで既にlistenされてるよ」とのエラーが発生し、設定が適用されなくなってしまうので注意。
confファイルの作成が終わったら設定をリロードします。

$ sudo systemctl reload nginx

Certbotの実行

準備が完了したので、certbotを実行してSSL通信に必要な設定を反映していきます。-dオプションでconfファイルに記述したドメイン名を指定するのを忘れずに。

$ sudo certbot -d example.com -d www.example.com

あとはCertbotが渡されたドメインが実際に存在するかなどをチェックして、自動でconfファイルに追記・変更を加えてくれます。
全ての処理が完了したら、サーバとのHTTPS通信が可能になります。

TerraformでProxmox上にVMを作成してみた

インフラエンジニア志望なのにIaCツール触ったことないのはどうなのよ?ってことで、Terraformをさわさわしてみました。
AWSインスタンスをプロビジョニングしても良かったんですが、インスタンスを止め忘れてえげつない請求が飛んでくるのが怖かったので、とりあえず手元のProxmox環境にVMを立てることから始めてみました。

大まかな手順

  • Terraformから接続するためのユーザ・権限を作成する
  • VMのテンプレートを作成する
  • HCLを記述する
  • Terraform plan/applyでVMを作成する

Terraform用ユーザの作成

https://registry.terraform.io/providers/Telmate/proxmox/latest/docs
公式ドキュメントに沿ってユーザと権限を設定していきます。

pveum role add TerraformProv -privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Monitor VM.Audit VM.PowerMgmt Datastore.AllocateSpace Datastore.Audit"
pveum user add terraform-prov@pve --password <password>
pveum aclmod / -user terraform-prov@pve -role TerraformProv

VMのテンプレート作成

テンプレート作成の手順に関しては、こちらの記事を参考にさせていただきました。
blog.nishi.network
ここで作成したテンプレートをもとにVMを作成していきます。

HCLの記述

先ほど作成したテンプレートをクローンして新たにVMを立てていくため、各種構成を記述したtfファイルを作成します。
はじめに、Proxmoxサーバに接続するための情報をmain.tfに定義していきます。

provider "proxmox"{
    pm_api_url = var.api_url
    pm_user = var.user
    pm_tls_insecure =true
}

さらに、公開したくない情報を別のファイルに定義しておくこともできるため、var.tfにいくつかの値を定義しておきました。

variable "api_url"{
    default = "ProxmoxサーバのURL"
}
variable "user"{
    default = "Proxmoxサーバにログインするユーザ"
}

また、今回はProxmoxプロバイダを使用するため、プラグイン関連の設定をversion.tfに記述しました。

terraform {
    required_providers {
        proxmox = {
            source = "Telmate/proxmox"
            version = "2.9.0"
        }
    }
}

最後に、作成するVMの構成をVMs.tfにまとめていきます。今回は3台のVMKubernetesクラスタを作成することを見込んだ設定としました。

resource "proxmox_vm_qemu" "UbuntuCP"{
    name = "ubuntucp"
    target_node = "atras"
    clone = "UbuntuServerTemplate"
    memory = 2048
    os_type = "cloud-init"

    disk{
        type = "scsi"
        storage = "VMStore"
        size = "20G"
    }
}

resource "proxmox_vm_qemu" "UbuntuWN1"{
    name = "ubuntuwn1"
    target_node = "atras"
    clone = "UbuntuServerTemplate"
    memory = 2048
    os_type = "cloud-init"

    disk{
        type = "scsi"
        storage = "VMStore"
        size = "20G"
    }
}

resource "proxmox_vm_qemu" "UbuntuWN2"{
    name = "ubuntuwn2"
    target_node = "atras"
    clone = "UbuntuServerTemplate"
    memory = 2048
    os_type = "cloud-init"

    disk{
        type = "scsi"
        storage = "VMStore"
        size = "20G"
    }
}

VMの作成

さて、必要なファイルをあらかた定義し終えたので、さっそくProxmoxサーバにVMを作成していきます。
$export PM_PASS="PASSWORD"でProxmoxユーザのパスワードを環境変数に登録し、 $terraform planで、エラーチェックと作成されるVMの構成の確認を行います。
ここで表示された構成で問題がなければ、$terraform applyで実際にVMを作成します。 Apply complete!と表示されれば無事作成が終了していますので、実際にProxmox上で確認してみると、
f:id:KLag:20211111145908p:plain
こんな風に作成できていることがわかります。

ProxmoxへのLVMパーティションの追加

サーバにHDDを増設したことに伴って、Proxmox環境に新しくLVMストレージを追加しようとしたんですが、Webコンソール上のストレージ設定から作成しようとしたところ、ディスク単位でしか新規作成できなさそうだったのでここにメモっておく。

ざっくりとした手順

パーティション作成

300GBの物理ディスク /dev/sdb内に、ISOイメージ保管用のパーティションを100GB、余った部分をVMの仮想ストレージ保管用のパーティションとして作成します。

# fdisk /dev/sdb 
Welcome to fdisk (util-linux 2.36.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): n
Partition Number: (デフォルトで確定)
First Sector: (デフォルトで確定)
Last sector, +/-sectors or +/-size{K,M,G,T,P}: +100G

Created a new partition 1 of type 'Linux filesystem' and of size 100 GiB.

Command (m for help): n
Partition Number: (デフォルトで確定)
First Sector: (デフォルトで確定)
Last sector, +/-sectors or +/-size{K,M,G,T,P}: (空欄)

Created a new partition 2 of type 'Linux filesystem' and of size xxx GiB.

これでパーティションを作成できたので、mkfsでフォーマットを行っていきます。続行しますか?的なことが聞かれるのでyで続行。

# mkfs -t ext4 /dev/sdb1
# mkfs -t ext4 /dev/sdb2

ここでディスクの下準備は完了したので、VGの作成に移っていきます。

VGの作成

今回はVMのストレージ領域のみをLVMとしてProxmoxに追加していきます。そのため、/dev/sdb2のみを含むVGを作成します。

# vgcreate VMStore /dev/sdb2
# vgscan

vgdisplayvgsで作成したVMStoreが表示されていればOK。

Proxmoxのストレージ設定変更

Proxmoxのストレージ設定ファイルである/etc/pve/storage.cfgを編集していきます。
さっき作成したLVMのVG名を次のように指定して追記。

lvm: VMStore(ここは任意の名前でOK)
        vgname VMStore(作成したVGの名前)

ここまで設定が終われば、以下のように反映されます。
f:id:KLag:20211107235706p:plain
本筋からは逸れますが、使用しなくなったストレージを削除する場合も同じファイルのエントリを削除することによって行います。

自宅のIPアドレスが変わったら自動的に通知してくれるDiscord Botを作った話

お久しぶりです。
前回紹介した通り、IX2105で自宅にVPN接続を可能にしているのですが、DDNSの登録などをしていないので、ルータの電源が落ちるなどしてIPアドレスが変わったときに外部から接続できなくなってしまう弱点がありまして、これはあまりにも不便だということで、

一時的なしのぎの措置として、こんな感じに通知してくれるようなBotを作成しました。(アイコンに関するツッコミは受け付けておりません。)

つくったもの

以下のリンクからコードを見ることができます。(可読性はゴミカス以下)

github.com

簡単な説明

動かすために必要なファイルは以下の通り。

  • IPNotify.py (メイン処理)
  • DiscordConfig.py (トークン・チャンネルIDの記入用)

2ファイルで作ってますが、これはGithubに公開するにあたってトークンの類を非公開にしたかったためです。そのため、.gitignoreにDiscordConfig.pyを登録しています。

IPNotify.pyでやってること

簡単に言ってしまえば、指定した時間ごとに自分のIPアドレスを確認し、前回確認時から変更があればDiscordのAPIを叩いてメッセージを送信しているだけです。
リクエスト間隔はtime.sleep内の値を変更することで調節できますが、あまりにも短いとただのDoS攻撃になってしまうので注意。

結論

早いとこDDNS登録しよ...
以上、海月でした。

IX2105の初期化からWeb GUI表示まで

 クーラーなしでPCを動かすのが厳しい時期に入りつつあります今日この頃、皆様どのようにお過ごしでしょうか?  僕は先日某所でIX2105ルータを購入し、ようやく自宅にVPNが通せると喜んでおります。あ、申し遅れました。どうも、海月です。

 ちょっと前からIX2105の存在は知っていたのですが、そこそこ安価で手に入るとは思ってもいなかったので正直驚いています。
 早速使ってみようってなわけで、電源ケーブルを突っ込んだ後にやったことをある程度ここに書き残しておきたいと思います。

最低限必要なもの

やったこと

コンソールの接続

 はじめに、 Web-GUIからの設定を可能にするための設定を行いました。ここの操作を行う際、シリアルコンソールケーブルが必要になってきます。
 ルータのConsoleポートにRJ-45コネクタを接続し、もう一方のUSB端子をPCに接続したら、ls /devで接続されたシリアルケーブルのデバイスファイルがあるかを確認します。Macなら/dev/usbserial.<文字列> って感じの名前になってるはず。

 デバイスファイルの確認が終わったら、screen <デバイスファイルのパス>でコンソールにつなぐことができます。
 あ、Windows(Tera Term)から設定する場合は、接続先を「シリアル」にしてポートを選んで接続すればOKです。ポート番号(COM 1みたいなの)はデバイスマネージャで確認しましょう。

設定の初期化

 コンソール接続ができたら、ルータの電源を投入します。POSTの実行結果がダーッと流れた後に、ブートシーケンスの進捗を示す「#」で描かれたゲージが表示されたら、Ctrl+Cを叩きます。これでブートモニタモードに入れます。そこから先は以下の通り。

boot[0] > cc 
(消してもいいですか?的なことを聞かれるので) y
boot[0] > b

  ccおそらく"clear config"の略で、設定の初期化を行うコマンドです。bで再起動するのでこれは"boot"の略だと思われます。

ユーザとIPアドレスの設定

 ルータの再起動が終わると、「管理者ユーザを設定してね!」って感じのメッセージとともにプロンプトが表示されます。ルータの言うことに従って管理者ユーザを作成します。
 デフォルトのルータのIPアドレスは 192.168.1.254 ですが、今回は変更しました。ルータのIPアドレスが変わると固定IPを振ってる機器の設定を更新する必要があり、いろいろめんどくさいからです。

Router# en        //コンフィグモードへの移行 
Router(config)# username <ユーザ名> password plain <パスワード> administrator    //管理者ユーザの作成
% User <ユーザ名> has been added.
Router(config)# interface GigaEthernet1.0    //LAN側インタフェースの選択
Router(config-GigaEthernet1.0)# ip address <IPアドレス>/<ネットワークアドレス長>    //LAN側IPアドレスの変更
Router(config-GigaEthernet1.0)# exit    //インタフェース設定の終了
Router(config)# write memory    //設定の保存

Web GUISSHの有効化

 Univergeシリーズのルータには、便利なWeb GUIが搭載されているので、そいつを有効化してやります。また、設定画面に入るためのユーザもここで指定します。

Router(config)# http-server ip enable
Router(config)# http-server username <ユーザ名>
Router(config)# write memory

 これでWeb GUI自体は使えるようになっているのですが、このままだとACLの設定でアクセスが弾かれてしまうので、ルールを追加します。

Router(config)# ip access-list permit src any dest <ルータのIPアドレス>/32

 また、コンソール接続しないでどこからでも設定したかったので、sshも有効化しました。

Router(config)# ssh-server ip enable

おわりに

 とりあえず、ここまで設定すればWeb GUIからPPPoEやらVPNやらの設定ができるようになります。

MS-DOSからDOS/V起動ディスクを作った話

 お久しぶりです。MS-DOS実行形式を生成可能なCコンパイラ(ia16-elf-gcc)を見つけてテンションが上がってる海月です(C言語が書けるとは言ってない)。
 macOSのVBoxにWindows Meを導入しようと思って、Windows機から98の起動ディスクを引っ張ってきたのはいいんですけど、見事にファイルがぶっ壊れてて使い物になりませんでした。どうして(現場猫)。
 ここで思い出す。「仮想アプライアンスで移行してきたMS-DOSがあるじゃん。」
 ってなわけで、すでにセットアップ済みのMS-DOS環境から日本語DOSの起動に必要なファイルをコピーして起動ディスクを作っていきます。

作業開始

 まずはいつものやつ。新しいフロッピーイメージを用意して、C:\> FORMAT A: /S。 FORMATコマンドに /S オプションをつけてフォーマット後にシステムファイルの転送まで一気にやっちゃう。追加で拡張メモリ領域を利用するために、HIMEM.SYS, EMM386.EXEも転送しました。これでDOS自体は起動しますが、日本語表示ができないので「ダメ〜」(トム・ブラウン)。
 DOS/V環境が動作してるので、必要なドライバファイルの確認は簡単。C:\ > TYPE CONFIG.SYSで、起動時に組み込まれるドライバが見られます。細かく言えば色々違うけど、そこはご愛嬌。そもそもこのブログを読む人がいないので誰も気にしない。  

日本語ドライバの転送

 必要なドライバたちがわかったところで、早速ドライバを準備していきます。今回は以下のドライバを用意しました。たぶんこれだけあれば足りるはず。

  • BILING.SYS : バイリンガル対応に必要なドライバ
  • JDISP.SYS : ディスプレイドライバ
  • JFONT.SYS : フォントドライバ
  • JKEYB.SYS, JKEYBRD.SYS : 日本語キーボードドライバ
  • ANSI.SYS : エスケープシーケンス処理ドライバ
  • 各種フォントファイル(ANK16/24.FNT, KANJI16/24.FNT, GAIJI16/24.FNT)

 あとはフロッピー上にCONFIG.SYSとAUTOEXEC.BATを作成して、こいつらを起動時に読み込んであげるようにします。最終的にこんな感じになりました。

DEVICEHIGH=BILING.SYS
DEVICEHIGH=HIGHMEM.SYS
DEVICEHIGH=JFONT.SYS /P=A:\
DEVICEHIGH=JDISP.SYS /HS=LC
DEVICEHIGH=JKEYB.SYS /101 A:\JKEYBRD.SYS 
DEVICEHIGH=ANSI.SYS

 コンベンショナルメモリ領域を確保したいから全部ハイメモリ領域に展開したけど、本当にこれでいいのかわからん。でもとりあえず動くからヨシ!
 ここまでの作業が終われば最小構成での起動ができます。フロッピーしか読めないし、ファイルの編集もできないけど。

CDドライバとかその他もろもろ

 流石にテキストファイルの編集ができないのは不便なので、EDITが使えるようにします。とは言っても、C:\DOS\から EDIT.COM と QBASIC.EXE をコピーしてくるだけでOKです。楽チン。  また、セットアップのためにはCD-ROMが読めないとダメなので、例によってOAKCDROM.SYSを組み込んであげます。どこにでもいる野生の9xを捕まえて、OAKCDROM.SYS と MSCDEX.EXEを引っこ抜きましょう。終わったらCONFIG.SYSにDEVICEHIGH=OAKCDROM.SYS /D:CD001を、AUTOEXEC.BATにC:\DOS\MSCDEX.EXE /D:CD001 /L:Eをそれぞれ追記します。これでCDも読めます。
 インストールでもするかね、って思ったんですけどFDISKとFORMATができないとどうにもならんので、FDISK.EXEとFORMAT.EXEもコピー、っと。

 これで無事にDOS環境からインストールCDのSETUP.EXEが動くようになりました。早速Windows Meをインストールしよ......え、何?「Windows98からのアップグレード限定」...?
 そういうのは先に確認するようにしましょう。僕との約束だよ!ではまたいつか。