It's raining cats and dogs.

無駄なことなんてないはず

ansibleで "Encountered unknown tag 'd'." なエラーがでた

どうして今までハマらなかったんだろう的な話で、ansibleでapacheの設定ファイルで表題のエラーがでた。

apacheのLogFormatを追加してprovisionしたときに出たのだけど、原因としては追加したLogFormatの記述がansibleで使っているJinja2テンプレートとして解釈されてエラーとなっていた。
ちなみに追加したフォーマットを一部抜粋

LogFormat "... time:%{%d/%b/%Y:%H:%M:%S %z}t ..." my_format

具体的にはここ→ time:%{%d/%b/%Y:%H:%M:%S %z}t

ansibleのplaybookは以下のような感じ

- name: apache conf
  template: src=templates/apache/httpd.conf dest=/etc/httpd/conf/httpd.conf

回避策としては以下2つのどちらか

Jinja2の表記でJinja2の書式として認識させない

rawでそのまま出力

{% raw %}
LogFormat "... time:%{%d/%b/%Y:%H:%M:%S %z}t ..." my_format
{% endraw %}

Jinja2のテンプレートとして扱わない

playbookで templatecopy に変えることで、テンプレートとして解釈せずにそのまま単にコピーするだけ。

- name: apache conf
  copy: src=templates/apache/httpd.conf dest=/etc/httpd/conf/httpd.conf

ということで

どちらでも良いと思うけど、単にコピーしてほしいだけだったので、後者を選択した

slim3のMiddlewareの実行順

slim3のMiddlewareは後から追加した順に実行されるらしい

<?php

$app = new \Slim\App;

$app->add(new \MyMiddleware1);  // 実行順 3
$app->add(new \MyMiddleware2);  // 実行順 2
$app->add(new \MyMiddleware3);  // 実行順 1

上から実行されてほしい感ある

slim3でクライアントIPアドレスを取得する

slim2では

<?php
$app->request->getIp();

的なことができたのだけど、slim3だとどうやら取得する術が標準では存在しないぽい。マニュアルにも、サードパーティなライブラリ使えって書いてある。

てかどうしてなくした。普通クライアントIPとりたくなるだろ。

本体になくてもいいけど、せめて公式のコンポーネントとして作って欲しいなぁと。

apacheでS3にプロキシさせるときにbasic(digest)認証をかませていると400 bad requestになる

S3にBasic認証付きリクエストをすると400:Bad Requestになる | Siguniang's Blog

この記事のapache版。

apacheのmod_proxyでS3においてある画像にプロキシさせた時に、basic認証をかませているとS3から400 bad requestが返ってきてしまって画像が表示されなくなってしまった。
上記ブログはnginxだったので、apacheで解決したやり方をメモっておく。

<Location "/s3-images/">
  RequestHeader set Authorization ""
</Location>

ProxyPass        /s3-images/  http://hogehoge.s3-ap-northeast-1.amazonaws.com/s3-images/
ProxyPassReverse /s3-images/  http://hogehoge.s3-ap-northeast-1.amazonaws.com/s3-images/

やっていることは同じで、S3用のロケーションパス以下は Authorization ヘッダを潰している。
これで解決。

ところで、原因を調べていたんだけど、S3のサイトにそれっぽいことが書いてあった。

docs.aws.amazon.com

どうやらS3のREST APIは認証の時に Authorization ヘッダを使っていて、普通に(Authorizationヘッダがない状態で)プロキシしているだけだと認証情報を求められないのでそのままプロキシしてくれるんだけど、basic認証なりで Authorization ヘッダがついてしまうとS3側で認証しようとしてコケている感じなんだろうな。(IAMの話かもだけど)

学びがあった

余談

はてなブログシンタックスハイライト、 apachestyle なんてのがあったんだ。

多次元配列をうまいことマージしてくれるarray_replace_recursive便利

今更感あるけど、array_replace_recursive というメソッドが便利だった。

設定ファイルなんかはphpでサクッと書いちゃうことが多い。例えば

<?php
return [
    'key1' => [
        'key11' => 'val11',
        'key12' => 'val12'
    ],
    'key2' => 'val2'
];

みたいなファイルにしておけば

<?php
$config = require '/path/to/config.php';

な感じでrequireするだけだし、phpのコードでしかないから早いし、パースしなくていいし、記述ミスがあってもsyntax errorでコケてくれるし、OPcacheでもかませればメモリに乗ってくれるしいいコトづくめ。
なんだけど、例えばベースのconfig用配列をrequireした後に、個別の処理でconfigを上書きしたい時があったりしたときに、array_mergeでマージしてしまうと子要素がまるっと上書きされてしまうので、泥臭く手でマージしたりしてたんだけど、表題のarray_replace_recursive を使えば簡単に解決することがわかった。

たとえば、array_mergeだと

<?php
$a = [
    'key1' => [
        'key11' => 'val11',
        'key12' => 'val12'
    ],
    'key2' => 'val2'
];
// key12だけ上書きしたい
$b = [
    'key1' => [
        'key12' => 'hoge12'
    ]
];

var_dump(array_merge($a, $b));

// key11がなくなっちゃった!
array(2) {
  'key1' =>
  array(1) {
    'key12' =>
    string(6) "hoge12"
  }
  'key2' =>
  string(4) "val2"
}

こんな感じで子要素がまるっと上書きされてしまう。
でも、array_replace_recursive だと

<?php
$a = [
    'key1' => [
        'key11' => 'val11',
        'key12' => 'val12'
    ],
    'key2' => 'val2'
];
// key12だけ上書きしたい
$b = [
    'key1' => [
        'key12' => 'hoge12'
    ]
];

var_dump(array_replace_recursive($a, $b));

// key12だけ上書きされた!
array(2) {
  'key1' =>
  array(2) {
    'key11' =>
    string(5) "val11"
    'key12' =>
    string(6) "hoge12"
  }
  'key2' =>
  string(4) "val2"
}

こんな具合に key11 を残したまま key12 だけ上書きしてくれた。便利!