re-write all the things

This commit is contained in:
jms 2020-11-07 20:24:12 -06:00
parent 313ea55f57
commit 1f05d1579c
18 changed files with 724 additions and 446 deletions

View File

@ -25,7 +25,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"io/ioutil" "io/ioutil"
"log" "log"
"shiny-pancake/database" "shiny-pancake/internal/model"
"os" "os"
"shiny-pancake/logger" "shiny-pancake/logger"
@ -90,7 +90,7 @@ func configWriter(f *tview.Form){
var fp string var fp string
var hostFile string var hostFile string
var cfg []byte var cfg []byte
var c database.HostDetails var c model.HostDetails
var hd string var hd string
hd = viper.GetString("hostsDirectory") hd = viper.GetString("hostsDirectory")
@ -120,7 +120,7 @@ func configWriter(f *tview.Form){
} }
file, err := os.Create(fp) file, err := os.Create(fp)
c = database.HostDetails{ip[3], hostFile, ip[1], ip[2]} c = model.HostDetails{ip[3], hostFile, ip[1], ip[2]}
if err != nil { if err != nil {
log.Fatal("unable to create file: " + fp) log.Fatal("unable to create file: " + fp)
} }

View File

@ -26,8 +26,9 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"shiny-pancake/database" "shiny-pancake/internal/database"
"shiny-pancake/internal/gui" "shiny-pancake/internal/gui"
"shiny-pancake/internal/model"
"shiny-pancake/logger" "shiny-pancake/logger"
"strconv" "strconv"
"strings" "strings"
@ -83,9 +84,10 @@ to quickly create a Cobra application.`,
// Print out the final choice. // Print out the final choice.
if fn = <-result; len(fn) !=0 { if fn = <-result; len(fn) !=0 {
var hostDir string var hostDir string
var db *database.Database
hostDir = viper.GetString("hostsDirectory") hostDir = viper.GetString("hostsDirectory")
file, _ := ioutil.ReadFile(hostDir + "/"+ fn) file, _ := ioutil.ReadFile(hostDir + "/"+ fn)
data := database.HostDetails{} data := model.HostDetails{}
err := json.Unmarshal(file, &data) err := json.Unmarshal(file, &data)
if err != nil { if err != nil {
var l logger.Log var l logger.Log
@ -97,7 +99,13 @@ to quickly create a Cobra application.`,
var l logger.Log var l logger.Log
l = logger.Log{"info", logrus.Fields{"loaded": fn }, "config loading to ui"} l = logger.Log{"info", logrus.Fields{"loaded": fn }, "config loading to ui"}
logger.Lgr(&l) logger.Lgr(&l)
gui.Gui(data) db = database.New(&data)
// Initialise the Gui / Tui
gui := gui.New(db)
if err := gui.Start(); err != nil {
log.Fatalf("main: Cannot start tui: %s", err)
}
} }
}, },

View File

