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