summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md4
-rw-r--r--internal/config/args.go30
-rw-r--r--internal/config/config.go116
-rw-r--r--internal/config/initializer.go109
-rw-r--r--internal/config/setup.go17
-rw-r--r--internal/io/dlog/dlog.go5
-rw-r--r--internal/server/handlers/readcommand.go12
-rw-r--r--internal/server/handlers/serverhandler.go93
-rw-r--r--internal/source/source.go4
-rw-r--r--internal/version/version.go2
10 files changed, 191 insertions, 201 deletions
diff --git a/TODO.md b/TODO.md
index d91c859..836ab99 100644
--- a/TODO.md
+++ b/TODO.md
@@ -15,7 +15,7 @@ This is a loose list of what to do. Maybe for the next releae or maybe for a lat
[?] Client 4.x should print a warning when trying to connect to a 3.x server.
[ ] Update docs for color configuration
[ ] Update animated gifs
-[ ] Add more default fields to the default MAPREDUCE format.
+[x] Add more default fields to the default MAPREDUCE format.
[x] By default connect to localhost
[x] Can use additional args as file lists
[ ] Document the two things above
@@ -26,9 +26,7 @@ This is a loose list of what to do. Maybe for the next releae or maybe for a lat
[ ] test server health check
[ ] test spartan mode
[ ] document spartan mode
-[ ] Default client log dir is ~/log not ./log
[ ] Integration test for dcat in serverless mode
[ ] Integration test for dgrep in serverless mode
[ ] Integration test for dmap in serverless mode
[ ] Separate logger into server logger and client logger for serverless operation (e.g. server info logs are all Debug)
-[ ] In serverless, use prefix LOCAL and not REMOTE. And also use another color schema (magenta?)
diff --git a/internal/config/args.go b/internal/config/args.go
index 484aa8b..3e2eb1f 100644
--- a/internal/config/args.go
+++ b/internal/config/args.go
@@ -1,6 +1,7 @@
package config
import (
+ "encoding/base64"
"fmt"
"strings"
@@ -69,7 +70,32 @@ func (a *Args) String() string {
// SerializeOptions returns a string ready to be sent over the wire to the server.
func (a *Args) SerializeOptions() string {
- return fmt.Sprintf("quiet=%v:spartan=%v", a.Quiet, a.Spartan)
+ return fmt.Sprintf("quiet=%v:spartan=%v:serverless=%v", a.Quiet, a.Spartan, a.Serverless)
}
-// NEXT: Put the DeseializeOptions function here (move it away from the internal/server package)
+// DeserializeOptions deserializes the options, but into a map.
+func DeserializeOptions(opts []string) (map[string]string, error) {
+ options := make(map[string]string, len(opts))
+
+ for _, o := range opts {
+ kv := strings.SplitN(o, "=", 2)
+ if len(kv) != 2 {
+ return options, fmt.Errorf("Unable to parse options: %v", kv)
+ }
+ key := kv[0]
+ val := kv[1]
+
+ if strings.HasPrefix(val, "base64%") {
+ s := strings.SplitN(val, "%", 2)
+ decoded, err := base64.StdEncoding.DecodeString(s[1])
+ if err != nil {
+ return options, err
+ }
+ val = string(decoded)
+ }
+
+ options[key] = val
+ }
+
+ return options, nil
+}
diff --git a/internal/config/config.go b/internal/config/config.go
index 6d4730a..09ae994 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -1,14 +1,5 @@
package config
-import (
- "encoding/json"
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
-)
-
const (
// ControlUser is used for various DTail specific operations.
ControlUser string = "DTAIL-CONTROL"
@@ -33,97 +24,18 @@ var Server *ServerConfig
// Common holds common configs of both both, client and server.
var Common *CommonConfig
-// Used to initialize the configuration.
-type configInitializer struct {
- Common *CommonConfig
- Server *ServerConfig
- Client *ClientConfig
-}
-
-func (c *configInitializer) parseConfig(args *Args) {
- if strings.ToUpper(args.ConfigFile) == "NONE" {
- return
- }
-
- if args.ConfigFile != "" {
- c.parseSpecificConfig(args.ConfigFile)
- return
- }
-
- if homeDir, err := os.UserHomeDir(); err != nil {
- var paths []string
- paths = append(paths, fmt.Sprintf("%s/.config/dtail/dtail.conf", homeDir))
- paths = append(paths, fmt.Sprintf("%s/.dtail.conf", homeDir))
- for _, configPath := range paths {
- if _, err := os.Stat(configPath); !os.IsNotExist(err) {
- c.parseSpecificConfig(configPath)
- }
- }
- }
-}
-
-func (c *configInitializer) parseSpecificConfig(configFile string) {
- fd, err := os.Open(configFile)
- if err != nil {
- panic(fmt.Sprintf("Unable to read config file: %v", err))
- }
- defer fd.Close()
-
- cfgBytes, err := ioutil.ReadAll(fd)
- if err != nil {
- panic(fmt.Sprintf("Unable to read config file %s: %v", configFile, err))
- }
-
- err = json.Unmarshal([]byte(cfgBytes), c)
- if err != nil {
- panic(fmt.Sprintf("Unable to parse config file %s: %v", configFile, err))
- }
-}
-
-func (c *configInitializer) transformConfig(args *Args, additionalArgs []string,
- client *ClientConfig, server *ServerConfig, common *CommonConfig) (*ClientConfig, *ServerConfig, *CommonConfig) {
- if args.LogDir != "" {
- common.LogDir = args.LogDir
- }
- if strings.Contains(common.LogDir, "~/") {
- homeDir, err := os.UserHomeDir()
- if err != nil {
- panic(err)
- }
- common.LogDir = strings.ReplaceAll(common.LogDir, "~/", fmt.Sprintf("%s/", homeDir))
- }
- if common.LogStrategy == "" {
- common.LogStrategy = "daily"
- }
-
- if args.LogLevel != "" {
- common.LogLevel = args.LogLevel
- }
-
- if args.SSHPort != DefaultSSHPort {
- common.SSHPort = args.SSHPort
- }
- if args.NoColor {
- client.TermColorsEnable = false
- }
-
- if args.Spartan {
- args.Quiet = true
- args.NoColor = true
- }
-
- if args.Discovery == "" && args.ServersStr == "" {
- args.Serverless = true
- }
-
- // Interpret additional args as file list.
- if args.What == "" {
- var files []string
- for _, file := range flag.Args() {
- files = append(files, file)
- }
- args.What = strings.Join(files, ",")
- }
-
- return client, server, common
+// Setup the DTail configuration.
+func Setup(args *Args, additionalArgs []string) {
+ initializer := initializer{
+ Common: newDefaultCommonConfig(),
+ Server: newDefaultServerConfig(),
+ Client: newDefaultClientConfig(),
+ }
+ initializer.parseConfig(args)
+ Client, Server, Common = initializer.transformConfig(
+ args, additionalArgs,
+ initializer.Client,
+ initializer.Server,
+ initializer.Common,
+ )
}
diff --git a/internal/config/initializer.go b/internal/config/initializer.go
new file mode 100644
index 0000000..f1a9ec4
--- /dev/null
+++ b/internal/config/initializer.go
@@ -0,0 +1,109 @@
+package config
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+)
+
+// Used to initialize the configuration.
+type initializer struct {
+ Common *CommonConfig
+ Server *ServerConfig
+ Client *ClientConfig
+}
+
+func (c *initializer) parseConfig(args *Args) {
+ if strings.ToUpper(args.ConfigFile) == "NONE" {
+ return
+ }
+
+ if args.ConfigFile != "" {
+ c.parseSpecificConfig(args.ConfigFile)
+ return
+ }
+
+ if homeDir, err := os.UserHomeDir(); err != nil {
+ var paths []string
+ paths = append(paths, fmt.Sprintf("%s/.config/dtail/dtail.conf", homeDir))
+ paths = append(paths, fmt.Sprintf("%s/.dtail.conf", homeDir))
+ for _, configPath := range paths {
+ if _, err := os.Stat(configPath); !os.IsNotExist(err) {
+ c.parseSpecificConfig(configPath)
+ }
+ }
+ }
+}
+
+func (c *initializer) parseSpecificConfig(configFile string) {
+ fd, err := os.Open(configFile)
+ if err != nil {
+ panic(fmt.Sprintf("Unable to read config file: %v", err))
+ }
+ defer fd.Close()
+
+ cfgBytes, err := ioutil.ReadAll(fd)
+ if err != nil {
+ panic(fmt.Sprintf("Unable to read config file %s: %v", configFile, err))
+ }
+
+ err = json.Unmarshal([]byte(cfgBytes), c)
+ if err != nil {
+ panic(fmt.Sprintf("Unable to parse config file %s: %v", configFile, err))
+ }
+}
+
+func (c *initializer) transformConfig(args *Args, additionalArgs []string,
+ client *ClientConfig, server *ServerConfig, common *CommonConfig) (*ClientConfig, *ServerConfig, *CommonConfig) {
+ if args.LogDir != "" {
+ common.LogDir = args.LogDir
+ }
+ if strings.Contains(common.LogDir, "~/") {
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ panic(err)
+ }
+ common.LogDir = strings.ReplaceAll(common.LogDir, "~/", fmt.Sprintf("%s/", homeDir))
+ }
+ if common.LogStrategy == "" {
+ common.LogStrategy = "daily"
+ }
+
+ if args.LogLevel != "" {
+ common.LogLevel = args.LogLevel
+ } else if args.ServersStr == "" && args.Discovery == "" {
+ // We are in serverless mode. Default log level is WARN.
+ common.LogLevel = "WARN"
+ }
+
+ if args.SSHPort != DefaultSSHPort {
+ common.SSHPort = args.SSHPort
+ }
+ if args.NoColor {
+ client.TermColorsEnable = false
+ }
+
+ if args.Spartan {
+ args.Quiet = true
+ args.NoColor = true
+ }
+
+ if args.Discovery == "" && args.ServersStr == "" {
+ // We are not connecting to any servers.
+ args.Serverless = true
+ }
+
+ // Interpret additional args as file list.
+ if args.What == "" {
+ var files []string
+ for _, file := range flag.Args() {
+ files = append(files, file)
+ }
+ args.What = strings.Join(files, ",")
+ }
+
+ return client, server, common
+}
diff --git a/internal/config/setup.go b/internal/config/setup.go
deleted file mode 100644
index 7800914..0000000
--- a/internal/config/setup.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package config
-
-// Setup the DTail configuration.
-func Setup(args *Args, additionalArgs []string) {
- initializer := configInitializer{
- Common: newDefaultCommonConfig(),
- Server: newDefaultServerConfig(),
- Client: newDefaultClientConfig(),
- }
- initializer.parseConfig(args)
- Client, Server, Common = initializer.transformConfig(
- args, additionalArgs,
- initializer.Client,
- initializer.Server,
- initializer.Common,
- )
-}
diff --git a/internal/io/dlog/dlog.go b/internal/io/dlog/dlog.go
index 3de3120..6cacfe2 100644
--- a/internal/io/dlog/dlog.go
+++ b/internal/io/dlog/dlog.go
@@ -180,11 +180,6 @@ func (d *DLog) Warn(args ...interface{}) string {
}
func (d *DLog) Info(args ...interface{}) string {
- if d.sourcePackage == source.Server && d.sourceProcess != source.Client {
- // This can be dtail client in serverless mode. In this case log all
- // info server messages as verbose.
- return d.log(VERBOSE, args)
- }
return d.log(INFO, args)
}
diff --git a/internal/server/handlers/readcommand.go b/internal/server/handlers/readcommand.go
index c76ae2a..6579018 100644
--- a/internal/server/handlers/readcommand.go
+++ b/internal/server/handlers/readcommand.go
@@ -32,13 +32,13 @@ func (r *readCommand) Start(ctx context.Context, argc int, args []string, retrie
if argc >= 4 {
deserializedRegex, err := regex.Deserialize(strings.Join(args[2:], " "))
if err != nil {
- r.server.sendServerMessage(dlog.Server.Error(r.server.user, commandParseWarning, err))
+ r.server.send(r.server.serverMessages, dlog.Server.Error(r.server.user, commandParseWarning, err))
return
}
re = deserializedRegex
}
if argc < 3 {
- r.server.sendServerWarnMessage(dlog.Server.Warn(r.server.user, commandParseWarning, args, argc))
+ r.server.send(r.server.serverMessages, dlog.Server.Warn(r.server.user, commandParseWarning, args, argc))
return
}
r.readGlob(ctx, args[1], re, retries)
@@ -58,7 +58,7 @@ func (r *readCommand) readGlob(ctx context.Context, glob string, re regex.Regex,
if numPaths := len(paths); numPaths == 0 {
dlog.Server.Error(r.server.user, "No such file(s) to read", glob)
- r.server.sendServerWarnMessage(dlog.Server.Warn(r.server.user, "Unable to read file(s), check server logs"))
+ r.server.send(r.server.serverMessages, dlog.Server.Warn(r.server.user, "Unable to read file(s), check server logs"))
select {
case <-ctx.Done():
return
@@ -72,7 +72,7 @@ func (r *readCommand) readGlob(ctx context.Context, glob string, re regex.Regex,
return
}
- r.server.sendServerWarnMessage(dlog.Server.Warn(r.server.user, "Giving up to read file(s)"))
+ r.server.send(r.server.serverMessages, dlog.Server.Warn(r.server.user, "Giving up to read file(s)"))
return
}
@@ -93,7 +93,7 @@ func (r *readCommand) readFileIfPermissions(ctx context.Context, wg *sync.WaitGr
if !r.server.user.HasFilePermission(path, "readfiles") {
dlog.Server.Error(r.server.user, "No permission to read file", path, globID)
- r.server.sendServerWarnMessage(dlog.Server.Warn(r.server.user, "Unable to read file(s), check server logs"))
+ r.server.send(r.server.serverMessages, dlog.Server.Warn(r.server.user, "Unable to read file(s), check server logs"))
return
}
@@ -161,6 +161,6 @@ func (r *readCommand) makeGlobID(path, glob string) string {
return pathParts[len(pathParts)-1]
}
- r.server.sendServerWarnMessage(dlog.Server.Warn("Empty file path given?", path, glob))
+ r.server.send(r.server.serverMessages, dlog.Server.Warn("Empty file path given?", path, glob))
return ""
}
diff --git a/internal/server/handlers/serverhandler.go b/internal/server/handlers/serverhandler.go
index b664566..ace2626 100644
--- a/internal/server/handlers/serverhandler.go
+++ b/internal/server/handlers/serverhandler.go
@@ -15,8 +15,8 @@ import (
"github.com/mimecast/dtail/internal"
"github.com/mimecast/dtail/internal/config"
- "github.com/mimecast/dtail/internal/io/line"
"github.com/mimecast/dtail/internal/io/dlog"
+ "github.com/mimecast/dtail/internal/io/line"
"github.com/mimecast/dtail/internal/io/pool"
"github.com/mimecast/dtail/internal/mapr/server"
"github.com/mimecast/dtail/internal/omode"
@@ -46,6 +46,7 @@ type ServerHandler struct {
activeCommands int32
quiet bool
spartan bool
+ serverless bool
readBuf bytes.Buffer
writeBuf bytes.Buffer
}
@@ -99,6 +100,12 @@ func (h *ServerHandler) Read(p []byte) (n int, err error) {
return
}
+ if h.serverless {
+ // In serverless mode we have logged the server message already via the
+ // dlog logger, no need to send the message again to the client part.
+ return
+ }
+
// Handle normal server message (display to the user)
h.readBuf.WriteString("SERVER")
h.readBuf.WriteString(protocol.FieldDelimiter)
@@ -266,23 +273,24 @@ func (h *ServerHandler) handleUserCommand(ctx context.Context, argc int, args []
splitted := strings.Split(args[0], ":")
commandName := splitted[0]
- options, err := readOptions(splitted[1:])
+ options, err := config.DeserializeOptions(splitted[1:])
if err != nil {
- h.sendServerMessage(dlog.Server.Error(h.user, err))
+ h.send(h.serverMessages, dlog.Server.Error(h.user, err))
commandFinished()
return
}
- if quiet, ok := options["quiet"]; ok {
- if quiet == "true" {
- dlog.Server.Debug(h.user, "Enabling quiet mode")
- h.quiet = true
- }
+
+ if quiet, _ := options["quiet"]; quiet == "true" {
+ dlog.Server.Debug(h.user, "Enabling quiet mode")
+ h.quiet = true
}
- if spartan, ok := options["spartan"]; ok {
- if spartan == "true" {
- dlog.Server.Debug(h.user, "Enabling spartan mode")
- h.spartan = true
- }
+ if spartan, _ := options["spartan"]; spartan == "true" {
+ dlog.Server.Debug(h.user, "Enabling spartan mode")
+ h.spartan = true
+ }
+ if serverless, _ := options["serverless"]; serverless == "true" {
+ dlog.Server.Debug(h.user, "Enabling serverless mode")
+ h.serverless = true
}
switch commandName {
@@ -303,7 +311,7 @@ func (h *ServerHandler) handleUserCommand(ctx context.Context, argc int, args []
case "map":
command, aggregate, err := newMapCommand(h, argc, args)
if err != nil {
- h.sendServerMessage(err.Error())
+ h.send(h.serverMessages, err.Error())
dlog.Server.Error(h.user, err)
commandFinished()
return
@@ -320,14 +328,16 @@ func (h *ServerHandler) handleUserCommand(ctx context.Context, argc int, args []
commandFinished()
default:
- h.sendServerMessage(dlog.Server.Error(h.user, "Received unknown user command", commandName, argc, args, options))
+ h.send(h.serverMessages, dlog.Server.Error(h.user, "Received unknown user command", commandName, argc, args, options))
commandFinished()
}
}
func (h *ServerHandler) handleAckCommand(argc int, args []string) {
if argc < 3 {
- h.sendServerWarnMessage(dlog.Server.Warn(h.user, commandParseWarning, args, argc))
+ if !h.quiet {
+ h.send(h.serverMessages, dlog.Server.Warn(h.user, commandParseWarning, args, argc))
+ }
return
}
if args[1] == "close" && args[2] == "connection" {
@@ -346,23 +356,8 @@ func (h *ServerHandler) send(ch chan<- string, message string) {
}
}
-func (h *ServerHandler) sendServerMessage(message string) {
- h.send(h.serverMessageC(), message)
-}
-
-func (h *ServerHandler) sendServerWarnMessage(message string) {
- if h.quiet {
- return
- }
- h.send(h.serverMessageC(), message)
-}
-
-func (h *ServerHandler) serverMessageC() chan<- string {
- return h.serverMessages
-}
-
-func (h *ServerHandler) flushMessages() {
- dlog.Server.Debug(h.user, "flushMessages()")
+func (h *ServerHandler) flush() {
+ dlog.Server.Debug(h.user, "flush()")
unsentMessages := func() int {
return len(h.lines) + len(h.serverMessages) + len(h.maprMessages)
@@ -381,11 +376,11 @@ func (h *ServerHandler) flushMessages() {
func (h *ServerHandler) shutdown() {
dlog.Server.Debug(h.user, "shutdown()")
- h.flushMessages()
+ h.flush()
go func() {
select {
- case h.serverMessageC() <- ".syn close connection":
+ case h.serverMessages <- ".syn close connection":
case <-h.done.Done():
}
}()
@@ -408,31 +403,3 @@ func (h *ServerHandler) decrementActiveCommands() int32 {
atomic.AddInt32(&h.activeCommands, -1)
return atomic.LoadInt32(&h.activeCommands)
}
-
-func readOptions(opts []string) (map[string]string, error) {
- dlog.Server.Debug("Parsing options", opts)
- options := make(map[string]string, len(opts))
-
- for _, o := range opts {
- kv := strings.SplitN(o, "=", 2)
- if len(kv) != 2 {
- return options, fmt.Errorf("Unable to parse options: %v", kv)
- }
- key := kv[0]
- val := kv[1]
-
- if strings.HasPrefix(val, "base64%") {
- s := strings.SplitN(val, "%", 2)
- decoded, err := base64.StdEncoding.DecodeString(s[1])
- if err != nil {
- return options, err
- }
- val = string(decoded)
- }
-
- dlog.Server.Debug("Setting option", key, val)
- options[key] = val
- }
-
- return options, nil
-}
diff --git a/internal/source/source.go b/internal/source/source.go
index bf1c880..73dccb2 100644
--- a/internal/source/source.go
+++ b/internal/source/source.go
@@ -10,9 +10,9 @@ const (
func (s Source) String() string {
switch s {
case Client:
- return "Client"
+ return "CLIENT"
case Server:
- return "Server"
+ return "SERVER"
}
panic("Unknown log source type")
diff --git a/internal/version/version.go b/internal/version/version.go
index a54b560..4ff6eae 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -39,7 +39,7 @@ func PaintedString() string {
color.FgBlack, color.BgGreen)
additional := color.PaintStrWithAttr(fmt.Sprintf(" %s ", Additional),
- color.FgWhite, color.BgMagenta, color.AttrBlink)
+ color.FgWhite, color.BgMagenta, color.AttrUnderline)
return fmt.Sprintf("%s%v%s%s", name, version, protocol, additional)
}