Add info command (#141)

* Generate a new runid for each start of godis

* Add info command

* Add info command

* Generate a new runid for each start of godis

* Add unittests for the info command
This commit is contained in:
bjr
2023-03-19 16:17:40 +08:00
committed by GitHub
parent 3e6d2090c5
commit d7f6420f69
5 changed files with 128 additions and 2 deletions

View File

@@ -2,17 +2,26 @@ package config
import ( import (
"bufio" "bufio"
"github.com/hdt3213/godis/lib/utils"
"io" "io"
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/hdt3213/godis/lib/logger" "github.com/hdt3213/godis/lib/logger"
) )
var (
ClusterMode = "cluster"
StandaloneMode = "standalone"
)
// ServerProperties defines global config properties // ServerProperties defines global config properties
type ServerProperties struct { type ServerProperties struct {
// for Public configuration
RunID string `cfg:"runid"` // runID always different at every exec.
Bind string `cfg:"bind"` Bind string `cfg:"bind"`
Port int `cfg:"port"` Port int `cfg:"port"`
AppendOnly bool `cfg:"appendonly"` AppendOnly bool `cfg:"appendonly"`
@@ -27,19 +36,36 @@ type ServerProperties struct {
SlaveAnnounceIP string `cfg:"slave-announce-ip"` SlaveAnnounceIP string `cfg:"slave-announce-ip"`
ReplTimeout int `cfg:"repl-timeout"` ReplTimeout int `cfg:"repl-timeout"`
// for cluster mode configuration
ClusterEnabled string `cfg:"cluster-enabled"` // Not used at present.
Peers []string `cfg:"peers"` Peers []string `cfg:"peers"`
Self string `cfg:"self"` Self string `cfg:"self"`
// config file path
CfPath string `cfg:"cf,omitempty"`
}
type ServerInfo struct {
StartUpTime time.Time
} }
// Properties holds global config properties // Properties holds global config properties
var Properties *ServerProperties var Properties *ServerProperties
var EachTimeServerInfo *ServerInfo
func init() { func init() {
// A few stats we don't want to reset: server startup time, and peak mem.
EachTimeServerInfo = &ServerInfo{
StartUpTime: time.Now(),
}
// default config // default config
Properties = &ServerProperties{ Properties = &ServerProperties{
Bind: "127.0.0.1", Bind: "127.0.0.1",
Port: 6379, Port: 6379,
AppendOnly: false, AppendOnly: false,
RunID: utils.RandString(40),
} }
} }
@@ -109,4 +135,5 @@ func SetupConfig(configFilename string) {
} }
defer file.Close() defer file.Close()
Properties = parse(file) Properties = parse(file)
Properties.RunID = utils.RandString(40)
} }

View File

@@ -17,6 +17,8 @@ import (
"time" "time"
) )
var godisVersion = "1.2.8" // do not modify
// Server is a redis-server with full capabilities including multiple database, rdb loader, replication // Server is a redis-server with full capabilities including multiple database, rdb loader, replication
type Server struct { type Server struct {
dbSet []*atomic.Value // *DB dbSet []*atomic.Value // *DB
@@ -90,6 +92,10 @@ func (server *Server) Exec(c redis.Connection, cmdLine [][]byte) (result redis.R
if cmdName == "auth" { if cmdName == "auth" {
return Auth(c, cmdLine[1:]) return Auth(c, cmdLine[1:])
} }
// info
if cmdName == "info" {
return Info(c, cmdLine)
}
if !isAuthenticated(c) { if !isAuthenticated(c) {
return protocol.MakeErrReply("NOAUTH Authentication required") return protocol.MakeErrReply("NOAUTH Authentication required")
} }

View File

@@ -1,9 +1,14 @@
package database package database
import ( import (
"fmt"
"github.com/hdt3213/godis/config" "github.com/hdt3213/godis/config"
"github.com/hdt3213/godis/interface/redis" "github.com/hdt3213/godis/interface/redis"
"github.com/hdt3213/godis/redis/protocol" "github.com/hdt3213/godis/redis/protocol"
"os"
"runtime"
"strings"
"time"
) )
// Ping the server // 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 // Auth validate client's password
func Auth(c redis.Connection, args [][]byte) redis.Reply { func Auth(c redis.Connection, args [][]byte) redis.Reply {
if len(args) != 1 { if len(args) != 1 {
@@ -39,3 +62,57 @@ func isAuthenticated(c redis.Connection) bool {
} }
return c.GetPassword() == config.Properties.RequirePass 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
}

View File

@@ -39,3 +39,17 @@ func TestAuth(t *testing.T) {
asserts.AssertStatusReply(t, ret, "OK") 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)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/hdt3213/godis/config" "github.com/hdt3213/godis/config"
"github.com/hdt3213/godis/lib/logger" "github.com/hdt3213/godis/lib/logger"
"github.com/hdt3213/godis/lib/utils"
RedisServer "github.com/hdt3213/godis/redis/server" RedisServer "github.com/hdt3213/godis/redis/server"
"github.com/hdt3213/godis/tcp" "github.com/hdt3213/godis/tcp"
"os" "os"
@@ -23,6 +24,7 @@ var defaultProperties = &config.ServerProperties{
AppendOnly: false, AppendOnly: false,
AppendFilename: "", AppendFilename: "",
MaxClients: 1000, MaxClients: 1000,
RunID: utils.RandString(40),
} }
func fileExists(filename string) bool { func fileExists(filename string) bool {