diff --git a/cmd/do-obsd/main.go b/cmd/do-obsd/main.go new file mode 100644 index 00000000..83290a4e --- /dev/null +++ b/cmd/do-obsd/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/digitalocean/do-agent/internal/log" +) + +var ( + version = "dev" + revision = "none" + syslogFlag = flag.Bool("syslog", false, "enable logging to syslog") + versionFlag = flag.Bool("version", false, "display the version and exit") +) + +func main() { + flag.Parse() + + if *versionFlag { + fmt.Printf("do-obsd %s (rev: %s)\n", version, revision) + os.Exit(0) + } + + if *syslogFlag { + if err := log.InitSyslog(); err != nil { + log.Error("failed to initialize syslog: %+v", err) + } + } + + log.SetLevel(log.LevelDebug) + log.Debug("do-obsd: starting version=%s revision=%s pid=%d", version, revision, os.Getpid()) + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) + sig := <-sigCh + + log.Debug("do-obsd: received %s, shutting down", sig) +} diff --git a/packaging/etc/systemd/system/do-obsd.service b/packaging/etc/systemd/system/do-obsd.service new file mode 100644 index 00000000..4c719574 --- /dev/null +++ b/packaging/etc/systemd/system/do-obsd.service @@ -0,0 +1,20 @@ +[Unit] +Description=DigitalOcean Observability Supervisor +After=network-online.target +Wants=network-online.target + +[Service] +User=do-agent +ExecStart=/opt/digitalocean/bin/do-obsd --syslog +Restart=on-failure +RestartSec=5s + +OOMScoreAdjust=-900 +SyslogIdentifier=DigitalOceanObservabilitySupervisor +PrivateTmp=yes +ProtectSystem=full +ProtectHome=yes +NoNewPrivileges=yes + +[Install] +WantedBy=multi-user.target diff --git a/packaging/scripts/do-obsd-after-install.sh b/packaging/scripts/do-obsd-after-install.sh new file mode 100755 index 00000000..4b273928 --- /dev/null +++ b/packaging/scripts/do-obsd-after-install.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# noexpandtab is required for EOF/heredoc +# vim: noexpandtab +# +# IMPORTANT: this script will execute with /bin/sh which is dash on some +# systems so this shebang should not be changed + +set -ue + +SVC_NAME=do-obsd +INSTALL_DIR=/opt/digitalocean/${SVC_NAME} +CRON=/etc/cron.daily/${SVC_NAME} +POLKIT_RULE=/etc/polkit-1/rules.d/60-do-obsd.rules + +main() { + update_selinux + install_polkit_rule + enable_service + patch_updates +} + +update_selinux() { + echo "Detecting SELinux" + enforced=$(getenforce 2>/dev/null || echo) + + if [ "$enforced" != "Enforcing" ]; then + echo "SELinux not enforced" + return + fi + + echo "setting nis_enabled to 1 to allow ${SVC_NAME} to execute" + setsebool -P nis_enabled 1 || echo "Failed" > /dev/stderr + systemctl daemon-reexec || true +} + +install_polkit_rule() { + echo "Installing polkit rule for do-otelcol.service management" + mkdir -p /etc/polkit-1/rules.d + cat > "${POLKIT_RULE}" <<'POLKIT_EOF' +polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.systemd1.manage-units" && + action.lookup("unit") == "do-otelcol.service" && + subject.user == "do-agent") { + return polkit.Result.YES; + } +}); +POLKIT_EOF +} + +enable_service() { + if command -v systemctl >/dev/null 2>&1; then + echo "enable systemd service" + systemctl daemon-reload + systemctl enable -f ${SVC_NAME} + systemctl restart ${SVC_NAME} + else + echo "Unknown init system. Exiting..." > /dev/stderr + exit 1 + fi +} + +patch_updates() { + [ -f "${CRON}" ] && rm -f "${CRON}" + script="${INSTALL_DIR}/scripts/update.sh" + + cat <<-EOF > "${CRON}" + #!/bin/sh + /bin/bash ${script} >/dev/null 2>&1 + EOF + + chmod +x "${CRON}" +} + +main diff --git a/packaging/scripts/do-obsd-after-remove.sh b/packaging/scripts/do-obsd-after-remove.sh new file mode 100755 index 00000000..ffd5ac8a --- /dev/null +++ b/packaging/scripts/do-obsd-after-remove.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# noexpandtab is required for EOF/heredoc +# vim: noexpandtab +# +# IMPORTANT: this script will execute with /bin/sh which is dash on some +# systems so this shebang should not be changed + +set -ue + +SVC_NAME=do-obsd +CRON=/etc/cron.daily/do-obsd +POLKIT_RULE=/etc/polkit-1/rules.d/60-do-obsd.rules + +# fix an issue where this script runs on upgrades for rpm +# see https://github.com/jordansissel/fpm/issues/1175#issuecomment-240086016 +arg="${1:-0}" + +main() { + if echo "${arg}" | grep -qP '^\d+$' && [ "${arg}" -gt 0 ]; then + # rpm upgrade + exit 0 + elif echo "${arg}" | grep -qP '^upgrade$'; then + # deb upgrade + exit 0 + fi + + if command -v systemctl >/dev/null 2>&1; then + clean_systemd + else + echo "Unknown init system" > /dev/stderr + fi + + remove_polkit_rule + remove_cron +} + +clean_systemd() { + echo "Cleaning up systemd scripts" + systemctl stop ${SVC_NAME} || true + systemctl disable ${SVC_NAME}.service || true + systemctl daemon-reload || true +} + +remove_polkit_rule() { + echo "Removing polkit rule" + rm -f "${POLKIT_RULE}" +} + +remove_cron() { + rm -fv "${CRON}" +} + +main diff --git a/packaging/scripts/do-obsd-install.sh b/packaging/scripts/do-obsd-install.sh new file mode 100755 index 00000000..e3b2ff91 --- /dev/null +++ b/packaging/scripts/do-obsd-install.sh @@ -0,0 +1,199 @@ +#!/bin/bash +# +# This script is meant for quick & easy install via: +# curl -sSL https://repos.insights.digitalocean.com/do-obsd-install.sh | sudo bash +# or: +# wget -qO- https://repos.insights.digitalocean.com/do-obsd-install.sh | sudo bash +# +# vim: noexpandtab + +set -ueo pipefail + +REPO_HOST=https://repos.insights.digitalocean.com +REPO_GPG_KEY=${REPO_HOST}/sonar-agent.asc + +repo="do-obsd-preview" +stable_repo="do-agent" + +dist="unknown" +deb_list=/etc/apt/sources.list.d/digitalocean-obsd.list +deb_keyfile=/usr/share/keyrings/digitalocean-obsd-keyring.gpg +rpm_repo=/etc/yum.repos.d/digitalocean-obsd.repo + +# do-agent stable channel sources (needed to resolve the do-agent dependency) +stable_deb_list=/etc/apt/sources.list.d/digitalocean-agent.list +stable_deb_keyfile=/usr/share/keyrings/digitalocean-agent-keyring.gpg +stable_rpm_repo=/etc/yum.repos.d/digitalocean-agent.repo + +function main() { + [ "$(id -u)" != "0" ] && \ + abort "This script must be executed as root." + + clean + check_do + check_dist + + case "${dist}" in + debian|ubuntu) + install_apt + ;; + centos|cloudlinux|fedora|almalinux|rocky) + install_rpm + ;; + *) + not_supported + ;; + esac +} + +function wait_for_apt() { + while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do + echo "Waiting on apt.." + sleep 2 + done +} + +function install_apt() { + export DEBIAN_FRONTEND=noninteractive + wait_for_apt && ( apt-get purge -y do-obsd >/dev/null 2>&1 || : ) + + echo "Installing apt repository..." + wait_for_apt && ( apt-get -qq update || true ) + wait_for_apt && apt-get -qq install -y ca-certificates gnupg2 apt-utils apt-transport-https curl + + echo -n "Installing gpg key..." + curl -sL "${REPO_GPG_KEY}" | gpg --dearmor >"${deb_keyfile}" + + # configure do-agent stable channel so the do-agent dependency resolves + if [ ! -f "${stable_deb_list}" ]; then + echo "Configuring do-agent stable channel..." + cp "${deb_keyfile}" "${stable_deb_keyfile}" + echo "deb [signed-by=${stable_deb_keyfile}] ${REPO_HOST}/apt/${stable_repo} main main" >"${stable_deb_list}" + wait_for_apt && apt-get -qq update -o Dir::Etc::SourceParts=/dev/null -o APT::Get::List-Cleanup=no -o Dir::Etc::SourceList="sources.list.d/digitalocean-agent.list" + fi + + # configure do-obsd preview channel + echo "deb [signed-by=${deb_keyfile}] ${REPO_HOST}/apt/${repo} main main" >"${deb_list}" + wait_for_apt && apt-get -qq update -o Dir::Etc::SourceParts=/dev/null -o APT::Get::List-Cleanup=no -o Dir::Etc::SourceList="sources.list.d/digitalocean-obsd.list" + + wait_for_apt && apt-get -qq install -y do-obsd +} + +function install_rpm() { + echo "Installing yum repository..." + + yum remove -y do-obsd || : + + yum install -y gpgme ca-certificates + + # configure do-agent stable channel so the do-agent dependency resolves + if [ ! -f "${stable_rpm_repo}" ]; then + echo "Configuring do-agent stable channel..." + cat <<-STABLE_EOF > "${stable_rpm_repo}" + [digitalocean-agent] + name=DigitalOcean Agent + baseurl=${REPO_HOST}/yum/${stable_repo}/\$basearch + repo_gpgcheck=0 + gpgcheck=1 + enabled=1 + gpgkey=${REPO_GPG_KEY} + sslverify=0 + sslcacert=/etc/pki/tls/certs/ca-bundle.crt + metadata_expire=300 + STABLE_EOF + yum --disablerepo="*" --enablerepo="digitalocean-agent" makecache + fi + + # configure do-obsd preview channel + cat <<-EOF > /etc/yum.repos.d/digitalocean-obsd.repo + [digitalocean-obsd] + name=DigitalOcean Observability Supervisor + baseurl=${REPO_HOST}/yum/${repo}/\$basearch + repo_gpgcheck=0 + gpgcheck=1 + enabled=1 + gpgkey=${REPO_GPG_KEY} + sslverify=0 + sslcacert=/etc/pki/tls/certs/ca-bundle.crt + metadata_expire=300 + EOF + + yum --disablerepo="*" --enablerepo="digitalocean-obsd" makecache + yum install -y do-obsd +} + +function clean() { + echo -n "Cleaning up old sources..." + if [ -f "$deb_list" ]; then + rm -f "${deb_list}" + elif [ -f "$rpm_repo" ]; then + rm -f "${rpm_repo}" + fi + echo "OK" +} + +function check_dist() { + echo -n "Verifying compatibility with script..." + if [ -f /etc/os-release ]; then + dist=$(awk -F= '$1 == "ID" {gsub("\"", ""); print$2}' /etc/os-release) + elif [ -f /etc/redhat-release ]; then + dist=$(awk '{print tolower($1)}' /etc/redhat-release) + else + not_supported + fi + + dist=$(echo "${dist}" | tr '[:upper:]' '[:lower:]') + + case "${dist}" in + debian|ubuntu|centos|fedora|rocky) + echo "OK" + ;; + cloudlinux|almalinux) + echo "WARN ${dist} is not officially supported. Attempting RPM install" + ;; + *) + not_supported + ;; + esac +} + + +function check_do() { + echo -n "Verifying machine compatibility..." + read -r sys_vendor < /sys/devices/virtual/dmi/id/bios_vendor + if ! [ "$sys_vendor" = "DigitalOcean" ]; then + cat <<-EOF + + The DigitalOcean Observability Supervisor is only supported on DigitalOcean machines. + + If you are seeing this message on an older droplet, you may need to power-off + and then power-on at http://cloud.digitalocean.com. After power-cycling, + please re-run this script. + + EOF + exit 1 + fi + echo "OK" +} + +function not_supported() { + cat <<-EOF + + This script does not support the OS/Distribution on this machine. + If you feel that this is an error contact support@digitalocean.com + or create an issue at https://github.com/digitalocean/do-agent/issues/new. + + EOF + exit 1 +} + +# abort with an error message +function abort() { + read -r line func file <<< "$(caller 0)" + echo "ERROR in $file:$func:$line: $1" > /dev/stderr + exit 1 +} + + +# leave this last to prevent any partial execution +main diff --git a/packaging/scripts/do-obsd-update.sh b/packaging/scripts/do-obsd-update.sh new file mode 100755 index 00000000..5452717a --- /dev/null +++ b/packaging/scripts/do-obsd-update.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# vim: noexpandtab + +main() { + # add some jitter to prevent overloading the packaging machines + sleep $(( RANDOM % 900 )) + + export DEBIAN_FRONTEND="noninteractive" + if command -v apt-get 2&>/dev/null; then + apt-get -qq update -o Dir::Etc::SourceParts=/dev/null -o APT::Get::List-Cleanup=no -o Dir::Etc::SourceList="sources.list.d/digitalocean-obsd.list" + apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -qq install -y --only-upgrade do-obsd + elif command -v yum 2&>/dev/null; then + yum -q -y --disablerepo="*" --enablerepo="digitalocean-obsd" makecache + yum -q -y update do-obsd + fi +} + +main