github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/configstate/configcore/picfg_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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 configcore_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/boot"
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/osutil"
    33  	"github.com/snapcore/snapd/overlord/configstate/configcore"
    34  	"github.com/snapcore/snapd/release"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  type piCfgSuite struct {
    39  	configcoreSuite
    40  
    41  	mockConfigPath string
    42  }
    43  
    44  var _ = Suite(&piCfgSuite{})
    45  
    46  var mockConfigTxt = `
    47  # For more options and information see
    48  # http://www.raspberrypi.org/documentation/configuration/config-txt.md
    49  #hdmi_group=1
    50  # uncomment this if your display has a black border of unused pixels visible
    51  # and your display can output without overscan
    52  #disable_overscan=1
    53  unrelated_options=are-kept
    54  #gpu_mem_512=true
    55  `
    56  
    57  func (s *piCfgSuite) SetUpTest(c *C) {
    58  	s.configcoreSuite.SetUpTest(c)
    59  	c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc"), 0755), IsNil)
    60  
    61  	s.mockConfigPath = filepath.Join(dirs.GlobalRootDir, "/boot/uboot/config.txt")
    62  	err := os.MkdirAll(filepath.Dir(s.mockConfigPath), 0755)
    63  	c.Assert(err, IsNil)
    64  	s.mockConfig(c, mockConfigTxt)
    65  }
    66  
    67  func (s *piCfgSuite) mockConfig(c *C, txt string) {
    68  	err := ioutil.WriteFile(s.mockConfigPath, []byte(txt), 0644)
    69  	c.Assert(err, IsNil)
    70  }
    71  
    72  func (s *piCfgSuite) checkMockConfig(c *C, expected string) {
    73  	c.Check(s.mockConfigPath, testutil.FileEquals, expected)
    74  }
    75  
    76  func (s *piCfgSuite) TestConfigurePiConfigUncommentExisting(c *C) {
    77  	err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"disable_overscan": "1"})
    78  	c.Assert(err, IsNil)
    79  
    80  	expected := strings.Replace(mockConfigTxt, "#disable_overscan=1", "disable_overscan=1", -1)
    81  	s.checkMockConfig(c, expected)
    82  }
    83  
    84  func (s *piCfgSuite) TestConfigurePiConfigCommentExisting(c *C) {
    85  	s.mockConfig(c, mockConfigTxt+"avoid_warnings=1\n")
    86  
    87  	err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"avoid_warnings": ""})
    88  	c.Assert(err, IsNil)
    89  
    90  	expected := mockConfigTxt + "#avoid_warnings=1\n"
    91  	s.checkMockConfig(c, expected)
    92  }
    93  
    94  func (s *piCfgSuite) TestConfigurePiConfigAddNewOption(c *C) {
    95  	err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"framebuffer_depth": "16"})
    96  	c.Assert(err, IsNil)
    97  
    98  	expected := mockConfigTxt + "framebuffer_depth=16\n"
    99  	s.checkMockConfig(c, expected)
   100  
   101  	// add again, verify its not added twice but updated
   102  	err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"framebuffer_depth": "32"})
   103  	c.Assert(err, IsNil)
   104  	expected = mockConfigTxt + "framebuffer_depth=32\n"
   105  	s.checkMockConfig(c, expected)
   106  }
   107  
   108  func (s *piCfgSuite) TestConfigurePiConfigNoChangeUnset(c *C) {
   109  	// ensure we cannot write to the dir to test that we really
   110  	// do not update the file
   111  	err := os.Chmod(filepath.Dir(s.mockConfigPath), 0500)
   112  	c.Assert(err, IsNil)
   113  	defer os.Chmod(filepath.Dir(s.mockConfigPath), 0755)
   114  
   115  	err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"hdmi_group": ""})
   116  	c.Assert(err, IsNil)
   117  }
   118  
   119  func (s *piCfgSuite) TestConfigurePiConfigNoChangeSet(c *C) {
   120  	// ensure we cannot write to the dir to test that we really
   121  	// do not update the file
   122  	err := os.Chmod(filepath.Dir(s.mockConfigPath), 0500)
   123  	c.Assert(err, IsNil)
   124  	defer os.Chmod(filepath.Dir(s.mockConfigPath), 0755)
   125  
   126  	err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"unrelated_options": "cannot-be-set"})
   127  	c.Assert(err, ErrorMatches, `cannot set unsupported configuration value "unrelated_options"`)
   128  }
   129  
   130  func (s *piCfgSuite) TestConfigurePiConfigIntegration(c *C) {
   131  	restore := release.MockOnClassic(false)
   132  	defer restore()
   133  
   134  	err := configcore.Run(&mockConf{
   135  		state: s.state,
   136  		conf: map[string]interface{}{
   137  			"pi-config.disable-overscan": 1,
   138  		},
   139  	})
   140  	c.Assert(err, IsNil)
   141  
   142  	expected := strings.Replace(mockConfigTxt, "#disable_overscan=1", "disable_overscan=1", -1)
   143  	s.checkMockConfig(c, expected)
   144  
   145  	err = configcore.Run(&mockConf{
   146  		state: s.state,
   147  		conf: map[string]interface{}{
   148  			"pi-config.disable-overscan": "",
   149  		},
   150  	})
   151  	c.Assert(err, IsNil)
   152  
   153  	s.checkMockConfig(c, mockConfigTxt)
   154  }
   155  
   156  func (s *piCfgSuite) TestConfigurePiConfigRegression(c *C) {
   157  	restore := release.MockOnClassic(false)
   158  	defer restore()
   159  
   160  	err := configcore.Run(&mockConf{
   161  		state: s.state,
   162  		conf: map[string]interface{}{
   163  			"pi-config.gpu-mem-512": true,
   164  		},
   165  	})
   166  	c.Assert(err, IsNil)
   167  	expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1)
   168  	s.checkMockConfig(c, expected)
   169  }
   170  
   171  func (s *piCfgSuite) TestUpdateConfigUC20RunMode(c *C) {
   172  	restore := release.MockOnClassic(false)
   173  	defer restore()
   174  
   175  	// mock the device as uc20 run mode
   176  	mockCmdline := filepath.Join(dirs.GlobalRootDir, "cmdline")
   177  	err := ioutil.WriteFile(mockCmdline, []byte("snapd_recovery_mode=run"), 0644)
   178  	c.Assert(err, IsNil)
   179  	restore = osutil.MockProcCmdline(mockCmdline)
   180  	defer restore()
   181  
   182  	// write default config at both the uc18 style runtime location and uc20 run
   183  	// mode location to show that we only modify the uc20 one
   184  	piCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "config.txt")
   185  	uc18PiCfg := filepath.Join(dirs.GlobalRootDir, "/boot/uboot/config.txt")
   186  
   187  	err = os.MkdirAll(filepath.Dir(piCfg), 0755)
   188  	c.Assert(err, IsNil)
   189  	err = os.MkdirAll(filepath.Dir(uc18PiCfg), 0755)
   190  	c.Assert(err, IsNil)
   191  
   192  	err = ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644)
   193  	c.Assert(err, IsNil)
   194  	err = ioutil.WriteFile(uc18PiCfg, []byte(mockConfigTxt), 0644)
   195  	c.Assert(err, IsNil)
   196  
   197  	// apply the config
   198  	err = configcore.Run(&mockConf{
   199  		state: s.state,
   200  		conf: map[string]interface{}{
   201  			"pi-config.gpu-mem-512": true,
   202  		},
   203  	})
   204  	c.Assert(err, IsNil)
   205  
   206  	// make sure that the original pi config.txt in /boot/uboot/config.txt
   207  	// didn't change
   208  	c.Check(uc18PiCfg, testutil.FileEquals, mockConfigTxt)
   209  
   210  	// but the real one did change*
   211  	expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1)
   212  	c.Check(piCfg, testutil.FileEquals, expected)
   213  }
   214  
   215  func (s *piCfgSuite) testUpdateConfigUC20NonRunMode(c *C, mode string) {
   216  	restore := release.MockOnClassic(false)
   217  	defer restore()
   218  
   219  	// mock the device as the specified uc20 mode
   220  	mockCmdline := filepath.Join(dirs.GlobalRootDir, "cmdline")
   221  	err := ioutil.WriteFile(mockCmdline, []byte("snapd_recovery_mode="+mode), 0644)
   222  	c.Assert(err, IsNil)
   223  	restore = osutil.MockProcCmdline(mockCmdline)
   224  	defer restore()
   225  
   226  	piCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "config.txt")
   227  
   228  	err = os.MkdirAll(filepath.Dir(piCfg), 0755)
   229  	c.Assert(err, IsNil)
   230  
   231  	err = ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644)
   232  	c.Assert(err, IsNil)
   233  
   234  	// apply the config
   235  	err = configcore.Run(&mockConf{
   236  		state: s.state,
   237  		conf: map[string]interface{}{
   238  			"pi-config.gpu-mem-512": true,
   239  		},
   240  	})
   241  	c.Assert(err, IsNil)
   242  
   243  	// the config.txt didn't change at all
   244  	c.Check(piCfg, testutil.FileEquals, mockConfigTxt)
   245  }
   246  
   247  func (s *piCfgSuite) TestUpdateConfigUC20RecoverModeDoesNothing(c *C) {
   248  	s.testUpdateConfigUC20NonRunMode(c, "recover")
   249  }
   250  
   251  func (s *piCfgSuite) TestUpdateConfigUC20InstallModeDoesNothing(c *C) {
   252  	s.testUpdateConfigUC20NonRunMode(c, "install")
   253  }
   254  
   255  func (s *piCfgSuite) TestFilesystemOnlyApply(c *C) {
   256  	conf := configcore.PlainCoreConfig(map[string]interface{}{
   257  		"pi-config.gpu-mem-512": true,
   258  	})
   259  
   260  	tmpDir := c.MkDir()
   261  	c.Assert(os.MkdirAll(filepath.Join(tmpDir, "/boot/uboot"), 0755), IsNil)
   262  
   263  	// write default config
   264  	piCfg := filepath.Join(tmpDir, "/boot/uboot/config.txt")
   265  	c.Assert(ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644), IsNil)
   266  
   267  	c.Assert(configcore.FilesystemOnlyApply(tmpDir, conf, nil), IsNil)
   268  
   269  	expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1)
   270  	c.Check(piCfg, testutil.FileEquals, expected)
   271  }