ソースコードから理解する技術-UnderSourceCode

手を動かす(プログラムを組む)ことで技術を理解するブログ

go-linqを触ってみた

GolangC#LINQのようなデータ操作をできるgo-linqを触ってみました。
GitHub - ahmetb/go-linq: .NET LINQ capabilities in Go

使い方は公式のREADMEにそのまま書いてあるので改めてここには書きませんが
便利そうなので備忘録として残しておきます。

GoのSliceに対して、先に書いたようにLINQ likeな操作をすることができ

  • From(slice)
  • Where(predicate)
  • Select(selector)
  • Union(data)
  • GroupBy(selector)

を使った抽出や、Query型を使用した独自Queryを定義しての抽出などができるようです。
DBから一度に多めのデータを取得 → go-linqで細かい条件を指定しての抽出、などは実案件で割と使えそうな気がします。
とりあえず便利そうなので、気になった方は一度、一番上に張ったリンクのREADMEを一読することをお勧めします。

ginとendressを使ってGraceful Restartを実装してみた

GolangのWeb Frameworkであるgin、"Zero downtime restarts"を行うためのendressを使って
タイトルにあるようにGraceful RestartするWebのサンプルを作ってみました。

https://github.com/gin-gonic/gin
https://github.com/fvbock/endless

サンプルソース

ソースは以下のようになります。

main.go

package main

import (
	"fmt"
	"log"
	"os/exec"
	"syscall"
	"time"

	"github.com/fvbock/endless"
	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()

	router.GET("/hello", func(c *gin.Context) {
		t := time.Now()
		const layout = "2006-01-02 15:04:05"
		const version = "V1"
		message := fmt.Sprintf("Hello! Now is %s. This version is %s.", t.Format(layout), version)
		c.JSON(200, gin.H{"message": message})
	})
	router.GET("/restart", func(c *gin.Context) {
		pid, err := readPid()
		if err != nil {
			log.Fatal(err)
		}
		err = exec.Command("kill", "-SIGHUP", pid).Start()
		if err != nil {
			log.Fatal(err)
		}
		message := "accept restart. pid = " + pid
		c.JSON(200, gin.H{"message": message})
	})

	server := endless.NewServer("localhost:4430", router)
	server.BeforeBegin = func(add string) {
		pid := syscall.Getpid()
		err := writePid(pid)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Actual pid is %d", pid)
	}
	err := server.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}

pid.go

package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
)

func getPidFilePath() (string, error) {
	exe, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(exe) + "/pid", nil
}

func writePid(pid int) error {
	path, err := getPidFilePath()
	if err != nil {
		log.Fatal(err)
	}
	file, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	_, err = file.Write(([]byte)(strconv.Itoa(pid)))
	if err != nil {
		return err
	}
	return nil
}

