Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions test/e2e/fixtures/portable-profile-systemctl-shim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,51 @@
set -euo pipefail

runtime_dir="${XDG_RUNTIME_DIR:?}"
home_dir="${HOME:?}"
config_home="${XDG_CONFIG_HOME:-}"
[[ "$config_home" == /* ]] || config_home="${home_dir}/.config"
state_home="${XDG_STATE_HOME:-}"
[[ "$state_home" == /* ]] || state_home="${home_dir}/.local/state"
bin_home="${XDG_BIN_HOME:-}"
[[ "$bin_home" == /* ]] || bin_home="${home_dir}/.local/bin"
service_dir="${runtime_dir}/podman"
socket_path="${service_dir}/podman.sock"
backend_socket_path="${service_dir}/nemoclaw-podman-service.sock"
activator_pid_file="${runtime_dir}/nemoclaw-podman-socket-activator.pid"
service_pid_file="${runtime_dir}/nemoclaw-podman-service.pid"
log_file="${runtime_dir}/nemoclaw-podman-service.log"
gateway_service_name="nemoclaw-openshell-gateway"
gateway_unit_path="${config_home}/systemd/user/${gateway_service_name}.service"
gateway_binary_path="${bin_home}/openshell-gateway"
gateway_env_file="${config_home}/openshell/gateway.env"
gateway_tls_dir="${state_home}/openshell/tls"
gateway_state_dir="${state_home}/openshell/gateway"
gateway_pid_file="${runtime_dir}/nemoclaw-openshell-gateway.pid"
gateway_log_file="${runtime_dir}/nemoclaw-openshell-gateway.log"
gateway_environment_keys=(
CONTAINERS_CONF
DOCKER_HOST
OPENSHELL_DRIVERS
OPENSHELL_BIND_ADDRESS
OPENSHELL_SERVER_PORT
OPENSHELL_DISABLE_TLS
OPENSHELL_DISABLE_GATEWAY_AUTH
OPENSHELL_LOCAL_TLS_DIR
OPENSHELL_DB_URL
OPENSHELL_GRPC_ENDPOINT
OPENSHELL_SSH_GATEWAY_HOST
OPENSHELL_SSH_GATEWAY_PORT
OPENSHELL_DOCKER_NETWORK_NAME
OPENSHELL_DOCKER_SUPERVISOR_IMAGE
OPENSHELL_DOCKER_SUPERVISOR_BIN
OPENSHELL_PODMAN_SOCKET
OPENSHELL_GATEWAY_CONFIG
OPENSHELL_VM_DRIVER_STATE_DIR
OPENSHELL_DRIVER_DIR
NEMOCLAW_DOCKER_ENABLE_BIND_MOUNTS
NEMOCLAW_OPENSHELL_SANDBOX_NAMESPACE
NETAVARK_FW
)
process_identity_env="NEMOCLAW_PORTABLE_PROFILE_PROCESS_ID"
process_identity_failure_role="${NEMOCLAW_PODMAN_IDENTITY_FAILURE_ROLE:-}"
process_identity_failure_record="${NEMOCLAW_PODMAN_IDENTITY_FAILURE_RECORD:-}"
Expand Down Expand Up @@ -414,6 +453,139 @@ stop_runtime() {
rm -f "$socket_path" "$backend_socket_path"
}

validate_gateway_unit() {
if [[ ! -f "$gateway_unit_path" || -L "$gateway_unit_path" || ! -r "$gateway_unit_path" ]]; then
echo "Portable profile fixture requires the managed gateway user service at ${gateway_unit_path}." >&2
return 1
fi
if [[ "$(grep -Fxc '# NEMOCLAW_MANAGED_OPENSHELL_GATEWAY=1' "$gateway_unit_path" || true)" -ne 1 ]]; then
echo "Portable profile fixture rejected the foreign gateway user service at ${gateway_unit_path}." >&2
return 1
fi
if [[ "$(grep -Fxc "ExecStart=${gateway_binary_path}" "$gateway_unit_path" || true)" -ne 1 ]] \
|| [[ "$(grep -Fxc "ExecStartPre=${gateway_binary_path} generate-certs --output-dir \${OPENSHELL_LOCAL_TLS_DIR} --server-san host.openshell.internal" "$gateway_unit_path" || true)" -ne 1 ]] \
|| [[ "$(grep -Fxc 'StateDirectory=openshell/gateway' "$gateway_unit_path" || true)" -ne 1 ]] \
|| [[ "$(grep -Fxc 'Environment=OPENSHELL_LOCAL_TLS_DIR=%S/openshell/tls' "$gateway_unit_path" || true)" -ne 1 ]] \
|| [[ "$(grep -Fxc 'EnvironmentFile=-%E/openshell/gateway.env' "$gateway_unit_path" || true)" -ne 1 ]] \
|| [[ ! -x "$gateway_binary_path" || -L "$gateway_binary_path" ]]; then
echo "Portable profile fixture rejected the gateway user service identity at ${gateway_unit_path}." >&2
return 1
fi
}

load_gateway_environment() {
local key
for key in "${gateway_environment_keys[@]}"; do
unset "$key"
done
export OPENSHELL_LOCAL_TLS_DIR="$gateway_tls_dir"
[[ -e "$gateway_env_file" || -L "$gateway_env_file" ]] || return 0
if [[ ! -f "$gateway_env_file" || -L "$gateway_env_file" || ! -r "$gateway_env_file" ]]; then
echo "Portable profile fixture rejected the gateway environment file at ${gateway_env_file}." >&2
return 1
fi

local line value managed_key managed_key_candidate
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -n "$line" && "$line" != \#* ]] || continue
[[ "$line" == *=* ]] || {
echo "Portable profile fixture rejected an invalid gateway environment assignment." >&2
return 1
}
key="${line%%=*}"
value="${line#*=}"
managed_key=false
for managed_key_candidate in "${gateway_environment_keys[@]}"; do
if [[ "$key" == "$managed_key_candidate" ]]; then
managed_key=true
break
fi
done
if [[ "$managed_key" != true ]]; then
echo "Portable profile fixture rejected gateway environment key ${key}." >&2
return 1
fi
if [[ "$value" == \'* || "$value" == *\' ]]; then
if [[ "$value" != \'*\' || "${#value}" -lt 2 ]]; then
echo "Portable profile fixture rejected an invalid gateway environment value for ${key}." >&2
return 1
fi
value="${value:1:${#value}-2}"
fi
export "${key}=${value}"
done <"$gateway_env_file"
}

gateway_service_is_active() {
recorded_process_is_active "$gateway_pid_file" gateway
}

stop_gateway_service() {
stop_recorded_process "$gateway_pid_file" gateway
}

start_gateway_service() {
validate_gateway_unit
load_gateway_environment
install -d -m 700 "$OPENSHELL_LOCAL_TLS_DIR" "$gateway_state_dir"
if ! "$gateway_binary_path" generate-certs --output-dir "$OPENSHELL_LOCAL_TLS_DIR" \
--server-san host.openshell.internal >>"$gateway_log_file" 2>&1; then
cat "$gateway_log_file" >&2 || true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return 1
fi

local gateway_identity gateway_pid gateway_pid_file_tmp
gateway_identity="gateway:$(node -e 'process.stdout.write(require("node:crypto").randomBytes(16).toString("hex"))')"
NEMOCLAW_PORTABLE_PROFILE_PROCESS_ID="$gateway_identity" nohup "$gateway_binary_path" \
>>"$gateway_log_file" 2>&1 </dev/null &
gateway_pid=$!
if acquire_process_identity "$gateway_pid" "$gateway_identity"; then
gateway_pid_file_tmp="${gateway_pid_file}.$$.tmp"
printf '%s\t%s\t%s\n' "$gateway_pid" "$acquired_process_start_time" "$gateway_identity" \
>"$gateway_pid_file_tmp"
chmod 600 "$gateway_pid_file_tmp"
mv "$gateway_pid_file_tmp" "$gateway_pid_file"
else
stop_unrecorded_process "$gateway_pid" "$gateway_identity" "" || true
echo "Portable profile fixture could not create the gateway process identity record." >&2
cat "$gateway_log_file" >&2 || true
return 1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
fi

if gateway_service_is_active; then
return 0
fi
rm -f "$gateway_pid_file"
cat "$gateway_log_file" >&2 || true
return 1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
}

restart_gateway_service() {
validate_gateway_unit
stop_gateway_service
start_gateway_service
}

print_gateway_identity() {
validate_gateway_unit
printf 'FragmentPath=%s\n' "$gateway_unit_path"
printf 'ExecStart={ path=%s ; argv[]=%s ; }\n' "$gateway_binary_path" "$gateway_binary_path"
}

print_active_gateway_identity() {
print_gateway_identity
local status gateway_pid=0 active_state=inactive
if gateway_service_is_active; then
gateway_pid="$recorded_pid"
active_state=active
else
status=$?
[[ "$status" -eq 1 ]] || return "$status"
fi
printf 'ActiveState=%s\n' "$active_state"
printf 'MainPID=%s\n' "$gateway_pid"
}

refresh_service() {
local status
if service_is_active; then
Expand Down Expand Up @@ -906,6 +1078,74 @@ if [[ "$#" -eq 4 &&
exit 0
fi

if [[ "$#" -eq 2 &&
"$1" == "--user" &&
"$2" == "daemon-reload" ]]; then
validate_gateway_unit
exit 0
fi

if [[ "$#" -eq 5 &&
"$1" == "--user" &&
"$2" == "show" &&
"$3" == "$gateway_service_name" &&
"$4" == "--property=FragmentPath" &&
"$5" == "--property=ExecStart" ]]; then
print_gateway_identity
exit 0
fi

if [[ "$#" -eq 7 &&
"$1" == "--user" &&
"$2" == "show" &&
"$3" == "$gateway_service_name" &&
"$4" == "--property=FragmentPath" &&
"$5" == "--property=ExecStart" &&
"$6" == "--property=ActiveState" &&
"$7" == "--property=MainPID" ]]; then
print_active_gateway_identity
exit 0
fi

if [[ "$#" -eq 3 &&
"$1" == "--user" &&
"$2" == "stop" &&
"$3" == "$gateway_service_name" ]]; then
validate_gateway_unit
stop_gateway_service
exit 0
fi

if [[ "$#" -eq 3 &&
"$1" == "--user" &&
"$2" == "enable" &&
"$3" == "$gateway_service_name" ]]; then
validate_gateway_unit
exit 0
fi

if [[ "$#" -eq 3 &&
"$1" == "--user" &&
"$2" == "restart" &&
"$3" == "$gateway_service_name" ]]; then
restart_gateway_service
exit 0
fi

if [[ "$#" -eq 4 &&
"$1" == "--user" &&
"$2" == "is-active" &&
"$3" == "--quiet" &&
"$4" == "$gateway_service_name" ]]; then
if gateway_service_is_active; then
exit 0
else
status=$?
[[ "$status" -eq 1 ]] || exit "$status"
fi
exit 3
fi

if [[ "$#" -eq 3 &&
"$1" == "--user" &&
"$2" == "try-restart" &&
Expand Down
1 change: 1 addition & 0 deletions test/e2e/fixtures/portable-profile-systemctl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const PROCESS_QUERY_TIMEOUT_MS = 5_000;
const FIXTURE_PID_FILES = [
["nemoclaw-podman-socket-activator.pid", "activator"],
["nemoclaw-podman-service.pid", "service"],
["nemoclaw-openshell-gateway.pid", "gateway"],
] as const;
const FIXTURE_SOCKET_FILES = ["podman.sock", "nemoclaw-podman-service.sock"] as const;

Expand Down
Loading
Loading