わたねこコーリング

野良プログラマ発、日々のアウトプット

休日専科 〜 とろけるスライドギター

いまさらですが Spotify、イイですね。20世紀だった頃に好きな音楽のレビューサイトを作っていて、音楽を言葉で伝えることの難しさに七転八倒しながら「ページに音源を貼り付けてリアルタイムで聴きながら記事を読んでもらえたらなー」とため息をついてたものですが、いつの間にかそれが本当にできるようになってしまいました。

Spotify は日本ローンチの際にアカウントを作って、ブログ等にプレイヤーを埋め込めるのも知ってはいたのですが、良い切り口が見つからない等と言い訳しながら時間ばかりが経ってしまった今日この頃。久々に時間が空いた休日なので、えいやっと昔 iTunes Store 用に作ったプレイリストを再現してみました。題して「休日専科 〜 とろけるスライドギター」w

休日の午後に合いそうな、ゆったりしたテンポのアコースティック・スライドギターのインストゥルメンタル曲ばかり集めた1時間弱のプレイリストです。テーマ的にハワイアン率高めかも。しかし、どの曲も比較的マイナーで古めの音源なのですが、ほぼ全曲を揃えることができたのは流石 Spotify ですな。

5万曲入りの iTunes ライブラリを外付けドライブに引っ越そうとして難儀した件

自宅 MacBook Pro (15″Mid 2015 / Mojave)の内蔵ドライブ(SSD)が手狭になってきて仕事に差し支えるようになったので、250GB と半分程容量を消費している iTunes フォルダを外付けドライブに引っ越すことにしました。以前もやったことがある作業なので高をくくってたら予想以上に難儀してしまったので、備忘録として書き残します。

最初に試した方法は、ネットで調べた「iTunes フォルダを移動して、Option キーを押しながら iTunes を起動して、移動先フォルダを選択する」という方法でした。ネット記事によると、起動後の iTunes で「曲の情報」から「ファイル」タブで確認すると、格納場所が移動先に変わっているということだったのですが、自分の場合は以前の場所のままでした。原因は分からずじまい。

前述の「以前にやった」方法は、移動した先の iTunes フォルダにある「iTunes Library.xml」の音源ファイルパスを適宜書き換えるというものだったので、これも上記の失敗の後で試してみたのですが、iTunes を起動すると書き換える前の内容に戻ってしまうという現象で、失敗に終わりました。以前は iTunes のライブラリデータベース・ファイルが「iTunes Library.xml」だったのが、現在では「iTunes Library.itl」に変わっており、XML ファイルはそのシャドウになってしまったというのが原因のようです。

その後、あれこれ試行錯誤して何とか移動に成功したので、下記に手順を記します。その前に注記しておくと、当方環境ではリッピングや購入した音源を iTunes/アーティスト名/アルバム名/ に格納する昔ながらの設定になっており、「iTunes Media」フォルダに統合することはしていません。この辺が違うと、下記方法ではダメだと思います。

  1. 移動前の状態の iTunes にて、「環境設定」を開き、設定内容を控えておく。
  2. iTunes フォルダを丸ごと、移動先にコピーする。
  3. コピーした iTunes フォルダ内にある設定ファイルの類(自分の環境では iTunes Library.itl, iTunes Library.xml, iTunes Library.itl, iTunes Library Extras.itdb, iTunes Library Genius.itdb)を削除する。
  4. Option キーを押しながら iTunes を起動して、ダイアログの「ライブラリを選択」ボタンをクリックし、移動先フォルダを選択する。起動後の iTunes は左ペインライブラリの「曲」で何も表示されないまっさらな状態になっていれば OK。
  5. 「環境設定」で、控えておいた設定内容に設定する。その際、「詳細」タブの設定で「ライブラリの追加時にファイルを"iTunes Media"フォルダにコピー」のチェックは外しておくこと。
  6. コピー元の iTunes フォルダ内にある「iTunes Library.xml」のファイルパスを移動先に合わせて書き換えたものを複製する。ファイルパスは、<key>Location</key> で始まる行の <string> タグの中に URI 書式で記述されています。例えば、/Users/mariyudu/Music/iTunes から /Volumes/Music-Drive/iTunes に移動するように書き換えるなら、「/Users/mariyudu/Music/」という文字列を「/Volumes/Music-Drive/」に置換すればイイですね。テキストエディタでやってもいいですが、当方ではデータ量が多いので ターミナルから sed で一発書き換えしました。
  7. iTunes の「ファイル」メニューから「ライブラリ」>「プレイリストを読み込む」を選択して、上で作成したファイルを読み込ませます。当方の5万曲弱の場合では、30分程かかりました。
  8. これで移動場所にコピーした音源ファイルが取り込まれ、これまでに作成したプレイリストや再生回数等の情報も全て、新しい環境に反映されました。ポッドキャストの購読情報等はきれいさっぱり無くなっていましたが、それはまぁ良しとします。

