arp-scanでLANにつながっているデバイスのIPアドレスを調べる

先日、RaspberryPiを触っていた時に、LANに接続している内のIPアドレスを知る方法はないかと思っていろいろ調べてみた。すると、arp-scanというコマンドがあった。

$ arp -i en0 -a
? (192.168.11.1) at 0:24:a5:f:8f:ae on en0 ifscope [ethernet]
? (192.168.11.14) at 70:48:f:74:ce:b on en0 ifscope [ethernet]
? (192.168.11.255) at ff:ff:ff:ff:ff:ff on en0 ifscope [ethernet]
? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
broadcasthost (255.255.255.255) at ff:ff:ff:ff:ff:ff on en0 ifscope [ethernet]

arpコマンドでもある程度は出るんだけど、arp-scanコマンドを使うとよりわかりやすい情報を見ることができる。

Macではbrew install arp-scanで簡単にインストールできる。

インストールしたらコマンドを実行すると、次のような結果が出力される。

$ sudo arp-scan -l
Password:
Interface: en0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
192.168.11.1    00:24:a5:0f:8f:ae   Buffalo Inc.
192.168.11.7    00:17:88:1c:ac:62   Philips Lighting BV
192.168.11.15   b8:27:eb:07:66:12   Raspberry Pi Foundation
192.168.11.8    00:25:00:4d:f8:90   Apple, Inc
192.168.11.10   84:d6:d0:a1:c3:f9   (Unknown)
192.168.11.9    f0:f6:1c:d0:0c:65   (Unknown)

これを見ると、RaspberryPiのIPアドレス192.168.11.15であることがわかる。便利。

Atom Key Binding Resolver

Atomを使っていてキーバインディングで迷子になった時いつも調べるのでメモ。 基本は cmd+shift+pkeymap を検索して、 もしくは cmd+, で設定の Keybindings からキーバインディング設定を開いて確認する。 ない場合は 「Key Binding Resolver」を使うと便利。 毎回この起動方法を忘れて思い出せないので一応メモしておこうと思う。

cmd+shift+p を起動して Key Binding Resolver を検索すると起動できる。cmd+.でも起動する。

f:id:ringo6119:20170617171650g:plain

Terraformで循環参照のエラーが出た時

これは、リソースを変数で参照して整合性?を保とうとしたときに良くなる。

variable "env" {
  type = "string"
  default = "dev"
}
variable "service" {
  type = "string"
  default = "foo"
}

resource "aws_cloudfront_origin_access_identity" "origin_access" {
  comment = "access-identity-${var.env}-${var.service}.s3.amazonaws.com"
}

data "template_file" "s3_policy" {
  template = "${file("s3-bucket-policy.json")}"

  vars {
    bucket_name            = "${var.env}-${var.service}"
    origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access.id}"
  }
}

resource "aws_s3_bucket" "foo" {
  bucket = "${var.env}-${var.service}"
  acl    = "private"
  policy = "${data.template_file.s3_policy.rendered}"

  tags {
    Name        = "${var.env}-${var.service}"
    Environment = "${var.env}"
    ServiceName = "${var.service}"
  }
}

となっている場合、循環参照が発生する。

❯ terraform plan
Error configuring: 1 error(s) occurred:

* Cycle: data.template_file.s3_policy, aws_s3_bucket.foo, aws_cloudfront_origin_access_identity.origin_access

もう、わけわかんないし辛い気持ちになるけど、Terraformには便利な機能がある。

依存グラフの出力である。

terraform graph -draw-cycles | dot -Tpng > graph.png

f:id:ringo6119:20170322163153p:plain

-draw-cyclesをつけると赤くなるので、分かりやすい。 後は、その依存関係を解決していけば良いだけ。

今回のケースは、aws_cloudfront_origin_access_identityでバケット名を参照しているが、バケット名は"${var.env}-${var.service}"で置き換え可能なため、

resource "aws_cloudfront_origin_access_identity" "origin_access" {
  comment = "access-identity-${var.env}-${var.service}.s3.amazonaws.com"
}

とすると直る。

f:id:ringo6119:20170322163152p:plain

References

Terraformでテンプレートを使ってポリシーを定義する

Terraformにはテンプレート機能がある。よく使われるのは、ポリシーの設定などで、ヒアドキュメントで記述されるようなものを、別ファイルに記述し変数を与えて埋めていく。

ポリシーを定義する

