生涯未熟

生涯未熟

プログラミングをちょこちょこと。

ウルヴァリンがハッキングする映画はいいぞ

というわけで、こちらは プログラマーにおすすめしたい映画 Advent Calendar 2018 14日目の記事になります。

君は『ソードフィッシュ』を知っているか?

名前からして何の映画か分からない『ソードフィッシュ

まずはパッケージ画像を見てみましょう。

f:id:syossan:20181212011445j:plain

キャストが濃い!!!

特にジョン・トラボルタなんて濃さの塊ですよね。
パルプ・フィクションでもそうなんですが、トラボルタがいるだけで映画の雰囲気が3割増しで濃くなります。

さて、そんな中一番右にございまするお方は・・・

ウルヴァリンウルヴァリンじゃないか!

はい、というわけでヒュー・ジャックマンが出演しております。
いやー・・・かっこいい・・・雄って感じがして最高ですよね、ね?

ハル・ベリードン・チードルもMarvel映画好きからするとお馴染みの面々ですね。

肝心の映画自体の話に入るのですが、あらすじを簡単に説明すると

『凄腕ハッカーヒュー・ジャックマンジョン・トラボルタハル・ベリーが持ちかけた”ソードフィッシュ計画”なるハッキングでの裏金奪取計画に巻き込まれていく・・・』

という感じです。

大袈裟だけどそそられるプログラミングシーン

あらすじからも分かる通り、ヒュー・ジャックマンが凄腕ハッカーという設定なので何回かハッキングシーンが出てきます。
もう、それが「絶対にそんなん無理やろwww」ってツッコミたくなることうけあいなんですが、どこかそそられてしまう代物。

最初のハッキングは、ジョン・トラボルタがハッキングの腕を試すために並のハッカーが60分かかるセキュリティを60秒で突破しろというもの。
いやいやいやいや、無理無理無理無理!エンジニアの生産性が人によっては100倍差が出るとか言われるけど無理でしょ!

しかも、映画を観たらわかるんですが下半身をゴニョゴニョされながらハッキングさせられるんですよね・・・うらやましい

ただ、そこはウルヴァリン。鍛えた上腕二頭筋でバッチバチにキーボード叩きながらハッキングを成功させてしまいます。すげぇな、おい。

f:id:syossan:20181212013738p:plain
せやな

次のハッキングシーンは、ソードフィッシュ計画に使うワームを作るんですが、そのシーンがまた見物でめちゃくちゃ四苦八苦しながら謎の3DCGと向き合いながらプログラミングしてるんですよね。
荒唐無稽っちゃ荒唐無稽なんですが、ヒュー・ジャックマンが必死こいて頑張ってるのみてると、なんとも言えない「オッ、やっとるな」って気持ちになります。いいですね。


飲酒プログラミングでテンションの上がるヒュー・ジャックマン

正直なところ

ハッキングって要素は添え物で、本番は後半の圧巻な銃撃戦に詰まってます。
後半観てると「一体今までのハッキング云々は何だったんだろう・・・🤔」って感じになることでしょう。
あと無駄にハル・ベリーがエロい

まぁ、そんな感じで普通の映画として観るとまぁまぁな本作ですがエンジニアが観るとハッキングシーンにオッとなるのでオススメです。
Amazon Prime Videoにもあるので、気になった方は観てみましょう!💪

チャレンジングなことをしてみたらエッチなクソアプリが誕生した

どうも、この記事は クソアプリアドベントカレンダー の10日目のようです。

さて、今年ひり出したクソアプリは

エッチなやつです

f:id:syossan:20181210005011p:plain

エッチなリンク

業務で触った技術がなんか活かせないかな〜?と考えていたら、何故かエロクソアプリが誕生していました。不思議ですね。

どんなクソなの?

で、どんなアプリなのかというと「サクッと条件に合うAV女優探してサクッとxvideosかDMMに飛べるやつ」です。便利ですね。

これ以上、特に解説することがなくて困ってしまいましたので、技術的な解説に入りましょう。

技術の話

今回は、フロントをElmで、バックをGo + Lambda + S3 Selectで構築しました。

f:id:syossan:20181210011649p:plain

フロントの話

Elmを採用した理由としては、最近Elmを布教する教祖の人に影響を受けてTODOアプリ以外のもの作ってみてーなーということからやってみました。
実際ガッツリやってみると、コアなところはミニマルで言語仕様をサクッと把握出来るので学習コストがかなり低い言語だなーと改めて感じました。

ただ、パッケージによっては結構扱い方がピーキーなものしかなかったりして、まだそこらへんのエコシステム周りが潤ってない感はありました。
ハマりどころはあるものの良い言語です。皆さんもやりましょう。

