-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
493 lines (420 loc) · 16.1 KB
/
install.sh
File metadata and controls
493 lines (420 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
#!/bin/sh
# install.sh
set -eu
if (set -o pipefail 2>/dev/null); then
set -o pipefail
fi
# Color codes for output.
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color.
# Default ports.
DEFAULT_HTTP_PORT=80
DEFAULT_HTTPS_PORT=443
# Default ports for user access without root privileges.
# Used for podman or rootless docker installations.
DEFAULT_USER_HTTP_PORT=8080
DEFAULT_USER_HTTPS_PORT=8443
# Initialize variables with default values
HTTP_PORT=$DEFAULT_HTTP_PORT
HTTPS_PORT=$DEFAULT_HTTPS_PORT
ENTERPRISE_INSTALL=0
# Did the user specify a custom HTTP or HTTPS port?
HTTP_PORT_SET=0
HTTPS_PORT_SET=0
EXISITING_ENV=0
USE_SSL=0
USE_PODMAN=0
ROOTLESS_INSTALL=0
DOCKER_COMPOSE_VERSION=2
DOCKER_COMPOSE="docker compose"
# Minimum required Docker Compose version.
MIN_COMPOSE_VERSION="1.27.0"
DOWNLOAD_URL="https://github.com/Rocketgraphai/rocketgraph"
# Log functions.
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
parse_args() {
# Parse command line options
while [ $# -gt 0 ]; do
case "$1" in
--http-port)
if [ $# -ge 2 ] && [ -n "$2" ] && [ "${2#-}" = "$2" ]; then
HTTP_PORT=$2
HTTP_PORT_SET=1
shift 2
else
log_error "Error: Argument for $1 is missing"
exit 1
fi
;;
--https-port)
if [ $# -ge 2 ] && [ -n "$2" ] && [ "${2#-}" = "$2" ]; then
HTTPS_PORT=$2
HTTPS_PORT_SET=1
shift 2
else
log_error "Error: Argument for $1 is missing"
exit 1
fi
;;
--enterprise)
ENTERPRISE_INSTALL=1
shift 1
;;
--use-podman)
USE_PODMAN=1
shift
;;
-h|--help)
# Default ports may change based on the container tool used.
detect_container_tool
if [ "$ROOTLESS_INSTALL" -eq 1 ]; then
http_port_default=$DEFAULT_USER_HTTP_PORT
https_port_default=$DEFAULT_USER_HTTPS_PORT
else
http_port_default=$DEFAULT_HTTP_PORT
https_port_default=$DEFAULT_HTTPS_PORT
fi
echo "Usage: $0 [OPTIONS]"
echo "Available options:"
echo " --http-port PORT Specify custom HTTP port (default: $http_port_default)"
echo " --https-port PORT Specify custom HTTPS port (default: $https_port_default)"
echo " --enterprise Enable multi-user enterprise installation"
echo " --use-podman Use Podman instead of Docker, falling back to Docker if Podman is not installed"
echo " -h, --help Show this help message"
exit 0
;;
*)
log_error "Unknown option: $1"
echo "Use -h or --help to see available options"
exit 1
;;
esac
done
}
portable_sed_i() {
# Usage: portable_sed_i 's|pattern|replacement|' filename
local expr="$1"
local file="$2"
if sed --version >/dev/null 2>&1; then
# GNU sed
sed -i "$expr" "$file"
else
# BSD sed (macOS)
sed -i '' "$expr" "$file"
fi
}
# Function to check if a command exists.
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to check if a port is in use.
port_in_use() {
lsof -iTCP:"$1" -P -n | grep LISTEN >/dev/null 2>&1
}
# Function to compare versions.
version_ge() {
[ "$(printf '%s\n' "$@" | sort -V | head -n 1)" = "$1" ]
}
# Function to run command with timeout.
run_with_timeout() {
local timeout=$1
shift
local tmpfile
tmpfile=$(mktemp)
# Run the command in the background, capture stdout/stderr
"$@" >"$tmpfile" 2>&1 &
local pid=$!
local count=0
while [ $count -lt "$timeout" ] && kill -0 "$pid" 2>/dev/null; do
sleep 1
count=$((count + 1))
done
if kill -0 "$pid" 2>/dev/null; then
kill -TERM "$pid"
wait "$pid" 2>/dev/null
_run_with_timeout_output=$(cat "$tmpfile")
rm -f "$tmpfile"
return 124 # Timeout
fi
wait "$pid"
local code=$?
_run_with_timeout_output=$(cat "$tmpfile")
rm -f "$tmpfile"
return $code
}
# Check system requirements.
check_requirements() {
local container_tool="$1"
local compose_tool="$2"
log_info "Checking system requirements."
# Check if Docker/Podman is installed.
if ! command_exists $container_tool; then
log_error "$container_tool is not installed. Please install $container_tool first."
[ "$container_tool" = "docker" ] && log_info "Visit https://docs.docker.com/get-docker/ for installation instructions."
exit 1
fi
# Check that Docker/Podman is running and the user has permissions to use it.
if ! run_with_timeout 10 "$container_tool" ps; then
log_error "$container_tool is either not running or this user doesn't have permission to use $container_tool. Make sure $container_tool is started. If $container_tool is running, it is likely the user doesn't have permission to use $container_tool. Either run the script as root or contact your system administrator. Output:"
printf '%s\n' "$_run_with_timeout_output" | while IFS= read -r line; do
log_error " $line"
done
exit 1
fi
# Check if Docker Compose or Podman-Compose is installed.
if ! run_with_timeout 10 $compose_tool version; then
log_error "$container_tool is installed but $compose_tool is not. Please install $compose_tool first. Output:"
printf '%s\n' "$_run_with_timeout_output" | while IFS= read -r line; do
log_error " $line"
done
[ "$container_tool" = "docker" ] && log_info "Visit https://docs.docker.com/compose/install/ for installation instructions."
exit 1
fi
if [ "$container_tool" = "docker" ]; then
# Check Docker Compose version.
compose_version=$($compose_tool version --short 2>/dev/null || docker-compose --version | awk '{print $3}')
compose_version=$(echo "$compose_version" | sed 's/[^0-9.]*//g')
if ! version_ge "$MIN_COMPOSE_VERSION" "$compose_version"; then
log_error "Docker Compose version $compose_version is too old. Please install Docker Compose version $MIN_COMPOSE_VERSION or higher."
exit 1
fi
fi
# Check curl.
if ! command_exists curl; then
log_error "curl is not installed. Please install curl first."
exit 1
fi
# Check for sufficient disk space (at least 1GB free).
if [ "$(df -P . | awk 'NR==2 {print $4}')" -lt 1048576 ]; then
log_error "Insufficient disk space. Please ensure at least 1GB of free space."
exit 1
fi
# Check network connectivity.
if ! curl -s --head --request GET ${DOWNLOAD_URL} | grep "200" >/dev/null; then
log_error "Network connectivity issue. Unable to reach ${DOWNLOAD_URL}."
exit 1
fi
}
# Create installation directory (use current directory).
check_installation_dir() {
local install_dir="$(pwd)"
log_info "Using installation directory at ${install_dir}/."
if [ ! -w "${install_dir}" ]; then
log_error "Write permissions required in ${install_dir}".
exit 1
fi
}
# Download config files.
download_config() {
local url="https://raw.githubusercontent.com/Rocketgraphai/rocketgraph/main"
log_info "Downloading config files from ${DOWNLOAD_URL}/."
# Download docker-compose.yml.
if ! curl -sSL "${url}/docker-compose.yml" -o docker-compose.yml; then
log_error "Failed to download docker-compose.yml."
exit 1
fi
# Remove unsupported 'name' key for Docker Compose v1.
# This is set via the COMPOSE_PROJECT_NAME environment variable.
if [ "${DOCKER_COMPOSE_VERSION}" = "1" ]; then
log_info "Docker compose v1 detected: updating the yml to be compatible with v1."
sed -i '/^name:[[:space:]]*\(${COMPOSE_PROJECT_NAME:-rocketgraph}\|rocketgraph\)/{N;/\n[[:space:]]*$/d;}' docker-compose.yml
fi
# Download env.template.
if ! curl -sSL "${url}/env.template" -o env.template; then
log_warn "Failed to download an env.template file."
exit 1
fi
if [ -f ".env" ]; then
EXISITING_ENV=1
changes=0
log_info "Checking for potentially new keys added to env.template since initial install."
log_info "This may help identify missing entries in .env, but some may be false positives."
while IFS= read -r line; do
case "$line" in
''|\#*) continue ;; # skip empty lines and comments
esac
key=$(printf "%s" "$line" | cut -d '=' -f 1)
if ! grep -q "^$key=" .env; then
log_warn "Key '$key' is present in env.template but not found in .env. If this key is new, consider adding it:"
log_warn "$line"
changes=1
fi
done < env.template
rm -f env.template
[ "$changes" -eq 0 ] && log_info "No new keys were detected."
else
log_info "Creating .env file from env.template."
mv env.template .env
fi
# Set appropriate permissions.
if ! chmod 600 .env >/dev/null 2>&1; then
log_warn "Failed to set permissions on .env file."
fi
if ! chmod 644 docker-compose.yml >/dev/null 2>&1; then
log_warn "Failed to set permissions on docker-compose.yml file."
fi
}
# Set the values of variables needed by the script. These get a value from the
# .env file or a default value. Note that these variables do NOT affect the
# docker containers. They get their values strictly from the .env file.
set_variables() {
if [ "$EXISITING_ENV" -eq 1 ]; then
log_info "Existing .env file found. Ignoring any new configuration values passed to the script."
log_info "To apply new values, edit the .env file manually."
return
fi
log_info "Setting up .env configuration file."
if grep -q '^#MC_PORT=' .env && [ "$HTTP_PORT" != "$DEFAULT_HTTP_PORT" ]; then
log_info "Using non-standard HTTP_PORT=${HTTP_PORT}"
portable_sed_i "s|^#MC_PORT=.*|MC_PORT=${HTTP_PORT}|" .env
fi
if grep -q '^#MC_SSL_PORT=' .env && [ "$HTTPS_PORT" != "$DEFAULT_HTTPS_PORT" ]; then
log_info "Using non-standard HTTPS_PORT=${HTTPS_PORT}"
portable_sed_i "s|^#MC_SSL_PORT=.*|MC_SSL_PORT=${HTTPS_PORT}|" .env
fi
# Determine if SSL is being used to serve Mission Control.
if grep -q '^MC_SSL_PUBLIC_CERT=' .env &&
grep -q '^MC_SSL_PRIVATE_KEY=' .env; then
USE_SSL=1
fi
# Check if xgt.lic license file exists
if [ -f xgt.lic ]; then
log_info "Custom license file found."
portable_sed_i "s|^#XGT_LICENSE_FILE=.*|XGT_LICENSE_FILE=$(pwd)/xgt.lic|" .env
fi
# Comment out empty authorization list to enable multi-user auth.
if [ $ENTERPRISE_INSTALL -eq 1 ]; then
portable_sed_i "s|^XGT_AUTH_TYPES=|#XGT_AUTH_TYPES=|" .env
fi
}
deploy_containers() {
local container_tool="$1"
local compose_tool="$2"
local arch="$3"
if [ "$arch" = "ppc64le" ]; then
export MC_MONGODB_IMAGE=ibmcom/mongodb-ppc64le
portable_sed_i 's|^#MC_MONGODB_IMAGE=.*|MC_MONGODB_IMAGE=ibmcom/mongodb-ppc64le|' .env
fi
log_info "Pulling latest container images."
$compose_tool pull
if [ "$arch" = "ppc64le" ]; then
# Ensure volume exists
if ! $container_tool volume inspect rocketgraph_mongodb-data >/dev/null 2>&1; then
log_info "Creating MongoDB volume..."
if ! $container_tool volume create rocketgraph_mongodb-data >/dev/null; then
log_error "Failed to create MongoDB volume."
exit 1
fi
fi
# Fix permissions on the volume for MongoDB
log_info "Setting correct permissions on MongoDB volume..."
if ! $container_tool unshare chown -R 999:999 "$(podman volume inspect rocketgraph_mongodb-data -f '{{.Mountpoint}}')" >/dev/null; then
log_warn "Failed to set permissions on MongoDB volume."
fi
# Check if there are existing containers that need to be removed
if $container_tool ps -a --format "{{.Names}}" | grep -q "rocketgraph_"; then
log_info "Removing existing Rocketgraph containers..."
$compose_tool down >/dev/null 2>&1
fi
fi
log_info "Starting containers."
set +e
$compose_tool up -d
# Check if the command failed
if [ $? -ne 0 ]; then
log_error "If a port is already in use, you may need to change it in the .env file (e.g., MC_PORT (default: 80), MC_SSL_PORT (default: 443), etc.), then rerun the install."
exit 1 # Exit the script with an error code
fi
set -e
if [ "$container_tool" = "podman" ]; then
if ! loginctl enable-linger >/dev/null 2>&1; then
log_warn "Failed to enable linger for user sessions."
fi
fi
# Try to extract templates from a running container first
container_id=$($container_tool ps --filter "ancestor=rocketgraph/mission-control-backend:latest" --format "{{.ID}}" | head -n 1)
if [ -n "$container_id" ]; then
if $container_tool cp "${container_id}:/app/templates" ./ >/dev/null 2>&1; then
log_info "Site-config templates extracted successfully from running container."
else
log_info "No templates found or failed to copy from running container. Skipping."
fi
else
# Fall back to running a temporary container
if $container_tool run --rm -v "$(pwd):/output" rocketgraph/mission-control-backend:latest \
sh -c 'cp -r /app/templates /output/' >/dev/null 2>&1; then
log_info "Site-config templates extracted successfully from fresh container."
else
log_info "No site-config templates found in image or extraction failed."
fi
fi
}
detect_container_tool() {
command_exists docker && has_docker=1 || has_docker=0
command_exists podman && has_podman=1 || has_podman=0
if [ "$USE_PODMAN" = "1" ] && [ "$has_docker" = "1" ] && [ "$has_podman" = "0" ]; then
USE_PODMAN=0
log_warn "Podman is not installed. Docker found. Falling back to Docker."
fi
if [ "$USE_PODMAN" = "0" ] && [ "$has_docker" = "0" ] && [ "$has_podman" = "1" ]; then
USE_PODMAN=1
log_info "Docker is not installed. Podman found. Falling back to Podman."
fi
if [ "$USE_PODMAN" = "1" ] || docker info --format '{{.SecurityOptions}}' 2>/dev/null | grep -q rootless; then
ROOTLESS_INSTALL=1
# Update ports if the user didn't specify them.
if [ "$HTTP_PORT_SET" = "0" ]; then
HTTP_PORT=$DEFAULT_USER_HTTP_PORT
fi
if [ "$HTTPS_PORT_SET" = "0" ]; then
HTTPS_PORT=$DEFAULT_USER_HTTPS_PORT
fi
log_info "Rootless installation detected."
fi
}
# Main installation process.
main() {
# Handle command line arguments.
parse_args "$@"
log_info "Starting installation process."
detect_container_tool
arch=$(uname -m)
if [ "$USE_PODMAN" = "1" ]; then
check_requirements podman podman-compose
else
if command_exists docker && docker compose version >/dev/null 2>&1; then
DOCKER_COMPOSE="docker compose"
else
DOCKER_COMPOSE="docker-compose"
DOCKER_COMPOSE_VERSION=1
fi
check_requirements docker "$DOCKER_COMPOSE"
fi
check_installation_dir
download_config
set_variables
if [ "$USE_PODMAN" = "1" ]; then
deploy_containers podman podman-compose $arch
else
deploy_containers docker "$DOCKER_COMPOSE" $arch
fi
log_info "Installation completed successfully!"
if [ "$USE_SSL" -eq 1 ]; then
log_info "Mission Control is now running at https://localhost:${HTTPS_PORT}"
else
log_info "Mission Control is now running at http://localhost:${HTTP_PORT}"
fi
if [ "$USE_PODMAN" = "1" ]; then
log_info "To check the status, run: podman-compose ps"
log_info "To view logs, run: podman-compose logs"
else
log_info "To check the status, run: $DOCKER_COMPOSE ps"
log_info "To view logs, run: $DOCKER_COMPOSE logs"
fi
}
# Run main function.
main "$@"