Coverage for virtuoso_utilities / native_entrypoint.py: 51%
100 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-04-14 09:16 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2026-04-14 09:16 +0000
1#!/usr/bin/env python3
3# SPDX-FileCopyrightText: 2025 Arcangelo Massari <arcangelo.massari@unibo.it>
4#
5# SPDX-License-Identifier: ISC
7"""
8Virtuoso native mode entrypoint.
10Wrapper script that configures virtuoso.ini from environment variables,
11then execs the original Virtuoso entrypoint. Designed for use as a Docker
12container entrypoint.
14Usage in Dockerfile:
15 FROM openlink/virtuoso-opensource-7:latest
16 RUN pip install virtuoso-utilities
17 ENTRYPOINT ["virtuoso-native-launch"]
19Environment variables:
20 VIRTUOSO_MEMORY: Memory limit (e.g., "8g"). Default: 2/3 of available RAM
21 VIRTUOSO_DBA_PASSWORD: DBA password. Also accepts DBA_PASSWORD for compatibility
22 VIRTUOSO_ESTIMATED_DB_SIZE_GB: Estimated DB size for MaxCheckpointRemap
23 VIRTUOSO_PARALLEL_THREADS: CPU cores for query parallelization
24 VIRTUOSO_ENABLE_WRITE_PERMISSIONS: Enable SPARQL write ("true"/"1")
25 VIRTUOSO_NUMBER_OF_BUFFERS: Override automatic buffer calculation
26 VIRTUOSO_MAX_DIRTY_BUFFERS: Override automatic dirty buffer calculation
27 VIRTUOSO_DATA_DIR: Data directory path
28 VIRTUOSO_EXTRA_DIRS_ALLOWED: Additional DirsAllowed paths (comma-separated)
29 VIRTUOSO_ORIGINAL_ENTRYPOINT: Original entrypoint to exec
30"""
32import os
33import sys
35from virtuoso_utilities.launch_virtuoso import (
36 DEFAULT_CONTAINER_DATA_DIR,
37 DEFAULT_DIRS_ALLOWED,
38 bytes_to_docker_mem_str,
39 calculate_max_query_mem,
40 calculate_threading_config,
41 get_default_memory,
42 get_optimal_buffer_values,
43 get_virt_env_vars,
44 grant_write_permissions,
45 update_ini_memory_settings,
46 wait_for_virtuoso_ready,
47)
49ENV_MEMORY = "VIRTUOSO_MEMORY"
50ENV_DBA_PASSWORD = "VIRTUOSO_DBA_PASSWORD"
51ENV_ESTIMATED_DB_SIZE_GB = "VIRTUOSO_ESTIMATED_DB_SIZE_GB"
52ENV_PARALLEL_THREADS = "VIRTUOSO_PARALLEL_THREADS"
53ENV_ENABLE_WRITE_PERMISSIONS = "VIRTUOSO_ENABLE_WRITE_PERMISSIONS"
54ENV_NUMBER_OF_BUFFERS = "VIRTUOSO_NUMBER_OF_BUFFERS"
55ENV_MAX_DIRTY_BUFFERS = "VIRTUOSO_MAX_DIRTY_BUFFERS"
56ENV_DATA_DIR = "VIRTUOSO_DATA_DIR"
57ENV_EXTRA_DIRS_ALLOWED = "VIRTUOSO_EXTRA_DIRS_ALLOWED"
58ENV_ORIGINAL_ENTRYPOINT = "VIRTUOSO_ORIGINAL_ENTRYPOINT"
60DEFAULT_ORIGINAL_ENTRYPOINT = "/virtuoso-entrypoint.sh"
62CGROUP_V2_MEMORY_MAX = "/sys/fs/cgroup/memory.max"
63CGROUP_V1_MEMORY_LIMIT = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
66def get_container_memory_limit():
67 for path in [CGROUP_V2_MEMORY_MAX, CGROUP_V1_MEMORY_LIMIT]:
68 try:
69 with open(path) as f:
70 value = f.read().strip()
71 if value != "max":
72 return int(value)
73 except (FileNotFoundError, ValueError, PermissionError):
74 continue
75 return None
78def get_native_default_memory():
79 container_limit = get_container_memory_limit()
80 if container_limit:
81 default_mem = max(int(container_limit * (2 / 3)), 1 * 1024**3)
82 return bytes_to_docker_mem_str(default_mem)
83 return get_default_memory()
86def get_config_from_env():
87 config = {}
88 config["memory"] = os.environ.get(ENV_MEMORY) or get_native_default_memory()
89 config["dba_password"] = (
90 os.environ.get(ENV_DBA_PASSWORD) or os.environ.get("DBA_PASSWORD", "dba")
91 )
92 config["estimated_db_size_gb"] = float(
93 os.environ.get(ENV_ESTIMATED_DB_SIZE_GB, "0")
94 )
95 threads_str = os.environ.get(ENV_PARALLEL_THREADS)
96 config["parallel_threads"] = int(threads_str) if threads_str else None
97 config["enable_write_permissions"] = os.environ.get(
98 ENV_ENABLE_WRITE_PERMISSIONS, ""
99 ).lower() in ("1", "true", "yes")
100 buffers_str = os.environ.get(ENV_NUMBER_OF_BUFFERS)
101 config["number_of_buffers"] = int(buffers_str) if buffers_str else None
102 dirty_str = os.environ.get(ENV_MAX_DIRTY_BUFFERS)
103 config["max_dirty_buffers"] = int(dirty_str) if dirty_str else None
104 config["data_dir"] = os.environ.get(ENV_DATA_DIR, DEFAULT_CONTAINER_DATA_DIR)
105 config["extra_dirs_allowed"] = os.environ.get(ENV_EXTRA_DIRS_ALLOWED, "")
106 config["original_entrypoint"] = os.environ.get(
107 ENV_ORIGINAL_ENTRYPOINT, DEFAULT_ORIGINAL_ENTRYPOINT
108 )
109 return config
112def configure_virtuoso(config):
113 data_dir = config["data_dir"]
114 ini_path = os.path.join(data_dir, "virtuoso.ini")
116 if config["number_of_buffers"] is None or config["max_dirty_buffers"] is None:
117 num_buffers, max_dirty = get_optimal_buffer_values(config["memory"])
118 if config["number_of_buffers"] is None:
119 config["number_of_buffers"] = num_buffers
120 if config["max_dirty_buffers"] is None:
121 config["max_dirty_buffers"] = max_dirty
123 dirs = DEFAULT_DIRS_ALLOWED.copy()
124 dirs.add(data_dir)
125 if config["extra_dirs_allowed"]:
126 extra = set(
127 d.strip() for d in config["extra_dirs_allowed"].split(",") if d.strip()
128 )
129 dirs.update(extra)
131 threading = calculate_threading_config(config["parallel_threads"])
132 max_query_mem_value = calculate_max_query_mem(
133 config["memory"], config["number_of_buffers"]
134 )
135 update_ini_memory_settings(
136 ini_path=ini_path,
137 data_dir_path=data_dir,
138 number_of_buffers=config["number_of_buffers"],
139 max_dirty_buffers=config["max_dirty_buffers"],
140 dirs_allowed=",".join(dirs),
141 async_queue_max_threads=threading["async_queue_max_threads"],
142 threads_per_query=threading["threads_per_query"],
143 max_client_connections=threading["max_client_connections"],
144 adjust_vector_size=0,
145 vector_size=1000,
146 checkpoint_interval=1,
147 max_query_mem=max_query_mem_value,
148 http_server_threads=threading["max_client_connections"],
149 thread_cleanup_interval=1,
150 resources_cleanup_interval=1,
151 )
153 print(
154 f"Info: Configured Virtuoso with NumberOfBuffers={config['number_of_buffers']}, "
155 f"MaxDirtyBuffers={config['max_dirty_buffers']}"
156 )
159def set_virt_env_vars(config):
160 env_vars = get_virt_env_vars(
161 memory=config["memory"],
162 number_of_buffers=config["number_of_buffers"],
163 max_dirty_buffers=config["max_dirty_buffers"],
164 parallel_threads=config["parallel_threads"],
165 estimated_db_size_gb=config["estimated_db_size_gb"],
166 )
167 for key, value in env_vars.items():
168 os.environ[key] = value
171def apply_write_permissions_async(dba_password):
172 pid = os.fork()
173 if pid == 0:
174 try:
175 if wait_for_virtuoso_ready(dba_password):
176 grant_write_permissions(dba_password)
177 except Exception as e:
178 print(f"Error applying write permissions: {e}", file=sys.stderr)
179 os._exit(0)
182def main():
183 config = get_config_from_env()
185 print("=" * 70)
186 print("Virtuoso Native Mode Configuration")
187 print(f" Memory: {config['memory']}")
188 print(f" Data Dir: {config['data_dir']}")
189 print(f" Write Permissions: {config['enable_write_permissions']}")
190 print(f" Original Entrypoint: {config['original_entrypoint']}")
191 print("=" * 70)
193 configure_virtuoso(config)
194 set_virt_env_vars(config)
196 if config["enable_write_permissions"]:
197 apply_write_permissions_async(config["dba_password"])
199 remaining_args = sys.argv[1:] if len(sys.argv) > 1 else []
200 print(f"Info: Executing original entrypoint: {config['original_entrypoint']}")
201 sys.stdout.flush()
202 sys.stderr.flush()
203 os.execve(config["original_entrypoint"], [config["original_entrypoint"]] + remaining_args, os.environ)
205 return 0
208if __name__ == "__main__":
209 sys.exit(main())