2021-10-26 14:53:56 +08:00
|
|
|
# Copyright 2021 Memgraph Ltd.
|
|
|
|
#
|
|
|
|
# Use of this software is governed by the Business Source License
|
|
|
|
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
|
|
# License, and you may not use this file except in compliance with the Business Source License.
|
|
|
|
#
|
|
|
|
# As of the Change Date specified in that file, in accordance with
|
|
|
|
# the Business Source License, use of this software will be governed
|
|
|
|
# by the Apache License, Version 2.0, included in the file
|
|
|
|
# licenses/APL.txt.
|
|
|
|
|
2021-01-16 02:04:44 +08:00
|
|
|
import copy
|
|
|
|
import os
|
2023-12-14 21:36:33 +08:00
|
|
|
import shutil
|
2021-01-16 02:04:44 +08:00
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
|
|
|
|
import mgclient
|
|
|
|
|
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", ".."))
|
|
|
|
BUILD_DIR = os.path.join(PROJECT_DIR, "build")
|
|
|
|
MEMGRAPH_BINARY = os.path.join(BUILD_DIR, "memgraph")
|
2023-10-25 22:01:59 +08:00
|
|
|
SIGNAL_SIGTERM = 15
|
2021-01-16 02:04:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
def wait_for_server(port, delay=0.01):
|
|
|
|
cmd = ["nc", "-z", "-w", "1", "127.0.0.1", str(port)]
|
|
|
|
count = 0
|
|
|
|
while subprocess.call(cmd) != 0:
|
|
|
|
time.sleep(0.01)
|
|
|
|
if count > 10 / 0.01:
|
|
|
|
print("Could not wait for server on port", port, "to startup!")
|
|
|
|
sys.exit(1)
|
|
|
|
count += 1
|
|
|
|
time.sleep(delay)
|
|
|
|
|
|
|
|
|
|
|
|
def extract_bolt_port(args):
|
|
|
|
for arg_index, arg in enumerate(args):
|
2022-02-17 17:36:10 +08:00
|
|
|
if arg.startswith("--bolt-port="):
|
|
|
|
maybe_port = arg.split("=")[1]
|
2021-01-16 02:04:44 +08:00
|
|
|
if not maybe_port.isdigit():
|
2022-02-17 17:36:10 +08:00
|
|
|
raise Exception("Unable to read Bolt port after --bolt-port=.")
|
2021-01-16 02:04:44 +08:00
|
|
|
return int(maybe_port)
|
2022-02-17 17:36:10 +08:00
|
|
|
elif arg == "--bolt-port":
|
2021-01-16 02:04:44 +08:00
|
|
|
maybe_port = args[arg_index + 1]
|
|
|
|
if not maybe_port.isdigit():
|
2022-02-17 17:36:10 +08:00
|
|
|
raise Exception("Unable to read Bolt port after --bolt-port.")
|
2021-01-16 02:04:44 +08:00
|
|
|
return int(maybe_port)
|
|
|
|
return 7687
|
|
|
|
|
|
|
|
|
2022-02-17 17:36:10 +08:00
|
|
|
def replace_paths(path):
|
|
|
|
return path.replace("$PROJECT_DIR", PROJECT_DIR).replace("$SCRIPT_DIR", SCRIPT_DIR).replace("$BUILD_DIR", BUILD_DIR)
|
|
|
|
|
|
|
|
|
|
|
|
class MemgraphInstanceRunner:
|
2024-02-05 18:37:00 +08:00
|
|
|
def __init__(self, binary_path=MEMGRAPH_BINARY, use_ssl=False, delete_on_stop=None, username=None, password=None):
|
2022-02-17 17:36:10 +08:00
|
|
|
self.host = "127.0.0.1"
|
|
|
|
self.bolt_port = None
|
2021-01-16 02:04:44 +08:00
|
|
|
self.binary_path = binary_path
|
2022-02-17 17:36:10 +08:00
|
|
|
self.args = None
|
2021-01-16 02:04:44 +08:00
|
|
|
self.proc_mg = None
|
2022-02-17 17:36:10 +08:00
|
|
|
self.ssl = use_ssl
|
2023-12-14 21:36:33 +08:00
|
|
|
self.delete_on_stop = delete_on_stop
|
2024-02-05 18:37:00 +08:00
|
|
|
self.username = username
|
|
|
|
self.password = password
|
2021-01-16 02:04:44 +08:00
|
|
|
|
2023-06-27 04:43:34 +08:00
|
|
|
def execute_setup_queries(self, setup_queries):
|
|
|
|
if setup_queries is None:
|
|
|
|
return
|
2024-02-05 18:37:00 +08:00
|
|
|
conn = mgclient.connect(
|
|
|
|
host=self.host,
|
|
|
|
port=self.bolt_port,
|
|
|
|
sslmode=self.ssl,
|
|
|
|
username=(self.username or ""),
|
|
|
|
password=(self.password or ""),
|
|
|
|
)
|
2023-06-27 04:43:34 +08:00
|
|
|
conn.autocommit = True
|
|
|
|
cursor = conn.cursor()
|
2023-08-30 19:42:11 +08:00
|
|
|
for query_coll in setup_queries:
|
|
|
|
if isinstance(query_coll, str):
|
|
|
|
cursor.execute(query_coll)
|
|
|
|
elif isinstance(query_coll, list):
|
|
|
|
for query in query_coll:
|
|
|
|
cursor.execute(query)
|
2023-06-27 04:43:34 +08:00
|
|
|
cursor.close()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
# NOTE: Both query and get_connection may esablish new connection -> auth
|
|
|
|
# details required -> username/password should be optional arguments.
|
|
|
|
def query(self, query, conn=None, username="", password=""):
|
|
|
|
new_conn = conn is None
|
|
|
|
if new_conn:
|
|
|
|
conn = self.get_connection(username, password)
|
|
|
|
cursor = conn.cursor()
|
2021-01-16 02:04:44 +08:00
|
|
|
cursor.execute(query)
|
2023-06-27 04:43:34 +08:00
|
|
|
data = cursor.fetchall()
|
|
|
|
cursor.close()
|
|
|
|
if new_conn:
|
|
|
|
conn.close()
|
|
|
|
return data
|
|
|
|
|
|
|
|
def get_connection(self, username="", password=""):
|
|
|
|
conn = mgclient.connect(
|
|
|
|
host=self.host, port=self.bolt_port, sslmode=self.ssl, username=username, password=password
|
|
|
|
)
|
|
|
|
conn.autocommit = True
|
|
|
|
return conn
|
|
|
|
|
|
|
|
def start(self, restart=False, args=None, setup_queries=None):
|
2021-01-16 02:04:44 +08:00
|
|
|
if not restart and self.is_running():
|
|
|
|
return
|
|
|
|
self.stop()
|
2023-06-27 04:43:34 +08:00
|
|
|
if args is not None:
|
|
|
|
self.args = copy.deepcopy(args)
|
2022-02-17 17:36:10 +08:00
|
|
|
self.args = [replace_paths(arg) for arg in self.args]
|
|
|
|
args_mg = [
|
|
|
|
self.binary_path,
|
|
|
|
"--storage-wal-enabled",
|
|
|
|
"--storage-snapshot-interval-sec",
|
|
|
|
"300",
|
|
|
|
"--storage-properties-on-edges",
|
|
|
|
] + self.args
|
2021-01-16 02:04:44 +08:00
|
|
|
self.bolt_port = extract_bolt_port(args_mg)
|
|
|
|
self.proc_mg = subprocess.Popen(args_mg)
|
|
|
|
wait_for_server(self.bolt_port)
|
2023-06-27 04:43:34 +08:00
|
|
|
self.execute_setup_queries(setup_queries)
|
2021-01-16 02:04:44 +08:00
|
|
|
assert self.is_running(), "The Memgraph process died!"
|
|
|
|
|
|
|
|
def is_running(self):
|
|
|
|
if self.proc_mg is None:
|
|
|
|
return False
|
|
|
|
if self.proc_mg.poll() is not None:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2023-12-14 21:36:33 +08:00
|
|
|
def stop(self, keep_directories=False):
|
2021-01-16 02:04:44 +08:00
|
|
|
if not self.is_running():
|
|
|
|
return
|
2023-10-23 18:48:26 +08:00
|
|
|
|
|
|
|
pid = self.proc_mg.pid
|
|
|
|
try:
|
2023-10-25 22:01:59 +08:00
|
|
|
os.kill(pid, SIGNAL_SIGTERM)
|
2023-10-23 18:48:26 +08:00
|
|
|
except os.OSError:
|
|
|
|
assert False
|
|
|
|
|
|
|
|
time.sleep(1)
|
2022-06-15 17:16:11 +08:00
|
|
|
|
2023-12-14 21:36:33 +08:00
|
|
|
if not keep_directories:
|
|
|
|
for folder in self.delete_on_stop or {}:
|
2024-02-12 23:42:57 +08:00
|
|
|
try:
|
|
|
|
shutil.rmtree(folder)
|
|
|
|
except Exception as e:
|
|
|
|
pass # couldn't delete folder, skip
|
2023-12-14 21:36:33 +08:00
|
|
|
|
|
|
|
def kill(self, keep_directories=False):
|
2022-06-15 17:16:11 +08:00
|
|
|
if not self.is_running():
|
|
|
|
return
|
|
|
|
self.proc_mg.kill()
|
|
|
|
code = self.proc_mg.wait()
|
2023-12-14 21:36:33 +08:00
|
|
|
if not keep_directories:
|
|
|
|
for folder in self.delete_on_stop or {}:
|
|
|
|
shutil.rmtree(folder)
|
2022-06-20 18:28:42 +08:00
|
|
|
assert code == -9, "The killed Memgraph process exited with non-nine!"
|