github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/overlord/configstate/handler_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 configstate_test 21 22 import ( 23 "io/ioutil" 24 "path/filepath" 25 "testing" 26 "time" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/asserts" 31 "github.com/snapcore/snapd/asserts/assertstest" 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/overlord" 34 "github.com/snapcore/snapd/overlord/configstate" 35 "github.com/snapcore/snapd/overlord/configstate/config" 36 "github.com/snapcore/snapd/overlord/hookstate" 37 "github.com/snapcore/snapd/overlord/hookstate/hooktest" 38 "github.com/snapcore/snapd/overlord/snapstate" 39 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 40 "github.com/snapcore/snapd/overlord/state" 41 "github.com/snapcore/snapd/release" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/snap/snaptest" 44 "github.com/snapcore/snapd/sysconfig" 45 "github.com/snapcore/snapd/testutil" 46 ) 47 48 func TestConfigState(t *testing.T) { TestingT(t) } 49 50 type configureHandlerSuite struct { 51 testutil.BaseTest 52 53 state *state.State 54 context *hookstate.Context 55 handler hookstate.Handler 56 } 57 58 var _ = Suite(&configureHandlerSuite{}) 59 60 func (s *configureHandlerSuite) SetUpTest(c *C) { 61 s.BaseTest.SetUpTest(c) 62 dirs.SetRootDir(c.MkDir()) 63 s.AddCleanup(func() { dirs.SetRootDir("/") }) 64 65 s.state = state.New(nil) 66 s.state.Lock() 67 defer s.state.Unlock() 68 69 coreSnapYaml := `name: core 70 version: 1.0 71 type: os 72 ` 73 snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{ 74 RealName: "core", 75 Revision: snap.R(1), 76 }) 77 snapstate.Set(s.state, "core", &snapstate.SnapState{ 78 Active: true, 79 Sequence: []*snap.SideInfo{ 80 {RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"}, 81 }, 82 Current: snap.R(1), 83 SnapType: "os", 84 }) 85 86 s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 87 88 task := s.state.NewTask("test-task", "my test task") 89 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} 90 91 var err error 92 s.context, err = hookstate.NewContext(task, task.State(), setup, hooktest.NewMockHandler(), "") 93 c.Assert(err, IsNil) 94 95 s.handler = configstate.NewConfigureHandler(s.context) 96 } 97 98 func (s *configureHandlerSuite) TestBeforeInitializesTransaction(c *C) { 99 // Initialize context 100 s.context.Lock() 101 s.context.Set("patch", map[string]interface{}{ 102 "foo": "bar", 103 }) 104 s.context.Unlock() 105 106 c.Check(s.handler.Before(), IsNil) 107 108 s.context.Lock() 109 tr := configstate.ContextTransaction(s.context) 110 s.context.Unlock() 111 112 var value string 113 c.Check(tr.Get("test-snap", "foo", &value), IsNil) 114 c.Check(value, Equals, "bar") 115 } 116 117 func makeModel(override map[string]interface{}) *asserts.Model { 118 model := map[string]interface{}{ 119 "type": "model", 120 "authority-id": "brand", 121 "series": "16", 122 "brand-id": "brand", 123 "model": "baz-3000", 124 "architecture": "armhf", 125 "gadget": "brand-gadget", 126 "kernel": "kernel", 127 "timestamp": "2018-01-01T08:00:00+00:00", 128 } 129 return assertstest.FakeAssertion(model, override).(*asserts.Model) 130 } 131 132 func (s *configureHandlerSuite) TestBeforeInitializesTransactionUseDefaults(c *C) { 133 r := release.MockOnClassic(false) 134 defer r() 135 136 const mockGadgetSnapYaml = ` 137 name: canonical-pc 138 type: gadget 139 ` 140 var mockGadgetYaml = []byte(` 141 defaults: 142 testsnapidididididididididididid: 143 bar: baz 144 num: 1.305 145 146 volumes: 147 volume-id: 148 bootloader: grub 149 `) 150 151 info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}) 152 err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644) 153 c.Assert(err, IsNil) 154 155 s.state.Lock() 156 snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{ 157 Active: true, 158 Sequence: []*snap.SideInfo{ 159 {RealName: "canonical-pc", Revision: snap.R(1)}, 160 }, 161 Current: snap.R(1), 162 SnapType: "gadget", 163 }) 164 165 r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{ 166 "gadget": "canonical-pc", 167 })) 168 defer r() 169 170 const mockTestSnapYaml = ` 171 name: test-snap 172 hooks: 173 configure: 174 ` 175 176 snaptest.MockSnap(c, mockTestSnapYaml, &snap.SideInfo{Revision: snap.R(11)}) 177 snapstate.Set(s.state, "test-snap", &snapstate.SnapState{ 178 Active: true, 179 Sequence: []*snap.SideInfo{ 180 {RealName: "test-snap", Revision: snap.R(11), SnapID: "testsnapidididididididididididid"}, 181 }, 182 Current: snap.R(11), 183 SnapType: "app", 184 }) 185 s.state.Unlock() 186 187 // Initialize context 188 s.context.Lock() 189 s.context.Set("use-defaults", true) 190 s.context.Unlock() 191 192 c.Assert(s.handler.Before(), IsNil) 193 194 s.context.Lock() 195 tr := configstate.ContextTransaction(s.context) 196 s.context.Unlock() 197 198 var value string 199 c.Check(tr.Get("test-snap", "bar", &value), IsNil) 200 c.Check(value, Equals, "baz") 201 var fl float64 202 c.Check(tr.Get("test-snap", "num", &fl), IsNil) 203 c.Check(fl, Equals, 1.305) 204 } 205 206 func (s *configureHandlerSuite) TestBeforeUseDefaultsMissingHook(c *C) { 207 r := release.MockOnClassic(false) 208 defer r() 209 210 const mockGadgetSnapYaml = ` 211 name: canonical-pc 212 type: gadget 213 ` 214 var mockGadgetYaml = []byte(` 215 defaults: 216 testsnapidididididididididididid: 217 bar: baz 218 num: 1.305 219 220 volumes: 221 volume-id: 222 bootloader: grub 223 `) 224 225 info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}) 226 err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644) 227 c.Assert(err, IsNil) 228 229 s.state.Lock() 230 snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{ 231 Active: true, 232 Sequence: []*snap.SideInfo{ 233 {RealName: "canonical-pc", Revision: snap.R(1)}, 234 }, 235 Current: snap.R(1), 236 SnapType: "gadget", 237 }) 238 239 r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{ 240 "gadget": "canonical-pc", 241 })) 242 defer r() 243 244 snapstate.Set(s.state, "test-snap", &snapstate.SnapState{ 245 Active: true, 246 Sequence: []*snap.SideInfo{ 247 {RealName: "test-snap", Revision: snap.R(11), SnapID: "testsnapidididididididididididid"}, 248 }, 249 Current: snap.R(11), 250 SnapType: "app", 251 }) 252 s.state.Unlock() 253 254 // Initialize context 255 s.context.Lock() 256 s.context.Set("use-defaults", true) 257 s.context.Unlock() 258 259 err = s.handler.Before() 260 c.Check(err, ErrorMatches, `cannot apply gadget config defaults for snap "test-snap", no configure hook`) 261 } 262 263 type configcoreHandlerSuite struct { 264 testutil.BaseTest 265 266 o *overlord.Overlord 267 state *state.State 268 } 269 270 var _ = Suite(&configcoreHandlerSuite{}) 271 272 func (s *configcoreHandlerSuite) SetUpTest(c *C) { 273 s.BaseTest.SetUpTest(c) 274 275 dirs.SetRootDir(c.MkDir()) 276 s.AddCleanup(func() { dirs.SetRootDir("/") }) 277 278 s.o = overlord.Mock() 279 s.state = s.o.State() 280 281 restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 282 s.AddCleanup(restore) 283 284 hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner()) 285 c.Assert(err, IsNil) 286 s.o.AddManager(hookMgr) 287 r := configstate.MockConfigcoreExportExperimentalFlags(func(_ config.ConfGetter) error { 288 return nil 289 }) 290 s.AddCleanup(r) 291 292 err = configstate.Init(s.state, hookMgr) 293 294 c.Assert(err, IsNil) 295 s.o.AddManager(s.o.TaskRunner()) 296 297 r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{ 298 "gadget": "canonical-pc", 299 })) 300 s.AddCleanup(r) 301 302 s.state.Lock() 303 defer s.state.Unlock() 304 305 s.state.Set("seeded", true) 306 snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{ 307 Active: true, 308 Sequence: []*snap.SideInfo{ 309 {RealName: "canonical-pc", Revision: snap.R(1)}, 310 }, 311 Current: snap.R(1), 312 SnapType: "gadget", 313 }) 314 } 315 316 const mockGadgetSnapYaml = ` 317 name: canonical-pc 318 type: gadget 319 ` 320 321 func (s *configcoreHandlerSuite) TestRunsWhenSnapdOnly(c *C) { 322 r := release.MockOnClassic(false) 323 defer r() 324 325 var mockGadgetYaml = ` 326 defaults: 327 system: 328 foo: bar 329 330 volumes: 331 volume-id: 332 bootloader: grub 333 ` 334 s.state.Lock() 335 defer s.state.Unlock() 336 337 ts := configstate.Configure(s.state, "core", nil, snapstate.UseConfigDefaults) 338 chg := s.state.NewChange("configure-core", "configure core") 339 chg.AddAll(ts) 340 341 snaptest.MockSnapWithFiles(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}, [][]string{ 342 {"meta/gadget.yaml", mockGadgetYaml}, 343 }) 344 345 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 346 Active: true, 347 Sequence: []*snap.SideInfo{ 348 {RealName: "snapd", Revision: snap.R(1)}, 349 }, 350 Current: snap.R(1), 351 SnapType: "snapd", 352 }) 353 354 witnessConfigcoreRun := func(dev sysconfig.Device, conf config.Conf) error { 355 c.Check(dev.Kernel(), Equals, "kernel") 356 // called with no state lock! 357 conf.State().Lock() 358 defer conf.State().Unlock() 359 var val string 360 err := conf.Get("core", "foo", &val) 361 c.Assert(err, IsNil) 362 c.Check(val, Equals, "bar") 363 return nil 364 } 365 r = configstate.MockConfigcoreRun(witnessConfigcoreRun) 366 defer r() 367 368 s.state.Unlock() 369 err := s.o.Settle(5 * time.Second) 370 s.state.Lock() 371 // Initialize context 372 c.Assert(err, IsNil) 373 374 tr := config.NewTransaction(s.state) 375 var foo string 376 err = tr.Get("core", "foo", &foo) 377 c.Assert(err, IsNil) 378 c.Check(foo, Equals, "bar") 379 } 380 381 func (s *configcoreHandlerSuite) TestRunsWhenCoreOnly(c *C) { 382 r := release.MockOnClassic(false) 383 defer r() 384 385 var mockGadgetYaml = ` 386 defaults: 387 system: 388 foo: bar 389 390 volumes: 391 volume-id: 392 bootloader: grub 393 ` 394 s.state.Lock() 395 defer s.state.Unlock() 396 397 ts := configstate.Configure(s.state, "core", nil, snapstate.UseConfigDefaults) 398 chg := s.state.NewChange("configure-core", "configure core") 399 chg.AddAll(ts) 400 401 snaptest.MockSnapWithFiles(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}, [][]string{ 402 {"meta/gadget.yaml", mockGadgetYaml}, 403 }) 404 405 snapstate.Set(s.state, "core", &snapstate.SnapState{ 406 Active: true, 407 Sequence: []*snap.SideInfo{ 408 {RealName: "core", Revision: snap.R(1)}, 409 }, 410 Current: snap.R(1), 411 SnapType: "os", 412 }) 413 414 witnessConfigcoreRun := func(dev sysconfig.Device, conf config.Conf) error { 415 c.Check(dev.Kernel(), Equals, "kernel") 416 // called with no state lock! 417 conf.State().Lock() 418 defer conf.State().Unlock() 419 var val string 420 err := conf.Get("core", "foo", &val) 421 c.Assert(err, IsNil) 422 c.Check(val, Equals, "bar") 423 return nil 424 } 425 r = configstate.MockConfigcoreRun(witnessConfigcoreRun) 426 defer r() 427 428 s.state.Unlock() 429 err := s.o.Settle(5 * time.Second) 430 s.state.Lock() 431 // Initialize context 432 c.Assert(err, IsNil) 433 434 tr := config.NewTransaction(s.state) 435 var foo string 436 err = tr.Get("core", "foo", &foo) 437 c.Assert(err, IsNil) 438 c.Check(foo, Equals, "bar") 439 }