Skip to content

Commit 0daf001

Browse files
Add Neo4j Client (#204)
Signed-off-by: Fazle Rabbi Sarker <fazlerabbi@appscode.com> Signed-off-by: obaydullahmhs <obaydullah@appscode.com> Co-authored-by: obaydullahmhs <obaydullah@appscode.com>
1 parent 8f85204 commit 0daf001

File tree

101 files changed

+17428
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+17428
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
github.com/lib/pq v1.10.7
2626
github.com/michaelklishin/rabbit-hole/v3 v3.1.0
2727
github.com/microsoft/go-mssqldb v1.6.0
28+
github.com/neo4j/neo4j-go-driver/v5 v5.28.4
2829
github.com/opensearch-project/opensearch-go v1.1.0
2930
github.com/opensearch-project/opensearch-go/v2 v2.3.0
3031
github.com/pkg/errors v0.9.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6f
279279
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
280280
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
281281
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
282+
github.com/neo4j/neo4j-go-driver/v5 v5.28.4 h1:7toxehVcYkZbyxV4W3Ib9VcnyRBQPucF+VwNNmtSXi4=
283+
github.com/neo4j/neo4j-go-driver/v5 v5.28.4/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
282284
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
283285
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
284286
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

neo4j/client.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright AppsCode Inc. and Contributors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package neo4j
18+
19+
import (
20+
neo4jdriver "github.com/neo4j/neo4j-go-driver/v5/neo4j"
21+
)
22+
23+
type Client struct {
24+
neo4jdriver.DriverWithContext
25+
}

neo4j/kubedb_client_builder.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
Copyright AppsCode Inc. and Contributors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package neo4j
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"time"
24+
25+
"strings"
26+
27+
"github.com/go-logr/logr"
28+
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
29+
"github.com/neo4j/neo4j-go-driver/v5/neo4j/config"
30+
core "k8s.io/api/core/v1"
31+
kerr "k8s.io/apimachinery/pkg/api/errors"
32+
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/klog/v2"
34+
"kubedb.dev/apimachinery/apis/kubedb"
35+
api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2"
36+
apiutils "kubedb.dev/apimachinery/pkg/utils"
37+
"sigs.k8s.io/controller-runtime/pkg/client"
38+
)
39+
40+
type KubeDBClientBuilder struct {
41+
kc client.Client
42+
db *api.Neo4j
43+
log logr.Logger
44+
url string
45+
podName string
46+
ctx context.Context
47+
}
48+
49+
func NewKubeDBClientBuilder(kc client.Client, db *api.Neo4j) *KubeDBClientBuilder {
50+
return &KubeDBClientBuilder{
51+
kc: kc,
52+
db: db,
53+
}
54+
}
55+
56+
func (o *KubeDBClientBuilder) WithContext(ctx context.Context) *KubeDBClientBuilder {
57+
o.ctx = ctx
58+
return o
59+
}
60+
61+
func (o *KubeDBClientBuilder) WithLog(log logr.Logger) *KubeDBClientBuilder {
62+
o.log = log
63+
return o
64+
}
65+
66+
func (o *KubeDBClientBuilder) GetNeo4jClient() (*Client, error) {
67+
// Guard against nil receiver or missing required fields to avoid nil deref
68+
if o == nil {
69+
return nil, fmt.Errorf("KubeDBClientBuilder is nil")
70+
}
71+
if o.db == nil {
72+
return nil, fmt.Errorf("neo4j object is nil")
73+
}
74+
if o.kc == nil {
75+
return nil, fmt.Errorf("kubernetes client is nil")
76+
}
77+
// Ensure context is non-nil
78+
if o.ctx == nil {
79+
o.ctx = context.TODO()
80+
}
81+
// Get domain and fallback to cluster.local if not found
82+
domain := apiutils.FindDomain()
83+
if domain == "" {
84+
domain = "cluster.local"
85+
}
86+
87+
// Construct URL - use default service if podName not provided
88+
if o.podName != "" {
89+
o.url = fmt.Sprintf("neo4j://%s.%s.%s.svc.%s:%d", o.podName, o.db.GoverningServiceName(), o.db.Namespace, domain, kubedb.Neo4jBoltPort)
90+
} else {
91+
o.url = fmt.Sprintf("neo4j://%s.%s.svc.%s:%d", o.db.ServiceName(), o.db.Namespace, domain, kubedb.Neo4jBoltPort)
92+
}
93+
94+
klog.V(3).Infof("Attempting to connect to Neo4j at: %s", o.url)
95+
96+
var dbUser, dbPassword string
97+
authSecret := &core.Secret{}
98+
99+
if !o.db.Spec.DisableSecurity {
100+
err := o.kc.Get(o.ctx, types.NamespacedName{
101+
Namespace: o.db.Namespace,
102+
Name: o.db.GetAuthSecretName(),
103+
}, authSecret)
104+
if err != nil {
105+
if kerr.IsNotFound(err) {
106+
klog.Error(err, "Auth-secret not found")
107+
return nil, errors.New("auth-secret is not found")
108+
}
109+
klog.Error(err, "Failed to get auth-secret")
110+
return nil, err
111+
}
112+
113+
// Extract NEO4J_AUTH from Secret.Data
114+
auth, ok := authSecret.Data["NEO4J_AUTH"]
115+
if !ok {
116+
return nil, fmt.Errorf("secret %s/%s does not contain key NEO4J_AUTH", o.db.Namespace, o.db.GetAuthSecretName())
117+
}
118+
authStr := strings.TrimSpace(string(auth))
119+
120+
// Expect "username/password", split safely
121+
parts := strings.SplitN(authStr, "/", 2)
122+
if len(parts) != 2 || parts[0] == "" {
123+
return nil, fmt.Errorf("invalid NEO4J_AUTH format in secret %s/%s. Expected \"username/password\"", o.db.Namespace, o.db.GetAuthSecretName())
124+
}
125+
dbUser = parts[0]
126+
dbPassword = parts[1]
127+
} else {
128+
klog.Info("Security is disabled for Neo4j, no credentials will be used.")
129+
}
130+
131+
// Create driver and check for errors immediately
132+
driver, err := neo4j.NewDriverWithContext(o.url, neo4j.BasicAuth(dbUser, dbPassword, ""), func(c *config.Config) {
133+
c.SocketConnectTimeout = 60 * time.Second
134+
c.ConnectionAcquisitionTimeout = 60 * time.Second
135+
c.MaxTransactionRetryTime = 60 * time.Second
136+
c.MaxConnectionLifetime = 30 * time.Minute
137+
c.MaxConnectionPoolSize = 100
138+
})
139+
if o.db.Spec.DisableSecurity {
140+
driver, err = neo4j.NewDriverWithContext(o.url, neo4j.NoAuth())
141+
}
142+
if err != nil {
143+
klog.Error(err, "Failed to create Neo4j driver")
144+
return nil, err
145+
}
146+
147+
// Now verify connectivity on the successfully created driver
148+
if err = driver.VerifyConnectivity(o.ctx); err != nil {
149+
klog.Error(err, "Failed to connect to Neo4j")
150+
// Close driver on verification failure
151+
_ = driver.Close(o.ctx)
152+
return nil, err
153+
}
154+
155+
fmt.Println("Connection established.")
156+
157+
return &Client{
158+
DriverWithContext: driver,
159+
}, nil
160+
}
161+
162+
func (c *Client) ExecuteQuery(ctx context.Context, query string, params map[string]any, dbName string) (*neo4j.EagerResult, error) {
163+
return neo4j.ExecuteQuery(ctx, c, query, params, neo4j.EagerResultTransformer,
164+
neo4j.ExecuteQueryWithDatabase(dbName))
165+
}

0 commit comments

Comments
 (0)