diff --git a/Makefile b/Makefile index 01904a2f..8cfcb5cd 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ else endif RESTIC_VER := 0.13.1 -REDIS_DUMP_VER := 0.8.0-ac +REDIS_DUMP_VER := 0.8.1-ac ### ### These variables should not need tweaking. diff --git a/go.mod b/go.mod index fe98e7a6..edabd294 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module stash.appscode.dev/redis go 1.18 require ( - github.com/mediocregopher/radix/v3 v3.8.0 + github.com/mediocregopher/radix/v3 v3.8.1 github.com/spf13/cobra v1.6.0 github.com/yannh/redis-dump-go v0.0.0-00010101000000-000000000000 go.bytebuilders.dev/license-verifier/kubernetes v0.12.0 @@ -97,4 +97,4 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -replace github.com/yannh/redis-dump-go => github.com/kubedb/redis-dump-go v0.8.1-0.20230429151509-2f2a7ce60763 +replace github.com/yannh/redis-dump-go => github.com/kubedb/redis-dump-go v0.8.1-ac diff --git a/go.sum b/go.sum index e8a8f50e..24d1920e 100644 --- a/go.sum +++ b/go.sum @@ -293,8 +293,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kubedb/redis-dump-go v0.8.1-0.20230429151509-2f2a7ce60763 h1:HyNjcmSJSLEPXN+y6wNNLtNPfEcZiPotGPHbnkhj1g0= -github.com/kubedb/redis-dump-go v0.8.1-0.20230429151509-2f2a7ce60763/go.mod h1:u6sFg98XPtTAaIyUv5oq+4D8D6krErkijf78cV30VOA= +github.com/kubedb/redis-dump-go v0.8.1-ac h1:Cv126EMUQxBOKvVJCO/d4SRuBQBIuQzFvgPayG0mSlI= +github.com/kubedb/redis-dump-go v0.8.1-ac/go.mod h1:nEQHeV2eDU9UjWkd+PXjU5skPdS9CAGXaQs39VGi1NA= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -306,8 +306,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/mediocregopher/radix/v3 v3.8.0 h1:HI8EgkaM7WzsrFpYAkOXIgUKbjNonb2Ne7K6Le61Pmg= -github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M= +github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= diff --git a/pkg/backup.go b/pkg/backup.go index 910f15f1..376d16cc 100644 --- a/pkg/backup.go +++ b/pkg/backup.go @@ -205,19 +205,23 @@ func (opt *redisOptions) backupRedis(targetRef api_v1beta1.TargetRef) (*restic.B if err != nil { return nil, err } + err = opt.writeTLSCertsToFile(appBinding) + if err != nil { + return nil, err + } s := redisdump.Host{ Host: hostname, Port: int(port), Username: username, Password: password, - TlsHandler: nil, // TODO(Shaad7): Add support for tls protected redis + TlsHandler: nil, } session := opt.newSessionWrapper(RedisDumpCMD) session.setDatabaseCredentials(password) - err = opt.setTLSParameters(appBinding, session.cmd) + opt.setTLSParametersToCMD(appBinding, session.cmd) if err != nil { return nil, err } @@ -227,6 +231,13 @@ func (opt *redisOptions) backupRedis(targetRef api_v1beta1.TargetRef) (*restic.B return nil, err } + if appBinding.Spec.ClientConfig.CABundle != nil { + // clear all the args ( tls args ) + session.cmd.Args = session.cmd.Args[:0] + session.cmd.Args = append(session.cmd.Args, "--tls") + session.cmd.Args = append(session.cmd.Args, "--insecure") + } + session.cmd.Args = append(session.cmd.Args, "-host", s.Host) // if port is specified, append port in the arguments if s.Port != 0 { diff --git a/pkg/restore.go b/pkg/restore.go index de39adf2..bcfb8d8a 100644 --- a/pkg/restore.go +++ b/pkg/restore.go @@ -184,12 +184,27 @@ func (opt *redisOptions) restoreRedis(targetRef api_v1beta1.TargetRef) (*restic. return nil, err } + err = opt.writeTLSCertsToFile(appBinding) + if err != nil { + return nil, err + } + + var tlsHandler *redisdump.TlsHandler = nil + ca, cert, key := opt.getTLSParameter(appBinding) + if ca != "" { + tlsHandler = &redisdump.TlsHandler{ + CACertPath: ca, + CertPath: cert, + KeyPath: key, + } + } + s := redisdump.Host{ Host: hostname, Port: int(port), Username: username, Password: password, - TlsHandler: nil, // TODO(Shaad7): Add support for tls protected redis + TlsHandler: tlsHandler, } if hosts, err := redisdump.GetHosts(s, opt.NWorkers); err != nil { @@ -200,6 +215,13 @@ func (opt *redisOptions) restoreRedis(targetRef api_v1beta1.TargetRef) (*restic. startTime := time.Now() beforeKeys := 0 afterKeys := 0 + if appBinding.Spec.ClientConfig.CABundle != nil { + for i := range hosts { + hosts[i].TlsHandler = &redisdump.TlsHandler{ + SkipVerify: true, + } + } + } for _, host := range hosts { session := opt.newSessionWrapper(RedisRestoreCMD) @@ -209,7 +231,7 @@ func (opt *redisOptions) restoreRedis(targetRef api_v1beta1.TargetRef) (*restic. return nil, err } - err = opt.setTLSParameters(appBinding, session.cmd) + opt.setTLSParametersToCMD(appBinding, session.cmd) if err != nil { return nil, err } diff --git a/pkg/util.go b/pkg/util.go index 0eec0513..84cfcb1c 100644 --- a/pkg/util.go +++ b/pkg/util.go @@ -115,7 +115,7 @@ func (session *sessionWrapper) setDatabaseCredentials(password string) { session.sh.SetEnv(EnvRedisDumpGoAuth, password) } -func (opt *redisOptions) setTLSParameters(appBinding *appcatalog.AppBinding, cmd *restic.Command) error { +func (opt *redisOptions) writeTLSCertsToFile(appBinding *appcatalog.AppBinding) error { // if ssl enabled, add ca.crt in the arguments if appBinding.Spec.ClientConfig.CABundle != nil { parameters := v1alpha1.RedisConfiguration{} @@ -128,9 +128,6 @@ func (opt *redisOptions) setTLSParameters(appBinding *appcatalog.AppBinding, cmd if err := os.WriteFile(filepath.Join(opt.setupOptions.ScratchDir, core.ServiceAccountRootCAKey), appBinding.Spec.ClientConfig.CABundle, 0o600); err != nil { return err } - caPath := filepath.Join(opt.setupOptions.ScratchDir, core.ServiceAccountRootCAKey) - cmd.Args = append(cmd.Args, "--tls") - cmd.Args = append(cmd.Args, "--cacert", caPath) if parameters.ClientCertSecret != nil { clientSecret, err := opt.kubeClient.CoreV1().Secrets(opt.namespace).Get(context.TODO(), parameters.ClientCertSecret.Name, metav1.GetOptions{}) @@ -145,7 +142,6 @@ func (opt *redisOptions) setTLSParameters(appBinding *appcatalog.AppBinding, cmd if err := os.WriteFile(filepath.Join(opt.setupOptions.ScratchDir, core.TLSCertKey), certByte, 0o600); err != nil { return err } - certPath := filepath.Join(opt.setupOptions.ScratchDir, core.TLSCertKey) keyByte, ok := clientSecret.Data[core.TLSPrivateKeyKey] if !ok { @@ -155,12 +151,58 @@ func (opt *redisOptions) setTLSParameters(appBinding *appcatalog.AppBinding, cmd if err := os.WriteFile(filepath.Join(opt.setupOptions.ScratchDir, core.TLSPrivateKeyKey), keyByte, 0o600); err != nil { return err } + + } + } + return nil +} + +func (opt *redisOptions) setTLSParametersToCMD(appBinding *appcatalog.AppBinding, cmd *restic.Command) { + // if ssl enabled, add ca.crt in the arguments + if appBinding.Spec.ClientConfig.CABundle != nil { + parameters := v1alpha1.RedisConfiguration{} + if appBinding.Spec.Parameters != nil { + if err := json.Unmarshal(appBinding.Spec.Parameters.Raw, ¶meters); err != nil { + klog.Errorf("unable to unmarshal appBinding.Spec.Parameters.Raw. Reason: %v", err) + } + } + + caPath := filepath.Join(opt.setupOptions.ScratchDir, core.ServiceAccountRootCAKey) + cmd.Args = append(cmd.Args, "--tls") + cmd.Args = append(cmd.Args, "--cacert", caPath) + + if parameters.ClientCertSecret != nil { + certPath := filepath.Join(opt.setupOptions.ScratchDir, core.TLSCertKey) + keyPath := filepath.Join(opt.setupOptions.ScratchDir, core.TLSPrivateKeyKey) cmd.Args = append(cmd.Args, "--cert", certPath, "--key", keyPath) } } - return nil +} + +func (opt *redisOptions) getTLSParameter(appBinding *appcatalog.AppBinding) (string, string, string) { + // if ssl enabled, add ca.crt in the arguments + if appBinding.Spec.ClientConfig.CABundle != nil { + parameters := v1alpha1.RedisConfiguration{} + if appBinding.Spec.Parameters != nil { + if err := json.Unmarshal(appBinding.Spec.Parameters.Raw, ¶meters); err != nil { + klog.Errorf("unable to unmarshal appBinding.Spec.Parameters.Raw. Reason: %v", err) + } + } + + caPath := filepath.Join(opt.setupOptions.ScratchDir, core.ServiceAccountRootCAKey) + + if parameters.ClientCertSecret != nil { + certPath := filepath.Join(opt.setupOptions.ScratchDir, core.TLSCertKey) + + keyPath := filepath.Join(opt.setupOptions.ScratchDir, core.TLSPrivateKeyKey) + + return caPath, certPath, keyPath + } + return caPath, "", "" + } + return "", "", "" } func (session *sessionWrapper) setUserArgs(args string) { diff --git a/vendor/github.com/mediocregopher/radix/v3/CHANGELOG.md b/vendor/github.com/mediocregopher/radix/v3/CHANGELOG.md index 1a04e28a..642fbe63 100644 --- a/vendor/github.com/mediocregopher/radix/v3/CHANGELOG.md +++ b/vendor/github.com/mediocregopher/radix/v3/CHANGELOG.md @@ -1,5 +1,15 @@ Changelog from v3.0.1 and up. Prior changes don't have a changelog. +# v3.8.1 + +* Fixed `NewCluster` not returning an error if it can't connect to any of the + redis instances given. (#319) + +* Fix deadlock in `Cluster` when using `DoSecondary`. (#317) + +* Fix parsing for `CLUSTER SLOTS` command, which changed slightly with redis + 7.0. (#322) + # v3.8.0 **New** diff --git a/vendor/github.com/mediocregopher/radix/v3/README.md b/vendor/github.com/mediocregopher/radix/v3/README.md index 4ad07633..588398d9 100644 --- a/vendor/github.com/mediocregopher/radix/v3/README.md +++ b/vendor/github.com/mediocregopher/radix/v3/README.md @@ -7,6 +7,10 @@ below for documentation and general usage examples. **[v4 Documentation](https://pkg.go.dev/github.com/mediocregopher/radix/v4#section-documentation)** +**[Discussion/Support Chat](https://matrix.to/#/#radix:waffle.farm)** + +Please open an issue, or start a discussion in the chat, before opening a pull request! + ## Features * Standard print-like API which supports **all current and future redis commands**. diff --git a/vendor/github.com/mediocregopher/radix/v3/cluster.go b/vendor/github.com/mediocregopher/radix/v3/cluster.go index 5c472713..7de66a53 100644 --- a/vendor/github.com/mediocregopher/radix/v3/cluster.go +++ b/vendor/github.com/mediocregopher/radix/v3/cluster.go @@ -201,16 +201,24 @@ func NewCluster(clusterAddrs []string, opts ...ClusterOpt) (*Cluster, error) { } } + var err error + // make a pool to base the cluster on for _, addr := range clusterAddrs { - p, err := c.co.pf("tcp", addr) - if err != nil { + + var p Client + + if p, err = c.co.pf("tcp", addr); err != nil { continue } c.pools[addr] = p break } + if len(c.pools) == 0 { + return nil, fmt.Errorf("could not connect to any redis instances, last error was: %w", err) + } + p, err := c.pool("") if err != nil { for _, p := range c.pools { @@ -486,10 +494,9 @@ func (c *Cluster) syncEvery(d time.Duration) { }() } -func (c *Cluster) addrForKey(key string) string { +// v3.8.5 add the getting master node without lock to fix the fix deadlock. +func (c *Cluster) addrForKeyWithNoLock(key string) string { s := ClusterSlot([]byte(key)) - c.l.RLock() - defer c.l.RUnlock() for _, t := range c.primTopo { for _, slot := range t.Slots { if s >= slot[0] && s < slot[1] { @@ -500,10 +507,16 @@ func (c *Cluster) addrForKey(key string) string { return "" } +func (c *Cluster) addrForKey(key string) string { + c.l.RLock() + defer c.l.RUnlock() + return c.addrForKeyWithNoLock(key) +} + func (c *Cluster) secondaryAddrForKey(key string) string { c.l.RLock() defer c.l.RUnlock() - primAddr := c.addrForKey(key) + primAddr := c.addrForKeyWithNoLock(key) for addr := range c.secondaries[primAddr] { return addr } diff --git a/vendor/github.com/mediocregopher/radix/v3/cluster_topo.go b/vendor/github.com/mediocregopher/radix/v3/cluster_topo.go index b105a0a4..9398889b 100644 --- a/vendor/github.com/mediocregopher/radix/v3/cluster_topo.go +++ b/vendor/github.com/mediocregopher/radix/v3/cluster_topo.go @@ -6,6 +6,7 @@ import ( "io" "net" "sort" + "strconv" "github.com/mediocregopher/radix/v3/resp" "github.com/mediocregopher/radix/v3/resp/resp2" @@ -157,8 +158,15 @@ func (tss topoSlotSet) MarshalRESP(w io.Writer) error { marshal(resp2.Any{I: tss.slots[1] - 1}) for _, n := range tss.nodes { - host, port, _ := net.SplitHostPort(n.Addr) - node := []string{host, port} + + host, portStr, _ := net.SplitHostPort(n.Addr) + + port, err := strconv.Atoi(portStr) + if err != nil { + return err + } + + node := []interface{}{host, port} if n.ID != "" { node = append(node, n.ID) } @@ -186,21 +194,44 @@ func (tss *topoSlotSet) UnmarshalRESP(br *bufio.Reader) error { var primaryNode ClusterNode for i := 0; i < arrHead.N; i++ { - var nodeStrs []string - if err := (resp2.Any{I: &nodeStrs}).UnmarshalRESP(br); err != nil { + + var nodeArrHead resp2.ArrayHeader + if err := nodeArrHead.UnmarshalRESP(br); err != nil { + return err + } else if nodeArrHead.N < 2 { + return fmt.Errorf("expected at least 2 array elements, got %d", nodeArrHead.N) + } + + var ip resp2.BulkString + if err := ip.UnmarshalRESP(br); err != nil { return err - } else if len(nodeStrs) < 2 { - return fmt.Errorf("malformed node array: %#v", nodeStrs) } - ip, port := nodeStrs[0], nodeStrs[1] - var id string - if len(nodeStrs) > 2 { - id = nodeStrs[2] + + var port resp2.Int + if err := port.UnmarshalRESP(br); err != nil { + return err + } + + nodeArrHead.N -= 2 + + var id resp2.BulkString + if nodeArrHead.N > 0 { + if err := id.UnmarshalRESP(br); err != nil { + return err + } + nodeArrHead.N-- + } + + // discard anything after + for i := 0; i < nodeArrHead.N; i++ { + if err := (resp2.Any{}).UnmarshalRESP(br); err != nil { + return err + } } node := ClusterNode{ - Addr: net.JoinHostPort(ip, port), - ID: id, + Addr: net.JoinHostPort(ip.S, strconv.FormatInt(port.I, 10)), + ID: id.S, Slots: [][2]uint16{tss.slots}, } diff --git a/vendor/github.com/yannh/redis-dump-go/pkg/redisdump/tlsutils.go b/vendor/github.com/yannh/redis-dump-go/pkg/redisdump/tlsutils.go index 3aa38042..9d26fabc 100644 --- a/vendor/github.com/yannh/redis-dump-go/pkg/redisdump/tlsutils.go +++ b/vendor/github.com/yannh/redis-dump-go/pkg/redisdump/tlsutils.go @@ -9,17 +9,17 @@ import ( ) type TlsHandler struct { - skipVerify bool - caCertPath string - certPath string - keyPath string + SkipVerify bool + CACertPath string + CertPath string + KeyPath string } func NewTlsHandler(caCertPath, certPath, keyPath string, insecure bool) (*TlsHandler, error) { if caCertPath == "" && certPath == "" && keyPath == "" { if insecure { return &TlsHandler{ - skipVerify: true, + SkipVerify: true, }, nil } else { return nil, errors.New("no cert is set. if skip cert validation to set -insecure option") @@ -27,10 +27,10 @@ func NewTlsHandler(caCertPath, certPath, keyPath string, insecure bool) (*TlsHan } return &TlsHandler{ - skipVerify: false, - caCertPath: caCertPath, - certPath: certPath, - keyPath: keyPath, + SkipVerify: false, + CACertPath: caCertPath, + CertPath: certPath, + KeyPath: keyPath, }, nil } @@ -39,7 +39,7 @@ func tlsConfig(tlsHandler *TlsHandler) (*tls.Config, error) { return nil, nil } - if tlsHandler.skipVerify { + if tlsHandler.SkipVerify { return &tls.Config{ InsecureSkipVerify: true, }, nil @@ -47,8 +47,8 @@ func tlsConfig(tlsHandler *TlsHandler) (*tls.Config, error) { certPool := x509.NewCertPool() // ca cert is optional - if tlsHandler.caCertPath != "" { - pem, err := ioutil.ReadFile(tlsHandler.caCertPath) + if tlsHandler.CACertPath != "" { + pem, err := ioutil.ReadFile(tlsHandler.CACertPath) if err != nil { return nil, fmt.Errorf("connectionpool: unable to open CA certs: %v", err) } @@ -63,8 +63,8 @@ func tlsConfig(tlsHandler *TlsHandler) (*tls.Config, error) { RootCAs: certPool, } - if tlsHandler.certPath != "" && tlsHandler.keyPath != "" { - cert, err := tls.LoadX509KeyPair(tlsHandler.certPath, tlsHandler.keyPath) + if tlsHandler.CertPath != "" && tlsHandler.KeyPath != "" { + cert, err := tls.LoadX509KeyPair(tlsHandler.CertPath, tlsHandler.KeyPath) if err != nil { return nil, err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 991f0aa0..aecd87fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -107,7 +107,7 @@ github.com/json-iterator/go github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter -# github.com/mediocregopher/radix/v3 v3.8.0 +# github.com/mediocregopher/radix/v3 v3.8.1 ## explicit; go 1.13 github.com/mediocregopher/radix/v3 github.com/mediocregopher/radix/v3/internal/bytesutil @@ -138,7 +138,7 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/yannh/redis-dump-go v0.0.0-00010101000000-000000000000 => github.com/kubedb/redis-dump-go v0.8.1-0.20230429151509-2f2a7ce60763 +# github.com/yannh/redis-dump-go v0.0.0-00010101000000-000000000000 => github.com/kubedb/redis-dump-go v0.8.1-ac ## explicit; go 1.18 github.com/yannh/redis-dump-go/pkg/config github.com/yannh/redis-dump-go/pkg/redisdump @@ -709,4 +709,4 @@ stash.appscode.dev/apimachinery/pkg/conditions stash.appscode.dev/apimachinery/pkg/invoker stash.appscode.dev/apimachinery/pkg/restic stash.appscode.dev/apimachinery/pkg/util -# github.com/yannh/redis-dump-go => github.com/kubedb/redis-dump-go v0.8.1-0.20230429151509-2f2a7ce60763 +# github.com/yannh/redis-dump-go => github.com/kubedb/redis-dump-go v0.8.1-ac