Browse Source

Code refactoring
1) Move database testing functionality out of FastCGI (it’ s pointless to run it every time it’s called!!)
2) Attempt to set the error level to something less obnoxious than “DEBUG” (not working)
3) When on interactive mode, use a quasi-readline library to get all kinds of fancy shell functionality (including Emacs binding _and_ history!!)

Gwyneth Llewelyn 2 years ago
parent
commit
cb0321fa0c
4 changed files with 114 additions and 72 deletions
  1. 1 1
      config.toml
  2. 3 0
      go.mod
  3. 6 0
      go.sum
  4. 104 71
      gosl.go

+ 1 - 1
config.toml

@@ -1,6 +1,6 @@
 [config]
 BATCH_BLOCK	= 100000
-myPort		= 3000
+# myPort		=
 myDir		= "slkvdb"
 isServer	= false
 isShell		= true

+ 3 - 0
go.mod

@@ -2,6 +2,8 @@ module git.gwynethllewelyn.net/GwynethLlewelyn/gosl-basics
 
 go 1.17
 
+replace gitlab.com/cznic/readline => modernc.org/readline v1.0.0
+
 require (
 	github.com/dgraph-io/badger v1.6.2
 	github.com/dgraph-io/badger/v3 v3.2103.2
@@ -12,6 +14,7 @@ require (
 	github.com/spf13/viper v1.9.0
 	github.com/syndtr/goleveldb v1.0.0
 	github.com/tidwall/buntdb v1.2.7
+	gitlab.com/cznic/readline v1.0.0
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0
 )
 

+ 6 - 0
go.sum

@@ -61,8 +61,10 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -260,6 +262,7 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -510,6 +513,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -760,6 +764,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+modernc.org/readline v1.0.0 h1:wpWbnEzXNRkO4bMIu4pV6QZxsC2QGu0S1lX1ReiEoM8=
+modernc.org/readline v1.0.0/go.mod h1:HPTugf/VPaRtDJarvAQfrN8irjbXGKgv/HxKLpdZA+Y=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

+ 104 - 71
gosl.go

@@ -2,7 +2,7 @@
 package main
 
 import (
-	"bufio"
+//	"bufio"			// replaced by the more sophisticated readline (gwyneth 20211106)
 	"encoding/json"
 	"fmt"
 	"net/http"
@@ -22,6 +22,7 @@ import (
 	"github.com/spf13/viper"
 	"github.com/syndtr/goleveldb/leveldb"
 	"github.com/tidwall/buntdb"
+	"gitlab.com/cznic/readline"
 //	"gopkg.in/go-playground/validator.v9"	// to validate UUIDs... and a lot of thinks
 	"gopkg.in/natefinch/lumberjack.v2"
 )
@@ -146,6 +147,9 @@ func main() {
 		fmt.Println("gosl is starting...")
 	}
 
+	// This is mostly to deal with scoping issues below. (gwyneth 20211106)
+	var err error
+
 	// Setup the lumberjack rotating logger. This is because we need it for the go-logging logger when writing to files. (20170813)
 	rotatingLogger := &lumberjack.Logger{
 		Filename:	goslConfig.logFilename,
@@ -161,22 +165,23 @@ func main() {
 	backendFile				:= logging.NewLogBackend(rotatingLogger, "", 0)
 	backendFileFormatter	:= logging.NewBackendFormatter(backendFile, logFormat)
 	backendFileLeveled 		:= logging.AddModuleLevel(backendFileFormatter)
-	backendFileLeveled.SetLevel(logging.INFO, "gosl")	// we just send debug data to logs if we run as shell
+
+	theLogLevel, err := logging.LogLevel(goslConfig.logLevel)
+	if err != nil {
+		log.Warningf("could not set log level to %q — invalid?\nlogging.LogLevel() returned error %q\n", goslConfig.logLevel, err)
+	} else {
+		log.Debugf("requested file log level: %q\n", theLogLevel.String())
+		log.Debugf("file log level set to: %v\n", backendFileLeveled.GetLevel("gosl"))
+	}
+
+	backendFileLeveled.SetLevel(theLogLevel, "gosl")	// we just send debug data to logs if we run as shell
 
 	if goslConfig.isServer || goslConfig.isShell {
 		backendStderr			:= logging.NewLogBackend(os.Stderr, "", 0)
 		backendStderrFormatter	:= logging.NewBackendFormatter(backendStderr, logFormat)
 		backendStderrLeveled 	:= logging.AddModuleLevel(backendStderrFormatter)
-
-		theLogLevel, err := logging.LogLevel(goslConfig.logLevel)
-		if err != nil {
-			fmt.Printf("could not set log level to %q — invalid?\nlogging.LogLevel() returned error %q\n", goslConfig.logLevel, err)
-		} else {
-			fmt.Printf("requested log level: %q \n", theLogLevel.String())
-		}
-
 		backendStderrLeveled.SetLevel(theLogLevel, "gosl")
-		log.Debugf("level set to: %v\n", backendStderrLeveled.GetLevel("gosl"))
+		log.Debugf("stderr log level set to: %v\n", backendStderrLeveled.GetLevel("gosl"))
 	}
 /*
 		// deprecated, now we set it explicitly if desired
@@ -194,7 +199,7 @@ func main() {
 	// Check if this directory actually exists; if not, create it. Panic if something wrong happens (we cannot proceed without a valid directory for the database to be written)
 	if stat, err := os.Stat(goslConfig.myDir); err == nil && stat.IsDir() {
 		// path is a valid directory
-		log.Infof("valid directory: %q\n", goslConfig.myDir)
+		log.Debugf("valid directory: %q\n", goslConfig.myDir)
 	} else {
 		// try to create directory
 		if err = os.Mkdir(goslConfig.myDir, 0700); err != nil {
@@ -207,22 +212,9 @@ func main() {
 		log.Debugf("created new directory: %q\n", goslConfig.myDir)
 	}
 
-	// Prepare testing data! (common to all types)
-	const testAvatarName = "Nobody Here"
-	var err error
-
-	log.Infof("gosl started and logging is set up. Proceeding to test database (%s) at %q\n",goslConfig.database, goslConfig.myDir)
-	// generate a random UUID (gwyneth2021103) (gwyneth 20211031)
-
-	var (
-		testUUID = uuid.New().String()	// Random UUID (gwyneth 20211031 — from )
-		testValue = avatarUUID{ testAvatarName, testUUID, "all grids" }
-	)
-	jsonTestValue, err := json.Marshal(testValue)
-	checkErrPanic(err) // something went VERY wrong
-
-	// KVDB Initialisation & Tests
-	// Each case is different
+	// Special options configuration.
+	// Currently, this is only needed for Badger v3, the others have much simpler configurations.
+	// (gwyneth 20211106)
 	switch goslConfig.database {
 		case "badger":
 			// Badger v3 - fully rewritten configuration (much simpler!!) (gwyneth 20211026)
@@ -256,63 +248,104 @@ func main() {
 			Opt.WithLoggingLevel(badger.ERROR)
 			goslConfig.BATCH_BLOCK = 1000	// try to import less at each time, it will take longer but hopefully work
 			log.Info("trying to avoid too much memory consumption")
-			// Badger setup is ready, now the rest is similar to the others
-			kv, err := badger.Open(Opt)
-			checkErrPanic(err) // should probably panic, cannot prep new database
-			txn := kv.NewTransaction(true)
-			err = txn.Set([]byte(testAvatarName), jsonTestValue)
-			checkErrPanic(err)
-			err = txn.Set([]byte(testUUID), jsonTestValue)
-			checkErrPanic(err)
-			err = txn.Commit()
-			checkErrPanic(err)
-			log.Debugf("badger SET %+v (json: %v)\n", testValue, string(jsonTestValue))
-			kv.Close()
-		case "buntdb":
-			goslConfig.dbNamePath = filepath.Join(goslConfig.myDir, databaseName)
-			db, err := buntdb.Open(goslConfig.dbNamePath)
-			checkErrPanic(err)
-			err = db.Update(func(tx *buntdb.Tx) error {
-				_, _, err := tx.Set(testAvatarName, string(jsonTestValue), nil)
-				return err
-			})
-			checkErr(err)
-			log.Debugf("buntdb SET %+v (json: %v)\n", testValue, string(jsonTestValue))
-			db.Close()
-		case "leveldb":
-			goslConfig.dbNamePath = filepath.Join(goslConfig.myDir, databaseName)
-			db, err := leveldb.OpenFile(goslConfig.dbNamePath, nil)
-			checkErrPanic(err)
-			err = db.Put([]byte(testAvatarName), jsonTestValue, nil)
-			checkErrPanic(err)
-			log.Debugf("leveldb SET %+v (json: %v)\n", testValue, string(jsonTestValue))
-			db.Close()
+		// the other databases do not require any special configuration (for now)
 	} // /switch
-	// common to all databases:
-	key, grid := searchKVname(testAvatarName)
-	log.Debugf("GET %q returned %q [grid %q]\n", testAvatarName, key, grid)
-	log.Info("KV database seems fine.")
 
+	// if importFilename isn't empty, this means we
 	if goslConfig.importFilename != "" {
 		log.Info("attempting to import", goslConfig.importFilename, "...")
 		importDatabase(goslConfig.importFilename)
 		log.Info("database finished import.")
 	} else {
-		// it's not an error if there is no nam2key database available for import (gwyneth 20211027)
-		log.Warning("no database configured for import")
+		// it's not an error if there is no name2key database available for import (gwyneth 20211027)
+		log.Debug("no database configured for import")
+	}
+
+	// Prepare testing data! (common to all database types)
+	// Note: this only works for shell/server; for FastCGI it's definitely overkill (gwyneth 20211106),
+	//  so we do it only for server/shell mode.
+	if goslConfig.isServer || goslConfig.isShell {
+		const testAvatarName = "Nobody Here"
+
+		log.Infof("gosl started and logging is set up. Proceeding to test database (%s) at %q\n",goslConfig.database, goslConfig.myDir)
+		// generate a random UUID (gwyneth2021103) (gwyneth 20211031)
+
+		var (
+			testUUID = uuid.New().String()	// Random UUID (gwyneth 20211031 — from )
+			testValue = avatarUUID{ testAvatarName, testUUID, "all grids" }
+		)
+		jsonTestValue, err := json.Marshal(testValue)
+		checkErrPanic(err) // something went VERY wrong
+
+		// KVDB Initialisation & Tests
+		// Each case is different
+		switch goslConfig.database {
+			case "badger":
+				// Opt has already been set earlier. (gwyneth 20211106)
+				kv, err := badger.Open(Opt)
+				checkErrPanic(err) // should probably panic, cannot prep new database
+				txn := kv.NewTransaction(true)
+				err = txn.Set([]byte(testAvatarName), jsonTestValue)
+				checkErrPanic(err)
+				err = txn.Set([]byte(testUUID), jsonTestValue)
+				checkErrPanic(err)
+				err = txn.Commit()
+				checkErrPanic(err)
+				log.Debugf("badger SET %+v (json: %v)\n", testValue, string(jsonTestValue))
+				kv.Close()
+			case "buntdb":
+				goslConfig.dbNamePath = filepath.Join(goslConfig.myDir, databaseName)
+				db, err := buntdb.Open(goslConfig.dbNamePath)
+				checkErrPanic(err)
+				err = db.Update(func(tx *buntdb.Tx) error {
+					_, _, err := tx.Set(testAvatarName, string(jsonTestValue), nil)
+					return err
+				})
+				checkErr(err)
+				log.Debugf("buntdb SET %+v (json: %v)\n", testValue, string(jsonTestValue))
+				db.Close()
+			case "leveldb":
+				goslConfig.dbNamePath = filepath.Join(goslConfig.myDir, databaseName)
+				db, err := leveldb.OpenFile(goslConfig.dbNamePath, nil)
+				checkErrPanic(err)
+				err = db.Put([]byte(testAvatarName), jsonTestValue, nil)
+				checkErrPanic(err)
+				log.Debugf("leveldb SET %+v (json: %v)\n", testValue, string(jsonTestValue))
+				db.Close()
+		} // /switch
+		// common to all databases:
+		key, grid := searchKVname(testAvatarName)
+		log.Debugf("GET %q returned %q [grid %q]\n", testAvatarName, key, grid)
+		log.Info("KV database seems fine.")
+
+		if goslConfig.importFilename != "" {
+			log.Info("attempting to import", goslConfig.importFilename, "...")
+			importDatabase(goslConfig.importFilename)
+			log.Info("database finished import.")
+		} else {
+			// it's not an error if there is no name2key database available for import (gwyneth 20211027)
+			log.Debug("no database configured for import")
+		}
 	}
 
 	if goslConfig.isShell {
 		log.Info("starting to run as interactive shell")
-		reader := bufio.NewReader(os.Stdin)
 		fmt.Println("Ctrl-C to quit.")
 		var err error	// to avoid assigning text in a different scope (this is a bit awkward, but that's the problem with bi-assignment)
-		var checkInput, avatarName, avatarKey, gridName string
+		var avatarName, avatarKey, gridName string
+
+		rl, err := readline.New("> ")
+		if err != nil {
+			panic(err)
+		}
+		defer rl.Close()
+
 		for {
-			// Prompt and read
 			fmt.Print("enter avatar name or UUID: ")
-			checkInput, err = reader.ReadString('\n')
-			checkErr(err)
+			checkInput, err := rl.Readline()
+			if err != nil || checkInput == "quit" { // io.EOF
+				break
+			}
 			checkInput = strings.TrimRight(checkInput, "\r\n")
 			// fmt.Printf("Ok, got %s length is %d and UUID is %v\n", checkInput, len(checkInput), isValidUUID(checkInput))
 			if (len(checkInput) == 36) && isValidUUID(checkInput) {
@@ -336,7 +369,7 @@ func main() {
 	http.HandleFunc("/", handler)
 	log.Info("directory for database:", goslConfig.myDir)
 
-	if (goslConfig.isServer) {
+	if goslConfig.isServer {
 		log.Info("starting to run as web server on port :" + goslConfig.myPort)
 		err := http.ListenAndServe(":" + goslConfig.myPort, nil) // set listen port
 		checkErrPanic(err) // if it can't listen to all the above, then it has to abort anyway