あと、CSSフレームワークはBulmaを使いました。名前からして今回のアプリにぴったりですね。

S3のWeb Hostingを使ってるので、デプロイも npm run build して生成されたdist fileを放り込むだけで完了。平易。

さて、今回フロント側ではスケベデータ取得用Lambdaに問い合わせた結果を単純に表示しているだけの簡単な作りになっています。
あまり語ることが無いのでバックエンドの話に移りましょう。

フロントのソースコードはこちら

github.com

 バックエンドの話

バックエンドは手に馴染んだGoを使い、Lambdaに乗せてS3 Selectを用いてCSVデータを取得してくるように作りました。
S3 Selectが前から気になってて、やっとこさまともに使ってみたのですがドキュメントに書いてないハマりどころがあり、めちゃくちゃハマってしまいました・・・

その辺の詳しい話はGoアドベントカレンダーの記事で書こうかなと思うのですが、概要だけ書くと

  • メッセージタイプがRecordsの場合、65000バイトのchunk dataで送信される
  • CSV列の途中に欠損データがある場合、欠損データ行までしか比較演算子の対象とならない

という感じで、特に2つ目は1日溶かすレベルで悩みました・・・

そんなS3 Selectですが、1600件ほどのデータから簡単なクエリで引っ張ってくるだけなら300msくらいで返してくれるので、簡単なデータを扱うならこれでもいいのかなーと思える感じでした。

また、コンソールからSQLも叩けるのでデバッグとかに重宝しました。

S3バケット内のCSVにS3 Selectというタブがあるので

f:id:syossan:20181210015244p:plain

そこからSQL実行できます。

f:id:syossan:20181210015333p:plain

バックエンドのソースコードはこちら

github.com

まとめ

作った期間は2日くらいですが、すごく久々にクソをひり出せて満足感が高かったです。
用意したデータ自体の不備が結構あったので、全部で約1600件のデータしか返せないのが残念ですが、暇を見つけて充足させていきたいと思います。

みんな、エロはいいぞ。

現場からは以上です。

GoLandにCassandra対応が来たぞ!!

掲題の通り、GoLand 2018.3でCassandraのDataSourceが使えるようになりましたぞ!

www.jetbrains.com

今までCassandraのGUIツールといえば、DataStax DevCenterくらいしか無かったのですが、これで新しい選択肢が増えましたね。

qiita.com

試しに使ってみたのですが、他のDataSourceと同じように特に違和感なく使うことができます。いやー、嬉しい。

f:id:syossan:20181123183159p:plain

あと、DataGripはEAPになっている2018.3対応でCassandra Supportが来るみたいですね。
Release Noteにも書いてあるように「Thanks to our wonderful colleagues on the DataGrip team」だ!ありがとう!

guregu/nullでぬるぬるする

Goでサーバーサイドの開発を行っているとMySQLなどのRDBに接続して、取得した値をJSONで返す機会は多いはず。
そして大概ハマるのが「NULLを許容してJSONで返すのはどうやればいいんだ・・・」というところでしょう。

今回はそんな話をします。

ノーガード戦法

まずは特に何もしなかったパターンを見てみましょう。

テストデータは以下のようなものを使います。

+-------+------+
| name  | age  |
+-------+------+
| tarou |   12 |
| NULL  | NULL |
|       |    0 |
+-------+------+

適当な値が入ったレコードと、NULLが入ったレコードと、空文字・0が入ったレコードになります。
これを以下のようなコードで使ってみましょう。

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Name string
    Age int
}

func main() {
    db, err := sql.Open("mysql", "root:@/test")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT * FROM user")
    if err != nil {
        return
    }


    for rows.Next() {
        var user User
        rows.Scan(&user.Name, &user.Age)
        JSON, err := json.Marshal(user)
        if err != nil {
            continue
        }
        fmt.Println(string(JSON))
    }
}

これを動かしてみると・・・

{"Name":"tarou","Age":12}
{"Name":"","Age":0}
{"Name":"","Age":0}

おっ、NULLが空文字と0になりましたねー。
このように特に何もしなかった場合は型に応じた初期値になるんですよね。

王道パターン

さて、ここで「ちゃんとNULLで出力出来ないと困るよ!」って場合には以下のようにUser構造体の型を変えることで判別できます。

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "github.com/k0kubun/pp"
)

type User struct {
    Name sql.NullString
    Age sql.NullInt64
}

func main() {
    db, err := sql.Open("mysql", "root:@/test")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT * FROM user")
    if err != nil {
        return
    }

    for rows.Next() {
        var user User
        rows.Scan(&user.Name, &user.Age)
        pp.Println(user)
    }
}

