- 投稿日:2019-10-02T22:40:08+09:00
golangにおけるJSONPの解析
How would I parse JSONP!!
https://www.reddit.com/r/golang/comments/a1oqxp/how_would_i_parse_jsonp/
上記は、
callback関数に囲まれたJSONPの形で返ってくるんですけど......
Unmarshalできなくて困るんですけど......
どうやってJSONP
toJSON
toStruct
しましょう...という記事です。例)
func main() { jsonpStr := ` callback({ "hello": "world!!", "num": 123456 });` var v interface{} err := json.Unmarshal(jsonpStr, &v) fmt.Println(err) // invalid character 'c' looking for beginning of value // JSONを期待しているのに先頭にcallbackという不要な文字列があるためUnmarshalがエラーを返します。 }対応方法
一番上に乗せた
titpetricさんの対応方法
先頭から
(
までと、後ろから)
までをtrimしちゃえYO!という方法です。jsonpStr := `callback({ "hello": "world!!", "num": 123456 });` jsonStr := jsonpStr[strings.Index(jsonpStr, "(")+1 : strings.Index(jsonpStr, ")")] fmt.Println(jsonStr) // { // "hello": "world!!", // "num": 123456 // } var v interface{} json.Unmarshal([]byte(jsonStr), &v) fmt.Printf("%+v", v) // map[hello:world!! num:123456]jerfさんの対応方法
io.ReaderのWrapperとして用意しようYO!という方法です。
type JSONPWrapper struct { Prefix string Underlying io.Reader gotPrefix bool } func (jpw *JSONPWrapper) Read(b []byte) (int, error) { if jpw.gotPrefix { return jpw.Underlying.Read(b) } prefix := make([]byte, len(jpw.Prefix)) n, err := io.ReadFull(jpw.Underlying, prefix) if err != nil { return n, err } if string(prefix) != jpw.Prefix { return n, fmt.Errorf("JSONP prefix mismatch: expected %q, got %q", jpw.Prefix, prefix) } // read until the (; in general, this should just be one read char := make([]byte, 1) for char[0] != '(' { n, err = jpw.Underlying.Read(char) if n == 0 || err != nil { return n, err } } // We've now consumed the JSONP prefix. jpw.gotPrefix = true return jpw.Underlying.Read(b) } func main() { jsonpStr := `callback({ "hello": "world!!", "num": 123456 });` prefix := "callback" jsonp := bytes.NewBuffer([]byte(jsonpStr)) var decoded interface{} decoder := json.NewDecoder(&JSONPWrapper{Prefix: prefix, Underlying: jsonp}) // This code depends on the fact the JSON parser stops when it // finishes a JSON object, so we don't have to handle the concluding // paren. decoder.Decode(&decoded) fmt.Println(decoded) // map[hello:world!! num:123456] }
json.NewDecoder
の引数にJSONPWrapper(io.Readerインターフェースを満たした)
を渡すことにて、
decoder.Decode
のタイミングにてJSONWrapper.Read()
が実行されます。
JSONWrapper.Read()
の中では、Prefix
に設定された文字列をjsonp
文字列の先頭から比較して削除しています。
- 投稿日:2019-10-02T21:38:33+09:00
UbuntuでGo書きたい男
11月に転職が決まりました。
次の会社から「Go言語とAWSを調べてきて」というざっくりした指令をいただき、それを遂行したい男、私です。とりあえずGo!!
まずはGo言語の勉強からやろうと思います。
言語の特性を知るだけならまぁそんな時間かからんでしょーUbuntuで書きたいわ〜
Windows嫌いです。特に10から。理由はOSアップデートだったり、アカウント切り替えだったり、設定の面倒さだったり。
Macもいいですが、新しいのは高くて買えないので既存のWindowsノートをUbuntu18.04LTSに入れ替えました。
最初はGNOME3で使ってたんですが、KDEが気になったのでそっちに変更。今のところいい感じ。環境設定
・自前ファイルサーバーをマウント
・仮想デスクトップを2x2に設定
・電源管理でディスプレイが暗くなるまでの時間とかを伸ばす
・.vimrc設定
などショートカットキー
・ターミナル起動
・仮想デスクトップ上下左右に移動
・ウィンドウを仮想デスクトップ上下左右に移動
など
参考
何も考えず~/.vimrcにこれを書くんだ! 〜vim初心者によるvim初心者のためのvim入門〜
Go環境を入れたいわ〜
UbuntuにGoを入れてパス通すマン
参考
Atomかっこいいわ〜
エディタはAtom使います。かっこいいからですね。
本体、日本語化パッケージ、Go用のパッケージをインストール。参考
テキストエディタ「Atom」のインストールから日本語化まで
Go言語はじめましたできたわ〜
- 投稿日:2019-10-02T21:26:07+09:00
Goの練習 PHPerが業務用Webアプリケーションでありそうな処理をGoと速度比較してみる
はじめに
PHPerとして仕事を始めて6年半(2019年現在)。様々な現場で業務用Webアプリケーションの構築に携わってきました。
Webアプリケーションであれば業務で使うような大体の機能はPHPで実現できるのですが、1システムに1機能はPHPだと物足りないというか、痒い所に手が届かないなと思う箇所はどうしても出てきます。顕著にそれを思うのは大量データの処理などのパフォーマンスを発揮したい機能です。
PHP7の登場でPHP5と比較して格段にパフォーマンスが向上しました。ですがそれでももっと速く処理したいと思う場面がしばしばあります。そこで自身のスキルの幅を広げることも含めて、スクリプト言語のように記述出来てハイパフォーマンスが期待できるGoを学んでみようと思いました。
本記事の概要
経験上Webアプリケーションでありそう、かつ処理が重そうな機能を考えた結果、CSVアップロード/ダウンロード機能が思いつきました。
今回はフロント部分は省略して、コマンドラインでCSVをDBに保存する機能とDBからCSVを作成する機能をGoとPHPで作成し、そのパフォーマンスの差を検証します。
諸先輩方が通ってきた道かと思いますが、自学のため。機能の概要
アップロード
- あらかじめ用意したCSVを読み込み、DBにアップロードする。
- 指定した件数をひとかたまりとして処理する
- 新規追加フラグを設け、これが1の場合は新規追加とみなしDBとの重複チェックを行う。
使用したCSVは国交省提供の位置参照情報ダウンロードサービスから全自治体のデータをダウンロードし加工したものになります。
およそ21万件ほど。UPSERTをするようにしたので結局重複チェックは意味をなさないのですが、それっぽく作るため入れてみました。
ダウンロード
- DBからデータを全量取得し、CSVに出力する。
- 指定した件数をひとかたまりとして処理する
こちらの方が純粋なパフォーマンス比較ができそうです。
両方の機能でGoでは並行処理での処理を指定可能。並行処理では2スレッドで処理します。
やらないこと
細かいことは抜きにする方針にします。
- フロント部分(GUI)の作成
- 細かいバリデーション
- アップロード用CSVの存在チェック
- 形式チェック
- SQLインジェクション対策
等々
ハマったら容赦なくOSSライブラリを使おうと思います。
逆に言えばハマるまでは標準パッケージで頑張ります。実行環境等
サーバ
Amazon Linux 2 AMI (HVM), SSD Volume Type
t2.medium(CPU 2コア メモリ 4GB)言語
go v1.13
php v7.2.22DB
MySQL 5.7AmamzonLinuxの上にそれぞれのアプリケーションとDBのDockerコンテナを建てて実行しています。
実行結果
ソースは→ GitHub
GNU版timeコマンドを利用しています。Goはコンパイル済みのファイルを実行。
まずはアップロードから。重複エラーがないパターン。
言語 real user sys 消費メモリ Go 7.91s 1.00s 0.05s 3.62MB Go(並行処理) 4.18s 1.00s 0.09s 5.65MB PHP 7.00s 1.61s 0.11s 6.32MB 全件重複エラーするパターン。
言語 real user sys 消費メモリ Go 8.46s 1.20s 0.30s 3.62MB Go(並行処理) 5.27s 1.23s 0.23s 6.18MB PHP 8.91s 1.90s 0.15s 16.99MB 次はダウンロード
言語 real user sys 消費メモリ Go 0.92s 0.74s 0.05s 2.42MB Go(並行処理) 0.70s 0.82s 0.04s 3.97MB PHP 1.07s 0.54s 0.24s 7.47MB グラフにしてみます。
実際に処理にかかった時間。意外とPHPと差はない、どころか逐次実行の場合はGoがPHPより1秒ほどかかっています。並行処理の場合は流石に速いですね。
CPUが処理をした時間。並行処理と逐次処理の間に大きな差はなし。アップロードにおいてはPHPの方が占有時間が長いようです。
OSがシステムコールに使った時間。正直なところ語れるほど詳しくないのですが場合によりけりといった印象です。所感
測定結果に関して
当初想定していたよりもGoとPHPの差はあまりありませんでした。実際の処理時間に焦点を当てると場合によってはPHPの方が速かったりということもあり、驚きました。
私自身がGo初学者というのもあり、パフォーマンスチューニングができていない可能性も高いです。
ただし並行処理になるとやはりというべきか結構な時間短縮になりますね。メモリ消費量はGoの方がパフォーマンスが良さそうなので、同じ処理でもサーバの負荷を抑えられるなどのメリットはありそうです。
ここはスクリプト言語とコンパイル形式の言語の違いもありそうですが。データ量が増えたりするとまた差がでたりするのでしょうか。
実装に関して
- Scan関数の引数で取得したカラムを一つ一つ指定して変数に入れてあげないといけないのは少し面倒。
- DBから取得したデータをうまいことCSVに書き出す方法を見つけるのにハマったので結局gocsvに頼りました。
- エラーハンドリングはもう少ししっかりやった方がいいかなと思いました。特に問答無用のexitは実務レベルではあまりやらないと思います。
- アップロードで指定した件数をひとかたまりとしてに重複チェックと投入をしていますが、1件でも重複チェックに引っかかったかたまりは投入されず、引っかからなかったかたまりは投入されるというよくわからない状況になっています。業務では引っかからなかったデータだけ投入するとか、エラーが1件でもあったら投入しないという仕様にするのが妥当だと思います。完成してから気づきました… 今回は重複チェック意味ないのでご容赦を。
- 今回Goの実装に費やした時間は余暇時間を使って環境構築したりハマったり調べたりを繰り返し、大体1週間ぐらいでできました。時間がかかるのは想定していましたが、それでも結構時間がかかった印象です。本人のスキルが多分にありそうですが、毛色の違う言語から来たときの学習コストがなんとなくわかりました。PHPの方は1時間もかからずにできました。
なんとなく触ることはできたので、次は何か開発してみたいと思います。いつできるかはわかりませんが。
- 投稿日:2019-10-02T20:06:59+09:00
cli-kintoneでやってみた「後で追加した計算フィールドに一括で値を反映する方法」
cybozu developer network の kintone Tips に、
後で追加した計算フィールドに一括で値を反映する方法
という記事があります。今回、Go製の kintoneコマンドラインツール cli-kintone を使って、コードを書かずワンライナーで自動計算フィールドに値を一括して反映する Tips を公開します。
動作環境
- macOS 10.13.6
- cli-kintone 0.9.4
cli-kintone の準備
cli-kintoneのZipファイルをダウンロード、解凍後に実行ファイルをパスの通ったディレクトリに配置します。
https://github.com/kintone/cli-kintone
https://github.com/kintone/cli-kintone/releaseskintoneアプリの準備
テスト用のkintoneアプリを用意します。
営業支援パック導入
スペースを作り、アプリストアから営業支援パックをサンプルデータを含めて作成します。
案件管理に集計用年月フィールドを追加
文字列1行フィールドを追加して、自動計算を設定します。
一覧の項目に追加したフィールドを表示させておきます。
考え方
下記の仕組みにより計算フィールドのみの更新を可能としています。
- リクエストのJSONデータに記述しないフィールドは更新されません
更新時にテーブルのデータを含まない場合、テーブルのデータは保持されます
cli-kintoneによる処理
処理の流れです。
- レコードIDのみを取得します
- 取得したレコードIDを読み込みます
処理コードと結果
$ cli-kintone -a 123 -t API-Token -d Domain -c "\$id" | cli-kintone --import -a 123 -t API-Token -d Domain Start from lines: 1 - 11 => SUCCESS DONEcli-kintoneの1,2の処理をパイプで繋げて実行する事で、新規に追加した自動計算フィールドが更新されます。
参考リンク
- レコードの一括登録と更新
- [レコードの一括更新] ファイルを編集する
- 入出力できるデータの一覧
- kintone REST API レコードの更新(PUT)
- kintone REST API レコードの更新(PUT)注意事項
- kintone コマンドラインツール Version 0.9.0 新機能紹介
——
- 投稿日:2019-10-02T17:11:50+09:00
Golang - Makefileの書き方
はじめに
Golang開発のタスクランナーとしてMakefileを利用することが多いようなので書き方を整理します。
サンプル
https://github.com/so-heee/golang_example/tree/master/makefile_example
タスク
タスクの実行
terminal$ make taskname
タスクの設定
Makefile.PHONY: taskname taskname: command # 実行コマンド非表示 .PHONY: taskname taskname: @command # タスク定義が複数のコマンド .PHONY: taskname taskname: command1 command2 # 複数コマンドをワンライナーで実行 .PHONY: taskname taskname: command1 && command2 # 上のケースの別の方法 .PHONY: taskname taskname: command1 ; command2 ;\ command3 # タスクの前に別のタスクを実行 .PHONY: taskname3 taskname3: taskname1 taskname2 command # 自身のMakefileに定義してある別のタスク定義を実行 .PHONY: taskname2 taskname2: $(MAKE) taskname1 command.PHONYについて
タスク名と同名のファイルやディレクトリがあるとタスクが実行されません。
.PHONYは省略可能ですが、定義することで上記の条件でも実行が可能になるため
基本的には書いていこうかと思います。変数
Makefile# 変数名=値 VAR=hello make .PHONY: taskname taskname: echo $(VAR) # shell commandの実行結果を変数に格納 NOW=$(shell date) .PHONY: taskname taskname: echo $(NOW) # シェルスクリプト変数・環境変数は$$で参照 .PHONY: taskname taskname: VAR=$$GOPATH && echo $$VAR # タスク実行中に環境変数を書き換えたい場合は、コマンド実行が1行終わるごとに環境変数がmakeコマンド実行時に戻る .PHONY: taskname taskname: export VAR="この値は消えてしまいます" echo VAR=$${VAR} .PHONY: taskname taskname: export VAR="この値は残ります" ;\ echo VAR=$${VAR} # サブディレクトリのMakefileのタスクを実行する .PHONY: taskname taskname: make -C sub_directory sub_task # make実行時に変数の値を渡す(make task VAR=XXX) VAR="これは上書きされる" .PHONY: taskname taskname: echo $(VAR) $$VAROSSプロダクトのMakefileを見てみる
Docker
kubernetes
Terraform
Gobot
Gin.PHONYは全てのタスクで定義
参考のmakefileでは全てのタスクで.PHONYが定義されていました
一括で.PHONYを定義することもできる.PHONY: bin cover default dev e2etest fmt fmtcheck generate protobuf plugin-dev quickdev test-compile test testacc testrace tools vendor-status website website-testよく見かけたタスク
以下のタスクをよく見かけました
- fmt
- test
- lint/vet
- clean
- help
タスクの詳細はshellを用意
複雑なタスクはshellを用意し、Makefileは実行だけするようにしていた箇所がいくつかありました
ifeqで条件分岐
Makefileifeq ($(XXX),y) taskname: command1 else taskname: command2 endif参考
- 投稿日:2019-10-02T16:16:39+09:00
システムプログラミング実践
これはなに?
Goで覗くシステムプログラミングの世界で学んだことをアウトプットする記事
システムプログラミングとは?
以下のような様々定義があるが、今回は一番下の「OSの提供する機能を使ったプログラミング」をシステムプログラミングの定義として進める。
- C言語によるプログラミング
- アセンブリ言語を意識したC言語によるプログラミング
- 言語処理系(インタプリタを含む)、特にネイティブコードを生成するコンパイラの開発
- OS自身のプログラミング
- OSの提供する機能を使ったプログラミング
OSの機能について
一般的なコンピュータに搭載されているOSについて、その機能の最大公約数をとれば、次の機能に集約されるでしょう。
- メモリの管理
- プロセスの管理
- プロセス間通信
- ファイルシステム
- ネットワーク管理
- ユーザ管理(権限など)
- タイマー
Go言語
Go言語は、C言語の性能とPythonの書きやすさ/読みやすさを両立させ、モダンな言語特徴をうまく取り入れた言語となることを目標にGoogleが開発したプログラミング言語。
import "fmt" func main(){ fmt.println("hello world¥n") }デバックをしていく事により、上記プログラムがシステムコールの呼び出しをしていることがわかる。
- 投稿日:2019-10-02T16:08:40+09:00
Go言語からシェルスクリプトが実行できない(シェバングの必要性)
概要
Go言語を用いてシェルスクリプトを実行しようとしたところ、エラーになり実行できなかった。
lsなどコマンド単体での実行はでき、シェルスクリプトのみ実行できなかった。
シェルスクリプト自体は、コンソールログインした状態では実行できる状態であった。原因としては、シェルスクリプトにシェバングがないためであった。
シェルスクリプトにシェバングを追加することで、実行可能となった。実行されるシェルスクリプト
test.shdate "+%Y.%m.%d-%H.%M.%S" >> test.logシェルスクリプトを実行するGoプログラム
gotest.gopackage main import ( "os/exec" "fmt" ) func main() { out,err := exec.Command("/test.sh").Output() if err != nil { fmt.Println("Command Exec Error.") } fmt.Printf("result: \n%s", string(out)) }エラー原因
シェルスクリプトにシェバングが記載されていないため。
コンソールからログインしている場合は、ログイン時のシェルがいい感じにスクリプトを読み取ってくれるため、
シェバングがなくとも、シェルスクリプトは実行できる。Goから実行する場合は、新規プロセスの生成となるため実行可能ファイル以外では、何らかの手段で起動方法を連携する必要がある。
シェバングがないと、なにも情報がないため、実行が不可能となっていた。対応策
シェルスクリプトにシェバングを追加した。
test.sh#!/bin/sh <= 追加 date "+%Y.%m.%d-%H.%M.%S" >> test.log
- 投稿日:2019-10-02T14:31:08+09:00
Gofeed利用時に発生した「Failed to detect feed type」の対応
現象
Cloud Functionsを利用して、gofeedでブログをパースしようとしたところ、以下のメッセージと共にエラーが発生。
Failed to detect feed typemain.gofp := gofeed.NewParser() feed, _ := fp.ParseString(xmlData) // ここでエラー原因切り分けのために条件を変えて実行したところ、以下がわかった。
- エラーが発生しないブログもある
- ローカルで実行するとエラーにならない
環境
- Golang 1.11.0
- gofeed v1.0.0-beta2
- Google Cloud SDK 264.0.0
原因
httpアクセスする際のCloud FunctionsのIPアドレスがブログ側からブロックされてしまったためと思われる。
対応
Cloud Functionsのregionをasia-northeast1からus-east1に変更したところ、正常に処理が完了した。
参考
https://github.com/mmcdole/gofeed/issues/75
https://github.com/mmcdole/gofeed/issues/96
- 投稿日:2019-10-02T11:23:16+09:00
BindJSONとShouldBindJSONの違い(gin)
golang で gin を利用している際に、BindJSON と ShouldBindJSON があって名前が似ててどう違うのか気になったので記事にしてみます。間違ってたらご指摘していただけると嬉しいです。
ginについて
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
引用:https://github.com/gin-gonic/ginGoで扱われている、HTTPのウェブフレームワークです。同じお酒の名前のフレームワーク、Martini(https://github.com/go-martini/martini ) よりもパフォーマンスが良いと評判みたいです。
BindJSON vs. ShoulBindJSON
以下にGoDocに基づく比較を書きます( https://godoc.org/github.com/gin-gonic/gin )
BindJSON ShoulBindJSON 原型:MustBindWith 原型:ShouldBindWith 指定されたバインディングエンジンを使用して、渡された構造体ポインターをバインドする。エラーが発生した場合、HTTP 400でリクエストを中止します。 指定されたバインディングエンジンを使用して、渡された構造体ポインターをバインドする。 つまりはこういうこと↓
ソースコード
context.go//MustBindWith(BindJSON) func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { if err := c.ShouldBindWith(obj, b); err != nil { c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck return err } return nil } //ShouldBindWith(ShouldBindJSON) func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { return b.Bind(c.Request, obj) }ShouldBindJSONに、400番でエラーを返す機能を含んでるのがBindJSONなんですね!
BindJSON と ShouldBindJSON のまとめ
今回学んだ違いは
BindJSON ShoulBindJSON エラー処理の際に400を返してくれる エラー処理が内部では施されていない 独自エラーハンドリング不要 独自エラーハンドリング必要 エラーハンドリングを独自にやりたい場合は ShouldBindJSONを使うのが良さそうですね!
学びました。参考
gin(GoDoc):https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindJSON
- 投稿日:2019-10-02T08:24:30+09:00
kintoneコマンドラインツール cli-kintone を使ってkintoneクエリを理解する
説明
cli-kintoneは、kintoneアプリのデータをファイルに入出力することができるコマンドラインツールです。
Windows、Linux、Mac OS X 版があります。
https://github.com/kintone/cli-kintoneZipファイルを解凍後に、実行ファイルをパスの通ったディレクトリに設置します。
コマンドラインからcli-kinton
を打ち込んで Usage が表示されることを確認します。日本語解説
https://developer.cybozu.io/hc/ja/articles/202957070英語解説
https://developer.kintone.io/hc/en-us/articles/115002614853cli-kintoneは Go言語で書かれています。ソースから実行ファイルを生成することも出来ます。
オプション一覧
Usage
Usage: cli-kintone [OPTIONS] Application Options: -d= Domain name (specify the FQDN) 必須。cybozuのドメインを指定します。例) -d sample -a= App ID (default: 0) (必須)アプリのIDを指定します。例) -a 123 -u= User's log in name (準必須)kintoneにアクセスする際にログインパスワードを使うときに指定 例) -u user -p= User's password(準必須)kintoneにアクセスする際にログインパスワードを使うときに指定 例) -p password -t= API token(準必須)kintoneにアクセスする際にAPIトークンを使うときに指定 例) -t APIToken -g= Guest Space ID (default: 0) (任意)kintoneのゲストスペースにアクセスする場合に指定 -o= Output format. Specify either 'json' or 'csv' (default: csv) (任意)出力形式を指定。省略時はCSVです。 -e= Character encoding. Specify one of the following -> 'utf-8'(default), 'utf-16', 'utf-16be-with-signature', 'utf-16le-with-signature', 'sjis' or 'euc-jp' (default: utf-8)(任意)出力形式の文字コードを指定。デフォルトはUTF-8です。 -U= Basic authentication user name (任意)アクセスするkintoneドメインにBASIC認証が設定されている場合に指定。パスワードとセットで使う。 -P= Basic authentication password(任意)アクセスするkintoneドメインにBASIC認証が設定されている場合に指定。ユーザー名とセットで使う。 -q= Query string (任意)アプリに渡すクエリ文字列。SQLライクな検索絞り込みが出来ます。例) -q "limit 10" -c= Fields to export (comma separated). Specify the field code name (任意)出力する際のフィールドコードをカンマ区切りで指定。省略した場合は全てのフィールドが出力されます。例) -c "$id,文字列1行" -f= Input file path(任意)CSVファイル等からレコードを追加・更新する場合にファイル名を指定。例) -f ./import.csv -b= Attachment file directory (任意)添付ファイルをダウンロードする場合に、ダウンロードするディレクトリを指定。ディレクトリは自動作成される。 -D Delete records before insert. You can specify the deleting record condition by option "-q"(任意)レコードを削除する場合に指定。-q オプションを組み合わせて、クエリで絞り込んだ特定のレコードを削除することができる。 -l= Position index of data in the input file (default: 1) (任意)インポートするファイルの読み込み位置を指定することができる。 --import Import data from stdin. If "-f" is also specified, data is imported from the file instead(任意)指定すると標準入力からデータを読み込む。 --export Export kintone data to stdout(任意)指定すると標準出力にレコードを出力する。 Help Options:(任意)cli-kintoneのオプションを表示する。 -h, --help Show this help message実行例
テスト環境は macOS 10.13.6
APIトークンを使って最初の10行をCSVで取得する
cli-kintone -a 123 -t API-Token -d Domain -q "limit 10"
APIトークンを使って全てのレコードをCSVでバックアップする
cli-kintone -a 123 -t API-Token -d Domain > backup.csv
Mac OS X にてファイルを確認する
head backup.csv
ファイルの先頭から10行表示
tail backup.csv
ファイルの最後から10行表示レコードIDのみをCSVで取得する
cli-kintone -a 123 -t API-Token -d Domain -c '$id'
または、ダブルクォーテーションで囲む場合は、$をバックスラッシュでエスケイプする。
cli-kintone -a 123 -t API-Token -d Domain -c "\$id"シェルのパイプと組み合わせて、ヘッダー行のみを取得する
cli-kintone -a 123 -t API-Token -d Domain | head -1
シェルのパイプと組み合わせて、新規に追加した自動計算フィールドを更新する
自動計算フィールドを後で追加した場合、レコードの再保存を繰り返す事無く一気に更新を掛けます。
cli-kintone -a 123 -t API-Token -d Domain -c "\$id" | cli-kintone --import -a 123 -t API-Token -d Domain
ルックアップフィールドを持つアプリにレコードを追加する
ルックアップフィールドにはAPIトークン認証ではレコードの追加・更新ができません。この場合はパスワード認証を使います。
営業支援パックの「案件管理」にレコードを追加します。
フィールドコード顧客名がルックアップ、案件名が必須項目です。importfile.csv"顧客名","案件名" "戸田ネットソリューションズ","新規案件1" "金都運総研","新規案件2"cli-kintone --import -a 123 -u 'userid' -p 'password' -d Domain -f ./importfile.csv : Start from lines: 1 - 3 => SUCCESS : DONE
- 投稿日:2019-10-02T00:36:17+09:00
「GoとAWS CDKで作る本格SlackBot入門」を読んで、自分でもCloudWatch AlarmをSlack通知してみる
はじめに
先日開催された技術書典7の 「GoとAWS CDKで作る本格SlackBot入門」 という本の内容を受けて自分でもやってみました!本の内容に従いつつ、自分で少しアレンジしています。今回はツイートしたようにEC2のディスク使用量をSlack通知するBotを作ってみました。
AWS CDKの勉強かねて、友達のこの本参考に実践してみた。せっかくだからqiitaにアウトプットする https://t.co/Akw5CQkmHe
— ogady@アニオタエンジニア (@gadyma) September 30, 2019現在は、下記サイトから、電子版を購入することできます、ぜひ今回の記事を読んで興味を持っていただけたら、購入してみてください。
AWS CDK、Goなどの技術を用いて、ChatOps(Slackなどのチャットを用いて快適に開発・運用していこー的なやつ)を導入することの素敵さが存分に味わえる本です。
- 購入先(BOOTH):GoとAWS CDKで作る本格SlackBot入門
前提
- SlackAPIの用意ができていること
- AWSアカウントとAWS CLIの設定ができていること
- nodeの開発環境ができていること
- Goの開発環境ができていること
技術スタック
- Go 1.13
- node 12.10.0
- npm 6.11.3
- typescript 3.6.3
- AWS CLI 1.16.234
- AWS(Lambda、SNS、CloudWatch)
- AWS CDK(TypeScript) 1.9.0
AWS CDKとは
AWS CDK(Cloud Development Kit)は、CloudForamationテンプレートを、TypeScriptやJavaScript、Java、Python、.NETで生成することができます。
AWS CDKのメリット
- 生のCloudForamationで書くよりも記述量を減らすことができる
- コンポーネント分割が用意なので、保守性の高いインフラ設計が可能
- アプリケーション開発で使っている便利ツールをインフラのコードでも使用できる
AWS CDKの導入
CDKの導入npm install -g aws-cdkCDKプロジェクトの作成// プロジェクトのディレクトリに移動して cdk init app --language=typescriptこれを実行すると、以下のようにディレクトリが作成されます。
プロジェクトディレクトリ. ├── README.md ├── bin │ └── alert_ec2_disk_used.ts ├── cdk.json ├── jest.config.js ├── lib │ └── alert_ec2_disk_used-stack.ts ├── node_modules ├── package-lock.json ├── package.json ├── test │ └── alert_ec2_disk_used.test.ts └── tsconfig.jsonまた、CDKのソースがCloudFormationテンプレートとなるため、S3バケットが必要ですが、これは事前にcdkの用意されたコマンドを実行することで CDK Toolkit StackとしてAWS環境にデプロイすることができます。
sh:CDK Toolkit StackをAWS環境にデプロイ
cdk bootstrap
必要なライブラリをインストールするnpm install @types/node@8 @aws-cdk/aws-lambda @aws-cdk/aws-sns @aws-cdk/aws-sns-subscriptions
1. CDKを書いてみる
lib/alert_ec2_disk_used-stack.tsを編集する
ここでは
- Stack・・・デプロイの単位、リージョン、AWSアカウント
- Constracts・・・デプロイするリソースの定義
を定義します。
Lambda周りの定義
lib/alert_ec2_disk_used-stack.ts(Lambda周りの定義)// Lambda Function定義 import { Function, Runtime, Code } from "@aws-cdk/aws-lambda" import { Topic, Subscription, SubscriptionProtocol } from '@aws-cdk/aws-sns'; import { LambdaSubscription } from '@aws-cdk/aws-sns-subscriptions'; /* 中略 */ const lambdaFunction: Function = new Function(this, "alert_ec2_disk_used_func", { functionName: "alert_ec2_disk_used_func", runtime: Runtime.GO_1_X, code: Code.asset("./lambdaSrc"), handler: "handler", memorySize: 128, timeout: cdk.Duration.seconds(10), environment: { "BOT_USER_TOKEN": "{SlackのBot User OAuth Access Token}", "CANNEL_ID": "{送信対象のSlackチャンネルID}", "MESSAGE": "ディスク使用量が75%を超えています。", }, }) // Lambdaサブスクリプションの定義 const lambdaSub: LambdaSubscription = new LambdaSubscription(lambdaFunction) lambdaSub.bind(snsTopic_Over75)SNS周りの定義
lib/alert_ec2_disk_used-stack.ts(SNS周りの定義)// SNSトピック定義 const snsTopic_Over75: Topic = new Topic(this, "ec2_disk_used_over_75", { displayName: "alert_ec2_disk_used_over_75", topicName: "alert_ec2_disk_used_over_75", }) // SNSサブスクリプション定義 const snsSub_Over75: Subscription = new Subscription(this, "ec2_disk_used_over_75_sub", { endpoint: lambdaFunction.functionArn, topic: snsTopic_Over75, protocol: SubscriptionProtocol.LAMBDA, })bin/alert_ec2_disk_used.tsを編集する
ここでは、デプロイの定義(Lambdaソースのコンパイル、実行コマンドなど)を記載します。ここはほとんど本の内容を参考にしています。
bin/alert_ec2_disk_used.ts#!/usr/bin/env node import 'source-map-support/register'; import cdk = require('@aws-cdk/core'); import { AlertEc2DiskUsedStack } from '../lib/alert_ec2_disk_used-stack'; const util = require('util'); const exec = util.promisify(require('child_process').exec); async function deploy() { //Go のソースを build する await exec('go get -v -t -d ./lambdaSrc/... && ' + 'GOOS=linux GOARCH=amd64 ' + 'go build -o ./lambdaSrc/handler ./lambdaSrc/**.go'); const app = new cdk.App(); new AlertEc2DiskUsedStack(app, 'AlertEc2DiskUsedStack'); app.synth(); //build 結果のバイナリを消去する await exec('rm ./lambdaSrc/handler'); } deploy()2. LambdaのGoソース
今回は特定のSlackチャンネルに「Incomming Webhooks」を利用して通知を投げます。今回はCDKの勉強がしたかったので、ここは割と雑。。。
handler.go(抜粋)package main import ( "context" "encoding/json" "os" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/labstack/gommon/log" "github.com/pkg/errors" ) type SNSMessages struct { AlarmName string `json:"AlarmName"` AlarmDescription string `json:"AlarmDescription"` AWSAccountId string `json:"AWSAccountId"` NewStateValue string `json:"NewStateValue"` NewStateReason string `json:"NewStateReason"` StateChangeTime string `json:"StateChangeTime"` Region string `json:"Region"` OldStateValue string `json:"OldStateValue"` } const ( EC2_DISK_USED_OVER_75_ALERT = "alert_ec2_disk_used_over_75" ) const ( GOOD = "good" WARNING = "warning" DANGER = "danger" ) func main() { lambda.Start(noticeHandler) } func noticeHandler(ctx context.Context, snsEvent events.SNSEvent) (e error) { cannelID := os.Getenv("CANNEL_ID") footer := os.Getenv("FOOTER") var snsMessages SNSMessages for _, record := range snsEvent.Records { snsEvent := record.SNS snsMessage := snsEvent.Message err := json.Unmarshal([]byte(snsMessage), &snsMessages) if err != nil { log.Error(err) return err } switch snsMessages.AlarmName { case EC2_DISK_USED_OVER_75_ALERT: color := WARNING if err := PostToSlack(os.Getenv("MESSAGE"), color, cannelID, footer); err != nil { log.Error(err) return err } return nil default: return errors.New("想定するTopicではない") } } return nil }slack_poster.gopackage main import ( "encoding/json" "fmt" "os" "strconv" "time" "github.com/nlopes/slack" ) func PostToSlack(message string, color string, channel string, footer string) error { api := slack.New(os.Getenv("BOT_USER_TOKEN")) attachment := slack.Attachment{ Color: color, Text: message, Footer: footer, Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)), } _, _, err := api.PostMessage(channel, slack.MsgOptionAttachments(attachment)) if err != nil { fmt.Println(err) } return nil }3. ビルドとデプロイ
CDKをビルドする//.tsファイルを実行可能な.js ファイルにコンパイルします。 npm run buildスタックをデプロイするcdk deployデプロイ完了するとこうなる。〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 7/7 | 0:04:09 | CREATE_COMPLETE | AWS::CloudFormation::Stack | AlertEc2DiskUsedStack ✅ AlertEc2DiskUsedStack Stack ARN: arn:aws:cloudformation:ap-northeast-1:434353216964:stack/AlertEc2DiskUsedStack/8a3a46f0-e45c-11e9-b8b3-06680f8688ee※スタックを削除する場合cdk destroy {stack名}4. 確認してみる
ちゃんとCloudFormation上でデプロイが成功していることが確認できます。
やってみて
AWS CDKはすごいですね。CloudFormationでシコシコ書いていたのが、こんな簡単にIaCを実現できるとは。。。
ただ、CloudFormationとCDKを書く言語の両方の知識が必要なため、知識ゼロで始めるとなると学習コストが低いわけではないかな、と思います。とはいえ、アプリ開発と同じ感覚でIDE等の恩恵を受けることができるので、快適に作業することができる点などが本当に魅力的です。マジで。
今回参考にした「GoとAWS CDKで作る本格SlackBot入門」では、AWSアカウント登録から、EC2の起動停止を行うSlackBot構築までを一つ一つ進めていくハンズオン形式になっています。
ここら辺の技術をあまり触ったことがない人でもAWS CDK、Go、ChatOpsの素晴らしさを体験することができるので、ここら辺の技術を使ってみたい方にはかなりオススメです!実案件にもすぐに導入することができる!これからやってみたいこと
今回は、すでに構築してある EC2 と CloudWatchAlarm に対し、SNSTopic と Lambda をCDKで構築したので、次はカスタムメトリクス含めてイチからやってみたいです。(やり方はこれから調べる。。。)
あとは、これで一通りのサーバレスアーキテクチャを構築できるようになるところまでやろうと思います。参考にしたもの