tech
コンテナ環境に慣れてしまうとEC2久しぶりに使うかってなると、あれなんだっけな・・・って何回も調べ直してたので整理します。
# パッケージ更新 sudo yum update -y # Asia/Tokyoに更新 sudo timedatectl set-timezone Asia/Tokyo # localeを日本にする sudo localectl set-locale LANG=ja_JP.UTF-8 # ec2-userを変更 sudo useradd USERNAME sudo cp -arp /home/ec2-user/.ssh/ /home/USERNAME/ sudo chown -R USERNAME /home/USERNAME/.ssh # 新規userをsudo使えるように書き換える sudo visudo -f /etc/sudoers.d/90-cloud-init-users > # Created by cloud-init v. 19.3-3.amzn2 on Sat, 29 Aug 2020 10:48:59 +0000 > > # User rules for ec2-user > # ec2-user ALL=(ALL) NOPASSWD:ALL > USERNAME admin ALL=(ALL) NOPASSWD:ALL # root権限持っているか確認 sudo su - exit # ec2-userを削除する sudo userdel -r ec2-user それだけです。
var _ Foo = (*foo)(nil) みたいな記述を見かけて何だろうなと気になっていました。
How can I guarantee my type satisfies an interface?に書いてありました。
You can ask the compiler to check that the type T implements the interface I by attempting an assignment using the zero value for T or pointer to T, as appropriate:
type T struct{} var _ I = T{} // Verify that T implements I. var _ I = (*T)(nil) // Verify that *T implements I.
Read more
前回にてgin-gonic/ginで触りこぼしていたGraceful-Shutdownをちゃんと試してみました。
Graceful Shutdown 名前の通り終了処理にあたってセッションがクローズするまでなるべく待ってあげることです。
調べた結果は簡単に次の通りです。
Ginも標準パッケージもGraceful処理は変わらない contextで最長の待ち時間を決定できる Graceful-ShutdownのManuallyに記載されている方法をまずは試してみます。
$ curl localhost:8080 で5秒後に返信がきます。
$ go run main.go [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET / --> main.main.func1 (3 handlers) [GIN] 2020/08/25 - 15:19:54 | 200 | 5.003282194s | 127.0.0.1 | GET "/" ^C # <--- SIGINT 2020/08/25 15:19:58 Shutting down server.
Read more
前回にてgin-gonic/ginで触りこぼしていたValidatorをちゃんと試してみました。
Validator Model binding and validationに記載されているようにginではgo-playground/validatorを使っています。
Qiita: go-playground/validator リクエストパラメータ向けValidationパターンまとめで多くのパターンが整理されているので、こちらが参考になります。
QueryとBodyの両方で試しましたが、問題なくvalidateかけれていますね。
func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { var usr User if err := c.ShouldBindQuery(&usr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, usr) }) r.POST("/", func(c *gin.Context) { var usr User if err := c.ShouldBindJSON(&usr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, usr) }) r.Run(":8080") } type User struct { Name string `form:"name"` Age int `form:"age" binding:"required,max=10,min=5"` } # Test Query $ curl 'localhost:8080?
Read more
カスタムエラーについて理解が浅かったので改めて整理したいと思います。
基本形 仕様を確認 errorはinterfaceのため Error() string を満たせばいいとあります。
type error interface { Error() string } errosがまさにその実装を踏まえて作られております。
package errors // errors.New()はその構造体を返す func New(text string) error { return &errorString{text} } // errorStringというError() stringを満たす構造体を作る type errorString struct { s string } func (e *errorString) Error() string { return e.s } 実装してみる 同じように作ってみました。 エラー情報として欲しい内容を構造体に定義したら良さそうですね。
type TestError struct { first, second string } func (e TestError) Error() string { return fmt.Sprintf("%s --- %s", e.first, e.second) } func NewErr(first, second string) error { return &TestError{first, second} } func main() { fmt.
Read more
前回の続きということでday2です
TODOにUpdate, Deleteを追加 Packageを分ける CRUDなTODOになったと思います。
作成したコードはsourjp/go-practice/day2にあります。
構成 少ない内容であればMVCで分ける方がわかりやすいですね。
$ tree . ├── Makefile ├── controllers │ ├── router.go │ └── todo.go ├── go.mod ├── go.sum ├── main.go ├── models │ ├── db.go │ └── todo.go └── todo.sql package models todo.sql
CREATE TABLE IF NOT EXISTS todo ( id SERIAL NOT NULL PRIMARY KEY, title VARCHAR(25), message VARCHAR(255), created_at TIMESTAMP, updated_at TIMESTAMP, finished_at TIMESTAMP ) models/db.go
package models import ( "database/sql" "fmt" ) func NewDB() (*sql.
Read more
最初プログラミングを勉強し始めた時はひたらすら書く練習をしたくて、AtCoderを通して計算量や数学など学んでいました。 ただアプリケーションを開発するスキルがつくわけではないので最近は少し休んでいました。
Goの書き方もわかってきたので久しぶりにやってみたのですが、面白いですね。 当時はPythonしか書けなかったので、Goと比較しながら簡単なA, Bだけ解いてみました。
ABC175A 3つしか数字がないので2^3 = 8通りなのでロジック考えるより場合分けした方が生産的ですね。
Python - ABC175A/Rainy Season
s = input() if s == "RRR": print(3) elif s == "RRS" or s == "SRR": print(2) elif s == "SSS": print(0) else: print(1) Go - ABC175A/Rainy Season
package main import "fmt" func main() { var n string fmt.Scan(&n) switch n { case "RRR": fmt.Println(3) case "RRS", "SRR": fmt.Println(2) case "SSS": fmt.Println(0) default: fmt.Println(1) } } ABC175B 三角形なので次の定理を満たせばいいですね
Read more
Qiita: FieldRef.labels[‘KEY’]知らなかった を書きました。
gin-gonicとdatabase/sqlの基本的な動作は試せたので、早速ど定番のTodoアプリを作ってみます。やっぱ試してみないと不明点は見つかりにくいですしね!
day1ということで簡単な内容で今回は作ります
main.goのみ DBはpostgres使う TODOアプリで登録、取得のみ対応 作成したコードはsourjp/go-practice/day1にあります。
1. TODOを定義 ここでのポイントは FinishedAt の、*time.Time です。たとえ omitempty を使っていても、 time.Time のゼロ値は対象外(表示される)となります。
JSON omitempty With time.Time Fieldが参考になります。
そのため、pointerにしてnil扱いにすることでemptyと判定させることができます。 ただDBの視点で考えたときに、登録時にそもそもデータがないのだからomitempty関係なく空だからそりゃでないじゃんという・・・
構造体をMarshalする時には知っといた方がいいポイントかと思いました。
type TODO struct { ID int `db:"id" json:"id"` Title string `db:"title" json:"title"` Message string `db:"message" json:"message"` CreatedAt time.Time `db:"created_at" json:"created_at"` FinishedAt *time.Time `db:"finished_at" json:"finished_at,omitempty"` } /* $ make t-get [ { "id": 1, "title": "study", "message": "study", "created_at": "2020-08-19T22:59:42.568475Z", "finished_at": "0001-01-01T00:00:00Z" <--- time.Timeのゼロ値で入れてしまった }, { "id": 2, "title": "study2", "message": "study2", "created_at": "2020-08-19T23:14:50.
Read more
database/sqlを触ってますが、いまいちしっかり説明してくれるリソースがないなあと思ってたら、goのwikiにGo database/sql tutorialがありました!
ということで試しながらポイントを整理してきます。
Overview DBへのアクセスには sql.DB を利用 sql.DB は指定したDriverへアクセスを抽象化した存在 これによってDriverがPostgresでも、MySQLでも統一した操作を提供 Importing a Database Driver 利用には database/sql とDriverパッケージの二つが必要 一般的にDriverパッケージは依存するため使わない database/sql を介してアクセス Dirver自体は次のようにすることでDriverとして登録(Go import _ ってなんだみてね) import ( "database/sql" _ "github.com/lib/pq" ) wikiではmysqlですが、今回はpostgres driverを使います
Accessing the Database sql.Open()により取得したobjectでDBを操作可能 第一引数はdriverの名前で、第二引数はパラメーター(PGの場合はココ)を渡します
テストとしてDBが必要なのでDockerのpostgres利用することにして、簡単にMakefileを用意します
$ docker run --name go-pg -e POSTGRES_USER=root -e POSTGRES_PASSWORD=root -e POSTGRES_DB=test -p 5432:5432 -d postgres:12.4
init-db: @docker run --name go-pg -e POSTGRES_USER=root -e POSTGRES_PASSWORD=root -e POSTGRES_DB=test -p 5432:5432 -d postgres:12.
Read more