git.delta.rocks / jrsonnet / refs/commits / 1e20ff1f8900

difftreelog

source

cmds/fleet/src/cmds/rollback.rs3.5 KiBsourcehistory
1use std::collections::HashSet;23use anyhow::{Result, bail};4use clap::Parser;5use fleet_base::{6	deploy::{DeployAction, deploy_task, upload_task},7	host::{Config, ConfigHost, Generation, GenerationStorage},8	opts::FleetOpts,9};10use tabled::Table;11use tracing::{info, warn};1213#[derive(Parser)]14pub struct RollbackSingle {15	machine: String,16	#[clap(subcommand)]17	action: RollbackAction,18}1920#[derive(Parser, Clone)]21struct DeployOptions {22	/// Rollback target to use23	id: String,24	/// Rollback to the current generation if rollback fails25	// Automatic rollback seems to be unnecessary for manual rollback...26	#[clap(long)]27	enable_rollback: bool,28	/// Specialization to use29	#[clap(long)]30	specialization: Option<String>,31}3233#[derive(Parser, Clone)]34enum RollbackAction {35	/// List available rollback targets36	ListTargets,37	/// Upload and execute the activation script, old version will be used after reboot.38	Test(#[clap(flatten)] DeployOptions),39	/// Upload, set current profile, and execute activation script.40	Switch(#[clap(flatten)] DeployOptions),41	/// Upload and set as current system profile, but do not execute activation script.42	Boot(#[clap(flatten)] DeployOptions),43}4445pub async fn list_all_generations(host: &ConfigHost, config: &Config) -> Vec<Generation> {46	let stored_on_machine = host47		.list_generations("system")48		.await49		.inspect_err(|e| {50			warn!("failed to list generations available on the remote machine: {e}");51		})52		.unwrap_or_default();53	let on_machine_store_paths = stored_on_machine54		.iter()55		.map(|g| &g.store_path)56		.collect::<HashSet<_>>();57	let mut stored_locally = config58		.local_host()59		.list_generations(&format!("{}-{}", config.data.gc_root_prefix, host.name))60		.await61		.inspect_err(|e| {62			warn!("failed to list generations available locally: {e}");63		})64		.unwrap_or_default();65	dbg!(&stored_locally);66	stored_locally.retain(|g| !on_machine_store_paths.contains(&g.store_path));67	for ele in stored_locally.iter_mut() {68		ele.current = false;69		ele.location = GenerationStorage::Deployer;70	}71	stored_locally.extend(stored_on_machine);72	stored_locally.sort_by_key(|v| v.datetime);73	stored_locally74}7576impl RollbackSingle {77	pub(crate) async fn run(&self, config: &Config, _opts: &FleetOpts) -> Result<()> {78		let host = config.host(&self.machine)?;79		match &self.action {80			RollbackAction::ListTargets => {81				let generations = list_all_generations(&host, config).await;82				if generations.is_empty() {83					bail!("no available rollback targets found");84				}85				info!("Generation list:\n{}", Table::new(&generations));86				Ok(())87			}88			RollbackAction::Boot(o) | RollbackAction::Test(o) | RollbackAction::Switch(o) => {89				let DeployOptions {90					id,91					enable_rollback,92					specialization,93				} = o;94				let action: DeployAction = match self.action {95					RollbackAction::Test { .. } => DeployAction::Test,96					RollbackAction::Switch { .. } => DeployAction::Switch,97					RollbackAction::Boot { .. } => DeployAction::Boot,98					_ => unreachable!(),99				};100				let generations = list_all_generations(&host, config).await;101				let Some(generation) = generations.iter().find(|g| &g.rollback_id() == id) else {102					bail!(103						"generation by this name is not found, existing generations:\n{}",104						Table::new(&generations)105					);106				};107				let remote_path = upload_task(108					config,109					&host,110					generation.location,111					generation.store_path.clone(),112				)113				.await?;114115				deploy_task(116					action,117					&host,118					remote_path,119					specialization.clone(),120					!*enable_rollback,121				)122				.await?;123				Ok(())124			}125		}126	}127}