今年送ったOSSへのPRまとめ

SREチームの鈴木です。

Kayac Advent Calendar 13日目を担当させていただきます。

OSSへの貢献について

弊社では業務時間内にOSSへの貢献について特に制限されていませんので、使っている・導入を検討中のソフトウェアにバグや改善の余地を発見した場合は、業務としてPRを送ったり、Issue を立てて author に報告しています。

この取り組みの成果として、今年送ったOSSへのへのPRのうち無事 merge されたものをいくつか紹介させていただきます。

目当てのPRの探し方

まずこの記事を書くためにはどんなPRを送ってきたかを振り返らなければいけません。

「OOS へ送ったPR」を厳密に絞り込む必要はないですが、業務でも github を利用しているため、それらのPRの中に埋もれているOSSへのPRをいい感じにみつけたいです。そこで github の pulls で以下のような検索クエリを使いました。

is:pr is:closed is:public merged:>2018-01-01 author:dozen -user:dozen -user:kayac

これでだいたいOSSっぽい repo へのPRの一覧が見られるようになりました。 merged: はマージされた時期のフィルターですが、初めて知りました。

f:id:jetzou:20181212105316p:plain
検索クエリを適用した pulls page

PRの紹介

リポジトリごとに簡単に説明し、送ったPRについて紹介させていただきます。

coinbase/terraform-landscape

landscape は terraform plan の結果を受け取り、人間が見やすいように整形してくれるCLIツールです。

github.com

terraform plan の出力の一つに "old value" => "new value" という、リソースの設定値の差分を示すものがありますが、 landscape の従来の実装ではこの部分を eval() を用いて hash のように扱うことでパースしていました。 この方法ではパースに失敗するケースがあり、例えば aws_lb_listener#{host} といった値を使っていると式展開として扱われてしまいコケてしまいます (issue#62)。

ALB のリソースをいじるときに landscape がコケて不便というのもありますが、任意コード実行が可能(通常 tf ファイルやAWS のリソースは自分たちが管理しているので実害は出づらいとは思いますが)な状態だったので修正することにしました。

terraform-providers/terraform-provider-mysql

terraform-provider-mysql は MySQL のDBやユーザ、権限を terraform で管理できるようにするプラグインです。

結局導入しなかったプラグインですが、以前「特定のテーブルの特定カラムだけ参照可能なユーザがほしい」という要望があった際に、そのユーザとGRANTをどう管理しようか…と思っていて見つけました。 導入を検証していた際にカラムまで絞り込むことができなかったので修正のPRを出すことにしました。

github.com

このPRは mysql_grant resource で権限をテーブルごとに与えられるようにするものです。 mysql_grant はデータベース単位で権限を付与するリソースでしたが、この修正でテーブルも指定できるようになりました。 privileges ではカラムを指定することもできますのでテーブルの指定とあわせてより細かく権限を付与できます。

resource "mysql_grant" "jdoe" {
  user       = "${mysql_user.jdoe.user}"
  host       = "${mysql_user.jdoe.host}"
  database   = "app"
  table      = "user"
  privileges = ["SELECT (id, name)"]
}

mackerelio/mackerel-agent-plugins / mackerelio/go-check-plugins

弊社では Zabbix から mackerel にサーバ監視を移行しています。 mackerel による監視を追加していく中で、 mackerel のプラグインについてもいくつかPRを送りました。

github.com

このPRは mackerel-agent-plugin-redis が送るメトリクスに Redis の uptime を含めるようにするというもので、 Redisノードの再起動を検知するために Uptime が必要でした。

github.com

この PR では expired_keys を前回からの増分を計算して送るように変更しました。

expired_keys は積算の値ですが、知りたいのは期間中の増分であるためです。(長期間運用していると平らなグラフになってなにがなんだかわからない)

github.com

こちらは check-tcp というcheck監視プラグインに対するPRです。

check監視はコマンドを実行した時の終了コードで監視のステータスを更新するもので、終了コードと監視のステータスががそれぞれ 0=OK, 1=WARN, 2=CRITICAL, 3~255=UNKNOWN に対応付けられています。

