@@ -20,6 +20,297 @@ set -eu
2020set -x
2121set -o pipefail
2222
23+ GC_PATH=" /opt/genesis_core"
24+ GC_CFG_DIR=/etc/genesis_core
25+ VENV_PATH=" $GC_PATH /.venv"
26+
27+ # Disk auto-provisioning for Genesis data directory.
28+ #
29+ # Goal:
30+ # - Detect the "secondary" disk (any disk that is not the root disk)
31+ # - If it has no partitions: create a single GPT partition
32+ # - If the partition has no filesystem: format it as ext4
33+ # - Ensure it is mounted at /var/lib/genesis/data
34+ # - Ensure a persistent /etc/fstab entry using UUID
35+ #
36+ # This block is intended to be idempotent and safe to run on every boot.
37+
38+ log () {
39+ echo " [genesis-bootstrap] $* "
40+ }
41+
42+ host_mountpoint () {
43+ if command -v nsenter > /dev/null 2>&1 ; then
44+ nsenter -t 1 -m -- mountpoint -q " $1 "
45+ return $?
46+ fi
47+
48+ mountpoint -q " $1 "
49+ }
50+
51+ # Identify the block device that backs the root filesystem.
52+ # Example output:
53+ # [genesis-bootstrap] root source: /dev/vda1
54+ # [genesis-bootstrap] root disk: vda
55+ ROOT_SRC=" $( findmnt -n -o SOURCE / || true) "
56+ ROOT_DISK=" "
57+ if [[ -n " ${ROOT_SRC} " ]]; then
58+ ROOT_DISK=" $( lsblk -no PKNAME " ${ROOT_SRC} " 2> /dev/null || true) "
59+ fi
60+ log " root source: ${ROOT_SRC:- <unknown>} "
61+ log " root disk: ${ROOT_DISK:- <unknown>} "
62+
63+ # Pick the first disk device that is not the root disk.
64+ # Example output:
65+ # [genesis-bootstrap] secondary disk candidate: vdb
66+ SECOND_DISK=" $( lsblk -dn -o NAME,TYPE | awk -v root_disk=" ${ROOT_DISK} " ' $2=="disk" && $1!=root_disk {print $1; exit}' ) "
67+ log " secondary disk candidate: ${SECOND_DISK:- <none>} "
68+
69+ if [[ -n " ${SECOND_DISK} " ]]; then
70+ DISK_DEV=" /dev/${SECOND_DISK} "
71+
72+ # Count partitions on the selected disk.
73+ # Example output:
74+ # [genesis-bootstrap] /dev/vdb partitions: 0
75+ PART_COUNT=" $( lsblk -nr -o TYPE " ${DISK_DEV} " | awk ' $1=="part" {c++} END {print c+0}' ) "
76+ log " ${DISK_DEV} partitions: ${PART_COUNT} "
77+
78+ # Create a single partition when there are no partitions yet.
79+ # Example output:
80+ # [genesis-bootstrap] creating GPT partition table and one partition on /dev/vdb
81+ if [[ " ${PART_COUNT} " -eq 0 ]]; then
82+ log " creating GPT partition table and one partition on ${DISK_DEV} "
83+ sfdisk --label gpt " ${DISK_DEV} " << 'EOF '
84+ ,,
85+ EOF
86+ partprobe " ${DISK_DEV} " || true
87+ udevadm settle || true
88+ else
89+ log " ${DISK_DEV} already has partitions; skipping partition creation"
90+ fi
91+
92+ # Pick the first partition on the disk.
93+ # Example output:
94+ # [genesis-bootstrap] selected partition: /dev/vdb1
95+ PART_NAME=" $( lsblk -nr -o NAME,TYPE " ${DISK_DEV} " | awk ' $2=="part" {print $1; exit}' ) "
96+ if [[ -n " ${PART_NAME} " ]]; then
97+ PART_DEV=" /dev/${PART_NAME} "
98+ log " selected partition: ${PART_DEV} "
99+
100+ # Detect filesystem type; create ext4 if unformatted.
101+ # Example output:
102+ # [genesis-bootstrap] filesystem on /dev/vdb1: <none>
103+ # [genesis-bootstrap] formatting /dev/vdb1 as ext4
104+ FS_TYPE=" $( blkid -o value -s TYPE " ${PART_DEV} " 2> /dev/null || true) "
105+ log " filesystem on ${PART_DEV} : ${FS_TYPE:- <none>} "
106+ if [[ -z " ${FS_TYPE} " ]]; then
107+ log " formatting ${PART_DEV} as ext4"
108+ mkfs.ext4 -F " ${PART_DEV} "
109+ FS_TYPE=" ext4"
110+ else
111+ log " ${PART_DEV} already has filesystem '${FS_TYPE} '; skipping mkfs"
112+ fi
113+
114+ # Mount and persist only if it is ext4.
115+ if [[ " ${FS_TYPE} " == " ext4" ]]; then
116+ MOUNTPOINT=" /var/lib/genesis/data"
117+
118+ # Ensure mountpoint exists.
119+ # Example output:
120+ # [genesis-bootstrap] ensuring mountpoint exists: /var/lib/genesis/data
121+ mkdir -p " ${MOUNTPOINT} "
122+ log " ensuring mountpoint exists: ${MOUNTPOINT} "
123+
124+ # Ensure /etc/fstab has the correct UUID-based entry.
125+ # Example output:
126+ # [genesis-bootstrap] partition UUID: 1234-...
127+ # [genesis-bootstrap] updating /etc/fstab entry for /var/lib/genesis/data
128+ UUID=" $( blkid -o value -s UUID " ${PART_DEV} " 2> /dev/null || true) "
129+ if [[ -n " ${UUID} " ]]; then
130+ log " partition UUID: ${UUID} "
131+ if grep -qs " [[:space:]]${MOUNTPOINT} [[:space:]]" /etc/fstab; then
132+ if ! grep -qs " ^[[:space:]]*UUID=${UUID} [[:space:]].*[[:space:]]${MOUNTPOINT} [[:space:]]" /etc/fstab; then
133+ log " updating /etc/fstab entry for ${MOUNTPOINT} "
134+ TMP_FSTAB=" $( mktemp) "
135+ awk -v mp=" ${MOUNTPOINT} " ' !(($2==mp)) {print}' /etc/fstab > " ${TMP_FSTAB} "
136+ printf ' %s\n' " UUID=${UUID} ${MOUNTPOINT} ext4 defaults,nofail 0 2" >> " ${TMP_FSTAB} "
137+ mv " ${TMP_FSTAB} " /etc/fstab
138+
139+ # Reload systemd units generated from fstab so the mount can work on first run.
140+ # Example output:
141+ # [genesis-bootstrap] running: systemctl daemon-reload
142+ if command -v systemctl > /dev/null 2>&1 ; then
143+ log " running: systemctl daemon-reload"
144+ systemctl daemon-reload || true
145+ fi
146+ else
147+ log " /etc/fstab already has the correct UUID entry for ${MOUNTPOINT} "
148+ fi
149+ else
150+ log " adding /etc/fstab entry for ${MOUNTPOINT} "
151+ printf ' %s\n' " UUID=${UUID} ${MOUNTPOINT} ext4 defaults,nofail 0 2" >> /etc/fstab
152+
153+ # Reload systemd units generated from fstab so the mount can work on first run.
154+ # Example output:
155+ # [genesis-bootstrap] running: systemctl daemon-reload
156+ if command -v systemctl > /dev/null 2>&1 ; then
157+ log " running: systemctl daemon-reload"
158+ systemctl daemon-reload || true
159+ fi
160+ fi
161+ else
162+ log " could not determine UUID for ${PART_DEV} ; skipping fstab update"
163+ fi
164+
165+ # Mount if not mounted yet.
166+ # Example output:
167+ # [genesis-bootstrap] mounting /var/lib/genesis/data
168+ if ! host_mountpoint " ${MOUNTPOINT} " ; then
169+ log " mounting ${MOUNTPOINT} "
170+ mount " ${MOUNTPOINT} " || mount " ${PART_DEV} " " ${MOUNTPOINT} "
171+
172+ # Some systemd units may run with an isolated mount namespace (e.g. PrivateMounts=yes).
173+ # In that case, a mount performed here may not be visible from the host namespace.
174+ # If it is still not mounted, retry in PID 1 mount namespace.
175+ # Example output:
176+ # [genesis-bootstrap] mount not visible after mount; retrying in PID 1 mount namespace
177+ if ! host_mountpoint " ${MOUNTPOINT} " ; then
178+ if command -v nsenter > /dev/null 2>&1 ; then
179+ log " mount not visible from host namespace; retrying in PID 1 mount namespace"
180+ nsenter -t 1 -m -- mount " ${MOUNTPOINT} " || nsenter -t 1 -m -- mount " ${PART_DEV} " " ${MOUNTPOINT} " || true
181+ fi
182+ fi
183+
184+ if host_mountpoint " ${MOUNTPOINT} " ; then
185+ log " mounted successfully: ${MOUNTPOINT} "
186+ else
187+ log " mount failed or not visible: ${MOUNTPOINT} "
188+ fi
189+ else
190+ log " ${MOUNTPOINT} is already mounted; skipping"
191+ fi
192+ else
193+ log " filesystem on ${PART_DEV} is '${FS_TYPE} ', not ext4; skipping mount setup"
194+ fi
195+ else
196+ log " no partition found on ${DISK_DEV} ; skipping"
197+ fi
198+ else
199+ log " no secondary disk detected; skipping data disk provisioning"
200+ fi
201+
202+
203+ # Execution can continue only if the secondary disk was detected and is mounted at /var/lib/genesis/data.
204+ if [[ -z " ${SECOND_DISK} " ]]; then
205+ echo " [genesis-bootstrap] ERROR: secondary disk was not detected; refusing to continue" >&2
206+ exit 1
207+ fi
208+
209+ if ! host_mountpoint " /var/lib/genesis/data" ; then
210+ echo " [genesis-bootstrap] ERROR: /var/lib/genesis/data is not mounted; refusing to continue" >&2
211+ exit 1
212+ fi
213+
214+ # PostgreSQL data relocation and genesis_core DB bootstrap.
215+ #
216+ # Goal:
217+ # - Keep PostgreSQL packages installation logic in install.sh
218+ # - Ensure PostgreSQL data files live under /var/lib/genesis/data so the disk can be moved
219+
220+ if host_mountpoint " /var/lib/genesis/data" ; then
221+ if command -v psql > /dev/null 2>&1 ; then
222+ GC_PG_USER=" ${GC_PG_USER:- genesis_core} "
223+ GC_PG_PASS=" ${GC_PG_PASS:- genesis_core} "
224+ GC_PG_DB=" ${GC_PG_DB:- genesis_core} "
225+
226+ PG_VERSION_DIR=" "
227+ if [[ -d /etc/postgresql ]]; then
228+ PG_VERSION_DIR=" $( ls -1 /etc/postgresql 2> /dev/null | sort -V | tail -n 1 || true) "
229+ fi
230+
231+ if [[ -n " ${PG_VERSION_DIR} " ]]; then
232+ PG_CONF_DIR=" /etc/postgresql/${PG_VERSION_DIR} /main"
233+ PG_CONF_FILE=" ${PG_CONF_DIR} /postgresql.conf"
234+ OLD_PGDATA=" /var/lib/postgresql/${PG_VERSION_DIR} /main"
235+ NEW_PGDATA=" /var/lib/genesis/data/postgresql/${PG_VERSION_DIR} /main"
236+
237+ log " postgresql version detected: ${PG_VERSION_DIR} "
238+ log " postgresql old data dir: ${OLD_PGDATA} "
239+ log " postgresql new data dir: ${NEW_PGDATA} "
240+
241+ if [[ -f " ${PG_CONF_FILE} " ]]; then
242+ if ! grep -qs " ^[[:space:]]*data_directory[[:space:]]*=[[:space:]]*'${NEW_PGDATA} '" " ${PG_CONF_FILE} " ; then
243+ log " configuring PostgreSQL data_directory to ${NEW_PGDATA} "
244+
245+ if command -v systemctl > /dev/null 2>&1 ; then
246+ systemctl stop postgresql || true
247+ fi
248+
249+ mkdir -p " ${NEW_PGDATA} "
250+ chown -R postgres:postgres " /var/lib/genesis/data/postgresql" || true
251+
252+ if [[ -d " ${OLD_PGDATA} " && ! -f " ${NEW_PGDATA} /PG_VERSION" ]]; then
253+ log " copying PostgreSQL data directory to ${NEW_PGDATA} "
254+ if command -v rsync > /dev/null 2>&1 ; then
255+ rsync -aHAX --numeric-ids " ${OLD_PGDATA} /" " ${NEW_PGDATA} /"
256+ else
257+ cp -a " ${OLD_PGDATA} /." " ${NEW_PGDATA} /"
258+ fi
259+ chown -R postgres:postgres " ${NEW_PGDATA} " || true
260+ else
261+ log " PostgreSQL data directory already present under ${NEW_PGDATA} ; skipping copy"
262+ fi
263+
264+ if grep -qs " ^[[:space:]]*data_directory[[:space:]]*=" " ${PG_CONF_FILE} " ; then
265+ sed -i " s|^[[:space:]]*data_directory[[:space:]]*=.*|data_directory = '${NEW_PGDATA} '|" " ${PG_CONF_FILE} "
266+ else
267+ printf ' %s\n' " data_directory = '${NEW_PGDATA} '" >> " ${PG_CONF_FILE} "
268+ fi
269+
270+ if command -v systemctl > /dev/null 2>&1 ; then
271+ systemctl daemon-reload || true
272+ systemctl start postgresql || true
273+ fi
274+ else
275+ log " PostgreSQL is already configured to use ${NEW_PGDATA} "
276+ fi
277+
278+ if command -v systemctl > /dev/null 2>&1 ; then
279+ systemctl start postgresql || true
280+ fi
281+ else
282+ log " PostgreSQL config not found at ${PG_CONF_FILE} ; skipping PostgreSQL relocation"
283+ fi
284+ else
285+ log " PostgreSQL version directory not found under /etc/postgresql; skipping PostgreSQL relocation"
286+ fi
287+ else
288+ log " psql is not available; skipping PostgreSQL relocation"
289+ fi
290+ else
291+ log " /var/lib/genesis/data is not mounted; skipping PostgreSQL relocation"
292+ fi
293+
294+ # Additional PostgreSQL configuration
295+ sudo -u postgres psql -c " ALTER SYSTEM SET io_method = 'io_uring';"
296+
297+ # Apply migrations
298+ source " $VENV_PATH /bin/activate"
299+ # TODO(akremenetsky): Database configuration parameters should be taken
300+ # from persistent configuration file.
301+ ra-apply-migration --config-dir " $GC_CFG_DIR /" --path " $GC_PATH /migrations"
302+
303+ # Enable genesis core services
304+ sudo systemctl enable --now \
305+ gc-user-api \
306+ gc-orch-api \
307+ gc-status-api \
308+ gc-boot-api \
309+ gc-gservice \
310+ gc-core-agent \
311+ genesis-universal-agent \
312+ genesis-universal-scheduler
313+
23314# Perform the bootstrap of GC
24315gc-bootstrap
25316
0 commit comments