From 38ca7c168020a7be1a162abc061fd2eece530f92 Mon Sep 17 00:00:00 2001 From: Kristian Tsivkov <109758711+ktsivkov@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:03:47 +0200 Subject: [PATCH] Add support for MODULE LOADEX command (#2490) * Added support for MODULE LOADEX command Co-authored-by: Anurag Bandyopadhyay --- commands.go | 31 ++++++++++++++++++++++++++++++ commands_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++- export_test.go | 4 ++++ main_test.go | 2 +- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index c94ccdfe..3721df4e 100644 --- a/commands.go +++ b/commands.go @@ -497,6 +497,8 @@ type Cmdable interface { GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd ACLDryRun(ctx context.Context, username string, command ...interface{}) *StringCmd + + ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd } type StatefulCmdable interface { @@ -3888,3 +3890,32 @@ func (c cmdable) ACLDryRun(ctx context.Context, username string, command ...inte _ = c(ctx, cmd) return cmd } + +// ModuleLoadexConfig struct is used to specify the arguments for the MODULE LOADEX command of redis. +// `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]` +type ModuleLoadexConfig struct { + Path string + Conf map[string]interface{} + Args []interface{} +} + +func (c *ModuleLoadexConfig) toArgs() []interface{} { + args := make([]interface{}, 3, 3+len(c.Conf)*3+len(c.Args)*2) + args[0] = "MODULE" + args[1] = "LOADEX" + args[2] = c.Path + for k, v := range c.Conf { + args = append(args, "CONFIG", k, v) + } + for _, arg := range c.Args { + args = append(args, "ARGS", arg) + } + return args +} + +// ModuleLoadex Redis `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]` command. +func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd { + cmd := NewStringCmd(ctx, conf.toArgs()...) + _ = c(ctx, cmd) + return cmd +} diff --git a/commands_test.go b/commands_test.go index 89dcf932..e20470f2 100644 --- a/commands_test.go +++ b/commands_test.go @@ -1926,11 +1926,59 @@ var _ = Describe("Commands", func() { Expect(replace.Val()).To(Equal(int64(1))) }) - It("should acl dryryn", func() { + It("should acl dryrun", func() { dryRun := client.ACLDryRun(ctx, "default", "get", "randomKey") Expect(dryRun.Err()).NotTo(HaveOccurred()) Expect(dryRun.Val()).To(Equal("OK")) }) + + It("should fail module loadex", func() { + dryRun := client.ModuleLoadex(ctx, &redis.ModuleLoadexConfig{ + Path: "/path/to/non-existent-library.so", + Conf: map[string]interface{}{ + "param1": "value1", + }, + Args: []interface{}{ + "arg1", + }, + }) + Expect(dryRun.Err()).To(HaveOccurred()) + Expect(dryRun.Err().Error()).To(Equal("ERR Error loading the extension. Please check the server logs.")) + }) + + It("converts the module loadex configuration to a slice of arguments correctly", func() { + conf := &redis.ModuleLoadexConfig{ + Path: "/path/to/your/module.so", + Conf: map[string]interface{}{ + "param1": "value1", + }, + Args: []interface{}{ + "arg1", + "arg2", + 3, + }, + } + + args := conf.ToArgs() + + // Test if the arguments are in the correct order + expectedArgs := []interface{}{ + "MODULE", + "LOADEX", + "/path/to/your/module.so", + "CONFIG", + "param1", + "value1", + "ARGS", + "arg1", + "ARGS", + "arg2", + "ARGS", + 3, + } + + Expect(args).To(Equal(expectedArgs)) + }) }) Describe("hashes", func() { diff --git a/export_test.go b/export_test.go index 19a9dddc..3f92983d 100644 --- a/export_test.go +++ b/export_test.go @@ -98,3 +98,7 @@ func (c *Ring) ShardByName(name string) *ringShard { shard, _ := c.sharding.GetByName(name) return shard } + +func (c *ModuleLoadexConfig) ToArgs() []interface{} { + return c.toArgs() +} diff --git a/main_test.go b/main_test.go index b1a1f395..8e87d76f 100644 --- a/main_test.go +++ b/main_test.go @@ -314,7 +314,7 @@ func startRedis(port string, args ...string) (*redisProcess, error) { return nil, err } - baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir} + baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir, "--enable-module-command", "yes"} process, err := execCmd(redisServerBin, append(baseArgs, args...)...) if err != nil { return nil, err