Cobra upgrade: includes shell completion work (#81)

Addressed #80
This commit is contained in:
Neil O'Toole 2021-02-22 00:37:00 -07:00 committed by GitHub
parent b5fdc5c0d3
commit 6870327508
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1229 additions and 567 deletions

View File

@ -101,7 +101,7 @@ brews:
name: sq name: sq
homepage: "https://sq.io" homepage: "https://sq.io"
description: "sq is a swiss army knife for data" description: "sq is a swiss army knife for data"
caveats: "This is a preview release of sq. Use with caution." caveats: "For shell completion installation instructions, execute: sq completion --help"
tap: tap:
owner: neilotoole owner: neilotoole

View File

@ -16,6 +16,7 @@ or dropping tables.
For other installation options, see [here](https://github.com/neilotoole/sq/wiki/Home#Install). For other installation options, see [here](https://github.com/neilotoole/sq/wiki/Home#Install).
It is strongly advised to install [shell completion](#shell-completion).
### macOS ### macOS
@ -52,6 +53,11 @@ sudo rpm -i https://github.com/neilotoole/sq/releases/latest/download/sq-linux-a
yum localinstall -y https://github.com/neilotoole/sq/releases/latest/download/sq-linux-amd64.rpm yum localinstall -y https://github.com/neilotoole/sq/releases/latest/download/sq-linux-amd64.rpm
``` ```
## Shell completion
Shell completion is available for `bash`, `zsh`, `fish`, and `powershell`.
Execute `sq completion --help` for installation instructions.
## Quickstart ## Quickstart

View File

@ -32,6 +32,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
@ -68,6 +69,10 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func init() {
cobra.EnableCommandSorting = false
}
// errNoMsg is a sentinel error indicating that a command // errNoMsg is a sentinel error indicating that a command
// has failed, but that no error message should be printed. // has failed, but that no error message should be printed.
// This is useful in the case where any error information may // This is useful in the case where any error information may
@ -77,7 +82,7 @@ var errNoMsg = errors.New("")
// Execute builds a RunContext using ctx and default // Execute builds a RunContext using ctx and default
// settings, and invokes ExecuteWith. // settings, and invokes ExecuteWith.
func Execute(ctx context.Context, stdin *os.File, stdout, stderr io.Writer, args []string) error { func Execute(ctx context.Context, stdin *os.File, stdout, stderr io.Writer, args []string) error {
rc, err := newDefaultRunContext(ctx, stdin, stdout, stderr) rc, err := newDefaultRunContext(stdin, stdout, stderr)
if err != nil { if err != nil {
printError(rc, err) printError(rc, err)
return err return err
@ -85,35 +90,64 @@ func Execute(ctx context.Context, stdin *os.File, stdout, stderr io.Writer, args
defer rc.Close() // ok to call rc.Close on nil rc defer rc.Close() // ok to call rc.Close on nil rc
return ExecuteWith(rc, args) return ExecuteWith(ctx, rc, args)
} }
// ExecuteWith invokes the cobra CLI framework, ultimately // ExecuteWith invokes the cobra CLI framework, ultimately
// resulting in a command being executed. The caller must // resulting in a command being executed. The caller must
// invoke rc.Close. // invoke rc.Close.
func ExecuteWith(rc *RunContext, args []string) error { func ExecuteWith(ctx context.Context, rc *RunContext, args []string) error {
rc.Log.Debugf("EXECUTE: %s", strings.Join(args, " ")) rc.Log.Debugf("EXECUTE: %s", strings.Join(args, " "))
rc.Log.Debugf("Build: %s %s %s", buildinfo.Version, buildinfo.Commit, buildinfo.Timestamp) rc.Log.Debugf("Build: %s %s %s", buildinfo.Version, buildinfo.Commit, buildinfo.Timestamp)
rc.Log.Debugf("Config (cfg version %q) from: %s", rc.Config.Version, rc.ConfigStore.Location()) rc.Log.Debugf("Config (cfg version %q) from: %s", rc.Config.Version, rc.ConfigStore.Location())
// NOTE: (Feb 2021): The project has been finally been upgraded
// to spf13/cobra v1.1.3, which does provide support
// for context.Context. However, the sq codebase is still using
// the workaround to smuggle Context to the commands. Presumably
// we'll refactor the code at some point to make use of cobra's
// support for Context.
ctx = WithRunContext(ctx, rc)
rootCmd := newCommandTree(rc) rootCmd := newCommandTree(rc)
var err error
// The following is a workaround for the fact that cobra doesn't // The following is a workaround for the fact that cobra doesn't
// currently (as of 2017) support executing the root command with // currently (as of 2017) support executing the root command with
// arbitrary args. That is to say, if you execute: // arbitrary args. That is to say, if you execute:
// //
// sq arg1 arg2 // $ sq @sakila_sl3.actor
// //
// then cobra will look for a command named "arg1", and when it // then cobra will look for a command named "@sakila_sl3.actor",
// doesn't find such a command, it returns an "unknown command" // and when it doesn't find such a command, it returns
// error. // an "unknown command" error.
cmd, _, err := rootCmd.Find(args[1:]) //
// NOTE: This entire mechanism is ancient. Perhaps cobra
// now handles this situation?
// We need to perform handling for autocomplete
if len(args) > 0 && args[0] == "__complete" {
if hasMatchingChildCommand(rootCmd, args[1]) {
// If there is a matching child command, we let rootCmd
// handle it, as per normal.
rootCmd.SetArgs(args)
} else {
// There's no command matching the first argument to __complete.
// Therefore, we assume that we want to perform completion
// for the "slq" command (which is the pseudo-root command).
effectiveArgs := append([]string{"__complete", "slq"}, args[1:]...)
rootCmd.SetArgs(effectiveArgs)
}
} else {
var cmd *cobra.Command
cmd, _, err = rootCmd.Find(args)
if err != nil { if err != nil {
// This err will be the "unknown command" error. // This err will be the "unknown command" error.
// cobra still returns cmd though. It should be // cobra still returns cmd though. It should be
// the root cmd. // the root cmd.
if cmd == nil || cmd.Name() != rootCmd.Name() { if cmd == nil || cmd.Name() != rootCmd.Name() {
// should never happen // Not sure if this can happen anymore? Can prob delete?
panic(fmt.Sprintf("bad cobra cmd state: %v", cmd)) panic(fmt.Sprintf("bad cobra cmd state: %v", cmd))
} }
@ -121,15 +155,15 @@ func ExecuteWith(rc *RunContext, args []string) error {
// to the "slq" command by modifying args to // to the "slq" command by modifying args to
// look like: [query, arg1, arg2] -- noting that SetArgs // look like: [query, arg1, arg2] -- noting that SetArgs
// doesn't want the first args element. // doesn't want the first args element.
queryCmdArgs := append([]string{"slq"}, args[1:]...) effectiveArgs := append([]string{"slq"}, args...)
rootCmd.SetArgs(queryCmdArgs) rootCmd.SetArgs(effectiveArgs)
} else { } else {
if cmd.Name() == rootCmd.Name() { if cmd.Name() == rootCmd.Name() {
// Not sure why we have two paths to this, but it appears // Not sure why we have two paths to this, but it appears
// that we've found the root cmd again, so again // that we've found the root cmd again, so again
// we redirect to "query" cmd. // we redirect to "slq" cmd.
a := append([]string{"slq"}, args[1:]...) a := append([]string{"slq"}, args...)
rootCmd.SetArgs(a) rootCmd.SetArgs(a)
} else { } else {
// It's just a normal command like "sq ls" or such. // It's just a normal command like "sq ls" or such.
@ -137,13 +171,14 @@ func ExecuteWith(rc *RunContext, args []string) error {
// Explicitly set the args on rootCmd as this makes // Explicitly set the args on rootCmd as this makes
// cobra happy when this func is executed via tests. // cobra happy when this func is executed via tests.
// Haven't explored the reason why. // Haven't explored the reason why.
rootCmd.SetArgs(args[1:]) rootCmd.SetArgs(args)
}
} }
} }
// Execute the rootCmd; cobra will find the appropriate // Execute rootCmd; cobra will find the appropriate
// sub-command, and ultimately execute that command. // sub-command, and ultimately execute that command.
err = rootCmd.Execute() err = rootCmd.ExecuteContext(ctx)
if err != nil { if err != nil {
printError(rc, err) printError(rc, err)
} }
@ -151,11 +186,17 @@ func ExecuteWith(rc *RunContext, args []string) error {
return err return err
} }
// cobraMu exists because cobra relies upon package-level
// constructs. This does not sit well with parallel tests.
var cobraMu sync.Mutex
// newCommandTree builds sq's command tree, returning // newCommandTree builds sq's command tree, returning
// the root cobra command. // the root cobra command.
func newCommandTree(rc *RunContext) (rootCmd *cobra.Command) { func newCommandTree(rc *RunContext) (rootCmd *cobra.Command) {
rootCmd = newRootCmd() cobraMu.Lock()
defer cobraMu.Unlock()
rootCmd = newRootCmd()
rootCmd.SetOut(rc.Out) rootCmd.SetOut(rc.Out)
rootCmd.SetErr(rc.ErrOut) rootCmd.SetErr(rc.ErrOut)
@ -164,42 +205,52 @@ func newCommandTree(rc *RunContext) (rootCmd *cobra.Command) {
// The behavior of cobra in this regard seems to have // The behavior of cobra in this regard seems to have
// changed? This particular incantation currently does the trick. // changed? This particular incantation currently does the trick.
rootCmd.Flags().Bool(flagHelp, false, "Show sq help") rootCmd.Flags().Bool(flagHelp, false, "Show sq help")
helpCmd := addCmd(rc, rootCmd, newHelpCmd) rootCmd.Flags().SortFlags = false
helpCmd := addCmd(rc, rootCmd, newHelpCmd())
rootCmd.SetHelpCommand(helpCmd) rootCmd.SetHelpCommand(helpCmd)
addCmd(rc, rootCmd, newSLQCmd) addCmd(rc, rootCmd, newSLQCmd())
addCmd(rc, rootCmd, newSQLCmd) addCmd(rc, rootCmd, newSQLCmd())
addCmd(rc, rootCmd, newSrcCommand) addCmd(rc, rootCmd, newSrcCommand())
addCmd(rc, rootCmd, newSrcAddCmd) addCmd(rc, rootCmd, newSrcAddCmd())
addCmd(rc, rootCmd, newSrcListCmd) addCmd(rc, rootCmd, newSrcListCmd())
addCmd(rc, rootCmd, newSrcRemoveCmd) addCmd(rc, rootCmd, newSrcRemoveCmd())
addCmd(rc, rootCmd, newScratchCmd) addCmd(rc, rootCmd, newScratchCmd())
addCmd(rc, rootCmd, newInspectCmd) addCmd(rc, rootCmd, newInspectCmd())
addCmd(rc, rootCmd, newPingCmd) addCmd(rc, rootCmd, newPingCmd())
addCmd(rc, rootCmd, newVersionCmd) addCmd(rc, rootCmd, newVersionCmd())
addCmd(rc, rootCmd, newDriversCmd)
tblCmd := addCmd(rc, rootCmd, newTblCmd) driverCmd := addCmd(rc, rootCmd, newDriverCmd())
addCmd(rc, tblCmd, newTblCopyCmd) addCmd(rc, driverCmd, newDriverListCmd())
addCmd(rc, tblCmd, newTblTruncateCmd)
addCmd(rc, tblCmd, newTblDropCmd)
addCmd(rc, rootCmd, newInstallBashCompletionCmd) tblCmd := addCmd(rc, rootCmd, newTblCmd())
addCmd(rc, rootCmd, newGenerateZshCompletionCmd) addCmd(rc, tblCmd, newTblCopyCmd())
addCmd(rc, tblCmd, newTblTruncateCmd())
addCmd(rc, tblCmd, newTblDropCmd())
addCmd(rc, rootCmd, newCompletionCmd())
return rootCmd return rootCmd
} }
// runFunc is an expansion of cobra's RunE func that // hasMatchingChildCommand returns true if s is a full or prefix
// adds a RunContext as the first param. // match for any of cmd's children. For example, if cmd has
type runFunc func(rc *RunContext, cmd *cobra.Command, args []string) error // children [inspect, ls, rm], then "insp" or "ls" would return true.
func hasMatchingChildCommand(cmd *cobra.Command, s string) bool {
for _, child := range cmd.Commands() {
if strings.HasPrefix(child.Name(), s) {
return true
}
}
return false
}
// addCmd adds the command returned by cmdFn to parentCmd. // addCmd adds the command returned by cmdFn to parentCmd.
func addCmd(rc *RunContext, parentCmd *cobra.Command, cmdFn func() (*cobra.Command, runFunc)) *cobra.Command { func addCmd(rc *RunContext, parentCmd, cmd *cobra.Command) *cobra.Command {
cmd, fn := cmdFn() cmd.Flags().SortFlags = false
if cmd.Name() != "help" { if cmd.Name() != "help" {
// Don't add the --help flag to the help command. // Don't add the --help flag to the help command.
@ -209,18 +260,19 @@ func addCmd(rc *RunContext, parentCmd *cobra.Command, cmdFn func() (*cobra.Comma
cmd.PreRunE = func(cmd *cobra.Command, args []string) error { cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
rc.Cmd = cmd rc.Cmd = cmd
rc.Args = args rc.Args = args
err := rc.preRunE() err := rc.init()
return err return err
} }
runE := cmd.RunE
cmd.RunE = func(cmd *cobra.Command, args []string) error { cmd.RunE = func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed(flagVersion) { if cmd.Flags().Changed(flagVersion) {
// Bit of a hack: flag --version on any command // Bit of a hack: flag --version on any command
// results in execVersion being invoked // results in execVersion being invoked
return execVersion(rc, cmd, args) return execVersion(cmd, args)
} }
return fn(rc, cmd, args) return runE(cmd, args)
} }
// We handle the errors ourselves (rather than let cobra do it) // We handle the errors ourselves (rather than let cobra do it)
@ -232,13 +284,26 @@ func addCmd(rc *RunContext, parentCmd *cobra.Command, cmdFn func() (*cobra.Comma
return cmd return cmd
} }
type runContextKey struct{}
// WithRunContext returns ctx with rc added as a value.
func WithRunContext(ctx context.Context, rc *RunContext) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, runContextKey{}, rc)
}
// RunContextFrom extracts the RunContext added to ctx via WithRunContext.
func RunContextFrom(ctx context.Context) *RunContext {
return ctx.Value(runContextKey{}).(*RunContext)
}
// RunContext is a container for injectable resources passed // RunContext is a container for injectable resources passed
// to all execX funcs. The Close method should be invoked when // to all execX funcs. The Close method should be invoked when
// the RunContext is no longer needed. // the RunContext is no longer needed.
type RunContext struct { type RunContext struct {
// Context is the run's context.Context.
Context context.Context
// Stdin typically is os.Stdin, but can be changed for testing. // Stdin typically is os.Stdin, but can be changed for testing.
Stdin *os.File Stdin *os.File
@ -269,6 +334,9 @@ type RunContext struct {
// Log is the run's logger. // Log is the run's logger.
Log lg.Log Log lg.Log
initOnce sync.Once
initErr error
// writers holds the various writer types that // writers holds the various writer types that
// the CLI uses to print output. // the CLI uses to print output.
writers *writers writers *writers
@ -288,9 +356,8 @@ type RunContext struct {
// example if there's a config error). We do this to provide // example if there's a config error). We do this to provide
// enough framework so that such an error can be logged or // enough framework so that such an error can be logged or
// printed per the normal mechanisms if at all possible. // printed per the normal mechanisms if at all possible.
func newDefaultRunContext(ctx context.Context, stdin *os.File, stdout, stderr io.Writer) (*RunContext, error) { func newDefaultRunContext(stdin *os.File, stdout, stderr io.Writer) (*RunContext, error) {
rc := &RunContext{ rc := &RunContext{
Context: ctx,
Stdin: stdin, Stdin: stdin,
Out: stdout, Out: stdout,
ErrOut: stderr, ErrOut: stderr,
@ -325,10 +392,21 @@ func newDefaultRunContext(ctx context.Context, stdin *os.File, stdout, stderr io
return rc, nil return rc, nil
} }
// preRunE is invoked by cobra prior to the command RunE being // init is invoked by cobra prior to the command RunE being
// invoked. It sets up the registry, databases, writers and related // invoked. It sets up the registry, databases, writers and related
// fundamental components. // fundamental components. Subsequent invocations of this method
func (rc *RunContext) preRunE() error { // are no-op.
func (rc *RunContext) init() error {
rc.initOnce.Do(func() {
rc.initErr = rc.doInit()
})
return rc.initErr
}
// doInit performs the actual work of initializing rc.
// It must only be invoked once.
func (rc *RunContext) doInit() error {
rc.clnup = cleanup.New() rc.clnup = cleanup.New()
log, cfg := rc.Log, rc.Config log, cfg := rc.Log, rc.Config

View File

@ -2,6 +2,7 @@ package cli_test
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"image/gif" "image/gif"
"io/ioutil" "io/ioutil"
@ -28,27 +29,23 @@ func TestSmoke(t *testing.T) {
t.Parallel() t.Parallel()
// Execute a bunch of smoke test cases. // Execute a bunch of smoke test cases.
sqargs := func(a ...string) []string {
return append([]string{"sq"}, a...)
}
testCases := []struct { testCases := []struct {
a []string a []string
// errBecause, if non-empty, indicates an error is expected. // errBecause, if non-empty, indicates an error is expected.
errBecause string errBecause string
}{ }{
{a: sqargs("ls")}, {a: []string{"ls"}},
{a: sqargs("ls", "-v")}, {a: []string{"ls", "-v"}},
{a: sqargs("ls", "--help")}, {a: []string{"ls", "--help"}},
{a: sqargs("inspect"), errBecause: "no active data source"}, {a: []string{"inspect"}, errBecause: "no active data source"},
{a: sqargs("inspect", "--help")}, {a: []string{"inspect", "--help"}},
{a: sqargs("version")}, {a: []string{"version"}},
{a: sqargs("--version")}, {a: []string{"--version"}},
{a: sqargs("help")}, {a: []string{"help"}},
{a: sqargs("--help")}, {a: []string{"--help"}},
{a: sqargs("ping", "all")}, {a: []string{"ping", "all"}},
{a: sqargs("ping", "--help")}, {a: []string{"ping", "--help"}},
{a: sqargs("ping"), errBecause: "no active data source"}, {a: []string{"ping"}, errBecause: "no active data source"},
} }
for _, tc := range testCases { for _, tc := range testCases {
@ -58,7 +55,7 @@ func TestSmoke(t *testing.T) {
t.Parallel() t.Parallel()
rc, out, errOut := newTestRunCtx(testlg.New(t)) rc, out, errOut := newTestRunCtx(testlg.New(t))
err := cli.ExecuteWith(rc, tc.a) err := cli.ExecuteWith(context.Background(), rc, tc.a)
// We log sq's output before doing assert, because it reads // We log sq's output before doing assert, because it reads
// better in testing's output that way. // better in testing's output that way.

View File

@ -12,34 +12,34 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func newSrcAddCmd() (*cobra.Command, runFunc) { func newSrcAddCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "add [--driver=TYPE] [--handle=@HANDLE] LOCATION", Use: "add [--driver=TYPE] [--handle=@HANDLE] LOCATION",
RunE: execSrcAdd,
Example: ` # add a Postgres source; will have generated handle @sakila_pg Example: ` # add a Postgres source; will have generated handle @sakila_pg
sq add 'postgres://user:pass@localhost/sakila?sslmode=disable' $ sq add 'postgres://user:pass@localhost/sakila?sslmode=disable'
# same as above, but explicitly setting flags # same as above, but explicitly setting flags
sq add --handle=@sakila_pg --driver=postgres 'postgres://user:pass@localhost/sakila?sslmode=disable' $ sq add --handle=@sakila_pg --driver=postgres 'postgres://user:pass@localhost/sakila?sslmode=disable'
# same as above, but with short flags # same as above, but with short flags
sq add -h @sakila_pg --d postgres 'postgres://user:pass@localhost/sakila?sslmode=disable' $ sq add -h @sakila_pg --d postgres 'postgres://user:pass@localhost/sakila?sslmode=disable'
# add a SQL Server source; will have generated handle @sakila_mssql # add a SQL Server source; will have generated handle @sakila_mssql or similar
sq add 'sqlserver://user:pass@localhost?database=sakila' $ sq add 'sqlserver://user:pass@localhost?database=sakila'
# add a sqlite db # add a sqlite db
sq add ./testdata/sqlite1.db $ sq add ./testdata/sqlite1.db
# add an Excel spreadsheet, with options # add an Excel spreadsheet, with options
sq add ./testdata/test1.xlsx --opts=header=true $ sq add ./testdata/test1.xlsx --opts=header=true
# add a CSV source, with options # add a CSV source, with options
sq add ./testdata/person.csv --opts='header=true' $ sq add ./testdata/person.csv --opts=header=true
# add a CSV source from a server (will be downloaded) # add a CSV source from a server (will be downloaded)
sq add https://sq.io/testdata/actor.csv $ sq add https://sq.io/testdata/actor.csv
`, `,
Short: "Add data source",
Long: `Add data source specified by LOCATION and optionally identified by @HANDLE. Long: `Add data source specified by LOCATION and optionally identified by @HANDLE.
The format of LOCATION varies, but is generally a DB connection string, a The format of LOCATION varies, but is generally a DB connection string, a
file path, or a URL. file path, or a URL.
@ -53,35 +53,41 @@ on LOCATION and the source driver type.
If flag --driver is omitted, sq will attempt to determine the If flag --driver is omitted, sq will attempt to determine the
type from LOCATION via file suffix, content type, etc.. If the result type from LOCATION via file suffix, content type, etc.. If the result
is ambiguous, specify the driver tye type via flag --driver. is ambiguous, specify the driver type via flag --driver.
Flag --opts sets source specific options. Generally opts are relevant Flag --opts sets source-specific options. Generally opts are relevant
to document source types (such as a CSV file). The most common to document source types (such as a CSV file). The most common
use is to specify that the document has a header row: use is to specify that the document has a header row:
sq add actor.csv --opts=header=true $ sq add actor.csv --opts=header=true
Available source driver types can be listed via "sq drivers". Available source driver types can be listed via "sq driver ls".
At a minimum, the following drivers are bundled: At a minimum, the following drivers are bundled:
sqlite3 SQLite3 sqlite3 SQLite
postgres Postgres postgres PostgreSQL
sqlserver Microsoft SQL Server sqlserver Microsoft SQL Server
xlsx Microsoft Excel XLSX
mysql MySQL mysql MySQL
csv Comma-Separated Values csv Comma-Separated Values
tsv Tab-Separated Values tsv Tab-Separated Values
json JSON
jsona JSON Array: LF-delimited JSON arrays
jsonl JSON Lines: LF-delimited JSON objects
xlsx Microsoft Excel XLSX
`, `,
Short: "Add data source",
} }
cmd.Flags().StringP(flagDriver, flagDriverShort, "", flagDriverUsage) cmd.Flags().StringP(flagDriver, flagDriverShort, "", flagDriverUsage)
_ = cmd.RegisterFlagCompletionFunc(flagDriver, completeDriverType)
cmd.Flags().StringP(flagSrcOptions, "", "", flagSrcOptionsUsage) cmd.Flags().StringP(flagSrcOptions, "", "", flagSrcOptionsUsage)
cmd.Flags().StringP(flagHandle, flagHandleShort, "", flagHandleUsage) cmd.Flags().StringP(flagHandle, flagHandleShort, "", flagHandleUsage)
return cmd, execSrcAdd return cmd
} }
func execSrcAdd(rc *RunContext, cmd *cobra.Command, args []string) error { func execSrcAdd(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
if len(args) != 1 { if len(args) != 1 {
return errz.Errorf(msgInvalidArgs) return errz.Errorf(msgInvalidArgs)
} }
@ -94,9 +100,8 @@ func execSrcAdd(rc *RunContext, cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed(flagDriver) { if cmd.Flags().Changed(flagDriver) {
val, _ := cmd.Flags().GetString(flagDriver) val, _ := cmd.Flags().GetString(flagDriver)
typ = source.Type(strings.TrimSpace(val)) typ = source.Type(strings.TrimSpace(val))
} else { } else {
typ, err = rc.files.Type(rc.Context, loc) typ, err = rc.files.Type(cmd.Context(), loc)
if err != nil { if err != nil {
return err return err
} }
@ -148,8 +153,8 @@ func execSrcAdd(rc *RunContext, cmd *cobra.Command, args []string) error {
// unlike the other SQL DBs sq supports so far. // unlike the other SQL DBs sq supports so far.
// Both of these forms are allowed: // Both of these forms are allowed:
// //
// sq add sqlite3:///path/to/sakila.db // $ sq add sqlite3:///path/to/sakila.db
// sq add /path/to/sakila.db // $ sq add /path/to/sakila.db
// //
// The second form is particularly nice for bash completion etc. // The second form is particularly nice for bash completion etc.
if typ == sqlite3.Type { if typ == sqlite3.Type {
@ -182,7 +187,7 @@ func execSrcAdd(rc *RunContext, cmd *cobra.Command, args []string) error {
} }
// TODO: should we really be pinging this src right now? // TODO: should we really be pinging this src right now?
err = drvr.Ping(rc.Context, src) err = drvr.Ping(cmd.Context(), src)
if err != nil { if err != nil {
return errz.Wrapf(err, "failed to ping %s [%s]", src.Handle, src.RedactedLocation()) return errz.Wrapf(err, "failed to ping %s [%s]", src.Handle, src.RedactedLocation())
} }

View File

@ -1,109 +1,75 @@
package cli package cli
import ( import (
"os"
"runtime"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/core/errz"
) )
const bashCompletionFunc = ` func newCompletionCmd() *cobra.Command {
__sq_list_sources()
{
local sq_output out
if sq_output=$(sq ls 2>/dev/null); then
out=($(echo "${sq_output}" | awk 'NR > 1 {print $1}'))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__sq_get_resource()
{
if [[ ${#nouns[@]} -eq 0 ]]; then
return 1
fi
__sq_list_sources ${nouns[${#nouns[@]} -1]}
if [[ $? -eq 0 ]]; then
return 0
fi
}
__custom_func() {
case ${last_command} in
sq_ls | sq_src | sq_rm | sq_inspect )
__sq_list_sources
return
;;
*)
;;
esac
}
`
func newInstallBashCompletionCmd() (*cobra.Command, runFunc) {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "install-bash-completion", Use: "completion [bash|zsh|fish|powershell]",
Short: "Install bash completion script on Unix-ish systems.", Short: "Generate completion script",
Hidden: true, RunE: execCompletion,
Long: `To load completions:
Bash:
$ source <(sq completion bash)
# To load completions for each session, execute once:
Linux:
$ sq completion bash > /etc/bash_completion.d/sq
MacOS:
$ sq completion bash > /usr/local/etc/bash_completion.d/sq
Zsh:
# If shell completion is not already enabled in your environment you will need
# to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ sq completion zsh > "${fpath[1]}/_sq"
# You will need to start a new shell for this setup to take effect.
Fish:
$ sq completion fish | source
# To load completions for each session, execute once:
$ sq completion fish > ~/.config/fish/completions/sq.fish
Powershell:
PS> sq completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> sq completion powershell > sq.ps1
# and source this file from your powershell profile.
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
} }
return cmd, execInstallBashCompletion return cmd
} }
func execInstallBashCompletion(rc *RunContext, cmd *cobra.Command, args []string) error { func execCompletion(cmd *cobra.Command, args []string) error {
log := rc.Log rc := RunContextFrom(cmd.Context())
var path string switch args[0] {
case "bash":
switch runtime.GOOS { return cmd.Root().GenBashCompletion(rc.Out)
case "windows": case "zsh":
log.Warnf("skipping install bash completion on windows") return cmd.Root().GenZshCompletion(rc.Out)
return nil case "fish":
case "darwin": return cmd.Root().GenFishCompletion(rc.Out, true)
path = "/usr/local/etc/bash_completion.d/sq" case "powershell":
return cmd.Root().GenPowerShellCompletion(rc.Out)
default: default:
// it's unixish return errz.Errorf("invalid arg: %s", args[0])
path = " /etc/bash_completion.d/sq"
} }
// TODO: only write if necessary (check for version/timestamp/checksum)
err := cmd.Root().GenBashCompletionFile(path)
if err != nil {
log.Warnf("failed to write bash completion to %q: %v", path, err)
return err
}
return nil
}
func newGenerateZshCompletionCmd() (*cobra.Command, runFunc) {
cmd := &cobra.Command{
Use: "gen-zsh-completion",
Short: "Generate zsh completion script on Unix-ish systems.",
Hidden: true,
}
return cmd, execGenerateZshCompletion
}
func execGenerateZshCompletion(rc *RunContext, cmd *cobra.Command, args []string) error {
log := rc.Log
var path string
switch runtime.GOOS {
case "windows":
log.Warnf("skipping install zsh completion on windows")
return nil
case "darwin":
path = "/usr/local/etc/bash_completion.d/sq"
default:
// it's unixish
path = " /etc/bash_completion.d/sq"
}
err := cmd.Root().GenZshCompletion(os.Stdout)
if err != nil {
log.Warnf("failed to write zsh completion to %q: %v", path, err)
return err
}
return nil
} }

46
cli/cmd_driver.go Normal file
View File

@ -0,0 +1,46 @@
package cli
import (
"github.com/spf13/cobra"
)
func newDriverCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "driver",
Short: "List or manage drivers",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
Example: ` # List drivers
$ sq driver ls
# Install User Driver [TBD]
$ sq driver install ./rss.sq.yml
`,
}
return cmd
}
func newDriverListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Short: "List available drivers",
Args: cobra.ExactArgs(0),
RunE: execDriverList,
}
cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage)
cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage)
cmd.Flags().BoolP(flagMonochrome, flagMonochromeShort, false, flagMonochromeUsage)
return cmd
}
func execDriverList(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
drvrs := rc.registry.DriversMetadata()
return rc.writers.metaw.DriverMetadata(drvrs)
}

View File

@ -1,30 +0,0 @@
package cli
import (
"github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/core/errz"
)
func newDriversCmd() (*cobra.Command, runFunc) {
cmd := &cobra.Command{
Use: "drivers",
Short: "List available drivers",
}
cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage)
cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage)
cmd.Flags().BoolP(flagMonochrome, flagMonochromeShort, false, flagMonochromeUsage)
return cmd, execDrivers
}
func execDrivers(rc *RunContext, cmd *cobra.Command, args []string) error {
if len(args) > 0 {
return errz.Errorf("invalid arguments: zero arguments expected")
}
drvrs := rc.registry.DriversMetadata()
return rc.writers.metaw.DriverMetadata(drvrs)
}

View File

@ -2,16 +2,17 @@ package cli
import "github.com/spf13/cobra" import "github.com/spf13/cobra"
func newHelpCmd() (*cobra.Command, runFunc) { func newHelpCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "help", Use: "help",
Short: "Show sq help", Short: "Show sq help",
Hidden: true, Hidden: true,
RunE: execHelp,
} }
return cmd, execHelp return cmd
} }
func execHelp(rc *RunContext, cmd *cobra.Command, args []string) error { func execHelp(cmd *cobra.Command, args []string) error {
return cmd.Root().Help() return cmd.Root().Help()
} }

View File

@ -7,40 +7,44 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func newInspectCmd() (*cobra.Command, runFunc) { func newInspectCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "inspect [@HANDLE|@HANDLE.TABLE|.TABLE]", Use: "inspect [@HANDLE|@HANDLE.TABLE|.TABLE]",
Example: ` # inspect active data source Args: cobra.MaximumNArgs(1),
sq inspect ValidArgsFunction: (&handleTableCompleter{
max: 1,
# inspect @pg1 data source }).complete,
sq inspect @pg1 RunE: execInspect,
# inspect 'tbluser' in @pg1 data source
sq inspect @pg1.tbluser
# inspect 'tbluser' in active data source
sq inspect .tbluser
# inspect piped data
cat data.xlsx | sq inspect`,
Short: "Inspect data source schema and stats", Short: "Inspect data source schema and stats",
Long: `Inspect a data source, or a particular table in a source, Long: `Inspect a data source, or a particular table in a source,
listing table details, column names and types, row counts, etc. listing table details, column names and types, row counts, etc.
If @HANDLE is not provided, the active data source is assumed.`, If @HANDLE is not provided, the active data source is assumed.`,
Example: ` # inspect active data source
$ sq inspect
# inspect @pg1 data source
$ sq inspect @pg1
# inspect 'actor' in @pg1 data source
$ sq inspect @pg1.actor
# inspect 'actor' in active data source
$ sq inspect .actor
# inspect piped data
$ cat data.xlsx | sq inspect`,
} }
cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage) cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage)
cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage) cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
cmd.Flags().Bool(flagInspectFull, false, flagInspectFullUsage) cmd.Flags().Bool(flagInspectFull, false, flagInspectFullUsage)
return cmd, execInspect return cmd
} }
func execInspect(rc *RunContext, cmd *cobra.Command, args []string) error { func execInspect(cmd *cobra.Command, args []string) error {
if len(args) > 1 { ctx := cmd.Context()
return errz.Errorf("too many arguments") rc := RunContextFrom(ctx)
}
srcs := rc.Config.Sources srcs := rc.Config.Sources
@ -56,7 +60,7 @@ func execInspect(rc *RunContext, cmd *cobra.Command, args []string) error {
// - We're inspecting the active src // - We're inspecting the active src
// check if there's input on stdin // check if there's input on stdin
src, err = checkStdinSource(rc) src, err = checkStdinSource(ctx, rc)
if err != nil { if err != nil {
return err return err
} }
@ -107,15 +111,14 @@ func execInspect(rc *RunContext, cmd *cobra.Command, args []string) error {
} }
} }
dbase, err := rc.databases.Open(rc.Context, src) dbase, err := rc.databases.Open(ctx, src)
if err != nil { if err != nil {
return errz.Wrapf(err, "failed to inspect %s", src.Handle) return errz.Wrapf(err, "failed to inspect %s", src.Handle)
} }
//defer rc.Log.WarnIfCloseError(dbase)
if table != "" { if table != "" {
var tblMeta *source.TableMetadata var tblMeta *source.TableMetadata
tblMeta, err = dbase.TableMetadata(rc.Context, table) tblMeta, err = dbase.TableMetadata(ctx, table)
if err != nil { if err != nil {
return err return err
} }
@ -123,7 +126,7 @@ func execInspect(rc *RunContext, cmd *cobra.Command, args []string) error {
return rc.writers.metaw.TableMetadata(tblMeta) return rc.writers.metaw.TableMetadata(tblMeta)
} }
meta, err := dbase.SourceMetadata(rc.Context) meta, err := dbase.SourceMetadata(ctx)
if err != nil { if err != nil {
return errz.Wrapf(err, "failed to read %s source metadata", src.Handle) return errz.Wrapf(err, "failed to read %s source metadata", src.Handle)
} }

View File

@ -2,25 +2,23 @@ package cli
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/core/errz"
) )
func newSrcListCmd() (*cobra.Command, runFunc) { func newSrcListCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "ls", Use: "ls",
Short: "List data sources", Short: "List data sources",
Args: cobra.ExactArgs(0),
RunE: execSrcList,
} }
cmd.Flags().BoolP(flagVerbose, flagVerboseShort, false, flagVerboseUsage) cmd.Flags().BoolP(flagVerbose, flagVerboseShort, false, flagVerboseUsage)
cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage) cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage)
return cmd, execSrcList return cmd
} }
func execSrcList(rc *RunContext, cmd *cobra.Command, args []string) error { func execSrcList(cmd *cobra.Command, args []string) error {
if len(args) != 0 { rc := RunContextFrom(cmd.Context())
return errz.Errorf(msgInvalidArgs)
}
return rc.writers.srcw.SourceSet(rc.Config.Sources) return rc.writers.srcw.SourceSet(rc.Config.Sources)
} }

View File

@ -14,27 +14,41 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func newPingCmd() (*cobra.Command, runFunc) { func newPingCmd() *cobra.Command {
argsFn := func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Suggestions are: handles, plus the string "all".
rc := RunContextFrom(cmd.Context())
suggestions := append([]string{"all"}, rc.Config.Sources.Handles()...)
return suggestions, cobra.ShellCompDirectiveNoFileComp
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "ping [@HANDLE|all]", Use: "ping [all|@HANDLE [@HANDLE_N]]",
//Args: cobra.MaximumNArgs(1),
RunE: execPing,
ValidArgsFunction: argsFn,
Short: "Ping data sources",
Long: `Ping data sources to check connection health. If no arguments provided, the
active data source is pinged. Provide the handles of one or more sources
to ping those sources, or "all" to ping all sources.
The exit code is 1 if ping fails for any of the sources.`,
Example: ` # ping active data source Example: ` # ping active data source
sq ping $ sq ping
# ping all data sources # ping all data sources
sq ping all $ sq ping all
# ping @my1 with 2s timeout
sq ping @my1 --timeout=2s
# ping @my1 and @pg1 # ping @my1 and @pg1
sq ping @my1 @pg1 $ sq ping @my1 @pg1
# ping @my1 with 2s timeout
$ sq ping @my1 --timeout=2s
# output in TSV format # output in TSV format
sq ping --tsv @my1`, $ sq ping --tsv @my1`,
Short: "Check data source connection health",
Long: `Ping data sources to check connection health. If no arguments provided, the
active data source is pinged. The exit code is 1 if ping fails for any of the sources.`,
} }
cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage) cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
@ -42,10 +56,11 @@ active data source is pinged. The exit code is 1 if ping fails for any of the so
cmd.Flags().BoolP(flagTSV, flagTSVShort, false, flagTSVUsage) cmd.Flags().BoolP(flagTSV, flagTSVShort, false, flagTSVUsage)
cmd.Flags().Duration(flagTimeout, time.Second*10, flagTimeoutPingUsage) cmd.Flags().Duration(flagTimeout, time.Second*10, flagTimeoutPingUsage)
return cmd, execPing return cmd
} }
func execPing(rc *RunContext, cmd *cobra.Command, args []string) error { func execPing(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
cfg := rc.Config cfg := rc.Config
var srcs []*source.Source var srcs []*source.Source
var gotAll bool var gotAll bool
@ -93,19 +108,23 @@ func execPing(rc *RunContext, cmd *cobra.Command, args []string) error {
} }
} }
timeout := cfg.Defaults.Timeout timeout := cfg.Defaults.PingTimeout
if cmdFlagChanged(cmd, flagTimeout) { if cmdFlagChanged(cmd, flagTimeout) {
timeout, _ = cmd.Flags().GetDuration(flagTimeout) timeout, _ = cmd.Flags().GetDuration(flagTimeout)
} }
rc.Log.Debugf("Using timeout value: %s", timeout) rc.Log.Debugf("Using timeout value: %s", timeout)
return pingSources(rc.Context, rc.Log, rc.registry, srcs, rc.writers.pingw, timeout) return pingSources(cmd.Context(), rc.Log, rc.registry, srcs, rc.writers.pingw, timeout)
} }
// pingSources pings each of the sources in srcs, and prints results // pingSources pings each of the sources in srcs, and prints results
// to w. If any error occurs pinging any of srcs, that error is printed // to w. If any error occurs pinging any of srcs, that error is printed
// inline as part of the ping results, and an errNoMsg is returned. // inline as part of the ping results, and an errNoMsg is returned.
//
// NOTE: This ping code has an ancient lineage, in that it was written
// originally laid down before context.Context was a thing. Thus,
// the entire thing could probably be rewritten for simplicity.
func pingSources(ctx context.Context, log lg.Log, dp driver.Provider, srcs []*source.Source, w output.PingWriter, timeout time.Duration) error { func pingSources(ctx context.Context, log lg.Log, dp driver.Provider, srcs []*source.Source, w output.PingWriter, timeout time.Duration) error {
w.Open(srcs) w.Open(srcs)
defer log.WarnIfFuncError(w.Close) defer log.WarnIfFuncError(w.Close)

View File

@ -4,26 +4,23 @@ import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/core/errz"
) )
func newSrcRemoveCmd() (*cobra.Command, runFunc) { func newSrcRemoveCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rm @HANDLE", Use: "rm @HANDLE",
Example: ` sq rm @my1`, Example: ` $ sq rm @my1`,
Aliases: []string{"remove"},
Short: "Remove data source", Short: "Remove data source",
Args: cobra.ExactArgs(1),
RunE: execSrcRemove,
ValidArgsFunction: completeHandle(1),
} }
return cmd, execSrcRemove return cmd
} }
func execSrcRemove(rc *RunContext, cmd *cobra.Command, args []string) error { func execSrcRemove(cmd *cobra.Command, args []string) error {
if len(args) != 1 { rc := RunContextFrom(cmd.Context())
return errz.Errorf(msgInvalidArgs)
}
cfg := rc.Config cfg := rc.Config
src, err := cfg.Sources.Get(args[0]) src, err := cfg.Sources.Get(args[0])
if err != nil { if err != nil {

View File

@ -19,66 +19,67 @@ output to a database table.
You can query using sq's own jq-like syntax, or in native SQL. You can query using sq's own jq-like syntax, or in native SQL.
Execute "sq completion --help" for how to install shell completion.
More at https://sq.io More at https://sq.io
`, `,
Example: ` # pipe an Excel file and output the first 10 rows from sheet1 Example: ` # pipe an Excel file and output the first 10 rows from sheet1
cat data.xlsx | sq '.sheet1 | .[0:10]' $ cat data.xlsx | sq '.sheet1 | .[0:10]'
# add Postgres source identified by handle @sakila_pg # add Postgres source identified by handle @sakila_pg
sq add --handle=@sakila_pg 'postgres://user:pass@localhost:5432/sakila?sslmode=disable' $ sq add --handle=@sakila_pg 'postgres://user:pass@localhost:5432/sakila?sslmode=disable'
# add SQL Server source; will have generated handle @sakila_mssql # add SQL Server source; will have generated handle @sakila_mssql
sq add 'sqlserver://user:pass@localhost?database=sakila' $ sq add 'sqlserver://user:pass@localhost?database=sakila'
# list available data sources # list available data sources
sq ls $ sq ls
# ping all data sources # ping all data sources
sq ping all $ sq ping all
# set active data source # set active data source
sq src @sakila_pg $ sq src @sakila_pg
# get specified cols from table address in active data source # get specified cols from table address in active data source
sq '.address | .address_id, .city, .country' $ sq '.address | .address_id, .city, .country'
# get metadata (schema, stats etc) for data source # get metadata (schema, stats etc) for data source
sq inspect @sakila_pg $ sq inspect @sakila_pg
# get metadata for a table # get metadata for a table
sq inspect @pg1.person $ sq inspect @pg1.person
# output in JSON # output in JSON
sq -j '.person | .uid, .username, .email' $ sq -j '.person | .uid, .username, .email'
# output in table format (with header) # output in table format (with header)
sq -th '.person | .uid, .username, .email' $ sq -th '.person | .uid, .username, .email'
# output in table format (no header) # output in table format (no header)
sq -t '.person | .uid, .username, .email' $ sq -t '.person | .uid, .username, .email'
# output to a HTML file # output to a HTML file
sq --html '@sakila_sl3.actor' -o actor.html $ sq --html '@sakila_sl3.actor' -o actor.html
# join across data sources # join across data sources
sq '@my1.person, @pg1.address | join(.uid) | .username, .email, .city' $ sq '@my1.person, @pg1.address | join(.uid) | .username, .email, .city'
# insert query results into a table in another data source # insert query results into a table in another data source
sq --insert=@pg1.person '@my1.person | .username, .email' $ sq --insert=@pg1.person '@my1.person | .username, .email'
# execute a database-native SQL query, specifying the source # execute a database-native SQL query, specifying the source
sq sql --src=@pg1 'SELECT uid, username, email FROM person LIMIT 2' $ sq sql --src=@pg1 'SELECT uid, username, email FROM person LIMIT 2'
# copy a table (in the same source) # copy a table (in the same source)
sq tbl copy @sakila_sl3.actor .actor2 $ sq tbl copy @sakila_sl3.actor .actor2
# truncate tables # truncate tables
sq tbl truncate @sakila_sl3.actor2 $ sq tbl truncate @sakila_sl3.actor2
# drop table # drop table
sq tbl drop @sakila_sl3.actor2 $ sq tbl drop @sakila_sl3.actor2
`, `,
BashCompletionFunction: bashCompletionFunc,
} }
addQueryCmdFlags(cmd) addQueryCmdFlags(cmd)

View File

@ -2,7 +2,6 @@ package cli
import ( import (
"github.com/neilotoole/sq/drivers/sqlite3" "github.com/neilotoole/sq/drivers/sqlite3"
"github.com/neilotoole/sq/libsq/core/errz"
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -10,24 +9,26 @@ import (
// TODO: dump all this "internal" stuff: make the options as follows: @HANDLE, file, memory // TODO: dump all this "internal" stuff: make the options as follows: @HANDLE, file, memory
func newScratchCmd() (*cobra.Command, runFunc) { func newScratchCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "scratch [@HANDLE|internal|internal:file|internal:mem|@scratch]", Use: "scratch [@HANDLE|internal|internal:file|internal:mem|@scratch]",
// This command is likely to be ditched in favor of a generalized "config" cmd // This command is likely to be ditched in favor of a generalized "config" cmd
// such as "sq config scratchdb=@my1" // such as "sq config scratchdb=@my1"
Hidden: true, Hidden: true,
Args: cobra.ExactArgs(1),
RunE: execScratch,
Example: ` # get scratch data source Example: ` # get scratch data source
sq scratch $ sq scratch
# set @my1 as scratch data source # set @my1 as scratch data source
sq scratch @my1 $ sq scratch @my1
# use the default embedded db # use the default embedded db
sq scratch internal $ sq scratch internal
# explicitly specify use of embedded file db # explicitly specify use of embedded file db
sq scratch internal:file $ sq scratch internal:file
# explicitly specify use of embedded memory db # explicitly specify use of embedded memory db
sq scratch internal:mem $ sq scratch internal:mem
# restore default scratch db (equivalent to "internal") # restore default scratch db (equivalent to "internal")
sq scratch @scratch`, $ sq scratch @scratch`,
Short: "Get or set scratch data source", Short: "Get or set scratch data source",
Long: `Get or set scratch data source. The scratch db is used internally by sq for multiple purposes such as Long: `Get or set scratch data source. The scratch db is used internally by sq for multiple purposes such as
importing non-SQL data, or cross-database joins. If no argument provided, get the current scratch data importing non-SQL data, or cross-database joins. If no argument provided, get the current scratch data
@ -35,14 +36,11 @@ source. Otherwise, set @HANDLE or an internal db as the scratch data source. The
`, `,
} }
return cmd, execScratch return cmd
} }
func execScratch(rc *RunContext, cmd *cobra.Command, args []string) error { func execScratch(cmd *cobra.Command, args []string) error {
if len(args) > 1 { rc := RunContextFrom(cmd.Context())
return errz.Errorf(msgInvalidArgs)
}
cfg := rc.Config cfg := rc.Config
var src *source.Source var src *source.Source

View File

@ -15,24 +15,28 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func newSLQCmd() (*cobra.Command, runFunc) { func newSLQCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "slq", Use: "slq",
Short: "Execute SLQ query", Short: "Execute SLQ query",
Hidden: false, Hidden: true,
Args: cobra.MaximumNArgs(1),
RunE: execSLQ,
ValidArgsFunction: completeSLQ,
} }
addQueryCmdFlags(cmd) addQueryCmdFlags(cmd)
cmd.Flags().Bool(flagVersion, false, flagVersionUsage) cmd.Flags().Bool(flagVersion, false, flagVersionUsage)
return cmd, execSLQ return cmd
} }
func execSLQ(rc *RunContext, cmd *cobra.Command, args []string) error { func execSLQ(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
srcs := rc.Config.Sources srcs := rc.Config.Sources
// check if there's input on stdin // check if there's input on stdin
src, err := checkStdinSource(rc) src, err := checkStdinSource(cmd.Context(), rc)
if err != nil { if err != nil {
return err return err
} }
@ -65,7 +69,7 @@ func execSLQ(rc *RunContext, cmd *cobra.Command, args []string) error {
if !cmdFlagChanged(cmd, flagInsert) { if !cmdFlagChanged(cmd, flagInsert) {
// The user didn't specify the --insert=@src.tbl flag, // The user didn't specify the --insert=@src.tbl flag,
// so we just want to print the records. // so we just want to print the records.
return execSLQPrint(rc) return execSLQPrint(cmd.Context(), rc)
} }
// Instead of printing the records, they will be // Instead of printing the records, they will be
@ -85,19 +89,19 @@ func execSLQ(rc *RunContext, cmd *cobra.Command, args []string) error {
return err return err
} }
return execSLQInsert(rc, destSrc, destTbl) return execSLQInsert(cmd.Context(), rc, destSrc, destTbl)
} }
// execSQLInsert executes the SLQ and inserts resulting records // execSQLInsert executes the SLQ and inserts resulting records
// into destTbl in destSrc. // into destTbl in destSrc.
func execSLQInsert(rc *RunContext, destSrc *source.Source, destTbl string) error { func execSLQInsert(ctx context.Context, rc *RunContext, destSrc *source.Source, destTbl string) error {
args, srcs, dbases := rc.Args, rc.Config.Sources, rc.databases args, srcs, dbases := rc.Args, rc.Config.Sources, rc.databases
slq, err := preprocessUserSLQ(rc, args) slq, err := preprocessUserSLQ(ctx, rc, args)
if err != nil { if err != nil {
return err return err
} }
ctx, cancelFn := context.WithCancel(rc.Context) ctx, cancelFn := context.WithCancel(ctx)
defer cancelFn() defer cancelFn()
destDB, err := dbases.Open(ctx, destSrc) destDB, err := dbases.Open(ctx, destSrc)
@ -132,14 +136,14 @@ func execSLQInsert(rc *RunContext, destSrc *source.Source, destTbl string) error
} }
// execSLQPrint executes the SLQ query, and prints output to writer. // execSLQPrint executes the SLQ query, and prints output to writer.
func execSLQPrint(rc *RunContext) error { func execSLQPrint(ctx context.Context, rc *RunContext) error {
slq, err := preprocessUserSLQ(rc, rc.Args) slq, err := preprocessUserSLQ(ctx, rc, rc.Args)
if err != nil { if err != nil {
return err return err
} }
recw := output.NewRecordWriterAdapter(rc.writers.recordw) recw := output.NewRecordWriterAdapter(rc.writers.recordw)
execErr := libsq.ExecuteSLQ(rc.Context, rc.Log, rc.databases, rc.databases, rc.Config.Sources, slq, recw) execErr := libsq.ExecuteSLQ(ctx, rc.Log, rc.databases, rc.databases, rc.Config.Sources, slq, recw)
_, waitErr := recw.Wait() _, waitErr := recw.Wait()
if execErr != nil { if execErr != nil {
return execErr return execErr
@ -171,8 +175,8 @@ func execSLQPrint(rc *RunContext) error {
// to the query. This allows a query where the first selector // to the query. This allows a query where the first selector
// segment is the table name. // segment is the table name.
// //
// $ sq '.person' --> sq '@active.person' // $ sq '.person' --> $ sq '@active.person'
func preprocessUserSLQ(rc *RunContext, args []string) (string, error) { func preprocessUserSLQ(ctx context.Context, rc *RunContext, args []string) (string, error) {
log, reg, dbases, srcs := rc.Log, rc.registry, rc.databases, rc.Config.Sources log, reg, dbases, srcs := rc.Log, rc.registry, rc.databases, rc.Config.Sources
activeSrc := srcs.Active() activeSrc := srcs.Active()
@ -204,13 +208,13 @@ func preprocessUserSLQ(rc *RunContext, args []string) (string, error) {
// This isn't a monotable src, so we can't // This isn't a monotable src, so we can't
// just select @stdin.data. Instead we'll select // just select @stdin.data. Instead we'll select
// the first table name, as found in the source meta. // the first table name, as found in the source meta.
dbase, err := dbases.Open(rc.Context, activeSrc) dbase, err := dbases.Open(ctx, activeSrc)
if err != nil { if err != nil {
return "", err return "", err
} }
defer log.WarnIfCloseError(dbase) defer log.WarnIfCloseError(dbase)
srcMeta, err := dbase.SourceMetadata(rc.Context) srcMeta, err := dbase.SourceMetadata(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -299,9 +303,14 @@ func addQueryCmdFlags(cmd *cobra.Command) {
cmd.Flags().BoolP(flagPretty, "", true, flagPrettyUsage) cmd.Flags().BoolP(flagPretty, "", true, flagPrettyUsage)
cmd.Flags().StringP(flagInsert, "", "", flagInsertUsage) cmd.Flags().StringP(flagInsert, "", "", flagInsertUsage)
_ = cmd.RegisterFlagCompletionFunc(flagInsert, (&handleTableCompleter{onlySQL: true, handleRequired: true}).complete)
cmd.Flags().StringP(flagActiveSrc, "", "", flagActiveSrcUsage) cmd.Flags().StringP(flagActiveSrc, "", "", flagActiveSrcUsage)
_ = cmd.RegisterFlagCompletionFunc(flagActiveSrc, completeHandle(0))
// The driver flag can be used if data is piped to sq over stdin // The driver flag can be used if data is piped to sq over stdin
cmd.Flags().StringP(flagDriver, "", "", flagQueryDriverUsage) cmd.Flags().StringP(flagDriver, "", "", flagQueryDriverUsage)
_ = cmd.RegisterFlagCompletionFunc(flagDriver, completeDriverType)
cmd.Flags().StringP(flagSrcOptions, "", "", flagQuerySrcOptionsUsage) cmd.Flags().StringP(flagSrcOptions, "", "", flagQuerySrcOptionsUsage)
} }

View File

@ -16,7 +16,7 @@ import (
"github.com/neilotoole/sq/libsq/core/stringz" "github.com/neilotoole/sq/libsq/core/stringz"
) )
func newSQLCmd() (*cobra.Command, runFunc) { func newSQLCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "sql QUERY|STMT", Use: "sql QUERY|STMT",
Short: "Execute DB-native SQL query or statement", Short: "Execute DB-native SQL query or statement",
@ -28,17 +28,18 @@ If flag --query is set, sq will run the input as a query
(SELECT) and return the query rows. If flag --exec is set, (SELECT) and return the query rows. If flag --exec is set,
sq will execute the input and return the result. If neither sq will execute the input and return the result. If neither
flag is set, sq attempts to determine the appropriate mode.`, flag is set, sq attempts to determine the appropriate mode.`,
RunE: execSQL,
Example: ` # Select from active source Example: ` # Select from active source
sq sql 'SELECT * FROM actor' $ sq sql 'SELECT * FROM actor'
# Select from a specified source # Select from a specified source
sq sql --src=@sakila_pg12 'SELECT * FROM actor' $ sq sql --src=@sakila_pg12 'SELECT * FROM actor'
# Drop table @sakila_pg12.actor # Drop table @sakila_pg12.actor
sq sql --exec --src=@sakila_pg12 'DROP TABLE actor' $ sq sql --exec --src=@sakila_pg12 'DROP TABLE actor'
# Select from active source and write results to @sakila_ms17.actor # Select from active source and write results to @sakila_ms17.actor
sq sql 'SELECT * FROM actor' --insert=@sakila_ms17.actor`, $ sq sql 'SELECT * FROM actor' --insert=@sakila_ms17.actor`,
} }
addQueryCmdFlags(cmd) addQueryCmdFlags(cmd)
@ -48,10 +49,11 @@ flag is set, sq attempts to determine the appropriate mode.`,
// User explicitly wants to execute the SQL using sql.DB.Exec // User explicitly wants to execute the SQL using sql.DB.Exec
cmd.Flags().Bool(flagSQLExec, false, flagSQLExecUsage) cmd.Flags().Bool(flagSQLExec, false, flagSQLExecUsage)
return cmd, execSQL return cmd
} }
func execSQL(rc *RunContext, cmd *cobra.Command, args []string) error { func execSQL(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
switch len(args) { switch len(args) {
default: default:
// FIXME: we should allow multiple args and concat them // FIXME: we should allow multiple args and concat them
@ -64,7 +66,7 @@ func execSQL(rc *RunContext, cmd *cobra.Command, args []string) error {
} }
} }
err := determineSources(rc) err := determineSources(cmd.Context(), rc)
if err != nil { if err != nil {
return err return err
} }
@ -77,7 +79,7 @@ func execSQL(rc *RunContext, cmd *cobra.Command, args []string) error {
if !cmdFlagChanged(cmd, flagInsert) { if !cmdFlagChanged(cmd, flagInsert) {
// The user didn't specify the --insert=@src.tbl flag, // The user didn't specify the --insert=@src.tbl flag,
// so we just want to print the records. // so we just want to print the records.
return execSQLPrint(rc, activeSrc) return execSQLPrint(cmd.Context(), rc, activeSrc)
} }
// Instead of printing the records, they will be // Instead of printing the records, they will be
@ -97,20 +99,20 @@ func execSQL(rc *RunContext, cmd *cobra.Command, args []string) error {
return err return err
} }
return execSQLInsert(rc, activeSrc, destSrc, destTbl) return execSQLInsert(cmd.Context(), rc, activeSrc, destSrc, destTbl)
} }
// execSQLPrint executes the SQL and prints resulting records // execSQLPrint executes the SQL and prints resulting records
// to the configured writer. // to the configured writer.
func execSQLPrint(rc *RunContext, fromSrc *source.Source) error { func execSQLPrint(ctx context.Context, rc *RunContext, fromSrc *source.Source) error {
args := rc.Args args := rc.Args
dbase, err := rc.databases.Open(rc.Context, fromSrc) dbase, err := rc.databases.Open(ctx, fromSrc)
if err != nil { if err != nil {
return err return err
} }
recw := output.NewRecordWriterAdapter(rc.writers.recordw) recw := output.NewRecordWriterAdapter(rc.writers.recordw)
err = libsq.QuerySQL(rc.Context, rc.Log, dbase, recw, args[0]) err = libsq.QuerySQL(ctx, rc.Log, dbase, recw, args[0])
if err != nil { if err != nil {
return err return err
} }
@ -120,10 +122,10 @@ func execSQLPrint(rc *RunContext, fromSrc *source.Source) error {
// execSQLInsert executes the SQL and inserts resulting records // execSQLInsert executes the SQL and inserts resulting records
// into destTbl in destSrc. // into destTbl in destSrc.
func execSQLInsert(rc *RunContext, fromSrc, destSrc *source.Source, destTbl string) error { func execSQLInsert(ctx context.Context, rc *RunContext, fromSrc, destSrc *source.Source, destTbl string) error {
args := rc.Args args := rc.Args
dbases := rc.databases dbases := rc.databases
ctx, cancelFn := context.WithCancel(rc.Context) ctx, cancelFn := context.WithCancel(ctx)
defer cancelFn() defer cancelFn()
fromDB, err := dbases.Open(ctx, fromSrc) fromDB, err := dbases.Open(ctx, fromSrc)

View File

@ -2,35 +2,29 @@ package cli
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/core/errz"
) )
func newSrcCommand() (*cobra.Command, runFunc) { func newSrcCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "src [@HANDLE]", Use: "src [@HANDLE]",
RunE: execSrc,
Example: ` # get active data source Example: ` # get active data source
sq src $ sq src
# set @my1 as active data source # set @my1 as active data source
sq src @my1`, $ sq src @my1`,
// RunE: execSrc, Args: cobra.MaximumNArgs(1),
ValidArgsFunction: completeHandle(1),
Short: "Get or set active data source", Short: "Get or set active data source",
Long: `Get or set active data source. If no argument provided, get the active data Long: `Get or set active data source. If no argument provided, get the active data
source. Otherwise, set @HANDLE as the active data source.`, source. Otherwise, set @HANDLE as the active data source.`,
} }
//cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage) return cmd
//cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
//cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage)
return cmd, execSrc
} }
func execSrc(rc *RunContext, cmd *cobra.Command, args []string) error { func execSrc(cmd *cobra.Command, args []string) error {
if len(args) > 1 { rc := RunContextFrom(cmd.Context())
return errz.Errorf(msgInvalidArgs)
}
cfg := rc.Config cfg := rc.Config
if len(args) == 0 { if len(args) == 0 {

View File

@ -11,52 +11,55 @@ import (
"github.com/neilotoole/sq/libsq/source" "github.com/neilotoole/sq/libsq/source"
) )
func newTblCmd() (*cobra.Command, runFunc) { func newTblCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "tbl", Use: "tbl",
Short: "Common actions on tables (copy, truncate, drop)", Short: "Common actions on tables (copy, truncate, drop)",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
Example: ` # Copy table actor to new table actor2 Example: ` # Copy table actor to new table actor2
sq tbl copy @sakila_sl3.actor actor2 $ sq tbl copy @sakila_sl3.actor actor2
# Truncate table actor2 # Truncate table actor2
sq tbl truncate @sakila_sl3.actor2 $ sq tbl truncate @sakila_sl3.actor2
# Drop table actor2 # Drop table actor2
sq tbl drop @sakila_sl3.actor2`, $ sq tbl drop @sakila_sl3.actor2`,
} }
return cmd, func(rc *RunContext, cmd *cobra.Command, args []string) error { return cmd
return cmd.Help()
}
} }
func newTblCopyCmd() (*cobra.Command, runFunc) { func newTblCopyCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "copy @HANDLE.TABLE NEWTABLE", Use: "copy @HANDLE.TABLE NEWTABLE",
Short: "Make a copy of a table", Short: "Make a copy of a table",
Long: `Make a copy of a table in the same database. The table data is also copied by default.`, Long: `Make a copy of a table in the same database. The table data is also copied by default.`,
Example: ` # Copy table "actor" in @sakila_sl3" to new table "actor2" ValidArgsFunction: completeTblCopy,
sq tbl copy @sakila_sl3.actor .actor2 RunE: execTblCopy,
Example: ` # Copy table "actor" in @sakila_sl3 to new table "actor2"
$ sq tbl copy @sakila_sl3.actor .actor2
# Copy table "actor" in active src to table "actor2" # Copy table "actor" in active src to table "actor2"
sq tbl copy .actor .actor2 $ sq tbl copy .actor .actor2
# Copy table "actor" in active src to generated table name (e.g. "@sakila_sl3.actor_copy__1ae03e9b") # Copy table "actor" in active src to generated table name (e.g. "@sakila_sl3.actor_copy__1ae03e9b")
sq tbl copy .actor $ sq tbl copy .actor
# Copy table structure, but don't copy table data # Copy table structure, but don't copy table data
sq tbl copy --data=false .actor $ sq tbl copy --data=false .actor
`, `,
} }
cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage) cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage)
cmd.Flags().Bool(flagTblData, true, flagTblDataUsage) cmd.Flags().Bool(flagTblData, true, flagTblDataUsage)
return cmd, execTblCopy return cmd
} }
func execTblCopy(rc *RunContext, cmd *cobra.Command, args []string) error { func execTblCopy(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
if len(args) == 0 || len(args) > 2 { if len(args) == 0 || len(args) > 2 {
return errz.New("one or two table args required") return errz.New("one or two table args required")
} }
@ -108,12 +111,12 @@ func execTblCopy(rc *RunContext, cmd *cobra.Command, args []string) error {
} }
var dbase driver.Database var dbase driver.Database
dbase, err = rc.databases.Open(rc.Context, tblHandles[0].src) dbase, err = rc.databases.Open(cmd.Context(), tblHandles[0].src)
if err != nil { if err != nil {
return err return err
} }
copied, err := sqlDrvr.CopyTable(rc.Context, dbase.DB(), tblHandles[0].tbl, tblHandles[1].tbl, copyData) copied, err := sqlDrvr.CopyTable(cmd.Context(), dbase.DB(), tblHandles[0].tbl, tblHandles[1].tbl, copyData)
if err != nil { if err != nil {
return errz.Wrapf(err, "failed tbl copy %s.%s --> %s.%s", return errz.Wrapf(err, "failed tbl copy %s.%s --> %s.%s",
tblHandles[0].handle, tblHandles[0].tbl, tblHandles[0].handle, tblHandles[0].tbl,
@ -137,28 +140,35 @@ func execTblCopy(rc *RunContext, cmd *cobra.Command, args []string) error {
return nil return nil
} }
func newTblTruncateCmd() (*cobra.Command, runFunc) { func newTblTruncateCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "truncate @HANDLE.TABLE", Use: "truncate @HANDLE.TABLE|.TABLE",
Short: "Truncate one or more tables", Short: "Truncate one or more tables",
Long: `Truncate one or more tables. Note that this command
only applies to SQL sources.`,
RunE: execTblTruncate,
ValidArgsFunction: (&handleTableCompleter{
onlySQL: true,
}).complete,
Example: ` # truncate table "actor"" in source @sakila_sl3 Example: ` # truncate table "actor"" in source @sakila_sl3
sq tbl truncate @sakila_sl3.actor $ sq tbl truncate @sakila_sl3.actor
# truncate table "payment"" in the active src # truncate table "payment"" in the active src
sq tbl truncate .payment $ sq tbl truncate .payment
# truncate multiple tables # truncate multiple tables
sq tbl truncate .payment @sakila_sl3.actor $ sq tbl truncate .payment @sakila_sl3.actor
`, `,
} }
cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage) cmd.Flags().BoolP(flagJSON, flagJSONShort, false, flagJSONUsage)
cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage) cmd.Flags().BoolP(flagTable, flagTableShort, false, flagTableUsage)
return cmd, execTblTruncate return cmd
} }
func execTblTruncate(rc *RunContext, cmd *cobra.Command, args []string) (err error) { func execTblTruncate(cmd *cobra.Command, args []string) (err error) {
rc := RunContextFrom(cmd.Context())
var tblHandles []tblHandle var tblHandles []tblHandle
tblHandles, err = parseTableHandleArgs(rc.registry, rc.Config.Sources, args) tblHandles, err = parseTableHandleArgs(rc.registry, rc.Config.Sources, args)
if err != nil { if err != nil {
@ -167,7 +177,7 @@ func execTblTruncate(rc *RunContext, cmd *cobra.Command, args []string) (err err
for _, tblH := range tblHandles { for _, tblH := range tblHandles {
var affected int64 var affected int64
affected, err = tblH.drvr.Truncate(rc.Context, tblH.src, tblH.tbl, true) affected, err = tblH.drvr.Truncate(cmd.Context(), tblH.src, tblH.tbl, true)
if err != nil { if err != nil {
return err return err
} }
@ -180,25 +190,32 @@ func execTblTruncate(rc *RunContext, cmd *cobra.Command, args []string) (err err
return nil return nil
} }
func newTblDropCmd() (*cobra.Command, runFunc) { func newTblDropCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "drop @HANDLE.TABLE", Use: "drop @HANDLE.TABLE",
Short: "Drop one or more tables", Short: "Drop one or more tables",
Long: `Drop one or more tables. Note that this command
only applies to SQL sources.`,
RunE: execTblDrop,
ValidArgsFunction: (&handleTableCompleter{
onlySQL: true,
}).complete,
Example: `# drop table "actor" in src @sakila_sl3 Example: `# drop table "actor" in src @sakila_sl3
sq tbl drop @sakila_sl3.actor $ sq tbl drop @sakila_sl3.actor
# drop table "payment"" in the active src # drop table "payment"" in the active src
sq tbl drop .payment $ sq tbl drop .payment
# drop multiple tables # drop multiple tables
sq drop .payment @sakila_sl3.actor $ sq drop .payment @sakila_sl3.actor
`, `,
} }
return cmd, execTblDrop return cmd
} }
func execTblDrop(rc *RunContext, cmd *cobra.Command, args []string) (err error) { func execTblDrop(cmd *cobra.Command, args []string) (err error) {
rc := RunContextFrom(cmd.Context())
var tblHandles []tblHandle var tblHandles []tblHandle
tblHandles, err = parseTableHandleArgs(rc.registry, rc.Config.Sources, args) tblHandles, err = parseTableHandleArgs(rc.registry, rc.Config.Sources, args)
if err != nil { if err != nil {
@ -212,11 +229,11 @@ func execTblDrop(rc *RunContext, cmd *cobra.Command, args []string) (err error)
} }
var dbase driver.Database var dbase driver.Database
dbase, err = rc.databases.Open(rc.Context, tblH.src) dbase, err = rc.databases.Open(cmd.Context(), tblH.src)
if err != nil { if err != nil {
return err return err
} }
err = sqlDrvr.DropTable(rc.Context, dbase.DB(), tblH.tbl, false) err = sqlDrvr.DropTable(cmd.Context(), dbase.DB(), tblH.tbl, false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -8,16 +8,18 @@ import (
"github.com/neilotoole/sq/cli/buildinfo" "github.com/neilotoole/sq/cli/buildinfo"
) )
func newVersionCmd() (*cobra.Command, runFunc) { func newVersionCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "version", Use: "version",
Short: "Print sq version", Short: "Print sq version",
RunE: execVersion,
} }
return cmd, execVersion return cmd
} }
func execVersion(rc *RunContext, cmd *cobra.Command, args []string) error { func execVersion(cmd *cobra.Command, args []string) error {
rc := RunContextFrom(cmd.Context())
rc.writers.fmt.Hilite.Fprintf(rc.Out, "sq %s", buildinfo.Version) rc.writers.fmt.Hilite.Fprintf(rc.Out, "sq %s", buildinfo.Version)
if len(buildinfo.Commit) > 0 { if len(buildinfo.Commit) > 0 {

362
cli/completion.go Normal file
View File

@ -0,0 +1,362 @@
package cli
import (
"context"
"strings"
"github.com/spf13/cobra"
"github.com/neilotoole/sq/libsq/source"
)
// completionFunc is a shell completion function.
type completionFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
var (
_ completionFunc = completeDriverType
_ completionFunc = completeSLQ
_ completionFunc = new(handleTableCompleter).complete
)
// completeHandle is a completionFunc that suggests handles.
// The max arg is the maximum number of completions. Set to 0
// for no limit.
func completeHandle(max int) completionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if max > 0 && len(args) >= max {
return nil, cobra.ShellCompDirectiveNoFileComp
}
rc := RunContextFrom(cmd.Context())
handles := rc.Config.Sources.Handles()
return handles, cobra.ShellCompDirectiveNoFileComp
}
}
// completeSLQ is a completionFunc that completes SLQ queries.
// The completion functionality is rudimentary: it only
// completes the "table select" segment (that is, the @HANDLE.NAME)
// segment.
func completeSLQ(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveError
}
c := &handleTableCompleter{}
return c.complete(cmd, args, toComplete)
}
// completeDriverType is a completionFunc that suggests drivers.
func completeDriverType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
rc := RunContextFrom(cmd.Context())
if rc.databases == nil {
err := rc.init()
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
}
drivers := rc.registry.Drivers()
types := make([]string, len(drivers))
for i, driver := range rc.registry.Drivers() {
types[i] = string(driver.DriverMetadata().Type)
}
return types, cobra.ShellCompDirectiveNoFileComp
}
// completeTblCopy is a completionFunc for the "tbl copy" command.
func completeTblCopy(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Example invocation:
//
// sq tbl copy @sakila_sl3.actor .new_table
//
// Note that the second arg can only be a table name (and
// not a @HANDLE.TABLE), and it must also be a _new_ table
// (because we can't copy over an existing table), thus
// we only suggest "." for the second arg, forcing the user
// to supply the rest of that new table name.
switch len(args) {
case 0:
c := &handleTableCompleter{onlySQL: true}
return c.complete(cmd, args, toComplete)
case 1:
return []string{"."}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
default:
return nil, cobra.ShellCompDirectiveError
}
}
// handleTableCompleter encapsulates completion of a handle
// ("@sakila_sl3"), table (".actor"), or @HANDLE.TABLE
// ("@sakila_sl3.actor"). Its complete method is a completionFunc.
type handleTableCompleter struct {
// onlySQL, when true, filters out non-SQL sources.
onlySQL bool
// handleRequired, when true, means that only @HANDLE.TABLE
// suggestions are offered. That is, naked .TABLE suggestions
// will not be offered.
handleRequired bool
// max indicates the maximum number of completions
// to offer. Use 0 to indicate no limit. Frequently this
// is set to 1 to if the command accepts only one argument.
max int
}
// complete is the completionFunc for handleTableCompleter.
func (c *handleTableCompleter) complete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
rc := RunContextFrom(cmd.Context())
if err := rc.init(); err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
// We don't want the user to wait around forever for
// shell completion, so we set a timeout. Typically
// this is something like 500ms.
ctx, cancelFn := context.WithTimeout(cmd.Context(), rc.Config.Defaults.ShellCompletionTimeout)
defer cancelFn()
if c.max > 0 && len(args) >= c.max {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if toComplete == "" {
if c.handleRequired {
return c.completeHandle(ctx, rc, args, toComplete)
}
return c.completeEither(ctx, rc, args, toComplete)
}
// There's some input. We expect the input to be of the
// the form "@handle" or ".table". That is, the input should
// start with either '@' or '.'.
switch toComplete[0] {
default:
// User input was something other than '@' or '.'
return nil, cobra.ShellCompDirectiveError
case '@':
return c.completeHandle(ctx, rc, args, toComplete)
case '.':
if c.handleRequired {
return nil, cobra.ShellCompDirectiveError
}
return c.completeTableOnly(ctx, rc, args, toComplete)
}
}
// completeTableOnly returns suggestions given input beginning with
// a period. Effectively this is completion for tables in the
// active src.
func (c *handleTableCompleter) completeTableOnly(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
activeSrc := rc.Config.Sources.Active()
if activeSrc == nil {
rc.Log.Error("Active source is nil")
return nil, cobra.ShellCompDirectiveError
}
if c.onlySQL {
isSQL, err := handleIsSQLDriver(rc, activeSrc.Handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if !isSQL {
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
tables, err := getTableNamesForHandle(ctx, rc, activeSrc.Handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
var suggestions []string
for _, table := range tables {
if strings.HasPrefix(table, toComplete[1:]) {
suggestions = append(suggestions, "."+table)
}
}
return suggestions, cobra.ShellCompDirectiveNoFileComp
}
// completeHandle returns suggestions given input beginning with
// a '@'. The returned suggestions could be @HANDLE, or @HANDLE.TABLE.
func (c *handleTableCompleter) completeHandle(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// We're dealing with a handle.
// But we could be dealing with just the handle ("@sakila_sl3")
// or a @HANDLE.TABLE ("@sakila_sl3.actor").
if strings.ContainsRune(toComplete, '.') {
if strings.Count(toComplete, ".") > 1 {
// Can only have one period
return nil, cobra.ShellCompDirectiveError
}
// It's a handle with a full handle and at least a
// partial table name, such as "@sakila_sl3.fil"
handle, partialTbl, err := source.ParseTableHandle(strings.TrimSuffix(toComplete, "."))
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if c.onlySQL {
isSQL, err := handleIsSQLDriver(rc, handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if !isSQL {
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
tables, err := getTableNamesForHandle(ctx, rc, handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
var suggestions []string
for _, table := range tables {
if strings.HasPrefix(table, partialTbl) {
suggestions = append(suggestions, handle+"."+table)
}
}
return suggestions, cobra.ShellCompDirectiveNoFileComp
}
handles := rc.Config.Sources.Handles()
// Else, we're dealing with just a handle so far
var matchingHandles []string
for _, handle := range handles {
if strings.HasPrefix(handle, toComplete) {
if c.onlySQL {
isSQL, err := handleIsSQLDriver(rc, handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if !isSQL {
continue
}
}
matchingHandles = append(matchingHandles, handle)
}
}
switch len(matchingHandles) {
default:
return matchingHandles, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
case 0:
return nil, cobra.ShellCompDirectiveNoFileComp
case 1:
// Only one handle match, so we will present that complete
// handle, plus a suggestion (@HANDLE.TABLE) for each of the tables
// for that handle
}
tables, err := getTableNamesForHandle(ctx, rc, matchingHandles[0])
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
suggestions := []string{matchingHandles[0]}
for _, table := range tables {
suggestions = append(suggestions, matchingHandles[0]+"."+table)
}
return suggestions, cobra.ShellCompDirectiveNoFileComp
}
func (c *handleTableCompleter) completeEither(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// There's no input yet.
// Therefore we want to return a union of all handles
// plus the tables from the active source.
activeSrc := rc.Config.Sources.Active()
if activeSrc == nil {
rc.Log.Error("Active source is nil")
return nil, cobra.ShellCompDirectiveError
}
var activeSrcTables []string
isSQL, err := handleIsSQLDriver(rc, activeSrc.Handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if !c.onlySQL || isSQL {
activeSrcTables, err = getTableNamesForHandle(ctx, rc, activeSrc.Handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
}
var suggestions []string
for _, table := range activeSrcTables {
suggestions = append(suggestions, "."+table)
}
for _, src := range rc.Config.Sources.Items() {
if c.onlySQL {
isSQL, err = handleIsSQLDriver(rc, src.Handle)
if err != nil {
rc.Log.Error(err)
return nil, cobra.ShellCompDirectiveError
}
if !isSQL {
continue
}
}
suggestions = append(suggestions, src.Handle)
}
return suggestions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
}
func handleIsSQLDriver(rc *RunContext, handle string) (bool, error) {
src, err := rc.Config.Sources.Get(handle)
if err != nil {
return false, err
}
driver, err := rc.registry.DriverFor(src.Type)
if err != nil {
return false, err
}
return driver.DriverMetadata().IsSQL, nil
}
func getTableNamesForHandle(ctx context.Context, rc *RunContext, handle string) ([]string, error) {
src, err := rc.Config.Sources.Get(handle)
if err != nil {
return nil, err
}
db, err := rc.databases.Open(ctx, src)
if err != nil {
return nil, err
}
md, err := db.SourceMetadata(ctx)
if err != nil {
return nil, err
}
return md.TableNames(), nil
}

View File

@ -36,11 +36,21 @@ type Ext struct {
UserDrivers []*userdriver.DriverDef `yaml:"user_drivers" json:"user_drivers"` UserDrivers []*userdriver.DriverDef `yaml:"user_drivers" json:"user_drivers"`
} }
// Defaults contains sq default values. // Defaults contains default config values.
type Defaults struct { type Defaults struct {
Timeout time.Duration `yaml:"timeout" json:"timeout"` // Format is the default output format: json, table, etc.
Format Format `yaml:"output_format" json:"output_format"` Format Format `yaml:"output_format" json:"output_format"`
// Header determines if a header should be printed (if relevant
// for the output format).
Header bool `yaml:"output_header" json:"output_header"` Header bool `yaml:"output_header" json:"output_header"`
// PingTimeout is the allowed time for a ping.
PingTimeout time.Duration `yaml:"ping_timeout" json:"ping_timeout"`
// ShellCompletionTimeout is the time allowed for the shell
// completion callback to execute.
ShellCompletionTimeout time.Duration `yaml:"shell_completion_timeout" json:"shell_completion_timeout"`
} }
// New returns a config instance with default options set. // New returns a config instance with default options set.
@ -66,11 +76,15 @@ func initCfg(cfg *Config) {
cfg.Defaults.Format = FormatTable cfg.Defaults.Format = FormatTable
} }
if cfg.Defaults.Timeout == 0 { if cfg.Defaults.PingTimeout == 0 {
// Probably should be setting this in the New function, // Probably should be setting this in the New function,
// but we haven't yet defined cli's behavior wrt // but we haven't yet defined cli's behavior wrt
// a zero timeout. Does it mean no timeout? // a zero timeout. Does it mean no timeout?
cfg.Defaults.Timeout = 10 * time.Second cfg.Defaults.PingTimeout = 10 * time.Second
}
if cfg.Defaults.ShellCompletionTimeout == 0 {
cfg.Defaults.ShellCompletionTimeout = time.Millisecond * 500
} }
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/neilotoole/sq/cli/config" "github.com/neilotoole/sq/cli/config"
"github.com/neilotoole/sq/testh"
"github.com/neilotoole/sq/testh/proj" "github.com/neilotoole/sq/testh/proj"
) )
@ -70,14 +71,21 @@ func TestFileStore_Load(t *testing.T) {
fs := &config.YAMLFileStore{HookLoad: hookExpand} fs := &config.YAMLFileStore{HookLoad: hookExpand}
for _, match := range good { for _, match := range good {
match := match
t.Run(testh.Name(match), func(t *testing.T) {
fs.Path = match fs.Path = match
_, err = fs.Load() _, err = fs.Load()
require.NoError(t, err, match) require.NoError(t, err, match)
})
} }
for _, match := range bad { for _, match := range bad {
match := match
t.Run(testh.Name(match), func(t *testing.T) {
fs.Path = match fs.Path = match
_, err = fs.Load() _, err = fs.Load()
require.Error(t, err, match) require.Error(t, err, match)
})
} }
} }

View File

@ -1,2 +1,2 @@
defaults: defaults:
timeout: not_a_duration ping_timeout: not_a_duration

2
cli/config/testdata/bad.08.sq.yml vendored Normal file
View File

@ -0,0 +1,2 @@
defaults:
shell_completion_timeout: not_a_duration

View File

@ -1,5 +1,6 @@
defaults: defaults:
timeout: 10s ping_timeout: 10s
shell_completion_timeout: 1s
output_format: table output_format: table
output_header: true output_header: true
sources: sources:

View File

@ -96,7 +96,7 @@ func (w *mdWriter) SourceMetadata(meta *source.Metadata) error {
meta.Name, meta.Name,
stringz.ByteSized(meta.Size, 1, ""), stringz.ByteSized(meta.Size, 1, ""),
fmt.Sprintf("%d", len(meta.Tables)), fmt.Sprintf("%d", len(meta.Tables)),
meta.Location, source.RedactLocation(meta.Location),
} }
} else { } else {
headers = []string{"HANDLE", "DRIVER", "NAME", "FQ NAME", "SIZE", "TABLES", "LOCATION"} headers = []string{"HANDLE", "DRIVER", "NAME", "FQ NAME", "SIZE", "TABLES", "LOCATION"}
@ -110,7 +110,7 @@ func (w *mdWriter) SourceMetadata(meta *source.Metadata) error {
meta.FQName, meta.FQName,
stringz.ByteSized(meta.Size, 1, ""), stringz.ByteSized(meta.Size, 1, ""),
fmt.Sprintf("%d", len(meta.Tables)), fmt.Sprintf("%d", len(meta.Tables)),
meta.Location, source.RedactLocation(meta.Location),
} }
} }

View File

@ -29,7 +29,6 @@ func newTestRunCtx(log lg.Log) (rc *cli.RunContext, out, errOut *bytes.Buffer) {
errOut = &bytes.Buffer{} errOut = &bytes.Buffer{}
rc = &cli.RunContext{ rc = &cli.RunContext{
Context: context.Background(),
Stdin: os.Stdin, Stdin: os.Stdin,
Out: out, Out: out,
ErrOut: errOut, ErrOut: errOut,
@ -102,11 +101,7 @@ func (ru *run) exec(args ...string) error {
return err return err
} }
if len(args) > 0 && args[0] != "sq" { execErr := cli.ExecuteWith(context.Background(), ru.rc, args)
args = append([]string{"sq"}, args...)
}
execErr := cli.ExecuteWith(ru.rc, args)
if !ru.hushOutput { if !ru.hushOutput {
// We log sq's output now (before calling rc.Close) because // We log sq's output now (before calling rc.Close) because

View File

@ -1,6 +1,7 @@
package cli package cli
import ( import (
"context"
"strings" "strings"
"github.com/neilotoole/lg" "github.com/neilotoole/lg"
@ -17,7 +18,7 @@ import (
// mutate rc.Config.Sources as necessary. If no error // mutate rc.Config.Sources as necessary. If no error
// is returned, it is guaranteed that there's an active // is returned, it is guaranteed that there's an active
// source on the source set. // source on the source set.
func determineSources(rc *RunContext) error { func determineSources(ctx context.Context, rc *RunContext) error {
cmd, srcs := rc.Cmd, rc.Config.Sources cmd, srcs := rc.Cmd, rc.Config.Sources
activeSrc, err := activeSrcFromFlagsOrConfig(cmd, srcs) activeSrc, err := activeSrcFromFlagsOrConfig(cmd, srcs)
if err != nil { if err != nil {
@ -26,7 +27,7 @@ func determineSources(rc *RunContext) error {
// Note: ^ activeSrc could still be nil // Note: ^ activeSrc could still be nil
// check if there's input on stdin // check if there's input on stdin
stdinSrc, err := checkStdinSource(rc) stdinSrc, err := checkStdinSource(ctx, rc)
if err != nil { if err != nil {
return err return err
} }
@ -95,7 +96,7 @@ func activeSrcFromFlagsOrConfig(cmd *cobra.Command, srcs *source.Set) (*source.S
// If there is, that pipe is inspected, and if it has recognizable // If there is, that pipe is inspected, and if it has recognizable
// input, a new source instance with handle @stdin is constructed // input, a new source instance with handle @stdin is constructed
// and returned. // and returned.
func checkStdinSource(rc *RunContext) (*source.Source, error) { func checkStdinSource(ctx context.Context, rc *RunContext) (*source.Source, error) {
cmd := rc.Cmd cmd := rc.Cmd
f := rc.Stdin f := rc.Stdin
@ -140,7 +141,7 @@ func checkStdinSource(rc *RunContext) (*source.Source, error) {
} }
if typ == source.TypeNone { if typ == source.TypeNone {
typ, err = rc.files.TypeStdin(rc.Context) typ, err = rc.files.TypeStdin(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -171,7 +171,7 @@ func (d *driveri) CopyTable(ctx context.Context, db sqlz.DB, fromTable, toTable
stmt += " WHERE 0" stmt += " WHERE 0"
} }
affected, err := sqlz.ExecResult(ctx, db, stmt) affected, err := sqlz.ExecAffected(ctx, db, stmt)
if err != nil { if err != nil {
return 0, errz.Err(err) return 0, errz.Err(err)
} }
@ -324,7 +324,7 @@ func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string,
return 0, errz.Append(err, errz.Err(tx.Rollback())) return 0, errz.Append(err, errz.Err(tx.Rollback()))
} }
affected, err = sqlz.ExecResult(ctx, tx, fmt.Sprintf("TRUNCATE TABLE `%s`", tbl)) affected, err = sqlz.ExecAffected(ctx, tx, fmt.Sprintf("TRUNCATE TABLE `%s`", tbl))
if err != nil { if err != nil {
return affected, errz.Append(err, errz.Err(tx.Rollback())) return affected, errz.Append(err, errz.Err(tx.Rollback()))
} }

View File

@ -243,7 +243,7 @@ func (d *driveri) CopyTable(ctx context.Context, db sqlz.DB, fromTable, toTable
stmt += " WITH NO DATA" stmt += " WITH NO DATA"
} }
affected, err := sqlz.ExecResult(ctx, db, stmt) affected, err := sqlz.ExecAffected(ctx, db, stmt)
if err != nil { if err != nil {
return 0, errz.Err(err) return 0, errz.Err(err)
} }

View File

@ -103,16 +103,27 @@ func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string,
return 0, errz.Err(err) return 0, errz.Err(err)
} }
affected, err = sqlz.ExecResult(ctx, tx, fmt.Sprintf("DELETE FROM %q", tbl)) affected, err = sqlz.ExecAffected(ctx, tx, fmt.Sprintf("DELETE FROM %q", tbl))
if err != nil { if err != nil {
return affected, errz.Append(err, errz.Err(tx.Rollback())) return affected, errz.Append(err, errz.Err(tx.Rollback()))
} }
if reset { if reset {
_, err = sqlz.ExecResult(ctx, tx, "UPDATE sqlite_sequence SET seq = 0 WHERE name = ?", tbl) // First check that the sqlite_sequence table event exists. It
// may not exist if there are no auto-increment columns?
const q = `SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='sqlite_sequence'`
var count int64
err = tx.QueryRowContext(ctx, q).Scan(&count)
if err != nil { if err != nil {
return 0, errz.Append(err, errz.Err(tx.Rollback())) return 0, errz.Append(err, errz.Err(tx.Rollback()))
} }
if count > 0 {
_, err = tx.ExecContext(ctx, "UPDATE sqlite_sequence SET seq = 0 WHERE name = ?", tbl)
if err != nil {
return 0, errz.Append(err, errz.Err(tx.Rollback()))
}
}
} }
return affected, errz.Err(tx.Commit()) return affected, errz.Err(tx.Commit())
@ -188,7 +199,7 @@ func (d *driveri) CopyTable(ctx context.Context, db sqlz.DB, fromTable, toTable
} }
stmt := fmt.Sprintf("INSERT INTO %q SELECT * FROM %q", toTable, fromTable) stmt := fmt.Sprintf("INSERT INTO %q SELECT * FROM %q", toTable, fromTable)
affected, err := sqlz.ExecResult(ctx, db, stmt) affected, err := sqlz.ExecAffected(ctx, db, stmt)
if err != nil { if err != nil {
return 0, errz.Err(err) return 0, errz.Err(err)
} }

View File

@ -159,7 +159,7 @@ func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string,
} }
defer d.log.WarnIfFuncError(db.Close) defer d.log.WarnIfFuncError(db.Close)
affected, err = sqlz.ExecResult(ctx, db, fmt.Sprintf("DELETE FROM %q", tbl)) affected, err = sqlz.ExecAffected(ctx, db, fmt.Sprintf("DELETE FROM %q", tbl))
if err != nil { if err != nil {
return affected, errz.Wrapf(err, "truncate: failed to delete from %q", tbl) return affected, errz.Wrapf(err, "truncate: failed to delete from %q", tbl)
} }
@ -285,7 +285,7 @@ func (d *driveri) CopyTable(ctx context.Context, db sqlz.DB, fromTable, toTable
stmt = fmt.Sprintf("SELECT TOP(0) * INTO %q FROM %q", toTable, fromTable) stmt = fmt.Sprintf("SELECT TOP(0) * INTO %q FROM %q", toTable, fromTable)
} }
affected, err := sqlz.ExecResult(ctx, db, stmt) affected, err := sqlz.ExecAffected(ctx, db, stmt)
if err != nil { if err != nil {
return 0, errz.Err(err) return 0, errz.Err(err)
} }

38
go.mod
View File

@ -1,60 +1,48 @@
module github.com/neilotoole/sq module github.com/neilotoole/sq
go 1.14 go 1.15
// Using forked cobra for now because v1.1.3 does not pass Context
// to valid args completion funcs. There's an open PR for
// this: https://github.com/spf13/cobra/pull/1265
replace github.com/spf13/cobra v1.1.3 => github.com/neilotoole/cobra v1.1.4-0.20210220092732-c11dbd416310
require ( require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4 // indirect
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca
github.com/aws/aws-sdk-go v1.12.10 // indirect github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/buger/jsonparser v1.0.0
github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9 github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9
github.com/coreos/go-etcd v2.0.0+incompatible // indirect
github.com/cpuguy83/go-md2man v1.0.10 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec
github.com/djherbis/fscache v0.10.1 github.com/djherbis/fscache v0.10.1
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/emirpasic/gods v1.9.0 github.com/emirpasic/gods v1.9.0
github.com/fatih/color v1.9.0 github.com/fatih/color v1.9.0
github.com/go-ini/ini v1.30.0 // indirect
github.com/go-sql-driver/mysql v1.5.0 github.com/go-sql-driver/mysql v1.5.0
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/h2non/filetype v1.1.0 github.com/h2non/filetype v1.1.0
github.com/hashicorp/go-cleanhttp v0.0.0-20170211013415-3573b8b52aa7 // indirect
github.com/hashicorp/go-getter v0.0.0-20171007181130-2f449c791e6a
github.com/hashicorp/go-version v0.0.0-20170914154128-fc61389e27c7 // indirect
github.com/jackc/pgconn v1.5.0 github.com/jackc/pgconn v1.5.0
github.com/jackc/pgx/v4 v4.6.0 github.com/jackc/pgx/v4 v4.6.0
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect
github.com/jondot/goweight v1.0.5 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/magefile/mage v1.9.0 github.com/magefile/mage v1.9.0
github.com/mattn/go-colorable v0.1.4 github.com/mattn/go-colorable v0.1.4
github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty v0.0.12
github.com/mattn/go-runewidth v0.0.4 github.com/mattn/go-runewidth v0.0.4
github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/mattn/go-zglob v0.0.3 // indirect
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/neilotoole/errgroup v0.1.5 github.com/neilotoole/errgroup v0.1.5
github.com/neilotoole/lg v0.3.0 github.com/neilotoole/lg v0.3.0
github.com/nlopes/slack v0.1.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/ryboe/q v1.0.12 github.com/ryboe/q v1.0.12
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/segmentio/encoding v0.1.14 github.com/segmentio/encoding v0.1.14
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect github.com/spf13/cobra v1.1.3
github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.5.1 github.com/stretchr/testify v1.5.1
github.com/tbruyelle/hipchat-go v0.0.0-20160921153256-749fb9e14beb
github.com/tealeg/xlsx/v2 v2.0.1 github.com/tealeg/xlsx/v2 v2.0.1
github.com/testcontainers/testcontainers-go v0.5.0 github.com/testcontainers/testcontainers-go v0.5.0
github.com/thoas/go-funk v0.7.0 // indirect
github.com/ulikunitz/xz v0.5.4 // indirect
github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3 github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
go.uber.org/atomic v1.5.0 go.uber.org/atomic v1.5.0
go.uber.org/multierr v1.4.0 go.uber.org/multierr v1.4.0
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
@ -62,5 +50,5 @@ require (
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
gopkg.in/djherbis/atime.v1 v1.0.0 // indirect gopkg.in/djherbis/atime.v1 v1.0.0 // indirect
gopkg.in/djherbis/stream.v1 v1.3.1 // indirect gopkg.in/djherbis/stream.v1 v1.3.1 // indirect
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.4.0
) )

267
go.sum
View File

@ -1,45 +1,60 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
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/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= 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/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
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=
github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4 h1:EBTWhcAX7rNQ80RLwLCpHZBBrJuzallFHnF+yMXo928=
github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca h1:QHbltbNkVcw97h4zA/L8gA4o3dJiFvBZ0gyZHrYXHbs= github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca h1:QHbltbNkVcw97h4zA/L8gA4o3dJiFvBZ0gyZHrYXHbs=
github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.12.10 h1:ihg0UOujHVcFciyc6zs/q5VLhoG1K+oDLqgpCxkAh04= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/aws/aws-sdk-go v1.12.10/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9 h1:N6In6gI4jApKZ6F8QqeVgPti7612P5EdLISBNbjQ8tk= github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9 h1:N6In6gI4jApKZ6F8QqeVgPti7612P5EdLISBNbjQ8tk=
github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/c2h5oh/datasize v0.0.0-20170519143321-54516c931ae9/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -47,6 +62,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos= github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos=
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/djherbis/fscache v0.10.1 h1:hDv+RGyvD+UDKyRYuLoVNbuRTnf2SrA2K3VyR1br9lk= github.com/djherbis/fscache v0.10.1 h1:hDv+RGyvD+UDKyRYuLoVNbuRTnf2SrA2K3VyR1br9lk=
github.com/djherbis/fscache v0.10.1/go.mod h1:yyPYtkNnnPXsW+81lAcQS6yab3G2CRfnPLotBvtbf0c= github.com/djherbis/fscache v0.10.1/go.mod h1:yyPYtkNnnPXsW+81lAcQS6yab3G2CRfnPLotBvtbf0c=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
@ -57,22 +74,22 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d h1:lDrio3iIdNb0Gw9CgH7cQF+iuB5mOOjdJ9ERNJCBgb4=
github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo= github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY= github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY=
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-ini/ini v1.30.0 h1:bcFeUQUA+99t1cZPXmtc7HpGv2KTlZGIFeBDWQh2DRw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-ini/ini v1.30.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
@ -84,39 +101,69 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA= github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/go-cleanhttp v0.0.0-20170211013415-3573b8b52aa7 h1:67fHcS+inUoiIqWCKIqeDuq2AlPHNHPiTqp97LdQ+bc= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/go-cleanhttp v0.0.0-20170211013415-3573b8b52aa7/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/go-getter v0.0.0-20171007181130-2f449c791e6a h1:deqk3CV92a9CYhyfYjIJPAB2X4/vheVghmoBCno6WQM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-getter v0.0.0-20171007181130-2f449c791e6a/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-version v0.0.0-20170914154128-fc61389e27c7 h1:Tijq+ZHupzK8WfomfH2s5dpKkpZd2TcN2i1LDbzWbwk= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-version v0.0.0-20170914154128-fc61389e27c7/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
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/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@ -163,17 +210,18 @@ github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oA
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jondot/goweight v1.0.5 h1:aRpnyj1G8BLLNhem8xezuuV0GlFz4G11e3/UtBU/FlQ=
github.com/jondot/goweight v1.0.5/go.mod h1:3PRcpOwkyspe1t4+KCNgauas+aNDTSSCwZ6AQ4kDD/A=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
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 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
@ -192,10 +240,13 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -208,25 +259,31 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53 h1:tGfIHhDghvEnneeRhODvGYOt305TPwingKt6p90F4MU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/neilotoole/cobra v1.1.4-0.20210220092732-c11dbd416310 h1:++ilwBgxD5woFaMRfHmbJDbZ4mpoJT/ghS2I5pjrn+A=
github.com/neilotoole/cobra v1.1.4-0.20210220092732-c11dbd416310/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/neilotoole/errgroup v0.1.5 h1:DxEGoIfFm5ooGicidR+okiHjoOaGRKFaSxDPVZuuu2I= github.com/neilotoole/errgroup v0.1.5 h1:DxEGoIfFm5ooGicidR+okiHjoOaGRKFaSxDPVZuuu2I=
github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
github.com/neilotoole/lg v0.3.0 h1:2/IESY8l903AeK9nvH3AWVI/p8up+8wmKWIuts7PpxY= github.com/neilotoole/lg v0.3.0 h1:2/IESY8l903AeK9nvH3AWVI/p8up+8wmKWIuts7PpxY=
github.com/neilotoole/lg v0.3.0/go.mod h1:CUHyAinxegpXWV2uPbGP8R3OzRMlAd78YxtA9xiOeFA= github.com/neilotoole/lg v0.3.0/go.mod h1:CUHyAinxegpXWV2uPbGP8R3OzRMlAd78YxtA9xiOeFA=
github.com/nlopes/slack v0.1.0 h1:YnVhdQvWT/m0TDh3VNpSoCBDlD7Y4pz1qUqb/NrNyUs= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/nlopes/slack v0.1.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -238,6 +295,7 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@ -246,36 +304,55 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryboe/q v1.0.12 h1:Ehx4fAUEaZ5KW9Cf9w+ShQ41r5Znc5076jPdnqbfZ3o= github.com/ryboe/q v1.0.12 h1:Ehx4fAUEaZ5KW9Cf9w+ShQ41r5Znc5076jPdnqbfZ3o=
github.com/ryboe/q v1.0.12/go.mod h1:6xM6Lm3ZAh5H8d85zLohckSJcqIjJspf1gGkWWS8YGs= github.com/ryboe/q v1.0.12/go.mod h1:6xM6Lm3ZAh5H8d85zLohckSJcqIjJspf1gGkWWS8YGs=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/encoding v0.1.14 h1:BfnglNbNRohLaBLf93uP5/IwKqeWrezXK/g6IRnj75c= github.com/segmentio/encoding v0.1.14 h1:BfnglNbNRohLaBLf93uP5/IwKqeWrezXK/g6IRnj75c=
github.com/segmentio/encoding v0.1.14/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/segmentio/encoding v0.1.14/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
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=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@ -287,25 +364,23 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tbruyelle/hipchat-go v0.0.0-20160921153256-749fb9e14beb h1:mb7xv0kx9XpGsLy5kCCa6+3HqSj495cEBQNMgljqZ48= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tbruyelle/hipchat-go v0.0.0-20160921153256-749fb9e14beb/go.mod h1:CJEWrlDz1qHCF/nywogFd3AqHUWbKCdpu9pSAdf1OzY=
github.com/tealeg/xlsx/v2 v2.0.1 h1:RP+VEscpPFjH2FnpKh1p9HVLAk1htqb9Urcxi2AU1ns= github.com/tealeg/xlsx/v2 v2.0.1 h1:RP+VEscpPFjH2FnpKh1p9HVLAk1htqb9Urcxi2AU1ns=
github.com/tealeg/xlsx/v2 v2.0.1/go.mod h1:l9GvhCCjdaIGkAyZcFedDALcYcXUOei55f6umRMOz9c= github.com/tealeg/xlsx/v2 v2.0.1/go.mod h1:l9GvhCCjdaIGkAyZcFedDALcYcXUOei55f6umRMOz9c=
github.com/testcontainers/testcontainers-go v0.5.0 h1:u7jCdf130QHBY5KK9xkFzZpkep24Rtl8HdWscMO9H/8= github.com/testcontainers/testcontainers-go v0.5.0 h1:u7jCdf130QHBY5KK9xkFzZpkep24Rtl8HdWscMO9H/8=
github.com/testcontainers/testcontainers-go v0.5.0/go.mod h1:BXwe1JilTOLT8cmVyPMDbIw7e+8UCGeAhxjBwguG5wQ= github.com/testcontainers/testcontainers-go v0.5.0/go.mod h1:BXwe1JilTOLT8cmVyPMDbIw7e+8UCGeAhxjBwguG5wQ=
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 h1:knaxjm6QMbUMNvuaSnJZmw0gRX4V/79JVUQiziJGM84= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713/go.mod h1:mlR+dHGb+4YgXkf13rkQTuzrneeHANxOm6+ZnEV9HsA=
github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y=
github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/ulikunitz/xz v0.5.4/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3 h1:NC3CI7do3KHtiuYhk1CdS9V2qS3jNa7Fs2Afcnnt+IE= github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3 h1:NC3CI7do3KHtiuYhk1CdS9V2qS3jNa7Fs2Afcnnt+IE=
github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3/go.mod h1:A47W3pdWONaZmXuLZgfKLAVgUY0qvfTRM5vVDKS40S4= github.com/xo/dburl v0.0.0-20200124232849-e9ec94f52bc3/go.mod h1:A47W3pdWONaZmXuLZgfKLAVgUY0qvfTRM5vVDKS40S4=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
@ -321,6 +396,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -328,6 +404,7 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -335,36 +412,75 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+v
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
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-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-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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/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=
@ -372,32 +488,67 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
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/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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
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/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk= google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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=
@ -411,15 +562,25 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/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.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=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0= gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0=
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -42,9 +42,9 @@ type DB interface {
Preparer Preparer
} }
// ExecResult invokes db.ExecContext, returning the count of rows // ExecAffected invokes db.ExecContext, returning the count of rows
// affected and any error. // affected and any error.
func ExecResult(ctx context.Context, db Execer, query string, args ...interface{}) (affected int64, err error) { func ExecAffected(ctx context.Context, db Execer, query string, args ...interface{}) (affected int64, err error) {
var res sql.Result var res sql.Result
res, err = db.ExecContext(ctx, query, args...) res, err = db.ExecContext(ctx, query, args...)
if err != nil { if err != nil {

View File

@ -135,7 +135,7 @@ type SQLDriver interface {
// must honor the table name and column names and kinds from tblDef. // must honor the table name and column names and kinds from tblDef.
CreateTable(ctx context.Context, db sqlz.DB, tblDef *sqlmodel.TableDef) error CreateTable(ctx context.Context, db sqlz.DB, tblDef *sqlmodel.TableDef) error
// TableExists returns true if there's an exist table tbl in db. // TableExists returns true if there's an existing table tbl in db.
TableExists(ctx context.Context, db sqlz.DB, tbl string) (bool, error) TableExists(ctx context.Context, db sqlz.DB, tbl string) (bool, error)
// CopyTable copies fromTable into a new table toTable. // CopyTable copies fromTable into a new table toTable.

View File

@ -90,14 +90,6 @@ func (s *Set) Add(src *Source) error {
return nil return nil
} }
// IndexOf returns the index of handle in s.
func (s *Set) IndexOf(handle string) (int, *Source) {
s.mu.Lock()
defer s.mu.Unlock()
return s.indexOf(handle)
}
// Exists returns true if handle already exists in the set. // Exists returns true if handle already exists in the set.
func (s *Set) Exists(handle string) bool { func (s *Set) Exists(handle string) bool {
s.mu.Lock() s.mu.Lock()
@ -263,6 +255,19 @@ func (s *Set) Remove(handle string) error {
return nil return nil
} }
// Handles returns the set of source handles.
func (s *Set) Handles() []string {
s.mu.Lock()
defer s.mu.Unlock()
handles := make([]string, len(s.data.Items))
for i := range s.data.Items {
handles[i] = s.data.Items[i].Handle
}
return handles
}
// VerifySetIntegrity verifies the internal state of s. // VerifySetIntegrity verifies the internal state of s.
// Typically this func is invoked after s has been loaded // Typically this func is invoked after s has been loaded
// from config, verifying that the config is not corrupt. // from config, verifying that the config is not corrupt.

View File

@ -67,8 +67,13 @@ func (s *Source) RedactedLocation() string {
if s == nil { if s == nil {
return "" return ""
} }
loc := s.Location return RedactLocation(s.Location)
}
// RedactLocation returns a redacted version of the source
// location loc, with the password component (if any) of
// the location masked.
func RedactLocation(loc string) string {
switch { switch {
case loc == "": case loc == "":
return "" return ""

View File

@ -21,7 +21,7 @@ func main() {
cancelFn() cancelFn()
}() }()
err := cli.Execute(ctx, os.Stdin, os.Stdout, os.Stderr, os.Args) err := cli.Execute(ctx, os.Stdin, os.Stdout, os.Stderr, os.Args[1:])
if err != nil { if err != nil {
cancelFn() cancelFn()
os.Exit(1) os.Exit(1)