以上です。XML データ書き換えが必要なので、その辺を理解できて個別的なトラブルに対処できる方以外にはお勧めできませんが、ひとつの情報として。あと、この種の作業の前にはとにかくバックアップを忘れずに! 当方も直前にタイムマシンバックアップで丸ごと状態保全したのは言うまでもありません。

DNS ゾーンレコード変更を監視して音声でお知らせ

最近ブログ書いてないので小ネタも山の賑わい的な。

当方では自分のドメインバリュードメインさんで管理しているのですが、コンパネからゾーン情報を編集しても DNS サーバに反映されるまで5〜30分程(公称)かかるんですよね。「変わったかな?」と数分毎に dig を叩くのも面倒なので、10秒おきにチェックして音声で知らせるワンライナーを書きました(Mac 限定)。

以下は、TXT レコードの一部を hogehoge を含む内容に書き換えた際、プライマリネームサーバをチェックして変更が反映されたらお知らせする例です。ご利用にあたっては <> 内を書き換えて下さい。

while :; do date ; dig @<プライマリネームサーバ> -t TXT <当該レコードのドメイン> | grep hogehoge && say "やったー、変わりましたよ!" ; sleep 10 ; done

これでお知らせが来たら、あとは設定してある TTL だけ待てば第三者からの名前解決にも「浸透」を期待できるという訳ですな。

※音声出力がマクの say コマンドなので、他プラットフォームの場合はその辺を適宜アレンジして下さい。

手元の phpMyAdmin からリモートホストのデータベースを SSH トンネル経由で操作する

ウェブ開発では、かつては MySQL サーバを立てたら次は同サーバ内か同一 LAN 内に phpMyAdmin のセットアップをというのが定石でしたが、コンテナ時代の今では phpMyAdmin もデスクトップアプリと同様の手軽さで利用できるようになりますた。という訳で、手元の開発用マシンで phpMyAdmin を Docker コンテナで起動して、リモートホストMySQL サーバに SSH ポートフォワード経由で繋いでしまおうという小ネタです。

まず、DB を立ててあるリモートホスト foo@bar.net との間にトンネルを掘ります。トンネルの入り口は localhost じゃなくて Docker コンテナ内から接続できるようにホストマシンのローカル IP (例では 192.168.12.34)にしておくのがミソ。

ssh foo@bar.net -N -L 192.168.12.34:3306:localhost:3306

phpMyAdmin純正イメージを使って、コンテナを起動します。環境変数 PMA_HOST で DB サーバアドレスを指定できます。

docker run --rm --name pma -d -e PMA_HOST=192.168.12.34 -p 8080:80 phpmyadmin/phpmyadmin

あとは http://localhost:8080/ から phpMyAdmin 使い放題、と。

受注開発で DB は用意されていても必ずしも phpMyAdmin が使えるとは限らない等、割とよくあるユースケースじゃないかと思うのですが、不思議にネットでソリューションが見つからなかったので書いてみました。

既存 RDB 用の GraphQL API サーバを Apollo Server + TypeORM で書いてみた

コロナ巣篭もりを良い機会にという訳でもないですが、ここひと月程 GraphQL について学習してました。が、書籍でもネット記事でもデータベースを含めた新規開発なストーリー(データストレージはたいてい NoSQL タイプ)が殆どで、「既存の RDB データベースの為に API サーバを立てる」という(少なくとも自分には)とても良くありそうなユースケースが無かったので、簡単な CRUD が出来るプログラムを書いてみました。これです↓。

github.com

既存 RDB前回記事で紹介した world database を採用。動作までの手順は以下の通りです。

  1. world database をセットアップ(前回記事紹介の Docker イメージを使えば簡単)。
  2. cp ormconfig.json.sample ormconfig.json して、上記 DB の諸元を記入。
  3. npm install
  4. npm start

http://localhost:4000/graphql から Apollo Server ビルトインの PlayGround で API を試せます。こんな感じ↓です。

f:id:mariyudu:20200505175459p:plain

プログラミングの参考にしたのは下記の書籍とネット記事です。

初めてのGraphQL ―Webサービスを作って学ぶ新世代API

初めてのGraphQL ―Webサービスを作って学ぶ新世代API

medium.com

既存 DB 用に TypeORM エンティティをガリガリ書くのはしんどいなぁと思ってたら typeorm-model-generator という便利なものがあったので、これで生成後に調整する、という手順がラクラクでした。

TypeORM は触り始めたばかりで未だよく分かっておらず、特に更新時のリレーションについては GraphQL なスキーマの枠組みの中でどのように実装したら良いのか未だ答えが出ていないので、現状では手つかずです。誰か良いやりかたをご存知でしたら教えて下さい。

データ入りの演習用 MySQL DB を1分で用意する

プログラマやってると、ミドルウェアフレームワーク等を検証したり、チュートリアルを試してみるということは日常的にあるあるな訳ですが、そんな時にゃテンポラリな演習用 DB がちょくちょく必要になりますよね。今回はそんな煩わしい作業を超スピードで片付けよう、という話です(MySQL 限定ですが)。

データの入ったボイラープレート的な DB って誰か作って無いかなーと探してたら、MySQL サイトにちゃんと情報が↓。

