suguru.dev

バンクーバーで働くエンジニアの備忘録

AnyenvでGoのバージョン管理

概要

Anyenvとはこれ一つで色々なパッケージ管理ができるスグレモノのようです。今回はGolangまわりだけ整理していたので、Golangのセットアップ手順について書きます。

インストール

まずはリポジトリをクローン。

$ git clone https://github.com/riywo/anyenv ~/.anyenv

.zshrcに以下を追記。

# anyenv
export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"

goenvをインストールします。

$ anyenv install goenv

次にGolangをインストールします。

$ goenv install 1.8

.zshrcにGOPATHを設定します。(goenv rehashで解決してくれると思ったらそうでもない)

# Go
export GO_VERSION=1.8
export GOROOT=$HOME/.anyenv/envs/goenv/versions/$GO_VERSION
export GOPATH=$HOME/dev
export PATH=$HOME/.anyenv/envs/goenv/shims/bin:$PATH
export PATH=$GOROOT/bin:$PATH
export PATH=$GOPATH/bin:$PATH
echo Now using golang v$GO_VERSION

なんとなくnvmと同じechoを追加します。

Now using node v6.10.1 (npm v3.10.10)
Now using golang v1.8

$ go version
go version go1.8 darwin/amd64

良い感じになりました。

まとめ

Version Managerにこだわりが無いようでしたら、新しい言語をインストールするときにあちこちVersion Managerを探しに行くよりサクッとインストールできるのでいいかなぁと思います。

リンク

GitHub - riywo/anyenv: all in one for **env

GitHub - ingtk/dotfiles

Dockerのお掃除

概要

MBPの容量が圧迫しておりどうやら犯人はDocker for Macのようなのでお掃除していきます。

不要なコンテナの削除

Docker – Clean Up After Yourself! | Yohan Liyanage

こちらのブログより。

exited containersは自動削除されないとのことで、こちらを実行してくださいとの。

$ docker rm -v $(docker ps -a -q -f status=exited)

rmコマンドは少なくとも1つ以上の引数を必要とするので、

"docker rm" requires at least 1 argument(s).
See 'docker rm --help'.

Usage:  docker rm [OPTIONS] CONTAINER [CONTAINER...]

Remove one or more containers

このエラーが出た場合は削除できるexited containersが存在しないとのことなので気にせず進みます。

不要なイメージの削除

macos - How to clean up Docker for Mac containers - Ask Different

続いてこちら。 ポイポイしていきます。

$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v /etc:/etc spotify/docker-gc

不要なコンテナ・イメージの削除【追記】

Christinaさんよりアドバイスいただき、以下のコマンドで上記2つは解決できるそうです、ありがとうございます。

$ docker system prune

Docker.qcow2の削除

Where does Docker keep images/containers so i can better track my disk usage - Docker for Mac - Docker Forums

最後のこちらが一番大物で、私の場合は30GBほど占領していました。 キャッシュのようですが容量をこんなに占拠されては困りますのでポイポイしていきます。

$ rm ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2

ソース

Node.jsで内部変数・関数の取得

概要

テストやちょっとしたツールを作りたいときに内部変数・関数にアクセスしたい時がありますが、 そんなときに便利なのがvmモジュールです。しかしvmではvarの変数は取得できるものの、letconstの変数は取得できません。 そこで色々試行錯誤して作ったツールについて書きます。

vmを用いた内部関数の取得

vm.runInNewContextではコードを実行時にglobalオブジェクトをセットすることができます。vm.runInThisContextは自動的に現在のglobalオブジェクトが使われます。

この関数は実行時にglobalスコープ上のvarをglobalオブジェクトに代入することで外からのアクセスを可能にしてくれますが、letconstは変換されません。

sample.js

const test1 = 'test1';
let test2 = 'test2';
var test3 = 'test3';

function test(arg1, arg2, arg3) {
  const sum = arg1 + arg2 + arg3;
  let num1 = 1;
  var num2 = 2;
  num3 = 3;
  return sum + num1 + num2 + num3;
}

exec.js

const vm = require('vm');
const fs = require('fs');
const path = require('path');

const filepath = path.resolve(__dirname, 'sample.js');
const file = fs.readFileSync(filepath, 'utf8');

const context = {};
vm.runInNewContext(file, context);
console.log(context);
/* 
 * { test3: 'test3', 
 *   test: [Function: test] } // let, constが取得できない
 */

vm-agent

今回作ったツールはlet, constが取得できるのと、関数を自動実行して値を取得できるツールを作りました。 また関数の実行も可能です。

const fs = require('fs');
const path = require('path');
const { Agent } = require('vm-agent');

