[GCP] GCEにGo Revelの環境を構築してデプロイ

### 環境構築
1.ローカルにGCloudをインストール
$ curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-363.0.0-linux-x86_64.tar.gz
$ tar -zxvf google-cloud-sdk-363.0.0-linux-x86_64.tar.gz
$ ./google-cloud-sdk/install.sh
$ ./google-cloud-sdk/bin/gcloud init
Your Google Cloud SDK is configured and ready to use!
$ pwd
/home/vagrant/gcp/

$ cd ~
$ sudo vi .bash_profile

source /home/vagrant/gcp/gcpgoogle-cloud-sdk/completion.bash.inc
source /home/vagrant/gcp/gcpgoogle-cloud-sdk/path.bash.inc
$ source ~/.bashrc

2.GCPでGCEの作成
Region: asia-northeast1(Tokyo)
Zone: asia-northeast1-a
Machine Series: E2
Machine Type: e2-micro(2 vCPU, 1GB memory)
Boot disk: Ubuntu20.04LTS ※defaultだとDebianになっているので、 ubuntuに変更する
Access Scope: Allow default access
Firewall: Allow HTTP traffic
-> create
-> 設定した内容で作られているかinstancesのviewで確認できる

gcloudによる接続確認
$ gcloud compute ssh instance-3

3.GCE(ubuntu20.04)にGoのインストール
$ sudo apt install golang-go
$ sudo apt-get install –reinstall ca-certificates
$ git config –global http.sslverify false
$ go get github.com/revel/revel
$ go get github.com/revel/cmd/revel
$ go get github.com/cbonello/revel-csrf
$ go get github.com/vansante/go-ffprobe
$ go get github.com/aws/aws-sdk-go
$ go get github.com/go-sql-driver/mysql

$ cd go/src/github.com
$ mkdir me

4.GCEにデプロイ
$ gcloud compute scp –recurse go/src/github.com/me/prd instance-3:/home/vagrant/go/src/github.com/me –zone asia-northeast1-a

5.GCEにmysqlインストール
$ sudo apt update
$ sudo apt install mysql-server
$ sudo mysql –defaults-file=/etc/mysql/debian.cnf
mysql> ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘${new password}’;
$ mysql -u root -p
mysql> create database test;
mysql> use test;

create table users(
id int primary key auto_increment,
name varchar(255) unique,
email varchar(255),
password varchar(255),
filepath varchar(255),
message text
);
insert user
mysql> insert into users(name, password) values (‘user1’, ‘5f4dcc3b5aa765d61d8327deb882cf99’);

sudo vi /home/vagrant/go/src/github.com/me/prd/conf/app.conf
http.port = 80

firewall設定
GCPホーム > ネットワーキング > VPCネットワーク > ファイアウォールルール

項目 入力例
名前 default-allow-9000
説明 Allow 9000 from anywhere
ログ オフ
ネットワーク default
優先度 1000
送信 / 受信 上り
一致したときのアクション 許可
ターゲット タグ use-9000
IP 範囲 0.0.0.0/0
プロトコルとポート tcp:9000

GCE
GCPホーム > コンピューティング > Compute Engine > VMインスタンス
ファイアウォール ネットワークタグ に先ほどのターゲットタグである「use-9000」追記

/home/vagrant/go/bin/revel run -a prd
${externalIP}:9000 で動作確認

ぎゃああああああああああああああああああああああああああああ

[Go言語] テキストの文字数を取得したい

まずダミーテキストを用意します。

/src/test.txt

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

main.go

import (
	"fmt"
	"unicode/utf8"
	"os"
	"io/ioutil"
)

func main() {
	f, err := os.Open("src/test.txt")
	if err != nil{
		fmt.Println("error")
	}
	defer f.Close()
	b, err := ioutil.ReadAll(f)
	// fmt.Println(string(b))
	count := utf8.RuneCountInString(string(b))
	fmt.Println(count)
}

$ go run test.go
445

utf8.RuneCountInString(b)とすると cannot use b (type []byte) as type string in argument to utf8.RuneCountInString となるので、stringで指定する必要がある

OKOK
続いて、KVSからmp4ダウンロード^^

[Go言語] MP4の時間(duration)を取得する

// コマンドラインでffprobeを実行した場合
$ ffprobe src/video.mp4
ffprobe version 4.2.4-1ubuntu0.1 Copyright (c) 2007-2020 the FFmpeg developers
// 省略
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from ‘src/video.mp4’:
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41isomavc1
creation_time : 2021-07-20T06:13:11.000000Z
Duration: 00:00:14.56, start: 0.000000, bitrate: 1530 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280×720, 1527 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
Metadata:
creation_time : 2021-07-20T06:13:11.000000Z
handler_name : L-SMASH Video Handler
encoder : AVC Coding