例えば、ユーザやグループにポリシーを設定するために、まずポリシーを定義する。 ここで、 bucket_name は変数になっている。ポリシーの意味は ${bucket_name} 以下にあるオブジェクトに対しての操作を全て許可するというもの。リソースの最後に /* をつけないとバケットの操作1になってしまうので注意。 これを、 s3_bucket_policy.tpl.json というファイルに保存しておこう。拡張子を json とかにしておいたほうがシンタックスハイライトの恩恵が受けられる(と個人的には思う)。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": [
        "arn:aws:s3:::${bucket_name}/*",
      ]
    }
  ]
}

IAM Policy リソースの定義

次に、リソースを定義しよう。 今までは policy<<EOF ... EOF などを書いてポリシーを埋め込んでいたと思う。 ココも変数にする。

resource "aws_iam_policy" "developer" {
    name = "developer_policy"
    path = "/developer"
    description = "Developer IAM policy"
    policy = "${data.template_file.foo.rendered}"
}

Template データの定義

最後にテンプレートの定義をしていこう。datatemplate_fileを定義する。 template には最初で作成したファイルを指定する。fileメソッドを使うとファイルを引っ張って来ることができ、引数にはパス、ファイル名を与えよう。最初に作成したファイル名はs3_bucket_policy.tpl.jsonなので、それを指定する。

テンプレート内にbukcet_nameという変数があったが、それをここで埋める。 varsのマップを記述すると、そのままテンプレートに渡され、埋められる。

data "template_file" "foo" {
  template = "${file("s3_bucket_policy.tpl.json")}"

  vars {
    bucket_name = "bar"
  }
}

bukcet_name をTerraformで作成したバケットにしたい場合は、varsに変数を与えればいい。

resource "aws_s3_bucket" "bar" {
  bucket = "dev-var-bucket"
  acl    = "private"
}

data "template_file" "foo" {
  template = "${file("s3_bucket_policy.tpl.json")}"

  vars {
    bucket_name = "${aws_s3_bucket.bar.bucket}"
  }
}

Appendix

AWS_IAM_POLICY_DOCUMENT resource

AWS IAMポリシーの作成はHCLで記述できることを @mia_0032 さんから教えていただきました。 Terraform version 0.7.0 から aws_iam_policy_document リソースが入っていた(CHANGELOG)。

New Data Source: aws_iam_policy_document (#6881)

これのメリットをちょっと考えてみると次のような感じだと思う。

  • HCLのシンタックスハイライトを使ってポリシーを書ける
  • plan 実行時にある程度の構文チェックができる
  • 書くものが減りすこしスッキリする(テンプレートの方式だと、 json ファイル、 data ソース、 aws_iam_policy リソースを用意しないといけない)

結構、多くのメリットがありそう。

ぼくがポリシーを書くときは、AWSコンソールで直接ポリシーを作成して、検証しながらJSONを組み立てていく。整ったところで、JSONをコピーし、変数になる部分の記述を変換してテンプレートを仕上げている。

JSONのポリシーをHCLに変換してくれる何かがほしい。 ちょっと調べてみた感じだとありそう(kvz/json2hcl)。

References

Terraform で変数を使う

Terrafromでは変数が利用でき、variableを定義して、値を注入する。 一番シンプルな例は次の通り。

provider "aws" {
  region = "ap-northeast-1"
}

variable "foo" {}

resource "aws_s3_bucket" "s3_bucket" {
  bucket = "${var.foo}"
  acl = "private"
}

variablefoo を宣言し、S3バケットのところで ${var.foo} 利用している。

値の注入方法

値の指定方法は次の4通りある。

  1. 実行時に指定
  2. コマンド引数による指定
  3. 環境変数による指定
  4. 設定ファイルによる指定

実行時に指定

変数を宣言した後に、terraform planなどを実行すると、コンソールでどんな値を指定するか聞かれるので、そこで指定する。

❯ terraform plan
var.foo
  Enter a value: bar

...

+ aws_s3_bucket.s3_bucket
...
    arn:                 "<computed>"
    bucket:              "bar"
    force_destroy:       "false"
...

Plan: 1 to add, 0 to change, 0 to destroy.

コマンド引数による指定

コマンド実行時 -var オプションを利用して値を指定する。 試しに terraform plan -var 'foo=test-bucket' を実行してみると、ちゃんと注入されている。

環境変数による指定

TF_VAR_プレフィックスを付けて環境変数を設定すると、その値が変数にロードされる。 TF_VAR_foo='env-test' terraform plan を実行すると同様の結果が得られる。

設定ファイルによる指定

変数の値を指定する設定ファイルを作成し、-var-fileで指定すると同様のことが出来る。

vars.tfvars

foo = "tfvars-file"

terraform plan -var-file=vars.tfvars を実行すると同様の結果が得られる。

変数戦略/設計

Terrafromは *.tf ファイルを全て読み込むので、 変数定義をvariables.tfなどに記述しておき、 実行時に-var-fileで値ファイルを指定して環境などを切り替えると良いかもしれない。

  • terrafrom plan -var-file=dev.tfvars
  • terrafrom plan -var-file=prod.tfvars

ディレクトリとか、ファイルはこんな感じにすると雰囲気はつかめると思う。

main.tf

provider "aws" { region = "ap-northeast-1" }
resource "aws_s3_bucket" "s3_bucket" {
  bucket = "${var.foo}"
  acl = "private"
}

variables.tf

variable "foo" {}

dev.tfvars

foo="dev-var"

prod.tfvars

foo="prod-var"

その他Tips

default を記述するとファイルを指定しなくてもそれになる。 また、type などで型も指定できる。 descriptionで、変数の説明もかけるので書いたほうがいい。

variable "foo" {
  type = "string"
  default = "default-var"
  description = "Sample Variable"
}

References

Terraform でループして複数のリソースを作成する

TerraformでListの変数を使ってループさせたい時がある。 例えば、ユーザアカウントを複数作りたいとき。

以下のようなリソース定義を必要な分だけ書かないといけない。10個あったら10個。メッチャ大変。

resource "aws_iam_user" "developer" {
  name = "foo"
  path = "/developer"
}

普通に考えて、変数を定義してループさせたくなる。 まず変数を定義する。あとは、コイツを上手く回す方法を考える。

variables "developer" {
  default = ["aoki0", "aoki1", "aoki2"]
}

リソースを複数作成する

リソースにはcountというパラメータがあり、この値を指定するとn個のリソースを作成してくれる。便利。

resource "aws_iam_user" "developer" {
  connt = 3
  name = "aoki"
  path = "/developer"
}

すると、3つの aws_iam_user が作成される。

+ aws_iam_user.developer.0
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

+ aws_iam_user.developer.1
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

+ aws_iam_user.developer.2
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

Plan: 3 to add, 0 to change, 0 to destroy.

しかし、2つの課題がある。 - 生成する数(3)をハードコーディングしてる - name が全部同じ

変数に指定されたリストの長さを取得する

Terraformには length というリストの長さを取得する関数がある。 こいつを、利用してリスト長分のリソースを作成する。

resource "aws_iam_user" "developer" {
  count = "${ length( var.developer ) }"
  name = "aoki"
  path = "/developer"
}

いい感じ。

+ aws_iam_user.developer.0
+ aws_iam_user.developer.1
+ aws_iam_user.developer.2
Plan: 3 to add, 0 to change, 0 to destroy.

リストの要素を利用する

リストの要素を取得するには element という関数、 現在のインデックスを取得するには count.index が使える。 これを、埋め込みたい箇所に "${}" でくくって記述する。

resource "aws_iam_user" "developer" {
  count = "${ length( var.developer ) }"
  name = "${element(var.developer, count.index)}"
  path = "/developer"
}

ちゃんと、値も個数も設定ファイルから取得できた。

+ aws_iam_user.developer.0
    name:          "aoki0"
+ aws_iam_user.developer.1
    name:          "aoki1"
+ aws_iam_user.developer.2
    name:          "aoki2"
Plan: 3 to add, 0 to change, 0 to destroy.

あとは、nameも上手く取得したい。

MapのListも使える

変数の値を次のようにして、Listで回せばイロイロできそう。

ips = [
  { name = "foo", ip = "aaa.bbb.ccc.ddd/32", description = "foo IP" },
  { name = "bar", ip = "bbb.bbb.ccc.ddd/32", description = "bar IP" },
  { name = "baz", ip = "ccc.bbb.ccc.ddd/32", description = "baz IP" }
]

と、いいたいところなんですが、GitHubにIssueが上がっていて、現在(2016/11/29)使えない😢。

zipmap

対処療法として、二つのListを用意してzipmapで無理やり結合する。

# tfvars
names = ["foo", "bar", "baz"]
ips = [
 "aaa.bbb.ccc.ddd/32",
 "bbb.bbb.ccc.ddd/32",
 "ccc.bbb.ccc.ddd/32"
]

zipmap は二つのリストをzippingする。 たとえば、前述したnamesipsをzipすると、

{
  foo = "aaa.bbb.ccc.ddd/32",
  bar = "bbb.bbb.ccc.ddd/32",
  baz = "ccc.bbb.ccc.ddd/32"
}

となる。

はやくIssueが解決されて欲しい。

References

PebbleのWatchFaceを作ってみた

去年、PebbleがFitBitに買収されてたので、在庫が無くなる前にPebble Time Roundを買った。 今までは、Pebble Timeを使っていたんだけど、四角いし厚いしいまいちオシャレじゃないなーと思ってたのでよい機会。

毎年、正月休みに新しい何かを触ってみようと思っていて、去年はSwiftでTODOアプリを作り、TravisCIでビルド(ココが一番時間かかった記憶がある)した。ので、今年の正月休みはPebbleのWatchFaceを作ってみた。

公式のチュートリアルをなぞっただけで、特別なことはやっていないんだけども、デザインを自分で考えるのは楽しい。機能としては、日付け、時間、天気、気温、電池残量を表示するだけ。

CloudPebbleを使って開発したんだけれども、これが結構よくできていて、コーディングからビルド、エミュレータの起動、gitのコミット・プッシュまで出来る。 でも、ブラウザアプリはキーバインドが衝突したり、ブラウザバックしちゃったりするのが嫌いなので、nativefierというツールでデスクトップアプリ化した。 フレームレスウィンドウが好きなのでついでに、クローンしてパッチを当てた。

f:id:ringo6119:20170109174157p:plain

WatchFace側はC言語、アプリ側?はJavaScriptで書いた。C言語のほうがロウエナジーだってどこかに書いてあったからそうしたけど、計測はしてない。

TravisCIでビルドしてGitHubのリリースに置こうと思ったけど上手く動かず。python2系とnpm3系が必要でインストールとか面倒くいので別の機会に。 ビルドするためのdockerイメージも作ろうしたけども同じ理由でまた今度。

反省点じゃないけれども、やってみたいことが色々出てきた。

  • OpenWeatherMapは精度が悪いので、Yahoo天気とか何かにしたい
  • 設定画面でAPIトークンや、天気機能のON/OFFを切り替えたい
  • 色を変えられるようにしたい
  • font-awesomeを使って天気や電池、接続情報をアイコンで表現したい
  • 同じようなことをやっているコードのリファクタリング・抽象化

f:id:ringo6119:20170109174230p:plain

いやー、C言語って難しいね。

References

TerraformのstateをS3でリモート管理する

Terraformの管理下にあるリソースの情報は tfstate ファイルに保存されている。たとえば、チームでこのtfstateを共有したいときにどうすべきかというのが課題になる。

tfstate ファイルをS3バケットに保存する(version >= 0.9.0)

Terraform 0.9.0 から terraform remote の変わりに Remote Backend になった。 - terraform/CHANGELOG.md at master · hashicorp/terraform · GitHub

❯ tf remote pull 
Local and remote state in sync

tfstate ファイルをS3バケットに保存する(version < 0.9.0)

S3バケットにこの状態ファイルを保存するのは簡単で、単に以下のコマンドで設定すれば良い。

terraform remote config \
  -backend=S3 \
  -backend-config="region=ap-northeast-1" \
  -backend-config="bucket=<YOUR_STATE_BUCKET_NAEM>" \
  -backend-config="key=<STATE_PATH>/<STATE_FILE_NAME>.tfstate"

この設定を行った後にpushする。

terraform remote push

以下のコマンドを実行すればpushしたファイルが一覧されるはず。

aws s3 ls s3://<YOUR_STATE_BUCKET_NAME> --recursive

ローカルにリモートのステートを持ってくるにはpullを実行する。

terraform remote pull

また、

terraform remote config -disable

でリモート管理をやめ、ローカルに戻せるらしい。

References

Alfred 3 のテーマ

先日、Alfred 3がリリースされた。パワーパックは2で購入していたので £12 でアップグレードできる。 とはいえ、2で満足できてたし1,600円払ってまでアップグレードするほどの目玉機能入ったの?? 早くなった?ワークフローが強くなった?いやー、2で十分でしょと思っていた。

でも、即アップグレードすることになった。

2で唯一困っていたことは「テーマのフォントが好きなフォントに変更できない」ということ。 不満に思っている人も多いらしく、海外の掲示板でもイロイロ議論されていた。

しかし、今回3ではこのフォント変更が可能になったことに加え、Blurエフェクトをつけることが可能になった。 この機能追加を見た途端、アップグレードするしかないと思い立ち £12 の支払いをPayPalにて完了した。

幾つかテーマを作って公開しようとしたが、公式(?)に投稿するテーマには制限があり「Standard Fonts」を使ったものしか投稿できない仕様になっているようだ(そんな気はしてた)。

To share themes on alfredapp.com, your theme needs to use fonts from the 'Standard Fonts' section in the font popup menu. These fonts will work in Alfred on every standard Mac configuration.

最終的にはGitHubにアップロードした。

作ったテーマは以下の3つ。

f:id:ringo6119:20161003001843p:plain

f:id:ringo6119:20161003001849p:plain

f:id:ringo6119:20161003001856p:plain

やっぱ、細いフォント綺麗だね。Retinaあってこそだけど。

よかったらどうぞ(Hiragino Sans 使ってます)。

沖縄の岩場 読谷ボルダーと瀬底ボルダー

ぼくは、趣味であるスキューバダイビングをするため、年に一度くらいのペースで沖縄に行く。 さらに、もう一つの趣味であるボルダリングもついでにやろうと思い登れる岩場を調査した。調べてみると、あまり多くの情報は無く(あっても古く)トポなどは存在しなかった。 沖縄のボルダーを登る人は少ないかもしれないが、探している人のためにメモを書いておこうと思う。今回は名護に滞在したため、読谷と瀬底ボルダーに行った(前回は具志頭に行った)。

トポについて

以前はコーラルロックというところでトポが販売されていた模様。「ぶり」(具志頭ボルダーのトポ)も「Coral on the beach 沖縄ボルダリング課題集」(沖縄本島のトポ)も販売していないようだ。

前回沖縄に訪れたときは、具志頭ボルダーに行ったときに地元のボルダーの方がトポ(ぶり)を持っていたので観せてくれて大変助かった。トポを持っていない状態ではYoutubeの動画を観ながらラインや岩を探すしかないのが現状。

少しでも参考になるように(自分で忘れないために)、場所だけメモしておく。 また、余裕があったら写真も撮ってきたので何処かにメモ用のトポをアップロードしようと思う。

トポ情報お待ちしております(切実)。

岩質

岩は琉球石灰岩という沖縄特有のもので、登ると凄い痛い。岩肌が鋭利なうえ、課題はバルジやルーフが多くとにかく痛い。テーピングが必須とされる。親指以外の指の腹に細いテーピングを巻きつけるとよい。膝とかをぶつけるとすぐ血が出るので注意。

読谷ボルダー

ホテル日航アリビラ」の隣(?)のビーチにある「アリビラボルダー」と、近くにある「体験王国むら咲むら」の「むら咲むらビーチ」の隣りにある「熟女岩」からなるボルダーで、今回はアリビラボルダーしか行けなかった。

最初は、それっぽい岩がありすぎて、どの岩が「それ」なのか全く分からず30分くらいマットを背負って浜辺を歩き回った。

f:id:ringo6119:20160929123113p:plain f:id:ringo6119:20160929123123p:plain

それっぽいのが見つかったのがこれ。おそらく右側がアリビラボルダーで、左側もなんだか少し登った形跡がある。

f:id:ringo6119:20160920143534j:plain

右側の岩は、写真右上のヤシの木(?)と、左下の三角形のように黒く飛び出している岩の形が特徴だと思う。あと、濡れてる。

f:id:ringo6119:20160920144518j:plain

下地は岩がむき出しだったが、平らだったのでブルーシートとマットを敷いた。 正直課題はどこになにがあるかよくわからなかったので、適当に登ってYoutubeにあった動画から「無名4級」を登った。他にも初段とか熟女岩の方も挑戦したかったが、この後名護まで行かなければならず、時間がなくて撤退。

f:id:ringo6119:20160929122931p:plain

地図はこんな感じ。

もっと、やりたかった。。。あと、トポがほしい。。

参考動画・サイト

瀬底ボルダー

ここもあまり情報は無かったので、Youtubeの動画をもとに岩を探してそれっぽいのを見つけた。この後、ヘリオス酒造の酒造見学があったので、滞在時間が30分しかなく悲しかった。酒造見学に行かずここで登っていたかったがドライバーがぼくしかいなかったので断念(そして、見学に行ってもドライバーなので飲めない)。

瀬底ビーチの駐車場(1,000円)に車を停め5分くらい浜辺を北に歩く。どれも登れそうで、時間があれば課題とか気にせず登ってみたかった。

こういうルーフとかがたくさんあった。

f:id:ringo6119:20160922091033j:plain

すると、動画に写っているっぽい岩が見つかった。写真右側の手前に写っている、ふたコブの岩がそうだと思う。水の流れたような黒い跡や、左奥に写っているヤシの木の生えた岩が目印。

f:id:ringo6119:20160922092033j:plain

ところが動画を見てみると、スタートはもっと下からしているようにみえる。どうやら砂で1mほど埋まってしまっている模様。仕方ないので出来るところからスタートして登れるところまで登った。動画も一つしか見つからず、時間もないのですぐ撤退。

地図はこんな感じ。

参考動画

まとめ

今回は、名護周辺のボルダーを回った。まだ、北側や東側のボルダーは行ったことがないので調査して行ってみたい。具志頭も行きたかったが真逆だったため断念。岩場の規模としては具志頭ボルダーが一番大きいようだ。

課題が設定されていない場所(そもそもトポがないからよくわからないが)じゃなくても登って楽しめる岩がたくさんあるのでまた行きたい。あとチョークは一応持っていったけど全く使わなかった。ゴミは残さないように。

最後に、沖縄ボルダリングマップを置いておきます。

参考文献

Terraform に入門してみる

Terraformを使うのでとりあえず入門記事を漁って見る。

とりあえず、 Terraform簡易チュートリアル on AWS - Qiita をハンズオンしてみる。

事前準備

AWS アクセスキーの発行

公式ドキュメントを参照(アクセスキー ID と秘密アクセスキーの取得 )して、アクセスキーとシークレットアクセスキーを発行する。

また、 aws-cli をインストールして aws configure を実行しておく。

Terraformのインストール

OSXなのでhomebrewを使ってインストール

brew update && brew install terraform

インストール出来た。

$ terraform -v
Terraform v0.6.14

Your version of Terraform is out of date! The latest version
is 0.6.15. You can update by downloading from www.terraform.io

AWSにEC2インスタンスを立ててみる

Terraform の設定ファイルを書く

「Terraform」の設定ファイルは「JSON」か「HCL」のどちらかで記述できる。 JSON の場合は *.tf.json 、 HCL の場合は *.tf という拡張子をつける。 HCL は「HashiCorp Configuration Language」の略で、設定ファイルを記述するために HashiCorp が独自に設計したシンタックスJSON と比較して可読性がよいらしい(いちいち{}でくくらなくてもよいのもメリット)。

とりあえず、 git init して書き始めよう。

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_instance" "hello-aws" {
  ami = "ami-a21529cc"
  instance_type = "t2.micro"
}

この、 provider とか resource をブロックを呼ぶらしい。

まず、 provider を記述する。今回は AWS に構築をしてみるので aws を指定指定する。 プロバイダの一覧は公式ドキュメント参照access_keysecret_keyaws configure で設定していない場合はここで記述する。 reagion には対象のリージョンを指定する(リージョンとアベイラビリティーゾーン)。東京は ap-northeast-1 なので、これを指定。

resource ブロックは引数を二つとり、第一引数に「作成するリソースのタイプ」を、第二引数に「リソース名」を指定する。プロバイダが AWS の場合に指定できるリソースは公式ドキュメント参照(AWS PROVIDER)。 とりあえずチュートリアルに習って、 EC2 のインスタンスを作成してみる。

リソースブロックの第一引数に aws_instance 、第二引数の名前に hello-aws を記述(AWS: aws_instance - Terraform by HashiCorp)。

ドキュメントをみると、この aws_instance には必須項目が二つある。

AMI はなんとなく ami-a21529ccUbuntu (無料枠の対象)を指定してみる。 インスタンスタイプは t2.nano が一番安いんだけど、無料枠の対象の t2.micro を指定。

インスタンスを起動する

plan で Dry Run してみる

terraform のサブコマンドに plan というのがある。このコマンドを実行することにより、どのような変更が適用されるか事前に確認することができる。

terraform plan
Refreshing Terraform state prior to plan...


The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_instance.hello-aws
    ami:                      "" => "ami-a21529cc"
    availability_zone:        "" => "<computed>"
    ebs_block_device.#:       "" => "<computed>"
    ephemeral_block_device.#: "" => "<computed>"
    instance_state:           "" => "<computed>"
    instance_type:            "" => "t2.micro"
    key_name:                 "" => "<computed>"
    placement_group:          "" => "<computed>"
    private_dns:              "" => "<computed>"
    private_ip:               "" => "<computed>"
    public_dns:               "" => "<computed>"
    public_ip:                "" => "<computed>"
    root_block_device.#:      "" => "<computed>"
    security_groups.#:        "" => "<computed>"
    source_dest_check:        "" => "1"
    subnet_id:                "" => "<computed>"
    tenancy:                  "" => "<computed>"
    vpc_security_group_ids.#: "" => "<computed>"


Plan: 1 to add, 0 to change, 0 to destroy.

Note が表示された。

Note: You didn’t specify an “-out” parameter to save this plan, so when “apply” is called, Terraform can’t guarantee this is what will execute.

これは、「 out パラメータを指定してプランを保存しなかったら、 apply 呼ばれた時に何を実行するか保証できないよ!」ってことだと思う。多分、 plan した後に別の人が別のところで apply した場合は、今回行った plan は意味なくなるから気をつけろよってことだと思う。

あとで、実験しよう。

あと、 terraform.tfstate というファイルが作成される。これは、 Terraform が管理しているリソースを JSON で保存しておくファイルらしい。 いまは、何も適用してないから空っぽいファイルができてるね。

{
    "version": 1,
    "serial": 0,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {}
        }
    ]
}

apply で適用する

apply のサブコマンドを実行すると、 *.tf ファイルの記述に従って処理が適用される。

$ terraform apply
aws_instance.hello-aws: Creating...
  ami:                      "" => "ami-a21529cc"
  availability_zone:        "" => "<computed>"
  ebs_block_device.#:       "" => "<computed>"
  ephemeral_block_device.#: "" => "<computed>"
  instance_state:           "" => "<computed>"
  instance_type:            "" => "t2.micro"
  key_name:                 "" => "<computed>"
  placement_group:          "" => "<computed>"
  private_dns:              "" => "<computed>"
  private_ip:               "" => "<computed>"
  public_dns:               "" => "<computed>"
  public_ip:                "" => "<computed>"
  root_block_device.#:      "" => "<computed>"
  security_groups.#:        "" => "<computed>"
  source_dest_check:        "" => "1"
  subnet_id:                "" => "<computed>"
  tenancy:                  "" => "<computed>"
  vpc_security_group_ids.#: "" => "<computed>"
aws_instance.hello-aws: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

作成されたっぽい。 computed の部分は特になにも指定してないので動的に作成される(っぽい)。 インスタンスの詳細を見るには show を使えって書いてあるので実行してみる。

$ terraform show
aws_instance.hello-aws:
  id = i-4a10aed5
  ami = ami-a21529cc
  availability_zone = ap-northeast-1a
  ebs_block_device.# = 0
  ebs_optimized = false
  ephemeral_block_device.# = 0
  iam_instance_profile =
  instance_state = running
  instance_type = t2.micro
  key_name =
  monitoring = false
  private_dns = ip-172-31-4-134.ap-northeast-1.compute.internal
  private_ip = 172.31.4.134
  public_dns = ec2-52-192-1-94.ap-northeast-1.compute.amazonaws.com
  public_ip = 52.192.1.94
  root_block_device.# = 1
  root_block_device.0.delete_on_termination = true
  root_block_device.0.iops = 24
  root_block_device.0.volume_size = 8
  root_block_device.0.volume_type = gp2
  security_groups.# = 0
  source_dest_check = true
  subnet_id = subnet-8b78affc
  tags.# = 0
  tenancy = default
  vpc_security_group_ids.# = 1
  vpc_security_group_ids.1967208828 = sg-9257fef7

なんかイロイロ表示されましたね。 ちゃんと起動してるかチェック。

$ aws ec2 describe-instances --instance-ids "i-4a10aed5" | jq '.Reservations[].Instances[] | {"Image ID": .ImageId, "Instance ID": .InstanceId, "Status": .State.Name}'
{
  "Image ID": "ami-a21529cc",
  "Instance ID": "i-4a10aed5",
  "Status": "running"
}

起動してるっぽい。

インスタンスを変更する

ためにしに、 AMI を Ubuntu から Amazon Linux に変更してみる。

diff --git hello-aws.tf hello-aws.tf
index cbe294a..62aeca0 100644
--- hello-aws.tf
+++ hello-aws.tf
@@ -3,6 +3,6 @@ provider "aws" {
 }

 resource "aws_instance" "hello-aws" {
-  ami = "ami-a21529cc"
+  ami = "ami-f80e0596"
   instance_type = "t2.micro"
 }

terraform plan を実行。

Refreshing Terraform state prior to plan...

aws_instance.hello-aws: Refreshing state... (ID: i-4a10aed5)

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

-/+ aws_instance.hello-aws
    ami:                      "ami-a21529cc" => "ami-f80e0596" (forces new resource)
    availability_zone:        "ap-northeast-1a" => "<computed>"
    ebs_block_device.#:       "0" => "<computed>"
    ephemeral_block_device.#: "0" => "<computed>"
    instance_state:           "running" => "<computed>"
    instance_type:            "t2.micro" => "t2.micro"
    key_name:                 "" => "<computed>"
    placement_group:          "" => "<computed>"
    private_dns:              "ip-172-31-4-134.ap-northeast-1.compute.internal" => "<computed>"
    private_ip:               "172.31.4.134" => "<computed>"
    public_dns:               "ec2-52-192-1-94.ap-northeast-1.compute.amazonaws.com" => "<computed>"
    public_ip:                "52.192.1.94" => "<computed>"
    root_block_device.#:      "1" => "<computed>"
    security_groups.#:        "0" => "<computed>"
    source_dest_check:        "true" => "1"
    subnet_id:                "subnet-8b78affc" => "<computed>"
    tenancy:                  "default" => "<computed>"
    vpc_security_group_ids.#: "1" => "<computed>"


Plan: 1 to add, 0 to change, 1 to destroy.

新しいリソースを作るみたいな表示になってる。

ami: "ami-a21529cc" => "ami-f80e0596" (forces new resource) Plan: 1 to add, 0 to change, 1 to destroy.

terraform apply を実行。

aws_instance.hello-aws: Refreshing state... (ID: i-4a10aed5)
aws_instance.hello-aws: Destroying...
aws_instance.hello-aws: Destruction complete
aws_instance.hello-aws: Creating...
  ami:                      "" => "ami-f80e0596"
  availability_zone:        "" => "<computed>"
  ebs_block_device.#:       "" => "<computed>"
  ephemeral_block_device.#: "" => "<computed>"
  instance_state:           "" => "<computed>"
  instance_type:            "" => "t2.micro"
  key_name:                 "" => "<computed>"
  placement_group:          "" => "<computed>"
  private_dns:              "" => "<computed>"
  private_ip:               "" => "<computed>"
  public_dns:               "" => "<computed>"
  public_ip:                "" => "<computed>"
  root_block_device.#:      "" => "<computed>"
  security_groups.#:        "" => "<computed>"
  source_dest_check:        "" => "1"
  subnet_id:                "" => "<computed>"
  tenancy:                  "" => "<computed>"
  vpc_security_group_ids.#: "" => "<computed>"
aws_instance.hello-aws: Creation complete

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

確認してみる。

$ terraform show | grep " id ="
  id = i-1cd06883
$ aws ec2 describe-instances --instance-ids "i-4a10aed5" "i-1cd06883" | jq '.Reservations[].Instances[] | {"Image ID": .ImageId, "Instance ID": .InstanceId, "Status": .State.Name}'
{
  "Image ID": "ami-a21529cc",
  "Instance ID": "i-4a10aed5",
  "Status": "terminated"
}
{
  "Image ID": "ami-f80e0596",
  "Instance ID": "i-1cd06883",
  "Status": "running"
}

古い方は terminated になってるし、新しい方は違う AMI ID で running になってる。

インスタンスの削除

インスタンスの削除は plan-destroy オプションをつけてプランファイルを作成し、 apply 実行時にプランファイルを指定して実行する。

プランファイルを作成して。

$ terraform plan -destroy -out=destroy.tfplan
efreshing Terraform state prior to plan...

aws_instance.hello-aws: Refreshing state... (ID: i-1cd06883)

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.

Your plan was also saved to the path below. Call the "apply" subcommand
with this plan file and Terraform will exactly execute this execution
plan.

Path: destroy.tfplan

- aws_instance.hello-aws


Plan: 0 to add, 0 to change, 1 to destroy.

削除。

$ terraform apply ./destroy.tfplan
aws_instance.hello-aws: Destroying...
aws_instance.hello-aws: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

確認してみると両方とも terminated になってた。

$ aws ec2 describe-instances --instance-ids "i-4a10aed5" "i-1cd06883" | jq '.Reservations[].Instances[] | {"Image ID": .ImageId, "Instance ID": .InstanceId, "Status": .State.Name}'
{
  "Image ID": "ami-a21529cc",
  "Instance ID": "i-4a10aed5",
  "Status": "terminated"
}
{
  "Image ID": "ami-f80e0596",
  "Instance ID": "i-1cd06883",
  "Status": "terminated"
}

References

ES2015(ES6) でスクリプトを書ける Hubot 環境を作る

Hubotのスクリプトを書くとき、今更coffeeは辛いのでES2015(ES6)で運用できる環境を作りたくなりました。

STEP1: Hubotのスケルトンを作成する

まず、Hubotのスケルトンを作成します。公式ドキュメント がよいです。

本当は対話的に各項目を埋めていくんだけど、オプションで指定するとコマンド一発でいけます。サイコーですね。

npm install -g yo generator-hubot
mkdir aokibot && cd aokibot && \
  yo hubot \
    --owner="Yoshiki Aoki <yoshiki_aoki@example.com>" \
    --name="aokibot" \
    --description="Excellent working bot" \
    --adapter=slack

試しに実行するにはbin/hubotで起動します。

STEP2: ES2015の環境を整える

babelを使ってES2015で書いたコードをES5に変換します。 変換に必要なモジュールをnpmでインストール。それと、ES2015のソース用ディレクトリとビルド後のディレクトリを作成しておきます。

npm i --save-dev babel babel-cli babel-preset-es2015
mkdir src builds

STEP3: サンプルスクリプトを書いてみる

src/example.jsにES2015の文法を使って書いたサンプルスクリプトを追加します。

// Description:
//  Example scripts for you to examine and try out.
//
// Notes:
//  They are commented out by default, because most of them are pretty silly and
//  wouldn't be useful and amusing enough for day to day huboting.
//  Uncomment the ones you want to try and experiment with.
//
//  These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md

module.exports = (robot => {
  robot.hear(/hello/i, res => {
    res.send("World!!");
  });
});

Hello World!!

STEP4: 起動設定をする

ビルドして起動してってあんまりゴチャゴチャさせたくないですよね。なので、トランスパイル、起動はnpm経由でやるのがスマートです。

package.jsonにビルドや起動の設定を追加します。

まず、run-scriptbuildを追加してトランスパイルを行います。

"build": "babel src --presets es2015 --out-dir ./builds"

そして、起動スクリプトにbin/hubotを指定します。この時、-rオプションでbuildsディレクトリを指定してやることによって、素の(?)スクリプトはscripts、ES2015からトランスパイルされたスクリプトはbuildsと住み分けることができます。

"start": "bin/hubot -r builds"

最後に起動するときはnpm run build && npm startとすれば起動します。 が、そんなダサい方法は使いたくありません。 npm startだけで終わらせたいので、prestartnpm run buildを指定してあげます。

"prestart": "npm run build"

トランスパイルしてから起動するコマンドはnpm startだけでよくなりました。スマートなのはいいですね。

最後に差分貼っておきます。

diff --git a/package.json b/package.json
index 71768eb..879620b 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,11 @@
   "private": true,
   "author": "Yoshiki Aoki <yoshiki_aoki@example.com>",
   "description": "Excellent working bot",
+  "scripts": {
+    "build": "babel src --presets es2015 --out-dir ./builds",
+    "prestart": "npm run build",
+    "start": "bin/hubot -r builds"
+  },
   "dependencies": {
     "hubot": "2.18.0",
     "hubot-help": "0.1.3",

Problems

deprecated documentation syntax というINFOメッセージが出る

トランスパイル後のファイルには先頭に"use strict"がついている。 これが、原因で builds/example.js is using deprecated documentation syntax というメッセージが出てしまう。悪影響は全くないけども、解決する方法があるのであれば解決したいですね。

References

Amazon Fire TV Stick のリモコンを忘れた話

正月は実家に帰ってAmazon Fire TV Stick 海外ドラマ見てダラダラ過ごす他ほかないと計画し、TV Stick を忘れずにカバンに入れて安心しきっていた。いざ、ゴロゴロしようと思ったらリモコンを忘れていたことに気づいた*1

色々調べてみると、BRAVIA とかだとテレビのリモコンがそのまま使えるって書いてあったので、家にあった REGZA 37H3100 で試してみたが全然反応しなかった。もう一個小さいテレビ(REGZA 19A2)があったので、それで試すとちゃんとTVのリモコンでも反応して大勝利だった。

そいつでWi-Fiを設定したら、あとは、iPhoneAmazon Fire TV Remote を認証してiPhoneから操作することができた。

リモコンを忘れてもなんとかなるという知見が得られて非常によかった。 でも、やっぱりリモコンのほうがいいね。アプリめっちゃ操作し辛い。

References

*1:もちろんArduinoも忘れた

dockerがVPNのせいで繋がらなくなった時は面倒くさい

VPNとか繋いだりすると、詳しいことはよくわからないがdockerコマンドが返ってこないことがよくある。

そういう時は、次のコマンドを打つと直る。

boot2docker down
sudo route -nv delete -net 192.168.59 -interface vboxnet1
sudo route -nv add -net 192.168.59 -interface vboxnet1
boot2docker up

それも面倒くさいので、.zshrcに以下の様なエイリアスを付けてやると楽。

alias redock="boot2docker down && sudo route -nv delete -net 192.168.59 -interface vboxnet1 && sudo route -nv add -net 192.168.59 -interface vboxnet1 && boot2docker up"

redock を打つだけで再起動できる。

References

OSX docker のセットアップ

今更、書くことでもないが。 基本的に公式ドキュメント見ればいいと思う。

VertualBoxが必要なので、予めインストールしておくとよい。

インストール

インストールは homebrew でサックっと行う。

brew update
brew install boot2docker

OS起動時に起動設定するので、書いてある通り実行する。

To have launchd start boot2docker at login:
    ln -sfv /usr/local/opt/boot2docker/*.plist ~/Library/LaunchAgents
Then to load boot2docker now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.boot2docker.plist

環境変数の設定

今までは

export DOCKER_HOST=tcp://192.168.59.103:2376
export DOCKER_CERT_PATH=${HOME}/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1

を書いてたけど、

shellinitコマンドができてから、

eval "$(boot2docker shellinit)"

でよくなったみたい。

これだと、毎回シェル起動するたびに Writing ~/.boot2docker/certs/boot2docker-vm/ca.pem みたいに出てきてうざいので、少しアレンジして以下の行をrcなどに追加するとよい。

eval "$(boot2docker shellinit 2> /dev/null)"

ただ、毎回Writing~も実行されてるっぽくて気持ち悪い場合は、最初のexport形式に戻すことになる。

実験。

$ time (eval "$(boot2docker shellinit 2> /dev/null)")
( eval "$(boot2docker shellinit 2> /dev/null)"; )  0.05s user 0.04s system 38% cpu 0.244 total
$ time (export DOCKER_HOST=tcp://192.168.59.103:2376 && export DOCKER_CERT_PATH=${HOME}/.boot2docker/certs/boot2docker-vm && export DOCKER_TLS_VERIFY=1 )
( export DOCKER_HOST=tcp://192.168.59.103:2376 && export  && export ; )  0.00s user 0.00s system 61% cpu 0.001 total

exportのほうがいいね。