田中ブログ

さいたまに住んでいる男です。仕事はSESでプログラムを書いてることが多いです。聖地巡礼、日々の生活、技術的なことを書いていこうと思います。

nginxのerror_pageとluaでerror_page内でステータスの変更・処理を行う

error_pageを指定しているはずなのに、期待したページが表示されなかったことがあったのでメモ。
やりたいことは以下です。
①404エラー時にステータスコードを502エラーとしてカスタムしたエラーページ(50x.html)を返す
②error_pageの中では条件によっては200を返す

環境としては、openrestyを利用してnginxとluaを使う感じです。

まずは①を試します。
通常は、error_pageの箇所に=502を記述し、リダイレクト先を/50x.htmlとすれば済みますが、最終的にlocationの中でいろいろやりたいので以下のように書いて試しました。

↓nginx.confの抜粋

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        error_page 404 /404.html;
        location = /404.html {
            return 502;
        }

この場合、curlでリクエストを投げるとopenrestyとバージョンが表示されたデフォルトっぽい自動で生成されたエラーページが表示されました。
(error_pageを書いてない場合、以下のフォーマットのページがデフォルトで表示されるのかな?)
curlとレスポンス

curl http://localhost:80/abc  -w '%{http_code}\n' -s
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>openresty/1.13.6.2</center>
</body>
</html>
404

ログを見ても/404.htmlにもアクセスしておらず、return 502でステータスコードが変わるわけでもなくダメダメの模様。(ちなみにreturn 502を消すと/404.htmlまではアクセスされた)

↓ログ

2019/05/03 15:27:55 [error] 141#141: *18 open() "/usr/local/openresty/nginx/html/abc" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /abc HTTP/1.1", host: "localhost"

これが動かなかった理由ですが、まずrecursive_error_pagesをONとしていなかったのが原因。
recursive_error_pagesはデフォルトでOFFになっており、error_page内で再度エラーとしてもハンドリングされないみたい。

以下のようにrecursive_error_pagesをONにしてやり、上位の404で処理するlocation内で、再度error_page404の記述をしてやれば動きました。
(下記の例のlocationはまったぐ意味がないが再帰的なerror_pageが動くということを示すためのもの。)

↓修正版

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # ここに定義が必要
        recursive_error_pages   on;

        error_page 404  /404.html;
        location = /404.html {
            error_page 404 =502 /50x.html;
        }

結果カスタムしたエラーページが返ってきてレスポンスコードも書き換わっている。

curlとレスポンス

 curl http://localhost:80/abc  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>
<p><em>Faithfully yours, OpenResty.</em></p>
</body>
</html>
502

これに加えてluaも利用したうえで記事の先頭の方で書いた①と②を実現する処理を書いてみる。
luaのコードを加えたnginx.conf

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # これをONにすることでluaでexit(502)とした際も上記のerror_pageで処理される
        recursive_error_pages   on;

        error_page 404 @myError;

        location @myError {
            content_by_lua_block {
               if ngx.var.arg_flg == "1" then
                   ngx.status = 200;
                   -- execだけではstatusは書き換わらない
                   ngx.exec("/");
               else
                  return ngx.exit(502);
               end
            }
            # 404に対してerror_pageを書きたいときはここに記述
            #error_page 404 /50x.html;
        }

flg=1とした場合
curlとレスポンス

 curl "http://localhost:80/abc?flg=1"  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Welcome to OpenResty!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to OpenResty!</h1>
<p>If you see this page, the OpenResty web platform is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="https://openresty.org/">openresty.org</a>.<br/>
Commercial support is available at
<a href="https://openresty.com/">openresty.com</a>.</p>

<p><em>Thank you for flying OpenResty.</em></p>
</body>
</html>
200

flg=2とした場合
curlとレスポンス

 curl "http://localhost:80/abc?flg=2"  -w '%{http_code}\n' -s
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>
<p><em>Faithfully yours, OpenResty.</em></p>
</body>
</html>
404

意図したとおり、luaを利用してのerror_page内で条件によるステータスの変更、error_pageでも自動生成されるエラーページでなく、用意したエラーページが表示されることが確認できました!キモはrecursive_error_pages をONにすることでした。
また、error_pageをlocation内に再定義することで再帰的にerror_pageの処理もできるみたいですね。

以上

Mockitoでインターフェースをモック化する

技術ネタ。
タイトルの通りMockito(PowerMockito)でインターフェースをモック化しようとしたときにハマったのでメモ。