@ -28,6 +28,7 @@ var (
hostsDirectory = "hosts" hostsDirectory = "hosts"
logsDirectory = "logs" logsDirectory = "logs"
configDirectory = "config" configDirectory = "config"
queryDirectory = "query"
) )
var cfgFile string var cfgFile string
@ -91,6 +92,7 @@ func initConfig() {
viper.SetDefault("hostsDirectory", home + "/" + configHome + "/" + hostsDirectory) viper.SetDefault("hostsDirectory", home + "/" + configHome + "/" + hostsDirectory)
viper.SetDefault("logsDirectory", home + "/" + configHome + "/" + logsDirectory) viper.SetDefault("logsDirectory", home + "/" + configHome + "/" + logsDirectory)
viper.SetDefault("configDirectory", home + "/" +configHome + "/" + configDirectory) viper.SetDefault("configDirectory", home + "/" +configHome + "/" + configDirectory)
viper.SetDefault("queryDirectory", home + "/" +configHome + "/" + queryDirectory)
viper.SetDefault("logLevel", "debug") viper.SetDefault("logLevel", "debug")
viper.SetDefault("logfile", "log") viper.SetDefault("logfile", "log")
viper.AutomaticEnv() // read in environment variables that match viper.AutomaticEnv() // read in environment variables that match
@ -118,6 +120,7 @@ func directoryInit() {
logsDirectory, logsDirectory,
hostsDirectory, hostsDirectory,
configDirectory, configDirectory,
queryDirectory,
} }
for _, dr := range s { for _, dr := range s {
if _, err := os.Stat(cfgDir + "/" + dr); os.IsNotExist(err) { if _, err := os.Stat(cfgDir + "/" + dr); os.IsNotExist(err) {

View File

@ -1,68 +0,0 @@
package database
import (
_ "github.com/lib/pq"
)
//func DbConn(c HostDetails) (*sql.DB, error) {
// var h string
// var u string
// var pw string
// var dbn string
// var psqlInfo string
// h = c.Hostname
// u = c.Username
// pw = c.Secret
// dbn = c.DatabaseName
//
// logger.Logger("[LOG] connection details: host = " + h + " username = " + u)
//
// found, testUserName := data.ViperPgmConfig("test_user", "r")
// if found == true {
// logger.Logger("[LOG] found test user in .pgm.yaml")
// }
// psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
// "password=%s dbname=%s sslmode=disable",
// h, 5432, u, pw, dbn)
// db, err := sql.Open("postgres", psqlInfo)
// if err != nil {
// var db *sql.DB
// switch u {
// case testUserName:
// logger.Logger("[ERROR] go tests expects to connect with the docker compose database." +
// "[ERROR] run \"docker-compose up\" and re-run tests")
// err := errors.New("unable to ping db" + dbn + "local db is closed, please run docker-compose up")
// return db, err
// default:
// logger.Logger("[ERROR] db error: " + err.Error())
// return db, err
// }
//
// }
// logger.Logger("[LOG] pinging db " + dbn)
// ctx := context.Background()
// err = db.PingContext(ctx)
//
// if err != nil {
// var db *sql.DB
// switch u {
// case testUserName:
// logger.Logger("[ERROR] db error, unable to ping: " + err.Error()+
// "\n[ERROR] ensure docker database is running and re-run tests")
// fmt.Println("local db is closed, please run docker-compose up")
// err1 := errors.New("unable to ping db" + dbn)
// return db, err1
// default:
// logger.Logger("[ERROR] db error: " + err.Error())
// return db, err
// }
// }
// return db, nil
//}
type HostDetails struct {
Secret string `json:"secret"`
Hostname string `json:"hostname"`
DatabaseName string `json:"databaseName"`
Username string `json:"username"`
}

View File

@ -0,0 +1,18 @@
version: '3.7'
services:
pg-db:
image: postgres:11.5
container_name: pg-db
expose:
- "5432"
ports:
- "5432:5432"
environment:
- POSTGRES_DB=localdb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- ${PWD}/internal/database/container/init.sql:/docker-entrypoint-initdb.d/init.sql

5
go.mod
View File

@ -4,7 +4,9 @@ go 1.15
require ( require (
github.com/charmbracelet/bubbletea v0.12.2 github.com/charmbracelet/bubbletea v0.12.2
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 github.com/gdamore/tcell v1.2.0
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1
github.com/idlephysicist/cave-logger v1.2.2
github.com/lib/pq v1.8.0 github.com/lib/pq v1.8.0
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/muesli/termenv v0.7.4 github.com/muesli/termenv v0.7.4
@ -12,4 +14,5 @@ require (
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
gitlab.com/tslocum/cview v1.5.1
) )

31
go.sum
View File

@ -13,6 +13,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -23,6 +24,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bvinc/go-sqlite-lite v0.6.1 h1:JU8Rz5YAOZQiU3WEulKF084wfXpytRiqD2IaW2QjPz4=
github.com/bvinc/go-sqlite-lite v0.6.1/go.mod h1:2GiE60NUdb0aNhDdY+LXgrqAVDpi2Ijc6dB6ZMp9x6s=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/charmbracelet/bubbletea v0.12.2 h1:y9Yo2Pv8tcm3mAJsWONGsmHhzrbNxJVxpVtemikxE9A= github.com/charmbracelet/bubbletea v0.12.2 h1:y9Yo2Pv8tcm3mAJsWONGsmHhzrbNxJVxpVtemikxE9A=
github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg= github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg=
@ -43,8 +46,14 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE=
github.com/gdamore/tcell v1.2.0 h1:ikixzsxc8K8o3V2/CEmyoEW8mJZaNYQQ3NP3VIQdUe4=
github.com/gdamore/tcell v1.2.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4= github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1 h1:gp9ujdOQmQf1gMvqOYYgxdMS5tRpRGE3HAgRH4Hgzd4=
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -97,6 +106,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/idlephysicist/cave-logger v1.2.2 h1:PoGzbYx9rJ1l/hZDA3bDJUW9M++fTMAlAbC7AW8thbI=
github.com/idlephysicist/cave-logger v1.2.2/go.mod h1:BZG2qItPt8BnN/+T837nzY57qRihQXcb2YXUZoRYcrU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -106,11 +117,13 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -118,6 +131,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@ -154,8 +168,10 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/tview v0.0.0-20190829161255-f8bc69b90341/go.mod h1:+rKjP5+h9HMwWRpAfhIkkQ9KE3m3Nz5rwn7YtUpwgqk=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743 h1:9BBjVJTRxuYBeCAv9DFH2hSzY0ujLx5sxMg5D3K/Xeg= github.com/rivo/tview v0.0.0-20201018122409-d551c850a743 h1:9BBjVJTRxuYBeCAv9DFH2hSzY0ujLx5sxMg5D3K/Xeg=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743/go.mod h1:t7mcA3nlK9dxD1DMoz/DQRMWFMkGBUj6rJBM5VNfLFA= github.com/rivo/tview v0.0.0-20201018122409-d551c850a743/go.mod h1:t7mcA3nlK9dxD1DMoz/DQRMWFMkGBUj6rJBM5VNfLFA=
github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -165,6 +181,7 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -184,9 +201,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
gitlab.com/tslocum/cbind v0.1.3 h1:FT/fTQ4Yj3eo5021lB3IbkIt8eVtYGhrw/xur+cjvUU=
gitlab.com/tslocum/cbind v0.1.3/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI=
gitlab.com/tslocum/cview v1.5.1 h1:1zHvNJvyhHxAVBf3FsSzzqgB15mrojNEm7VbvLXWcpQ=
gitlab.com/tslocum/cview v1.5.1/go.mod h1:BRtUi0zXzVXufhqFm/1GD7GL+iznKh5m9pEGN19SnKA=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -249,24 +271,31 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7 h1:XtNJkfEjb4zR3q20BBBcYUykVOEMgZeIUOpBPfNYgxg= golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7 h1:XtNJkfEjb4zR3q20BBBcYUykVOEMgZeIUOpBPfNYgxg=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -311,11 +340,13 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,21 @@
create schema test;
create table test.employees (
Id SERIAL NOT NULL PRIMARY KEY,
Name VARCHAR(50),
Location VARCHAR(50)
);
create table test.company (
cid SERIAL NOT NULL PRIMARY KEY,
Name VARCHAR(50)
);
create table test.employer (
eid SERIAL NOT NULL PRIMARY KEY,
Id INT,
cid INT
);
ALTER TABLE test.employer add constraint emp_fk foreign key(Id) REFERENCES test.employees(id) ON UPDATE CASCADE ON DELETE CASCADE;
insert into test.employees (name,location) VALUES ('bob','kalamazoo');
create role test with login password 'test';
insert into test.employees (name,location) VALUES ('cheeks','magoo');
update test.employees set location = 'buttsville' where name = 'cheeks';
grant all privileges on schema test to test;

View File

@ -0,0 +1,77 @@
package database
import (
"context"
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/sirupsen/logrus"
"shiny-pancake/internal/model"
)
const datetime = `2006-01-02T15:04:05Z`
const date = `2006-01-02`
type Database struct {
conn *sql.DB
log *logrus.Logger
ctx context.Context
}
func New(c *model.HostDetails) *Database {
var db Database
var h string
var u string
var pw string
var dbn string
var psqlInfo string
var log *logrus.Logger
h = c.Hostname
u = c.Username
pw = c.Secret
dbn = c.DatabaseName
db.ctx = context.Background()
db.log = log
psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
h, 5432, u, pw, dbn)
dbC, err := sql.Open("postgres", psqlInfo)
if err != nil {
log.Fatalf("database.new: Cannot establish connection to %s: %v", h, err)
}
db.conn = dbC
return &db
}
func (db *Database) UserSessions() ([]*model.Session, error) {
query := `select usename, count(usename) from pg_stat_activity where usename is not null group by usename`
rows, err := db.conn.Query(query)
if err != nil {
// handle this error better than this
panic(err)
}
sessions := make([]*model.Session, 0)
defer rows.Close()
for rows.Next() {
var sessCount model.Session
err = rows.Scan(&sessCount.Username, &sessCount.Count)
if err != nil {
// handle this error
panic(err)
}
sessions = append(sessions, &sessCount)
}
// get any error encountered during iteration
err = rows.Err()
if err != nil {
panic(err)
}
return sessions, err
}

View File

@ -0,0 +1,4 @@
[sessions.Current]
columns = 2
headers = ["users", "count users"]
query = "select usename, count(usename) from pg_stat_activity where usename is not null"

View File

@ -1,367 +1,367 @@
package gui package gui
//
import ( //import (
"database/sql" // "database/sql"
"fmt" // "fmt"
"github.com/sirupsen/logrus" // "github.com/sirupsen/logrus"
"reflect" // "reflect"
"shiny-pancake/database" // "shiny-pancake/internal/database"
"shiny-pancake/logger" // "shiny-pancake/logger"
"strconv" // "strconv"
"sync" // "sync"
"time" // "time"
//
"github.com/gdamore/tcell/v2" // "github.com/gdamore/tcell/v2"
_ "github.com/lib/pq" // _ "github.com/lib/pq"
"github.com/rivo/tview" // "github.com/rivo/tview"
) //)
//
const ( //const (
batchSize = 80 // The number of rows loaded per batch. // batchSize = 80 // The number of rows loaded per batch.
finderPage = "*finder*" // The name of the Finder page. // finderPage = "*finder*" // The name of the Finder page.
) //)
//
var ( //var (
app *tview.Application // The tview application. // app *tview.Application // The tview application.
pages *tview.Pages // The application pages. // panels *tview.Pages // The application panels.
finderFocus tview.Primitive // The primitive in the Finder that last had focus. // finderFocus tview.Primitive // The primitive in the Finder that last had focus.
) //)
//
// Main entry point. //// Main entry point.
func data(host database.HostDetails) { //func data(host database.HostDetails) {
var h string // var h string
var u string // var u string
var pw string // var pw string
var dbn string // var dbn string
var psqlInfo string // var psqlInfo string
h = host.Hostname // h = host.Hostname
u = host.Username // u = host.Username
pw = host.Secret // pw = host.Secret
dbn = host.DatabaseName // dbn = host.DatabaseName
// Start the application. // // Start the application.
psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ // psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable", // "password=%s dbname=%s sslmode=disable",
h, 5432, u, pw, dbn) // h, 5432, u, pw, dbn)
app = tview.NewApplication() // app = tview.NewApplication()
finder(psqlInfo) // finder(psqlInfo)
if err := app.Run(); err != nil { // if err := app.Run(); err != nil {
fmt.Printf("Error running application: %s\n", err) // fmt.Printf("Error running application: %s\n", err)
} // }
} //}
//
// Sets up a "Finder" used to navigate the databases, tables, and columns. //// Sets up a "Finder" used to navigate the databases, tables, and columns.
func finder(connString string) { //func finder(connString string) {
// Create the basic objects. // // Create the basic objects.
databases := tview.NewList().ShowSecondaryText(false) // databases := tview.NewList().ShowSecondaryText(false)
databases.SetBorder(true).SetTitle("Databases") // databases.SetBorder(true).SetTitle("Databases")
columns := tview.NewTable().SetBorders(true) // columns := tview.NewTable().SetBorders(true)
columns.SetBorder(true).SetTitle("Columns") // columns.SetBorder(true).SetTitle("Columns")
tables := tview.NewList() // tables := tview.NewList()
tables.ShowSecondaryText(false). // tables.ShowSecondaryText(false).
SetDoneFunc(func() { // SetDoneFunc(func() {
tables.Clear() // tables.Clear()
columns.Clear() // columns.Clear()
app.SetFocus(databases) // app.SetFocus(databases)
}) // })
tables.SetBorder(true).SetTitle("Tables") // tables.SetBorder(true).SetTitle("Tables")
//
// Create the layout. // // Create the layout.
flex := tview.NewFlex(). // flex := tview.NewFlex().
AddItem(databases, 0, 1, true). // AddItem(databases, 0, 1, true).
AddItem(tables, 0, 1, false). // AddItem(tables, 0, 1, false).
AddItem(columns, 0, 3, false) // AddItem(columns, 0, 3, false)
//
// We keep one connection pool per database. // // We keep one connection pool per database.
dbMutex := sync.Mutex{} // dbMutex := sync.Mutex{}
dbs := make(map[string]*sql.DB) // dbs := make(map[string]*sql.DB)
getDB := func(database string) *sql.DB { // getDB := func(database string) *sql.DB {
// Connect to a new database. // // Connect to a new database.
dbMutex.Lock() // dbMutex.Lock()
defer dbMutex.Unlock() // defer dbMutex.Unlock()
if db, ok := dbs[database]; ok { // if db, ok := dbs[database]; ok {
return db // return db
} // }
db, err := sql.Open("postgres", connString) // db, err := sql.Open("postgres", connString)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
dbs[database] = db // dbs[database] = db
return db // return db
} // }
//
// Get a list of all databases. // // Get a list of all databases.
generalDB, err := sql.Open("postgres", connString) // generalDB, err := sql.Open("postgres", connString)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
defer generalDB.Close() // We really close the DB because we only use it for this one query. // defer generalDB.Close() // We really close the DB because we only use it for this one query.
rows, err := generalDB.Query("select datname from pg_database where datistemplate = false") // rows, err := generalDB.Query("select datname from pg_database where datistemplate = false")
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
defer rows.Close() // defer rows.Close()
for rows.Next() { // for rows.Next() {
var dbName string // var dbName string
if err := rows.Scan(&dbName); err != nil { // if err := rows.Scan(&dbName); err != nil {
panic(err) // panic(err)
} // }
databases.AddItem(dbName, "", 0, func() { // databases.AddItem(dbName, "", 0, func() {
// A database was selected. Show all of its tables. // // A database was selected. Show all of its tables.
columns.Clear() // columns.Clear()
tables.Clear() // tables.Clear()
db := getDB(dbName) // db := getDB(dbName)
t, err := db.Query("select table_name from information_schema.tables where table_schema not in ('information_schema','pg_catalog')") // t, err := db.Query("select table_name from information_schema.tables where table_schema not in ('information_schema','pg_catalog')")
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
defer t.Close() // defer t.Close()
for t.Next() { // for t.Next() {
var tableName string // var tableName string
if err := t.Scan(&tableName); err != nil { // if err := t.Scan(&tableName); err != nil {
panic(err) // panic(err)
} // }
tables.AddItem(tableName, "", 0, nil) // tables.AddItem(tableName, "", 0, nil)
} // }
if err := t.Err(); err != nil { // if err := t.Err(); err != nil {
panic(err) // panic(err)
} // }
app.SetFocus(tables) // app.SetFocus(tables)
//
// When the user navigates to a table, show its columns. // // When the user navigates to a table, show its columns.
tables.SetChangedFunc(func(i int, tableName string, t string, s rune) { // tables.SetChangedFunc(func(i int, tableName string, t string, s rune) {
// A table was selected. Show its columns. // // A table was selected. Show its columns.
columns.Clear() // columns.Clear()
c, err := db.Query(` // c, err := db.Query(`
select // select
c.column_name, // c.column_name,
c.is_nullable, // c.is_nullable,
c.data_type, // c.data_type,
c.character_maximum_length, // c.character_maximum_length,
c.numeric_precision, // c.numeric_precision,
c.numeric_scale, // c.numeric_scale,
c.ordinal_position, // c.ordinal_position,
tc.constraint_type pkey // tc.constraint_type pkey
from // from
information_schema.columns c // information_schema.columns c
left join // left join
information_schema.constraint_column_usage as ccu // information_schema.constraint_column_usage as ccu
on // on
c.table_schema = ccu.table_schema // c.table_schema = ccu.table_schema
and c.table_name = ccu.table_name // and c.table_name = ccu.table_name
and c.column_name = ccu.column_name // and c.column_name = ccu.column_name
left join // left join
information_schema.table_constraints as tc // information_schema.table_constraints as tc
on // on
ccu.constraint_schema = tc.constraint_schema // ccu.constraint_schema = tc.constraint_schema
and ccu.constraint_name = tc.constraint_name // and ccu.constraint_name = tc.constraint_name
where // where
c.table_schema not in ('information_schema','pg_catalog') // c.table_schema not in ('information_schema','pg_catalog')
and c.table_name = $1 // and c.table_name = $1
`, tableName) // `, tableName)
if err != nil { // if err != nil {
var l logger.Log // var l logger.Log
l = logger.Log{"error", logrus.Fields{"database": err.Error() }, "an error occured in view."} // l = logger.Log{"error", logrus.Fields{"database": err.Error()}, "an error occured in view."}
logger.Lgr(&l) // logger.Lgr(&l)
} // }
defer c.Close() // defer c.Close()
columns.SetCell(0, 0, &tview.TableCell{Text: "Name", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // columns.SetCell(0, 0, &tview.TableCell{Text: "Name", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 1, &tview.TableCell{Text: "Type", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 1, &tview.TableCell{Text: "Type", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 2, &tview.TableCell{Text: "Size", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 2, &tview.TableCell{Text: "Size", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 3, &tview.TableCell{Text: "Null", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 3, &tview.TableCell{Text: "Null", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 4, &tview.TableCell{Text: "Constraint", Align: tview.AlignCenter, Color: tcell.ColorYellow}) // SetCell(0, 4, &tview.TableCell{Text: "Constraint", Align: tview.AlignCenter, Color: tcell.ColorYellow})
for c.Next() { // for c.Next() {
var ( // var (
columnName, isNullable, dataType string // columnName, isNullable, dataType string
constraintType sql.NullString // constraintType sql.NullString
size, numericPrecision, numericScale sql.NullInt64 // size, numericPrecision, numericScale sql.NullInt64
ordinalPosition int // ordinalPosition int
) // )
if err := c.Scan(&columnName, // if err := c.Scan(&columnName,
&isNullable, // &isNullable,
&dataType, // &dataType,
&size, // &size,
&numericPrecision, // &numericPrecision,
&numericScale, // &numericScale,
&ordinalPosition, // &ordinalPosition,
&constraintType, // &constraintType,
); err != nil { // ); err != nil {
panic(err) // panic(err)
} // }
sizeText := "" // sizeText := ""
if size.Valid { // if size.Valid {
sizeText = strconv.Itoa(int(size.Int64)) // sizeText = strconv.Itoa(int(size.Int64))
} else if numericPrecision.Valid { // } else if numericPrecision.Valid {
sizeText = strconv.Itoa(int(numericPrecision.Int64)) // sizeText = strconv.Itoa(int(numericPrecision.Int64))
if numericScale.Valid { // if numericScale.Valid {
sizeText += "," + strconv.Itoa(int(numericScale.Int64)) // sizeText += "," + strconv.Itoa(int(numericScale.Int64))
} // }
} // }
color := tcell.ColorWhite // color := tcell.ColorWhite
if constraintType.Valid { // if constraintType.Valid {
color = map[string]tcell.Color{ // color = map[string]tcell.Color{
"CHECK": tcell.ColorGreen, // "CHECK": tcell.ColorGreen,
"FOREIGN KEY": tcell.ColorDarkMagenta, // "FOREIGN KEY": tcell.ColorDarkMagenta,
"PRIMARY KEY": tcell.ColorRed, // "PRIMARY KEY": tcell.ColorRed,
"UNIQUE": tcell.ColorDarkCyan, // "UNIQUE": tcell.ColorDarkCyan,
}[constraintType.String] // }[constraintType.String]
} // }
columns.SetCell(ordinalPosition, 0, &tview.TableCell{Text: columnName, Color: color}). // columns.SetCell(ordinalPosition, 0, &tview.TableCell{Text: columnName, Color: color}).
SetCell(ordinalPosition, 1, &tview.TableCell{Text: dataType, Color: color}). // SetCell(ordinalPosition, 1, &tview.TableCell{Text: dataType, Color: color}).
SetCell(ordinalPosition, 2, &tview.TableCell{Text: sizeText, Align: tview.AlignRight, Color: color}). // SetCell(ordinalPosition, 2, &tview.TableCell{Text: sizeText, Align: tview.AlignRight, Color: color}).
SetCell(ordinalPosition, 3, &tview.TableCell{Text: isNullable, Align: tview.AlignRight, Color: color}). // SetCell(ordinalPosition, 3, &tview.TableCell{Text: isNullable, Align: tview.AlignRight, Color: color}).
SetCell(ordinalPosition, 4, &tview.TableCell{Text: constraintType.String, Align: tview.AlignLeft, Color: color}) // SetCell(ordinalPosition, 4, &tview.TableCell{Text: constraintType.String, Align: tview.AlignLeft, Color: color})
} // }
if err := c.Err(); err != nil { // if err := c.Err(); err != nil {
panic(err) // panic(err)
} // }
}) // })
tables.SetCurrentItem(0) // Trigger the initial selection. // tables.SetCurrentItem(0) // Trigger the initial selection.
//
// When the user selects a table, show its content. // // When the user selects a table, show its content.
tables.SetSelectedFunc(func(i int, tableName string, t string, s rune) { // tables.SetSelectedFunc(func(i int, tableName string, t string, s rune) {
content(db, dbName, tableName) // content(db, dbName, tableName)
}) // })
}) // })
} // }
if err := rows.Err(); err != nil { // if err := rows.Err(); err != nil {
panic(err) // panic(err)
} // }
//
// Set up the pages and show the Finder. // // Set up the panels and show the Finder.
pages = tview.NewPages(). // panels = tview.NewPages().
AddPage(finderPage, flex, true, true) // AddPage(finderPage, flex, true, true)
app.SetRoot(pages, true) // app.SetRoot(panels, true)
} //}
//
// Shows the contents of the given table. //// Shows the contents of the given table.
func content(db *sql.DB, dbName, tableName string) { //func content(db *sql.DB, dbName, tableName string) {
finderFocus = app.GetFocus() // finderFocus = app.GetFocus()
//
// If this page already exists, just show it. // // If this page already exists, just show it.
if pages.HasPage(dbName + "." + tableName) { // if panels.HasPage(dbName + "." + tableName) {
pages.SwitchToPage(dbName + "." + tableName) // panels.SwitchToPage(dbName + "." + tableName)
return // return
} // }
var l logger.Log // var l logger.Log
l = logger.Log{"info", logrus.Fields{"database": tableName }, "selected table to view"} // l = logger.Log{"info", logrus.Fields{"database": tableName}, "selected table to view"}
logger.Lgr(&l) // logger.Lgr(&l)
var schemaName string // var schemaName string
err := db.QueryRow(fmt.Sprintf("select table_schema from information_schema.tables where table_schema not in ('information_schema','pg_catalog') and table_name = '%s'", tableName)).Scan(&schemaName) // err := db.QueryRow(fmt.Sprintf("select table_schema from information_schema.tables where table_schema not in ('information_schema','pg_catalog') and table_name = '%s'", tableName)).Scan(&schemaName)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
//
// We display the data in a table embedded in a frame. // // We display the data in a table embedded in a frame.
table := tview.NewTable(). // table := tview.NewTable().
SetFixed(1, 0). // SetFixed(1, 0).
SetBordersColor(tcell.ColorYellow) // SetBordersColor(tcell.ColorYellow)
frame := tview.NewFrame(table). // frame := tview.NewFrame(table).
SetBorders(0, 0, 0, 0, 0, 0) // SetBorders(0, 0, 0, 0, 0, 0)
frame.SetBorder(true). // frame.SetBorder(true).
SetTitle(fmt.Sprintf(`Contents of table "%s.%s"`,schemaName, tableName)) // SetTitle(fmt.Sprintf(`Contents of table "%s.%s"`, schemaName, tableName))
//
// How many rows does this table have? // // How many rows does this table have?
var rowCount int // var rowCount int
err = db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", schemaName, tableName)).Scan(&rowCount) // err = db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", schemaName, tableName)).Scan(&rowCount)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
//
// Load a batch of rows. // // Load a batch of rows.
loadRows := func(offset int) { // loadRows := func(offset int) {
rows, err := db.Query(fmt.Sprintf("select * from %s.%s limit $1 offset $2", schemaName, tableName), batchSize, offset) // rows, err := db.Query(fmt.Sprintf("select * from %s.%s limit $1 offset $2", schemaName, tableName), batchSize, offset)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
defer rows.Close() // defer rows.Close()
//
// The first row in the table is the list of column names. // // The first row in the table is the list of column names.
columnNames, err := rows.Columns() // columnNames, err := rows.Columns()
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
for index, name := range columnNames { // for index, name := range columnNames {
table.SetCell(0, index, &tview.TableCell{Text: name, Align: tview.AlignCenter, Color: tcell.ColorYellow}) // table.SetCell(0, index, &tview.TableCell{Text: name, Align: tview.AlignCenter, Color: tcell.ColorYellow})
} // }
//
// Read the rows. // // Read the rows.
columns := make([]interface{}, len(columnNames)) // columns := make([]interface{}, len(columnNames))
columnPointers := make([]interface{}, len(columns)) // columnPointers := make([]interface{}, len(columns))
for index := range columnPointers { // for index := range columnPointers {
columnPointers[index] = &columns[index] // columnPointers[index] = &columns[index]
} // }
for rows.Next() { // for rows.Next() {
// Read the columns. // // Read the columns.
err := rows.Scan(columnPointers...) // err := rows.Scan(columnPointers...)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
//
// Transfer them to the table. // // Transfer them to the table.
row := table.GetRowCount() // row := table.GetRowCount()
for index, column := range columns { // for index, column := range columns {
switch value := column.(type) { // switch value := column.(type) {
case int64: // case int64:
table.SetCell(row, index, &tview.TableCell{Text: strconv.Itoa(int(value)), Align: tview.AlignRight, Color: tcell.ColorDarkCyan}) // table.SetCell(row, index, &tview.TableCell{Text: strconv.Itoa(int(value)), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
case float64: // case float64:
table.SetCell(row, index, &tview.TableCell{Text: strconv.FormatFloat(value, 'f', 2, 64), Align: tview.AlignRight, Color: tcell.ColorDarkCyan}) // table.SetCell(row, index, &tview.TableCell{Text: strconv.FormatFloat(value, 'f', 2, 64), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
case string: // case string:
table.SetCellSimple(row, index, value) // table.SetCellSimple(row, index, value)
case time.Time: // case time.Time:
t := value.Format("2006-01-02") // t := value.Format("2006-01-02")
table.SetCell(row, index, &tview.TableCell{Text: t, Align: tview.AlignRight, Color: tcell.ColorDarkMagenta}) // table.SetCell(row, index, &tview.TableCell{Text: t, Align: tview.AlignRight, Color: tcell.ColorDarkMagenta})
case []uint8: // case []uint8:
str := make([]byte, len(value)) // str := make([]byte, len(value))
for index, num := range value { // for index, num := range value {
str[index] = byte(num) // str[index] = byte(num)
} // }
table.SetCell(row, index, &tview.TableCell{Text: string(str), Align: tview.AlignRight, Color: tcell.ColorGreen}) // table.SetCell(row, index, &tview.TableCell{Text: string(str), Align: tview.AlignRight, Color: tcell.ColorGreen})
case nil: // case nil:
table.SetCell(row, index, &tview.TableCell{Text: "NULL", Align: tview.AlignCenter, Color: tcell.ColorRed}) // table.SetCell(row, index, &tview.TableCell{Text: "NULL", Align: tview.AlignCenter, Color: tcell.ColorRed})
default: // default:
// We've encountered a type that we don't know yet. // // We've encountered a type that we don't know yet.
t := reflect.TypeOf(value) // t := reflect.TypeOf(value)
str := "?nil?" // str := "?nil?"
if t != nil { // if t != nil {
str = "?" + t.String() + "?" // str = "?" + t.String() + "?"
} // }
table.SetCellSimple(row, index, str) // table.SetCellSimple(row, index, str)
} // }
} // }
} // }
if err := rows.Err(); err != nil { // if err := rows.Err(); err != nil {
panic(err) // panic(err)
} // }
//
// Show how much we've loaded. // // Show how much we've loaded.
frame.Clear() // frame.Clear()
loadMore := "" // loadMore := ""
if table.GetRowCount()-1 < rowCount { // if table.GetRowCount()-1 < rowCount {
loadMore = " - press Enter to load more" // loadMore = " - press Enter to load more"
} // }
loadMore = fmt.Sprintf("Loaded %d of %d rows%s. esc to return to previous menu", table.GetRowCount()-1, rowCount, loadMore) // loadMore = fmt.Sprintf("Loaded %d of %d rows%s. esc to return to previous menu", table.GetRowCount()-1, rowCount, loadMore)
frame.AddText(loadMore, false, tview.AlignCenter, tcell.ColorYellow) // frame.AddText(loadMore, false, tview.AlignCenter, tcell.ColorYellow)
} // }
//
// Load the first batch of rows. // // Load the first batch of rows.
loadRows(0) // loadRows(0)
//
// Handle key presses. // // Handle key presses.
table.SetDoneFunc(func(key tcell.Key) { // table.SetDoneFunc(func(key tcell.Key) {
switch key { // switch key {
case tcell.KeyEscape: // case tcell.KeyEscape:
// Go back to Finder. // // Go back to Finder.
pages.SwitchToPage(finderPage) // panels.SwitchToPage(finderPage)
if finderFocus != nil { // if finderFocus != nil {
app.SetFocus(finderFocus) // app.SetFocus(finderFocus)
} // }
case tcell.KeyEnter: // case tcell.KeyEnter:
// Load the next batch of rows. // // Load the next batch of rows.
loadRows(table.GetRowCount() - 1) // loadRows(table.GetRowCount() - 1)
table.ScrollToEnd() // table.ScrollToEnd()
} // }
}) // })
//
// Add a new page and show it. // // Add a new page and show it.
pages.AddPage(dbName+"."+tableName, frame, true, true) // panels.AddPage(dbName+"."+tableName, frame, true, true)
} //}

View File

@ -1,6 +1,15 @@
package gui package gui
import "github.com/rivo/tview" import (
"fmt"
"github.com/sirupsen/logrus"
"shiny-pancake/logger"
"shiny-pancake/internal/database"
"strings"
tview "gitlab.com/tslocum/cview"
)
type panels struct { type panels struct {
currentPanel int currentPanel int
@ -23,8 +32,84 @@ func newState() *state {
type Gui struct { type Gui struct {
app *tview.Application app *tview.Application
pages *tview.Pages panels *tview.Panels
state *state state *state
db *db.Database db *database.Database
//statsLocations *statsLocations }
func New(db *database.Database) *Gui {
return &Gui{
app: tview.NewApplication(),
panels: tview.NewPanels(),
state: newState(),
db: db,
}
}
// Start start application
func (g *Gui) Start() error {
g.initPanels()
var l logger.Log
l = logger.Log{"info", logrus.Fields{"init" : "ran"}, "got this far"}
logger.Lgr(&l)
if err := g.app.Run(); err != nil {
g.app.Stop()
return err
}
return nil
}
func (g *Gui) Stop() {
g.app.Stop()
}
// Page "definitions"
func (g *Gui) sessionPanel() *activeSessions {
for _, panel := range g.state.panels.panel {
if panel.name() == `activeSessions` {
return panel.(*activeSessions)
}
}
return nil
}
func (g *Gui) initPanels() {
g.state.tabBar = newTabBar()
// Page definitions
s := newSessions(g)
var l logger.Log
l = logger.Log{"info", logrus.Fields{"initPanels" : "ran"}, "got this far"}
logger.Lgr(&l)
/*
// NOTE: I would really like to get this working as it would be far neater.
// The issue is with the three panels being of different types.
// cannot use pg (type panel) as type tview.Primitive in argument to g.panels.AddPage:
// panel does not implement tview.Primitive (missing Blur method)
for idx, pg := range []panel{trips, cavers, caves} {
name := pg.name()
g.panels.AddPage(name, pg, true, idx == 0)
fmt.Fprintf(g.state.tabBar, ` %d ["%d"][darkcyan]%s[white][""] `, idx+1, idx, strings.Title(name))
}
g.state.tabBar.Highlight("0")
*/
// Add panels to the "book"
g.panels.AddPanel(`activeSessions`, s, true, true)
fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 0, 1, strings.Title(s.name()))
g.state.tabBar.Highlight("0")
// Panels
g.state.panels.panel = append(g.state.panels.panel, s)
g.app.SetRoot(g.panels, true)
} }

View File

@ -0,0 +1,18 @@
package gui
//func (g *Gui) startMonitoring() {
// stop := make(chan int, 1)
// g.state.stopChans["activeSessions"] = stop
// go g.sessionPanel().monitoringSessions(g)
//}
//
//func (g *Gui) stopMonitoring() {
// g.state.stopChans["activeSessions"] <- 1
//
//}
/*func (g *Gui) updateTask() {
g.app.QueueUpdateDraw(func() {
g.caversPanel().setEntries(g) // REVIEW: Why is this just people ?
})
}*/

9
internal/gui/panel.go Normal file
View File

@ -0,0 +1,9 @@
package gui
type panel interface {
name() string
entries(*Gui)
//setEntries(*Gui)
//focus(*Gui)
//unfocus()
}

46
internal/gui/sessions.go Normal file
View File

@ -0,0 +1,46 @@
package gui
import (
tview "gitlab.com/tslocum/cview"
"shiny-pancake/internal/model"
)
type resources struct {
ActSess []*model.Session
//statsLocations []*model.Statistic
}
type activeSessions struct {
*tview.Table
sn chan *model.Session
filterCol, filterTerm string
}
func newSessions(g *Gui) *activeSessions {
sess := &activeSessions{
Table: tview.NewTable(),
sn: make(chan *model.Session),
}
sess.SetBorder(true)
sess.entries(g)
return sess
}
func (t *activeSessions) name() string {
return `activeSessions`
}
func (t *activeSessions) entries(g *Gui) {
sss, err := g.db.UserSessions()
if err != nil {
return
}
g.state.resources.ActSess = sss
}

10
internal/gui/tabbar.go Normal file
View File

@ -0,0 +1,10 @@
package gui
import (
tview "gitlab.com/tslocum/cview"
)
func newTabBar() *tview.TextView {
return tview.NewTextView()
}

13
internal/model/forms.go Normal file
View File

@ -0,0 +1,13 @@
package model
type HostDetails struct {
Secret string `json:"secret"`
Hostname string `json:"hostname"`
DatabaseName string `json:"databaseName"`
Username string `json:"username"`
}
type Session struct {
Username string
Count int
}

BIN
main

Binary file not shown.