install.bats
Rama master · réplica de lectura del repositorio original.
#!/usr/bin/env bats
load install.helpers
setup() {
setup_install_env
stub_bin id 'echo 0'
skip_enrollment_wait
export SERVER_ADDRESS="https://cloud.example.test"
export AGENT_VERSION="v0.0.0-test"
export AGENT_IMAGE="shellhubio/agent:v0.0.0-test"
export INSTALL_DIR="$BATS_TEST_TMPDIR/usr-local-bin"
export TMP_DIR="$BATS_TEST_TMPDIR/tmp"
export AGENT_KEY="$BATS_TEST_TMPDIR/shellhub.key"
export PRIVATE_KEY="/host$AGENT_KEY"
export PROC_VERSION="$BATS_TEST_TMPDIR/proc-version"
export OS_RELEASE="$BATS_TEST_TMPDIR/os-release"
mkdir -p "$INSTALL_DIR" "$TMP_DIR"
echo "Linux version 6.8.0-generic" > "$PROC_VERSION"
echo 'NAME="Ubuntu"' > "$OS_RELEASE"
RUNTIME=docker
}
skip_enrollment_wait() {
touch "$BATS_TEST_TMPDIR/shellhub.key"
stub_bin sleep 'exit 0'
}
stub_agent_log() {
cat > "$BATS_TEST_TMPDIR/agent.log"
stub_bin agent-log "cat '$BATS_TEST_TMPDIR/agent.log'"
}
stub_runtime() {
stub_bin "$1" "echo \"$1 \$*\" >> \"\$CALLS\"; ${2:+$2; }[ \"\$1\" = ps ] && echo shellhub; exit 0"
}
stub_container_logs() {
stub_runtime "$1" "[ \"\$1\" = logs ] && cat '$BATS_TEST_TMPDIR/agent.log'"
}
stub_journal() {
stub_bin journalctl "cat '$BATS_TEST_TMPDIR/agent.log'"
}
with_tenant() {
export TENANT_ID="00000000-0000-4000-a000-000000000000"
}
as_non_root() {
stub_bin id 'echo 1000'
stub_sudo
}
stub_sudo() {
stub_bin sudo 'echo "sudo $*" >> "$CALLS"; export RAN_UNDER_SUDO=1; exec "$@"'
}
use_podman() {
RUNTIME=podman
stub_bin systemctl 'exit 0'
}
container_install() {
[ -x "$STUB_DIR/$RUNTIME" ] || stub_runtime "$RUNTIME"
call_install "${RUNTIME}_install" "$@"
}
fake_agent_binary() {
export AGENT_BINARY="$BATS_TEST_TMPDIR/fake-agent"
printf '#!/bin/sh\n[ "$*" = "install --help" ] && exit 0\necho "agent $*" >> "$CALLS"\nexit %s\n' "${1:-0}" > "$AGENT_BINARY"
chmod +x "$AGENT_BINARY"
}
enter_wsl() {
echo "Linux version ${1:-5.15.167.4-microsoft-standard-WSL2} (Microsoft@Microsoft.com) #1 SMP" > "$PROC_VERSION"
}
serve_release() {
export BINARY_ARCH=amd64
RELEASE_DIR="$BATS_TEST_TMPDIR/release"
mkdir -p "$RELEASE_DIR"
printf '#!/bin/sh\necho "release binary ran $*" >> "$CALLS"\n' | gzip > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"
(cd "$RELEASE_DIR" && sha256sum shellhub-agent-linux-amd64.gz > checksums.txt)
local tool="${1:-curl}"
stub_bin "$tool" "echo \"$tool \$*\" >> \"\$CALLS\"
for a; do
case \"\$prev\" in --output|-o|-O) out=\$a ;; esac
case \"\$a\" in https://*) url=\$a ;; esac
prev=\$a
done
cp '$RELEASE_DIR'/\"\${url##*/}\" \"\$out\" 2>/dev/null || exit 1"
stub_bin id 'echo 1000'
stub_bin sudo "echo \"sudo \$*\" >> \"\$CALLS\"; [ \"\${1%.new}\" = '$INSTALL_DIR/shellhub-agent' ] && exit 0; exec \"\$@\""
}
assert_installed_release_binary() {
gzip -dc "$RELEASE_DIR/shellhub-agent-linux-amd64.gz" | cmp -s - "$INSTALL_DIR/shellhub-agent" || {
echo "$INSTALL_DIR/shellhub-agent is not the binary the release served"
return 1
}
assert_called "sudo $INSTALL_DIR/shellhub-agent install --server-address=$SERVER_ADDRESS"
refute_called "release binary ran"
}
discover_version() {
unset AGENT_VERSION
printf '{"version":"%s","endpoints":{"ssh":"localhost:22"}}\n' "$1" > "$BATS_TEST_TMPDIR/info"
stub_bin curl "echo \"curl \$*\" >> \"\$CALLS\"; case \"\$*\" in */info*) cat '$BATS_TEST_TMPDIR/info' ;; esac"
}
@test "check_podman_boot_restart aborts when systemctl is missing" {
call_install check_podman_boot_restart
[ "$status" -eq 1 ]
assert_output_contains "systemctl not found"
assert_output_contains "SKIP_BOOT_RESTART_CHECK=1"
}
@test "check_podman_boot_restart aborts when podman-restart.service is disabled" {
stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 1'
call_install check_podman_boot_restart
[ "$status" -eq 1 ]
assert_called "systemctl is-enabled podman-restart.service"
assert_output_contains "podman-restart.service is not enabled"
assert_output_contains "sudo systemctl enable podman-restart.service"
}
@test "check_podman_boot_restart passes silently when podman-restart.service is enabled" {
stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 0'
call_install check_podman_boot_restart
[ "$status" -eq 0 ]
[ -z "$output" ]
assert_called "systemctl is-enabled podman-restart.service"
}
@test "SKIP_BOOT_RESTART_CHECK installs anyway when systemctl is missing" {
export SKIP_BOOT_RESTART_CHECK=1
call_install check_podman_boot_restart
[ "$status" -eq 0 ]
assert_output_contains "skipping podman-restart.service check"
assert_output_contains "will NOT auto-start on boot"
}
@test "SKIP_BOOT_RESTART_CHECK installs anyway when podman-restart.service is disabled" {
export SKIP_BOOT_RESTART_CHECK=1
stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 1'
call_install check_podman_boot_restart
[ "$status" -eq 0 ]
refute_called "systemctl is-enabled"
assert_output_contains "skipping podman-restart.service check"
}
@test "enrollment_summary reports a provisioning key ahead of a tenant" {
with_tenant
export PROVISIONING_KEY=key-1
call_install enrollment_summary
[ "$output" = "provisioning key" ]
}
@test "enrollment_summary reports a tenant as landing pending" {
with_tenant
call_install enrollment_summary
[ "$output" = "tenant $TENANT_ID" ]
}
@test "enrollment_summary points at the login flow when no credential is given" {
call_install enrollment_summary
[ "$output" = "none — enroll with 'shellhub-agent login'" ]
}
@test "enrollment_summary reports a tenant a previous enrollment left behind" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
call_install enrollment_summary
[ "$output" = "tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)" ]
}
@test "enrollment_summary reports a persisted tenant ahead of a provisioning key" {
export PROVISIONING_KEY=key-1
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
call_install enrollment_summary
[ "$output" = "tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)" ]
}
@test "persisted_tenant escalates to read a tenant file only root can read" {
as_non_root
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
chmod 000 "$PRIVATE_KEY.tenant"
call_install persisted_tenant
assert_called "sudo head -n 1 $PRIVATE_KEY.tenant"
}
@test "persisted_tenant reads the tenant file a container install names under /host" {
export PRIVATE_KEY="/host$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$BATS_TEST_TMPDIR/shellhub.key.tenant"
call_install persisted_tenant
[ "$output" = "00000000-0000-4000-0000-000000000000" ]
}
@test "enroll_agent_interactively runs the login flow when no credential names a namespace" {
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
assert_called "shellhub-agent login"
}
@test "enroll_agent_interactively waits for the agent key before starting the login flow" {
rm -f "$AGENT_KEY"
stub_bin sleep 'echo "sleep $*" >> "$CALLS"'
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
[ "$(grep -c '^sleep 1$' "$CALLS")" -eq 30 ]
assert_called "shellhub-agent login"
}
@test "enroll_agent_interactively does not wait when the agent key is already on disk" {
stub_bin sleep 'echo "sleep $*" >> "$CALLS"'
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
refute_called "sleep"
}
@test "enroll_agent_interactively survives a login the user abandons" {
stub_bin shellhub-agent 'echo "shellhub-agent $*" >> "$CALLS"; kill -INT "$PPID" "$$"'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
}
@test "enroll_agent_interactively lets Ctrl-C stop the installer again after the login" {
stub_bin shellhub-agent 'echo "shellhub-agent $*" >> "$CALLS"; kill -INT "$PPID" "$$"'
call_install eval "enroll_agent_interactively shellhub-agent '$AGENT_KEY'; kill -INT \$\$; echo survived"
[ "$status" -eq 130 ]
refute_output_contains "survived"
}
@test "enroll_agent_interactively skips the login flow for a provisioning key" {
export PROVISIONING_KEY=key-1
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
refute_called "shellhub-agent"
}
@test "enroll_agent_interactively skips the login flow for a persisted tenant" {
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
export PRIVATE_KEY="$AGENT_KEY"
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
refute_called "shellhub-agent"
assert_output_contains "remembered at $AGENT_KEY.tenant"
}
@test "enroll_agent_interactively reports a persisted tenant ahead of a provisioning key" {
export PROVISIONING_KEY=key-1
export PRIVATE_KEY="$AGENT_KEY"
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
assert_output_contains "remembered at $AGENT_KEY.tenant"
}
@test "enroll_agent_interactively skips the login flow for a tenant" {
with_tenant
stub_bin shellhub-agent
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
[ "$status" -eq 0 ]
refute_called "shellhub-agent"
}
@test "observe_enrollment reports a device the server accepted" {
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=accepted tenant_id=t"'
call_install observe_enrollment agent-log
[ "$status" -eq 0 ]
assert_output_contains "The device is accepted"
refute_output_contains "pending"
}
@test "observe_enrollment reports a device the server left pending" {
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=pending tenant_id=t"'
call_install observe_enrollment agent-log
[ "$status" -eq 0 ]
assert_output_contains "The device is pending: accept it in the console"
}
@test "observe_enrollment reports a device rejected in the console" {
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=rejected tenant_id=t"'
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "the device is rejected"
assert_output_contains "A member rejected it in the console. Accept it there to enroll it."
refute_output_contains "provisioning key"
refute_output_contains "pending"
}
@test "observe_enrollment names the provisioning key's policy for a rejected device" {
export PROVISIONING_KEY=key-1
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=rejected tenant_id=t"'
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "provisioning key's allowlist or webhook refused it"
}
@test "enroll_agent_interactively names a persisted tenant ahead of the observed outcome" {
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
export PRIVATE_KEY="$AGENT_KEY"
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" status=accepted tenant_id=t"'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log
assert_output_contains "remembered at $AGENT_KEY.tenant"
assert_output_contains "The device is accepted"
}
@test "observe_enrollment does not claim a status the agent did not log" {
stub_bin agent-log 'echo "time=now level=info msg=\"Listening for connections\" tenant_id=t"'
call_install observe_enrollment agent-log
[ "$status" -eq 0 ]
assert_output_contains "is listening for connections"
assert_output_contains "accepted or still pending"
}
@test "enroll_agent_interactively does not predict a pending device before a refusal" {
with_tenant
stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log
assert_output_contains "404 Not Found"
refute_output_contains "pending"
}
@test "refusal_reason prints only the reason a log line carries" {
call_install refusal_reason 'time="now" level=warning msg="Cannot authorize the device" attempt=1 error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}" server_address="http://localhost:80"'
[ "$status" -eq 0 ]
[ "$output" = 'the server answered 404 Not Found: {"message":"namespace not found"}' ]
}
@test "refusal_reason falls back to the line when it carries no reason" {
call_install refusal_reason 'level=warning msg="Cannot authorize the device"'
[ "$status" -eq 0 ]
[ "$output" = 'level=warning msg="Cannot authorize the device"' ]
}
@test "observe_enrollment reports the server's refusal" {
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}" server_address="http://localhost:80"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "The server refused this device"
assert_output_contains 'the server answered 404 Not Found: {"message":"namespace not found"}'
}
@test "observe_enrollment keeps the agent's own bookkeeping out of what it reports" {
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found" server_address="http://localhost:80"
LOG
call_install observe_enrollment agent-log
assert_output_contains "the server answered 404 Not Found"
refute_output_contains "level=fatal"
refute_output_contains "server_address"
}
@test "observe_enrollment reports the settings an agent refused its own configuration over" {
stub_agent_log <<'LOG'
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="validation failed"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "refused its own configuration"
assert_output_contains "SHELLHUB_TENANT_ID must be a UUID"
refute_output_contains "has not enrolled yet"
}
@test "observe_enrollment names the host path of a persisted tenant the agent refused" {
stub_agent_log <<'LOG'
time="now" level=error msg="/host/etc/shellhub.key.tenant must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains " /etc/shellhub.key.tenant must be a UUID"
refute_output_contains "invalid structure"
}
@test "observe_enrollment names each refused setting once across agent restarts" {
stub_agent_log <<'LOG'
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
time="now" level=error msg="SHELLHUB_TENANT_ID must be a UUID"
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="invalid structure"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
[ "$(echo "$output" | grep -c 'SHELLHUB_TENANT_ID must be a UUID')" -eq 1 ]
}
@test "observe_enrollment falls back to the error when a refused configuration names no setting" {
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to load the configuration from the environment variables" error="missing required value: SERVER_ADDRESS"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "missing required value: SERVER_ADDRESS"
refute_output_contains "has not enrolled yet"
}
@test "observe_enrollment reports an agent still retrying when the window closes" {
stub_agent_log <<'LOG'
time="now" level=warning msg="Cannot authorize the device, retrying until the server accepts it" attempt=1 error="the server answered 404 Not Found: {\"message\":\"namespace not found\"}"
LOG
call_install observe_enrollment agent-log
[ "$status" -eq 1 ]
assert_output_contains "The server is still refusing this device"
assert_output_contains 'the server answered 404 Not Found: {"message":"namespace not found"}'
refute_output_contains "level=warning"
}
@test "observe_enrollment names the command to inspect an agent that says nothing" {
stub_bin agent-log
call_install observe_enrollment agent-log
[ "$status" -eq 0 ]
assert_output_contains "agent-log"
}
@test "observe_enrollment does not claim an outcome for a runtime it cannot read" {
call_install observe_enrollment ""
[ "$status" -eq 0 ]
assert_output_contains "does not expose"
}
@test "enroll_agent_interactively observes the outcome of a provisioning key enrollment" {
export PROVISIONING_KEY=key-1
stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log
assert_output_contains "404 Not Found"
}
@test "enroll_agent_interactively does not claim an outcome it cannot observe" {
export PROVISIONING_KEY=key-1
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY"
assert_output_contains "does not expose"
}
@test "enroll_agent_interactively observes the outcome of a persisted tenant enrollment" {
export PERSISTED_TENANT=00000000-0000-4000-0000-000000000000
export PRIVATE_KEY="$AGENT_KEY"
stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log
assert_output_contains "remembered at $AGENT_KEY.tenant"
assert_output_contains "404 Not Found"
}
@test "enroll_agent_interactively observes the outcome of a tenant enrollment" {
with_tenant
stub_bin agent-log 'echo "level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\""'
call_install enroll_agent_interactively shellhub-agent "$AGENT_KEY" agent-log
assert_output_contains "404 Not Found"
}
@test "docker_install runs the container with unless-stopped so it survives a reboot" {
container_install
[ "$status" -eq 0 ]
assert_called "docker run -d --name=shellhub --restart=unless-stopped"
}
@test "podman_install runs the container with unless-stopped so podman-restart.service picks it up" {
use_podman
container_install
[ "$status" -eq 0 ]
assert_called "podman run -d --name=shellhub --replace --restart=unless-stopped"
}
@test "docker_install grants the agent the host access it needs" {
container_install
[ "$status" -eq 0 ]
assert_called "--privileged --net=host --pid=host"
assert_called "-v /:/host"
assert_called "-v /var/run/docker.sock:/var/run/docker.sock"
assert_called "-v /etc/resolv.conf:/etc/resolv.conf"
}
@test "podman_install grants the agent the host access it needs" {
use_podman
container_install
[ "$status" -eq 0 ]
assert_called "--privileged --pid=host --security-opt label=disable --network host"
assert_called "-v /:/host"
}
@test "podman_install presents the podman socket to the agent as the docker socket" {
use_podman
container_install
[ "$status" -eq 0 ]
assert_called "-v /var/run/podman/podman.sock:/var/run/docker.sock"
}
@test "docker_install labels the agent container so the wrapper can find it" {
container_install
[ "$status" -eq 0 ]
assert_called "--label shellhub.role=agent"
assert_output_contains "Starting ShellHub container in Agent mode"
}
@test "podman_install labels the agent container so the wrapper can find it" {
use_podman
container_install
[ "$status" -eq 0 ]
assert_called "--label shellhub.role=agent"
}
@test "docker_install treats an explicit agent mode as the default mode" {
container_install agent
[ "$status" -eq 0 ]
assert_called "--label shellhub.role=agent"
assert_output_contains "Starting ShellHub container in Agent mode"
}
@test "podman_install treats an explicit agent mode as the default mode" {
use_podman
container_install agent
[ "$status" -eq 0 ]
assert_called "--label shellhub.role=agent"
}
@test "docker_install omits the agent label in connector mode" {
with_tenant
container_install connector
[ "$status" -eq 0 ]
refute_called "--label shellhub.role=agent"
assert_called "--name=shellhub-connector"
assert_called "-e SHELLHUB_PRIVATE_KEYS=/host/etc/shellhub/connector/keys"
}
@test "podman_install omits the agent label in connector mode" {
use_podman
with_tenant
container_install connector
[ "$status" -eq 0 ]
refute_called "--label shellhub.role=agent"
assert_called "--name=shellhub-connector"
assert_called "-e SHELLHUB_PRIVATE_KEYS=/host/etc/shellhub/connector/keys"
}
@test "docker_install refuses connector mode without a tenant or a provisioning key" {
container_install connector
[ "$status" -eq 1 ]
assert_output_contains "TENANT_ID or PROVISIONING_KEY is required"
refute_called "docker run"
}
@test "podman_install refuses connector mode without a tenant or a provisioning key" {
use_podman
container_install connector
[ "$status" -eq 1 ]
assert_output_contains "TENANT_ID or PROVISIONING_KEY is required"
refute_called "podman run"
}
@test "docker_install starts the connector with a provisioning key alone" {
export PROVISIONING_KEY=key-1
container_install connector
[ "$status" -eq 0 ]
assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
refute_called "SHELLHUB_TENANT_ID"
}
@test "podman_install starts the connector with a provisioning key alone" {
use_podman
export PROVISIONING_KEY=key-1
container_install connector
[ "$status" -eq 0 ]
assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
refute_called "SHELLHUB_TENANT_ID"
}
@test "docker_install rejects an unknown mode" {
container_install bogus
[ "$status" -eq 1 ]
assert_output_contains "Invalid mode: bogus"
refute_called "docker run"
}
@test "podman_install rejects an unknown mode" {
use_podman
container_install bogus
[ "$status" -eq 1 ]
assert_output_contains "Invalid mode: bogus"
refute_called "podman run"
}
@test "docker_install maps credentials and preferences onto the agent environment" {
with_tenant
export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
export PREFERRED_HOSTNAME=box PREFERRED_IDENTITY=eth0
container_install
[ "$status" -eq 0 ]
assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
assert_called "-e SHELLHUB_KEEPALIVE_INTERVAL=45"
assert_called "-e SHELLHUB_PREFERRED_HOSTNAME=box"
assert_called "-e SHELLHUB_PREFERRED_IDENTITY=eth0"
assert_called "-e SHELLHUB_TENANT_ID=$TENANT_ID"
assert_called "-e SHELLHUB_SERVER_ADDRESS=$SERVER_ADDRESS"
}
@test "podman_install maps credentials and preferences onto the agent environment" {
use_podman
with_tenant
export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
export PREFERRED_HOSTNAME=box PREFERRED_IDENTITY=eth0
container_install
[ "$status" -eq 0 ]
assert_called "-e SHELLHUB_PROVISIONING_KEY=key-1"
assert_called "-e SHELLHUB_KEEPALIVE_INTERVAL=45"
assert_called "-e SHELLHUB_PREFERRED_HOSTNAME=box"
assert_called "-e SHELLHUB_PREFERRED_IDENTITY=eth0"
assert_called "-e SHELLHUB_TENANT_ID=$TENANT_ID"
assert_called "-e SHELLHUB_SERVER_ADDRESS=$SERVER_ADDRESS"
}
@test "docker_install omits environment variables for options left unset" {
container_install
[ "$status" -eq 0 ]
refute_called "SHELLHUB_TENANT_ID"
refute_called "SHELLHUB_PROVISIONING_KEY"
refute_called "SHELLHUB_KEEPALIVE_INTERVAL"
refute_called "SHELLHUB_PREFERRED_"
assert_called "-e SHELLHUB_PRIVATE_KEY=$PRIVATE_KEY"
}
@test "podman_install omits environment variables for options left unset" {
use_podman
container_install
[ "$status" -eq 0 ]
refute_called "SHELLHUB_TENANT_ID"
refute_called "SHELLHUB_PROVISIONING_KEY"
refute_called "SHELLHUB_KEEPALIVE_INTERVAL"
refute_called "SHELLHUB_PREFERRED_"
assert_called "-e SHELLHUB_PRIVATE_KEY=$PRIVATE_KEY"
}
@test "docker_install pulls the agent image by default" {
container_install
[ "$status" -eq 0 ]
assert_called "docker pull -q $AGENT_IMAGE"
assert_output_contains "Downloading ShellHub container image"
}
@test "podman_install pulls the agent image by default" {
use_podman
container_install
[ "$status" -eq 0 ]
assert_called "podman pull -q $AGENT_IMAGE"
assert_output_contains "Downloading ShellHub container image"
}
@test "docker_install skips the pull when the image was overridden" {
export AGENT_IMAGE_OVERRIDDEN=1
container_install
[ "$status" -eq 0 ]
refute_called "docker pull"
assert_output_contains "skipping pull"
}
@test "podman_install skips the pull when the image was overridden" {
use_podman
export AGENT_IMAGE_OVERRIDDEN=1
container_install
[ "$status" -eq 0 ]
refute_called "podman pull"
assert_output_contains "skipping pull"
}
@test "docker_install aborts when the image pull fails" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = pull ] && exit 1; exit 0'
container_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to download shellhub container image"
refute_called "docker run"
}
@test "podman_install aborts when the image pull fails" {
use_podman
stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = pull ] && exit 1; exit 0'
container_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to download shellhub container image"
refute_called "podman run"
}
@test "docker_install honours CONTAINER_NAME" {
export CONTAINER_NAME=my-agent
container_install
[ "$status" -eq 0 ]
assert_called "--name=my-agent"
assert_called "docker rm -f my-agent"
}
@test "podman_install honours CONTAINER_NAME" {
use_podman
export CONTAINER_NAME=my-agent
container_install
[ "$status" -eq 0 ]
assert_called "--name=my-agent"
}
@test "docker_install removes an existing container so the new one carries the role label" {
container_install
[ "$status" -eq 0 ]
assert_called "docker rm -f shellhub"
}
@test "podman_install checks the boot restart unit before touching podman" {
RUNTIME=podman
container_install
[ "$status" -eq 1 ]
assert_output_contains "systemctl not found"
refute_called "podman"
}
@test "install_agent_wrapper writes an executable wrapper bound to podman" {
call_install install_agent_wrapper podman
[ "$status" -eq 0 ]
[ -x "$INSTALL_DIR/shellhub-agent" ]
assert_output_contains "Installed shellhub-agent wrapper at $INSTALL_DIR/shellhub-agent"
assert_file_contains "$INSTALL_DIR/shellhub-agent" "podman ps --filter label=shellhub.role=agent"
assert_file_contains "$INSTALL_DIR/shellhub-agent" "podman exec"
refute_file_contains "$INSTALL_DIR/shellhub-agent" "docker exec"
}
@test "install_agent_wrapper writes an executable wrapper bound to docker" {
call_install install_agent_wrapper docker
[ "$status" -eq 0 ]
[ -x "$INSTALL_DIR/shellhub-agent" ]
assert_file_contains "$INSTALL_DIR/shellhub-agent" "docker ps --filter label=shellhub.role=agent"
assert_file_contains "$INSTALL_DIR/shellhub-agent" "docker exec"
refute_file_contains "$INSTALL_DIR/shellhub-agent" "podman exec"
}
@test "install_agent_wrapper escalates to write into a root-owned directory" {
as_non_root
call_install install_agent_wrapper docker
[ "$status" -eq 0 ]
assert_called "sudo tee $INSTALL_DIR/shellhub-agent"
assert_called "sudo chmod +x $INSTALL_DIR/shellhub-agent"
}
@test "install_agent_wrapper aborts when the install directory does not exist" {
export INSTALL_DIR="$BATS_TEST_TMPDIR/absent"
call_install install_agent_wrapper docker
[ "$status" -eq 1 ]
assert_output_contains "Failed to install shellhub-agent wrapper"
}
@test "snap_install requires a tenant" {
stub_bin snap
call_install snap_install
[ "$status" -eq 1 ]
assert_output_contains "TENANT_ID is required"
refute_called "snap install"
}
@test "snap_install refuses a provisioning key it cannot honour" {
with_tenant
export PROVISIONING_KEY=key-1
stub_bin snap
call_install snap_install
[ "$status" -eq 1 ]
assert_output_contains "PROVISIONING_KEY is not supported by the snap install method"
refute_called "snap install"
}
@test "snap_install aborts when snap is not available" {
with_tenant
call_install snap_install
[ "$status" -eq 1 ]
assert_output_contains "Snap is not installed or not supported on this system"
}
@test "snap_install configures the service from the environment" {
with_tenant
export PREFERRED_HOSTNAME=box KEEPALIVE_INTERVAL=45
stub_bin snap
stub_sudo
call_install snap_install
[ "$status" -eq 0 ]
assert_called "snap install --classic --channel=latest/stable shellhub"
assert_called "snap set shellhub server-address=$SERVER_ADDRESS"
assert_called "snap set shellhub tenant-id=$TENANT_ID"
assert_called "snap set shellhub preferred-hostname=box"
assert_called "snap set shellhub keepalive-interval=45"
assert_called "snap start shellhub"
}
@test "standalone_install passes the credentials on to the agent service" {
with_tenant
export PROVISIONING_KEY=key-1 KEEPALIVE_INTERVAL=45
fake_agent_binary
call_install standalone_install
[ "$status" -eq 0 ]
assert_called "agent install --server-address=$SERVER_ADDRESS --tenant-id=$TENANT_ID --provisioning-key=key-1 --keepalive-interval=45"
[ -x "$INSTALL_DIR/shellhub-agent" ]
}
@test "standalone_install reads only the journal of the service it started" {
with_tenant
fake_agent_binary
stub_bin date 'echo 1700000000'
stub_bin journalctl
call_install standalone_install
[ "$status" -eq 0 ]
assert_called "journalctl -u shellhub-agent --no-pager -n 50 --since @1700000000"
}
@test "standalone_install escalates when it is not already root" {
with_tenant
as_non_root
fake_agent_binary
call_install standalone_install
[ "$status" -eq 0 ]
assert_output_contains "This install method requires root privileges"
assert_called "sudo $INSTALL_DIR/shellhub-agent install"
}
@test "standalone_install removes the binary when the service install fails" {
with_tenant
fake_agent_binary 1
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to install ShellHub agent service"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
[ ! -e "$TMP_DIR" ]
}
@test "standalone_install starts the agent it stopped again when the service install fails" {
with_tenant
fake_agent_binary 1
local stopped="$BATS_TEST_TMPDIR/old-agent-stopped"
stub_bin systemctl "echo \"systemctl \$*\" >> \"\$CALLS\"
case \"\$1\" in
stop) : > '$stopped' ;;
is-active) [ ! -e '$stopped' ] ;;
esac"
call_install standalone_install
[ "$status" -eq 1 ]
assert_called "systemctl enable --now shellhub-agent"
[ -x "$INSTALL_DIR/shellhub-agent" ]
}
@test "standalone_install leaves the running agent alone when the new binary cannot run its installer" {
with_tenant
export AGENT_BINARY="$BATS_TEST_TMPDIR/foreign-agent"
printf '#!/bin/sh\n[ "$1" = --help ] && exit 0\nexit 1\n' > "$AGENT_BINARY"
chmod +x "$AGENT_BINARY"
mkdir -p "$INSTALL_DIR"
echo "old agent" > "$INSTALL_DIR/shellhub-agent"
stub_bin systemctl 'echo "systemctl $*" >> "$CALLS"; exit 0'
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "cannot run its installer"
refute_called "systemctl stop"
[ "$(cat "$INSTALL_DIR/shellhub-agent")" = "old agent" ]
[ ! -e "$INSTALL_DIR/shellhub-agent.new" ]
}
@test "standalone_install reports a local binary it cannot copy" {
with_tenant
fake_agent_binary
mkdir -p "$INSTALL_DIR"
cp "$AGENT_BINARY" "$INSTALL_DIR/shellhub-agent.new"
export AGENT_BINARY="$BATS_TEST_TMPDIR/missing-agent"
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "Could not copy $AGENT_BINARY"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
[ ! -e "$INSTALL_DIR/shellhub-agent.new" ]
[ ! -e "$TMP_DIR" ]
}
@test "standalone_install reports a release binary it cannot extract" {
with_tenant
serve_release
echo "not gzip" > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"
(cd "$RELEASE_DIR" && sha256sum shellhub-agent-linux-amd64.gz > checksums.txt)
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to extract the agent binary"
refute_output_contains "Could not replace"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
[ ! -e "$TMP_DIR" ]
}
@test "standalone_install installs the agent binary that matches the release checksum" {
with_tenant
serve_release
call_install standalone_install
[ "$status" -eq 0 ]
assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/shellhub-agent-linux-amd64.gz"
assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/checksums.txt"
assert_installed_release_binary
}
@test "standalone_install installs the agent binary that matches the release checksum with wget" {
with_tenant
serve_release wget
call_install standalone_install
[ "$status" -eq 0 ]
assert_called "wget"
assert_called "https://github.com/shellhub-io/shellhub/releases/download/v0.0.0-test/checksums.txt"
assert_installed_release_binary
}
@test "standalone_install refuses an agent binary that does not match the release checksum" {
with_tenant
for tool in curl wget; do
echo "downloading with $tool"
rm -f "$STUB_DIR/curl" "$STUB_DIR/wget"
mkdir -p "$TMP_DIR"
serve_release "$tool"
printf '#!/bin/sh\necho "tampered $*" >> "$CALLS"\n' | gzip > "$RELEASE_DIR/shellhub-agent-linux-amd64.gz"
call_install standalone_install
[ "$status" -eq 1 ]
assert_called "$tool"
assert_output_contains "does not match the checksum published with release v0.0.0-test"
refute_called "tampered"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
done
}
@test "standalone_install refuses a release whose checksums do not list the agent binary" {
with_tenant
serve_release
sed -i 's/linux-amd64/linux-arm64/' "$RELEASE_DIR/checksums.txt"
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "does not match the checksum published with release v0.0.0-test"
refute_called "agent install"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
}
@test "standalone_install refuses a release whose checksums it cannot download" {
with_tenant
serve_release
rm "$RELEASE_DIR/checksums.txt"
call_install standalone_install
[ "$status" -eq 1 ]
assert_output_contains "Could not download the checksums published with release v0.0.0-test"
refute_called "agent install"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
}
@test "docker_uninstall removes the container and the wrapper" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
call_install install_agent_wrapper docker
call_install docker_uninstall
[ "$status" -eq 0 ]
assert_called "docker rm -f shellhub"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
}
@test "docker_uninstall escalates when it is not already root" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
call_install install_agent_wrapper docker
as_non_root
call_install docker_uninstall
[ "$status" -eq 0 ]
assert_called "sudo docker rm -f shellhub"
assert_called "sudo rm -f $INSTALL_DIR/shellhub-agent"
}
@test "podman_uninstall removes the container and the wrapper" {
stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
call_install install_agent_wrapper podman
call_install podman_uninstall
[ "$status" -eq 0 ]
assert_called "podman rm -f shellhub"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
}
@test "podman_uninstall escalates when it is not already root" {
stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
call_install install_agent_wrapper podman
as_non_root
call_install podman_uninstall
[ "$status" -eq 0 ]
assert_called "sudo podman rm -f shellhub"
assert_called "sudo rm -f $INSTALL_DIR/shellhub-agent"
}
@test "docker_uninstall reports a container that was already gone" {
stub_bin docker
call_install docker_uninstall
[ "$status" -eq 0 ]
assert_output_contains "not found (may already be removed)"
refute_called "docker rm -f"
}
@test "docker_uninstall aborts when the container cannot be removed" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; [ "$1" != rm ]'
call_install docker_uninstall
[ "$status" -eq 1 ]
assert_output_contains "Failed to remove container 'shellhub'"
refute_output_contains "uninstalled"
}
@test "podman_uninstall aborts when the container cannot be removed" {
stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; [ "$1" != rm ]'
call_install podman_uninstall
[ "$status" -eq 1 ]
assert_output_contains "Failed to remove container 'shellhub'"
refute_output_contains "uninstalled"
}
@test "docker_uninstall aborts when it cannot list containers" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" != ps ]'
call_install docker_uninstall
[ "$status" -eq 1 ]
assert_output_contains "Failed to look up container 'shellhub'"
refute_output_contains "not found"
refute_output_contains "uninstalled"
}
@test "podman_uninstall aborts when it cannot list containers" {
stub_bin podman 'echo "podman $*" >> "$CALLS"; [ "$1" != ps ]'
call_install podman_uninstall
[ "$status" -eq 1 ]
assert_output_contains "Failed to look up container 'shellhub'"
refute_output_contains "not found"
refute_output_contains "uninstalled"
}
@test "podman_uninstall reports a container that was already gone" {
stub_bin podman
call_install podman_uninstall
[ "$status" -eq 0 ]
assert_output_contains "not found (may already be removed)"
refute_called "podman rm -f"
}
@test "uninstall names the tenant file it leaves behind" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
stub_bin docker
call_install docker_uninstall
assert_output_contains "$PRIVATE_KEY.tenant"
}
@test "uninstall names the tenant file a container install left behind under /host" {
export PRIVATE_KEY="/host$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$BATS_TEST_TMPDIR/shellhub.key.tenant"
stub_bin docker
call_install docker_uninstall
assert_output_contains "$BATS_TEST_TMPDIR/shellhub.key.tenant"
[[ "$output" != *"/host$BATS_TEST_TMPDIR"* ]]
}
@test "podman_uninstall names the tenant file it leaves behind" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
stub_bin podman
call_install podman_uninstall
assert_output_contains "$PRIVATE_KEY.tenant"
}
@test "standalone_uninstall names the tenant file it leaves behind" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
echo "00000000-0000-4000-0000-000000000000" > "$PRIVATE_KEY.tenant"
fake_agent_binary
cp "$AGENT_BINARY" "$INSTALL_DIR/shellhub-agent"
call_install standalone_uninstall
assert_output_contains "$PRIVATE_KEY.tenant"
}
@test "uninstall names a tenant file it cannot read a tenant from" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
: > "$PRIVATE_KEY.tenant"
stub_bin docker
call_install docker_uninstall
[ "$status" -eq 0 ]
assert_output_contains "remembered in $PRIVATE_KEY.tenant"
}
@test "uninstall stays quiet about a tenant file that is not there" {
export PRIVATE_KEY="$BATS_TEST_TMPDIR/shellhub.key"
stub_bin docker
call_install docker_uninstall
[ "$status" -eq 0 ]
assert_output_contains "ShellHub agent uninstalled."
refute_output_contains ".tenant"
}
@test "standalone_uninstall reports a missing binary" {
call_install standalone_uninstall
[ "$status" -eq 1 ]
assert_output_contains "ShellHub agent binary not found"
}
@test "standalone_uninstall looks in the default install dir when the environment names none" {
unset INSTALL_DIR
call_install standalone_uninstall
[ "$status" -eq 1 ]
assert_output_contains "/usr/local/bin/shellhub-agent"
}
@test "standalone_uninstall stops the service and removes the binary" {
with_tenant
fake_agent_binary
call_install standalone_install
call_install standalone_uninstall
[ "$status" -eq 0 ]
assert_called "agent uninstall"
[ ! -e "$INSTALL_DIR/shellhub-agent" ]
}
@test "wsl_install aborts when systemd is not enabled" {
stub_bin systemctl 'exit 1'
call_install wsl_install
[ "$status" -eq 1 ]
assert_output_contains "requires systemd to be enabled"
}
@test "wsl_install aborts when WSL networking is not mirrored" {
stub_bin systemctl 'exit 0'
stub_bin wslinfo 'echo nat'
call_install wsl_install
[ "$status" -eq 1 ]
assert_output_contains "networking mode must be set to mirrored"
}
@test "wsl_install falls through to the standalone install once both preconditions hold" {
with_tenant
stub_bin systemctl 'exit 0'
stub_bin wslinfo 'echo mirrored'
fake_agent_binary
call_install wsl_install
[ "$status" -eq 0 ]
assert_called "agent install --server-address=$SERVER_ADDRESS"
}
@test "the installer aborts on FreeBSD" {
stub_bin uname 'echo FreeBSD'
run_install
[ "$status" -eq 1 ]
assert_output_contains "Automatic installation is not supported on FreeBSD"
}
@test "detection prefers docker when it is accessible" {
stub_runtime docker
stub_runtime podman
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using docker method"
assert_called "docker run -d"
refute_called "podman run"
}
@test "detection falls back to podman when docker is absent" {
stub_runtime podman
stub_bin systemctl 'exit 0'
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using podman method"
assert_called "podman run -d"
}
@test "detection retries docker with sudo when it is only reachable as root" {
unset AGENT_IMAGE
stub_runtime docker '[ -z "$RAN_UNDER_SUDO" ] && exit 1'
stub_sudo
run_install
[ "$status" -eq 0 ]
assert_called "sudo docker info"
assert_called "sudo docker pull -q docker.io/shellhubio/agent:$AGENT_VERSION"
assert_called "sudo docker run -d"
assert_called "sudo $INSTALL_DIR/shellhub-agent login"
}
@test "detection retries podman with sudo when it is only reachable as root" {
unset AGENT_IMAGE
stub_runtime podman '[ -z "$RAN_UNDER_SUDO" ] && exit 1'
stub_bin systemctl 'exit 0'
stub_sudo
run_install
[ "$status" -eq 0 ]
assert_called "sudo podman info"
assert_called "sudo podman pull -q docker.io/shellhubio/agent:$AGENT_VERSION"
assert_called "sudo podman run -d"
assert_called "sudo $INSTALL_DIR/shellhub-agent login"
}
@test "detection falls back to snap when no container runtime is available" {
with_tenant
stub_bin snap
stub_sudo
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using snap method"
assert_called "snap install --classic --channel=latest/stable shellhub"
}
@test "detection falls back to the standalone install when nothing else is available" {
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using standalone method"
assert_called "agent install --server-address=$SERVER_ADDRESS"
}
@test "an explicit INSTALL_METHOD skips detection entirely" {
export INSTALL_METHOD=standalone
stub_bin docker
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using standalone method"
refute_called "docker info"
}
@test "an unsupported INSTALL_METHOD is rejected" {
export INSTALL_METHOD=bogus
run_install
[ "$status" -eq 1 ]
assert_output_contains "Install method not supported"
}
@test "detection reports WSL 2 on Ubuntu" {
enter_wsl
stub_bin systemctl 'exit 0'
stub_bin wslinfo 'echo mirrored'
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_output_contains "Detected WSL environment"
assert_output_contains "Installing ShellHub using WSL method"
assert_called "agent install --server-address=$SERVER_ADDRESS"
}
@test "detection aborts on WSL 1" {
enter_wsl 4.4.0-19041-Microsoft
run_install
[ "$status" -eq 1 ]
assert_output_contains "WSL version 2 is required"
}
@test "detection aborts on the 2016 WSL 1 kernel release" {
enter_wsl 3.4.0-Microsoft
run_install
[ "$status" -eq 1 ]
assert_output_contains "WSL version 2 is required"
}
@test "detection lets a non-WSL-1 kernel release through once /proc/version names Microsoft" {
enter_wsl 6.1.0-custom-WSL2
stub_bin systemctl 'exit 0'
stub_bin wslinfo 'echo mirrored'
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using WSL method"
}
@test "detection aborts on WSL with a distro other than Ubuntu" {
enter_wsl
echo 'NAME="Debian GNU/Linux"' > "$OS_RELEASE"
run_install
[ "$status" -eq 1 ]
assert_output_contains "Only Ubuntu is supported in WSL"
}
@test "WSL detection overrides an explicit INSTALL_METHOD, so a chosen method does not survive WSL" {
export INSTALL_METHOD=standalone
enter_wsl
stub_bin systemctl 'exit 0'
stub_bin wslinfo 'echo mirrored'
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_output_contains "Installing ShellHub using WSL method"
}
@test "the installer reports the enrollment credential before installing anything" {
with_tenant
stub_bin docker
run_install
[ "$status" -eq 0 ]
assert_output_contains "Enrollment: tenant $TENANT_ID"
}
@test "the installer discovers the agent version from the server" {
discover_version v0.27.0-rc.17
unset AGENT_IMAGE
stub_runtime docker
run_install
[ "$status" -eq 0 ]
assert_called "docker pull -q docker.io/shellhubio/agent:v0.27.0-rc.17"
}
@test "the installer refuses a server whose certificate it cannot verify" {
discover_version v0.27.0-rc.17
stub_bin curl "echo \"curl \$*\" >> \"\$CALLS\"; for a; do case \"\$a\" in --insecure|-k*|-[!-]*k*) cat '$BATS_TEST_TMPDIR/info'; exit 0 ;; esac; done; exit 60"
unset AGENT_IMAGE
stub_bin docker
run_install
[ "$status" -eq 1 ]
assert_output_contains "did not report an agent version"
assert_output_contains "trust the certificate"
refute_called "docker pull"
}
@test "the installer refuses a server version that points the download at another repository" {
discover_version "../../../../audit-owner/audit-repo/releases/download/v1"
export INSTALL_METHOD=standalone
run_install
[ "$status" -eq 1 ]
assert_output_contains "not a ShellHub release tag"
refute_called "releases/download"
}
@test "the installer refuses a server version that is not a valid image tag" {
discover_version 'v0.27.0$(id)'
unset AGENT_IMAGE
stub_bin docker
run_install
[ "$status" -eq 1 ]
assert_output_contains "not a ShellHub release tag"
refute_called "docker pull"
}
@test "the installer refuses an agent version that is not a release tag" {
export INSTALL_METHOD=standalone
stub_bin curl
for version in "" "v1.2" "v0.27.0/../../x" $'v0.27.0\nv0.27.1' 'v0.27.0$(id)' "v0.27.0 x"; do
echo "AGENT_VERSION=$version"
AGENT_VERSION="$version" run_install
[ "$status" -eq 1 ]
if [ -z "$version" ]; then
assert_output_contains "did not report an agent version"
else
assert_output_contains "not a ShellHub release tag"
refute_output_contains "certificate"
fi
done
refute_called "releases/download"
}
@test "the installer accepts latest as the agent version" {
export AGENT_VERSION=latest
unset AGENT_IMAGE
stub_runtime docker
run_install
[ "$status" -eq 0 ]
assert_called "docker pull -q docker.io/shellhubio/agent:latest"
}
@test "the installer needs no server version when the agent image is given" {
discover_version ""
stub_runtime docker
run_install
[ "$status" -eq 0 ]
assert_called "docker run -d"
}
@test "the installer needs no server version when the agent binary is given" {
discover_version ""
export INSTALL_METHOD=standalone
fake_agent_binary
run_install
[ "$status" -eq 0 ]
assert_called "agent install --server-address=$SERVER_ADDRESS"
}
@test "uninstall does not depend on the server reporting a release version" {
discover_version "not a version"
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
run_install uninstall
[ "$status" -eq 0 ]
assert_called "docker rm -f shellhub"
}
@test "the installer reports a tenant a previous enrollment left behind" {
echo "00000000-0000-4000-0000-000000000000" > "$AGENT_KEY.tenant"
stub_bin docker
run_install
[ "$status" -eq 0 ]
assert_output_contains "Enrollment: tenant 00000000-0000-4000-0000-000000000000 (persisted by a previous enrollment)"
}
@test "the installer prefers a tenant given to this run over the persisted one" {
with_tenant
echo "00000000-0000-4000-0000-000000000000" > "$AGENT_KEY.tenant"
stub_bin docker
run_install
[ "$status" -eq 0 ]
assert_output_contains "Enrollment: tenant $TENANT_ID"
refute_output_contains "remembered at"
}
@test "the installer fails when the server refuses a device installed with docker" {
with_tenant
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
stub_container_logs docker
run_install
[ "$status" -eq 1 ]
assert_output_contains "The server refused this device"
assert_called "docker run -d"
}
@test "the installer succeeds when docker leaves the device pending" {
with_tenant
stub_agent_log <<'LOG'
time="now" level=info msg="Listening for connections" status=pending tenant_id=t
LOG
stub_container_logs docker
run_install
[ "$status" -eq 0 ]
assert_output_contains "The device is pending"
}
@test "the installer fails when the login flow fails" {
stub_runtime docker '[ "$1" = exec ] && exit 2'
run_install
[ "$status" -eq 1 ]
assert_called "docker exec shellhub agent login"
}
@test "the installer fails when docker cannot start the agent container" {
with_tenant
stub_runtime docker '[ "$1" = run ] && exit 1'
run_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to start the ShellHub container"
}
@test "the installer fails when podman cannot start the agent container" {
with_tenant
use_podman
stub_runtime podman '[ "$1" = run ] && exit 1'
run_install
[ "$status" -eq 1 ]
assert_output_contains "Failed to start the ShellHub container"
}
@test "the installer fails when the server refuses a device installed with podman" {
with_tenant
use_podman
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
stub_container_logs podman
run_install
[ "$status" -eq 1 ]
assert_output_contains "The server refused this device"
assert_called "podman run -d"
}
@test "the installer fails and still cleans up when the server refuses a standalone device" {
with_tenant
export INSTALL_METHOD=standalone
fake_agent_binary
stub_agent_log <<'LOG'
time="now" level=fatal msg="Failed to authorize the device" error="the server answered 404 Not Found"
LOG
stub_journal
run_install
[ "$status" -eq 1 ]
assert_output_contains "The server refused this device"
[ -x "$INSTALL_DIR/shellhub-agent" ]
[ ! -e "$TMP_DIR" ]
}
@test "the installer ignores what the agent it replaced logged before a standalone reinstall" {
with_tenant
export INSTALL_METHOD=standalone
fake_agent_binary
local stopped="$BATS_TEST_TMPDIR/old-agent-stopped" polls="$BATS_TEST_TMPDIR/journal-polls"
stub_bin systemctl "[ \"\$1\" = stop ] && : > '$stopped'; exit 0"
stub_bin sleep "[ -e '$stopped' ] && : > '$stopped.slept'; exit 0"
stub_bin date "[ -e '$stopped.slept' ] && echo 1700000001 || echo 1700000000"
stub_bin journalctl "echo >> '$polls'
case \"\$*\" in
*'--since @1700000000'*) echo 'level=fatal msg=\"Failed to authorize the device\" error=\"the server answered 404 Not Found\"' ;;
esac
[ \$(wc -l < '$polls') -gt 1 ] && echo 'level=info msg=\"Listening for connections\" status=accepted tenant_id=t'
exit 0"
run_install
[ "$status" -eq 0 ]
assert_output_contains "The device is accepted"
refute_output_contains "The server refused this device"
}
@test "the installer succeeds when a standalone device is accepted" {
with_tenant
export INSTALL_METHOD=standalone
fake_agent_binary
stub_agent_log <<'LOG'
time="now" level=info msg="Listening for connections" status=accepted tenant_id=t
LOG
stub_journal
run_install
[ "$status" -eq 0 ]
assert_output_contains "The device is accepted"
}
@test "uninstall dispatches to the detected method" {
stub_bin docker 'echo "docker $*" >> "$CALLS"; [ "$1" = ps ] && echo deadbeef; exit 0'
run_install uninstall
[ "$status" -eq 0 ]
assert_output_contains "Uninstalling ShellHub using docker method"
assert_called "docker rm -f shellhub"
}
@test "uninstall does no installer work before removing the agent" {
stub_bin docker
stub_bin curl 'echo "curl $*" >> "$CALLS"'
stub_bin wget 'echo "wget $*" >> "$CALLS"'
run_install uninstall
[ "$status" -eq 0 ]
refute_called "curl"
refute_called "wget"
[[ "$output" != *"Detected settings"* ]]
[[ "$output" != *"ShellHub Agent Installer"* ]]
}
@test "uninstall is refused for install methods that do not support it" {
export INSTALL_METHOD=snap
run_install uninstall
[ "$status" -eq 1 ]
assert_output_contains "Uninstall is not yet supported for 'snap' install method"
}