以下のインターフェース、クラスを作成しているとします。
・インターフェース:Sample
 ⇒メソッドとしてdoSomething()を定義
・実装クラス:SampleImpl
 ⇒メソッドとしてdoSomething()を実装

そして実装は以下のようになっているとする。

Sample instance = new SampleImpl();
instance.doSomething()

上記をMockito(PowerMockito)でモック化します。
以下で期待したように動きました。

// newするときと同じ感じでmock側は実装クラス
Sample mock = Mockito.mock(SampleImpl.class);
Mockito.doAnswer(~)when(mock).doSomething();
// モックはキャストする
PowerMockito.whenNew(SampleImpl.class).withAnyArgments().thenReturn((SampleImpl)mock);

// あとは、instance.doSomething()をコールしてる処理を実行

以上

2019年確定申告のメモ(ふるさと納税)

来年に確定申告をするときに忘れないようにメモ。

書類作成

ネット経由で作成。ふるさと納税したので納税情報を入力して印刷。押印する箇所があるので忘れなようにする。

添付書類は、通知カード+運転免許証と源泉徴収票。後は各自治体から受け取った寄付金受領証。

提出

アナログ思考があるので自分で提出へ。自分が住む大宮エリアはさいたまスーパーアリーナで受付とのことなのでスーパーアリーナへ。作成済みを提出、作成するといった感じで列が形成されていたので作成済を提出の列へ並びます。今年は休日の10:30ころに行ったのですが列が長く、提出完了まで40~50分かかりました。

ただ、入り口部分に作成済の書類を入れる箱が置いてあるのでそこに入れてもいいんだろうなと思いました。

列に並ぶメリットというと、書類をチェックしてもらう(検算はなし)、控えに押印してもらえるというところでしょうか。

※控えに押印してもらうなら、控えを忘れないようにする。

また、列に並んでいるときに張り紙でマイナンバーカードを提示してくださいとありましたが、特に提示せずに終了しました。なので記載内容に不備がないのであれば、持っていくのは作成した書類だけでOKということでしょう。

来年へ向けてのメモ

・並ぶ時間が結構長く、正直無駄なので平日に行きたい。

・並んで提出するなら、控えを持ってくのを忘れないこと。

・検算してもらう必要はなさそう。

以上

redisで大容量データ、大量データをコマンドで登録する

技術ネタを。
redisで1MB程度のデータをコマンドで登録しようとしたときに手こずったのでメモ。

結論からいうと以下のようなコマンドで登録できました。

redis-cli -h サーバ -x set キー名 < ファイル(jsonなど)

キモは-xオプションでした。

ちなみに上記のような大きなデータを登録する方法調べていたときに、パイプラインを使ったり、--pipeオプションを使ったりする方法がヒットしたのですが、そちらは大量データを登録する方法でした。
※大量データというのは、ファイルに1行ずつsetコマンドを書いた行が大量にあるみたいな感じです。
ちなみに、以下みたいなコマンドです。

cat ファイル名 | redis-clie -h サーバ set キー名

最初は、上記で1MBのデータが登録できると思ったのですが、パイプラインでデータを渡すときにはバッファサイズが決まっているようで(自分が試した環境では4096バイト)1MBデータがまとめて渡すことができずエラーとりました。

以上です。

日光だいや川公園オートキャンプ場で冬キャンプ

今回は日光のだいや川公園キャンプ場に行ってきました。冬キャンプです。

ホームページのリーフレットを見ると冬季は事前に予約してくださいとあったので予約してから行きました。

 

リーフレット

https://www.park-tochigi.com/daiyagawa/wp/wp-content/uploads/2018/08/%E3%82%AA%E3%83%BC%E3%83%88%E3%82%AD%E3%83%A3%E3%83%B3%E3%83%97%E5%A0%B4%E3%83%AA%E3%83%BC%E3%83%95%E3%83%AC%E3%83%83%E3%83%882018-3.pdf

 

日光は日光東照宮などの社寺が有名ですが、そこの手前にキャンプ場があります。

キャンプ場はだいや川公園の一部で、外から見た感じでもかなり整備されていそうな感じでした。いざ敷地に入っているとキャンプのエリアに入るところにゲートがあり、今ままで見たことが無いような感じでした。

 ▼サービスセンター。手前の駐車場に駐車して受付。

f:id:tanaka653:20190209113303j:plain

 

