diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e611191..3698ff1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,7 @@ jobs: device=$(losetup --list --noheadings --output NAME,BACK-FILE | grep myimage.raw | awk '{print $1}') sudo mount "${device}p2" /mnt/ sudo ls /mnt/EFI/centos/{grub.cfg,shimx64.efi} + sudo umount /mnt sudo losetup -D "${device}" sudo rm -f myimage.raw - name: bootc install to filesystem diff --git a/Makefile b/Makefile index 93edc613..f5292a4c 100644 --- a/Makefile +++ b/Makefile @@ -18,10 +18,8 @@ ifeq ($(CONTAINER_RUNTIME), podman) IMAGE_PREFIX = localhost/ endif -units = $(addprefix systemd/, bootupd.service bootupd.socket) - .PHONY: all -all: $(units) +all: cargo build ${CARGO_ARGS} ln -f target/${PROFILE}/bootupd target/${PROFILE}/bootupctl @@ -33,17 +31,11 @@ create-build-container: build-in-container: create-build-container ${CONTAINER_RUNTIME} run -ti --rm -v .:/srv/bootupd:z ${IMAGE_PREFIX}${IMAGE_NAME} make -.PHONY: install-units -install-units: $(units) - for unit in $(units); do install -D -m 644 --target-directory=$(DESTDIR)$(PREFIX)/lib/systemd/system/ $$unit; done - .PHONY: install -install: install-units +install: mkdir -p "${DESTDIR}$(PREFIX)/bin" "${DESTDIR}$(LIBEXECDIR)" install -D -t "${DESTDIR}$(LIBEXECDIR)" target/${PROFILE}/bootupd ln -f ${DESTDIR}$(LIBEXECDIR)/bootupd ${DESTDIR}$(PREFIX)/bin/bootupctl - install -d "${DESTDIR}$(PREFIX)/lib/systemd/system/multi-user.target.wants" - ln -s ../bootupd.socket "${DESTDIR}$(PREFIX)/lib/systemd/system/multi-user.target.wants" install-grub-static: install -m 644 -D -t ${DESTDIR}$(PREFIX)/lib/bootupd/grub2-static src/grub2/*.cfg diff --git a/contrib/packaging/bootupd.spec b/contrib/packaging/bootupd.spec index 056658f3..91da18f1 100644 --- a/contrib/packaging/bootupd.spec +++ b/contrib/packaging/bootupd.spec @@ -52,15 +52,6 @@ cargo build --release %make_install INSTALL="install -p -c" make install-grub-static DESTDIR=%{?buildroot} INSTALL="%{__install} -p" -%post -n %{crate} -%systemd_post bootupd.service bootupd.socket - -%preun -n %{crate} -%systemd_preun bootupd.service bootupd.socket - -%postun -n %{crate} -%systemd_postun bootupd.service bootupd.socket - %changelog * Tue Oct 18 2022 Colin Walters - 0.2.8-3 - Dummy changelog \ No newline at end of file diff --git a/src/bootupd.rs b/src/bootupd.rs index d0eef93c..72d025d0 100644 --- a/src/bootupd.rs +++ b/src/bootupd.rs @@ -1,31 +1,18 @@ #[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))] use crate::bios; +use crate::component; use crate::component::{Component, ValidationResult}; use crate::coreos; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use crate::efi; use crate::model::{ComponentStatus, ComponentUpdatable, ContentMetadata, SavedState, Status}; use crate::util; -use crate::{component, ipc}; use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::collections::BTreeMap; use std::path::Path; -/// A message sent from client to server -#[derive(Debug, Serialize, Deserialize)] -pub(crate) enum ClientRequest { - /// Update a component - Update { component: String }, - /// Update a component via adoption - AdoptAndUpdate { component: String }, - /// Validate a component - Validate { component: String }, - /// Print the current state - Status, -} - pub(crate) enum ConfigMode { None, Static, @@ -408,8 +395,8 @@ pub(crate) fn print_status(status: &Status) -> Result<()> { Ok(()) } -pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result<()> { - let status: Status = c.send(&ClientRequest::Status)?; +pub(crate) fn client_run_update() -> Result<()> { + let status: Status = status()?; if status.components.is_empty() && status.adoptable.is_empty() { println!("No components installed."); return Ok(()); @@ -420,9 +407,7 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result ComponentUpdatable::Upgradable => {} _ => continue, }; - match c.send(&ClientRequest::Update { - component: name.to_string(), - })? { + match update(name)? { ComponentUpdateResult::AtLatestVersion => { // Shouldn't happen unless we raced with another client eprintln!( @@ -450,9 +435,7 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result } for (name, adoptable) in status.adoptable.iter() { if adoptable.confident { - let r: ContentMetadata = c.send(&ClientRequest::AdoptAndUpdate { - component: name.to_string(), - })?; + let r: ContentMetadata = adopt_and_update(name)?; println!("Adopted and updated: {}: {}", name, r.version); updated = true; } else { @@ -465,32 +448,28 @@ pub(crate) fn client_run_update(c: &mut ipc::ClientToDaemonConnection) -> Result Ok(()) } -pub(crate) fn client_run_adopt_and_update(c: &mut ipc::ClientToDaemonConnection) -> Result<()> { - let status: Status = c.send(&ClientRequest::Status)?; +pub(crate) fn client_run_adopt_and_update() -> Result<()> { + let status: Status = status()?; if status.adoptable.is_empty() { println!("No components are adoptable."); } else { for (name, _) in status.adoptable.iter() { - let r: ContentMetadata = c.send(&ClientRequest::AdoptAndUpdate { - component: name.to_string(), - })?; + let r: ContentMetadata = adopt_and_update(name)?; println!("Adopted and updated: {}: {}", name, r.version); } } Ok(()) } -pub(crate) fn client_run_validate(c: &mut ipc::ClientToDaemonConnection) -> Result<()> { - let status: Status = c.send(&ClientRequest::Status)?; +pub(crate) fn client_run_validate() -> Result<()> { + let status: Status = status()?; if status.components.is_empty() { println!("No components installed."); return Ok(()); } let mut caught_validation_error = false; for (name, _) in status.components.iter() { - match c.send(&ClientRequest::Validate { - component: name.to_string(), - })? { + match validate(name)? { ValidationResult::Valid => { println!("Validated: {}", name); } diff --git a/src/cli/bootupctl.rs b/src/cli/bootupctl.rs index 3883a7a7..e8150b1e 100644 --- a/src/cli/bootupctl.rs +++ b/src/cli/bootupctl.rs @@ -1,10 +1,23 @@ use crate::bootupd; -use crate::ipc::ClientToDaemonConnection; -use crate::model::Status; use anyhow::Result; use clap::Parser; use log::LevelFilter; +use std::os::unix::process::CommandExt; +use std::process::Command; + +static SYSTEMD_ARGS_BOOTUPD: &[&str] = &[ + "--unit", + "bootupd", + "--property", + "PrivateNetwork=yes", + "--property", + "ProtectHome=yes", + "--property", + "MountFlags=slave", + "--pipe", +]; + /// `bootupctl` sub-commands. #[derive(Debug, Parser)] #[clap(name = "bootupctl", about = "Bootupd client application", version)] @@ -87,10 +100,8 @@ impl CtlCommand { /// Runner for `status` verb. fn run_status(opts: StatusOpts) -> Result<()> { - let mut client = ClientToDaemonConnection::new(); - client.connect()?; - - let r: Status = client.send(&bootupd::ClientRequest::Status)?; + ensure_running_in_systemd()?; + let r = bootupd::status()?; if opts.json { let stdout = std::io::stdout(); let mut stdout = stdout.lock(); @@ -101,38 +112,54 @@ impl CtlCommand { bootupd::print_status(&r)?; } - client.shutdown()?; Ok(()) } /// Runner for `update` verb. fn run_update() -> Result<()> { - let mut client = ClientToDaemonConnection::new(); - client.connect()?; - - bootupd::client_run_update(&mut client)?; - - client.shutdown()?; - Ok(()) + ensure_running_in_systemd()?; + bootupd::client_run_update() } /// Runner for `update` verb. fn run_adopt_and_update() -> Result<()> { - let mut client = ClientToDaemonConnection::new(); - client.connect()?; - - bootupd::client_run_adopt_and_update(&mut client)?; - - client.shutdown()?; - Ok(()) + ensure_running_in_systemd()?; + bootupd::client_run_adopt_and_update() } /// Runner for `validate` verb. fn run_validate() -> Result<()> { - let mut client = ClientToDaemonConnection::new(); - client.connect()?; - bootupd::client_run_validate(&mut client)?; - client.shutdown()?; - Ok(()) + ensure_running_in_systemd()?; + bootupd::client_run_validate() + } +} + +/// Checks if the current process is (apparently at least) +/// running under systemd. +fn running_in_systemd() -> bool { + std::env::var_os("INVOCATION_ID").is_some() +} + +/// Require root permission +fn require_root_permission() -> Result<()> { + if !nix::unistd::Uid::effective().is_root() { + anyhow::bail!("This command requires root privileges") + } + Ok(()) +} + +/// Detect if we're running in systemd; if we're not, we re-exec ourselves via +/// systemd-run. Then we can just directly run code in what is now the daemon. +fn ensure_running_in_systemd() -> Result<()> { + require_root_permission()?; + let running_in_systemd = running_in_systemd(); + if !running_in_systemd { + let r = Command::new("systemd-run") + .args(SYSTEMD_ARGS_BOOTUPD) + .args(std::env::args()) + .exec(); + // If we got here, it's always an error + return Err(r.into()); } + Ok(()) } diff --git a/src/cli/bootupd.rs b/src/cli/bootupd.rs index d2233761..4a6b8cf1 100644 --- a/src/cli/bootupd.rs +++ b/src/cli/bootupd.rs @@ -31,8 +31,6 @@ impl DCommand { /// CLI sub-commands. #[derive(Debug, Parser)] pub enum DVerb { - #[clap(name = "daemon", about = "Run service logic")] - Daemon, #[clap(name = "generate-update-metadata", about = "Generate metadata")] GenerateUpdateMetadata(GenerateOpts), #[clap(name = "install", about = "Install components")] @@ -88,7 +86,6 @@ impl DCommand { /// Run CLI application. pub fn run(self) -> Result<()> { match self.cmd { - DVerb::Daemon => crate::daemon::run(), DVerb::Install(opts) => Self::run_install(opts), DVerb::GenerateUpdateMetadata(opts) => Self::run_generate_meta(opts), } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c89c8661..98d45c0c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -62,7 +62,10 @@ mod tests { #[test] fn test_multicall_dispatch() { { - let d_argv = vec!["/usr/bin/bootupd".to_string(), "daemon".to_string()]; + let d_argv = vec![ + "/usr/bin/bootupd".to_string(), + "generate-update-metadata".to_string(), + ]; let cli = MultiCall::from_args(d_argv); match cli { MultiCall::Ctl(cmd) => panic!("{:?}", cmd), @@ -89,12 +92,15 @@ mod tests { #[test] fn test_verbosity() { - let default = MultiCall::from_args(vec!["bootupd".to_string(), "daemon".to_string()]); + let default = MultiCall::from_args(vec![ + "bootupd".to_string(), + "generate-update-metadata".to_string(), + ]); assert_eq!(default.loglevel(), LevelFilter::Warn); let info = MultiCall::from_args(vec![ "bootupd".to_string(), - "daemon".to_string(), + "generate-update-metadata".to_string(), "-v".to_string(), ]); assert_eq!(info.loglevel(), LevelFilter::Info); diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs deleted file mode 100644 index b83ba74f..00000000 --- a/src/daemon/mod.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! Daemon logic. - -use crate::component::ValidationResult; -use crate::model::Status; -use crate::{bootupd, ipc}; -use anyhow::{bail, Context, Result}; -use nix::sys::socket as nixsocket; -use std::os::unix::io::RawFd; - -/// Accept a single client and then exit; we don't want to -/// persistently run as a daemon. The systemd unit is mostly -/// and implementation detail - it lets us use things like -/// systemd's built in sandboxing (ProtectHome=yes) etc. and also -/// ensures that only a single bootupd instance is running at -/// a time (i.e. don't support concurrent updates). -pub fn run() -> Result<()> { - let srvsock_fd = systemd_activation().context("systemd service activation error")?; - - // Accept an incoming client. - let client = match accept_authenticate_client(srvsock_fd) { - Ok(auth_client) => auth_client, - Err(e) => { - log::error!("failed to authenticate client: {}", e); - return Ok(()); - } - }; - - // Process all requests from this client. - if let Err(e) = process_client_requests(client) { - log::error!("failed to process request from client: {}", e); - } - - // Sleep for a half second to avoid triggering systemd service - // restart limits. - std::thread::sleep(std::time::Duration::from_secs_f32(0.5)); - - Ok(()) -} - -/// Perform initialization steps required by systemd service activation. -/// -/// This ensures that the system is running under systemd, then receives the -/// socket-FD for main IPC logic, and notifies systemd about ready-state. -fn systemd_activation() -> Result { - use libsystemd::daemon::{self, NotifyState}; - use std::os::unix::io::IntoRawFd; - - if !daemon::booted() { - bail!("daemon is not running as a systemd service"); - } - - let srvsock_fd = { - let mut fds = libsystemd::activation::receive_descriptors(true) - .map_err(|e| anyhow::anyhow!("failed to receive file-descriptors: {}", e))?; - let srvsock_fd = if let Some(fd) = fds.pop() { - fd - } else { - bail!("no socket-fd received on service activation"); - }; - srvsock_fd.into_raw_fd() - }; - - let sent = daemon::notify(true, &[NotifyState::Ready]) - .map_err(|e| anyhow::anyhow!("failed to notify ready-state: {}", e))?; - if !sent { - log::warn!("failed to notify ready-state: service notifications not supported"); - } - - Ok(srvsock_fd) -} - -/// Accept an incoming connection, then authenticate the client. -fn accept_authenticate_client(srvsock_fd: RawFd) -> Result { - let accepted = nixsocket::accept4(srvsock_fd, nixsocket::SockFlag::SOCK_CLOEXEC)?; - let client = ipc::UnauthenticatedClient::new(accepted); - - let authed = client.authenticate()?; - - Ok(authed) -} - -/// Process all requests from a given client. -/// -/// This sequentially processes all requests from a client, until it -/// disconnects or a connection error is encountered. -fn process_client_requests(client: ipc::AuthenticatedClient) -> Result<()> { - use crate::bootupd::ClientRequest; - - let mut buf = [0u8; ipc::MSGSIZE]; - loop { - let n = nixsocket::recv(client.fd, &mut buf, nixsocket::MsgFlags::MSG_CMSG_CLOEXEC)?; - let buf = &buf[0..n]; - if buf.is_empty() { - log::trace!("client disconnected"); - break; - } - - let msg = bincode::deserialize(buf)?; - log::trace!("processing request: {:?}", &msg); - let r = match msg { - ClientRequest::Update { component } => { - bincode::serialize(&match bootupd::update(component.as_str()) { - Ok(v) => ipc::DaemonToClientReply::Success::(v), - Err(e) => ipc::DaemonToClientReply::Failure(format!("{:#}", e)), - })? - } - ClientRequest::AdoptAndUpdate { component } => { - bincode::serialize(&match bootupd::adopt_and_update(component.as_str()) { - Ok(v) => ipc::DaemonToClientReply::Success::(v), - Err(e) => ipc::DaemonToClientReply::Failure(format!("{:#}", e)), - })? - } - ClientRequest::Validate { component } => { - bincode::serialize(&match bootupd::validate(component.as_str()) { - Ok(v) => ipc::DaemonToClientReply::Success::(v), - Err(e) => ipc::DaemonToClientReply::Failure(format!("{:#}", e)), - })? - } - ClientRequest::Status => bincode::serialize(&match bootupd::status() { - Ok(v) => ipc::DaemonToClientReply::Success::(v), - Err(e) => ipc::DaemonToClientReply::Failure(format!("{:#}", e)), - })?, - }; - let written = nixsocket::send(client.fd, &r, nixsocket::MsgFlags::MSG_CMSG_CLOEXEC)?; - if written != r.len() { - bail!("wrote {} bytes to client, expected {}", written, r.len()); - } - } - Ok(()) -} diff --git a/src/ipc.rs b/src/ipc.rs deleted file mode 100644 index c57b0caa..00000000 --- a/src/ipc.rs +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2020 Red Hat, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -use anyhow::{bail, Context, Result}; -use fn_error_context::context; -use nix::sys::socket as nixsocket; -use serde::{Deserialize, Serialize}; -use std::os::unix::io::RawFd; - -pub(crate) const BOOTUPD_SOCKET: &str = "/run/bootupd.sock"; -pub(crate) const MSGSIZE: usize = 1_048_576; -/// Sent between processes along with SCM credentials -pub(crate) const BOOTUPD_HELLO_MSG: &str = "bootupd-hello\n"; - -#[derive(Debug, Serialize, Deserialize)] -pub(crate) enum DaemonToClientReply { - Success(T), - Failure(String), -} - -pub(crate) struct ClientToDaemonConnection { - fd: i32, -} - -impl Drop for ClientToDaemonConnection { - fn drop(&mut self) { - if self.fd != -1 { - nix::unistd::close(self.fd).expect("close"); - } - } -} - -impl ClientToDaemonConnection { - pub(crate) fn new() -> Self { - Self { fd: -1 } - } - - #[context("connecting to {}", BOOTUPD_SOCKET)] - pub(crate) fn connect(&mut self) -> Result<()> { - use nix::sys::uio::IoVec; - self.fd = nixsocket::socket( - nixsocket::AddressFamily::Unix, - nixsocket::SockType::SeqPacket, - nixsocket::SockFlag::SOCK_CLOEXEC, - None, - )?; - let addr = nixsocket::SockAddr::new_unix(BOOTUPD_SOCKET)?; - nixsocket::connect(self.fd, &addr)?; - let creds = libc::ucred { - pid: nix::unistd::getpid().as_raw(), - uid: nix::unistd::getuid().as_raw(), - gid: nix::unistd::getgid().as_raw(), - }; - let creds = nixsocket::UnixCredentials::from(creds); - let creds = nixsocket::ControlMessage::ScmCredentials(&creds); - let _ = nixsocket::sendmsg( - self.fd, - &[IoVec::from_slice(BOOTUPD_HELLO_MSG.as_bytes())], - &[creds], - nixsocket::MsgFlags::MSG_CMSG_CLOEXEC, - None, - )?; - Ok(()) - } - - pub(crate) fn send( - &mut self, - msg: &S, - ) -> Result { - { - let serialized = bincode::serialize(msg)?; - let _ = nixsocket::send(self.fd, &serialized, nixsocket::MsgFlags::MSG_CMSG_CLOEXEC) - .context("client sending request")?; - } - let reply: DaemonToClientReply = { - let mut buf = [0u8; MSGSIZE]; - let n = nixsocket::recv(self.fd, &mut buf, nixsocket::MsgFlags::MSG_CMSG_CLOEXEC) - .context("client recv")?; - let buf = &buf[0..n]; - if buf.is_empty() { - bail!("Server sent an empty reply"); - } - bincode::deserialize(buf).context("client parsing reply")? - }; - match reply { - DaemonToClientReply::Success::(r) => Ok(r), - DaemonToClientReply::Failure(buf) => { - // For now we just prefix server - anyhow::bail!("internal error: {}", buf); - } - } - } - - pub(crate) fn shutdown(&mut self) -> Result<()> { - nixsocket::shutdown(self.fd, nixsocket::Shutdown::Both)?; - Ok(()) - } -} - -pub(crate) struct UnauthenticatedClient { - fd: RawFd, -} - -impl UnauthenticatedClient { - pub(crate) fn new(fd: RawFd) -> Self { - Self { fd } - } - - pub(crate) fn authenticate(mut self) -> Result { - use nix::sys::uio::IoVec; - let fd = self.fd; - let mut buf = [0u8; 1024]; - - nixsocket::setsockopt(fd, nix::sys::socket::sockopt::PassCred, &true)?; - let iov = IoVec::from_mut_slice(buf.as_mut()); - let mut cmsgspace = nix::cmsg_space!(nixsocket::UnixCredentials); - let msg = nixsocket::recvmsg( - fd, - &[iov], - Some(&mut cmsgspace), - nixsocket::MsgFlags::MSG_CMSG_CLOEXEC, - )?; - let mut creds = None; - for cmsg in msg.cmsgs() { - if let nixsocket::ControlMessageOwned::ScmCredentials(c) = cmsg { - creds = Some(c); - break; - } - } - if let Some(creds) = creds { - if creds.uid() != 0 { - bail!("unauthorized pid:{} uid:{}", creds.pid(), creds.uid()) - } - println!("Connection from pid:{}", creds.pid()); - } else { - bail!("No SCM credentials provided"); - } - let hello = String::from_utf8_lossy(&buf[0..msg.bytes]); - if hello != BOOTUPD_HELLO_MSG { - bail!("Didn't receive correct hello message, found: {:?}", &hello); - } - let r = AuthenticatedClient { fd: self.fd }; - self.fd = -1; - Ok(r) - } -} - -impl Drop for UnauthenticatedClient { - fn drop(&mut self) { - if self.fd != -1 { - nix::unistd::close(self.fd).expect("close"); - } - } -} - -pub(crate) struct AuthenticatedClient { - pub(crate) fd: RawFd, -} - -impl Drop for AuthenticatedClient { - fn drop(&mut self) { - if self.fd != -1 { - nix::unistd::close(self.fd).expect("close"); - } - } -} diff --git a/src/main.rs b/src/main.rs index 5cbb02fd..b075bde0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,6 @@ mod bootupd; mod cli; mod component; mod coreos; -mod daemon; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] mod efi; mod filesystem; @@ -33,7 +32,6 @@ mod filetree; target_arch = "powerpc64" ))] mod grubconfigs; -mod ipc; mod model; mod model_legacy; mod ostreeutil; diff --git a/systemd/bootupd.service b/systemd/bootupd.service deleted file mode 100644 index 52735bfe..00000000 --- a/systemd/bootupd.service +++ /dev/null @@ -1,26 +0,0 @@ -[Unit] -Description=bootloader update daemon -Documentation=https://github.com/coreos/bootupd -# Because the daemon currently agressively auto-exits -# and our test suite runs many requests, let's allow -# a lot of restarts before failing. -StartLimitIntervalSec=2s -StartLimitBurst=10 - -[Service] -Type=notify -Environment=BOOTUPD_VERBOSITY="-v" -ExecStart=/usr/libexec/bootupd daemon $BOOTUPD_VERBOSITY -# This way our working directory isn't writable by default. -WorkingDirectory=/usr -# Various hardening flags just on general principle. We need -# to run as root, but let's avoid accidental damage. -ProtectHome=yes -ReadOnlyPaths=/usr -PrivateTmp=yes -PrivateNetwork=yes -ProtectHostname=yes -ProtectControlGroups=yes -RestrictSUIDSGID=yes -# So we can remount /boot writable -MountFlags=slave diff --git a/systemd/bootupd.socket b/systemd/bootupd.socket deleted file mode 100644 index c3f2269b..00000000 --- a/systemd/bootupd.socket +++ /dev/null @@ -1,6 +0,0 @@ -[Socket] -ListenSequentialPacket=/run/bootupd.sock -SocketMode=0600 - -[Install] -WantedBy=sockets.target diff --git a/tests/e2e-update/e2e-update.sh b/tests/e2e-update/e2e-update.sh index 2a3a1345..64af1afa 100755 --- a/tests/e2e-update/e2e-update.sh +++ b/tests/e2e-update/e2e-update.sh @@ -16,7 +16,8 @@ if test -z "${COSA_DIR:-}"; then fi # Validate source directory bootupd_git=$(cd ${dn} && git rev-parse --show-toplevel) -test -f ${bootupd_git}/systemd/bootupd.service +# https://github.com/coreos/bootupd/issues/551 +! test -f ${bootupd_git}/systemd/bootupd.service testtmp=$(mktemp -d -p /var/tmp bootupd-e2e.XXXXXXX) export test_tmpdir=${testtmp} diff --git a/tests/kola/test-bootupd b/tests/kola/test-bootupd index 7402c6fb..315656cc 100755 --- a/tests/kola/test-bootupd +++ b/tests/kola/test-bootupd @@ -47,18 +47,13 @@ assert_file_has_content_literal out.txt 'Update: At latest version' assert_file_has_content out.txt '^CoreOS aleph version:' ok status -# Validate we auto-exited -sleep 2 -systemctl show -p ActiveState bootupd > out.txt -assert_file_has_content_literal out.txt 'ActiveState=inactive' - bootupctl validate | tee out.txt ok validate if env LANG=C.UTF-8 runuser -u bin bootupctl status 2>err.txt; then fatal "Was able to bootupctl status as non-root" fi -assert_file_has_content err.txt 'error:.*: Permission denied' +assert_file_has_content err.txt 'error: This command requires root privileges' # From here we'll fake updates test -w /usr || rpm-ostree usroverlay