これを実行すると

{"Name":{"String":"tarou","Valid":true},"Age":{"Int64":12,"Valid":true}}
{"Name":{"String":"","Valid":false},"Age":{"Int64":0,"Valid":false}}
{"Name":{"String":"","Valid":true},"Age":{"Int64":0,"Valid":true}}

のようになります。
Valid というのがNULLかどうかのbool値を持っている構造体が含まれているのが分かりますね。

このままでは使い物にならないので、良い感じにNULLを返すために新たに sql.NULLXXX を含んだ構造体を作成して、ゴニョゴニョやるパターンが多く見受けられます。

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

type (
    User struct {
        Name NullString `json:"name"`
        Age NullInt64 `json:"age"`
    }

    NullString struct {
        sql.NullString
    }
    NullInt64 struct {
        sql.NullInt64
    }
)

func (ns NullString) MarshalJSON() ([]byte, error) {
    if !ns.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(ns.String)
}

func (ni NullInt64) MarshalJSON() ([]byte, error) {
    if !ni.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(ni.Int64)
}

func main() {
    db, err := sql.Open("mysql", "root:@/test")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT * FROM user")
    if err != nil {
        return
    }

    for rows.Next() {
        var user User
        rows.Scan(&user.Name, &user.Age)
        JSON, err := json.Marshal(user)
        if err != nil {
            continue
        }
        fmt.Println(string(JSON))
    }
}

MarshalJSON() ([]byte, error) を持つ構造体を作成することで、 Marshaler Interfaceを満たし json.Marshal 実行時に定義した MarshalJSON を実行してくれます。

これを実行した結果は・・・

{"name":"tarou","age":12}
{"name":null,"age":null}
{"name":"","age":0}

ちゃんとnullが許容されてますね!
さて、これで当初の目的は達成できたわけですが、いちいち sql.NULLXXX を含んだ構造体を作成して MarshalJSON を定義するのはヒジョーに面倒です。

そこで、 guregu/null の出番です。

guregu/null

使い方は非常に簡単。
NULL値が入った時に null.XXX 型にはnullを、 zero.XXX 型には型の初期値に変換されます。

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "gopkg.in/guregu/null.v3"
    "gopkg.in/guregu/null.v3/zero"
)

type (
    User struct {
        Name null.String `json:"name"`
        Age null.Int `json:"age"`
    }
)

func main() {
    db, err := sql.Open("mysql", "root:@/guregu")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT * FROM user")
    if err != nil {
        return
    }

    for rows.Next() {
        var user User
        rows.Scan(&user.Name, &user.Age)
        JSON, err := json.Marshal(user)
        if err != nil {
            continue
        }
        fmt.Println(string(JSON))
    }
}

こうすることで

{"name":"tarou","age":12}
{"name":null,"age":null}
{"name":"","age":0}

ちゃんと null.XXX はnullに、 zero.XXX は型の初期値になってますね。

単純にnullにしたい場合は便利なライブラリですね。
ただ、UnmarshalやMarshal時に特殊な処理をしたい場合は sql.NULLXXX を使った方法を使うのがベターでしょう。

S3にファイルがアップロードされた時にCloudFrontのキャッシュを削除してみる

掲題の通りのことが社で必要になったのでGoでやってみました。

やりたいこと

S3に置いた静的HTMLをCloudFrontを通してアクセスする、という形で配信しているシステムがあり、運用する中で静的HTMLを置き換えた時にすぐに切り替わるようにしたいということで、CloudFrontのキャッシュを都度削除する必要がありました。
ただし、毎日切り替わる代物なのでその都度手動でCloudFrontの特定Bucketに対してInvalideするのは面倒だなーと思ったので、それならファイルを置いた時に自動的にInvalidateが実行されるようにしたいというのが目的です。

どうやるか

さて、やり方ですが今回はLambdaを使って実現しました。

Lambdaのイベント発火条件として「S3のObjectCreatedByPut」を使います。
これでS3の指定したBucketにObjectが生成・更新された際にLambdaの関数が動いてくれます。

そして、以下のようなLambda関数を作成します。

簡単に説明すると、発火したイベント詳細を引数として受け、そこからバケット名を取得してCloudFrontのDistoributionのドメイン名と合致するものを探します。

見つかれば、そのDistributionに向かって CreateInvalidation 関数を使ってinvalidationを実行するだけです。非常に簡単ですね。

こんなにも簡単に作成出来てしまうのだからLambdaの力は凄いですね。
贅沢を言うとGoの設計テンプレートが全く無いので、AWSの中の人には頑張って追加して欲しいところ・・・🙏