github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/bootloader/withbootassettesting.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  // +build withbootassetstesting
     3  
     4  /*
     5   * Copyright (C) 2021 Canonical Ltd
     6   *
     7   * This program is free software: you can redistribute it and/or modify
     8   * it under the terms of the GNU General Public License version 3 as
     9   * published by the Free Software Foundation.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14   * GNU General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU General Public License
    17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18   *
    19   */
    20  
    21  package bootloader
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  
    31  	"github.com/snapcore/snapd/bootloader/assets"
    32  	"github.com/snapcore/snapd/logger"
    33  	"github.com/snapcore/snapd/snapdenv"
    34  )
    35  
    36  var maybeInjectOsReadlink = os.Readlink
    37  
    38  func MockMaybeInjectOsReadlink(m func(string) (string, error)) (restore func()) {
    39  	old := maybeInjectOsReadlink
    40  	maybeInjectOsReadlink = m
    41  	return func() {
    42  		maybeInjectOsReadlink = old
    43  	}
    44  }
    45  
    46  func MaybeInjectTestingBootloaderAssets() {
    47  	// this code is ran only when snapd is built with specific testing tag
    48  
    49  	if !snapdenv.Testing() {
    50  		return
    51  	}
    52  
    53  	// log an info level message, it is a testing build of snapd anyway
    54  	logger.Noticef("maybe inject boot assets?")
    55  
    56  	// is there a marker file at /usr/lib/snapd/ in the snap?
    57  	selfExe, err := maybeInjectOsReadlink("/proc/self/exe")
    58  	if err != nil {
    59  		panic(fmt.Sprintf("cannot readlink: %v", err))
    60  	}
    61  
    62  	injectPieceRaw, err := ioutil.ReadFile(filepath.Join(filepath.Dir(selfExe), "bootassetstesting"))
    63  	if os.IsNotExist(err) {
    64  		logger.Noticef("no boot asset testing marker")
    65  		return
    66  	}
    67  	if len(injectPieceRaw) == 0 {
    68  		logger.Noticef("boot asset testing snippet is empty")
    69  	}
    70  	injectPiece := strings.TrimSpace(string(injectPieceRaw))
    71  
    72  	// with boot assets testing enabled and the marker file present, inject
    73  	// a mock boot config update
    74  
    75  	grubBootconfig := assets.Internal("grub.cfg")
    76  	if grubBootconfig == nil {
    77  		panic("no bootconfig")
    78  	}
    79  	snippets := assets.SnippetsForEditions("grub.cfg:static-cmdline")
    80  	if len(snippets) == 0 {
    81  		panic(fmt.Sprintf("cannot obtain internal grub.cfg:static-cmdline snippets"))
    82  	}
    83  
    84  	internalEdition, err := editionFromConfigAsset(bytes.NewReader(grubBootconfig))
    85  	if err != nil {
    86  		panic(fmt.Sprintf("cannot inject boot config for asset: %v", err))
    87  	}
    88  	// bump the injected edition number
    89  	injectedEdition := internalEdition + 1
    90  
    91  	logger.Noticef("injecting grub boot assets for testing, edition: %v snippet: %q", injectedEdition, injectPiece)
    92  
    93  	lastSnippet := string(snippets[len(snippets)-1].Snippet)
    94  	injectedSnippet := lastSnippet + " " + injectPiece
    95  	injectedSnippets := append(snippets,
    96  		assets.ForEditions{FirstEdition: injectedEdition, Snippet: []byte(injectedSnippet)})
    97  
    98  	assets.InjectSnippetsForEditions("grub.cfg:static-cmdline", injectedSnippets)
    99  
   100  	origGrubBoot := string(grubBootconfig)
   101  	bumpedEdition := strings.Replace(origGrubBoot,
   102  		fmt.Sprintf("%s%d", editionHeader, internalEdition),
   103  		fmt.Sprintf("%s%d", editionHeader, injectedEdition),
   104  		1)
   105  	// see data/grub.cfg for reference
   106  	bumpedCmdlineAndEdition := strings.Replace(bumpedEdition,
   107  		fmt.Sprintf(`set snapd_static_cmdline_args='%s'`, lastSnippet),
   108  		fmt.Sprintf(`set snapd_static_cmdline_args='%s'`, injectedSnippet),
   109  		1)
   110  
   111  	assets.InjectInternal("grub.cfg", []byte(bumpedCmdlineAndEdition))
   112  }
   113  
   114  func init() {
   115  	MaybeInjectTestingBootloaderAssets()
   116  }