good progress
This commit is contained in:
parent
ac3e3a846e
commit
048940ef7a
14
cmd/add.go
14
cmd/add.go
@ -46,7 +46,6 @@ to quickly create a Cobra application.`,
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +63,6 @@ func init() {
|
|||||||
// addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const focusedTextColor = "205"
|
const focusedTextColor = "205"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -75,7 +73,6 @@ var (
|
|||||||
blurredSubmitButton = "[ " + te.String("Submit").Foreground(color("240")).String() + " ]"
|
blurredSubmitButton = "[ " + te.String("Submit").Foreground(color("240")).String() + " ]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// Will ask a user for a unique name until they exit or provide one.
|
// Will ask a user for a unique name until they exit or provide one.
|
||||||
func cfgRename(c data.HostDetails) {
|
func cfgRename(c data.HostDetails) {
|
||||||
p := tea.NewProgram(renameModel(c))
|
p := tea.NewProgram(renameModel(c))
|
||||||
@ -109,7 +106,6 @@ func toFile(fp string, c data.HostDetails) {
|
|||||||
tea.Quit()
|
tea.Quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type model struct {
|
type model struct {
|
||||||
index int
|
index int
|
||||||
dbFileName input.Model
|
dbFileName input.Model
|
||||||
@ -143,13 +139,10 @@ func initialModel() model {
|
|||||||
secret.Placeholder = "secret"
|
secret.Placeholder = "secret"
|
||||||
secret.Prompt = blurredPrompt
|
secret.Prompt = blurredPrompt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return model{0, dbFileName, hostName, dbName, userName, secret, blurredSubmitButton}
|
return model{0, dbFileName, hostName, dbName, userName, secret, blurredSubmitButton}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (m model) Init() tea.Cmd {
|
func (m model) Init() tea.Cmd {
|
||||||
return tea.Batch(
|
return tea.Batch(
|
||||||
input.Blink(m.dbFileName),
|
input.Blink(m.dbFileName),
|
||||||
@ -160,7 +153,6 @@ func (m model) Init() tea.Cmd {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
|
|
||||||
@ -224,7 +216,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.userName = inputs[3]
|
m.userName = inputs[3]
|
||||||
m.secret = inputs[4]
|
m.secret = inputs[4]
|
||||||
|
|
||||||
|
|
||||||
if m.index == len(inputs) {
|
if m.index == len(inputs) {
|
||||||
m.submitButton = focusedSubmitButton
|
m.submitButton = focusedSubmitButton
|
||||||
} else {
|
} else {
|
||||||
@ -240,7 +231,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func updateInputs(msg tea.Msg, m model) (model, tea.Cmd) {
|
func updateInputs(msg tea.Msg, m model) (model, tea.Cmd) {
|
||||||
var (
|
var (
|
||||||
cmd tea.Cmd
|
cmd tea.Cmd
|
||||||
@ -261,11 +251,9 @@ func updateInputs(msg tea.Msg, m model) (model, tea.Cmd) {
|
|||||||
|
|
||||||
m.secret, cmd = input.Update(msg, m.secret)
|
m.secret, cmd = input.Update(msg, m.secret)
|
||||||
|
|
||||||
|
|
||||||
return m, tea.Batch(cmds...)
|
return m, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (m model) View() string {
|
func (m model) View() string {
|
||||||
s := "\n"
|
s := "\n"
|
||||||
|
|
||||||
@ -311,7 +299,6 @@ func configurator(t []input.Model) {
|
|||||||
}
|
}
|
||||||
toFile(fp, c)
|
toFile(fp, c)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameModel(c data.HostDetails) rename {
|
func renameModel(c data.HostDetails) rename {
|
||||||
@ -363,7 +350,6 @@ func (m rename) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
m.textInput, cmd = input.Update(msg, m.textInput)
|
m.textInput, cmd = input.Update(msg, m.textInput)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|||||||
17
cmd/load.go
17
cmd/load.go
@ -17,9 +17,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"pgee/box"
|
|
||||||
"pgee/utils"
|
|
||||||
"pgm/data"
|
"pgm/data"
|
||||||
|
"pgm/loader"
|
||||||
|
"pgm/logger"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// billyCmd represents the billy command
|
// billyCmd represents the billy command
|
||||||
@ -35,15 +36,18 @@ to quickly create a Cobra application.`,
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var dbcfg string
|
var dbcfg string
|
||||||
var title string
|
var title string
|
||||||
var hosts []string
|
var pgHosts []string
|
||||||
hosts = data.ReadHosts()
|
pgHosts = data.ReadHosts()
|
||||||
title = "choose db to load, \"l\" or enter will load your choice"
|
title = "choose db to load, \"l\" or enter will load your choice"
|
||||||
box.Box(pgee, title, dbcfg)
|
err := loader.Loader(pgHosts, title, dbcfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] loading error for testing, ignore.")
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(loadCmd)
|
rootCmd.AddCommand(loadCmd)
|
||||||
|
|
||||||
@ -57,4 +61,3 @@ func init() {
|
|||||||
// is called directly, e.g.:
|
// is called directly, e.g.:
|
||||||
// billyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// billyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"pgm/config"
|
"pgm/config"
|
||||||
|
"pgm/logger"
|
||||||
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -64,6 +65,7 @@ func init() {
|
|||||||
// Cobra also supports local flags, which will only run
|
// Cobra also supports local flags, which will only run
|
||||||
// when this action is called directly.
|
// when this action is called directly.
|
||||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
viper.BindPFlag("temp_db", rootCmd.PersistentFlags().Lookup("temp_db"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
@ -78,7 +80,6 @@ func InitConfig() {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
os.Setenv("PGMHOME", home)
|
|
||||||
// Search config in home directory with name ".pgm" (without extension).
|
// Search config in home directory with name ".pgm" (without extension).
|
||||||
viper.AddConfigPath(home)
|
viper.AddConfigPath(home)
|
||||||
viper.SetConfigName(".pgm")
|
viper.SetConfigName(".pgm")
|
||||||
@ -100,7 +101,6 @@ func InitConfig() {
|
|||||||
|
|
||||||
// If a config file is found, read it in.
|
// If a config file is found, read it in.
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
if err := viper.ReadInConfig(); err == nil {
|
||||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
logger.Logger("[LOG] Using config file:"+ viper.ConfigFileUsed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,6 @@ func IsInit(h string, s string, l string, cfgFile string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// determines if key exists in config file
|
// determines if key exists in config file
|
||||||
func cfgExists(s string, d []string, cfgFile string) []string {
|
func cfgExists(s string, d []string, cfgFile string) []string {
|
||||||
var dir []string
|
var dir []string
|
||||||
@ -54,8 +53,6 @@ func cfgExists( s string, d []string, cfgFile string) []string {
|
|||||||
dir = strings.Split(s, "/")
|
dir = strings.Split(s, "/")
|
||||||
cfgPath = dir[len(dir)-1]
|
cfgPath = dir[len(dir)-1]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
configDirectoryKey := parseCfg(cfgPath)
|
configDirectoryKey := parseCfg(cfgPath)
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
if err := viper.ReadInConfig(); err == nil {
|
||||||
viper.SetConfigFile(cfgFile)
|
viper.SetConfigFile(cfgFile)
|
||||||
|
|||||||
24
data/data.go
24
data/data.go
@ -2,6 +2,7 @@ package data
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"pgm/logger"
|
"pgm/logger"
|
||||||
@ -40,3 +41,26 @@ func ReadHosts() []string {
|
|||||||
return pgee
|
return pgee
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns a value from the .pgm.yaml file
|
||||||
|
func ViperReturnKey(k string ) string {
|
||||||
|
var h string
|
||||||
|
var s string
|
||||||
|
h, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] could not set home directory: " + err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
viper.SetConfigName("scr")
|
||||||
|
viper.SetConfigType("json")
|
||||||
|
|
||||||
|
viper.AddConfigPath(h + "/.pgm/scn/")
|
||||||
|
err = viper.ReadInConfig() // Find and read the config file
|
||||||
|
if err != nil { // Handle errors reading the config file
|
||||||
|
logger.Logger("[ERROR] failed to load viper config: " + err.Error())
|
||||||
|
fmt.Println("error occurred and was logged.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
s = viper.GetString(k)
|
||||||
|
logger.Logger("[LOG] got value from .pgm.yaml: " + s )
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|||||||
55
db/db.go
Normal file
55
db/db.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"os"
|
||||||
|
"pgm/data"
|
||||||
|
"pgm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DbConn(c data.HostDetails) *sql.DB {
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
testUserName := data.ViperReturnKey("test_user")
|
||||||
|
logger.Logger("[LOG] reserved username is " + testUserName)
|
||||||
|
if err != nil {
|
||||||
|
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")
|
||||||
|
os.Exit(1)
|
||||||
|
default:
|
||||||
|
logger.Logger("[ERROR] db error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err = db.PingContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
switch u {
|
||||||
|
case testUserName:
|
||||||
|
logger.Logger("[ERROR] db error, unable to ping: " + err.Error()+
|
||||||
|
"[ERROR] ensure docker database is running and re-run tests")
|
||||||
|
os.Exit(1)
|
||||||
|
default:
|
||||||
|
logger.Logger("[ERROR] db error: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal 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}/sql/localDB.sql:/docker-entrypoint-initdb.d/localdb.sql
|
||||||
|
|
||||||
|
|
||||||
3
go.mod
3
go.mod
@ -6,9 +6,10 @@ require (
|
|||||||
github.com/charmbracelet/bubbles v0.7.1
|
github.com/charmbracelet/bubbles v0.7.1
|
||||||
github.com/charmbracelet/bubbletea v0.12.1
|
github.com/charmbracelet/bubbletea v0.12.1
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
|
github.com/lib/pq v1.8.0
|
||||||
github.com/magiconair/properties v1.8.4 // indirect
|
github.com/magiconair/properties v1.8.4 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
github.com/mitchellh/mapstructure v1.3.3
|
||||||
github.com/muesli/termenv v0.7.4
|
github.com/muesli/termenv v0.7.4
|
||||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||||
github.com/spf13/afero v1.4.1 // indirect
|
github.com/spf13/afero v1.4.1 // indirect
|
||||||
|
|||||||
1
go.sum
1
go.sum
@ -114,6 +114,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||||||
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/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=
|
||||||
|
|||||||
282
loader/loader.go
Normal file
282
loader/loader.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/muesli/termenv"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"pgm/config"
|
||||||
|
"pgm/data"
|
||||||
|
"pgm/db"
|
||||||
|
"pgm/logger"
|
||||||
|
"pgm/ui"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type host struct {
|
||||||
|
tic int
|
||||||
|
t string
|
||||||
|
choices []string
|
||||||
|
choice int
|
||||||
|
chosen bool
|
||||||
|
Quitting bool
|
||||||
|
Frames int
|
||||||
|
File string
|
||||||
|
isTest bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
term = termenv.ColorProfile()
|
||||||
|
subtle = makeFgStyle("241")
|
||||||
|
dot = colorFg(" • ", "236")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Loader(t []string, s string, b string) error {
|
||||||
|
var hostChoice host
|
||||||
|
var isT bool
|
||||||
|
isT = setTest(s)
|
||||||
|
logger.Logger("[LOG] is loader test: " + strconv.FormatBool(hostChoice.isTest))
|
||||||
|
hostChoice = host{60, s, t, 0, false, false, 0, b, isT}
|
||||||
|
if err := tea.NewProgram(hostChoice).Start(); err != nil {
|
||||||
|
if isT == true {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logger.Logger("[ERROR] tea error occured")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// method to enable go tests if set to true
|
||||||
|
// loader function will test that it can load the ui to chose a database
|
||||||
|
// and quits the ui
|
||||||
|
func setTest(s string) bool {
|
||||||
|
logger.Logger("[LOG] is test: " + s)
|
||||||
|
if s == "test" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m host) Init() tea.Cmd {
|
||||||
|
return tick()
|
||||||
|
}
|
||||||
|
|
||||||
|
func choicesUpdate(msg tea.Msg, m host) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "q", "esc":
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
case "j", "down":
|
||||||
|
m.choice += 1
|
||||||
|
if m.choice > len(m.choices) {
|
||||||
|
m.choice = len(m.choices)
|
||||||
|
}
|
||||||
|
case "k", "up":
|
||||||
|
m.choice -= 1
|
||||||
|
if m.choice < 0 {
|
||||||
|
m.choice = 0
|
||||||
|
}
|
||||||
|
case "l", "enter":
|
||||||
|
m.File = m.choices[m.choice]
|
||||||
|
m.chosen = true
|
||||||
|
return m, Frame()
|
||||||
|
}
|
||||||
|
|
||||||
|
case tickMsg:
|
||||||
|
if m.isTest == true {
|
||||||
|
m.File = "test"
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
if m.tic == 0 {
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
m.tic -= 1
|
||||||
|
return m, tick()
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update loop for the second view after a choice has been made
|
||||||
|
func (m host) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if msg, ok := msg.(tea.KeyMsg); ok {
|
||||||
|
k := msg.String()
|
||||||
|
if k == "q" || k == "esc" || k == "ctrl+c" {
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.chosen {
|
||||||
|
return choicesUpdate(msg, m)
|
||||||
|
}
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Views return a string based on data in the host. That string which will be
|
||||||
|
// rendered to the terminal.
|
||||||
|
func (m host) View() string {
|
||||||
|
logger.Logger("[LOG] view initialized")
|
||||||
|
var s string
|
||||||
|
if m.Quitting {
|
||||||
|
logger.Logger("[LOG] leaving pgm")
|
||||||
|
return "leaving pgm..."
|
||||||
|
|
||||||
|
}
|
||||||
|
if !m.chosen {
|
||||||
|
s = choicesView(m)
|
||||||
|
} else {
|
||||||
|
tea.Quit()
|
||||||
|
LoadUi(m)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passes our config to the screen file.
|
||||||
|
// screen is tasked with rendering more in depth views.
|
||||||
|
func LoadUi(m host) {
|
||||||
|
var g data.HostDetails
|
||||||
|
var h string
|
||||||
|
h, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] uable to set home dir: " + err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
config.ReadConfig()
|
||||||
|
l := viper.GetString("hosts_dir")
|
||||||
|
filepath := h + "/" + l + "/" + m.File
|
||||||
|
loadhost(filepath, &g)
|
||||||
|
Screener(g)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the menu for choosing a file from your configs.
|
||||||
|
// Exits after 60 seconds.
|
||||||
|
// Gets Title from outside in the load method if this... should ever be needed elsewhere.
|
||||||
|
func choicesView(m host) string {
|
||||||
|
var menu string
|
||||||
|
c := m.choice
|
||||||
|
|
||||||
|
tpl := m.t + "\n\n"
|
||||||
|
tpl += "%s\n\n"
|
||||||
|
tpl += "menu exits in %s seconds\n\n"
|
||||||
|
tpl += subtle("j/k, up/down: select") + dot + subtle("enter: choose") + dot + subtle("q, esc: quit")
|
||||||
|
for i, s := range m.choices {
|
||||||
|
menu = menu + "::" + checkbox(s, c == i)
|
||||||
|
}
|
||||||
|
mn := strings.Replace(menu, "::", "\n", -1)
|
||||||
|
choices := fmt.Sprintf(
|
||||||
|
mn,
|
||||||
|
)
|
||||||
|
|
||||||
|
return fmt.Sprintf(tpl, choices, colorFg(strconv.Itoa(m.tic), "79"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// the rest of these functions are to do with looks and aesthetics
|
||||||
|
|
||||||
|
// returns a neat checkbox
|
||||||
|
func checkbox(label string, checked bool) string {
|
||||||
|
if checked {
|
||||||
|
return colorFg("[x] "+label, "212")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[ ] %s", label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fun colors
|
||||||
|
func colorFg(val, color string) string {
|
||||||
|
return termenv.String(val).Foreground(term.Color(color)).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a function that will colorize the foreground of a given string.
|
||||||
|
func makeFgStyle(color string) func(string) string {
|
||||||
|
return termenv.Style{}.Foreground(term.Color(color)).Styled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame event
|
||||||
|
func Frame() tea.Cmd {
|
||||||
|
return tea.Tick(time.Second/60, func(time.Time) tea.Msg {
|
||||||
|
return FrameMsg{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type tickMsg struct{}
|
||||||
|
type FrameMsg struct{}
|
||||||
|
|
||||||
|
// exits if no choice is selected in a minute
|
||||||
|
func tick() tea.Cmd {
|
||||||
|
return tea.Tick(time.Second, func(time.Time) tea.Msg {
|
||||||
|
return tickMsg{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads a yaml file into the correct format for consumption.
|
||||||
|
// errs the pipeline step if there is not a configuration in this repository.
|
||||||
|
func loadhost(c string, p *data.HostDetails) *data.HostDetails {
|
||||||
|
logger.Logger("[LOG] file path string is: " + c)
|
||||||
|
cfg, err := ioutil.ReadFile(c)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] unable to find this filepath: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
json.Unmarshal(cfg, &p)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func Screener(g data.HostDetails) error {
|
||||||
|
var scn Scr
|
||||||
|
var dbConn *sql.DB
|
||||||
|
logger.Logger("[LOG] starting screen")
|
||||||
|
s := data.ViperReturnKey("UserSessions")
|
||||||
|
|
||||||
|
mapstructure.Decode(s, &scn)
|
||||||
|
dbConn = db.DbConn(g)
|
||||||
|
switch g.Username {
|
||||||
|
case "test":
|
||||||
|
dbConn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ui.GraphicUi(dbConn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple struct for a view in the UI.
|
||||||
|
type Scr struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Columns int `json:"columns"`
|
||||||
|
UIType string `json:"uiType"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Quitting bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Scr) Init() tea.Cmd {
|
||||||
|
return tick()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Scr) View() string {
|
||||||
|
|
||||||
|
return m.Query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Scr) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if msg, ok := msg.(tea.KeyMsg); ok {
|
||||||
|
k := msg.String()
|
||||||
|
if k == "q" || k == "esc" || k == "ctrl+c" {
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
@ -7,7 +7,10 @@ import (
|
|||||||
|
|
||||||
func Logger(s string) bool {
|
func Logger(s string) bool {
|
||||||
var h string
|
var h string
|
||||||
h = os.Getenv("PGMHOME")
|
h, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
if s == "test" {
|
if s == "test" {
|
||||||
os.Create("/var/tmp/PGMLOGTEST")
|
os.Create("/var/tmp/PGMLOGTEST")
|
||||||
file, err := os.OpenFile("/var/tmp/PGMLOGTEST", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
file, err := os.OpenFile("/var/tmp/PGMLOGTEST", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
|||||||
70
main_test.go
70
main_test.go
@ -1,10 +1,13 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"os"
|
"os"
|
||||||
"pgm/config"
|
"pgm/config"
|
||||||
|
"pgm/data"
|
||||||
|
"pgm/loader"
|
||||||
"pgm/logger"
|
"pgm/logger"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -14,7 +17,7 @@ import (
|
|||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
var cfg string
|
var cfg string
|
||||||
testCases := []initTests{
|
testCases := []initTest{
|
||||||
{a: "junk", b: "notreal", c: "failingonpurpose", want: true},
|
{a: "junk", b: "notreal", c: "failingonpurpose", want: true},
|
||||||
{a: "test_host_dir", b: "test_screen_dr", c: "test_log_dir", want: true},
|
{a: "test_host_dir", b: "test_screen_dr", c: "test_log_dir", want: true},
|
||||||
}
|
}
|
||||||
@ -57,7 +60,70 @@ func TestLoadConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type initTests struct {
|
// loader function will test that it can load the ui to chose a database
|
||||||
|
// and quits the ui.
|
||||||
|
// "test" is added into the array of files a user has configured and selected
|
||||||
|
func TestLoader(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var tc []string
|
||||||
|
var s string
|
||||||
|
var b string
|
||||||
|
var want error = nil
|
||||||
|
s = "test"
|
||||||
|
b = "test"
|
||||||
|
tc = data.ReadHosts()
|
||||||
|
tc = append(tc, "test")
|
||||||
|
got := loader.Loader(tc, s, b)
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("want %t, got %t", want, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestScreener(t *testing.T){
|
||||||
|
t.Parallel()
|
||||||
|
var p data.HostDetails
|
||||||
|
var testDb string
|
||||||
|
var want error = nil
|
||||||
|
var h string
|
||||||
|
h, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] could not set home directory: " + err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
viper.SetConfigName(".pgm") // config file name without extension
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath(h)
|
||||||
|
viper.AutomaticEnv() // read value ENV variable
|
||||||
|
|
||||||
|
err = viper.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] fatal error config file: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
testDb = viper.GetString("test_db")
|
||||||
|
err = json.Unmarshal([]byte(testDb), &p)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[LOG] test screener failed: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
got := loader.Screener(p)
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("want %t, got %t", want, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type viperHostTest struct {
|
||||||
|
a string
|
||||||
|
want bool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type initTest struct {
|
||||||
a, b, c string
|
a, b, c string
|
||||||
want bool
|
want bool
|
||||||
}
|
}
|
||||||
21
sql/localDB.sql
Normal file
21
sql/localDB.sql
Normal 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;
|
||||||
24
ui/ui.go
Normal file
24
ui/ui.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"database/sql"
|
||||||
|
"pgm/logger"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GraphicUi(db *sql.DB, ) {
|
||||||
|
row, err := db.Query("Select name from test.employees")
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger("[ERROR] database error occured: " + err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
defer row.Close()
|
||||||
|
for row.Next() {
|
||||||
|
fmt.Println(row)
|
||||||
|
}
|
||||||
|
tea.Quit()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user