From 989ca81844e794d1ba1bd68746081bf27c003dc5 Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Mon, 24 Mar 2025 14:22:45 +0600 Subject: [PATCH 01/12] Add Memcached Client Signed-off-by: Muhammad Raisul Islam Evan --- go.mod | 3 +- go.sum | 6 +- memcached/client.go | 9 + memcached/kubedb_client_builder.go | 112 +++ vendor/github.com/kubedb/gomemcache/AUTHORS | 9 + vendor/github.com/kubedb/gomemcache/LICENSE | 202 +++++ .../kubedb/gomemcache/memcache/memcache.go | 838 ++++++++++++++++++ .../kubedb/gomemcache/memcache/selector.go | 129 +++ vendor/modules.txt | 5 +- 9 files changed, 1309 insertions(+), 4 deletions(-) create mode 100644 memcached/client.go create mode 100644 memcached/kubedb_client_builder.go create mode 100644 vendor/github.com/kubedb/gomemcache/AUTHORS create mode 100644 vendor/github.com/kubedb/gomemcache/LICENSE create mode 100644 vendor/github.com/kubedb/gomemcache/memcache/memcache.go create mode 100644 vendor/github.com/kubedb/gomemcache/memcache/selector.go diff --git a/go.mod b/go.mod index 3ff2d8d41..d8cc24b87 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/go-sql-driver/mysql v1.9.0 github.com/gocql/gocql v1.6.0 github.com/grafadruid/go-druid v0.0.6 + github.com/kubedb/gomemcache v0.0.0-20240930104810-7694d68c3e21 github.com/lib/pq v1.10.7 github.com/michaelklishin/rabbit-hole/v3 v3.1.0 github.com/microsoft/go-mssqldb v1.6.0 @@ -34,7 +35,7 @@ require ( k8s.io/klog/v2 v2.130.1 kmodules.xyz/client-go v0.32.1 kmodules.xyz/custom-resources v0.32.0 - kubedb.dev/apimachinery v0.53.0 + kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 sigs.k8s.io/controller-runtime v0.20.3 xorm.io/xorm v1.3.6 ) diff --git a/go.sum b/go.sum index a4e291dd6..509e72c37 100644 --- a/go.sum +++ b/go.sum @@ -289,6 +289,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kubedb/gomemcache v0.0.0-20240930104810-7694d68c3e21 h1:5DhgzM4z088xe+G6+PS+LzjOFyjis+aVZelglkMlZp0= +github.com/kubedb/gomemcache v0.0.0-20240930104810-7694d68c3e21/go.mod h1:FSZMy/DY5gr7zUXkxt66DdA9dUtbjlB0aJb+sYwRaEg= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -659,8 +661,8 @@ kmodules.xyz/monitoring-agent-api v0.32.0 h1:cMQbWvbTc4JWeLI/zYE0HLefsdFYBzqvATL kmodules.xyz/monitoring-agent-api v0.32.0/go.mod h1:zgRKiJcuK7FOHy0Y1TsONRbJfgnPCs8t4Zh/6Afr+yU= kmodules.xyz/offshoot-api v0.32.0 h1:gogc5scSZe2JoXtZof72UGRl3Tit0kFaFRMkLLT1D8o= kmodules.xyz/offshoot-api v0.32.0/go.mod h1:tled7OxYZ3SkUJcrVFVVYyd+zXjsRSEm1R6Q3k4gcx0= -kubedb.dev/apimachinery v0.53.0 h1:uxgf/Kc27/A87/oZd+TjddAC2IS5C3ubZGiRBVqu0cc= -kubedb.dev/apimachinery v0.53.0/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= +kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 h1:8u0uUM9a3eHIsJ+zOMhmvC30zfNX50QauZXyKOGmfWE= +kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4/go.mod h1:JNMlsjf5aYe5OflUXiXpzj5P4Bp99QLyLBLIIIl5Zm0= kubeops.dev/petset v0.0.10 h1:sNaqmHrD9bW7pcrWnwPoiQrKvdRwRX0BaRQc5QA78Bg= kubeops.dev/petset v0.0.10/go.mod h1:uHL83kggwmtSxdlIfxNbY2isV22iYV6YjADv0y+Z7YA= kubeops.dev/sidekick v0.0.11 h1:OydXdIH6cYSiWxKIWvrywk95WhhHSERkc7RNPOmTekc= diff --git a/memcached/client.go b/memcached/client.go new file mode 100644 index 000000000..f77f65fe1 --- /dev/null +++ b/memcached/client.go @@ -0,0 +1,9 @@ +package memcached + +import ( + "github.com/kubedb/gomemcache/memcache" +) + +type Client struct { + *memcache.Client +} diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go new file mode 100644 index 000000000..0d1929b66 --- /dev/null +++ b/memcached/kubedb_client_builder.go @@ -0,0 +1,112 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package memcached + +import ( + "context" + "crypto/tls" + "crypto/x509" + "net" + "time" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + dbapi "kubedb.dev/apimachinery/apis/kubedb/v1" + + "github.com/kubedb/gomemcache/memcache" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type KubeDBClientBuilder struct { + kc client.Client + db *dbapi.Memcached + podName string + url string + database int +} + +func NewKubeDBClientBuilder(kc client.Client, db *dbapi.Memcached) *KubeDBClientBuilder { + return &KubeDBClientBuilder{ + kc: kc, + db: db, + } +} + +func (o *KubeDBClientBuilder) WithPod(podName string) *KubeDBClientBuilder { + o.podName = podName + return o +} + +func (o *KubeDBClientBuilder) WithURL(url string) *KubeDBClientBuilder { + o.url = url + return o +} + +func (o *KubeDBClientBuilder) WithDatabase(database int) *KubeDBClientBuilder { + o.database = database + return o +} + +func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, error) { + mcClient := memcache.New(o.db.Address()) + if o.db.Spec.TLS != nil { + // Secret for Memcached Client Certs + secret := &core.Secret{} + + err := o.kc.Get(ctx, types.NamespacedName{ + Namespace: o.db.Namespace, + Name: o.db.GetMemcachedAuthSecretName(), + }, secret) + if err != nil { + klog.Error(err, "Failed to get auth-secret") + return nil, errors.New("secret is not found") + } + + caCert := secret.Data["ca.crt"] + clientCert := secret.Data["tls.crt"] + clientKey := secret.Data["tls.key"] + + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(caCert); !ok { + klog.Infoln("Failed to append CA certificate to the pool") + } + + // Load client certificate + clientCertificate, err := tls.X509KeyPair(clientCert, clientKey) + if err != nil { + klog.Errorf("Failed to load client certificate: %v", err) + } + // Create TLS configuration + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{clientCertificate}, + RootCAs: caCertPool, + InsecureSkipVerify: false, // Ensure server's cert is verified + } + // Override the dialer to use TLS by setting the DialContext function + mcClient.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + return tls.DialWithDialer(&net.Dialer{ + Timeout: 10 * time.Second, + }, "tcp", o.db.Address(), tlsConfig) + } + } + + return &Client{ + mcClient, + }, nil +} diff --git a/vendor/github.com/kubedb/gomemcache/AUTHORS b/vendor/github.com/kubedb/gomemcache/AUTHORS new file mode 100644 index 000000000..86ca62074 --- /dev/null +++ b/vendor/github.com/kubedb/gomemcache/AUTHORS @@ -0,0 +1,9 @@ +The following people & companies are the copyright holders of this +package. Feel free to add to this list if you or your employer cares, +otherwise it's implicit from the git log. + +Authors: + +- Brad Fitzpatrick +- Google, Inc. (from Googlers contributing) +- Anybody else in the git log. diff --git a/vendor/github.com/kubedb/gomemcache/LICENSE b/vendor/github.com/kubedb/gomemcache/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/kubedb/gomemcache/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/kubedb/gomemcache/memcache/memcache.go b/vendor/github.com/kubedb/gomemcache/memcache/memcache.go new file mode 100644 index 000000000..a7a2249cd --- /dev/null +++ b/vendor/github.com/kubedb/gomemcache/memcache/memcache.go @@ -0,0 +1,838 @@ +/* +Copyright 2011 The gomemcache AUTHORS + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package memcache provides a client for the memcached cache server. +package memcache + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io" + "net" + "strconv" + "strings" + "sync" + "time" +) + +// Similar to: +// https://godoc.org/google.golang.org/appengine/memcache + +var ( + // ErrCacheMiss means that a Get failed because the item wasn't present. + ErrCacheMiss = errors.New("memcache: cache miss") + + // ErrCASConflict means that a CompareAndSwap call failed due to the + // cached value being modified between the Get and the CompareAndSwap. + // If the cached value was simply evicted rather than replaced, + // ErrNotStored will be returned instead. + ErrCASConflict = errors.New("memcache: compare-and-swap conflict") + + // ErrNotStored means that a conditional write operation (i.e. Add or + // CompareAndSwap) failed because the condition was not satisfied. + ErrNotStored = errors.New("memcache: item not stored") + + // ErrServer means that a server error occurred. + ErrServerError = errors.New("memcache: server error") + + // ErrNoStats means that no statistics were available. + ErrNoStats = errors.New("memcache: no statistics available") + + // ErrMalformedKey is returned when an invalid key is used. + // Keys must be at maximum 250 bytes long and not + // contain whitespace or control characters. + ErrMalformedKey = errors.New("malformed: key is too long or contains invalid characters") + + // ErrNoServers is returned when no servers are configured or available. + ErrNoServers = errors.New("memcache: no servers configured or available") + + // ErrNotAuthenticated is returned when Client Authentication failed due to any reason. + ErrNotAuthenticated = errors.New("memcache: Client Authentication Failed") +) + +const ( + // DefaultTimeout is the default socket read/write timeout. + DefaultTimeout = 500 * time.Millisecond + + // DefaultMaxIdleConns is the default maximum number of idle connections + // kept for any single address. + DefaultMaxIdleConns = 2 +) + +const buffered = 8 // arbitrary buffered channel size, for readability + +// resumableError returns true if err is only a protocol-level cache error. +// This is used to determine whether or not a server connection should +// be re-used or not. If an error occurs, by default we don't reuse the +// connection, unless it was just a cache error. +func resumableError(err error) bool { + switch err { + case ErrCacheMiss, ErrCASConflict, ErrNotStored, ErrMalformedKey: + return true + } + return false +} + +func legalKey(key string) bool { + if len(key) > 250 { + return false + } + for i := 0; i < len(key); i++ { + if key[i] <= ' ' || key[i] == 0x7f { + return false + } + } + return true +} + +var ( + crlf = []byte("\r\n") + space = []byte(" ") + resultOK = []byte("OK\r\n") + resultStored = []byte("STORED\r\n") + resultNotStored = []byte("NOT_STORED\r\n") + resultExists = []byte("EXISTS\r\n") + resultNotFound = []byte("NOT_FOUND\r\n") + resultDeleted = []byte("DELETED\r\n") + resultEnd = []byte("END\r\n") + resultOk = []byte("OK\r\n") + resultTouched = []byte("TOUCHED\r\n") + resultClientErrorPrefix = []byte("CLIENT_ERROR ") + versionPrefix = []byte("VERSION") + + // Authentication Related Error + resultUnauthenticatedError = []byte("CLIENT_ERROR unauthenticated\r\n") + resultAuthenticationFailure = []byte("CLIENT_ERROR authentication failure\r\n") + resultBadCommandFormat = []byte("CLIENT_ERROR bad command line format\r\n") + resultBadCommandFormatTermination = []byte("CLIENT_ERROR bad command line format termination\r\n") + resultBadAuthenticationTokenFormat = []byte("CLIENT_ERROR bad authentication token format\r\n") +) + +// New returns a memcache client using the provided server(s) +// with equal weight. If a server is listed multiple times, +// it gets a proportional amount of weight. +func New(server ...string) *Client { + ss := new(ServerList) + ss.SetServers(server...) + return NewFromSelector(ss) +} + +// NewFromSelector returns a new Client using the provided ServerSelector. +func NewFromSelector(ss ServerSelector) *Client { + return &Client{selector: ss} +} + +// Client is a memcache client. +// It is safe for unlocked use by multiple concurrent goroutines. +type Client struct { + // DialContext connects to the address on the named network using the + // provided context. + // + // To connect to servers using TLS (memcached running with "--enable-ssl"), + // use a DialContext func that uses tls.Dialer.DialContext. See this + // package's tests as an example. + DialContext func(ctx context.Context, network, address string) (net.Conn, error) + + // Timeout specifies the socket read/write timeout. + // If zero, DefaultTimeout is used. + Timeout time.Duration + + // MaxIdleConns specifies the maximum number of idle connections that will + // be maintained per address. If less than one, DefaultMaxIdleConns will be + // used. + // + // Consider your expected traffic rates and latency carefully. This should + // be set to a number higher than your peak parallel requests. + MaxIdleConns int + + selector ServerSelector + + lk sync.Mutex + freeconn map[string][]*conn +} + +// Item is an item to be got or stored in a memcached server. +type Item struct { + // Key is the Item's key (250 bytes maximum). + Key string + + // Value is the Item's value. + Value []byte + + // Flags are server-opaque flags whose semantics are entirely + // up to the app. + Flags uint32 + + // Expiration is the cache expiration time, in seconds: either a relative + // time from now (up to 1 month), or an absolute Unix epoch time. + // Zero means the Item has no expiration time. + Expiration int32 + + // CasID is the compare and swap ID. + // + // It's populated by get requests and then the same value is + // required for a CompareAndSwap request to succeed. + CasID uint64 + + // Username for Authentication + User string + + // Password for Authentication + Pass string +} + +// conn is a connection to a server. +type conn struct { + nc net.Conn + rw *bufio.ReadWriter + addr net.Addr + c *Client +} + +// release returns this connection back to the client's free pool +func (cn *conn) release() { + cn.c.putFreeConn(cn.addr, cn) +} + +func (cn *conn) extendDeadline() { + cn.nc.SetDeadline(time.Now().Add(cn.c.netTimeout())) +} + +// condRelease releases this connection if the error pointed to by err +// is nil (not an error) or is only a protocol level error (e.g. a +// cache miss). The purpose is to not recycle TCP connections that +// are bad. +func (cn *conn) condRelease(err *error) { + if *err == nil || resumableError(*err) { + cn.release() + } else { + cn.nc.Close() + } +} + +func (c *Client) putFreeConn(addr net.Addr, cn *conn) { + c.lk.Lock() + defer c.lk.Unlock() + if c.freeconn == nil { + c.freeconn = make(map[string][]*conn) + } + freelist := c.freeconn[addr.String()] + if len(freelist) >= c.maxIdleConns() { + cn.nc.Close() + return + } + c.freeconn[addr.String()] = append(freelist, cn) +} + +func (c *Client) getFreeConn(addr net.Addr) (cn *conn, ok bool) { + c.lk.Lock() + defer c.lk.Unlock() + if c.freeconn == nil { + return nil, false + } + freelist, ok := c.freeconn[addr.String()] + if !ok || len(freelist) == 0 { + return nil, false + } + cn = freelist[len(freelist)-1] + c.freeconn[addr.String()] = freelist[:len(freelist)-1] + return cn, true +} + +func (c *Client) netTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } + return DefaultTimeout +} + +func (c *Client) maxIdleConns() int { + if c.MaxIdleConns > 0 { + return c.MaxIdleConns + } + return DefaultMaxIdleConns +} + +// ConnectTimeoutError is the error type used when it takes +// too long to connect to the desired host. This level of +// detail can generally be ignored. +type ConnectTimeoutError struct { + Addr net.Addr +} + +func (cte *ConnectTimeoutError) Error() string { + return "memcache: connect timeout to " + cte.Addr.String() +} + +func (c *Client) dial(addr net.Addr) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), c.netTimeout()) + defer cancel() + + dialerContext := c.DialContext + if dialerContext == nil { + dialer := net.Dialer{ + Timeout: c.netTimeout(), + } + dialerContext = dialer.DialContext + } + + nc, err := dialerContext(ctx, addr.Network(), addr.String()) + if err == nil { + return nc, nil + } + + if ne, ok := err.(net.Error); ok && ne.Timeout() { + return nil, &ConnectTimeoutError{addr} + } + + return nil, err +} + +func (c *Client) getConn(addr net.Addr) (*conn, error) { + cn, ok := c.getFreeConn(addr) + if ok { + cn.extendDeadline() + return cn, nil + } + nc, err := c.dial(addr) + if err != nil { + return nil, err + } + cn = &conn{ + nc: nc, + addr: addr, + rw: bufio.NewReadWriter(bufio.NewReader(nc), bufio.NewWriter(nc)), + c: c, + } + cn.extendDeadline() + return cn, nil +} + +func (c *Client) onItem(item *Item, fn func(*Client, *bufio.ReadWriter, *Item) error) error { + addr, err := c.selector.PickServer(item.Key) + if err != nil { + return err + } + cn, err := c.getConn(addr) + if err != nil { + return err + } + defer cn.condRelease(&err) + if err := fn(c, cn.rw, item); err != nil { + return err + } + return nil +} + +func (c *Client) FlushAll() error { + return c.selector.Each(c.flushAllFromAddr) +} + +// Get gets the item for the given key. ErrCacheMiss is returned for a +// memcache cache miss. The key must be at most 250 bytes in length. +func (c *Client) Get(key string) (item *Item, err error) { + err = c.withKeyAddr(key, func(addr net.Addr) error { + return c.getFromAddr(addr, []string{key}, func(it *Item) { item = it }) + }) + if err == nil && item == nil { + err = ErrCacheMiss + } + return +} + +// Touch updates the expiry for the given key. The seconds parameter is either +// a Unix timestamp or, if seconds is less than 1 month, the number of seconds +// into the future at which time the item will expire. Zero means the item has +// no expiration time. ErrCacheMiss is returned if the key is not in the cache. +// The key must be at most 250 bytes in length. +func (c *Client) Touch(key string, seconds int32) (err error) { + return c.withKeyAddr(key, func(addr net.Addr) error { + return c.touchFromAddr(addr, []string{key}, seconds) + }) +} + +func (c *Client) withKeyAddr(key string, fn func(net.Addr) error) (err error) { + if !legalKey(key) { + return ErrMalformedKey + } + addr, err := c.selector.PickServer(key) + if err != nil { + return err + } + return fn(addr) +} + +func (c *Client) withAddrRw(addr net.Addr, fn func(*bufio.ReadWriter) error) (err error) { + cn, err := c.getConn(addr) + if err != nil { + return err + } + defer cn.condRelease(&err) + return fn(cn.rw) +} + +func (c *Client) withKeyRw(key string, fn func(*bufio.ReadWriter) error) error { + return c.withKeyAddr(key, func(addr net.Addr) error { + return c.withAddrRw(addr, fn) + }) +} + +func (c *Client) getFromAddr(addr net.Addr, keys []string, cb func(*Item)) error { + return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error { + if _, err := fmt.Fprintf(rw, "gets %s\r\n", strings.Join(keys, " ")); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + if err := parseGetResponse(rw.Reader, cb); err != nil { + return err + } + return nil + }) +} + +// flushAllFromAddr send the flush_all command to the given addr +func (c *Client) flushAllFromAddr(addr net.Addr) error { + return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error { + if _, err := fmt.Fprintf(rw, "flush_all\r\n"); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + if err != nil { + return err + } + switch { + case bytes.Equal(line, resultOk): + break + default: + return fmt.Errorf("memcache: unexpected response line from flush_all: %q", string(line)) + } + return nil + }) +} + +// ping sends the version command to the given addr +func (c *Client) ping(addr net.Addr) error { + return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error { + if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + if err != nil { + return err + } + + switch { + case bytes.HasPrefix(line, versionPrefix): + break + default: + return fmt.Errorf("memcache: unexpected response line from ping: %q", string(line)) + } + return nil + }) +} + +func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error { + return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error { + for _, key := range keys { + if _, err := fmt.Fprintf(rw, "touch %s %d\r\n", key, expiration); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + if err != nil { + return err + } + switch { + case bytes.Equal(line, resultTouched): + break + case bytes.Equal(line, resultNotFound): + return ErrCacheMiss + default: + return fmt.Errorf("memcache: unexpected response line from touch: %q", string(line)) + } + } + return nil + }) +} + +// GetMulti is a batch version of Get. The returned map from keys to +// items may have fewer elements than the input slice, due to memcache +// cache misses. Each key must be at most 250 bytes in length. +// If no error is returned, the returned map will also be non-nil. +func (c *Client) GetMulti(keys []string) (map[string]*Item, error) { + var lk sync.Mutex + m := make(map[string]*Item) + addItemToMap := func(it *Item) { + lk.Lock() + defer lk.Unlock() + m[it.Key] = it + } + + keyMap := make(map[net.Addr][]string) + for _, key := range keys { + if !legalKey(key) { + return nil, ErrMalformedKey + } + addr, err := c.selector.PickServer(key) + if err != nil { + return nil, err + } + keyMap[addr] = append(keyMap[addr], key) + } + + ch := make(chan error, buffered) + for addr, keys := range keyMap { + go func(addr net.Addr, keys []string) { + ch <- c.getFromAddr(addr, keys, addItemToMap) + }(addr, keys) + } + + var err error + for _ = range keyMap { + if ge := <-ch; ge != nil { + err = ge + } + } + return m, err +} + +// parseGetResponse reads a GET response from r and calls cb for each +// read and allocated Item +func parseGetResponse(r *bufio.Reader, cb func(*Item)) error { + for { + line, err := r.ReadSlice('\n') + if err != nil { + return err + } + if bytes.Equal(line, resultEnd) { + return nil + } + it := new(Item) + size, err := scanGetResponseLine(line, it) + if err != nil { + return err + } + it.Value = make([]byte, size+2) + _, err = io.ReadFull(r, it.Value) + if err != nil { + it.Value = nil + return err + } + if !bytes.HasSuffix(it.Value, crlf) { + it.Value = nil + return fmt.Errorf("memcache: corrupt get result read") + } + it.Value = it.Value[:size] + cb(it) + } +} + +// scanGetResponseLine populates it and returns the declared size of the item. +// It does not read the bytes of the item. +func scanGetResponseLine(line []byte, it *Item) (size int, err error) { + pattern := "VALUE %s %d %d %d\r\n" + dest := []interface{}{&it.Key, &it.Flags, &size, &it.CasID} + if bytes.Count(line, space) == 3 { + pattern = "VALUE %s %d %d\r\n" + dest = dest[:3] + } + n, err := fmt.Sscanf(string(line), pattern, dest...) + if err != nil || n != len(dest) { + return -1, fmt.Errorf("memcache: unexpected line in get response: %q", line) + } + return size, nil +} + +// Set writes the given item, unconditionally. +func (c *Client) Set(item *Item) error { + return c.onItem(item, (*Client).set) +} + +func (c *Client) set(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "set", item) +} + +// Add writes the given item, if no value already exists for its +// key. ErrNotStored is returned if that condition is not met. +func (c *Client) Add(item *Item) error { + return c.onItem(item, (*Client).add) +} + +func (c *Client) add(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "add", item) +} + +// Replace writes the given item, but only if the server *does* +// already hold data for this key +func (c *Client) Replace(item *Item) error { + return c.onItem(item, (*Client).replace) +} + +func (c *Client) replace(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "replace", item) +} + +// Append appends the given item to the existing item, if a value already +// exists for its key. ErrNotStored is returned if that condition is not met. +func (c *Client) Append(item *Item) error { + return c.onItem(item, (*Client).append) +} + +func (c *Client) append(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "append", item) +} + +// Prepend prepends the given item to the existing item, if a value already +// exists for its key. ErrNotStored is returned if that condition is not met. +func (c *Client) Prepend(item *Item) error { + return c.onItem(item, (*Client).prepend) +} + +func (c *Client) prepend(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "prepend", item) +} + +// CompareAndSwap writes the given item that was previously returned +// by Get, if the value was neither modified or evicted between the +// Get and the CompareAndSwap calls. The item's Key should not change +// between calls but all other item fields may differ. ErrCASConflict +// is returned if the value was modified in between the +// calls. ErrNotStored is returned if the value was evicted in between +// the calls. +func (c *Client) CompareAndSwap(item *Item) error { + return c.onItem(item, (*Client).cas) +} + +func (c *Client) cas(rw *bufio.ReadWriter, item *Item) error { + return c.populateOne(rw, "cas", item) +} + +func (c *Client) populateOne(rw *bufio.ReadWriter, verb string, item *Item) error { + if !legalKey(item.Key) { + return ErrMalformedKey + } + var err error + if verb == "cas" { + _, err = fmt.Fprintf(rw, "%s %s %d %d %d %d\r\n", + verb, item.Key, item.Flags, item.Expiration, len(item.Value), item.CasID) + } else { + + _, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n", + verb, item.Key, item.Flags, item.Expiration, len(item.Value)) + } + if err != nil { + return err + } + if _, err = rw.Write(item.Value); err != nil { + return err + } + if _, err := rw.Write(crlf); err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + if err != nil { + return err + } + + switch { + case bytes.Equal(line, resultStored): + return nil + case bytes.Equal(line, resultNotStored): + return ErrNotStored + case bytes.Equal(line, resultExists): + return ErrCASConflict + case bytes.Equal(line, resultNotFound): + return ErrCacheMiss + } + return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line)) +} + +func writeReadLine(rw *bufio.ReadWriter, format string, args ...interface{}) ([]byte, error) { + _, err := fmt.Fprintf(rw, format, args...) + if err != nil { + return nil, err + } + if err := rw.Flush(); err != nil { + return nil, err + } + line, err := rw.ReadSlice('\n') + return line, err +} + +func writeExpectf(rw *bufio.ReadWriter, expect []byte, format string, args ...interface{}) error { + line, err := writeReadLine(rw, format, args...) + if err != nil { + return err + } + switch { + case bytes.Equal(line, resultOK): + return nil + case bytes.Equal(line, expect): + return nil + case bytes.Equal(line, resultNotStored): + return ErrNotStored + case bytes.Equal(line, resultExists): + return ErrCASConflict + case bytes.Equal(line, resultNotFound): + return ErrCacheMiss + } + return fmt.Errorf("memcache: unexpected response line: %q", string(line)) +} + +// Delete deletes the item with the provided key. The error ErrCacheMiss is +// returned if the item didn't already exist in the cache. +func (c *Client) Delete(key string) error { + return c.withKeyRw(key, func(rw *bufio.ReadWriter) error { + return writeExpectf(rw, resultDeleted, "delete %s\r\n", key) + }) +} + +// DeleteAll deletes all items in the cache. +func (c *Client) DeleteAll() error { + return c.withKeyRw("", func(rw *bufio.ReadWriter) error { + return writeExpectf(rw, resultDeleted, "flush_all\r\n") + }) +} + +// Ping checks all instances if they are alive. Returns error if any +// of them is down. +func (c *Client) Ping() error { + return c.selector.Each(c.ping) +} + +// Increment atomically increments key by delta. The return value is +// the new value after being incremented or an error. If the value +// didn't exist in memcached the error is ErrCacheMiss. The value in +// memcached must be an decimal number, or an error will be returned. +// On 64-bit overflow, the new value wraps around. +func (c *Client) Increment(key string, delta uint64) (newValue uint64, err error) { + return c.incrDecr("incr", key, delta) +} + +// Decrement atomically decrements key by delta. The return value is +// the new value after being decremented or an error. If the value +// didn't exist in memcached the error is ErrCacheMiss. The value in +// memcached must be an decimal number, or an error will be returned. +// On underflow, the new value is capped at zero and does not wrap +// around. +func (c *Client) Decrement(key string, delta uint64) (newValue uint64, err error) { + return c.incrDecr("decr", key, delta) +} + +func (c *Client) incrDecr(verb, key string, delta uint64) (uint64, error) { + var val uint64 + err := c.withKeyRw(key, func(rw *bufio.ReadWriter) error { + line, err := writeReadLine(rw, "%s %s %d\r\n", verb, key, delta) + if err != nil { + return err + } + switch { + case bytes.Equal(line, resultNotFound): + return ErrCacheMiss + case bytes.HasPrefix(line, resultClientErrorPrefix): + errMsg := line[len(resultClientErrorPrefix) : len(line)-2] + return errors.New("memcache: client error: " + string(errMsg)) + } + val, err = strconv.ParseUint(string(line[:len(line)-2]), 10, 64) + if err != nil { + return err + } + return nil + }) + return val, err +} + +// Close closes any open connections. +// +// It returns the first error encountered closing connections, but always +// closes all connections. +// +// After Close, the Client may still be used. +func (c *Client) Close() error { + c.lk.Lock() + defer c.lk.Unlock() + var ret error + for _, conns := range c.freeconn { + for _, c := range conns { + if err := c.nc.Close(); err != nil && ret == nil { + ret = err + } + } + } + c.freeconn = nil + return ret +} + +// Memcached Authentication + +func (c *Client) SetAuth(item *Item) error { + return c.onItem(item, (*Client).setAuth) +} + +func (c *Client) setAuth(rw *bufio.ReadWriter, item *Item) error { + return c.authFunc(rw, "set", item) +} + +func (c *Client) authFunc(rw *bufio.ReadWriter, verb string, item *Item) error { + if !legalKey(item.Key) { + return ErrMalformedKey + } + var err error + _, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n%s %s\r\n", + verb, item.Key, item.Flags, item.Expiration, len(item.User)+len(item.Pass)+1, item.User, item.Pass) + if err != nil { + return err + } + if err := rw.Flush(); err != nil { + return err + } + line, err := rw.ReadSlice('\n') + + if err != nil { + return err + } + switch { + case bytes.Equal(line, resultStored): + return nil + case bytes.Equal(line, resultUnauthenticatedError): + return ErrNotAuthenticated + case bytes.Equal(line, resultAuthenticationFailure): + return ErrNotAuthenticated + case bytes.Equal(line, resultBadCommandFormat): + return ErrNotAuthenticated + case bytes.Equal(line, resultBadCommandFormatTermination): + return ErrNotAuthenticated + case bytes.Equal(line, resultBadAuthenticationTokenFormat): + return ErrNotAuthenticated + } + return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line)) +} diff --git a/vendor/github.com/kubedb/gomemcache/memcache/selector.go b/vendor/github.com/kubedb/gomemcache/memcache/selector.go new file mode 100644 index 000000000..964dbdb6a --- /dev/null +++ b/vendor/github.com/kubedb/gomemcache/memcache/selector.go @@ -0,0 +1,129 @@ +/* +Copyright 2011 The gomemcache AUTHORS + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package memcache + +import ( + "hash/crc32" + "net" + "strings" + "sync" +) + +// ServerSelector is the interface that selects a memcache server +// as a function of the item's key. +// +// All ServerSelector implementations must be safe for concurrent use +// by multiple goroutines. +type ServerSelector interface { + // PickServer returns the server address that a given item + // should be shared onto. + PickServer(key string) (net.Addr, error) + Each(func(net.Addr) error) error +} + +// ServerList is a simple ServerSelector. Its zero value is usable. +type ServerList struct { + mu sync.RWMutex + addrs []net.Addr +} + +// staticAddr caches the Network() and String() values from any net.Addr. +type staticAddr struct { + ntw, str string +} + +func newStaticAddr(a net.Addr) net.Addr { + return &staticAddr{ + ntw: a.Network(), + str: a.String(), + } +} + +func (s *staticAddr) Network() string { return s.ntw } +func (s *staticAddr) String() string { return s.str } + +// SetServers changes a ServerList's set of servers at runtime and is +// safe for concurrent use by multiple goroutines. +// +// Each server is given equal weight. A server is given more weight +// if it's listed multiple times. +// +// SetServers returns an error if any of the server names fail to +// resolve. No attempt is made to connect to the server. If any error +// is returned, no changes are made to the ServerList. +func (ss *ServerList) SetServers(servers ...string) error { + naddr := make([]net.Addr, len(servers)) + for i, server := range servers { + if strings.Contains(server, "/") { + addr, err := net.ResolveUnixAddr("unix", server) + if err != nil { + return err + } + naddr[i] = newStaticAddr(addr) + } else { + tcpaddr, err := net.ResolveTCPAddr("tcp", server) + if err != nil { + return err + } + naddr[i] = newStaticAddr(tcpaddr) + } + } + + ss.mu.Lock() + defer ss.mu.Unlock() + ss.addrs = naddr + return nil +} + +// Each iterates over each server calling the given function +func (ss *ServerList) Each(f func(net.Addr) error) error { + ss.mu.RLock() + defer ss.mu.RUnlock() + for _, a := range ss.addrs { + if err := f(a); nil != err { + return err + } + } + return nil +} + +// keyBufPool returns []byte buffers for use by PickServer's call to +// crc32.ChecksumIEEE to avoid allocations. (but doesn't avoid the +// copies, which at least are bounded in size and small) +var keyBufPool = sync.Pool{ + New: func() interface{} { + b := make([]byte, 256) + return &b + }, +} + +func (ss *ServerList) PickServer(key string) (net.Addr, error) { + ss.mu.RLock() + defer ss.mu.RUnlock() + if len(ss.addrs) == 0 { + return nil, ErrNoServers + } + if len(ss.addrs) == 1 { + return ss.addrs[0], nil + } + bufp := keyBufPool.Get().(*[]byte) + n := copy(*bufp, key) + cs := crc32.ChecksumIEEE((*bufp)[:n]) + keyBufPool.Put(bufp) + + return ss.addrs[cs%uint32(len(ss.addrs))], nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index aae8acec1..48519c631 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -888,6 +888,9 @@ github.com/klauspost/compress/zstd/internal/xxhash # github.com/klauspost/cpuid/v2 v2.0.9 ## explicit; go 1.13 github.com/klauspost/cpuid/v2 +# github.com/kubedb/gomemcache v0.0.0-20240930104810-7694d68c3e21 +## explicit; go 1.22.1 +github.com/kubedb/gomemcache/memcache # github.com/lib/pq v1.10.7 ## explicit; go 1.13 github.com/lib/pq @@ -1688,7 +1691,7 @@ kmodules.xyz/offshoot-api/api/v1 kmodules.xyz/offshoot-api/api/v1/conversion kmodules.xyz/offshoot-api/api/v2 kmodules.xyz/offshoot-api/util -# kubedb.dev/apimachinery v0.53.0 +# kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 ## explicit; go 1.23.0 kubedb.dev/apimachinery/apis kubedb.dev/apimachinery/apis/catalog From c52de54efd0238da13736e133977a86151f42eda Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Mon, 24 Mar 2025 14:41:00 +0600 Subject: [PATCH 02/12] Add Memcached Authentication Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 52 ++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 0d1929b66..db07f5eac 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -21,11 +21,13 @@ import ( "crypto/tls" "crypto/x509" "net" + "strings" "time" "github.com/pkg/errors" core "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "kubedb.dev/apimachinery/apis/kubedb" dbapi "kubedb.dev/apimachinery/apis/kubedb/v1" "github.com/kubedb/gomemcache/memcache" @@ -67,12 +69,7 @@ func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, mcClient := memcache.New(o.db.Address()) if o.db.Spec.TLS != nil { // Secret for Memcached Client Certs - secret := &core.Secret{} - - err := o.kc.Get(ctx, types.NamespacedName{ - Namespace: o.db.Namespace, - Name: o.db.GetMemcachedAuthSecretName(), - }, secret) + secret, err := o.GetSecret(ctx) if err != nil { klog.Error(err, "Failed to get auth-secret") return nil, errors.New("secret is not found") @@ -110,3 +107,46 @@ func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, mcClient, }, nil } + +func (o *KubeDBClientBuilder) SetAuth(ctx context.Context) error { + secret, err := o.GetSecret(ctx) + if err != nil { + return err + } + + authData := string(secret.Data[kubedb.AuthDataKey]) + separatePairs := strings.Split(authData, "\n") + usernamePasswordPairs := separatePairs[0] + + splitUsernamePassword := strings.Split(usernamePasswordPairs, ":") + memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) + + mcClient, err := o.GetMemcachedClient(ctx) + if err != nil { + klog.Error(err, "Failed to create Memcached client") + return err + } + err = mcClient.SetAuth(&memcache.Item{ + Key: kubedb.MemcachedHealthKey, Flags: 0, Expiration: 0, User: memcachedUserName, Pass: memcachedPassword, + }) + if err != nil { + klog.Errorf("Authentication Error: %v", err.Error()) + } else { + klog.V(5).Infof("Authentication Done Successfully !!...") + } + return nil +} + +func (o *KubeDBClientBuilder) GetSecret(ctx context.Context) (*core.Secret, error) { + secret := &core.Secret{} + + err := o.kc.Get(ctx, types.NamespacedName{ + Namespace: o.db.Namespace, + Name: o.db.GetMemcachedAuthSecretName(), + }, secret) + if err != nil { + klog.Error(err, "Failed to get auth-secret") + return nil, errors.New("secret is not found") + } + return secret, nil +} From 509ff8bdc5656bb43fb1ace2cc533fa16781d725 Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Mon, 24 Mar 2025 15:02:07 +0600 Subject: [PATCH 03/12] Fix AuthSecret Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index db07f5eac..0b24b7672 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -108,7 +108,7 @@ func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, }, nil } -func (o *KubeDBClientBuilder) SetAuth(ctx context.Context) error { +func (o *KubeDBClientBuilder) SetAuth(ctx context.Context, mcClient *Client) error { secret, err := o.GetSecret(ctx) if err != nil { return err @@ -121,11 +121,6 @@ func (o *KubeDBClientBuilder) SetAuth(ctx context.Context) error { splitUsernamePassword := strings.Split(usernamePasswordPairs, ":") memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) - mcClient, err := o.GetMemcachedClient(ctx) - if err != nil { - klog.Error(err, "Failed to create Memcached client") - return err - } err = mcClient.SetAuth(&memcache.Item{ Key: kubedb.MemcachedHealthKey, Flags: 0, Expiration: 0, User: memcachedUserName, Pass: memcachedPassword, }) From 5e2bffb531bd04891f41daaabc0b17e5c85e1d0c Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Mon, 24 Mar 2025 15:20:30 +0600 Subject: [PATCH 04/12] Fix Memcached AuthSecret Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 0b24b7672..9d33cffcf 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -26,7 +26,8 @@ import ( "github.com/pkg/errors" core "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "kubedb.dev/apimachinery/apis/kubedb" dbapi "kubedb.dev/apimachinery/apis/kubedb/v1" @@ -37,6 +38,7 @@ import ( type KubeDBClientBuilder struct { kc client.Client + Client kubernetes.Interface db *dbapi.Memcached podName string url string @@ -65,11 +67,11 @@ func (o *KubeDBClientBuilder) WithDatabase(database int) *KubeDBClientBuilder { return o } -func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, error) { +func (o *KubeDBClientBuilder) GetMemcachedClient() (*Client, error) { mcClient := memcache.New(o.db.Address()) if o.db.Spec.TLS != nil { // Secret for Memcached Client Certs - secret, err := o.GetSecret(ctx) + secret, err := o.GetSecret() if err != nil { klog.Error(err, "Failed to get auth-secret") return nil, errors.New("secret is not found") @@ -108,8 +110,8 @@ func (o *KubeDBClientBuilder) GetMemcachedClient(ctx context.Context) (*Client, }, nil } -func (o *KubeDBClientBuilder) SetAuth(ctx context.Context, mcClient *Client) error { - secret, err := o.GetSecret(ctx) +func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { + secret, err := o.GetSecret() if err != nil { return err } @@ -120,6 +122,7 @@ func (o *KubeDBClientBuilder) SetAuth(ctx context.Context, mcClient *Client) err splitUsernamePassword := strings.Split(usernamePasswordPairs, ":") memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) + klog.Infof("memcached username and password: %s %s", memcachedUserName, memcachedPassword) err = mcClient.SetAuth(&memcache.Item{ Key: kubedb.MemcachedHealthKey, Flags: 0, Expiration: 0, User: memcachedUserName, Pass: memcachedPassword, @@ -132,16 +135,11 @@ func (o *KubeDBClientBuilder) SetAuth(ctx context.Context, mcClient *Client) err return nil } -func (o *KubeDBClientBuilder) GetSecret(ctx context.Context) (*core.Secret, error) { - secret := &core.Secret{} - - err := o.kc.Get(ctx, types.NamespacedName{ - Namespace: o.db.Namespace, - Name: o.db.GetMemcachedAuthSecretName(), - }, secret) +func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { + secretName := o.db.GetMemcachedAuthSecretName() + secret, err := o.Client.CoreV1().Secrets(o.db.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { - klog.Error(err, "Failed to get auth-secret") - return nil, errors.New("secret is not found") + return nil, err } return secret, nil } From 6717a99fca8302c3a92fdc439bb3686adf389ab5 Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 11:22:34 +0600 Subject: [PATCH 05/12] Add client Signed-off-by: Muhammad Raisul Islam Evan --- go.mod | 2 +- go.sum | 4 ++-- memcached/kubedb_client_builder.go | 11 +++++++---- vendor/modules.txt | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index d8cc24b87..2e4c76163 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( k8s.io/klog/v2 v2.130.1 kmodules.xyz/client-go v0.32.1 kmodules.xyz/custom-resources v0.32.0 - kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 + kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d sigs.k8s.io/controller-runtime v0.20.3 xorm.io/xorm v1.3.6 ) diff --git a/go.sum b/go.sum index 509e72c37..ef8e6f8cb 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ kmodules.xyz/monitoring-agent-api v0.32.0 h1:cMQbWvbTc4JWeLI/zYE0HLefsdFYBzqvATL kmodules.xyz/monitoring-agent-api v0.32.0/go.mod h1:zgRKiJcuK7FOHy0Y1TsONRbJfgnPCs8t4Zh/6Afr+yU= kmodules.xyz/offshoot-api v0.32.0 h1:gogc5scSZe2JoXtZof72UGRl3Tit0kFaFRMkLLT1D8o= kmodules.xyz/offshoot-api v0.32.0/go.mod h1:tled7OxYZ3SkUJcrVFVVYyd+zXjsRSEm1R6Q3k4gcx0= -kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 h1:8u0uUM9a3eHIsJ+zOMhmvC30zfNX50QauZXyKOGmfWE= -kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4/go.mod h1:JNMlsjf5aYe5OflUXiXpzj5P4Bp99QLyLBLIIIl5Zm0= +kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d h1:9vR1Z75WV1s5V2Ge9v4EfabuHDS56BP/oLO1XCcEZrg= +kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= kubeops.dev/petset v0.0.10 h1:sNaqmHrD9bW7pcrWnwPoiQrKvdRwRX0BaRQc5QA78Bg= kubeops.dev/petset v0.0.10/go.mod h1:uHL83kggwmtSxdlIfxNbY2isV22iYV6YjADv0y+Z7YA= kubeops.dev/sidekick v0.0.11 h1:OydXdIH6cYSiWxKIWvrywk95WhhHSERkc7RNPOmTekc= diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 9d33cffcf..95cbf7168 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -45,10 +45,11 @@ type KubeDBClientBuilder struct { database int } -func NewKubeDBClientBuilder(kc client.Client, db *dbapi.Memcached) *KubeDBClientBuilder { +func NewKubeDBClientBuilder(kc client.Client, client kubernetes.Interface, db *dbapi.Memcached) *KubeDBClientBuilder { return &KubeDBClientBuilder{ - kc: kc, - db: db, + kc: kc, + Client: client, + db: db, } } @@ -113,7 +114,8 @@ func (o *KubeDBClientBuilder) GetMemcachedClient() (*Client, error) { func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { secret, err := o.GetSecret() if err != nil { - return err + klog.Error(err, "Failed to get auth-secret") + return errors.New("secret is not found") } authData := string(secret.Data[kubedb.AuthDataKey]) @@ -139,6 +141,7 @@ func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { secretName := o.db.GetMemcachedAuthSecretName() secret, err := o.Client.CoreV1().Secrets(o.db.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { + klog.Errorf("Get Secret Error: %v", err.Error()) return nil, err } return secret, nil diff --git a/vendor/modules.txt b/vendor/modules.txt index 48519c631..db01b8bc6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1691,7 +1691,7 @@ kmodules.xyz/offshoot-api/api/v1 kmodules.xyz/offshoot-api/api/v1/conversion kmodules.xyz/offshoot-api/api/v2 kmodules.xyz/offshoot-api/util -# kubedb.dev/apimachinery v0.53.0-rc.1.0.20250323010024-b7bdda585fd4 +# kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d ## explicit; go 1.23.0 kubedb.dev/apimachinery/apis kubedb.dev/apimachinery/apis/catalog From 8370770aa8c10b35eafb678b06b6a2140645496b Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 11:48:01 +0600 Subject: [PATCH 06/12] remove extra print Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 95cbf7168..6af217478 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -124,7 +124,6 @@ func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { splitUsernamePassword := strings.Split(usernamePasswordPairs, ":") memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) - klog.Infof("memcached username and password: %s %s", memcachedUserName, memcachedPassword) err = mcClient.SetAuth(&memcache.Item{ Key: kubedb.MemcachedHealthKey, Flags: 0, Expiration: 0, User: memcachedUserName, Pass: memcachedPassword, From 91c7d41a1399d53627b111ee3e24ecd33a282ced Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 12:33:37 +0600 Subject: [PATCH 07/12] update deps Signed-off-by: Muhammad Raisul Islam Evan --- go.mod | 2 +- go.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 2e4c76163..3bc82d58d 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( k8s.io/klog/v2 v2.130.1 kmodules.xyz/client-go v0.32.1 kmodules.xyz/custom-resources v0.32.0 - kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d + kubedb.dev/apimachinery v0.53.0 sigs.k8s.io/controller-runtime v0.20.3 xorm.io/xorm v1.3.6 ) diff --git a/go.sum b/go.sum index ef8e6f8cb..8bb803587 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ kmodules.xyz/monitoring-agent-api v0.32.0 h1:cMQbWvbTc4JWeLI/zYE0HLefsdFYBzqvATL kmodules.xyz/monitoring-agent-api v0.32.0/go.mod h1:zgRKiJcuK7FOHy0Y1TsONRbJfgnPCs8t4Zh/6Afr+yU= kmodules.xyz/offshoot-api v0.32.0 h1:gogc5scSZe2JoXtZof72UGRl3Tit0kFaFRMkLLT1D8o= kmodules.xyz/offshoot-api v0.32.0/go.mod h1:tled7OxYZ3SkUJcrVFVVYyd+zXjsRSEm1R6Q3k4gcx0= -kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d h1:9vR1Z75WV1s5V2Ge9v4EfabuHDS56BP/oLO1XCcEZrg= -kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= +kubedb.dev/apimachinery v0.53.0 h1:uxgf/Kc27/A87/oZd+TjddAC2IS5C3ubZGiRBVqu0cc= +kubedb.dev/apimachinery v0.53.0/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= kubeops.dev/petset v0.0.10 h1:sNaqmHrD9bW7pcrWnwPoiQrKvdRwRX0BaRQc5QA78Bg= kubeops.dev/petset v0.0.10/go.mod h1:uHL83kggwmtSxdlIfxNbY2isV22iYV6YjADv0y+Z7YA= kubeops.dev/sidekick v0.0.11 h1:OydXdIH6cYSiWxKIWvrywk95WhhHSERkc7RNPOmTekc= diff --git a/vendor/modules.txt b/vendor/modules.txt index db01b8bc6..6c890d305 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1691,7 +1691,7 @@ kmodules.xyz/offshoot-api/api/v1 kmodules.xyz/offshoot-api/api/v1/conversion kmodules.xyz/offshoot-api/api/v2 kmodules.xyz/offshoot-api/util -# kubedb.dev/apimachinery v0.53.1-0.20250325051323-80de63d39f0d +# kubedb.dev/apimachinery v0.53.0 ## explicit; go 1.23.0 kubedb.dev/apimachinery/apis kubedb.dev/apimachinery/apis/catalog From 18122510379aab72b1b46eea130d1d027486df5c Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 12:39:59 +0600 Subject: [PATCH 08/12] update deps-2 Signed-off-by: Muhammad Raisul Islam Evan --- go.mod | 2 +- go.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3bc82d58d..b1f086913 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( k8s.io/klog/v2 v2.130.1 kmodules.xyz/client-go v0.32.1 kmodules.xyz/custom-resources v0.32.0 - kubedb.dev/apimachinery v0.53.0 + kubedb.dev/apimachinery v0.53.1-0.20250325061150-ba449cf3bc49 sigs.k8s.io/controller-runtime v0.20.3 xorm.io/xorm v1.3.6 ) diff --git a/go.sum b/go.sum index 8bb803587..a1cd1a020 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ kmodules.xyz/monitoring-agent-api v0.32.0 h1:cMQbWvbTc4JWeLI/zYE0HLefsdFYBzqvATL kmodules.xyz/monitoring-agent-api v0.32.0/go.mod h1:zgRKiJcuK7FOHy0Y1TsONRbJfgnPCs8t4Zh/6Afr+yU= kmodules.xyz/offshoot-api v0.32.0 h1:gogc5scSZe2JoXtZof72UGRl3Tit0kFaFRMkLLT1D8o= kmodules.xyz/offshoot-api v0.32.0/go.mod h1:tled7OxYZ3SkUJcrVFVVYyd+zXjsRSEm1R6Q3k4gcx0= -kubedb.dev/apimachinery v0.53.0 h1:uxgf/Kc27/A87/oZd+TjddAC2IS5C3ubZGiRBVqu0cc= -kubedb.dev/apimachinery v0.53.0/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= +kubedb.dev/apimachinery v0.53.1-0.20250325061150-ba449cf3bc49 h1:NYtX/Dh/PeEC7WWGEc+QFF3vo7onKGmxIaytB3dfvYk= +kubedb.dev/apimachinery v0.53.1-0.20250325061150-ba449cf3bc49/go.mod h1:CSK+s+3FJcqJv7tx8R1VKDOUMa7bbruOQ9yk7U9dmMo= kubeops.dev/petset v0.0.10 h1:sNaqmHrD9bW7pcrWnwPoiQrKvdRwRX0BaRQc5QA78Bg= kubeops.dev/petset v0.0.10/go.mod h1:uHL83kggwmtSxdlIfxNbY2isV22iYV6YjADv0y+Z7YA= kubeops.dev/sidekick v0.0.11 h1:OydXdIH6cYSiWxKIWvrywk95WhhHSERkc7RNPOmTekc= diff --git a/vendor/modules.txt b/vendor/modules.txt index 6c890d305..cd03f4736 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1691,7 +1691,7 @@ kmodules.xyz/offshoot-api/api/v1 kmodules.xyz/offshoot-api/api/v1/conversion kmodules.xyz/offshoot-api/api/v2 kmodules.xyz/offshoot-api/util -# kubedb.dev/apimachinery v0.53.0 +# kubedb.dev/apimachinery v0.53.1-0.20250325061150-ba449cf3bc49 ## explicit; go 1.23.0 kubedb.dev/apimachinery/apis kubedb.dev/apimachinery/apis/catalog From bbc06d96f0847e5bb1ee7c1af18d3a57198cb354 Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 12:48:12 +0600 Subject: [PATCH 09/12] fix changes Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 6af217478..9519e6b75 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -26,7 +26,7 @@ import ( "github.com/pkg/errors" core "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "kubedb.dev/apimachinery/apis/kubedb" dbapi "kubedb.dev/apimachinery/apis/kubedb/v1" @@ -45,11 +45,10 @@ type KubeDBClientBuilder struct { database int } -func NewKubeDBClientBuilder(kc client.Client, client kubernetes.Interface, db *dbapi.Memcached) *KubeDBClientBuilder { +func NewKubeDBClientBuilder(kc client.Client, db *dbapi.Memcached) *KubeDBClientBuilder { return &KubeDBClientBuilder{ - kc: kc, - Client: client, - db: db, + kc: kc, + db: db, } } @@ -78,6 +77,10 @@ func (o *KubeDBClientBuilder) GetMemcachedClient() (*Client, error) { return nil, errors.New("secret is not found") } + if secret.Data["ca.crt"] == nil || secret.Data["tls.crt"] == nil || secret.Data["tls.key"] == nil { + return nil, errors.New("invalid auth-secret. Certificates not found.") + } + caCert := secret.Data["ca.crt"] clientCert := secret.Data["tls.crt"] clientKey := secret.Data["tls.key"] @@ -137,11 +140,14 @@ func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { } func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { - secretName := o.db.GetMemcachedAuthSecretName() - secret, err := o.Client.CoreV1().Secrets(o.db.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) + var authSecret *core.Secret + err := o.kc.Get(context.TODO(), types.NamespacedName{ + Name: o.db.GetMemcachedAuthSecretName(), + Namespace: o.db.Namespace, + }, authSecret) if err != nil { klog.Errorf("Get Secret Error: %v", err.Error()) return nil, err } - return secret, nil + return authSecret, nil } From 5f990422fce5c9e11da7685172d43dd04bb3d2bd Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 13:34:28 +0600 Subject: [PATCH 10/12] update changes Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 9519e6b75..3f8d77db2 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -123,9 +123,9 @@ func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { authData := string(secret.Data[kubedb.AuthDataKey]) separatePairs := strings.Split(authData, "\n") - usernamePasswordPairs := separatePairs[0] + usernamePasswordPair := separatePairs[0] - splitUsernamePassword := strings.Split(usernamePasswordPairs, ":") + splitUsernamePassword := strings.Split(usernamePasswordPair, ":") memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) err = mcClient.SetAuth(&memcache.Item{ @@ -146,7 +146,6 @@ func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { Namespace: o.db.Namespace, }, authSecret) if err != nil { - klog.Errorf("Get Secret Error: %v", err.Error()) return nil, err } return authSecret, nil From 45207f34ecec49ab4e09662c5e7a3c5ff665a3dd Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 14:02:37 +0600 Subject: [PATCH 11/12] fix getSecret Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 3f8d77db2..784c7df9d 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -140,13 +140,13 @@ func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { } func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { - var authSecret *core.Secret + var authSecret core.Secret err := o.kc.Get(context.TODO(), types.NamespacedName{ Name: o.db.GetMemcachedAuthSecretName(), Namespace: o.db.Namespace, - }, authSecret) + }, &authSecret) if err != nil { return nil, err } - return authSecret, nil + return &authSecret, nil } From cc23dee48a59f2851a281fadffe58b98eaba3af4 Mon Sep 17 00:00:00 2001 From: Muhammad Raisul Islam Evan Date: Tue, 25 Mar 2025 14:35:59 +0600 Subject: [PATCH 12/12] fix tlsSecret Signed-off-by: Muhammad Raisul Islam Evan --- memcached/kubedb_client_builder.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/memcached/kubedb_client_builder.go b/memcached/kubedb_client_builder.go index 784c7df9d..18b8e45b3 100644 --- a/memcached/kubedb_client_builder.go +++ b/memcached/kubedb_client_builder.go @@ -27,18 +27,17 @@ import ( "github.com/pkg/errors" core "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" "kubedb.dev/apimachinery/apis/kubedb" dbapi "kubedb.dev/apimachinery/apis/kubedb/v1" "github.com/kubedb/gomemcache/memcache" "k8s.io/klog/v2" + api "kubedb.dev/apimachinery/apis/kubedb/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) type KubeDBClientBuilder struct { kc client.Client - Client kubernetes.Interface db *dbapi.Memcached podName string url string @@ -71,14 +70,14 @@ func (o *KubeDBClientBuilder) GetMemcachedClient() (*Client, error) { mcClient := memcache.New(o.db.Address()) if o.db.Spec.TLS != nil { // Secret for Memcached Client Certs - secret, err := o.GetSecret() + secret, err := o.GetTLSSecret() if err != nil { - klog.Error(err, "Failed to get auth-secret") - return nil, errors.New("secret is not found") + klog.Error(err, "Failed to get TLS-secret") + return nil, err } if secret.Data["ca.crt"] == nil || secret.Data["tls.crt"] == nil || secret.Data["tls.key"] == nil { - return nil, errors.New("invalid auth-secret. Certificates not found.") + return nil, errors.New("invalid tls-secret.") } caCert := secret.Data["ca.crt"] @@ -87,7 +86,7 @@ func (o *KubeDBClientBuilder) GetMemcachedClient() (*Client, error) { caCertPool := x509.NewCertPool() if ok := caCertPool.AppendCertsFromPEM(caCert); !ok { - klog.Infoln("Failed to append CA certificate to the pool") + klog.Errorf("Failed to append CA certificate to the pool") } // Load client certificate @@ -123,9 +122,9 @@ func (o *KubeDBClientBuilder) SetAuth(mcClient *Client) error { authData := string(secret.Data[kubedb.AuthDataKey]) separatePairs := strings.Split(authData, "\n") - usernamePasswordPair := separatePairs[0] + firstUsernamePassPair := separatePairs[0] - splitUsernamePassword := strings.Split(usernamePasswordPair, ":") + splitUsernamePassword := strings.Split(firstUsernamePassPair, ":") memcachedUserName, memcachedPassword := strings.TrimSpace(splitUsernamePassword[0]), strings.TrimSpace(splitUsernamePassword[1]) err = mcClient.SetAuth(&memcache.Item{ @@ -150,3 +149,15 @@ func (o *KubeDBClientBuilder) GetSecret() (*core.Secret, error) { } return &authSecret, nil } + +func (o *KubeDBClientBuilder) GetTLSSecret() (*core.Secret, error) { + var tlsSecret core.Secret + err := o.kc.Get(context.TODO(), types.NamespacedName{ + Name: o.db.GetCertSecretName(api.MemcachedClientCert), + Namespace: o.db.Namespace, + }, &tlsSecret) + if err != nil { + return nil, err + } + return &tlsSecret, nil +}