diff --git a/config/config.go b/config/config.go index 1c1bfa3..d12c789 100644 --- a/config/config.go +++ b/config/config.go @@ -2,17 +2,26 @@ package config import ( "bufio" + "github.com/hdt3213/godis/lib/utils" "io" "os" "reflect" "strconv" "strings" + "time" "github.com/hdt3213/godis/lib/logger" ) +var ( + ClusterMode = "cluster" + StandaloneMode = "standalone" +) + // ServerProperties defines global config properties type ServerProperties struct { + // for Public configuration + RunID string `cfg:"runid"` // runID always different at every exec. Bind string `cfg:"bind"` Port int `cfg:"port"` AppendOnly bool `cfg:"appendonly"` @@ -27,19 +36,36 @@ type ServerProperties struct { SlaveAnnounceIP string `cfg:"slave-announce-ip"` ReplTimeout int `cfg:"repl-timeout"` - Peers []string `cfg:"peers"` - Self string `cfg:"self"` + // for cluster mode configuration + ClusterEnabled string `cfg:"cluster-enabled"` // Not used at present. + Peers []string `cfg:"peers"` + Self string `cfg:"self"` + + // config file path + CfPath string `cfg:"cf,omitempty"` +} + +type ServerInfo struct { + StartUpTime time.Time } // Properties holds global config properties var Properties *ServerProperties +var EachTimeServerInfo *ServerInfo + func init() { + // A few stats we don't want to reset: server startup time, and peak mem. + EachTimeServerInfo = &ServerInfo{ + StartUpTime: time.Now(), + } + // default config Properties = &ServerProperties{ Bind: "127.0.0.1", Port: 6379, AppendOnly: false, + RunID: utils.RandString(40), } } @@ -109,4 +135,5 @@ func SetupConfig(configFilename string) { } defer file.Close() Properties = parse(file) + Properties.RunID = utils.RandString(40) } diff --git a/database/server.go b/database/server.go index d11f41a..fff85ba 100644 --- a/database/server.go +++ b/database/server.go @@ -17,6 +17,8 @@ import ( "time" ) +var godisVersion = "1.2.8" // do not modify + // Server is a redis-server with full capabilities including multiple database, rdb loader, replication type Server struct { dbSet []*atomic.Value // *DB @@ -90,6 +92,10 @@ func (server *Server) Exec(c redis.Connection, cmdLine [][]byte) (result redis.R if cmdName == "auth" { return Auth(c, cmdLine[1:]) } + // info + if cmdName == "info" { + return Info(c, cmdLine) + } if !isAuthenticated(c) { return protocol.MakeErrReply("NOAUTH Authentication required") } diff --git a/database/systemcmd.go b/database/systemcmd.go index 6d38cfc..6db80dc 100644 --- a/database/systemcmd.go +++ b/database/systemcmd.go @@ -1,9 +1,14 @@ package database import ( + "fmt" "github.com/hdt3213/godis/config" "github.com/hdt3213/godis/interface/redis" "github.com/hdt3213/godis/redis/protocol" + "os" + "runtime" + "strings" + "time" ) // Ping the server @@ -17,6 +22,24 @@ func Ping(c redis.Connection, args [][]byte) redis.Reply { } } +// Info the information of the godis server returned by the INFO command +func Info(c redis.Connection, args [][]byte) redis.Reply { + if len(args) == 1 { + return protocol.MakeBulkReply(GenGodisInfoString()) + } else if len(args) == 2 { + section := strings.ToLower(string(args[1])) + switch section { + case "server": + reply := GenGodisInfoString() + return protocol.MakeBulkReply(reply) + } + } else { + return protocol.MakeErrReply("ERR wrong number of arguments for 'info' command") + } + + return &protocol.NullBulkReply{} +} + // Auth validate client's password func Auth(c redis.Connection, args [][]byte) redis.Reply { if len(args) != 1 { @@ -39,3 +62,57 @@ func isAuthenticated(c redis.Connection) bool { } return c.GetPassword() == config.Properties.RequirePass } + +func GenGodisInfoString() []byte { + startUpTimeFromNow := getGodisRuninngTime() + s := fmt.Sprintf("# Server\r\n"+ + "godis_version:%s\r\n"+ + //"godis_git_sha1:%s\r\n"+ + //"godis_git_dirty:%d\r\n"+ + //"godis_build_id:%s\r\n"+ + "godis_mode:%s\r\n"+ + "os:%s %s\r\n"+ + "arch_bits:%d\r\n"+ + //"multiplexing_api:%s\r\n"+ + "go_version:%s\r\n"+ + "process_id:%d\r\n"+ + "run_id:%s\r\n"+ + "tcp_port:%d\r\n"+ + "uptime_in_seconds:%d\r\n"+ + "uptime_in_days:%d\r\n"+ + //"hz:%d\r\n"+ + //"lru_clock:%d\r\n"+ + "config_file:%s\r\n", + godisVersion, + //TODO, + //TODO, + //TODO, + getGodisRunningMode(), + runtime.GOOS, runtime.GOARCH, + 32<<(^uint(0)>>63), + //TODO, + runtime.Version(), + os.Getpid(), + config.Properties.RunID, + config.Properties.Port, + startUpTimeFromNow, + startUpTimeFromNow/time.Duration(3600*24), + //TODO, + //TODO, + config.Properties.CfPath) + return []byte(s) +} + +// getGodisRunningMode return godis running mode +func getGodisRunningMode() string { + if config.Properties.ClusterEnabled == "yes" { + return config.ClusterMode + } else { + return config.StandaloneMode + } +} + +// getGodisRuninngTime return the running time of godis +func getGodisRuninngTime() time.Duration { + return time.Since(config.EachTimeServerInfo.StartUpTime) / time.Second +} diff --git a/database/systemcmd_test.go b/database/systemcmd_test.go index fe0e7ce..123abd1 100644 --- a/database/systemcmd_test.go +++ b/database/systemcmd_test.go @@ -39,3 +39,17 @@ func TestAuth(t *testing.T) { asserts.AssertStatusReply(t, ret, "OK") } + +func TestInfo(t *testing.T) { + c := connection.NewFakeConn() + ret := testServer.Exec(c, utils.ToCmdLine("INFO")) + asserts.AssertNotError(t, ret) + ret = testServer.Exec(c, utils.ToCmdLine("INFO", "server")) + asserts.AssertNotError(t, ret) + ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "SeRvEr")) + asserts.AssertNotError(t, ret) + ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "abc", "bde")) + asserts.AssertErrReply(t, ret, "ERR wrong number of arguments for 'info' command") + ret = testServer.Exec(c, utils.ToCmdLine("INFO", "abc")) + asserts.AssertNullBulk(t, ret) +} diff --git a/main.go b/main.go index 85ee214..d19f9fa 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hdt3213/godis/config" "github.com/hdt3213/godis/lib/logger" + "github.com/hdt3213/godis/lib/utils" RedisServer "github.com/hdt3213/godis/redis/server" "github.com/hdt3213/godis/tcp" "os" @@ -23,6 +24,7 @@ var defaultProperties = &config.ServerProperties{ AppendOnly: false, AppendFilename: "", MaxClients: 1000, + RunID: utils.RandString(40), } func fileExists(filename string) bool {