github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/boot/boottest/bootenv.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package boottest 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/bootloader/bootloadertest" 26 "github.com/snapcore/snapd/snap" 27 ) 28 29 // Bootenv16 implements manipulating a UC16/18 boot env for testing. 30 type Bootenv16 struct { 31 *bootloadertest.MockBootloader 32 statusVar string 33 } 34 35 // MockUC16Bootenv wraps a mock bootloader for UC16/18 boot env 36 // manipulation. 37 func MockUC16Bootenv(b *bootloadertest.MockBootloader) *Bootenv16 { 38 return &Bootenv16{ 39 MockBootloader: b, 40 statusVar: "snap_mode", 41 } 42 } 43 44 // SetBootKernel sets the current boot kernel string. Should be 45 // something like "pc-kernel_1234.snap". 46 func (b16 Bootenv16) SetBootKernel(kernel string) { 47 b16.SetBootVars(map[string]string{"snap_kernel": kernel}) 48 } 49 50 // SetBootTryKernel sets the try boot kernel string. Should be 51 // something like "pc-kernel_1235.snap". 52 func (b16 Bootenv16) SetBootTryKernel(kernel string) { 53 b16.SetBootVars(map[string]string{"snap_try_kernel": kernel}) 54 } 55 56 // SetBootBase sets the current boot base string. Should be something 57 // like "core_1234.snap". 58 func (b16 Bootenv16) SetBootBase(base string) { 59 b16.SetBootVars(map[string]string{"snap_core": base}) 60 } 61 62 // SetTryingDuringReboot indicates that new kernel or base are being tried 63 // same as done by bootloader config. 64 func (b16 Bootenv16) SetTryingDuringReboot(which []snap.Type) error { 65 if b16.BootVars[b16.statusVar] != "try" { 66 return fmt.Errorf("bootloader must be in 'try' mode") 67 } 68 b16.BootVars[b16.statusVar] = "trying" 69 return nil 70 } 71 72 func includesType(which []snap.Type, t snap.Type) bool { 73 for _, t1 := range which { 74 if t1 == t { 75 return true 76 } 77 } 78 return true 79 } 80 81 func exactlyType(which []snap.Type, t snap.Type) bool { 82 if len(which) != 1 { 83 return false 84 } 85 if which[0] != t { 86 return false 87 } 88 return true 89 } 90 91 // SetRollbackAcrossReboot will simulate a rollback across reboots. This 92 // means that the bootloader had "snap_try_{core,kernel}" set but this 93 // boot failed. In this case the bootloader will clear 94 // "snap_try_{core,kernel}" and "snap_mode" which means the "old" kernel,core 95 // in "snap_{core,kernel}" will be used. which indicates whether rollback 96 // applies to kernel, base or both. 97 func (b16 Bootenv16) SetRollbackAcrossReboot(which []snap.Type) error { 98 if b16.BootVars[b16.statusVar] != "try" { 99 return fmt.Errorf("rollback can only be simulated in 'try' mode") 100 } 101 rollbackBase := includesType(which, snap.TypeBase) 102 rollbackKernel := includesType(which, snap.TypeKernel) 103 if !rollbackBase && !rollbackKernel { 104 return fmt.Errorf("rollback of either base or kernel must be requested") 105 } 106 if rollbackBase && b16.BootVars["snap_core"] == "" && b16.BootVars["snap_kernel"] == "" { 107 return fmt.Errorf("base rollback can only be simulated if snap_core is set") 108 } 109 if rollbackKernel && b16.BootVars["snap_kernel"] == "" { 110 return fmt.Errorf("kernel rollback can only be simulated if snap_kernel is set") 111 } 112 // clean only statusVar - the try vars will be cleaned by snapd NOT by the 113 // bootloader 114 b16.BootVars[b16.statusVar] = "" 115 return nil 116 } 117 118 // RunBootenv20 implements manipulating a UC20 run-mode boot env for 119 // testing. 120 type RunBootenv20 struct { 121 *bootloadertest.MockExtractedRunKernelImageBootloader 122 } 123 124 // MockUC20EnvRefExtractedKernelRunBootenv wraps a mock bootloader for UC20 run-mode boot 125 // env manipulation. 126 func MockUC20EnvRefExtractedKernelRunBootenv(b *bootloadertest.MockBootloader) *Bootenv16 { 127 // TODO:UC20: implement this w/o returning Bootenv16 because that doesn't 128 // make a lot of sense to the caller 129 return &Bootenv16{ 130 MockBootloader: b, 131 statusVar: "kernel_status", 132 } 133 } 134 135 // MockUC20RunBootenv wraps a mock bootloader for UC20 run-mode boot 136 // env manipulation. 137 func MockUC20RunBootenv(b *bootloadertest.MockBootloader) *RunBootenv20 { 138 return &RunBootenv20{b.WithExtractedRunKernelImage()} 139 } 140 141 // TODO:UC20: expose actual snap-boostrap logic for testing 142 143 // SetTryingDuringReboot indicates that new kernel or base are being tried 144 // same as done by bootloader config. 145 func (b20 RunBootenv20) SetTryingDuringReboot(which []snap.Type) error { 146 if !exactlyType(which, snap.TypeKernel) { 147 return fmt.Errorf("for now only kernel related simulation is supported") 148 } 149 if b20.BootVars["kernel_status"] != "try" { 150 return fmt.Errorf("bootloader must be in 'try' mode") 151 } 152 b20.BootVars["kernel_status"] = "trying" 153 return nil 154 } 155 156 // SetRollbackAcrossReboot will simulate a rollback across reboots for either 157 // a new base or kernel or both, as indicated by which. 158 // TODO: only kernel is supported for now. 159 func (b20 RunBootenv20) SetRollbackAcrossReboot(which []snap.Type) error { 160 if !exactlyType(which, snap.TypeKernel) { 161 return fmt.Errorf("for now only kernel related simulation is supported") 162 } 163 if b20.BootVars["kernel_status"] != "try" { 164 return fmt.Errorf("rollback can only be simulated in 'try' mode") 165 } 166 // clean try bootvars and snap_mode 167 b20.BootVars["kernel_status"] = "" 168 return nil 169 }