const filepath = path.resolve(__dirname, ''sample.js');
const file = fs.readFileSync(filepath, 'utf8');

const result1 = new Agent(file)
  .run()
  .getInnerVariable();

console.log(result1);
/* 
 * { test1: 'test1',
 *   test2: 'test2',
 *   test3: 'test3',
 *   test: [Function: test] }
 */

const result2 = new Agent(result1.test)
  .setArguments(4, 5, 6)
  .run()
  .getInnerVariable();

console.log(result2);
/*
 * { arg1: 4, 
 *   arg2: 5, 
 *   arg3: 6, 
 *   sum: 15, 
 *   num1: 1, 
 *   num2: 2, 
 *   num3: 3 }
 */

vm-agent/example at master · suguru03/vm-agent · GitHub

実装内容

実装内容はesprimaを用いてlet, constvarに変換し、escodegenを用いてコードを再構築し実行するというものです。constが上書きできてしまったりパフォーマンスが低下したりしますが、テスト用なのでいったん良しとします。

また関数の実行については、関数の引数を解析し変数に変換することでvmモジュールでのアクセスを可能にしています。

ソース

Objective-CにおけるDate型の扱い

概要

なぜか最近仕事でObjective-Cを書いています。 サーバからmsのtimestampを受け取っているにも関わらず、時系列順に表示されなかったので調べてみました。

Types in Objective-C

まずはじめにハマったのが、msはint型には収まりません。JavaScriptの住人なのでここで時間をロスしました。

Types in objective-c on iPhone - Stack Overflow

この記事によると32bit端末ではlong型が4bytes扱い、64bit端末ではlong型が8bytes扱いだそうです。

Device一覧: https://en.wikipedia.org/wiki/List_of_iOS_devices

WikipediaによるとiPhone5S以降は64bitなので、long型を使うことにしました。(良いのかは不明)

Primitive sizes:
The size of a char is: 1.
The size of short is: 2.
The size of int is: 4.
The size of long is: 8.
The size of long long is: 8.
The size of a unsigned char is: 1.
The size of unsigned short is: 2.
The size of unsigned int is: 4.
The size of unsigned long is: 8.
The size of unsigned long long is: 8.
The size of a float is: 4.
The size of a double is 8.
Ranges:
CHAR_MIN:   -128
CHAR_MAX:   127
SHRT_MIN:   -32768
SHRT_MAX:   32767
INT_MIN:    -2147483648
INT_MAX:    2147483647
LONG_MIN:   -9223372036854775808
LONG_MAX:   9223372036854775807
ULONG_MAX:  18446744073709551615
LLONG_MIN:  -9223372036854775808
LLONG_MAX:  9223372036854775807
ULLONG_MAX: 18446744073709551615

Dateの扱い

Objective-CではDateがmsをサポートしていないため、以下のように書かなければなりません。

NSNumber *timestamp = responseDict[@"timestamp"];
long ts = [timestamp doubleValue]; // coredataへ保存
NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts/1000]; // ms -> s

JavaScriptの住人にとっては何だこれっていうレベルです。Swift知りませんが、きっとここらへんは改善されてるはず…祈

このプロジェクトではCoreDataにDate型で保存していたため、secondまでしかサポートされておらず時系列順にソートすると残念な結果になったので、しれっとInteger 64型に書き換えて保存するようにしました。

まとめ

せっかくならSwift書きたいです。

ソース

Google Cloud Platform上にContainer Clusterを作成する

概要

Google Cloud PlatformのContainer Cluster(Kubernetes)のセットアップからデプロイまでの手順です。プロジェクトの作成手順は省略します。

Container Clusterの作成

メニューから [Container Engine] -> [Container Clusters] を開き、[CREATE CLUSTER]をクリック。必要な項目を有効にして作成してください。 このプロジェクトではCloud SQLを利用しているので、Cloud SQLをEnabledにしています。

作成後、画面にConnectと出てくるので、メニューに従って実行します。

$ gcloud container clusters get-credentials production \
    --zone {指定したzone} --project {project-id}
$ kubectl proxy

gcloudのSDKはこちらからダウンロード可能です。

Cloud SDK  |  Google Cloud Platform

installの際にインストール不足のものはエラーとして表示されるので、それに従ってinstallして再度上のコマンドを実行すれば完了です。

http://localhost:8001/ui/にアクセスしKubernetesの画面が見れれば準備は完了です。

Redisのデプロイ

プロジェクトでredisを使用しているためredisのDockerイメージをデプロイします。Dockerをinstallしていない方はこちらからインストールできます。

Installation on Mac OS X

$ docker pull redis:alpine
$ docker tag redis:alpine gcr.io/{project-id}/redis:alpine
$ gcloud docker push gcr.io/{project-id}/redis:alpine

これでRedisのデプロイは完了です。

コンテナの配置

後は先程のブラウザ上でコンテナを配置するだけです。

f:id:suguru03:20160930145803p:plain

右上の[CREATE]をクリックし、必要に応じて入力してください。コンテナイメージのパスは [Compute Engine] -> [VM instances] のところのInstance templeteをコピペすれば完了です。 ポッド数は必要に応じて増やしてください。また今回のRedisは外部アクセスを許可していないので内部アクセスのみにしてあります。

他にNode.jsのDocker Imageもデプロイしましたがまた別の機会に作成手順と一緒に書きたいと思います。

感想