func readPid() (string, error) {
	path, err := getPidFilePath()
	if err != nil {
		log.Fatal(err)
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

挨拶と現在日時を返す/hello、(セキュリティ的には微妙ですが)Graceful Restartを行う/restartの2つのGetメソッドを持ちます。
やっていることはソースを見てもらえれば分かりやすいかとは思いますが、ざっと書くと

  • ginでrouterを作り、Getメソッドを実装する
  • そのrouterをendressに渡してserverとして起動する

ことを行っています。

起動時にはpid.go内のwritePid()にてプロセスIDをファイルに出力しておき、/restartが呼ばれた時にはreadPid()で読み込んで使用します。

動作確認

ビルドしてバイナリを起動すると以下のように出力されます。

[GIN-debug] GET    /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] GET    /restart                  --> main.main.func2 (3 handlers)
2019/06/03 22:03:51 Actual pid is 97

同時にpidファイルにプロセスIDが出力されるはずです。
別のターミナルを起動し、動作確認のためにいくつかのコマンドを実行します。

$ curl http://localhost:4430/hello
{"message":"Hello! Now is 2019-06-03 22:05:02. This version is V1."}

$ curl http://localhost:4430/restart
{"message":"accept restart. pid = 97"}

$ curl http://localhost:4430/hello
{"message":"Hello! Now is 2019-06-03 22:06:15. This version is V1."}

$ kill -SIGHUP 202 # 最初にバイナリを起動したターミナルにrestart後のプロセスIDが出力される
$ kill -SIGINT 212

「kill -SIGHUP プロセスID」でGraceful Restartができます。ここで指定するプロセスIDは、バイナリの起動時にターミナルに出力された値です。
URLの/restartでもRestartできますが、実際は「kill -SIGHUP」を内部で呼び出しているだけです。

終了は「kill -SIGINT プロセスID」で行います。

Microsoft TypeScript Vue Starter を vue-loader 15系でやってみた

github.com

3カ月くらい前ですが、TypeScriptの雰囲気を知るため、こちらにあるMicrosoftのTypeScript Vue Starterを途中までやってみました。
チュートリアル形式でREADMEが書かれているので、そのまま進めたのですが、途中で躓いたところがあったのでメモ代わりに書いておきます。

なおタイトルにあるようにvue-loader 15系での話なので、今後はどうなるかは分からないです。

やってみた範囲について

TypeScript Vue StarterのREADMEの「Single File Components」まで以下の修正をしつつ進めました。

webpack.config.jsの修正

npm run build をすると以下のようなエラーとなりました。

vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.

どうもvue-loaderの14→15系に上がった時に変更があったようです。
webpack.config.js にVueLoaderPluginを明示的に追加してあげることで上手くいきました。

const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  (中略)
  mode: "development",
  module: {
    rules: [
      {
        test: /\.vue$/,
        (中略)
      },
      (中略)
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ],


もう一つ、やはりnpm run build時に以下のエラーにもなりました。

You may need an appropriate loader to handle this file type.

要は適切なloaderを定義しろ、ってことです。
今回はstyleタグを読めなかったので、webpack.config.js に以下を追加しました。

var path = require('path')
var webpack = require('webpack')
const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  entry: './src/index.ts',
  (中略)
  module: {
    rules: [
      (中略)
      { 
        test: /\.css$/, 
        loader: 'style-loader!css-loader' 
      },
    ]
  },

Visual Studio CodeのRemote DevelopmentでWSL内のGolangを書いてみる

ちょっと前(だいぶか?)Visual Studio Codeでリモートで接続して編集できる「Remote Development」が登場して気になっていました。
Visual Studio Code Remote Development

登場したときは正規のVisual Studio Codeではなく、プレビュー版?のVisual Studio Code Insidersからしか使えなかったのですが
今回は正規のVisual Studio Codeで使ってみました。

以下、簡単ですがその手順を書きたいと思います。

前提条件

Windows上にWSL(Windows Subsystem for Linux)を入れておいてください。
今回はWSL上にGolangの開発環境を用意し、WSL上のGolangのプロジェクトが編集できるかを試してみました。

手順について

1. Visual Studio CodeにRemote Developmentをインストール
コマンドパレットのInstall Extentionsに「Remote Development」と入力すると検索結果として表示されるので、インストールします。

2. Visual Studio CodeをRemoteで起動
既にWSL上にはGolangのプロジェクトが存在するものとします。
ややこしいのですが、$ bashでWSLに切り替え、WSL上ででプロジェクト直下に移動して「$ code .」を実行します。

$ code .
Installing VS Code Server xxxxxxxxxxxxxxxx
Downloading: 100%
Unpacking: 100%

実行すると上のようになり、Visual Studio Codeが起動します。
Visual Studio Codeのタイトルバーに[WSL]と表示され、[view]-[terminal]でターミナルを軌道するとWSLに切り替わっていれば成功です。

3. Go Extentions を導入
Setup Go Development Environment with VS Code and WSL on Windows
こちらの記事を参考に、Extentisonsとして「Go Extensions」をインストールして設定します。
私の場合は既にインストール済だったので、「Go Extensions」の「Install on WSL」を行い、WSL用のもインストールしました。

4. Go toolsのインストール
Go tools that the Go extension depends on · microsoft/vscode-go Wiki · GitHub
こちらにあるコマンドをコピーして、WSLのターミナルでgo getを実行して必要なものをインストールします。

5. settings.jsonの設定
Setup Go Development Environment with VS Code and WSL on Windows
こちらにあるように、Visual Studio Codeの[file]-[preferences]-[settings]を押下してRemote[WSL]タブを選択します。
[Extentions]-[Go configuration]よりsettings.jsonを開き、先の参考サイトのような定義を追加します。

ただ私の場合は

  • "terminal.integrated.shell.linux"は設定しない
  • "go.formatTool"には"goimports"を指定する
  • "go.gopath"、"go.goroot"は適切なものを指定する

という点を変えました。

"goimports"についてはapt-get等でWSL側にインストールする必要があります。


以上でWSLのGolangVisual Studio Codeで開発できるようになりました。
WSL上のGolangのコードを編集してみましたが、ローカルで開発するときにようにファイル保存時のフォーマット・自動importや構文エラーの表示もできているようです。

Terraformを初めて触ってみた

タイトルにあるようにTerraformに初めて触ってみました。
やったこととしては、

です。

もう何番煎じの記事になるのか分からないですが、自分への備忘録として残しておきます。

インストール

インストールはWindows Subsystem for LinuxUbuntuで行いました。Linuxbrewを入れてあるので、Macと同様のコマンドになるかと思います。

$ brew update
$ brew install terraform

EC2を立てる~削除

既にAWS CLIAWSにアクセスできるよう、~/.aws/credentialsは定義済でした。なのでTerraformの定義ファイルを書き、実行しただけです。
まずTerraformの定義ファイルです。

example.tf

provider "aws" {
  region     = "ap-northeast-1"
}
resource "aws_instance" "example" {
  count         = 1
  ami           = "ami-084040f99a74ce8c3"
  instance_type = "t2.micro"

  tags = {
    Name = "${format("example%02d", count.index + 1)}"
  }
}

ファイル名は何でもいいと思いますが、.tfという拡張子とします。内容としては

  • リージョンにはap-northeast-1、つまり東京を使う
  • exampleという名前で作る
  • 数は一つ
  • AMIはAmazon LinuxのAMI IDを指定
  • インスタンスタイプはt2.micro
  • タグをフォーマットを指定して設定

をしています。

後はexample.tfを保存したフォルダで以下のコマンドを実行します。

$ terraform init      # init 初回だけ?
$ terraform plan    # plan 実行前に構築されるオブジェクトを確認できる
$ terraform apply  # apply 構築するコマンド

「apply」を行うと「Do you want to perform these actions?」と聞かれるので「yes」と入力します。
構築されたら、applyの実行結果とAWSのマネージメントコンソールとで結果を見比べてみましょう。また以下の「show」でも構築した結果を見ることができます。

$ terraform show

最後に構築したものを消して終了します。

$ terraform destroy

以前ちょくちょく使ったServerless Frameworkもそうですが、Terraformのような構成管理ツールはコマンド一発で作った環境を削除できることが、個人的には気に入っています。

survey を使ってインタラクティブなプロンプトを作ってみた

GitHub - AlecAivazis/survey: A golang library for building interactive prompts with full support for windows and posix terminals.

タイトルにあるように、surveyを使ってインタラクティブなプロンプトを作ってみました。
どんなことが出来るかやイメージは、先頭に載せたsurveyのURLのREADMEを見れば一発で分かるかと思いますw

使い方もREADMEに書かれているのですが、雰囲気を掴むために自分でも実装してみました。

以下、今回作ったものの簡単な解説になります。
作ったものの想定はシステムのインストーラのプロンプトとなり、以下の項目を入力 or 選択させます。
(見やすくするため各項目を一行ずつ開けてあります)

? What is this system name? sample system

? Input system detail. [Enter 2 empty lines to finish]
Sample
System
Installer

? Choose your OS.:  [Use arrows to move, space to select, type to filter]
> Windows
  Linux
  Mac
  Unix
  Other

? Select install functions:  [Use arrows to move, space to select, type to filter]
  [x]  Web Server
  [ ]  Application Server
  [x]  Slack Notification
  [ ]  Database Backup

? Please type your system password.: ********

? Would you like to insatll now?: (y/N) y

上から順に書くと

  • システム名 ・・・ 文字列
  • システム詳細 ・・・ システムの詳細。複数行文字列
  • OSの種類 ・・・ WindowsLinuxMacなどから一つを選択
  • インストール機能 ・・・ Web Server、Application Serverなどから複数選択
  • パスワード ・・・ パスワード入力
  • 確認 ・・・ y/Nでインストールするかの確認。

です。

最後まで入力すると、JSON形式で今回はターミナルに入力した内容を出力してみました。
よくあるターミナルからの入力は、大体これで作れるのではないかと思います。

今回私が作ったサンプルは以下に置いてあります。
先に書いたプロンプトの実装が知りたい場合は参考にしてみてください。
https://github.com/SrcHndWng/go-survey-sample

「Goならわかるシステムプログラミング」を読んだ

「Goならわかるシステムプログラミング」の一連の記事をゴールデンウィーク中に読んでました。
https://ascii.jp/elem/000/001/235/1235262/

単純にコーディングしているだけでは意識することが少ない、低レイヤーの話が分かりやすく書かれており、必要になった時に再読したいと思います。

以下、読んでだ時のメモを纏めた(纏まってのか?)、チラシの裏書きレベルのメモとなりますw
再読するときなど、読み直す箇所のあたりを付けるのに使おうかな、と。。。

第1回

デバッガなどの準備。

第2回・3回・4回

io.Writer、io.Readerについて。

https://jibun.atmarkit.co.jp/lskill01/rensai/fulinux/03/01.html

こちらの

Linuxには「ファイルシステム」「プロセス」「ストリーム」という3つの概念

を念頭に読み進めた。

プロセスはGolangで書くプログラムそのものだとすると、ファイルシステムとストリームを取り扱うのが
1~3回で扱っているio.Writer、io.Readerインターフェースとなるか。

なお「ファイル」は普段使うテキストファイルに留まらず、入出力先の全て(標準入力・出力や画面など)を指す。
OSレベルではファイルディスクリプタが指す先であり

POSIX系OSでは、可能な限りさまざまなものが「ファイル」として抽象化されて

いる。(https://ascii.jp/elem/000/001/243/1243667/ より)

第5回

システムコールについて。

第6・7・8・9回

ソケット通信まわりについて。

ソケット通信でもファイルシステムとストリームを取り扱うio.Writer・io.Readerを経由して読み書きしている。
Golangを案件で使っている会社であれば、新人研修でWebサーバを作り、ブラウザやcrulでアクセスしてレスポンスを取得する所をやると理解が深まるかも・・・、など思った。

第10・11・12回

ファイルシステムの様々な扱い方について
fsnotifyとgoルーティンを使ったファイルの変更を監視する例、ファイルをロックする例、など。
同期・非同期・ブロッキング・ノンブロッキングについての分かりやすい説明。

第13・14・15回

プロセス、シグナルについて。
Graceful Restartなどアプリでシグナルを扱う例など。

第16・17・18回

並列処理、「スレッドとgoroutineの違い」、「並列処理のパターン」について。
syncパッケージを使った、様々なgoroutineの制御の具体例。
並列処理を実装する前に再読したい。

第19回

メモリ管理について。

第20回

コンテナについて。
仮想化、コンテナの違い。
Golangでコンテナを実装する例。