ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/
at stable 11 kB view raw
1// SPDX-License-Identifier: AGPL-3.0-or-later 2// Copyright 2024-2025 wire Contributors 3 4#![allow(unused_assignments)] 5 6use std::{num::ParseIntError, path::PathBuf, process::ExitStatus, sync::mpsc::RecvError}; 7 8use miette::{Diagnostic, SourceSpan}; 9use nix_compat::flakeref::{FlakeRef, FlakeRefError}; 10use thiserror::Error; 11use tokio::task::JoinError; 12 13use crate::hive::node::{Name, SwitchToConfigurationGoal}; 14 15#[cfg(debug_assertions)] 16const DOCS_URL: &str = "http://localhost:5173/reference/errors.html"; 17#[cfg(not(debug_assertions))] 18const DOCS_URL: &str = "https://wire.althaea.zone/reference/errors.html"; 19 20#[derive(Debug, Diagnostic, Error)] 21pub enum KeyError { 22 #[diagnostic( 23 code(wire::key::File), 24 url("{DOCS_URL}#{}", self.code().unwrap()) 25 )] 26 #[error("error reading file")] 27 File(#[source] std::io::Error), 28 29 #[diagnostic( 30 code(wire::key::SpawningCommand), 31 help("Ensure wire has the correct $PATH for this command"), 32 url("{DOCS_URL}#{}", self.code().unwrap()) 33 )] 34 #[error("error spawning key command")] 35 CommandSpawnError { 36 #[source] 37 error: std::io::Error, 38 39 #[source_code] 40 command: String, 41 42 #[label(primary, "Program ran")] 43 command_span: Option<SourceSpan>, 44 }, 45 46 #[diagnostic( 47 code(wire::key::Resolving), 48 url("{DOCS_URL}#{}", self.code().unwrap()) 49 )] 50 #[error("Error resolving key command child process")] 51 CommandResolveError { 52 #[source] 53 error: std::io::Error, 54 55 #[source_code] 56 command: String, 57 }, 58 59 #[diagnostic( 60 code(wire::key::CommandExit), 61 url("{DOCS_URL}#{}", self.code().unwrap()) 62 )] 63 #[error("key command failed with status {}: {}", .0,.1)] 64 CommandError(ExitStatus, String), 65 66 #[diagnostic( 67 code(wire::key::Empty), 68 url("{DOCS_URL}#{}", self.code().unwrap()) 69 )] 70 #[error("Command list empty")] 71 Empty, 72 73 #[diagnostic( 74 code(wire::key::ParseKeyPermissions), 75 help("Refer to the documentation for the format of key file permissions."), 76 url("{DOCS_URL}#{}", self.code().unwrap()) 77 )] 78 #[error("Failed to parse key permissions")] 79 ParseKeyPermissions(#[source] ParseIntError), 80} 81 82#[derive(Debug, Diagnostic, Error)] 83pub enum ActivationError { 84 #[diagnostic( 85 code(wire::activation::SwitchToConfiguration), 86 url("{DOCS_URL}#{}", self.code().unwrap()) 87 )] 88 #[error("failed to run switch-to-configuration {0} on node {1}")] 89 SwitchToConfigurationError(SwitchToConfigurationGoal, Name, #[source] CommandError), 90} 91 92#[derive(Debug, Diagnostic, Error)] 93pub enum NetworkError { 94 #[diagnostic( 95 code(wire::network::HostUnreachable), 96 help( 97 "If you failed due to a fault in DNS, note that a node can have multiple targets defined." 98 ), 99 url("{DOCS_URL}#{}", self.code().unwrap()) 100 )] 101 #[error("Cannot reach host {host}")] 102 HostUnreachable { 103 host: String, 104 #[source] 105 source: CommandError, 106 }, 107 108 #[diagnostic( 109 code(wire::network::HostUnreachableAfterReboot), 110 url("{DOCS_URL}#{}", self.code().unwrap()) 111 )] 112 #[error("Failed to get regain connection to {0} after activation.")] 113 HostUnreachableAfterReboot(String), 114 115 #[diagnostic( 116 code(wire::network::HostsExhausted), 117 url("{DOCS_URL}#{}", self.code().unwrap()) 118 )] 119 #[error("Ran out of contactable hosts")] 120 HostsExhausted, 121} 122 123#[derive(Debug, Diagnostic, Error)] 124pub enum HiveInitialisationError { 125 #[diagnostic( 126 code(wire::hive_init::NoHiveFound), 127 help( 128 "Double check the path is correct. You can adjust the hive path with `--path` when the hive lies outside of the CWD." 129 ), 130 url("{DOCS_URL}#{}", self.code().unwrap()) 131 )] 132 #[error("No hive could be found in {}", .0.display())] 133 NoHiveFound(PathBuf), 134 135 #[diagnostic( 136 code(wire::hive_init::Parse), 137 help("If you cannot resolve this problem, please create an issue."), 138 url("{DOCS_URL}#{}", self.code().unwrap()) 139 )] 140 #[error("Failed to parse internal wire json.")] 141 ParseEvaluateError(#[source] serde_json::Error), 142 143 #[diagnostic( 144 code(wire::hive_init::ParsePrefetch), 145 help("please create an issue."), 146 url("{DOCS_URL}#{}", self.code().unwrap()) 147 )] 148 #[error("Failed to parse `nix flake prefetch --json`.")] 149 ParsePrefetchError(#[source] serde_json::Error), 150 151 #[diagnostic( 152 code(wire::hive_init::NodeDoesNotExist), 153 help("Please create an issue!"), 154 url("{DOCS_URL}#{}", self.code().unwrap()) 155 )] 156 #[error("node {0} not exist in hive")] 157 NodeDoesNotExist(String), 158} 159 160#[derive(Debug, Diagnostic, Error)] 161pub enum HiveLocationError { 162 #[diagnostic( 163 code(wire::hive_location::MalformedPath), 164 url("{DOCS_URL}#{}", self.code().unwrap()) 165 )] 166 #[error("Path was malformed: {}", .0.display())] 167 MalformedPath(PathBuf), 168 169 #[diagnostic( 170 code(wire::hive_location::Malformed), 171 url("{DOCS_URL}#{}", self.code().unwrap()) 172 )] 173 #[error("--path was malformed")] 174 Malformed(#[source] FlakeRefError), 175 176 #[diagnostic( 177 code(wire::hive_location::TypeUnsupported), 178 url("{DOCS_URL}#{}", self.code().unwrap()) 179 )] 180 #[error("The flakref had an unsupported type: {:#?}", .0)] 181 TypeUnsupported(Box<FlakeRef>), 182} 183 184#[derive(Debug, Diagnostic, Error)] 185pub enum CommandError { 186 #[diagnostic( 187 code(wire::command::TermAttrs), 188 url("{DOCS_URL}#{}", self.code().unwrap()) 189 )] 190 #[error("Failed to set PTY attrs")] 191 TermAttrs(#[source] nix::errno::Errno), 192 193 #[diagnostic( 194 code(wire::command::PosixPipe), 195 url("{DOCS_URL}#{}", self.code().unwrap()) 196 )] 197 #[error("There was an error in regards to a pipe")] 198 PosixPipe(#[source] nix::errno::Errno), 199 200 /// Error wrapped around `portable_pty`'s anyhow 201 /// errors 202 #[diagnostic( 203 code(wire::command::PortablePty), 204 url("{DOCS_URL}#{}", self.code().unwrap()) 205 )] 206 #[error("There was an error from the portable_pty crate")] 207 PortablePty(#[source] anyhow::Error), 208 209 #[diagnostic( 210 code(wire::command::Joining), 211 url("{DOCS_URL}#{}", self.code().unwrap()) 212 )] 213 #[error("Failed to join on some tokio task")] 214 JoinError(#[source] JoinError), 215 216 #[diagnostic( 217 code(wire::command::WaitForStatus), 218 url("{DOCS_URL}#{}", self.code().unwrap()) 219 )] 220 #[error("Failed to wait for the child's status")] 221 WaitForStatus(#[source] std::io::Error), 222 223 #[diagnostic( 224 code(wire::detached::NoHandle), 225 help("This should never happen, please create an issue!"), 226 url("{DOCS_URL}#{}", self.code().unwrap()) 227 )] 228 #[error("There was no handle to child io")] 229 NoHandle, 230 231 #[diagnostic( 232 code(wire::command::WritingClientStdout), 233 url("{DOCS_URL}#{}", self.code().unwrap()) 234 )] 235 #[error("Failed to write to client stderr.")] 236 WritingClientStderr(#[source] std::io::Error), 237 238 #[diagnostic( 239 code(wire::command::WritingMasterStdin), 240 url("{DOCS_URL}#{}", self.code().unwrap()) 241 )] 242 #[error("Failed to write to PTY master stdout.")] 243 WritingMasterStdout(#[source] std::io::Error), 244 245 #[diagnostic( 246 code(wire::command::Recv), 247 url("{DOCS_URL}#{}", self.code().unwrap()), 248 help("please create an issue!"), 249 )] 250 #[error("Failed to receive a message from the begin channel")] 251 RecvError(#[source] RecvError), 252 253 #[diagnostic( 254 code(wire::command::ThreadPanic), 255 url("{DOCS_URL}#{}", self.code().unwrap()), 256 help("please create an issue!"), 257 )] 258 #[error("Thread panicked")] 259 ThreadPanic, 260 261 #[diagnostic( 262 code(wire::command::CommandFailed), 263 url("{DOCS_URL}#{}", self.code().unwrap()), 264 help("`nix` commands are filtered, run with -vvv to view all"), 265 )] 266 #[error("{command_ran} failed ({reason}) with {code} (last 20 lines):\n{logs}")] 267 CommandFailed { 268 command_ran: String, 269 logs: String, 270 code: String, 271 reason: &'static str, 272 }, 273 274 #[diagnostic( 275 code(wire::command::RuntimeDirectory), 276 url("{DOCS_URL}#{}", self.code().unwrap()) 277 )] 278 #[error("error creating $XDG_RUNTIME_DIR/wire")] 279 RuntimeDirectory(#[source] std::io::Error), 280 281 #[diagnostic( 282 code(wire::command::RuntimeDirectoryMissing), 283 url("{DOCS_URL}#{}", self.code().unwrap()) 284 )] 285 #[error("$XDG_RUNTIME_DIR could not be used.")] 286 RuntimeDirectoryMissing(#[source] std::env::VarError), 287 288 #[diagnostic( 289 code(wire::command::OneshotRecvError), 290 url("{DOCS_URL}#{}", self.code().unwrap()) 291 )] 292 #[error("Error waiting for begin message")] 293 OneshotRecvError(#[source] tokio::sync::oneshot::error::RecvError), 294} 295 296#[derive(Debug, Diagnostic, Error)] 297pub enum HiveLibError { 298 #[error(transparent)] 299 #[diagnostic(transparent)] 300 HiveInitialisationError(HiveInitialisationError), 301 302 #[error(transparent)] 303 #[diagnostic(transparent)] 304 NetworkError(NetworkError), 305 306 #[error(transparent)] 307 #[diagnostic(transparent)] 308 ActivationError(ActivationError), 309 310 #[error(transparent)] 311 #[diagnostic(transparent)] 312 CommandError(CommandError), 313 314 #[error(transparent)] 315 #[diagnostic(transparent)] 316 HiveLocationError(HiveLocationError), 317 318 #[error("Failed to apply key {}", .0)] 319 KeyError( 320 String, 321 #[source] 322 #[diagnostic_source] 323 KeyError, 324 ), 325 326 #[diagnostic( 327 code(wire::BuildNode), 328 url("{DOCS_URL}#{}", self.code().unwrap()) 329 )] 330 #[error("failed to build node {name}")] 331 NixBuildError { 332 name: Name, 333 #[source] 334 source: CommandError, 335 }, 336 337 #[diagnostic( 338 code(wire::CopyPath), 339 url("{DOCS_URL}#{}", self.code().unwrap()) 340 )] 341 #[error("failed to copy path {path} to node {name}")] 342 NixCopyError { 343 name: Name, 344 path: String, 345 #[source] 346 error: Box<CommandError>, 347 #[help] 348 help: Option<Box<String>>, 349 }, 350 351 #[diagnostic(code(wire::Evaluate))] 352 #[error("failed to evaluate `{attribute}` from the context of a hive.")] 353 NixEvalError { 354 attribute: String, 355 356 #[source] 357 source: CommandError, 358 359 #[help] 360 help: Option<Box<String>>, 361 }, 362 363 #[diagnostic( 364 code(wire::Encoding), 365 url("{DOCS_URL}#{}", self.code().unwrap()) 366 )] 367 #[error("error encoding length delimited data")] 368 Encoding(#[source] std::io::Error), 369 370 #[diagnostic( 371 code(wire::SIGINT), 372 url("{DOCS_URL}#{}", self.code().unwrap()) 373 )] 374 #[error("SIGINT received, shut down")] 375 Sigint, 376}