github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  }