サービスセンターで受付を行うとICカードが渡されるので、それでゲートが開けられます。サービスセンターには売店(お菓子、ガス、薪など売ってた。)、シャワー、ランドリー、トイレがあり充実している感じでした。

今回は、予約した時に当日の受付時にサイトを決めさせてくださいと話をしたので、利用可能なサイトから人がまわりにあまりいなそうな場所をチョイスしました。

なお、冬季はサービスセンター付近のサイトのみ、トイレ・炊事場もサービスセンターのもののみ利用可能でした。ちなみに冬季は通常の料金より安く、1サイト2,700円で利用できるのでお得です。

 

サイトを決めたらサイトへ移動。時期のせいなのかもしれませんが、リーフレットのマップとキャンプ場内の印象は結構違いましたね。マップ的にはかなり広そうで中央を流れる川も控え目な感じでした。冬で緑が無いというのもありましたが、それはそれでよい雰囲気でした。

 ▼葉が落ち、雪も少し残っていて冬って感じです。

f:id:tanaka653:20190209113750j:plain

▼場内の川は思ったより細い。

f:id:tanaka653:20190209113852j:plain

今回は電源つきのサイトだったので設営のときには家電の準備なども行いました。電源を差す穴は、家で使ってるようなコンセント向けのは1つだけでした。2つはついているのかなと思ったので少し意外でしたね。

 ▼サイトはこんな雰囲気

f:id:tanaka653:20190209114136j:plain

▼電源はこんな感じ。後ろにあるのはトイレ。使用禁止でしたが綺麗そう。

f:id:tanaka653:20190209114221j:plain

設営を終えたら散歩へ。だいや川公園の一部なので、サイトからちょっと歩くだけで公園をぶらぶらできます。公園内は結構整備されており、池も綺麗で最高でした。

ちなみに、キャンプ場側の公園の道路を挟んだ向かい側に、直売所などがあるのでそこに行ってみるのも良いと思います。

 ▼トレーラーハウスもあります。

f:id:tanaka653:20190209114330j:plain

▼池もありすごく綺麗な景色

f:id:tanaka653:20190209114430j:plain

f:id:tanaka653:20190209114556j:plain

散歩から戻ったら、焚火の火おこしをして飲み食いして就寝。寒いとバーナーとかライターとか火が付きにくくなるんですね。。

f:id:tanaka653:20190209114617j:plain

朝も散歩してご飯食べて撤収。

 ▼朝日がのぼってきました。フリーサイトのエリアにて。

f:id:tanaka653:20190209114746j:plain

▼朝の場内散歩

f:id:tanaka653:20190209114824j:plain

今回のキャンプ場はかなり整備されており綺麗な場所でした。綺麗な場所だけあってキャンプシーズンはかなり混雑しそうな雰囲気だなあと。

冬にまた来たいですね!

御岳山を登る

12月末に御岳山へ行ってきました。

f:id:tanaka653:20190127145632j:plain

想像はしていましたが、御岳山まで行くのが結構遠かったです。以下のような流れで埼玉からは2時間以上かかりました。

御嶽駅へ電車で移動(ここがまず遠い)

・滝本駅へバスで移動

・御岳山駅へケーブルカーで移動

 

基本的に御嶽駅から御岳山駅へは接続が良いいので待ち時間は気にならずにスムーズに行けました。ケーブルカーも恐らく初めてのったので良い経験になりました。

f:id:tanaka653:20190127145343j:plain

ケーブルカーからの傾斜を見ていると、ケーブルカーを利用せずに滝本駅からの御岳山駅まで歩いてのぼっていくのは大変そうだなーと思いましたね。

f:id:tanaka653:20190127145228j:plain

ケーブルカー乗り場の売店で大福と御嶽汁を購入していざ出発です。

今回は以下のルート的で歩きました。ロックガーデンが人気とのことなのでそこを目指します。

御嶽神社

・長尾平

・七代の滝

・ロックガーデン

・戻り

 

マップは以下のサイトのものが確か青梅駅のホームにあったのでそれを回収して利用しました。

御岳登山鉄道

 

ケーブルカー乗り場を出るといきなり良い展望です。(自分の足では全然登山してないのですが。)

f:id:tanaka653:20190127141112j:plain

 

まずは、御嶽神社の方に向かいます。道には基本的に案内板があるのでそれに従って進んでいきました。道中は家とかが普通にあるので登山感は全然ありませんでした。茅葺屋根の建物もあったりで良い雰囲気です。

