GO言語のDocker開発環境構築

# 0.以下URLよりファイルをダウンロード

https://github.com/bluemooninc/docker-go-starter

このリポジトリは Docker community が go-lang 用にがメンテナンスしている Dockerfile を元に CRUD 開発向けにカスタマイズをしたものです。githubに全てのソースをアップしてありますのでダウンロードもしくはクローンして、以下コマンドを実行すればすぐにGO言語の開発を始める環境が整います。

```
docker-compose up
```

サンプル・プログラムは、JSON形式のAPIインターフェイスを持ち、ORマッパーを使用してMySQLサーバーにCRUD(生成・読込・更新・削除)する機能が実装されています。

起動したDockerイメージには、 GOコンテナとして Go v1.13 と Webフレームワークである Echo、OR/Mの GORM が含まれています。また、DBサーバとして MySQL のコンテナと、DBの管理用に PhpMyAdmin がそれぞれ起動しています。

また、 GOコンテナ内の fresh モジュールによってgoファイルの編集結果は自動的にホット・リロードされます。

Dockerfileの内容カスタマイズについては、以下をご覧ください。

https://github.com/docker-library/golang

## 1.dockerの起動

Dockeは、APIサーバーを作成して起動します。サンプルエンドポイントは 

```
http：//localhost：8080/helloWorld
```
 です。main.goを編集すると、freshモジュールによりサーバが自動的にホットリロードされます。

```
docker-compose up
Creating go ... done
Attaching to go
go     | 8:9:59 runner      | InitFolders
go     | 8:9:59 runner      | mkdir ./tmp
go     | 8:9:59 runner      | mkdir ./tmp: file exists
go     | 8:9:59 watcher     | Watching .
go     | 8:9:59 main        | Waiting (loop 1)...
go     | 8:9:59 main        | receiving first event /
go     | 8:9:59 main        | sleeping for 600 milliseconds
go     | 8:10:00 main        | flushing events
go     | 8:10:00 main        | Started! (5 Goroutines)
go     | 8:10:00 main        | remove tmp/runner-build-errors.log: no such file or directory
go     | 8:10:00 build       | Building...
go     | 8:10:04 runner      | Running...
go     | 8:10:04 main        | --------------------
go     | 8:10:04 main        | Waiting (loop 2)...
go     | 8:10:04 app         |
go     |    ____    __
go     |   / __/___/ /  ___
go     |  / _// __/ _ \/ _ \
go     | /___/\__/_//_/\___/ v3.3.10-dev
go     | High performance, minimalist Go web framework
go     | https://echo.labstack.com
go     | ____________________________________O/_______
go     |                                     O\
```

## 2. phpMyAdminで閲覧

http://localhost:8085/

ブラウザにtest_databaseが表示されます。

以下のJsonパラメーターを使用してリクエストを送信した後に、test_databaseの products テーブルを確認してください。

## 3. Json post による hello world

投稿リクエストにJSONパラメータを渡しJsonがそのまま帰ってくるサンプルです。

ローカル端末からapiを呼び出します。

```
curl   -X POST   http://localhost:8080/helloWorld   -H 'Content-Type: application/json'   -d '{"code": "helloWorld", "price": 999}'

{"code":"helloWorld","price":999}
```

## 4. Json post によるデータの挿入
```
curl   -X POST   http://localhost:8080/insert   -H 'Content-Type: application/json'   -d '{"code": "foo","price": 555}'

{"code":200,"body":"foo"}
```

## 5. Json Post によるデータの検索

```
curl   -X POST   http://localhost:8080/find   -H 'Content-Type: application/json'   -d '{"code": "foo"}'

{"code":200,"body":"555"}
```


## 6. Json Post によるデータの削除

```
curl   -X POST   http://localhost:8080/delete   -H 'Content-Type: application/json'   -d '{"code": "foo"}'

{"code":200,"body":"foo"}
```

最後に現時点での main.go を載せておきます。

```
package main

import (
    "net/http"
    "github.com/labstack/echo"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "strconv"
)
/*
** Config for Database table
 */
type Product struct {
    gorm.Model
    Code string
    Price uint
}
/*
** Config for Json data
 */
type Item struct {
    Code string `json:"code"`
    Price uint `json:"price"`
}
/*
** Config for MySQL setting
 */
func gormConnect() *gorm.DB {
    DBMS     := "mysql"
    USER     := "docker"
    PASS     := "docker"
    PROTOCOL := "tcp(mysql_host:3306)"
    DBNAME   := "test_database"
    // add parseTime option
    CONNECT := USER+":"+PASS+"@"+PROTOCOL+"/"+DBNAME+"?parseTime=true"
    db,err := gorm.Open(DBMS, CONNECT)

    if err != nil {
        panic(err.Error())
    }
    return db
}
/*
** Common return of Json
 */
func returnJson(c echo.Context, code string) error {
    return c.JSON(
        http.StatusOK,
        struct {
            Code int    `json:"code"`
            Text string `json:"body"`
        }{
            Code: http.StatusOK,
            Text: code,
        },
    )
}
/*
** HELLO WORLD!
 */
func helloWorld(c echo.Context) error {
    u := new(Item)
    if err := c.Bind(u); err != nil {
        return err
    }
    return c.JSON(http.StatusCreated,u)
}
/*
** Insert record by Json data
 */
func insert(c echo.Context) error {
    db := gormConnect()
    db.AutoMigrate(&Product{})
    u := new(Item)
    if err := c.Bind(u); err != nil {
        return err
    }
    db.Create(&Product{Code: u.Code, Price: u.Price})
    defer db.Close()
    return returnJson(c, u.Code)
}
/*
** Find record by Json data
 */
func find(c echo.Context) error {
    db := gormConnect()
    u := new(Item)
    if err := c.Bind(u); err != nil {
        return err
    }
    var product Product
    db.First(&product, "code = ?", u.Code)
    defer db.Close()
    return returnJson(c, strconv.FormatUint(uint64(product.Price),10))
}
/*
** Update record by Json data
 */
func update(c echo.Context) error {
    db := gormConnect()
    db.AutoMigrate(&Product{})
    u := new(Item)
    if err := c.Bind(u); err != nil {
        return err
    }
    var product Product
    db.First(&product, "code = ?", u.Code)
    db.Model(&product).Update("Price", u.Price)
    defer db.Close()
    return returnJson(c, u.Code)
}
/*
** Delete record by Json data
 */
func delete(c echo.Context) error {
    db := gormConnect()
    db.AutoMigrate(&Product{})
    u := new(Item)
    if err := c.Bind(u); err != nil {
        return err
    }
    var product Product
    db.First(&product, "code = ?", u.Code)
    db.Delete(&product)
    defer db.Close()
    return returnJson(c, u.Code)
}
/*
** main loop
 */
func main() {
    e := echo.New()
    // Routes
    e.POST("/helloWorld", helloWorld)
    e.POST("/insert", insert)
    e.POST("/find", find)
    e.POST("/update", update)
    e.POST("/delete", delete)
    // Start server
    e.Logger.Fatal(e.Start(":8080"))
}
```