Refactor how CLI manages configuration options

pull/7682/head
Edd Robinson 2016-12-02 16:02:07 +00:00
parent 840fb6af38
commit a98ad483ee
5 changed files with 113 additions and 134 deletions

View File

@ -87,14 +87,15 @@ func ParseConnectionString(path string, ssl bool) (url.URL, error) {
// UserAgent: If not provided, will default "InfluxDBClient",
// Timeout: If not provided, will default to 0 (no timeout)
type Config struct {
URL url.URL
UnixSocket string
Username string
Password string
UserAgent string
Timeout time.Duration
Precision string
UnsafeSsl bool
URL url.URL
UnixSocket string
Username string
Password string
UserAgent string
Timeout time.Duration
Precision string
WriteConsistency string
UnsafeSsl bool
}
// NewConfig will create a config to be used in connecting to the client

View File

@ -9,7 +9,6 @@ import (
"io"
"io/ioutil"
"net"
"net/url"
"os"
"os/signal"
"path/filepath"
@ -36,35 +35,29 @@ var ErrBlankCommand = errors.New("empty input")
// CommandLine holds CLI configuration and state
type CommandLine struct {
Client *client.Client
Line *liner.State
Host string
Port int
UnixSocket string
Username string
Password string
Database string
Ssl bool
UnsafeSsl bool
RetentionPolicy string
ClientVersion string
ServerVersion string
Pretty bool // controls pretty print for json
Format string // controls the output format. Valid values are json, csv, or column
Precision string
WriteConsistency string
Execute string
ShowVersion bool
Import bool
PPS int // Controls how many points per second the import will allow via throttling
Path string
Compressed bool
Chunked bool
Quit chan struct{}
IgnoreSignals bool // Ignore signals normally caught by this process (used primarily for testing)
ForceTTY bool // Force the CLI to act as if it were connected to a TTY
osSignals chan os.Signal
historyFilePath string
Line *liner.State
Host string
Port int
Database string
Ssl bool
RetentionPolicy string
ClientVersion string
ServerVersion string
Pretty bool // controls pretty print for json
Format string // controls the output format. Valid values are json, csv, or column
Execute string
ShowVersion bool
Import bool
Chunked bool
Quit chan struct{}
IgnoreSignals bool // Ignore signals normally caught by this process (used primarily for testing)
ForceTTY bool // Force the CLI to act as if it were connected to a TTY
osSignals chan os.Signal
historyFilePath string
Client *client.Client
ClientConfig client.Config // Client config options.
ImporterConfig v8.Config // Importer configuration options.
}
// New returns an instance of CommandLine
@ -84,7 +77,7 @@ func (c *CommandLine) Run() error {
// determine if they set the password flag but provided no value
for _, v := range os.Args {
v = strings.ToLower(v)
if (strings.HasPrefix(v, "-password") || strings.HasPrefix(v, "--password")) && c.Password == "" {
if (strings.HasPrefix(v, "-password") || strings.HasPrefix(v, "--password")) && c.ClientConfig.Password == "" {
promptForPassword = true
break
}
@ -96,8 +89,8 @@ func (c *CommandLine) Run() error {
}
// Read environment variables for username/password.
if c.Username == "" {
c.Username = os.Getenv("INFLUX_USERNAME")
if c.ClientConfig.Username == "" {
c.ClientConfig.Username = os.Getenv("INFLUX_USERNAME")
}
// If we are going to be prompted for a password, always use the entered password.
if promptForPassword {
@ -110,9 +103,9 @@ func (c *CommandLine) Run() error {
if e != nil {
return errors.New("Unable to parse password")
}
c.Password = p
} else if c.Password == "" {
c.Password = os.Getenv("INFLUX_PASSWORD")
c.ClientConfig.Password = p
} else if c.ClientConfig.Password == "" {
c.ClientConfig.Password = os.Getenv("INFLUX_PASSWORD")
}
if err := c.Connect(""); err != nil {
@ -122,7 +115,7 @@ func (c *CommandLine) Run() error {
}
// Modify precision.
c.SetPrecision(c.Precision)
c.SetPrecision(c.ClientConfig.Precision)
if c.Execute != "" {
// Make the non-interactive mode send everything through the CLI's parser
@ -137,25 +130,17 @@ func (c *CommandLine) Run() error {
}
if c.Import {
path := net.JoinHostPort(c.Host, strconv.Itoa(c.Port))
u, e := client.ParseConnectionString(path, c.Ssl)
addr := net.JoinHostPort(c.Host, strconv.Itoa(c.Port))
u, e := client.ParseConnectionString(addr, c.Ssl)
if e != nil {
return e
}
config := v8.NewConfig()
config.Username = c.Username
config.Password = c.Password
config.Precision = "ns"
config.WriteConsistency = "any"
config.Path = c.Path
config.Version = c.ClientVersion
// Copy the latest importer config and inject the latest client config
// into it.
config := c.ImporterConfig
config.Config = c.ClientConfig
config.URL = u
config.Compressed = c.Compressed
config.PPS = c.PPS
config.UnixSocket = c.UnixSocket
config.UnsafeSsl = c.UnsafeSsl
config.Precision = c.Precision
i := v8.NewImporter(config)
if err := i.Import(); err != nil {
@ -284,46 +269,39 @@ func (c *CommandLine) ParseCommand(cmd string) error {
// Connect connects client to a server
func (c *CommandLine) Connect(cmd string) error {
var cl *client.Client
var u url.URL
// Remove the "connect" keyword if it exists
path := strings.TrimSpace(strings.Replace(cmd, "connect", "", -1))
// If they didn't provide a connection string, use the current settings
if path == "" {
path = net.JoinHostPort(c.Host, strconv.Itoa(c.Port))
addr := strings.TrimSpace(strings.Replace(cmd, "connect", "", -1))
if addr == "" {
// If they didn't provide a connection string, use the current settings
addr = net.JoinHostPort(c.Host, strconv.Itoa(c.Port))
}
var e error
u, e = client.ParseConnectionString(path, c.Ssl)
if e != nil {
return e
URL, err := client.ParseConnectionString(addr, c.Ssl)
if err != nil {
return err
}
config := client.NewConfig()
config.URL = u
config.UnixSocket = c.UnixSocket
config.Username = c.Username
config.Password = c.Password
config.UserAgent = "InfluxDBShell/" + c.ClientVersion
config.Precision = c.Precision
config.UnsafeSsl = c.UnsafeSsl
cl, err := client.NewClient(config)
// Create copy of the current client config and create a new client.
ClientConfig := c.ClientConfig
ClientConfig.UserAgent = "InfluxDBShell/" + c.ClientVersion
ClientConfig.URL = URL
client, err := client.NewClient(ClientConfig)
if err != nil {
return fmt.Errorf("Could not create client %s", err)
}
c.Client = cl
c.Client = client
var v string
if _, v, e = c.Client.Ping(); e != nil {
return fmt.Errorf("Failed to connect to %s: %s\n", c.Client.Addr(), e.Error())
_, v, err := c.Client.Ping()
if err != nil {
return fmt.Errorf("Failed to connect to %s: %v\n", c.Client.Addr(), err)
}
c.ServerVersion = v
// Update the command with the current connection information
if h, p, err := net.SplitHostPort(config.URL.Host); err == nil {
c.Host = h
if i, err := strconv.Atoi(p); err == nil {
if host, port, err := net.SplitHostPort(ClientConfig.URL.Host); err == nil {
c.Host = host
if i, err := strconv.Atoi(port); err == nil {
c.Port = i
}
}
@ -343,25 +321,25 @@ func (c *CommandLine) SetAuth(cmd string) {
}
if len(args) == 2 {
c.Username = args[0]
c.Password = args[1]
c.ClientConfig.Username = args[0]
c.ClientConfig.Password = args[1]
} else {
u, e := c.Line.Prompt("username: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return
}
c.Username = strings.TrimSpace(u)
c.ClientConfig.Username = strings.TrimSpace(u)
p, e := c.Line.PasswordPrompt("password: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return
}
c.Password = p
c.ClientConfig.Password = p
}
// Update the client as well
c.Client.SetAuth(c.Username, c.Password)
c.Client.SetAuth(c.ClientConfig.Username, c.ClientConfig.Password)
}
func (c *CommandLine) use(cmd string) {
@ -378,7 +356,7 @@ func (c *CommandLine) use(cmd string) {
fmt.Printf("ERR: %s\n", err)
return
} else if err := response.Error(); err != nil {
if c.Username == "" {
if c.ClientConfig.Username == "" {
fmt.Printf("ERR: %s\n", err)
return
}
@ -423,11 +401,11 @@ func (c *CommandLine) SetPrecision(cmd string) {
switch cmd {
case "h", "m", "s", "ms", "u", "ns":
c.Precision = cmd
c.Client.SetPrecision(c.Precision)
c.ClientConfig.Precision = cmd
c.Client.SetPrecision(c.ClientConfig.Precision)
case "rfc3339":
c.Precision = ""
c.Client.SetPrecision(c.Precision)
c.ClientConfig.Precision = ""
c.Client.SetPrecision(c.ClientConfig.Precision)
default:
fmt.Printf("Unknown precision %q. Please use rfc3339, h, m, s, ms, u or ns.\n", cmd)
}
@ -460,7 +438,7 @@ func (c *CommandLine) SetWriteConsistency(cmd string) {
fmt.Printf("Unknown consistency level %q. Please use any, one, quorum, or all.\n", cmd)
return
}
c.WriteConsistency = cmd
c.ClientConfig.WriteConsistency = cmd
}
// isWhitespace returns true if the rune is a space, tab, or newline.
@ -549,8 +527,8 @@ func (c *CommandLine) Insert(stmt string) error {
},
Database: c.Database,
RetentionPolicy: c.RetentionPolicy,
Precision: c.Precision,
WriteConsistency: c.WriteConsistency,
Precision: c.ClientConfig.Precision,
WriteConsistency: c.ClientConfig.WriteConsistency,
})
if err != nil {
fmt.Printf("ERR: %s\n", err)
@ -776,11 +754,11 @@ func (c *CommandLine) Settings() {
} else {
fmt.Fprintf(w, "Host\t%s\n", c.Host)
}
fmt.Fprintf(w, "Username\t%s\n", c.Username)
fmt.Fprintf(w, "Username\t%s\n", c.ClientConfig.Username)
fmt.Fprintf(w, "Database\t%s\n", c.Database)
fmt.Fprintf(w, "Pretty\t%v\n", c.Pretty)
fmt.Fprintf(w, "Format\t%s\n", c.Format)
fmt.Fprintf(w, "Write Consistency\t%s\n", c.WriteConsistency)
fmt.Fprintf(w, "Write Consistency\t%s\n", c.ClientConfig.WriteConsistency)
fmt.Fprintln(w)
w.Flush()
}

View File

@ -67,7 +67,7 @@ func TestRunCLI_ExecuteInsert(t *testing.T) {
c := cli.New(CLIENT_VERSION)
c.Host = h
c.Port, _ = strconv.Atoi(p)
c.Precision = "ms"
c.ClientConfig.Precision = "ms"
c.Execute = "INSERT sensor,floor=1 value=2"
c.IgnoreSignals = true
c.ForceTTY = true
@ -87,11 +87,11 @@ func TestSetAuth(t *testing.T) {
c.SetAuth("auth " + u + " " + p)
// validate CLI configuration
if c.Username != u {
t.Fatalf("Username is %s but should be %s", c.Username, u)
if c.ClientConfig.Username != u {
t.Fatalf("Username is %s but should be %s", c.ClientConfig.Username, u)
}
if c.Password != p {
t.Fatalf("Password is %s but should be %s", c.Password, p)
if c.ClientConfig.Password != p {
t.Fatalf("Password is %s but should be %s", c.ClientConfig.Password, p)
}
}
@ -105,15 +105,15 @@ func TestSetPrecision(t *testing.T) {
// validate set non-default precision
p := "ns"
c.SetPrecision("precision " + p)
if c.Precision != p {
t.Fatalf("Precision is %s but should be %s", c.Precision, p)
if c.ClientConfig.Precision != p {
t.Fatalf("Precision is %s but should be %s", c.ClientConfig.Precision, p)
}
// validate set default precision which equals empty string
p = "rfc3339"
c.SetPrecision("precision " + p)
if c.Precision != "" {
t.Fatalf("Precision is %s but should be empty", c.Precision)
if c.ClientConfig.Precision != "" {
t.Fatalf("Precision is %s but should be empty", c.ClientConfig.Precision)
}
}
@ -142,22 +142,22 @@ func TestSetWriteConsistency(t *testing.T) {
// set valid write consistency
consistency := "all"
c.SetWriteConsistency("consistency " + consistency)
if c.WriteConsistency != consistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.WriteConsistency, consistency)
if c.ClientConfig.WriteConsistency != consistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.ClientConfig.WriteConsistency, consistency)
}
// set different valid write consistency and validate change
consistency = "quorum"
c.SetWriteConsistency("consistency " + consistency)
if c.WriteConsistency != consistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.WriteConsistency, consistency)
if c.ClientConfig.WriteConsistency != consistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.ClientConfig.WriteConsistency, consistency)
}
// set invalid write consistency and verify there was no change
invalidConsistency := "invalid_consistency"
c.SetWriteConsistency("consistency " + invalidConsistency)
if c.WriteConsistency == invalidConsistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.WriteConsistency, consistency)
if c.ClientConfig.WriteConsistency == invalidConsistency {
t.Fatalf("WriteConsistency is %s but should be %s", c.ClientConfig.WriteConsistency, consistency)
}
}
@ -340,7 +340,8 @@ func TestParseCommand_UseAuth(t *testing.T) {
t.Errorf("%d. unexpected error. expected %v, actual %v", i, nil, err)
continue
}
m := cli.CommandLine{Client: c, Username: tt.user}
m := cli.CommandLine{Client: c}
m.ClientConfig.Username = tt.user
if err := m.ParseCommand(tt.cmd); err != nil {
t.Fatalf(`%d. Got error %v for command %q, expected nil.`, i, err, tt.cmd)
@ -371,8 +372,8 @@ func TestParseCommand_Consistency(t *testing.T) {
t.Fatalf(`Got error %v for command %q, expected nil.`, err, test.cmd)
}
if c.WriteConsistency != "one" {
t.Fatalf(`Command "consistency" changed consistency to %q. Expected one`, c.WriteConsistency)
if c.ClientConfig.WriteConsistency != "one" {
t.Fatalf(`Command "consistency" changed consistency to %q. Expected one`, c.ClientConfig.WriteConsistency)
}
}
}

View File

@ -39,22 +39,22 @@ func main() {
fs := flag.NewFlagSet("InfluxDB shell version "+version, flag.ExitOnError)
fs.StringVar(&c.Host, "host", client.DefaultHost, "Influxdb host to connect to.")
fs.IntVar(&c.Port, "port", client.DefaultPort, "Influxdb port to connect to.")
fs.StringVar(&c.UnixSocket, "socket", c.UnixSocket, "Influxdb unix socket to connect to.")
fs.StringVar(&c.Username, "username", c.Username, "Username to connect to the server.")
fs.StringVar(&c.Password, "password", c.Password, `Password to connect to the server. Leaving blank will prompt for password (--password="").`)
fs.StringVar(&c.ClientConfig.UnixSocket, "socket", "", "Influxdb unix socket to connect to.")
fs.StringVar(&c.ClientConfig.Username, "username", "", "Username to connect to the server.")
fs.StringVar(&c.ClientConfig.Password, "password", "", `Password to connect to the server. Leaving blank will prompt for password (--password="").`)
fs.StringVar(&c.Database, "database", c.Database, "Database to connect to the server.")
fs.BoolVar(&c.Ssl, "ssl", false, "Use https for connecting to cluster.")
fs.BoolVar(&c.UnsafeSsl, "unsafeSsl", false, "Set this when connecting to the cluster using https and not use SSL verification.")
fs.BoolVar(&c.ClientConfig.UnsafeSsl, "unsafeSsl", false, "Set this when connecting to the cluster using https and not use SSL verification.")
fs.StringVar(&c.Format, "format", defaultFormat, "Format specifies the format of the server responses: json, csv, or column.")
fs.StringVar(&c.Precision, "precision", defaultPrecision, "Precision specifies the format of the timestamp: rfc3339,h,m,s,ms,u or ns.")
fs.StringVar(&c.WriteConsistency, "consistency", "all", "Set write consistency level: any, one, quorum, or all.")
fs.StringVar(&c.ClientConfig.Precision, "precision", defaultPrecision, "Precision specifies the format of the timestamp: rfc3339,h,m,s,ms,u or ns.")
fs.StringVar(&c.ClientConfig.WriteConsistency, "consistency", "all", "Set write consistency level: any, one, quorum, or all.")
fs.BoolVar(&c.Pretty, "pretty", false, "Turns on pretty print for the json format.")
fs.StringVar(&c.Execute, "execute", c.Execute, "Execute command and quit.")
fs.BoolVar(&c.ShowVersion, "version", false, "Displays the InfluxDB version.")
fs.BoolVar(&c.Import, "import", false, "Import a previous database.")
fs.IntVar(&c.PPS, "pps", defaultPPS, "How many points per second the import will allow. By default it is zero and will not throttle importing.")
fs.StringVar(&c.Path, "path", "", "path to the file to import")
fs.BoolVar(&c.Compressed, "compressed", false, "set to true if the import file is compressed")
fs.IntVar(&c.ImporterConfig.PPS, "pps", defaultPPS, "How many points per second the import will allow. By default it is zero and will not throttle importing.")
fs.StringVar(&c.ImporterConfig.Path, "path", "", "path to the file to import")
fs.BoolVar(&c.ImporterConfig.Compressed, "compressed", false, "set to true if the import file is compressed")
// Define our own custom usage to print
fs.Usage = func() {

View File

@ -17,11 +17,10 @@ const batchSize = 5000
// Config is the config used to initialize a Importer importer
type Config struct {
Path string
Version string
Compressed bool
WriteConsistency string
PPS int
Path string // Path to import data.
Version string
Compressed bool // Whether import data is gzipped.
PPS int // points per second importer imports with.
client.Config
}