f:id:mariyudu:20200427185453p:plain

5種類の DB が紹介されているのですが、今回は「world database」と「employee data」を利用させてもらいます。データが提供されているとは言え、これを手作業でちまちま流し込んでたんじゃ全然時短にならないので、構築済 DB の Docker コンテナでも作るかぁ、と思ったら… もうありましたw やっぱり皆考えることは同じ。では、DB 毎に利用できるようなるまでの手順を。

World Database

世界の都市と利用言語を格納したシンプルな DB です。内容説明とコンテナ提供元はこちら↓。

dev.mysql.com

hub.docker.com

コンテナ生成はこんな感じです↓。

$ docker run -it --name mysql-example-world -e BIND-ADDRESS=0.0.0.0 -p 3306:3306 -e TZ=Asia/Tokyo -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d kakakakakku/mysql-world-database

ホスト側から DB 接続してみます↓。

$ mysql -h 127.0.0.1 -u root world
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.24    |
+-----------+
1 row in set (0.00 sec)

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0.00 sec)

イイ感じですね。city テーブルには4079件の、country テーブルには239件のレコードが格納されています。ちなみに、下記が ERAlchemy で生成したこの DB の ER 図です。
f:id:mariyudu:20200426165506p:plain

Employee Data

架空の従業員データベースです。テーブル数は8で、中心となる社員テーブル employees には300,024件のレコードと、かなり本格的な内容です。内容説明とコンテナ提供元はこちら↓。

dev.mysql.com

hub.docker.com

コンテナ生成します↓。

$ docker run -it --name mysql-example-employee -e BIND-ADDRESS=0.0.0.0 -p 3306:3306 -e TZ=Asia/Tokyo -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d harrymartland/employees-practice-mysql

ホスト側から DB 接続します↓。

$ mysql -h127.0.0.1 -utest_user -ptest_password employees
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.1-dmr |
+-----------+
1 row in set (0.00 sec)

mysql> SHOW TABLES;
+----------------------+
| Tables_in_employees  |
+----------------------+
| current_dept_emp     |
| departments          |
| dept_emp             |
| dept_emp_latest_date |
| dept_manager         |
| employees            |
| salaries             |
| titles               |
+----------------------+
8 rows in set (0.01 sec)

これだけの規模だとスケールを意識した作業が出来るのでイイですねー。

以上です。出来合いの接続アカウントを変えたい場合は適宜

GRANT ALL ON yourdb.* TO yourname@'172.17.0.1' IDENTIFIED BY 'yourpassword';

等とすれば良いかと。他3件のデータベースも同じ要領でコンテナ化出来ると思うので、どんどん活用していきましょー。

AWS DcoumentDB クラスタに SSH トンネル越しで Node.js プログラムから接続する

小ネタですが表題件、地味にハマったので備忘録として。

下記情報を元に、VPC 外(つまりおうち or 職場のパソコン)で開発中の Node.js プログラムから DocumentDB に接続しようとしたらタイムアウトでなかなか接続できませんですた。

docs.aws.amazon.com

docs.aws.amazon.com

あれこれトライ&エラーを繰り返して、ようやく動作するようになったのが下記コード。

var MongoClient = require('mongodb').MongoClient,
  f = require('util').format,
  fs = require('fs');

//Specify the Amazon DocumentDB cert
var ca = [fs.readFileSync("/somewhere/over/the/rainbow/rds-combined-ca-bundle.pem")];

//Create a MongoDB client, open a connection to Amazon DocumentDB as a replica set, 
//  and specify the read preference as secondary preferred
var client = MongoClient.connect(
'mongodb://<sample-user>:<password>@localhost:27017/?ssl=true', 
{
  useUnifiedTopology: true,
  sslValidate: false,
  sslCA:ca,
  useNewUrlParser: true
},
function(err, client) {
    if(err)
        throw err;
        
    //Specify the database to be used
    db = client.db('sample-database');
    
    //Specify the collection to be used
    col = db.collection('sample-collection');

    //Insert a single document
    col.insertOne({'hello':'Amazon DocumentDB'}, function(err, result){
      //Find the document that was previously written
      col.findOne({'hello':'Amazon DocumentDB'}, function(err, result){
        //Print the result to the screen
        console.log(result);
        
        //Close the connection
        client.close()
      });
   });
});

キモは、MongoClient.connect() に渡す URL とオプションでした(↓)。AWS サンプルコードとの差分はこちら http://mergely.com/TXE4OQbw/ を。

  • URL のパラメータは、DocumentDB コンソールで提供されるものに含まれている ssl_ca_certs やら replicaSet 等は一切取っ払って、ssl=true だけにする。
  • オプションの sslValidate は false に(mongo shell オプションの sslAllowInvalidHostnames に相当するっぽい)。
  • useUnifiedTopology も付与すると吉(将来の設計変更に関するワーニングを抑止できる)。

以上。尚、諸元は以下のとおりです。

  • DocumentDB エンジンバージョン docdb 3.6.0
  • Node.js v12.16.0
  • MonboDB Node.js driver v3.5.6