211 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
# shellcheck source=install/lib/common.sh
source "${INSTALL_DIR}/lib/common.sh"
# shellcheck source=install/lib/logging.sh
source "${INSTALL_DIR}/lib/logging.sh"
# shellcheck source=install/lib/packages.sh
source "${INSTALL_DIR}/lib/packages.sh"
atlas_set_script_name "30-docker.sh"
atlas_install_error_trap
DOCKER_KEYRING="/etc/apt/keyrings/docker.asc"
DOCKER_SOURCE="/etc/apt/sources.list.d/docker.list"
DOCKER_PACKAGES=(docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin)
DOCKER_CONFLICTS=(docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc)
DOCKER_REPO_BASE="https://download.docker.com/linux/ubuntu"
DOCKER_USER="nick"
usage() {
cat <<'USAGE'
Usage: install/phases/30-docker.sh [options]
Install Docker Engine from Docker's official signed Ubuntu APT repository.
Options:
--help Show this help text.
--version Show script/framework version.
--dry-run Show intended actions without making changes.
--verbose Print additional diagnostic output.
USAGE
}
while [[ "$#" -gt 0 ]]; do
case "$1" in
--help)
usage
exit "${ATLAS_EXIT_OK}"
;;
--version)
atlas_print_version
exit "${ATLAS_EXIT_OK}"
;;
--dry-run)
atlas_enable_dry_run
;;
--verbose)
atlas_enable_verbose
;;
*)
atlas_usage_error "Unknown option: $1"
exit "${ATLAS_EXIT_USAGE}"
;;
esac
shift
done
load_os_release() {
# shellcheck disable=SC1091
. /etc/os-release
if [[ "${ID:-}" != "ubuntu" || "${VERSION_CODENAME:-}" != "resolute" ]]; then
atlas_log_error "Unsupported OS for this Docker phase: ${PRETTY_NAME:-unknown}"
exit "${ATLAS_EXIT_PREREQ}"
fi
}
docker_repo_available() {
curl -fsSL "${DOCKER_REPO_BASE}/dists/${VERSION_CODENAME}/Release" >/dev/null
}
install_prerequisites() {
local packages=(ca-certificates curl)
local missing=()
local package_name
for package_name in "${packages[@]}"; do
atlas_package_installed "$package_name" || missing+=("$package_name")
done
if [[ "${#missing[@]}" -gt 0 ]]; then
atlas_install_packages "${missing[@]}"
else
atlas_log_ok "Docker repository prerequisites are already installed"
fi
}
remove_conflicts() {
local installed=()
local package_name
for package_name in "${DOCKER_CONFLICTS[@]}"; do
atlas_package_installed "$package_name" && installed+=("$package_name")
done
if [[ "${#installed[@]}" -eq 0 ]]; then
atlas_log_ok "No conflicting Docker packages are installed"
return 0
fi
if [[ "${ATLAS_DRY_RUN}" == "1" ]]; then
atlas_log_info "Dry-run mode: would remove conflicting packages: ${installed[*]}"
return 0
fi
atlas_log_info "Removing conflicting Docker packages: ${installed[*]}"
atlas_sudo_run apt-get remove -y "${installed[@]}"
}
configure_repository() {
local arch repo_line
arch="$(dpkg --print-architecture)"
repo_line="deb [arch=${arch} signed-by=${DOCKER_KEYRING}] ${DOCKER_REPO_BASE} ${VERSION_CODENAME} stable"
if [[ -r "$DOCKER_SOURCE" ]] && grep -Fqx "$repo_line" "$DOCKER_SOURCE" && [[ -r "$DOCKER_KEYRING" ]]; then
atlas_log_ok "Docker APT repository is already configured"
return 0
fi
if [[ "${ATLAS_DRY_RUN}" == "1" ]]; then
atlas_log_info "Dry-run mode: would install Docker GPG key to ${DOCKER_KEYRING}"
atlas_log_info "Dry-run mode: would write Docker APT source ${DOCKER_SOURCE}"
atlas_log_info "Dry-run mode: would run apt-get update"
return 0
fi
atlas_sudo_run install -m 0755 -d /etc/apt/keyrings
tmp_key="$(mktemp)"
curl -fsSL "${DOCKER_REPO_BASE}/gpg" -o "$tmp_key"
atlas_sudo_run install -m 0644 "$tmp_key" "$DOCKER_KEYRING"
rm -f "$tmp_key"
printf '%s\n' "$repo_line" | sudo -n tee "$DOCKER_SOURCE" >/dev/null
atlas_sudo_run apt-get update
}
install_docker_packages() {
local missing=()
local package_name
for package_name in "${DOCKER_PACKAGES[@]}"; do
atlas_package_installed "$package_name" || missing+=("$package_name")
done
if [[ "${#missing[@]}" -eq 0 ]]; then
atlas_log_ok "Docker packages are already installed"
else
atlas_install_packages "${missing[@]}"
fi
}
enable_docker_service() {
atlas_log_info "Ensuring Docker service is enabled and active"
atlas_sudo_run systemctl enable docker
atlas_sudo_run systemctl start docker
}
ensure_docker_group() {
if id -nG "$DOCKER_USER" | tr ' ' '\n' | grep -qx docker; then
atlas_log_ok "User ${DOCKER_USER} is already in the docker group"
return 0
fi
if [[ "${ATLAS_DRY_RUN}" == "1" ]]; then
atlas_log_info "Dry-run mode: would add ${DOCKER_USER} to docker group"
return 0
fi
atlas_sudo_run usermod -aG docker "$DOCKER_USER"
atlas_log_warn "User ${DOCKER_USER} was added to docker group; a new login may be required"
}
verify_docker() {
if [[ "${ATLAS_DRY_RUN}" == "1" ]]; then
atlas_log_info "Dry-run mode: skipping Docker runtime verification"
return 0
fi
docker --version
docker compose version
docker buildx version
systemctl is-enabled docker >/dev/null
systemctl is-active docker >/dev/null
if docker info >/dev/null 2>&1; then
atlas_log_ok "Docker is available to the current shell without sudo"
else
atlas_log_warn "Current shell cannot use Docker without sudo; a new login or sg docker may be required"
sudo -n docker info >/dev/null
fi
atlas_log_ok "Docker verification passed"
}
main() {
load_os_release
atlas_require_sudo_noninteractive || {
atlas_log_error "Passwordless non-interactive sudo is required"
exit "${ATLAS_EXIT_PREREQ}"
}
atlas_require_command curl
atlas_require_command dpkg
atlas_require_command apt-get
atlas_require_command systemctl
docker_repo_available || {
atlas_log_error "Docker official repository is not available for ${VERSION_CODENAME}"
exit "${ATLAS_EXIT_PREREQ}"
}
install_prerequisites
remove_conflicts
configure_repository
install_docker_packages
enable_docker_service
ensure_docker_group
verify_docker
}
main