@@ -82,25 +82,6 @@ fn unset_boot_counter_at(grub_path: &str) -> Result<()> {
8282 unset_grub_var ( "boot_counter" , grub_path)
8383}
8484
85- /// sets greenboot_rollback_trigger=1 and fallback=1
86- pub fn set_rollback_trigger ( ) -> Result < ( ) > {
87- set_rollback_trigger_at ( GRUB_PATH )
88- }
89-
90- fn set_rollback_trigger_at ( grub_path : & str ) -> Result < ( ) > {
91- set_grub_var ( "greenboot_rollback_trigger" , 1 , grub_path) ?;
92- set_grub_var ( "fallback" , 1 , grub_path)
93- }
94-
95- /// unsets greenboot_rollback_trigger
96- pub fn unset_rollback_trigger ( ) -> Result < ( ) > {
97- unset_rollback_trigger_at ( GRUB_PATH )
98- }
99-
100- fn unset_rollback_trigger_at ( grub_path : & str ) -> Result < ( ) > {
101- unset_grub_var ( "greenboot_rollback_trigger" , grub_path)
102- }
103-
10485/// sets fallback=1 for GRUB-level kernel fallback protection
10586pub fn set_fallback ( ) -> Result < ( ) > {
10687 set_fallback_at ( GRUB_PATH )
@@ -128,13 +109,31 @@ fn get_fallback_at(grub_path: &str) -> Result<bool> {
128109 get_grub_bool_var ( "fallback" , grub_path)
129110}
130111
131- /// gets greenboot_rollback_trigger value, returns true if set to 1
132- pub fn get_rollback_trigger ( ) -> Result < bool > {
133- get_rollback_trigger_at ( GRUB_PATH )
112+ /// sets greenboot_next_deployment_id to the given deployment ID string
113+ pub fn set_next_deployment_id ( id : & str ) -> Result < ( ) > {
114+ set_next_deployment_id_at ( id , GRUB_PATH )
134115}
135116
136- fn get_rollback_trigger_at ( grub_path : & str ) -> Result < bool > {
137- get_grub_bool_var ( "greenboot_rollback_trigger" , grub_path)
117+ fn set_next_deployment_id_at ( id : & str , grub_path : & str ) -> Result < ( ) > {
118+ set_grub_str_var ( "greenboot_next_deployment_id" , id, grub_path)
119+ }
120+
121+ /// unsets greenboot_next_deployment_id
122+ pub fn unset_next_deployment_id ( ) -> Result < ( ) > {
123+ unset_next_deployment_id_at ( GRUB_PATH )
124+ }
125+
126+ fn unset_next_deployment_id_at ( grub_path : & str ) -> Result < ( ) > {
127+ unset_grub_var ( "greenboot_next_deployment_id" , grub_path)
128+ }
129+
130+ /// returns the stored deployment ID, or None if not set
131+ pub fn get_next_deployment_id ( ) -> Result < Option < String > > {
132+ get_next_deployment_id_at ( GRUB_PATH )
133+ }
134+
135+ fn get_next_deployment_id_at ( grub_path : & str ) -> Result < Option < String > > {
136+ get_grub_str_var ( "greenboot_next_deployment_id" , grub_path)
138137}
139138
140139fn get_grub_bool_var ( key : & str , grub_path : & str ) -> Result < bool > {
@@ -187,12 +186,45 @@ fn set_grub_var(key: &str, val: u16, grub_path: &str) -> Result<()> {
187186 Ok ( ( ) )
188187}
189188
189+ fn set_grub_str_var ( key : & str , val : & str , grub_path : & str ) -> Result < ( ) > {
190+ let grub_result = Command :: new ( "grub2-editenv" )
191+ . arg ( grub_path)
192+ . arg ( "set" )
193+ . arg ( format ! ( "{key}={val}" ) )
194+ . status ( )
195+ . context ( "Unable to set grubenv" ) ?;
196+
197+ if !grub_result. success ( ) {
198+ bail ! ( "Failed to set grubenv key: {key}" ) ;
199+ }
200+
201+ log:: info!( "Set grubenv: {key}={val}" ) ;
202+ Ok ( ( ) )
203+ }
204+
205+ fn get_grub_str_var ( key : & str , grub_path : & str ) -> Result < Option < String > > {
206+ let grub_vars = Command :: new ( "grub2-editenv" )
207+ . arg ( grub_path)
208+ . arg ( "list" )
209+ . output ( )
210+ . context ( format ! ( "Unable to list grubenv variables for key: {key}" ) ) ?;
211+
212+ let prefix = format ! ( "{key}=" ) ;
213+ let output = String :: from_utf8_lossy ( & grub_vars. stdout ) ;
214+ for line in output. lines ( ) {
215+ if let Some ( value) = line. strip_prefix ( & prefix) {
216+ return Ok ( Some ( value. to_string ( ) ) ) ;
217+ }
218+ }
219+ Ok ( None )
220+ }
221+
190222#[ cfg( test) ]
191223mod tests {
192224 use super :: {
193- get_boot_counter_at, get_fallback_at, get_rollback_trigger_at , set_boot_counter_at,
194- set_fallback_at, set_rollback_trigger_at , unset_boot_counter_at, unset_fallback_at,
195- unset_rollback_trigger_at ,
225+ get_boot_counter_at, get_fallback_at, get_next_deployment_id_at , set_boot_counter_at,
226+ set_fallback_at, set_next_deployment_id_at , unset_boot_counter_at, unset_fallback_at,
227+ unset_next_deployment_id_at ,
196228 } ;
197229 use anyhow:: Context ;
198230 use std:: fs;
@@ -266,41 +298,66 @@ mod tests {
266298 }
267299
268300 #[ test]
269- fn test_rollback_trigger_functions ( ) {
301+ fn test_next_deployment_id_set_and_get ( ) {
270302 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
271303
272- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
273- assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
304+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
274305
275- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
276- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
277- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
306+ let id = "sha256:abc123def456" ;
307+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
308+ assert_eq ! (
309+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
310+ Some ( id. to_string( ) )
311+ ) ;
312+ }
278313
279- // unset_rollback_trigger only clears the trigger, not fallback
280- unset_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
281- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
282- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
314+ #[ test]
315+ fn test_next_deployment_id_unset ( ) {
316+ let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
283317
284- // fallback has its own lifecycle, unset independently
285- unset_fallback_at ( & grubenv) . unwrap ( ) ;
286- assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
318+ let id = "sha256:abc123def456" ;
319+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
320+ assert ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) . is_some( ) ) ;
321+
322+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
323+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
287324 }
288325
289326 #[ test]
290- fn test_rollback_trigger_with_other_vars ( ) {
327+ fn test_next_deployment_id_coexists_with_boot_counter ( ) {
291328 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
292329
330+ let id = "sha256:abc123def456" ;
293331 set_boot_counter_at ( 3 , & grubenv) . unwrap ( ) ;
294- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
332+ set_next_deployment_id_at ( id , & grubenv) . unwrap ( ) ;
295333
296334 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
297- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
298- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
335+ assert_eq ! (
336+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
337+ Some ( id. to_string( ) )
338+ ) ;
299339
300- // unset_rollback_trigger leaves boot_counter and fallback intact
301- unset_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
340+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
302341 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
303- assert ! ( !get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
342+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
343+ }
344+
345+ #[ test]
346+ fn test_next_deployment_id_coexists_with_fallback ( ) {
347+ let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
348+
349+ let id = "sha256:abc123def456" ;
350+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
351+ set_fallback_at ( & grubenv) . unwrap ( ) ;
352+
353+ assert_eq ! (
354+ get_next_deployment_id_at( & grubenv) . unwrap( ) ,
355+ Some ( id. to_string( ) )
356+ ) ;
357+ assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
358+
359+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
360+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
304361 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
305362 }
306363
@@ -333,36 +390,30 @@ mod tests {
333390 assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
334391 }
335392
336- #[ test]
337- fn test_fallback_set_via_rollback_trigger ( ) {
338- let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
339-
340- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
341- assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
342- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
343- }
344-
345393 #[ test]
346394 fn test_fallback_coexists_with_boot_counter ( ) {
347395 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
348396
349397 set_boot_counter_at ( 3 , & grubenv) . unwrap ( ) ;
350- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
398+ set_fallback_at ( & grubenv) . unwrap ( ) ;
351399
352400 assert_eq ! ( get_boot_counter_at( & grubenv) . unwrap( ) , Some ( 3 ) ) ;
353401 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
354- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
355402 }
356403
357404 #[ test]
358- fn test_fallback_independent_unset ( ) {
405+ fn test_fallback_independent_of_deployment_id ( ) {
359406 let ( _temp_dir, grubenv) = setup_test_paths ( ) ;
360407
361- set_rollback_trigger_at ( & grubenv) . unwrap ( ) ;
408+ let id = "sha256:abc123def456" ;
409+ set_next_deployment_id_at ( id, & grubenv) . unwrap ( ) ;
410+ set_fallback_at ( & grubenv) . unwrap ( ) ;
411+
412+ unset_next_deployment_id_at ( & grubenv) . unwrap ( ) ;
413+ assert_eq ! ( get_next_deployment_id_at( & grubenv) . unwrap( ) , None ) ;
362414 assert ! ( get_fallback_at( & grubenv) . unwrap( ) ) ;
363415
364416 unset_fallback_at ( & grubenv) . unwrap ( ) ;
365417 assert ! ( !get_fallback_at( & grubenv) . unwrap( ) ) ;
366- assert ! ( get_rollback_trigger_at( & grubenv) . unwrap( ) ) ;
367418 }
368419}
0 commit comments