check-tcp プラグインはTCPサーバの死活監視をするプラグインですが、このプラグインの異常時の終了コードが 2 で固定になっていて WARN 程度におさえたいような監視項目についても CRITICAL が発火してしまうため、使いづらいケースが有りました。 そのため -W --error-warning という引数を追加して、この引数を指定した場合は異常時の終了コードが 1 (WARN) になるようにしました。

shogo82148/go-nginx-oauth2-adapter

go-nginx-oauth2-adapter は nginx-omniauth-adapter の golang port です。弊社ではいくつかのプロジェクトでアクセス制限をかけるのに使っています。

go-nginx-oauth2-adapter はいくつかの設定項目を設定ファイルからではなく環境変数から読み込むことができ、これはコンテナ化するときに環境変数からいろいろな設定値を与えられるため、そのような環境においてありがたい作りです。

そのうち、cookie store として使っている gorilla/sessions の認証鍵も環境変数 NGX_OMNIAUTH_SESSION_SECRET でセットできます。

github.com

このPRでは NGX_OMNIAUTH_SESSION_SECRET で鍵を複数指定できるようにしたものです。

cookie store を用いる場合の gorilla/sessions は以下のようにして初期化されます。

var store = sessions.NewCookieStore(
    []byte("new-authentication-key"),
    []byte("new-encryption-key"),
    []byte("old-authentication-key"),
    []byte("old-encryption-key"),
)

補足: new-authentication-key はペイロードのメッセージ認証に使う鍵、 new-encryption-key はペイロードの暗号化・復号につかう鍵です

PRが merge される前は指定できる鍵は1つだったため、認証用の鍵しかセットされず、cookie の内容が暗号化されません。また鍵のローテーションもできません。

鍵のローテーションは重要ではない(ローテーションせずに入れ替えればいい)のですが、 cookie の内容は暗号化したいですね。そこで環境変数 NGX_OMNIAUTH_SESSION_SECRET に鍵をカンマ区切りで入れていくことで、鍵を複数指定できるようにしました。

ryotarai/waker

waker はアラートエスカレーションシステムで、弊社では waker を使ってmackerelのアラートをエスカレーションしています。 waker についての詳細はクックパッドさんの開発者ブログにエントリーがありますので、そちらがわかりやすいと思います。

techlife.cookpad.com

主に mackerel 対応のPRを送っています。最近では、 mackerel に追加されたアラートグループへの対応を行いました。

github.com

fujiwara/stretcher

stretcher は S3 と Consul (もしくはSerf) を利用して動くデプロイツールです。 Lobi でも用いられているほか、社内のプロジェクトで広く使われているツールです。

stretcher を用いたデプロイ方法の詳細に関しましては以下のスライドが参考になるかと思います。

Consulと自作OSSを活用した100台規模のWebサービス運用 - Speaker Deck

stretcher のデプロイ結果のログを一覧で見るためのツールとして consul-kv-dashboard があります。 stretcher のログは Consul KV に保存され、 consul-kv-dashboard でまとめて見ることが可能です。

さて、Consul KV は1レコードに入れられる値の上限が512KBという制限がありますが、 stretcher が内部で実行する rsync コマンドの饒舌さがデフォルトでは -v で固定となっています。起動時に実行されるデプロイなど、差分が大きくなるような時には stretcher の出力するログが 512KB を超えてしまうことがあり、デプロイの結果を consul-kv-dashboard で見ることができなくなってしまうことがありました。

github.com

そこで、 場合によって stretcher の rsync の饒舌さを調節できるようなオプションを追加しました。

従来は固定で -v でしたが -rsync-verbose "" とすることで出力を抑制したり、逆に更に詳細なログを出したい場合には -rsync-verbose "-vvv" とすることもできます。

おわりに

今回のネタは谷脇さんに振っていただきました。そんなにPR作ってないと思っていたのですが、振り返ってみると書ききれないくらいありました。この度はアドベントカレンダー参加への機会を作っていただきありがとうございました。

明日のアドベントカレンダーは山田さんによる「突撃!隣のキーボード2018」です。

【Unity】アニメーションクリップでuGUIのマスクの形を変更させる

はじめに

はじめまして、ソーシャルゲーム事業部のUnityエンジニアのクアンです。
この記事はカヤックUnityアドベントカレンダー2018の13日目の記事になります。