### goでffprobe
$ go get github.com/vansante/go-ffprobe

package main

import (
	"log"
	"time"
	"context"

	ffprobe "github.com/vansante/go-ffprobe"
)

func main() {
	ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancelFn()

	data, err := ffprobe.ProbeURL(ctx, "src/video.mp4")
	if err != nil {
	    log.Panicf("Error getting data: %v", data)
	}
	log.Print(data.Format.Duration())
	
}

$ go run test.go
2021/11/13 21:37:01 14.56s

mp4が作成された日時とdurationが表示される
OKKKKKKKKKKKKKKKKKKKK

[Go Revel] Paginationを実装したい2

### previous, nextを書いていく
app.go

	var start, end, previous, next int
	
	// p はcurrentのint
	previous = p - 1
	next = p + 1

	return c.Render(content, previous, next)

html

    <nav class="pagination" role="navigation" aria-label="pagination">
      <a href="/paging?page={{.previous}}" class="pagination-previous">Previous</a>
      <a href="/paging?page={{.next}}" class="pagination-next">Next page</a>
    </nav>

ただし、previousとnextは、currentが最初もしくは最後のページの場合、非表示にする
app.go

	current = p
	previous = p - 1
	next = p + 1
    <nav class="pagination" role="navigation" aria-label="pagination">
      {{if ne .current 1}}
      <a href="/paging?page={{.previous}}" class="pagination-previous is-left">Previous</a>
      {{end}}
      <a href="/paging?page={{.next}}" class="pagination-next is-right">Next page</a>
    </nav>

うわああああああああああ、条件分岐が永遠増えていく…

これは次行こう

[Go Revel] Paginationを実装したい1

1. まずgetパラメータでpageの値を取得する
string型での取得

	var page int
	var s string=c.Params.Get("page")
	page, _ = strconv.Atoi(s)
	fmt.Println(page)

http://192.168.34.10:9000/paging?page=1
-> 1

2. ページのスタートと終わりの値を計算
例えば、1ページ5つの値のみを表示するとする場合、l(limit)=5として、startとendの値を求めます。

	var l int = 5
	var start, end int
	start = (p - 1) * l
	end = start + l
	fmt.Println(start)
	fmt.Println(end)

http://192.168.34.10:9000/paging?page=2
2 // 2ページ目
5 // start
10 // end

3. 配列からスライス(要素を取り出す)

	text := [15][2]string{
		{"1","content"},
		{"2","content"},
		{"3","content"},
		{"4","content"},
		{"5","content"},
		{"6","content"},
		{"7","content"},
		{"8","content"},
		{"9","content"},
		{"10","content"},
		{"11","content"},
		{"12","content"},
		{"13","content"},
		{"14","content"},
		{"15","content"},
	}
	fmt.Println(text[start:end])

http://192.168.34.10:9000/paging?page=2
-> [[6 content] [7 content] [8 content] [9 content] [10 content]]

4.全部をつなげて、templeteに出力

func (c App) Paging() revel.Result {

	var p int
	var s string=c.Params.Get("page")
	p, _ = strconv.Atoi(s)
	fmt.Println(p)

	var l int = 5
	var start, end int
	start = (p - 1) * l
	end = start + l
	fmt.Println(start)
	fmt.Println(end)

	text := [15][2]string{
		{"1","content"},
		{"2","content"},
		{"3","content"},
		{"4","content"},
		{"5","content"},
		{"6","content"},
		{"7","content"},
		{"8","content"},
		{"9","content"},
		{"10","content"},
		{"11","content"},
		{"12","content"},
		{"13","content"},
		{"14","content"},
		{"15","content"},

	}
	fmt.Println(text[start:end])
	content := text[start:end]
	return c.Render(content)
}

html側

    <h1>paging</h1>
    <ul>
    {{range .content}}
    <li>{{ index . 0 }} {{.}}</li>
    {{end}}
    </ul>

問題は、view側のページネーションのbuttonをどう実装するか…
0から作るの大変だな..

[Go言語]Revelで連想配列の値をrangeで表示する

app.go

func (c App) Paging() revel.Result {

	text := [3][2]string{
		{"20211113","2021/11/13"},
		{"20211112","2021/11/12"},
		{"20211111","2021/11/11"},
	}
	fmt.Println(text)
	fmt.Println(text[0][1])
	return c.Render(text)
}

