Skip to content

Commit 3972fee

Browse files
committed
feat: explicitly mark hosts as managed by fleet
Signed-off-by: Lach <[email protected]>
1 parent a1a72ce commit 3972fee

File tree

7 files changed

+91
-10
lines changed

7 files changed

+91
-10
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmds/fleet/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ indicatif = { version = "0.17", optional = true }
4747
nix-eval.workspace = true
4848
nom = "7.1.3"
4949
fleet-base = { version = "0.1.0", path = "../../crates/fleet-base" }
50+
indoc = "2.0.6"
5051

5152
[features]
5253
default = ["indicatif"]

cmds/fleet/src/cmds/build_systems.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf, time::Duration};
1+
use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf, str::FromStr, time::Duration};
22

3-
use anyhow::{anyhow, Result};
3+
use anyhow::{anyhow, bail, Result};
44
use clap::{Parser, ValueEnum};
55
use fleet_base::{
66
host::{Config, ConfigHost},
@@ -132,6 +132,7 @@ async fn deploy_task(
132132
disable_rollback: bool,
133133
) -> Result<()> {
134134
let mut failed = false;
135+
135136
// TODO: Lockfile, to prevent concurrent system switch?
136137
// TODO: If rollback target exists - bail, it should be removed. Lockfile will not work in case if rollback
137138
// is scheduler on next boot (default behavior). On current boot - rollback activator will fail due to
@@ -332,6 +333,24 @@ impl BuildSystems {
332333
}
333334
}
334335

336+
#[derive(Clone, PartialEq, Copy)]
337+
enum DeployKind {
338+
// NixOS => NixOS managed by fleet
339+
UpgradeToFleet,
340+
// NixOS managed by fleet => NixOS managed by fleet
341+
Fleet,
342+
}
343+
impl FromStr for DeployKind {
344+
type Err = anyhow::Error;
345+
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
346+
match s {
347+
"upgrade-to-fleet" => Ok(Self::UpgradeToFleet),
348+
"fleet" => Ok(Self::Fleet),
349+
v => bail!("unknown deploy_kind: {v}; expected on of \"upgrade-to-fleet\", \"fleet\""),
350+
}
351+
}
352+
}
353+
335354
impl Deploy {
336355
pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {
337356
let hosts = opts.filter_skipped(config.list_hosts().await?).await?;
@@ -348,6 +367,8 @@ impl Deploy {
348367
let local_host = config.local_host();
349368
let opts = opts.clone();
350369
let batch = batch.clone();
370+
let mut deploy_kind: Option<DeployKind> =
371+
opts.action_attr(&host, "deploy_kind").await?;
351372

352373
set.spawn_local(
353374
(async move {
@@ -356,10 +377,40 @@ impl Deploy {
356377
{
357378
Ok(path) => path,
358379
Err(e) => {
359-
error!("failed to deploy host: {}", e);
380+
error!("failed to build host system closure: {}", e);
360381
return;
361382
}
362383
};
384+
if deploy_kind == None {
385+
let is_fleet_managed = match host.file_exists("/etc/FLEET_HOST").await {
386+
Ok(v) => v,
387+
Err(e) => {
388+
error!("failed to query remote system kind: {}", e);
389+
return;
390+
},
391+
};
392+
if !is_fleet_managed {
393+
error!(indoc::indoc!{"
394+
host is not marked as managed by fleet
395+
if you're not trying to lustrate/install system from scratch,
396+
you should either
397+
1. manually create /etc/FLEET_HOST file on the target host,
398+
2. use ?deploy_kind=fleet host argument if you're upgrading from older version of fleet
399+
3. use ?deploy_kind=upgrade_to_fleet if you're upgrading from plain nixos to fleet-managed nixos
400+
"});
401+
return;
402+
}
403+
deploy_kind = Some(DeployKind::Fleet);
404+
}
405+
let deploy_kind = deploy_kind.expect("deploy_kind is set");
406+
407+
// TODO: Make disable_rollback a host attribute instead
408+
let mut disable_rollback = self.disable_rollback;
409+
if !disable_rollback && deploy_kind != DeployKind::Fleet {
410+
warn!("disabling rollback, as not supported by non-fleet deployment kinds");
411+
disable_rollback = true;
412+
}
413+
363414
if !opts.is_local(&hostname) {
364415
info!("uploading system closure");
365416
{
@@ -411,7 +462,7 @@ impl Deploy {
411462
error!("unreachable? failed to get specialization");
412463
return;
413464
},
414-
self.disable_rollback,
465+
disable_rollback,
415466
)
416467
.await
417468
{

cmds/fleet/src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ impl Prefetch {
6666

6767
#[derive(Parser)]
6868
enum Opts {
69-
/// Prepare systems for deployments
69+
/// Build system closures
7070
BuildSystems(BuildSystems),
71-
71+
/// Upload and switch system closures
7272
Deploy(Deploy),
7373
/// Secret management
7474
#[clap(subcommand)]

crates/fleet-base/src/command.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use better_command::{Handler, NixHandler, PlainHandler};
55
use futures::StreamExt;
66
use itertools::Either;
77
use openssh::{OverSsh, OwningCommand, Session};
8+
use serde::de::DeserializeOwned;
89
use tokio::{io::AsyncRead, process::Command, select};
910
use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec};
1011
use tracing::debug;
@@ -230,6 +231,10 @@ impl MyCommand {
230231
let bytes = self.run_bytes().await?;
231232
Ok(String::from_utf8(bytes)?)
232233
}
234+
pub async fn run_value<T: DeserializeOwned>(self) -> Result<T> {
235+
let v = self.run_string().await?;
236+
Ok(serde_json::from_str(&v)?)
237+
}
233238
pub async fn run_bytes(self) -> Result<Vec<u8>> {
234239
let str = self.clone().into_string();
235240
let cmd = self.wrap_sudo_if_needed().into_command()?;

crates/fleet-base/src/host.rs

+8
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ impl ConfigHost {
105105
let path = cmd.run_string().await?;
106106
Ok(path.trim_end().to_owned())
107107
}
108+
pub async fn file_exists(&self, path: impl AsRef<OsStr>) -> Result<bool> {
109+
let mut cmd = self.cmd("sh").await?;
110+
cmd.arg("-c")
111+
.arg("test -e \"$1\" && echo true || echo false")
112+
.arg("_")
113+
.arg(path);
114+
Ok(cmd.run_value().await?)
115+
}
108116
pub async fn read_file_bin(&self, path: impl AsRef<OsStr>) -> Result<Vec<u8>> {
109117
let mut cmd = self.cmd("cat").await?;
110118
cmd.arg(path);

modules/nixos/meta.nix

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
{lib, ...}: let
1+
{ lib, ... }:
2+
let
23
inherit (lib.modules) mkRemovedOptionModule;
3-
in {
4+
in
5+
{
46
imports = [
5-
(mkRemovedOptionModule ["tags"] "tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts.")
6-
(mkRemovedOptionModule ["network"] "network is now defined at the host level, not the nixos system level")
7+
(mkRemovedOptionModule [ "tags" ]
8+
"tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts."
9+
)
10+
(mkRemovedOptionModule [
11+
"network"
12+
] "network is now defined at the host level, not the nixos system level")
713
];
14+
15+
# Version of environment (fleet scripts such as rollback) already installed on the host
16+
config.environment.etc.FLEET_HOST.text = "1";
817
}

0 commit comments

Comments
 (0)