MySQL v8.0 ClientからProxySQLを使用する際の注意

公式でも言及されていることですが、ProxySQLはそのコントロール配下にMySQL v8.0を置くことは可能なものの、MySQL 8.0.4からのデフォルト認証方式であるcaching_sha2_passwordには対応しておりません。

MySQL 8.0 · sysown/proxysql Wiki · GitHub

そのため、何も気にせずMySQL v8.0のクライアントから接続すると、以下のように認証が正しく行われません。

mysql --host 127.0.0.1 --port 6033 --user admin_user -p
Enter password: <正しいパスワードを入力してEnter>
ERROR 1045 (28000): ProxySQL Error: Access denied for user 'admin_user'@'%' (using password: NO)

解消するにはまずサーバ側に既存の認証方式であるmysql_native_passwordを各所設定します。

mysql> select user, plugin from user where user = 'admin_user';
+-------------+-----------------------+
| user        | plugin                |
+-------------+-----------------------+
| admin_user | mysql_native_password |
+-------------+-----------------------+
1 row in set (0.21 sec)

mysql> show variables like 'default_auth%';
+-------------------------------+-----------------------+
| Variable_name                 | Value                 |
+-------------------------------+-----------------------+
| default_authentication_plugin | mysql_native_password |
+-------------------------------+-----------------------+
1 row in set (0.18 sec)

そして接続の際にオプション"--default-auth=mysql_native_password"を付与することで、正しく接続できるようになります。

mysql --host 127.0.0.1 --port 6033 --user admin_user -p --default-auth=mysql_native_password

“Chef Infra Client cannot execute without accepting the license”が発生した時の対処

Vagrantでchef-soloをprovisionerとしたupコマンド実行時に以下のようなエラーが発生するようになった。

==> host01: Chef Infra Client cannot execute without accepting the license
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.

chefのライセンス形態が変更になり、最新Chef v15から初回にEULA(End-User License Agreement)の承諾が必要になったもよう。

As of May 15, 2019, we have released Chef Infra Client version 15, which requires accepting the new EULA.

https://docs.chef.io/chef_license_accept.html

chefのコマンドから実行した場合は、以下のような対話方式で承諾する。

+---------------------------------------------+
            Chef License Acceptance

Before you can continue, 2 product licenses
must be accepted. View the license at
https://www.chef.io/end-user-license-agreement/

Licenses that need accepting:
  * Chef Infra Client
  * Chef InSpec

Do you accept the 2 product licenses (yes/no)?

>

オプションを引き渡すことでも承諾可能なようだがうまく動作せず。関連ツールを含め全てがまだ対応できていないからv14に固定してともあるので、

However, we are still in the process of releasing the rest of our product portfolio. As of today, the recommendation is to pin Chef Infra Client to version 14 until we add support to the rest of our tooling.

https://docs.chef.io/chef_license_accept.html

Vagrantfileに以下のように、chefのバージョンをv14の最新に固定するように変更。

Vagrant.configure("2") do |config|
  # 省略
    cfg.vm.provision "chef_solo" do |chef|
      chef.version = "14.12.9"
      # 省略
    end
  # 省略
end

動いた!

UI/UXの重要性をソフトウェアの本質から読み解く

UI/UXの重要性が叫ばれて久しい今日。日本国内においてもようやくその重要性が認識され始めたと言えるでしょう。ではなぜUI/UXがソフトウェアにおいて重要なのでしょうか。この問いをソフトウェアの本質から読み解いていきたいと思います。

情報を出し入れするだけの単なる箱としてのソフトウェア

メッセンジャー、ネットショッピング、SNS、ゲームなどのアプリを含む全てのソフトウェアは、情報を出し入れする単なる箱と言えるでしょう。そんなはずはない、それぞれ異なる要望を満たす全く異なるソフトウェアとお思いになるかもしれませんが、メッセンジャーアプリは会話というデータを入力し、あたかも会話のような吹き出しとして出力しているに過ぎません。ネットショッピングも商品のデータと取引のデータが、一定の調和をもって出たり入ったりしているに過ぎず、それが実際の配送といった別のプロセスと連携することで商品の購入という体験が実現します。
ここにひとつの段ボールがあるとしましょう。このままでは単なる箱です。例えば側面に”おもちゃばこ”と書いてあれば、子供でも認識が可能な玩具を片付けるケースになるでしょう。自販機の横に置けばゴミ箱である可能性が高いですし、さらに入口がペットボトルの形をしていればなお用途を限定できるはずです。こういった一連の表層や過程への施しこそがUI/UXであり、単なる箱としてのソフトウェアを意味付ける重要な要素なのです。

おわり

容量や材質といった機能がソフトウェアの個性を形付ける側面ももちろんありますが、ソフトウェアをUI/UXと切り離して考えることはできません。ソフトウェアに関わる全ての人がUI/UXを重要視し、そして今まで以上に洗練されていくことを願います。

RSpecでブラウザのダイアログを操作する方法

ブラウザのダイアログ

Railsエンジニアにはおなじみではありますが(*)、リスクのある操作を行う際に、以下のようなブラウザのダイアログを表示することで確認を促す処理はケースとして良くあることと思います。(画像はGoogle Chromeの例。)
f:id:kazukiyunoue:20180613114850p:plain
今回はそのような処理をRailsでスタンダードなテストフレームワークであるRSpecで実行させたいと思います。(*:Railsの持つ自動コード生成機能が生成するリソース削除の処理が、まさにブラウザのダイアログを表示し、"Are you sure?"と確認を促す流れとなっている。今回の題材もそれ。)

