From 58d6664faaa53a7919c78245b82071d98dd24da5 Mon Sep 17 00:00:00 2001 From: Kelvin Clement Mwinuka Date: Mon, 3 Jul 2023 08:39:19 +0800 Subject: [PATCH] Added plugins folder location in config for the server. Created get and ser plugin files. Created server function to load plugins. Moved Makefile inside server folder to build and run server. --- server/Makefile | 11 ++++ server/main.go | 80 ++++++++++++++++++++++++++++-- server/plugins/commands/get/get.go | 36 ++++++++++++++ server/plugins/commands/set/set.go | 36 ++++++++++++++ 4 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 server/Makefile create mode 100644 server/plugins/commands/get/get.go create mode 100644 server/plugins/commands/set/set.go diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 0000000..80c68a3 --- /dev/null +++ b/server/Makefile @@ -0,0 +1,11 @@ +build-plugins: + go build -buildmode=plugin -o bin/plugins/commands/command_get.so plugins/commands/get/get.go + go build -buildmode=plugin -o bin/plugins/commands/command_set.so plugins/commands/set/set.go + +build-server: + go build -o bin/server ./*.go + +build: build-plugins build-server + +run: + ./bin/server diff --git a/server/main.go b/server/main.go index 04d75b4..865e8f1 100644 --- a/server/main.go +++ b/server/main.go @@ -5,22 +5,35 @@ import ( "crypto/tls" "fmt" "io" + "io/ioutil" + "log" "net" "net/http" + "path" + "plugin" + "strings" "sync" "github.com/kelvinmwinuka/memstore/serialization" "github.com/kelvinmwinuka/memstore/utils" ) +type Plugin interface { + Name() string + Command() string + Description() string + HandleCommand(tokens []string, server interface{}) error +} + type Data struct { mu sync.Mutex data map[string]interface{} } type Server struct { - config utils.Config - data Data + config utils.Config + data Data + plugins []Plugin } func (server *Server) handleConnection(conn net.Conn) { @@ -116,11 +129,69 @@ func (server *Server) StartHTTP() { } } +func (server *Server) LoadPlugins() { + conf := server.config + + // Load plugins + pluginDirs, err := ioutil.ReadDir(conf.Plugins) + + if err != nil { + log.Fatal(err) + } + + for _, file := range pluginDirs { + if file.IsDir() { + switch file.Name() { + case "commands": + files, err := ioutil.ReadDir(path.Join(conf.Plugins, "commands")) + + if err != nil { + log.Fatal(err) + } + + for _, file := range files { + if !strings.HasSuffix(file.Name(), ".so") { + // Skip files that are not .so + continue + } + p, err := plugin.Open(path.Join(conf.Plugins, "commands", file.Name())) + if err != nil { + log.Fatal(err) + } + + pluginSymbol, err := p.Lookup("Plugin") + if err != nil { + fmt.Printf("unexpected plugin symbol in plugin %s\n", file.Name()) + continue + } + + plugin, ok := pluginSymbol.(Plugin) + if !ok { + fmt.Printf("invalid plugin signature in plugin %s \n", file.Name()) + continue + } + + // Check if a plugin that handles the same command already exists + for _, loadedPlugin := range server.plugins { + if loadedPlugin.Command() == plugin.Command() { + fmt.Printf("plugin that handles %s command already exists. Please handle a different command.", plugin.Command()) + } + } + + server.plugins = append(server.plugins, plugin) + } + } + } + } +} + func (server *Server) Start() { server.data.data = make(map[string]interface{}) conf := server.config + server.LoadPlugins() + if conf.TLS && (len(conf.Key) <= 0 || len(conf.Cert) <= 0) { fmt.Println("Must provide key and certificate file paths for TLS mode.") return @@ -134,10 +205,9 @@ func (server *Server) Start() { } func main() { - conf := utils.GetConfig() - server := Server{ - config: conf, + config: utils.GetConfig(), + plugins: []Plugin{}, } server.Start() diff --git a/server/plugins/commands/get/get.go b/server/plugins/commands/get/get.go new file mode 100644 index 0000000..fda450b --- /dev/null +++ b/server/plugins/commands/get/get.go @@ -0,0 +1,36 @@ +package main + +type Server interface { + GetData(key string) interface{} + SetData(key string, value interface{}) +} + +type plugin struct { + name string + command string + description string +} + +var Plugin plugin + +func (p *plugin) Name() string { + return p.name +} + +func (p *plugin) Command() string { + return p.command +} + +func (p *plugin) Description() string { + return p.description +} + +func (p *plugin) HandleCommand(tokens []string, server interface{}) error { + return nil +} + +func init() { + Plugin.name = "GetCommand" + Plugin.command = "get" + Plugin.description = "Get the value from the specified key" +} diff --git a/server/plugins/commands/set/set.go b/server/plugins/commands/set/set.go new file mode 100644 index 0000000..c197753 --- /dev/null +++ b/server/plugins/commands/set/set.go @@ -0,0 +1,36 @@ +package main + +type Server interface { + GetData(key string) interface{} + SetData(key string, value interface{}) +} + +type plugin struct { + name string + command string + description string +} + +var Plugin plugin + +func (p *plugin) Name() string { + return p.name +} + +func (p *plugin) Command() string { + return p.command +} + +func (p *plugin) Description() string { + return p.description +} + +func (p *plugin) HandleCommand(tokens []string, server interface{}) error { + return nil +} + +func init() { + Plugin.name = "SetCommand" + Plugin.command = "set" + Plugin.description = "Set the value of the specified key" +}