github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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/logger" 33 "github.com/snapcore/snapd/overlord/configstate/configcore" 34 "github.com/snapcore/snapd/testutil" 35 ) 36 37 type piCfgSuite struct { 38 configcoreSuite 39 40 mockConfigPath string 41 } 42 43 var _ = Suite(&piCfgSuite{}) 44 45 var mockConfigTxt = ` 46 # For more options and information see 47 # http://www.raspberrypi.org/documentation/configuration/config-txt.md 48 #hdmi_group=1 49 # uncomment this if your display has a black border of unused pixels visible 50 # and your display can output without overscan 51 #disable_overscan=1 52 unrelated_options=are-kept 53 #gpu_mem_512=true 54 ` 55 56 func (s *piCfgSuite) SetUpTest(c *C) { 57 s.configcoreSuite.SetUpTest(c) 58 c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc"), 0755), IsNil) 59 60 s.mockConfigPath = filepath.Join(dirs.GlobalRootDir, "/boot/uboot/config.txt") 61 err := os.MkdirAll(filepath.Dir(s.mockConfigPath), 0755) 62 c.Assert(err, IsNil) 63 s.mockConfig(c, mockConfigTxt) 64 } 65 66 func (s *piCfgSuite) mockConfig(c *C, txt string) { 67 err := ioutil.WriteFile(s.mockConfigPath, []byte(txt), 0644) 68 c.Assert(err, IsNil) 69 } 70 71 func (s *piCfgSuite) checkMockConfig(c *C, expected string) { 72 c.Check(s.mockConfigPath, testutil.FileEquals, expected) 73 } 74 75 func (s *piCfgSuite) TestConfigurePiConfigUncommentExisting(c *C) { 76 err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"disable_overscan": "1"}) 77 c.Assert(err, IsNil) 78 79 expected := strings.Replace(mockConfigTxt, "#disable_overscan=1", "disable_overscan=1", -1) 80 s.checkMockConfig(c, expected) 81 } 82 83 func (s *piCfgSuite) TestConfigurePiConfigCommentExisting(c *C) { 84 s.mockConfig(c, mockConfigTxt+"avoid_warnings=1\n") 85 86 err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"avoid_warnings": ""}) 87 c.Assert(err, IsNil) 88 89 expected := mockConfigTxt + "#avoid_warnings=1\n" 90 s.checkMockConfig(c, expected) 91 } 92 93 func (s *piCfgSuite) TestConfigurePiConfigAddNewOption(c *C) { 94 err := configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"framebuffer_depth": "16"}) 95 c.Assert(err, IsNil) 96 97 expected := mockConfigTxt + "framebuffer_depth=16\n" 98 s.checkMockConfig(c, expected) 99 100 // add again, verify its not added twice but updated 101 err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"framebuffer_depth": "32"}) 102 c.Assert(err, IsNil) 103 expected = mockConfigTxt + "framebuffer_depth=32\n" 104 s.checkMockConfig(c, expected) 105 } 106 107 func (s *piCfgSuite) TestConfigurePiConfigNoChangeUnset(c *C) { 108 // ensure we cannot write to the dir to test that we really 109 // do not update the file 110 err := os.Chmod(filepath.Dir(s.mockConfigPath), 0500) 111 c.Assert(err, IsNil) 112 defer os.Chmod(filepath.Dir(s.mockConfigPath), 0755) 113 114 err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"hdmi_group": ""}) 115 c.Assert(err, IsNil) 116 } 117 118 func (s *piCfgSuite) TestConfigurePiConfigNoChangeSet(c *C) { 119 // ensure we cannot write to the dir to test that we really 120 // do not update the file 121 err := os.Chmod(filepath.Dir(s.mockConfigPath), 0500) 122 c.Assert(err, IsNil) 123 defer os.Chmod(filepath.Dir(s.mockConfigPath), 0755) 124 125 err = configcore.UpdatePiConfig(s.mockConfigPath, map[string]string{"unrelated_options": "cannot-be-set"}) 126 c.Assert(err, ErrorMatches, `cannot set unsupported configuration value "unrelated_options"`) 127 } 128 129 func (s *piCfgSuite) TestConfigurePiConfigIntegration(c *C) { 130 err := configcore.Run(coreDev, &mockConf{ 131 state: s.state, 132 conf: map[string]interface{}{ 133 "pi-config.disable-overscan": 1, 134 }, 135 }) 136 c.Assert(err, IsNil) 137 138 expected := strings.Replace(mockConfigTxt, "#disable_overscan=1", "disable_overscan=1", -1) 139 s.checkMockConfig(c, expected) 140 141 err = configcore.Run(coreDev, &mockConf{ 142 state: s.state, 143 conf: map[string]interface{}{ 144 "pi-config.disable-overscan": "", 145 }, 146 }) 147 c.Assert(err, IsNil) 148 149 s.checkMockConfig(c, mockConfigTxt) 150 } 151 152 func (s *piCfgSuite) TestConfigurePiConfigRegression(c *C) { 153 err := configcore.Run(coreDev, &mockConf{ 154 state: s.state, 155 conf: map[string]interface{}{ 156 "pi-config.gpu-mem-512": true, 157 }, 158 }) 159 c.Assert(err, IsNil) 160 expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1) 161 s.checkMockConfig(c, expected) 162 } 163 164 func (s *piCfgSuite) TestUpdateConfigUC20RunMode(c *C) { 165 uc20DevRunMode := mockDev{ 166 mode: "run", 167 uc20: true, 168 } 169 170 // write default config at both the uc18 style runtime location and uc20 run 171 // mode location to show that we only modify the uc20 one 172 piCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "config.txt") 173 uc18PiCfg := filepath.Join(dirs.GlobalRootDir, "/boot/uboot/config.txt") 174 175 err := os.MkdirAll(filepath.Dir(piCfg), 0755) 176 c.Assert(err, IsNil) 177 err = os.MkdirAll(filepath.Dir(uc18PiCfg), 0755) 178 c.Assert(err, IsNil) 179 180 err = ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644) 181 c.Assert(err, IsNil) 182 err = ioutil.WriteFile(uc18PiCfg, []byte(mockConfigTxt), 0644) 183 c.Assert(err, IsNil) 184 185 // apply the config 186 err = configcore.Run(uc20DevRunMode, &mockConf{ 187 state: s.state, 188 conf: map[string]interface{}{ 189 "pi-config.gpu-mem-512": true, 190 }, 191 }) 192 c.Assert(err, IsNil) 193 194 // make sure that the original pi config.txt in /boot/uboot/config.txt 195 // didn't change 196 c.Check(uc18PiCfg, testutil.FileEquals, mockConfigTxt) 197 198 // but the real one did change* 199 expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1) 200 c.Check(piCfg, testutil.FileEquals, expected) 201 } 202 203 func (s *piCfgSuite) testUpdateConfigUC20NonRunMode(c *C, mode string) { 204 uc20DevMode := mockDev{ 205 mode: mode, 206 uc20: true, 207 } 208 209 piCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "config.txt") 210 211 err := os.MkdirAll(filepath.Dir(piCfg), 0755) 212 c.Assert(err, IsNil) 213 214 err = ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644) 215 c.Assert(err, IsNil) 216 217 // apply the config 218 err = configcore.Run(uc20DevMode, &mockConf{ 219 state: s.state, 220 conf: map[string]interface{}{ 221 "pi-config.gpu-mem-512": true, 222 }, 223 }) 224 c.Assert(err, IsNil) 225 226 // the config.txt didn't change at all 227 c.Check(piCfg, testutil.FileEquals, mockConfigTxt) 228 } 229 230 func (s *piCfgSuite) TestUpdateConfigUC20RecoverModeDoesNothing(c *C) { 231 s.testUpdateConfigUC20NonRunMode(c, "recover") 232 } 233 234 func (s *piCfgSuite) TestUpdateConfigUC20InstallModeDoesNothing(c *C) { 235 s.testUpdateConfigUC20NonRunMode(c, "install") 236 } 237 238 func (s *piCfgSuite) TestFilesystemOnlyApply(c *C) { 239 conf := configcore.PlainCoreConfig(map[string]interface{}{ 240 "pi-config.gpu-mem-512": true, 241 }) 242 243 tmpDir := c.MkDir() 244 c.Assert(os.MkdirAll(filepath.Join(tmpDir, "/boot/uboot"), 0755), IsNil) 245 246 // write default config 247 piCfg := filepath.Join(tmpDir, "/boot/uboot/config.txt") 248 c.Assert(ioutil.WriteFile(piCfg, []byte(mockConfigTxt), 0644), IsNil) 249 250 c.Assert(configcore.FilesystemOnlyApply(coreDev, tmpDir, conf), IsNil) 251 252 expected := strings.Replace(mockConfigTxt, "#gpu_mem_512=true", "gpu_mem_512=true", -1) 253 c.Check(piCfg, testutil.FileEquals, expected) 254 } 255 256 func (s *piCfgSuite) TestConfigurePiConfigSkippedOnAvnetKernel(c *C) { 257 logbuf, r := logger.MockLogger() 258 defer r() 259 260 avnetDev := mockDev{classic: false, kernel: "avnet-avt-iiotg20-kernel"} 261 262 err := configcore.Run(avnetDev, &mockConf{ 263 state: s.state, 264 conf: map[string]interface{}{ 265 "pi-config.disable-overscan": 1, 266 }, 267 }) 268 c.Assert(err, IsNil) 269 270 c.Check(logbuf.String(), testutil.Contains, "ignoring pi-config settings: configuration cannot be applied: boot measures config.txt") 271 // change was ignored 272 s.checkMockConfig(c, mockConfigTxt) 273 } 274 275 func (s *piCfgSuite) TestConfigurePiConfigSkippedOnWrongMode(c *C) { 276 logbuf, r := logger.MockLogger() 277 defer r() 278 279 uc20DevInstallMode := mockDev{ 280 classic: false, 281 mode: "install", 282 uc20: true, 283 } 284 285 err := configcore.Run(uc20DevInstallMode, &mockConf{ 286 state: s.state, 287 conf: map[string]interface{}{ 288 "pi-config.disable-overscan": 1, 289 }, 290 }) 291 c.Assert(err, IsNil) 292 293 c.Check(logbuf.String(), testutil.Contains, "ignoring pi-config settings: configuration cannot be applied: unsupported system mode") 294 // change was ignored 295 s.checkMockConfig(c, mockConfigTxt) 296 } 297 298 func (s *piCfgSuite) TestConfigurePiConfigSkippedOnIgnoreHeader(c *C) { 299 logbuf, r := logger.MockLogger() 300 defer r() 301 302 tests := []struct { 303 header string 304 shouldIgnore bool 305 }{ 306 // ignored 307 {"# Snapd-Edit: no", true}, 308 {"# Snapd-Edit: no ", true}, 309 {"# snapd-edit: No", true}, 310 {"# SNAPD-EDIT: NO", true}, 311 // not ignored 312 {"# Snapd-Edit: noAND THEN random words", false}, 313 {"not first line \n# SNAPD-EDIT: NO", false}, 314 {"# random things and then SNAPD-EDIT: NO", false}, 315 } 316 317 for _, tc := range tests { 318 mockConfigWithHeader := tc.header + mockConfigTxt 319 s.mockConfig(c, mockConfigWithHeader) 320 err := configcore.Run(coreDev, &mockConf{ 321 state: s.state, 322 conf: map[string]interface{}{ 323 "pi-config.disable-overscan": 1, 324 }, 325 }) 326 c.Assert(err, IsNil) 327 328 if tc.shouldIgnore { 329 c.Check(logbuf.String(), testutil.Contains, "ignoring pi-config settings: configuration cannot be applied: no-editing header found") 330 // change was ignored 331 s.checkMockConfig(c, mockConfigWithHeader) 332 } else { 333 c.Check(logbuf.String(), HasLen, 0) 334 expected := strings.Replace(mockConfigWithHeader, "#disable_overscan=1", "disable_overscan=1", -1) 335 s.checkMockConfig(c, expected) 336 } 337 338 logbuf.Reset() 339 } 340 }