テスト時の環境

ライブラリ バージョン
Ruby 2.5.1
Rails 5.2.0
RSpec 3.6.0
Capybara 3.2.1
Javascript Driver selenium_chrome

実際のコード

require 'rails_helper'

RSpec.feature "Tasks", type: :feature do
  scenario "User can delete a task", js: true do
    create(:task)

    visit tasks_path

    click_link "Destroy", match: :first
    expect {
      page.accept_confirm "Are you sure?"
      expect(page).to have_content "Task was successfully destroyed."
    }.to change { Task.count }.by(-1)
  end
end

pageインスタンスにaccept_confirmとdismiss_confirmというブラウザのダイアログ操作用メソッドが用意されていて、引数にダイアログで表示されるテキスト(今回のケースでは"Are you sure?")を入れることで、該当のダイアログをacceptまたはcancelできるようです。ちなみに"OK"ボタンしか表示されないようなダイアログ用と思われるaccept_alertもあり、今回のケースだとacceptさせたい場合はaccept_alertでも実行できました。RSpecではおなじみのテクニックではありますが、expectのブロック内にひとつ以上のexpectもしくはfindを入れないと、ダイアログが表示されてacceptされる前に次へ進んでしまうので注意が必要です。

macOS純正メモアプリ”メモ”(英名:Notes)で特定のメモを別ウインドウで開く方法

あまり注目してる人は少ないかもしれないmacOS純正メモアプリ”メモ”(英名:Notes)。軽快な動作でありながら、手書きや画像の添付、リッチテキスト対応など機能性も高いメモアプリでもあります。

特定のメモを別ウインドウで開きたい場合は、

f:id:kazukiyunoue:20170529144750p:plain

画像のように、赤枠で囲われた部分をダブルタップ(ダブルクリック)することで、

f:id:kazukiyunoue:20170529144838p:plain

このような別ウインドウでメモを開くことができます。メモを比較したり、参照したりするときに便利ですね!

MySQLv4.0(!)で、DISK容量の空きは十分なのにThe table is fullエラーが出た時の話

あるMyISAMテーブル(ここではtest1とします)を、スキーマ変更のため一時テーブル(test2)へコピーしようとした時、

mysql> create table test2 select * from test1;
ERROR 1114 (HY000): The table 'test2' is full

のようなエラーが発生した。

test1はそこそこ大きなテーブルなので、MySQLがDATA領域(tmp領域を含む)として使うDISKの空きを確認するが、十分なスペースがあった。

調べてみると、
どうやらcreate table A select * from B構文だとMAX_ROWSの値は受け継がれず、
デフォルトの領域しか割与えられない仕組みのよう。

試しに1件だけ挿入すると

mysql> show create table test1\G
*************************** 1. row ***************************
       Table: test1
Create Table: CREATE TABLE `test1` (
  `id` int(11) default NULL
) TYPE=MyISAM MAX_ROWS=100000000
1 row in set (0.00 sec)

mysql> create table test2 select * from test1 limit 1;
Query OK, 1 rows affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> show create table test2\G
*************************** 1. row ***************************
       Table: test2
Create Table: CREATE TABLE `test2` (
  `id` int(11) default NULL
) TYPE=MyISAM
1 row in set (0.00 sec)

test2からはMAX_ROWS=100000000の設定が消えてしまっている。

そこでcreate table A select * from B構文は使わず、

CREATE TABLE `test2` (
  `id` int(11) default NULL
) TYPE=MyISAM MAX_ROWS=100000000;

でテーブルを先に作成した上で、

INSERT INTO test2 SELECT * FROM test1;

とすると、無事コピーできた。

v4.0!

ActiveResourceでauth_token的なトークンで認証する方法

Rails4からは正式にプリインストール対象から外れたActiveResourceではありますが、アクセス先が絵に描いたようなRESTfulAPI、とりわけ開発者が内情を知ってるRailsアプリ間の連携等では今なお大いに力を発揮するもの。

しかし、

http://example.com/posts.json?auth_token=TOOOOKEN

このような形でのトークンの認証には対応していない。
そこで、

blog.mmmcorp.co.jp

を大変参考にさせていただき、モンキーパッチをあて、トークン認証に対応させる。

ActiveResourceが発行するURLのクエリーストリングを生成するメソッド、

def query_string(options)
        
  "?#{options.to_query}" unless options.nil? || options.empty?

end

というのがあるので、これをオーバーライドする。

全体ではこうなる。

class ActiveResource::Base
  class << selfdef query_string(options)

      if auth_token

        options.store(:auth_token, auth_token)

        "?#{options.to_query}" unless options.nil? || options.empty?

      elseraise ArgumentErrorendend
    def auth_tokenreturn @auth_token if defined?(@auth_token)

      nilend
    def auth_token=(auth_token)

      @auth_token = auth_token

    endend

end

下2つが単純な属性"auth_token"に対するゲッター、セッターメソッド。そしてquery_stringメソッドを、発行するURLのクエリーストリングになるハッシュ"options"にauth_tokenの要素を追加し、ない場合はArgumentErrorの例外を発生させるようにした。

このパッチを、Railsではlib配下等、読み込み対象ディレクトリに設置し、

class Post < ActiveResource::Base
  self.site = "http://example.com"
  self.auth_token = "TOOOOOOOOKEN"
  self.format = :xml
  self.timeout = 15
end

Post.all

このようにモデル側を実装すれば、トークンでの認証に対応が可能。

(当パッチはActiveResourceすべての処理において検証はしておりません。
ご利用の際はご注意ください。)