From 3972fee37ee382fb04b6ea02f9e43b6abead12cc Mon Sep 17 00:00:00 2001 From: Lach Date: Sat, 05 Apr 2025 12:19:37 +0000 Subject: [PATCH] feat: explicitly mark hosts as managed by fleet --- --- a/Cargo.lock +++ b/Cargo.lock @@ -924,6 +924,7 @@ "hostname", "human-repr", "indicatif", + "indoc", "itertools 0.13.0", "nix-eval", "nixlike", @@ -1537,6 +1538,12 @@ ] [[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" --- a/cmds/fleet/Cargo.toml +++ b/cmds/fleet/Cargo.toml @@ -47,6 +47,7 @@ nix-eval.workspace = true nom = "7.1.3" fleet-base = { version = "0.1.0", path = "../../crates/fleet-base" } +indoc = "2.0.6" [features] default = ["indicatif"] --- a/cmds/fleet/src/cmds/build_systems.rs +++ b/cmds/fleet/src/cmds/build_systems.rs @@ -1,6 +1,6 @@ -use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf, time::Duration}; +use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf, str::FromStr, time::Duration}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use clap::{Parser, ValueEnum}; use fleet_base::{ host::{Config, ConfigHost}, @@ -132,6 +132,7 @@ disable_rollback: bool, ) -> Result<()> { let mut failed = false; + // TODO: Lockfile, to prevent concurrent system switch? // TODO: If rollback target exists - bail, it should be removed. Lockfile will not work in case if rollback // is scheduler on next boot (default behavior). On current boot - rollback activator will fail due to @@ -332,6 +333,24 @@ } } +#[derive(Clone, PartialEq, Copy)] +enum DeployKind { + // NixOS => NixOS managed by fleet + UpgradeToFleet, + // NixOS managed by fleet => NixOS managed by fleet + Fleet, +} +impl FromStr for DeployKind { + type Err = anyhow::Error; + fn from_str(s: &str) -> std::result::Result { + match s { + "upgrade-to-fleet" => Ok(Self::UpgradeToFleet), + "fleet" => Ok(Self::Fleet), + v => bail!("unknown deploy_kind: {v}; expected on of \"upgrade-to-fleet\", \"fleet\""), + } + } +} + impl Deploy { pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> { let hosts = opts.filter_skipped(config.list_hosts().await?).await?; @@ -348,6 +367,8 @@ let local_host = config.local_host(); let opts = opts.clone(); let batch = batch.clone(); + let mut deploy_kind: Option = + opts.action_attr(&host, "deploy_kind").await?; set.spawn_local( (async move { @@ -356,10 +377,40 @@ { Ok(path) => path, Err(e) => { - error!("failed to deploy host: {}", e); + error!("failed to build host system closure: {}", e); return; } }; + if deploy_kind == None { + let is_fleet_managed = match host.file_exists("/etc/FLEET_HOST").await { + Ok(v) => v, + Err(e) => { + error!("failed to query remote system kind: {}", e); + return; + }, + }; + if !is_fleet_managed { + error!(indoc::indoc!{" + host is not marked as managed by fleet + if you're not trying to lustrate/install system from scratch, + you should either + 1. manually create /etc/FLEET_HOST file on the target host, + 2. use ?deploy_kind=fleet host argument if you're upgrading from older version of fleet + 3. use ?deploy_kind=upgrade_to_fleet if you're upgrading from plain nixos to fleet-managed nixos + "}); + return; + } + deploy_kind = Some(DeployKind::Fleet); + } + let deploy_kind = deploy_kind.expect("deploy_kind is set"); + + // TODO: Make disable_rollback a host attribute instead + let mut disable_rollback = self.disable_rollback; + if !disable_rollback && deploy_kind != DeployKind::Fleet { + warn!("disabling rollback, as not supported by non-fleet deployment kinds"); + disable_rollback = true; + } + if !opts.is_local(&hostname) { info!("uploading system closure"); { @@ -411,7 +462,7 @@ error!("unreachable? failed to get specialization"); return; }, - self.disable_rollback, + disable_rollback, ) .await { --- a/cmds/fleet/src/main.rs +++ b/cmds/fleet/src/main.rs @@ -66,9 +66,9 @@ #[derive(Parser)] enum Opts { - /// Prepare systems for deployments + /// Build system closures BuildSystems(BuildSystems), - + /// Upload and switch system closures Deploy(Deploy), /// Secret management #[clap(subcommand)] --- a/crates/fleet-base/src/command.rs +++ b/crates/fleet-base/src/command.rs @@ -5,6 +5,7 @@ use futures::StreamExt; use itertools::Either; use openssh::{OverSsh, OwningCommand, Session}; +use serde::de::DeserializeOwned; use tokio::{io::AsyncRead, process::Command, select}; use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec}; use tracing::debug; @@ -230,6 +231,10 @@ let bytes = self.run_bytes().await?; Ok(String::from_utf8(bytes)?) } + pub async fn run_value(self) -> Result { + let v = self.run_string().await?; + Ok(serde_json::from_str(&v)?) + } pub async fn run_bytes(self) -> Result> { let str = self.clone().into_string(); let cmd = self.wrap_sudo_if_needed().into_command()?; --- a/crates/fleet-base/src/host.rs +++ b/crates/fleet-base/src/host.rs @@ -105,6 +105,14 @@ let path = cmd.run_string().await?; Ok(path.trim_end().to_owned()) } + pub async fn file_exists(&self, path: impl AsRef) -> Result { + let mut cmd = self.cmd("sh").await?; + cmd.arg("-c") + .arg("test -e \"$1\" && echo true || echo false") + .arg("_") + .arg(path); + Ok(cmd.run_value().await?) + } pub async fn read_file_bin(&self, path: impl AsRef) -> Result> { let mut cmd = self.cmd("cat").await?; cmd.arg(path); --- a/modules/nixos/meta.nix +++ b/modules/nixos/meta.nix @@ -1,8 +1,17 @@ -{lib, ...}: let +{ lib, ... }: +let inherit (lib.modules) mkRemovedOptionModule; -in { +in +{ imports = [ - (mkRemovedOptionModule ["tags"] "tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts.") - (mkRemovedOptionModule ["network"] "network is now defined at the host level, not the nixos system level") + (mkRemovedOptionModule [ "tags" ] + "tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts." + ) + (mkRemovedOptionModule [ + "network" + ] "network is now defined at the host level, not the nixos system level") ]; + + # Version of environment (fleet scripts such as rollback) already installed on the host + config.environment.etc.FLEET_HOST.text = "1"; } -- gitstuff