views
L indexを使う

    <ul>
    {{range .text}}
    <li>{{ index . 0 }} {{.}}</li>
    {{end}}
    </ul>

ウヒョーーーーーー

.[0] で表示できないので、かなり時間がかかりました。

[Go言語] 配列と連想配列

### 配列

	var arr[2] string
	arr[0] = "2021/11/13"
	arr[1] = "2021/11/12"
	fmt.Println(arr)

[2021/11/13 2021/11/12]

### 連想配列(map)
組み込み関数make()を使用して宣言する

	mapTest := make(map[string]string, 2)

	mapTest["train"] = "JR"
	mapTest["bus"] = "kanachu"
	fmt.Println(mapTest)

map[bus:kanachu train:JR]

### 多次元配列

	text := [3][2]string{
		{"20211113","2021/11/13"},
		{"20211112","2021/11/12"},
		{"20211111","2021/11/11"},
	}
	fmt.Println(text)
	fmt.Println(text[0][1])

[[20211113 2021/11/13] [20211112 2021/11/12] [20211111 2021/11/11]]
2021/11/13

うん、なんかOKっぽい

[Go Revel] S3からファイルをDownloadする

まずHTMLとroutingを作ります。

    <button type=“button” onclick="location.href='/test/download'">download</button>
GET		/test 				  App.Test
GET     /test/download		  App.Download

続いてaws-sdk-goをget
$ go get -u github.com/aws/aws-sdk-go

最後にcontroller
app.go
L ファイルのリターンはc.RenderFileName(“${file_path}”, revel.Attachment)

import (
	"github.com/revel/revel"
	"os"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
func (c App) Download() revel.Result {


	creds := credentials.NewStaticCredentials("${AWS_ACCESS_KEY_ID}","${AWS_SECRET_ACCESS_KEY}","")
	sess := session.Must(session.NewSession(&aws.Config{
		Credentials: creds,
		Region: aws.String("ap-northeast-1"),
	}))

	f, err := os.Create("public/tmp/sample.json")
	if err != nil {
		log.Fatal(err)
	}

	bucketName := "${bucketName}"
	objectKey := "${file}"

	downloader := s3manager.NewDownloader(sess)
	n, err := downloader.Download(f, &s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key: aws.String(objectKey),
	})

	if err != nil {
		log.Fatal(err)
	}
	log.Printf("DownloadedSize: %d byte", n)


	return c.RenderFileName("public/tmp/sample.json", revel.Attachment)
}

動作テスト

OKOK, これで議事録機能はできたかな
ファイルパスのところを、/public/tmp/sample.json って書いてハマってしまったorz
さあ続いてKVS

[Go Revel] ランダムな文字列作成

app.go

import (
	"github.com/revel/revel"
	"app/app/models"
	"fmt"
	_ "image/jpeg"
	"io/ioutil"
	"os"
	"net/smtp"
	"crypto/rand"
	"errors"
)

func (c App) Rand() revel.Result {

	random, _ := MakeRandomStr(8)
	fmt.Println(random)
	return c.Render()
}

func MakeRandomStr(digit uint32)(string, error){
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

	b := make([]byte, digit)
	if _, err := rand.Read(b); err != nil {
		return "", errors.New("unexpected error...")
	}

	var result string
	for _, v := range b {
		result += string(letters[int(v)%len(letters)])
	}
	return result, nil
}

dJdJvsWf

OK, これでPasswordを作成する

[Go Revel] Mailtrapによるメール送信

gmailのsmtpで送信する場合と書き方はほぼ同じ

func (c App) Mail() revel.Result {

	name := "hpscript"
	email := "test@hpscript.com"
	comments := "桜木町の夜景は良いですね"

	auth := smtp.PlainAuth (
		"",
		"hogehoge", // Mailtrap Username
		"fugafuga", // Mailtrap Password
		"smtp.mailtrap.io",
	)

	msg := []byte("From: info@hpscript.com\r\n" +
		"To: " +email + "\r\n" +
		"Subject: SMTPテスト\r\n" +
		"\r\n" +
		"名前: " + name + "\r\n" +
		"メールアドレス" + email + "\r\n" +
		"問い合わせ内容: " + comments + "\r\n")

	err := smtp.SendMail(
		"smtp.mailtrap.io:25",
		auth,
		"info@hpscrpt.com",
		[]string{email}, // 宛先
		msg,
	)

	if err != nil {
		fmt.Println(err)
	}
	return c.Render()
}

OKOK