Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI history skips blank lines. #4768

Merged
merged 5 commits into from
Nov 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 74 additions & 46 deletions cmd/influx/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package cli

import (
"bytes"
"encoding/csv"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/url"
"os"
"os/signal"
"os/user"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
"text/tabwriter"

"github.com/influxdb/influxdb/client"
Expand All @@ -26,6 +28,7 @@ const (
noTokenMsg = "Visit https://enterprise.influxdata.com to register for updates, InfluxDB server management, and monitoring.\n"
)

// CommandLine holds CLI configuration and state
type CommandLine struct {
Client *client.Client
Line *liner.State
Expand All @@ -48,13 +51,25 @@ type CommandLine struct {
PPS int // Controls how many points per second the import will allow via throttling
Path string
Compressed bool
Quit chan struct{}
osSignals chan os.Signal
historyFile *os.File
}

// New returns an instance of CommandLine
func New(version string) *CommandLine {
return &CommandLine{ClientVersion: version}
return &CommandLine{
ClientVersion: version,
Quit: make(chan struct{}, 1),
osSignals: make(chan os.Signal, 1),
}
}

// Run executes the CLI
func (c *CommandLine) Run() {
// register OS signals for graceful termination
signal.Notify(c.osSignals, os.Kill, os.Interrupt, syscall.SIGTERM)

var promptForPassword bool
// determine if they set the password flag but provided no value
for _, v := range os.Args {
Expand Down Expand Up @@ -139,56 +154,50 @@ func (c *CommandLine) Run() {

c.Version()

var historyFile string
var historyFilePath string
usr, err := user.Current()
// Only load history if we can get the user
// Only load/write history if we can get the user
if err == nil {
historyFile = filepath.Join(usr.HomeDir, ".influx_history")

if f, err := os.Open(historyFile); err == nil {
c.Line.ReadHistory(f)
f.Close()
historyFilePath = filepath.Join(usr.HomeDir, ".influx_history")
if c.historyFile, err = os.OpenFile(historyFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640); err == nil {
defer c.historyFile.Close()
c.Line.ReadHistory(c.historyFile)
}
}

// read from prompt until exit is run
for {
l, e := c.Line.Prompt("> ")
if e != nil {
break
}
if c.ParseCommand(l) {
// write out the history
if len(historyFile) > 0 {
select {
case <-c.osSignals:
close(c.Quit)
case <-c.Quit:
c.exit()
default:
l, e := c.Line.Prompt("> ")
if e != nil {
break
}
if c.ParseCommand(l) {
c.Line.AppendHistory(l)
if f, err := os.Create(historyFile); err == nil {
c.Line.WriteHistory(f)
f.Close()
_, err := c.Line.WriteHistory(c.historyFile)
if err != nil {
fmt.Printf("There was an error writing history file: %s\n", err)
}
}
} else {
break // exit main loop
}
}
}

// ParseCommand parses an instruction and calls related method, if any
func (c *CommandLine) ParseCommand(cmd string) bool {
lcmd := strings.TrimSpace(strings.ToLower(cmd))

split := strings.Split(lcmd, " ")
var tokens []string
for _, token := range split {
if token != "" {
tokens = append(tokens, token)
}
}
tokens := strings.Fields(lcmd)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny thing, I learned about this in other parts of InfluxDB code.


if len(tokens) > 0 {
switch tokens[0] {
case "":
break
case "exit":
// signal the program to exit
return false
close(c.Quit)
case "gopher":
c.gopher()
case "connect":
Expand Down Expand Up @@ -221,8 +230,10 @@ func (c *CommandLine) ParseCommand(cmd string) bool {
default:
c.ExecuteQuery(cmd)
}

return true
}
return true
return false
}

// Connect connects client to a server
Expand Down Expand Up @@ -255,17 +266,17 @@ func (c *CommandLine) Connect(cmd string) error {
return fmt.Errorf("Could not create client %s", err)
}
c.Client = cl
if _, v, e := c.Client.Ping(); e != nil {

var v string
if _, v, e = c.Client.Ping(); e != nil {
return fmt.Errorf("Failed to connect to %s\n", c.Client.Addr())
} else {
c.ServerVersion = v
}

_, c.ServerVersion, _ = c.Client.Ping()
c.ServerVersion = v

return nil
}

// SetAuth sets client authentication credentials
func (c *CommandLine) SetAuth(cmd string) {
// If they pass in the entire command, we should parse it
// auth <username> <password>
Expand Down Expand Up @@ -309,6 +320,7 @@ func (c *CommandLine) use(cmd string) {
fmt.Printf("Using database %s\n", d)
}

// SetPrecision sets client precision
func (c *CommandLine) SetPrecision(cmd string) {
// Remove the "precision" keyword if it exists
cmd = strings.TrimSpace(strings.Replace(cmd, "precision", "", -1))
Expand All @@ -327,6 +339,7 @@ func (c *CommandLine) SetPrecision(cmd string) {
}
}

// SetFormat sets output format
func (c *CommandLine) SetFormat(cmd string) {
// Remove the "format" keyword if it exists
cmd = strings.TrimSpace(strings.Replace(cmd, "format", "", -1))
Expand All @@ -341,6 +354,7 @@ func (c *CommandLine) SetFormat(cmd string) {
}
}

// SetWriteConsistency sets cluster consistency level
func (c *CommandLine) SetWriteConsistency(cmd string) {
// Remove the "consistency" keyword if it exists
cmd = strings.TrimSpace(strings.Replace(cmd, "consistency", "", -1))
Expand Down Expand Up @@ -425,6 +439,7 @@ func (c *CommandLine) parseInto(stmt string) string {
return stmt
}

// Insert runs an INSERT statement
func (c *CommandLine) Insert(stmt string) error {
i, point := parseNextIdentifier(stmt)
if !strings.EqualFold(i, "insert") {
Expand Down Expand Up @@ -455,6 +470,7 @@ func (c *CommandLine) Insert(stmt string) error {
return nil
}

// ExecuteQuery runs any query statement
func (c *CommandLine) ExecuteQuery(query string) error {
response, err := c.Client.Query(client.Query{Command: query, Database: c.Database})
if err != nil {
Expand All @@ -473,6 +489,7 @@ func (c *CommandLine) ExecuteQuery(query string) error {
return nil
}

// DatabaseToken retrieves database token
func (c *CommandLine) DatabaseToken() (string, error) {
response, err := c.Client.Query(client.Query{Command: "SHOW DIAGNOSTICS for 'registration'"})
if err != nil {
Expand All @@ -491,6 +508,7 @@ func (c *CommandLine) DatabaseToken() (string, error) {
return "", nil
}

// FormatResponse formats output to previsouly chosen format
func (c *CommandLine) FormatResponse(response *client.Response, w io.Writer) {
switch c.Format {
case "json":
Expand Down Expand Up @@ -644,6 +662,7 @@ func interfaceToString(v interface{}) string {
}
}

// Settings prints current settings
func (c *CommandLine) Settings() {
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 1, '\t', 0)
Expand Down Expand Up @@ -685,14 +704,9 @@ func (c *CommandLine) help() {
}

func (c *CommandLine) history() {
usr, err := user.Current()
// Only load history if we can get the user
if err == nil {
historyFile := filepath.Join(usr.HomeDir, ".influx_history")
if history, err := ioutil.ReadFile(historyFile); err == nil {
fmt.Print(string(history))
}
}
var buf bytes.Buffer
c.Line.WriteHistory(&buf)
fmt.Print(buf.String())
}

func (c *CommandLine) gopher() {
Expand Down Expand Up @@ -752,6 +766,20 @@ func (c *CommandLine) gopher() {
`)
}

// Version prints CLI version
func (c *CommandLine) Version() {
fmt.Println("InfluxDB shell " + c.ClientVersion)
}

func (c *CommandLine) exit() {
// write to history file
_, err := c.Line.WriteHistory(c.historyFile)
if err != nil {
fmt.Printf("There was an error writing history file: %s\n", err)
}
// release line resources
c.Line.Close()
c.Line = nil
// exit CLI
os.Exit(0)
}
Loading