6日目の記事ではメッシュで枠を描画していましたが、この記事ではカットイン演出に使うマスクのメッシュ形状をアニメーションさせてみようと思います。

DynamicMaskとは

ランタイムで頂点からマスク形状を描画するためのコンポネートです。
この記事では四角形のマスクしか対応していません。

DynamicMaskを試しましょう。
下のような入れ子構造で、シーンに配置します。

f:id:quan-nguyen:20181206121217p:plain

さらに、「mask」にuGUIのマスクコンポネートと「DynamicMask」コンポネートを追加。

f:id:quan-nguyen:20181206121350p:plain

上のスクリーンショットのVert1(左下), Vert2(左上), Vert3(右上), Vert4(右下)はそれぞれ4つの頂点です。

頂点の座標を入力してコンテキストメニューで「SetVerticesDirty」を選択したら画像は描画されます。

f:id:quan-nguyen:20181206121431p:plain

実装

基本的にはUnityEngine.UI.Graphicクラスを継承してOnPopulateMesh関数をオーバーライドするところから始まります。
ではOnPopulateMesh関数の引数VertexHelperインスタンスに対して処理を書いていきましょう。

public class DynamicMask : Graphic
{
    // 各頂点の情報
    public Vector2 vert1;
    public Vector2 vert2;
    public Vector2 vert3;
    public Vector2 vert4;

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        base.OnPopulateMesh(toFill);

        toFill.Clear();

        // 頂点情報を渡す
        toFill.AddVert(vert1, color, new Vector2(0f, 0f));
        toFill.AddVert(vert2, color, new Vector2(0f, 1f));
        toFill.AddVert(vert3, color, new Vector2(1f, 1f));
        toFill.AddVert(vert4, color, new Vector2(1f, 0f));

        // 頂点の順番
        toFill.AddTriangle(0, 1, 2);
        toFill.AddTriangle(2, 3, 0);
    }

#if UNITY_EDITOR
    [ContextMenu("SetVerticesDirty")]
    private void MenuSetVerticesDirty()
    {
        SetVerticesDirty();
    }
#endif
}

UIコンポーネントを拡張したから、Inspectorで頂点のプロパティーを表示するためにEditorクラスを追加します

[CustomEditor(typeof(DynamicMask))]
public class DynamicMaskEditor : GraphicEditor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        var mask = (DynamicMask)target;
        mask.vert1 = EditorGUILayout.Vector2Field("Vert 1", mask.vert1);
        mask.vert2 = EditorGUILayout.Vector2Field("Vert 2", mask.vert2);
        mask.vert3 = EditorGUILayout.Vector2Field("Vert 3", mask.vert3);
        mask.vert4 = EditorGUILayout.Vector2Field("Vert 4", mask.vert4);
    }
}

アニメーションによって頂点の情報が変更されたときに画像を更新するため、OnDidApplyAnimationPropertiesをオーバーライドします。

// 頂点の情報がアニメーションによる変更が行われたときのコールバック
protected override void OnDidApplyAnimationProperties()
{
    base.OnDidApplyAnimationProperties();

    SetVerticesDirty();
}

次にマスクをアニメーションさせてみましょう。

画像とマスクを配置する

下のような入れ子構造で、シーンに画像とマスクを配置します。

f:id:quan-nguyen:20181206122222p:plain

f:id:quan-nguyen:20181206121531p:plain

上記のような「mask」にuGUIのマスクコンポネートと「DynamicMask」コンポネートを追加。

こうすることで、「mask」の子要素の表示が、「mask」のImage範囲のみに限定されます。

マスクをアニメーションで動かす

「DynamicMask」の頂点にキーフレームを追加。

f:id:quan-nguyen:20181206121552p:plain

実行すると、こんなかんじでマスクがアニメーションします。

f:id:quan-nguyen:20181206121613g:plain

おわりに

ゲームで必殺技とかのカットインによくマスクアニメーションを使用するのでご紹介しました。
読者の方のお役に立てば大変うれしいです。

明日は中島さんによる Androidで情報をローカル保存するときの話 になります。 よろしければお読みになってください。