github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configstate_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 "fmt" 24 "time" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/overlord" 29 "github.com/snapcore/snapd/overlord/configstate" 30 "github.com/snapcore/snapd/overlord/configstate/config" 31 "github.com/snapcore/snapd/overlord/hookstate" 32 "github.com/snapcore/snapd/overlord/snapstate" 33 "github.com/snapcore/snapd/overlord/state" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/testutil" 36 ) 37 38 type tasksetsSuite struct { 39 state *state.State 40 } 41 42 var _ = Suite(&tasksetsSuite{}) 43 var _ = Suite(&configcoreHijackSuite{}) 44 45 func (s *tasksetsSuite) SetUpTest(c *C) { 46 s.state = state.New(nil) 47 } 48 49 var configureTests = []struct { 50 patch map[string]interface{} 51 optional bool 52 ignoreError bool 53 useDefaults bool 54 }{{ 55 patch: nil, 56 optional: true, 57 ignoreError: false, 58 }, { 59 patch: map[string]interface{}{}, 60 optional: true, 61 ignoreError: false, 62 }, { 63 patch: map[string]interface{}{"foo": "bar"}, 64 optional: false, 65 ignoreError: false, 66 }, { 67 patch: nil, 68 optional: true, 69 ignoreError: true, 70 }, { 71 patch: nil, 72 optional: true, 73 ignoreError: true, 74 useDefaults: true, 75 }} 76 77 func (s *tasksetsSuite) TestConfigureInstalled(c *C) { 78 s.state.Lock() 79 snapstate.Set(s.state, "test-snap", &snapstate.SnapState{ 80 Sequence: []*snap.SideInfo{ 81 {RealName: "test-snap", Revision: snap.R(1)}, 82 }, 83 Current: snap.R(1), 84 Active: true, 85 SnapType: "app", 86 }) 87 s.state.Unlock() 88 89 for _, test := range configureTests { 90 var flags int 91 if test.ignoreError { 92 flags |= snapstate.IgnoreHookError 93 } 94 if test.useDefaults { 95 flags |= snapstate.UseConfigDefaults 96 } 97 98 s.state.Lock() 99 taskset := configstate.Configure(s.state, "test-snap", test.patch, flags) 100 s.state.Unlock() 101 102 tasks := taskset.Tasks() 103 c.Assert(tasks, HasLen, 1) 104 task := tasks[0] 105 106 c.Assert(task.Kind(), Equals, "run-hook") 107 108 summary := `Run configure hook of "test-snap" snap` 109 if test.optional { 110 summary += " if present" 111 } 112 c.Assert(task.Summary(), Equals, summary) 113 114 var hooksup hookstate.HookSetup 115 s.state.Lock() 116 err := task.Get("hook-setup", &hooksup) 117 s.state.Unlock() 118 c.Check(err, IsNil) 119 120 c.Assert(hooksup.Snap, Equals, "test-snap") 121 c.Assert(hooksup.Hook, Equals, "configure") 122 c.Assert(hooksup.Optional, Equals, test.optional) 123 c.Assert(hooksup.IgnoreError, Equals, test.ignoreError) 124 c.Assert(hooksup.Timeout, Equals, 5*time.Minute) 125 126 context, err := hookstate.NewContext(task, task.State(), &hooksup, nil, "") 127 c.Check(err, IsNil) 128 c.Check(context.InstanceName(), Equals, "test-snap") 129 c.Check(context.SnapRevision(), Equals, snap.Revision{}) 130 c.Check(context.HookName(), Equals, "configure") 131 132 var patch map[string]interface{} 133 var useDefaults bool 134 context.Lock() 135 context.Get("use-defaults", &useDefaults) 136 err = context.Get("patch", &patch) 137 context.Unlock() 138 if len(test.patch) > 0 { 139 c.Check(err, IsNil) 140 c.Check(patch, DeepEquals, test.patch) 141 } else { 142 c.Check(err, Equals, state.ErrNoState) 143 c.Check(patch, IsNil) 144 } 145 c.Check(useDefaults, Equals, test.useDefaults) 146 } 147 } 148 149 func (s *tasksetsSuite) TestConfigureInstalledConflict(c *C) { 150 s.state.Lock() 151 defer s.state.Unlock() 152 snapstate.Set(s.state, "test-snap", &snapstate.SnapState{ 153 Sequence: []*snap.SideInfo{ 154 {RealName: "test-snap", Revision: snap.R(1)}, 155 }, 156 Current: snap.R(1), 157 Active: true, 158 SnapType: "app", 159 }) 160 161 ts, err := snapstate.Disable(s.state, "test-snap") 162 c.Assert(err, IsNil) 163 chg := s.state.NewChange("other-change", "...") 164 chg.AddAll(ts) 165 166 patch := map[string]interface{}{"foo": "bar"} 167 _, err = configstate.ConfigureInstalled(s.state, "test-snap", patch, 0) 168 c.Check(err, ErrorMatches, `snap "test-snap" has "other-change" change in progress`) 169 } 170 171 func (s *tasksetsSuite) TestConfigureNotInstalled(c *C) { 172 patch := map[string]interface{}{"foo": "bar"} 173 s.state.Lock() 174 defer s.state.Unlock() 175 176 _, err := configstate.ConfigureInstalled(s.state, "test-snap", patch, 0) 177 c.Check(err, ErrorMatches, `snap "test-snap" is not installed`) 178 179 // core can be configure before being installed 180 _, err = configstate.ConfigureInstalled(s.state, "core", patch, 0) 181 c.Check(err, IsNil) 182 } 183 184 func (s *tasksetsSuite) TestConfigureDenyBases(c *C) { 185 patch := map[string]interface{}{"foo": "bar"} 186 s.state.Lock() 187 defer s.state.Unlock() 188 snapstate.Set(s.state, "test-base", &snapstate.SnapState{ 189 Sequence: []*snap.SideInfo{ 190 {RealName: "test-base", Revision: snap.R(1)}, 191 }, 192 Current: snap.R(1), 193 Active: true, 194 SnapType: "base", 195 }) 196 197 _, err := configstate.ConfigureInstalled(s.state, "test-base", patch, 0) 198 c.Check(err, ErrorMatches, `cannot configure snap "test-base" because it is of type 'base'`) 199 } 200 201 func (s *tasksetsSuite) TestConfigureDenySnapd(c *C) { 202 patch := map[string]interface{}{"foo": "bar"} 203 s.state.Lock() 204 defer s.state.Unlock() 205 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 206 Sequence: []*snap.SideInfo{ 207 {RealName: "snapd", Revision: snap.R(1)}, 208 }, 209 Current: snap.R(1), 210 Active: true, 211 SnapType: "snapd", 212 }) 213 214 _, err := configstate.ConfigureInstalled(s.state, "snapd", patch, 0) 215 c.Check(err, ErrorMatches, `cannot configure the "snapd" snap, please use "system" instead`) 216 } 217 218 type configcoreHijackSuite struct { 219 testutil.BaseTest 220 221 o *overlord.Overlord 222 state *state.State 223 } 224 225 func (s *configcoreHijackSuite) SetUpTest(c *C) { 226 s.BaseTest.SetUpTest(c) 227 s.o = overlord.Mock() 228 s.state = s.o.State() 229 hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner()) 230 c.Assert(err, IsNil) 231 s.o.AddManager(hookMgr) 232 r := configstate.MockConfigcoreExportExperimentalFlags(func(_ config.ConfGetter) error { 233 return nil 234 }) 235 s.AddCleanup(r) 236 237 err = configstate.Init(s.state, hookMgr) 238 c.Assert(err, IsNil) 239 s.o.AddManager(s.o.TaskRunner()) 240 } 241 242 type witnessManager struct { 243 state *state.State 244 committed bool 245 } 246 247 func (wm *witnessManager) Ensure() error { 248 wm.state.Lock() 249 defer wm.state.Unlock() 250 t := config.NewTransaction(wm.state) 251 var witnessCfg bool 252 t.GetMaybe("core", "witness", &witnessCfg) 253 if witnessCfg { 254 wm.committed = true 255 } 256 return nil 257 } 258 259 func (s *configcoreHijackSuite) TestHijack(c *C) { 260 configcoreRan := false 261 witnessCfg := false 262 witnessConfigcoreRun := func(conf config.Conf) error { 263 // called with no state lock! 264 conf.State().Lock() 265 defer conf.State().Unlock() 266 err := conf.Get("core", "witness", &witnessCfg) 267 c.Assert(err, IsNil) 268 configcoreRan = true 269 return nil 270 } 271 r := configstate.MockConfigcoreRun(witnessConfigcoreRun) 272 defer r() 273 274 witnessMgr := &witnessManager{ 275 state: s.state, 276 } 277 s.o.AddManager(witnessMgr) 278 279 s.state.Lock() 280 defer s.state.Unlock() 281 282 ts := configstate.Configure(s.state, "core", map[string]interface{}{ 283 "witness": true, 284 }, 0) 285 286 chg := s.state.NewChange("configure-core", "configure core") 287 chg.AddAll(ts) 288 289 // this will be run by settle helper once no more Ensure are 290 // scheduled, the witnessMgr Ensure would not see the 291 // committed config unless an additional Ensure Loop is 292 // scheduled when committing the configuration 293 observe := func() { 294 c.Check(witnessCfg, Equals, true) 295 c.Check(witnessMgr.committed, Equals, true) 296 } 297 298 s.state.Unlock() 299 err := s.o.SettleObserveBeforeCleanups(5*time.Second, observe) 300 s.state.Lock() 301 c.Assert(err, IsNil) 302 303 c.Check(chg.Err(), IsNil) 304 c.Check(configcoreRan, Equals, true) 305 } 306 307 type miscSuite struct{} 308 309 var _ = Suite(&miscSuite{}) 310 311 func (s *miscSuite) TestRemappingFuncs(c *C) { 312 // We don't change those. 313 c.Assert(configstate.RemapSnapFromRequest("foo"), Equals, "foo") 314 c.Assert(configstate.RemapSnapFromRequest("snapd"), Equals, "snapd") 315 c.Assert(configstate.RemapSnapFromRequest("core"), Equals, "core") 316 c.Assert(configstate.RemapSnapToResponse("foo"), Equals, "foo") 317 c.Assert(configstate.RemapSnapToResponse("snapd"), Equals, "snapd") 318 319 // This is the one we alter. 320 c.Assert(configstate.RemapSnapFromRequest("system"), Equals, "core") 321 c.Assert(configstate.RemapSnapToResponse("core"), Equals, "system") 322 } 323 324 type configcoreExportSuite struct { 325 o *overlord.Overlord 326 state *state.State 327 hookMgr *hookstate.HookManager 328 } 329 330 func (s *configcoreExportSuite) SetUpTest(c *C) { 331 s.o = overlord.Mock() 332 s.state = s.o.State() 333 hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner()) 334 c.Assert(err, IsNil) 335 s.o.AddManager(hookMgr) 336 s.hookMgr = hookMgr 337 } 338 339 func (s *configcoreExportSuite) TestExportHappy(c *C) { 340 var calls int 341 var val string 342 343 tr := config.NewTransaction(s.state) 344 tr.Set("core", "experimental.key", "foobar") 345 tr.Commit() 346 347 r := configstate.MockConfigcoreExportExperimentalFlags(func(conf config.ConfGetter) error { 348 calls++ 349 err := conf.Get("core", "experimental.keys", &val) 350 c.Assert(err, IsNil) 351 return nil 352 }) 353 defer r() 354 err := configstate.Init(s.state, s.hookMgr) 355 c.Assert(err, IsNil) 356 c.Assert(calls, Equals, 1) 357 c.Assert(val, Equals, "foobar") 358 } 359 360 func (s *configcoreExportSuite) TestExportErr(c *C) { 361 var calls int 362 363 r := configstate.MockConfigcoreExportExperimentalFlags(func(conf config.ConfGetter) error { 364 calls++ 365 return fmt.Errorf("bad bad") 366 }) 367 defer r() 368 err := configstate.Init(s.state, s.hookMgr) 369 c.Assert(err, ErrorMatches, "cannot export experimental config flags: bad bad") 370 c.Assert(calls, Equals, 1) 371 }