talos_api_rs/resources/
upgrade.rs1use crate::api::generated::machine::{
8 Upgrade as ProtoUpgrade, UpgradeRequest as ProtoUpgradeRequest,
9 UpgradeResponse as ProtoUpgradeResponse,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub enum UpgradeRebootMode {
15 #[default]
17 Default,
18 PowerCycle,
20}
21
22impl From<UpgradeRebootMode> for i32 {
23 fn from(mode: UpgradeRebootMode) -> Self {
24 match mode {
25 UpgradeRebootMode::Default => 0,
26 UpgradeRebootMode::PowerCycle => 1,
27 }
28 }
29}
30
31impl std::fmt::Display for UpgradeRebootMode {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 UpgradeRebootMode::Default => write!(f, "default"),
35 UpgradeRebootMode::PowerCycle => write!(f, "powercycle"),
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
57pub struct UpgradeRequest {
58 pub image: String,
60 pub preserve: bool,
62 pub stage: bool,
64 pub force: bool,
66 pub reboot_mode: UpgradeRebootMode,
68}
69
70impl UpgradeRequest {
71 #[must_use]
73 pub fn new(image: impl Into<String>) -> Self {
74 Self {
75 image: image.into(),
76 preserve: false,
77 stage: false,
78 force: false,
79 reboot_mode: UpgradeRebootMode::Default,
80 }
81 }
82
83 #[must_use]
85 pub fn builder(image: impl Into<String>) -> UpgradeRequestBuilder {
86 UpgradeRequestBuilder::new(image)
87 }
88}
89
90impl From<UpgradeRequest> for ProtoUpgradeRequest {
91 fn from(req: UpgradeRequest) -> Self {
92 Self {
93 image: req.image,
94 preserve: req.preserve,
95 stage: req.stage,
96 force: req.force,
97 reboot_mode: req.reboot_mode.into(),
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct UpgradeRequestBuilder {
105 image: String,
106 preserve: bool,
107 stage: bool,
108 force: bool,
109 reboot_mode: UpgradeRebootMode,
110}
111
112impl UpgradeRequestBuilder {
113 #[must_use]
115 pub fn new(image: impl Into<String>) -> Self {
116 Self {
117 image: image.into(),
118 preserve: false,
119 stage: false,
120 force: false,
121 reboot_mode: UpgradeRebootMode::Default,
122 }
123 }
124
125 #[must_use]
127 pub fn preserve(mut self, preserve: bool) -> Self {
128 self.preserve = preserve;
129 self
130 }
131
132 #[must_use]
134 pub fn stage(mut self, stage: bool) -> Self {
135 self.stage = stage;
136 self
137 }
138
139 #[must_use]
141 pub fn force(mut self, force: bool) -> Self {
142 self.force = force;
143 self
144 }
145
146 #[must_use]
148 pub fn reboot_mode(mut self, mode: UpgradeRebootMode) -> Self {
149 self.reboot_mode = mode;
150 self
151 }
152
153 #[must_use]
155 pub fn build(self) -> UpgradeRequest {
156 UpgradeRequest {
157 image: self.image,
158 preserve: self.preserve,
159 stage: self.stage,
160 force: self.force,
161 reboot_mode: self.reboot_mode,
162 }
163 }
164}
165
166#[derive(Debug, Clone)]
168pub struct UpgradeResult {
169 pub node: Option<String>,
171 pub ack: String,
173 pub actor_id: String,
175}
176
177impl From<ProtoUpgrade> for UpgradeResult {
178 fn from(proto: ProtoUpgrade) -> Self {
179 Self {
180 node: proto.metadata.map(|m| m.hostname),
181 ack: proto.ack,
182 actor_id: proto.actor_id,
183 }
184 }
185}
186
187#[derive(Debug, Clone)]
189pub struct UpgradeResponse {
190 pub results: Vec<UpgradeResult>,
192}
193
194impl From<ProtoUpgradeResponse> for UpgradeResponse {
195 fn from(proto: ProtoUpgradeResponse) -> Self {
196 Self {
197 results: proto
198 .messages
199 .into_iter()
200 .map(UpgradeResult::from)
201 .collect(),
202 }
203 }
204}
205
206impl UpgradeResponse {
207 #[must_use]
209 pub fn is_success(&self) -> bool {
210 !self.results.is_empty()
211 }
212
213 #[must_use]
215 pub fn first(&self) -> Option<&UpgradeResult> {
216 self.results.first()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_upgrade_request_new() {
226 let req = UpgradeRequest::new("ghcr.io/siderolabs/installer:v1.6.0");
227 assert_eq!(req.image, "ghcr.io/siderolabs/installer:v1.6.0");
228 assert!(!req.preserve);
229 assert!(!req.stage);
230 assert!(!req.force);
231 assert_eq!(req.reboot_mode, UpgradeRebootMode::Default);
232 }
233
234 #[test]
235 fn test_upgrade_request_builder() {
236 let req = UpgradeRequest::builder("ghcr.io/siderolabs/installer:v1.6.0")
237 .preserve(true)
238 .stage(true)
239 .force(true)
240 .reboot_mode(UpgradeRebootMode::PowerCycle)
241 .build();
242
243 assert_eq!(req.image, "ghcr.io/siderolabs/installer:v1.6.0");
244 assert!(req.preserve);
245 assert!(req.stage);
246 assert!(req.force);
247 assert_eq!(req.reboot_mode, UpgradeRebootMode::PowerCycle);
248 }
249
250 #[test]
251 fn test_upgrade_reboot_mode() {
252 assert_eq!(i32::from(UpgradeRebootMode::Default), 0);
253 assert_eq!(i32::from(UpgradeRebootMode::PowerCycle), 1);
254 assert_eq!(UpgradeRebootMode::PowerCycle.to_string(), "powercycle");
255 }
256
257 #[test]
258 fn test_proto_conversion() {
259 let req = UpgradeRequest::builder("test:v1.0")
260 .stage(true)
261 .force(true)
262 .build();
263
264 let proto: ProtoUpgradeRequest = req.into();
265 assert_eq!(proto.image, "test:v1.0");
266 assert!(proto.stage);
267 assert!(proto.force);
268 }
269}