Docker & Kubernetes初日でしたが、同僚の手助けの下、数時間でNode.js/Redis Clusterの動作確認ができました。とても便利なので今後も使っていきたいです。

ソース

Pokemon Goのちょっと得する情報

カナダで一足先にプレイしています。ポケモン効果はすごいです、家から基本出なかったカナディアンの友達が毎日外出してます。

さて色々損してきたのでさくっと箇条書きでまとめときます。

  • ピカチュウをゲットする
    • 最初の三匹を選ばずに待っていると現れます。
  • サークルは小さいほうが捕まえやすい
  • サークル内にヒットすると捕まりやすくなるのと経験値がもらえる
  • 足あとは少ないほど近い
  • Lv30までは「ほしのすな」・「アメ」は使わない
    • 育てたとしても野生で同じ強さ・もしくはそれ以上のポケモンが出てきます。
    • 育てても無駄です。
    • 個体値があるようなので、個体値MAXのポケモンは無駄ではないかも。(*追記 6/23)
  • 卵のレベル上限はLv20
  • 卵の内訳
  • 有料ボックスは5km以上の卵に使ったほうがコスパいい
    • 2kmは無限ボックスで孵化させましょう。
  • Lv上限は現在40
  • イーブイの進化のさせ方 (アニメ参照)
    • リネームする (最初は大文字だよ、日本語対応してるかわからない)
      • Sparkyが欲しい場合Jolteon
      • Vaporeonが欲しい場合Rainer
      • Flareonが欲しい場合Pyro
    • 再起動する
    • 進化させる
  • ズバット
    • 玉の浪費なので高CPは逃げる
    • カーブが気持ち射程長い気がしないでもない
  • カーブはボールをくるくるすれば投げられる
  • ラズベリーポケモンが逃げにくくなる
    • レアポケモンに使いましょう
    • ボールを投げるごとにあげましょう
  • ボールを投げた後にフリーズする
    • いくら待っても結果は変わりません、再起動してください。
  • googleアカウントをカナダにしてカナダドルでクレカ切るとお得
    • 14,500コインでおそらく1,200円くらいお得ですかね(81円換算)
  • Lure moduleのピンクのところをクリックするとアイテム使ってくれてる人がわかる
    • 見てあげてください
  • 個体値 *追記 6/23
    • サイズがでかいほうが高い
    • 進化はギリギリまでさせてはいけない
  • ポッポマラソン *追記 6/23
    • ポッポの入手が簡単
    • 進化させると経験値が入る
    • しあわせタマゴで経験値2倍
  • ミューツー、ファイヤー、サンダー、フリーザーは伝説
  • ミュウは(ry

Have a good Pokemon life!

リンク

GAEからCloud Storageに画像をアップロードする

はじめに

Google App Engine (GAE)のGolangAPI仕様がガラッと変わったようなので、古いバージョンを使っている方はここを参考に書き換えてください。

GitHub - golang/appengine: Go App Engine packages

Goのversionは1.6.2を使用しています。

画像のアップロード

実際はリクエストを受け取り、その画像をCloud Storageにアップロードしていますが、 実行しやすいようにローカルファイルとテストを使用しています。

package storage

import (
    "fmt"
    "google.golang.org/appengine"
    "google.golang.org/appengine/aetest"
    "google.golang.org/appengine/blobstore"
    "google.golang.org/appengine/image"
    "google.golang.org/appengine/log"
    "google.golang.org/cloud/storage"
    "io"
    "net/http"
    "os"
    "testing"
)

const (
    bucket = "<bucket path>"
    image = "<image path>"
)

func Upload(req *http.Request) (string, error) {
    ctx := appengine.NewContext(req)
    client, err := storage.NewClient(ctx)
    file, err := os.Open(image)
    userName := "tester"
    fileName := "test.jpg"
    mimeType := "image/jpeg"
    filePath := fmt.Sprintf("%s/%s", userName, fileName)
    blobWriter := client.Bucket(Bucket).Object(filePath).NewWriter(ctx)
    blobWriter.ContentType = mimeType
    io.Copy(blobWriter, file)
    err = blobWriter.Close()
    blobPath := fmt.Sprintf("/gs/%s/%s", Bucket, filePath)
    blobKey, err := blobstore.BlobKeyForFile(ctx, blobPath)

    // crop
    opts := image.ServingURLOptions{Secure: false, Crop: true}
    url, err := image.ServingURL(ctx, blobKey, &opts)
    log.Infof(ctx, "url", url)
    return url.String(), err
}

func TestUpload(t *testing.T) {
    inst, err := aetest.NewInstance(nil)
    req, err := inst.NewRequest("GET", "/gophers", nil)
    url, err := Upload(req)
    fmt.Printf("test", url, err)
}

ローカルで実行すると画像は正常にアップロードされますが、ServingURLがうまく働いてくれないようでhttp://localhost/_ah/img/encoded_gs_file:...のようなパスになってしまいます。 GAE上だと正常に動いているのですがテストを書く上でとても不便ですね。。