民宿も多々あり宿泊もできるようですね。

家のゾーンを進んで商店街。平日だったのもあり中々閑散としていました。

f:id:tanaka653:20190127141713j:plain

続いて御嶽神社へ。ここの神社は「おいぬ様」信仰だそうでところどころに犬要素が。

▼犬のベンチ

f:id:tanaka653:20190127141908j:plain

 着いた!

f:id:tanaka653:20190127142213j:plain

▼めっちゃ犬っぽいなあ。

f:id:tanaka653:20190127142325j:plain

続いて長尾平へ。御嶽神社までは全然山感が無かったので気持ちを引き締めていこうと思ったのですがここも距離的には近くすぐに到着しました。

ここにはカフェもあるようでしたが平日だったのでお休みだった模様。コーヒー買ってのんびり飲むのもいいでしょうね。

▼ちゃんとしたテーブル・イスもあり開放感も素晴らしく最高の場所ですね。

f:id:tanaka653:20190127142707j:plain

まだ全然登ってないですがここで昼食を。御嶽汁食べました。

f:id:tanaka653:20190127142835j:plain

そして、七代の滝へ。マップだと距離感がわかりませんでしたが、思ったよりは下りましたね。ずっと下りが続き。。。

 到着!やっぱり滝は癒される。

f:id:tanaka653:20190127143207j:plain

マップを見ると滝の周辺に鉄ハシゴがあるようで気になっていましたが、ここから登っていくときに利用する形でした。ちょっとびびってましたが普通の階段で安心しました。

f:id:tanaka653:20190127143531j:plain

しばらく上りが続いてロックガーデンへ。名前の通り、岩に囲まれている遊歩道でした。

f:id:tanaka653:20190127143752j:plain

 水もめっちゃ綺麗。

f:id:tanaka653:20190127143853j:plain

霜柱も。

f:id:tanaka653:20190127143946j:plain

ロックガーデンを進んでいくと綾広の滝が。

f:id:tanaka653:20190127144243j:plain

ロックガーデンを抜けた後は、御嶽神社の方へ向かう形で帰路へ。

 

今回のルートは所々で休憩できる場所があったりで、楽に登る(歩く)ことができました。長尾平でご飯を食べたり、ヤマコーしたりも最高だと思います。

確か御岳山はヤマノススメにも出てきていたらしいのでそちらのルートも行ってみたいなあ。

 

 

AWS Lambda(Node.js)でasync/awaitでS3の操作をする

 技術ネタを。
AWS Lambda(Node.js)でasync/awaitする。です。
Node.jsのバージョンは、8.10です。
 
async/awaitを利用した目的ですが、lambdaで以下のような処理を行いたかったためです。

①s3にアップロードされたzipファイルを解凍(lambdaにトリガー設定)
②古いファイルを削除

まあ、async/awaitを使わなくても動きそうですが、①が終わってから②の処理を行う方が素敵な感じなので。。。

コードは以下のイメージです。手元で実行できないのでイメージですみません。。
s3の各関数をコールするときにawaitを付ける感じです。

var AWS   = require('aws-sdk');
var s3 = new AWS.S3();

exports.handler = async (event, context) => {
   try {
    var s3Object = await s3.getObject(ここにパラメータ).promise();

    // zip解凍処理。node-zipとかで解凍。

    var arr = [];
    //  foreachでs3.putObjectするpromiseを作り配列にpush

    // putObjectを実行
    await Promises.all(arr);

    // 削除対象のリストを取得
    await s3.listObjects(ここにパラメータ).promise();

    var delArr = [];
    // foreachでs3.deleteObjectするpromiseを作り配列にpush

    await Promises.all(delArr);
  } catch(err) {
    console.log(err);
  }
}

 
handlerのところで、asyncと書くことで処理中でawaitが利用できるとのこと。
awaitを付けると、awaitを付けた処理が終了するまで待ってくれる。ちなみに結果も普通に取得できる。

s3.getObject(~).promise()のところですが、引数にコールバックを書いた場合にgetObjectが2回実行されました。
なので引数のコールバックは不要。

s3へのputやdeleteですが、forEachなどでやろうと思ったのですがawaitは使えないとのことで、promiseの配列を作成して、Promises.allで実行という形としました。
これだとS並列で実行してくれるようでなおいい感じですね。

 
以上