From 5fa3bcb60d202485b606dab89f8e3ffdef46e913 Mon Sep 17 00:00:00 2001 From: Sebastien FRIESS Date: Mon, 27 Mar 2017 00:51:38 +0200 Subject: [PATCH 1/2] refact(main): initialize one spanner client for the app --- .gitignore | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 86 ++++++++++++++------------ main_test.go | 15 +++++ 3 files changed, 230 insertions(+), 38 deletions(-) create mode 100644 .gitignore create mode 100644 main_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..efbaea0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,167 @@ +# Created by .ignore support plugin (hsz.mobi) +### Windows template +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk +### AppEngine template +# Google App Engine generated folder +appengine-generated/ +### Eclipse template + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet +### macOS template +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Go template +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/main.go b/main.go index 23a484b..f4ff8c9 100644 --- a/main.go +++ b/main.go @@ -1,39 +1,51 @@ package spanner_blockchain import ( - "fmt" - "log" - "net/http" - // "cloud.google.com/go/spanner/admin/database/apiv1" "cloud.google.com/go/spanner" "cloud.google.com/go/spanner/admin/database/apiv1" + "fmt" adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1" + "log" + "net/http" + "crypto/sha1" + "encoding/hex" + "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/api/iterator" "google.golang.org/appengine" "regexp" - "crypto/sha1" - "encoding/hex" ) +const ( + databaseUrl = "projects/%s/instances/test-instance/database/example-db" +) -func getDatabaseName(ctx context.Context) string { - return "projects/" + appengine.AppID(ctx) + "/instances/test-instance/databases/example-db" -} +var ( + spannerClient *spanner.Client +) func init() { + // init spanner db + ctx := context.Background() + sc, err := createDatabase(ctx) + if err != nil { + log.Fatal(err) + } + + // init global spanner client + spannerClient = sc + + // install handler http.HandleFunc("/write", writeData) - http.HandleFunc("/create", createDB) } -func createDataClient(ctx context.Context, db string) *spanner.Client { +/*func getDatabaseName(ctx context.Context) string { + return fmt.Sprintf(databaseUrl, appengine.AppID(ctx)) +}*/ - dataClient, err := spanner.NewClient(ctx, db) - if err != nil { - log.Fatal(err) - } - return dataClient +func getDatabaseName(ctx context.Context) string { + return "projects/randommeetupgenerator/instances/test-instance/databases/example-db" } func createAdminClient(ctx context.Context) *database.DatabaseAdminClient { @@ -45,15 +57,16 @@ func createAdminClient(ctx context.Context) *database.DatabaseAdminClient { return adminClient } -func createDatabase(ctx context.Context, client *spanner.Client, w http.ResponseWriter) error { +func createDatabase(ctx context.Context) (*spanner.Client, error) { db := getDatabaseName(ctx) adminClient := createAdminClient(ctx) matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db) if matches == nil || len(matches) != 3 { - return fmt.Errorf("Invalid database id %s", db) + return nil, errors.New("invalid database id" + db) } + op, err := adminClient.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{ Parent: matches[1], CreateStatement: "CREATE DATABASE `" + matches[2] + "`", @@ -68,11 +81,17 @@ func createDatabase(ctx context.Context, client *spanner.Client, w http.Response }, }) if err != nil { - return err + return nil, err } if _, err := op.Wait(ctx); err == nil { - fmt.Fprintf(w, "Created database [%s]\n", db) + log.Println("Created database " + db) } + + client, err := spanner.NewClient(ctx, db) + if err != nil { + log.Fatal(err) + } + newMessage := "Block 0" newMyHash := computeSha1(newMessage) newHashBefore := "" @@ -80,9 +99,9 @@ func createDatabase(ctx context.Context, client *spanner.Client, w http.Response blocksColumns := []string{"BlockId", "Message", "MyHash", "HashBefore", "HashAfter"} if err := writeMessage(blocksColumns, 1, newMessage, newMyHash, newHashBefore, newHashAfter, client, ctx); err != nil { - return err + return nil, err } - return nil + return client, nil } func computeSha1(message string) string { @@ -91,15 +110,15 @@ func computeSha1(message string) string { h.Write([]byte(message)) sha1_hash := hex.EncodeToString(h.Sum(nil)) - return sha1_hash + return sha1_hash } -func write(ctx context.Context, client *spanner.Client, newMessage string) error { +func write(ctx context.Context, newMessage string) error { blocksColumns := []string{"blockId", "Message", "MyHash", "HashBefore", "HashAfter"} stmt := spanner.Statement{ SQL: `select * FROM Blocks WHERE HashAfter = ""`} - iter := client.Single().Query(ctx, stmt) + iter := spannerClient.Single().Query(ctx, stmt) defer iter.Stop() for { @@ -137,12 +156,12 @@ func write(ctx context.Context, client *spanner.Client, newMessage string) error newHashAfter := "" // add new message - if err := writeMessage(blocksColumns, blockIDPrevious+1, newMessage, newMyHash, newHashBefore, newHashAfter, client, ctx); err != nil { + if err := writeMessage(blocksColumns, blockIDPrevious+1, newMessage, newMyHash, newHashBefore, newHashAfter, spannerClient, ctx); err != nil { return err } // update previous message - if err := writeMessage(blocksColumns, blockIDPrevious, messagePrevious, myHashPrevious, hashBeforePrevious, newMyHash, client, ctx); err != nil { + if err := writeMessage(blocksColumns, blockIDPrevious, messagePrevious, myHashPrevious, hashBeforePrevious, newMyHash, spannerClient, ctx); err != nil { return err } @@ -163,18 +182,9 @@ func writeMessage(blocksColumns []string, blockID int64, newMessage string, newM func writeData(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Write") message := r.URL.Query().Get("message") - if(message != "") { + if message != "" { c := appengine.NewContext(r) - - dataClient := createDataClient(c, getDatabaseName(c)) - err := write(c, dataClient, message) + err := write(c, message) fmt.Fprint(w, err) } } - -func createDB(w http.ResponseWriter, r *http.Request) { - c := appengine.NewContext(r) - dataClient := createDataClient(c, getDatabaseName(c)) - err := createDatabase(c, dataClient, w) - fmt.Fprint(w, err) -} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..3e5c2e4 --- /dev/null +++ b/main_test.go @@ -0,0 +1,15 @@ +package spanner_blockchain + +import ( + "fmt" + "regexp" + "testing" +) + +func TestGetDatabaseName(t *testing.T) { + db := fmt.Sprintf(databaseUrl, "randommeetupgenerator.appspot.com") + fmt.Println(db) + + matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db) + fmt.Println(len(matches)) +} From 74b15e03d172ef63854f7bb786dd8ecd7cdd8e32 Mon Sep 17 00:00:00 2001 From: Sebastien FRIESS Date: Mon, 27 Mar 2017 14:27:39 +0200 Subject: [PATCH 2/2] refact(main): make service run locally --- main.go | 16 ++++++++-------- main_test.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index f4ff8c9..17d6345 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -package spanner_blockchain +package main import ( "cloud.google.com/go/spanner" @@ -13,7 +13,6 @@ import ( "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/api/iterator" - "google.golang.org/appengine" "regexp" ) @@ -25,10 +24,8 @@ var ( spannerClient *spanner.Client ) -func init() { - // init spanner db - ctx := context.Background() - sc, err := createDatabase(ctx) +func main() { + sc, err := createDatabase() if err != nil { log.Fatal(err) } @@ -38,6 +35,7 @@ func init() { // install handler http.HandleFunc("/write", writeData) + log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)) } /*func getDatabaseName(ctx context.Context) string { @@ -57,7 +55,8 @@ func createAdminClient(ctx context.Context) *database.DatabaseAdminClient { return adminClient } -func createDatabase(ctx context.Context) (*spanner.Client, error) { +func createDatabase() (*spanner.Client, error) { + ctx := context.Background() db := getDatabaseName(ctx) adminClient := createAdminClient(ctx) @@ -80,6 +79,7 @@ func createDatabase(ctx context.Context) (*spanner.Client, error) { ) PRIMARY KEY (BlockId)`, }, }) + if err != nil { return nil, err } @@ -183,7 +183,7 @@ func writeData(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Write") message := r.URL.Query().Get("message") if message != "" { - c := appengine.NewContext(r) + c := context.Background() err := write(c, message) fmt.Fprint(w, err) } diff --git a/main_test.go b/main_test.go index 3e5c2e4..caf86f2 100644 --- a/main_test.go +++ b/main_test.go @@ -1,4 +1,4 @@ -package spanner_blockchain +package main import ( "fmt"