haml2slim を改善して haml から slim への移行をしやすくしました
動機
haml から slim へ移行しようとして haml2slim を使おうとしたものの、
上手く変換されず自力で直さないといけないところが多い状態でした。
最初は「リファクタリングする箇所も多いから、一行ずつ注視して自力で変更していけばいいか」と思っていたんですが、
同じところを何度も直していると、ミスも起こるし時間もかかるしで、
haml2slim 自体を直すのが効率良いなと気付きまして、以下のリポジトリを作りました。
気が向いたら本家にプルリクエストを送る可能性もありますが、
2014年のプルリクエストが2022年の今なお放置されている状態で、
希望が薄いので作成する可能性は低いです……。
フォーク元のベースとして、そのプルリクエストである https://github.com/a2ikm/haml2slim/tree/ruby19-hash を使わせていただきました。
ありがとうございます。
本家の haml2slim から変更したこと
https://github.com/nekonenene/haml2slim/compare/9d79c40...4ce0c64
1. .gitignore
を調整
Gemfile.lock
や .bundle
が gitignore の対象になっていたので削除しました。
逆に vendor/bundle
が gitignore の対象になっていなかったので追加しています。
関連する差分: https://github.com/nekonenene/haml2slim/compare/6490994...bef06a9
2. minitest を Gemfile に追加
minitest
をテストで使っているのに Gemfile
に指定がされていなかったので追加しています。
また、 minitest の現在のバージョンでは deprecated になっている記法を修正しました。
関連する差分: https://github.com/nekonenene/haml2slim/commit/dfc0d919433ee93af21853e8623409a14e11d755
3. Ruby 1.9 以降での Hash Syntax に対応
私がベースに使ったリポジトリの作者さんのプルリクエスト ( https://github.com/slim-template/haml2slim/pull/23 ) で
Ruby 1.9 以降での Hash Syntax に対応しているのですが、使ってみて、
これにはまだ不足があることに気付きました。
%button{data: { confirm: "OK?" }}
のような data 属性の変換に対応していないことです。
また、Hash Syntax に関わらず、
キーや値が single quotations ( '
)で囲まれている場合や、
値が Symbol である場合や、値にスペースが含まれる場合に、
正常に変換されていなかったので修正しました。
関連する差分:
- https://github.com/nekonenene/haml2slim/compare/dfc0d91...74a4c9e
- https://github.com/nekonenene/haml2slim/commit/a0d16cdcb21a773ab4f74346d96e622781953e90
- https://github.com/nekonenene/haml2slim/commit/26dfcb01c4498d67c7db550f0a3ab9463bd6caa5
4. 文字列内の変数展開の修正
%span #{1 + 1}
が
span #{1 + 1}
と変換されるようになっていましたが、これでは2行目でエラーを吐いてしまうので、
span | #{1 + 1}
と変換されるように修正しました。
また、
%span 次は#{1 + 2}回目です
がパースの関係で
span 次は# 1 + 2 回目です
のように変換されてしまう問題も修正しました。
関連する差分: https://github.com/nekonenene/haml2slim/compare/77a5072...17acd8d
5. (
, [
, {
は HTML エスケープするように
%span (#{aaa})
は haml では問題なく、文字列としてカッコがレンダリングされるのですが、
span (#{aaa})
と slim でなっている場合、カッコは Attributes Wrapper と思われてしまい
パースしようとするのですが、上手くいかずにパースエラーが起こってしまいます。
思わぬエラーの要因になるので、
HTMLタグのすぐ後ろに (
, [
, {
がある場合には HTML エスケープをおこなうようにしました。
不格好ではありますが、エラーが起こるよりは断然マシです。
関連する差分: https://github.com/nekonenene/haml2slim/commit/4ce0c640b2d0e580e9c3a5274d5be535ce8117d0
6. Vue の変数展開に対応
Rails と Vue.js を組み合わせて使っている場合のみに関係する話ですが、
%span {{ 1 + 1 }}
のように Vue の変数展開を書いているものを、
span {{ 1 + 1 }}
のように slim 向けに直しても上手くいってくれません。
(参考: https://qiita.com/Take-st/items/4bb3ebd25f361276c4c4#comment-ae32b23dae3266a24e54 )
span | {{ 1 + 1 }}
と変換されるようにしてみました。
関連する差分: https://github.com/nekonenene/haml2slim/commit/f23bdc1ee282919a57ad5f65cb4f448ea53c35c4
7. HTMLタグの後ろにスペースを付ける
haml では
%span= Time.now
のように、HTMLタグのすぐ後ろに =
を付けるのが一般的ですが、
slim では
span = Time.now
のようにHTMLタグの後ろにスペースを付けるのが一般的です。
( http://slim-lang.com )
そのように変換されるように調整しました。
関連する差分: https://github.com/nekonenene/haml2slim/compare/17acd8d...18b334e
解決していない問題
これで多くの問題が片付きましたが、これでもまだ全てではありません。
私の気付いているものですと、
= render partial: "dummy", collection: @some_variable, as: :some_variable
のように複数行に渡るものが
= render partial: "dummy", | collection: @some_variable, | as: :some_variable
と変換されてしまい、パースエラーになってしまう問題を残しています。
あまりあるケースではないのと、対応が面倒そうに感じるので放置しています。
おわりに
これで haml から slim への移行作業が大幅に効率化されました。
やはり、自力で繰り返しおこなう作業は、コンピューターに任せるのがなによりですね。
Amazon OpenSearch Service を使ってみてハマったところまとめ
Amazon OpenSearch Service を導入しようとしてハマったところをまとめます。
記事を残してくれた数多くの人に感謝です。
ハマったところいっぱいあったんですが、
逐一メモっていなかったので思い出した順に書いていきます。
↓ 前回の Amazon OpenSearch Service 関連の記事
CloudWatch Logs を OpenSearch に送る Lambda 処理でエラーが出る件
『CloudWatch Logs データの Amazon OpenSearch Service へのストリーミング - Amazon CloudWatch Logs』に従って進めて、
CloudWatch から OpenSearch Service にログを流すサブスクリプションフィルターを作ったあと、
最初はまずログが出せなくて困りました。
Lambda の Role に AWSLambdaBasicExecutionRole Policy をアタッチして解決しました。
いつも Lambda を新規作成する場合は勝手に作られるからその作業抜けてた。
で、ログ出すようになったら困ったのが以下のようなエラーが出ること。
どうも権限周りの話らしい。
{ "errorType": "Error", "errorMessage": "{\"statusCode\":403,\"responseBody\":{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda, backend_roles=[arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda], requestedTenant=null]\"}],\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda, backend_roles=[arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda], requestedTenant=null]\"},\"status\":403}}", "stack": [ "Error: {\"statusCode\":403,\"responseBody\":{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda, backend_roles=[arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda], requestedTenant=null]\"}],\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda, backend_roles=[arn:aws:iam::123456781234:role/ControlOpenSearchTestFromLambda], requestedTenant=null]\"},\"status\":403}}", " at _homogeneousError (/var/runtime/CallbackContext.js:12:12)", " at postError (/var/runtime/CallbackContext.js:29:54)", " at done (/var/runtime/CallbackContext.js:58:7)", " at fail (/var/runtime/CallbackContext.js:70:7)", " at Object.fail (/var/runtime/CallbackContext.js:106:16)", " at /var/task/index.js:43:25", " at IncomingMessage.<anonymous> (/var/task/index.js:177:13)", " at IncomingMessage.emit (events.js:326:22)", " at endReadableNT (_stream_readable.js:1241:12)", " at processTicksAndRejections (internal/process/task_queues.js:84:21)" ] }
https://aws.amazon.com/jp/premiumsupport/knowledge-center/opensearch-troubleshoot-cloudwatch-logs を読んだら解決方法が書いてあって、
OpenSearch の管理画面から all_access
Role に User と Backend role の両方で
AWS Role を当ててあげると解決するらしいので修正。
ちなみに、Backend role だけ割り当てると次に紹介するエラーが起こるので注意。
とりあえず上手くいって、じゃあ次に他のロググループからも同様に OpenSearch にデータを送ろうと思いました。
同名の Lambda は作れないので、最初と違って
『Create Amazon OpenSearch Service subscription filter』ではなく
『Lambda サブスクリプションフィルターを作成』を選ぶ点に注意。
じゃあそれで作成して上手くいくかと思いきや、以下のエラーが!
原因が……わからない……!!!
{ "errorType": "Error", "errorMessage": "{\"statusCode\":200,\"responseBody\":{\"took\":6,\"errors\":true}}", "stack": [ "Error: {\"statusCode\":200,\"responseBody\":{\"took\":6,\"errors\":true}}", " at _homogeneousError (/var/runtime/CallbackContext.js:12:12)", " at postError (/var/runtime/CallbackContext.js:29:54)", " at done (/var/runtime/CallbackContext.js:58:7)", " at fail (/var/runtime/CallbackContext.js:70:7)", " at Object.fail (/var/runtime/CallbackContext.js:106:16)", " at /var/task/index.js:43:25", " at IncomingMessage.<anonymous> (/var/task/index.js:177:13)", " at IncomingMessage.emit (events.js:412:35)", " at endReadableNT (internal/streams/readable.js:1317:12)", " at processTicksAndRejections (internal/process/task_queues.js:82:21)" ] }
ステータスコードが 200 なのにエラー……なかなか奥が深い状態です。
しばらく謎だったんですが、
こちら ↓ の記事が解答に導いてくれました。
複数の type を送れないようです。
ここを記事にあるように書き換えても、ロググループの名称は @log_group
から取得できるのでご安心ください。
AWS コンソールから見られない箇所がある
AWSコンソールで OpenSearch Service の「クラスターの状態」が表示されない問題がありました。
とはいえ、これは明らかに権限エラーだと表示されていたのですぐ解決できました。
OpenSearch の Role の all_access に、IAMユーザー名を User としてマッピングすれば解決です。
こういうの。 arn:aws:iam::123456781234:user/nekonenene
ただ、IAMユーザーを1つずつ割り当てるのは面倒なので、
IAM Role と IAM Group を活用していい感じに出来るのでは、などと思ったりします。
Amazon OpenSearch Service のヘルスエラー
これはあるあるのようですが、
『Elasticsearchのshardとreplica - なんかかきたい』の記事にあるように、
デフォルトだと number_of_shards
の数が多いために
『割り当てられていないシャード』が多く発生し、
AWSコンソールで Amazon OpenSearch Service のヘルスエラーが表示されます。
対応は簡単で、Dev Tools から例えば
POST _template/cwl-2021.11.12 { "index_patterns": ["cwl-*"], "order": 1, "settings": { "index": { "number_of_shards" : 2 } } }
のようにおこない、 number_of_shards を減らします。
もちろん、本番運用でなくテスト運用の場合です。
本番運用する場合のオススメの設定については AWS が書いているので、以下の記事を参考にしましょう。
Amazon Elasticsearch Service ドメインを設定するベストプラクティス | Amazon Web Services ブログ
インデックスの登録場所がわかりにくい
Stack Management > Index patterns の順に進みます。
未来の自分も忘れそう。
インデックスの自動削除(Rotation)の仕方がわからない
Index Management > State management policies のページでポリシーを作成するのですが、
JSONで書かなければならず、マジでよくわかんなかったです。
- Amazon OpenSearch Service でのインデックスステート管理 - Amazon OpenSearch Service
- SIEM on Amazon ES で生成されるインデックスに対して Index State Management を使ったライフサイクル設定をしてみた | DevelopersIO
この2つの記事のおかげで、無事に設定ができました。
Kibana を使ったことある方なら知っていることも多いんでしょうが、
自分は経験ないので大変でした。なんとか、なんとなくでやってます……ムズい……。
ようやくいろいろが終わって、データ分析に使えそうです。
AWS OpenSearch Dashboards に Google 認証でログインする方法
Amazon OpenSearch Service から作成した
AWS OpenSearch Dashboards に、Google 認証(Google Workspace を用いた SAML認証)でログインできるようにする方法を解説します。
1. Amazon OpenSearch Service のドメイン作成
まずはテスト用に適当に作成します。
カスタムエンドポイントを有効化する場合は、 Route 53 で CNAME 設定を入れることを忘れないようにします。(忘れてちょっとハマった)
カスタムエンドポイントを使用する場合の参考記事: https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/customendpoint.html
デフォルト設定から以上のような箇所を変更して作成しました。
作成後、画面上部に以下のようなメッセージが表示されます。
SAML設定をあとからおこなうのですが、このドメインの設定には時間がかかるので、その間に Google 側の設定を始めます。
2. Google 側の設定 その1
まず前提として、 Google Admin に入れる権限が必要です。
Google Workspace の管理者権限がない場合には付与してもらってください。
まず、ウェブアプリとモバイルアプリ のページから
「アプリを追加」>「カスタムSAMLアプリの追加」をおこないます。
アプリの詳細をわかりやすいように設定した後、
次の画面で IdP メタデータをダウンロードできます。これはあとで使います。
なお、SAML アプリケーションに対するシングル サインオン(SSO)の設定 からもダウンロードできます。
続いて、サービスプロバイダの詳細を設定する画面に進みます。
「ACS の URL」および「エンティティ ID」の入力を求められますが、
これは AWS 側の OpenSearch Service のドメイン作成が終わっている必要があるので、それを待ちます。
3. AWS OpenSearch Service の設定更新
AWS 側の OpenSearch Service のドメイン作成が終わっていたら、
ドメインの「セキュリティ設定の編集」に進みます。
再び「SAML 認証を有効化」のチェックをONにして、設定していきます。
ここの「サービスプロバイダーエンティティ ID」と「SP によって開始された SSO URL」は、このあとの 4. の工程で使います。
さて、「XMLファイルからインポート」のボタンから
2. の工程のときにダウンロードした IdP メタデータをアップロードします。
その後、その下の「SAML マスターユーザーネーム」の箇所に、
管理者として使いたい Google Workspace のメールアドレスを指定してあげてください。
「アクセスポリシー」については、最初の作成時に誰もアクセスできないような設定で作っていたので、
誰でもアクセスできるように今度は変更します。
そうしたら「変更の保存」を押して完了です。
4. Google 側の設定 その2
「ACS の URL」には、「SP によって開始された SSO URL」を設定します。
これが認証後のリダイレクト先です。
(例: https://search-opensearch-test-hogehoge.ap-northeast-1.es.amazonaws.com/dashboards/opendistro/_security/saml/acs )
「エンティティID」には「サービスプロバイダーエンティティ ID」を設定します。
(例: https://search-opensearch-test-hogehoge.ap-northeast-1.es.amazonaws.com/dashboards/opendistro/_security/saml/acs )
結果として、以下のようになります。
属性のマッピングは特に設定せず完了させます。
完了すると、以下のような画面になります。
この状態のままだと、まだユーザーアクセスが「オフ」になっていて誰もログインできないので、
「ユーザーアクセス」の箇所をクリックして、
このアカウントのすべてのユーザー、もしくはユーザーグループ単位でアクセスを許可します。(「オン」にします)
5. ダッシュボードへのアクセス
ダッシュボード(OpenSearch Dashboards の URL)にアクセスしてみましょう。 (例: https://search-opensearch-test-hogehoge.ap-northeast-1.es.amazonaws.com/_dashboards )
どの Google アカウントでログインするか選択する画面になるので、
3. の「SAML マスターユーザーネーム」で設定したメールアドレスを選択して、次の画面に進みます。
認証が許可されていない場合は以下のような 403 ページが表示されます。
もし、2. の「ユーザーアクセス」の設定を変更しているはずなのに表示される場合、
もう少し待ってから再度アクセスしてみてください。(Google の SAML 設定が反映されるまでは数分かかるため。最長24時間かかると書かれていますが、さすがにそんなにはかからないはず)
それでもダメなら、4. の「ユーザーアクセス」の設定や「エンティティID」の設定が失敗していないか再度確認してみてください。
無事成功すると、Dashboard のトップ画面に進みます! おめでとうございます!
6. ユーザーの招待
4. でユーザーアクセスを許可した人たちが Google ログインできるようになりましたが、
OpenSearch Dashboards のアカウントはまだ作られていないので、
3. で設定した「SAML マスターユーザーネーム」のメールアドレス以外でログインすると
Missing Role のエラーが表示されます。
「SAML マスターユーザーネーム」のメールアドレスでログインした
管理者が、他のユーザーの登録をおこなっておく必要があります。
Amazon OpenSearch Service のきめ細かなアクセスコントロール - Amazon OpenSearch Service
のページを参照してください。
メニューから Security > Roles に進んで、その画面から例えば「all_access」のロールに対してユーザーをアサインします。
ロールを選んで「Manage mapping」ボタンを押します。
「Map user」の画面に進んだら、
権限を与えたいユーザーのメールアドレスを入力し、「Map」ボタンを押して保存します。
なお、 Missing Role の画面からは Cookie などをいじらない限りログアウトできないという罠があるので、
もし他の Google アカウントでのログインを自身が試す場合は、
ログアウトせずに別の Web ブラウザから、他のアカウントを使ったログインを試すと良いです。
7. おわりに
公式ドキュメントには Okta を用いた SAML認証しか解説しておらず、
情報がなくてとってもとっても苦労したので、この記事を作成しました。
手順としてはそこまで複雑でないことが伝わったかと思います。
お役に立てましたら幸いです。