github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/handlers_rerefresh_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 snapstate_test 21 22 import ( 23 "context" 24 "errors" 25 "fmt" 26 "sort" 27 "strings" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/overlord/snapstate" 32 "github.com/snapcore/snapd/overlord/state" 33 "github.com/snapcore/snapd/snap" 34 . "github.com/snapcore/snapd/testutil" 35 ) 36 37 type reRefreshSuite struct { 38 baseHandlerSuite 39 } 40 41 var _ = Suite(&reRefreshSuite{}) 42 43 func logstr(task *state.Task) string { 44 return strings.Join(task.Log(), "\n") 45 } 46 47 func changeWithLanesAndSnapSetups(st *state.State, snapNames ...string) *state.Change { 48 chg := st.NewChange("dummy", "...") 49 for _, snapName := range snapNames { 50 lane := st.NewLane() 51 tsk := st.NewTask(fmt.Sprintf("a-task-for-snap-%s-in-lane-%d", snapName, lane), "test") 52 tsk.Set("snap-setup", &snapstate.SnapSetup{ 53 SideInfo: &snap.SideInfo{RealName: snapName}, 54 }) 55 chg.AddTask(tsk) 56 tsk.JoinLane(lane) 57 tsk.SetStatus(state.DoneStatus) 58 } 59 return chg 60 } 61 62 func (s *reRefreshSuite) TestDoCheckReRefreshFailsWithoutReRefreshSetup(c *C) { 63 s.state.Lock() 64 chg := changeWithLanesAndSnapSetups(s.state, "some-snap") 65 task := s.state.NewTask("check-rerefresh", "test") 66 chg.AddTask(task) 67 s.state.Unlock() 68 69 s.se.Ensure() 70 s.se.Wait() 71 72 s.state.Lock() 73 defer s.state.Unlock() 74 75 c.Check(task.Status(), Equals, state.ErrorStatus) 76 c.Check(logstr(task), Contains, `no state entry for key`) 77 } 78 79 func (s *reRefreshSuite) TestDoCheckReRefreshFailsIfUpdateFails(c *C) { 80 defer snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) { 81 return nil, nil, errors.New("bzzt") 82 })() 83 84 s.state.Lock() 85 chg := changeWithLanesAndSnapSetups(s.state, "some-snap") 86 task := s.state.NewTask("check-rerefresh", "test") 87 task.Set("rerefresh-setup", map[string]interface{}{}) 88 chg.AddTask(task) 89 s.state.Unlock() 90 91 s.se.Ensure() 92 s.se.Wait() 93 94 s.state.Lock() 95 defer s.state.Unlock() 96 97 c.Check(task.Status(), Equals, state.ErrorStatus) 98 c.Check(logstr(task), Contains, `bzzt`) 99 } 100 101 func (s *reRefreshSuite) TestDoCheckReRefreshNoReRefreshes(c *C) { 102 updaterCalled := false 103 defer snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) { 104 updaterCalled = true 105 return nil, nil, nil 106 })() 107 108 s.state.Lock() 109 chg := changeWithLanesAndSnapSetups(s.state, "some-snap") 110 task := s.state.NewTask("check-rerefresh", "test") 111 task.Set("rerefresh-setup", map[string]interface{}{}) 112 chg.AddTask(task) 113 s.state.Unlock() 114 115 s.se.Ensure() 116 s.se.Wait() 117 118 s.state.Lock() 119 defer s.state.Unlock() 120 121 c.Check(task.Status(), Equals, state.DoneStatus) 122 c.Check(logstr(task), Contains, `No re-refreshes found.`) 123 c.Check(updaterCalled, Equals, true) 124 } 125 126 func (s *reRefreshSuite) TestDoCheckReRefreshPassesReRefreshSetupData(c *C) { 127 var chgID string 128 defer snapstate.MockReRefreshUpdateMany(func(ctx context.Context, st *state.State, snaps []string, userID int, filter snapstate.UpdateFilter, flags *snapstate.Flags, changeID string) ([]string, []*state.TaskSet, error) { 129 c.Check(changeID, Equals, chgID) 130 expected := []string{"won", "too", "tree"} 131 sort.Strings(expected) 132 sort.Strings(snaps) 133 c.Check(snaps, DeepEquals, expected) 134 c.Check(userID, Equals, 42) 135 c.Check(flags, DeepEquals, &snapstate.Flags{ 136 DevMode: true, 137 JailMode: true, 138 }) 139 return nil, nil, nil 140 })() 141 142 s.state.Lock() 143 task := s.state.NewTask("check-rerefresh", "test") 144 task.Set("rerefresh-setup", map[string]interface{}{ 145 "user-id": 42, 146 "devmode": true, 147 "jailmode": true, 148 }) 149 chg := changeWithLanesAndSnapSetups(s.state, "won", "too", "tree") 150 chg.AddTask(task) 151 chgID = chg.ID() 152 s.state.Unlock() 153 154 s.se.Ensure() 155 s.se.Wait() 156 157 s.state.Lock() 158 defer s.state.Unlock() 159 160 c.Check(task.Status(), Equals, state.DoneStatus) 161 c.Check(logstr(task), Contains, `No re-refreshes found.`) 162 } 163 164 func (s *reRefreshSuite) TestDoCheckReRefreshAddsNewTasks(c *C) { 165 defer snapstate.MockReRefreshUpdateMany(func(ctx context.Context, st *state.State, snaps []string, userID int, filter snapstate.UpdateFilter, flags *snapstate.Flags, changeID string) ([]string, []*state.TaskSet, error) { 166 expected := []string{"won", "too", "tree"} 167 sort.Strings(expected) 168 sort.Strings(snaps) 169 c.Check(snaps, DeepEquals, expected) 170 171 task := st.NewTask("witness", "...") 172 173 return []string{"won"}, []*state.TaskSet{state.NewTaskSet(task)}, nil 174 })() 175 176 s.state.Lock() 177 chg := changeWithLanesAndSnapSetups(s.state, "won", "too", "tree") 178 task := s.state.NewTask("check-rerefresh", "test") 179 task.Set("rerefresh-setup", map[string]interface{}{}) 180 chg.AddTask(task) 181 s.state.Unlock() 182 183 s.se.Ensure() 184 s.se.Wait() 185 186 s.state.Lock() 187 defer s.state.Unlock() 188 189 c.Check(task.Status(), Equals, state.DoneStatus) 190 c.Check(logstr(task), Contains, `Found re-refresh for "won".`) 191 192 tasks := chg.Tasks() 193 c.Assert(tasks, HasLen, 5) 194 for i, kind := range []string{ 195 "a-task-for-snap-won-in-lane-1", 196 "a-task-for-snap-too-in-lane-2", 197 "a-task-for-snap-tree-in-lane-3", 198 "check-rerefresh", 199 "witness", 200 } { 201 c.Check(tasks[i].Kind(), Equals, kind) 202 } 203 } 204 205 // wrapper around snapstate.RefreshedSnaps for easier testing 206 func refreshedSnaps(task *state.Task) string { 207 snaps := snapstate.RefreshedSnaps(task) 208 sort.Strings(snaps) 209 return strings.Join(snaps, ",") 210 } 211 212 // add a lane with two tasks to chg, the first one with a SnapSetup 213 // for a snap with t1snap, the second one with status t2status. 214 func addLane(st *state.State, chg *state.Change, t1snap string, t2status state.Status) { 215 lane := st.NewLane() 216 t1 := st.NewTask("dummy1", "...") 217 t1.JoinLane(lane) 218 t1.Set("snap-setup", snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: t1snap}}) 219 t1.SetStatus(state.DoneStatus) 220 chg.AddTask(t1) 221 222 t2 := st.NewTask("dummy2", "...") 223 t2.JoinLane(lane) 224 t2.WaitFor(t1) 225 t2.SetStatus(t2status) 226 chg.AddTask(t2) 227 } 228 229 func (s *reRefreshSuite) TestLaneSnapsSimple(c *C) { 230 s.state.Lock() 231 defer s.state.Unlock() 232 chg := s.state.NewChange("testing", "...") 233 addLane(s.state, chg, "aaa", state.DoneStatus) 234 task := s.state.NewTask("check-rerefresh", "...") 235 chg.AddTask(task) 236 c.Check(refreshedSnaps(task), Equals, "aaa") 237 } 238 239 func (s *reRefreshSuite) TestLaneSnapsMoreLanes(c *C) { 240 s.state.Lock() 241 defer s.state.Unlock() 242 chg := s.state.NewChange("testing", "...") 243 addLane(s.state, chg, "aaa", state.DoneStatus) 244 // more lanes, no problem 245 addLane(s.state, chg, "bbb", state.DoneStatus) 246 task := s.state.NewTask("check-rerefresh", "...") 247 chg.AddTask(task) 248 c.Check(refreshedSnaps(task), Equals, "aaa,bbb") 249 } 250 251 func (s *reRefreshSuite) TestLaneSnapsFailedLane(c *C) { 252 s.state.Lock() 253 defer s.state.Unlock() 254 chg := s.state.NewChange("testing", "...") 255 addLane(s.state, chg, "aaa", state.DoneStatus) 256 addLane(s.state, chg, "bbb", state.DoneStatus) 257 // a lane that's failed, no problem 258 addLane(s.state, chg, "ccc", state.ErrorStatus) 259 task := s.state.NewTask("check-rerefresh", "...") 260 chg.AddTask(task) 261 c.Check(refreshedSnaps(task), Equals, "aaa,bbb") 262 } 263 264 func (s *reRefreshSuite) TestLaneSnapsRerefreshResets(c *C) { 265 s.state.Lock() 266 defer s.state.Unlock() 267 chg := s.state.NewChange("testing", "...") 268 addLane(s.state, chg, "aaa", state.DoneStatus) 269 addLane(s.state, chg, "bbb", state.DoneStatus) 270 // a check-rerefresh task resets the list 271 chg.AddTask(s.state.NewTask("check-rerefresh", "...")) 272 addLane(s.state, chg, "ddd", state.DoneStatus) 273 task := s.state.NewTask("check-rerefresh", "...") 274 chg.AddTask(task) 275 c.Check(refreshedSnaps(task), Equals, "ddd") 276 } 277 278 func (s *reRefreshSuite) TestLaneSnapsStopsAtSelf(c *C) { 279 s.state.Lock() 280 defer s.state.Unlock() 281 chg := s.state.NewChange("testing", "...") 282 addLane(s.state, chg, "aaa", state.DoneStatus) 283 addLane(s.state, chg, "bbb", state.DoneStatus) 284 task := s.state.NewTask("check-rerefresh", "...") 285 chg.AddTask(task) 286 addLane(s.state, chg, "ddd", state.DoneStatus) 287 chg.AddTask(s.state.NewTask("check-rerefresh", "...")) 288 289 // unless we're looking for _that_ task (this is defensive; can't really happen) 290 c.Check(refreshedSnaps(task), Equals, "aaa,bbb") 291 } 292 293 func (s *reRefreshSuite) TestLaneSnapsTwoSetups(c *C) { 294 // check that only the first SnapSetup is important 295 s.state.Lock() 296 defer s.state.Unlock() 297 298 ts := state.NewTaskSet() 299 t1 := s.state.NewTask("dummy1", "...") 300 t1.Set("snap-setup", snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "one"}}) 301 t1.SetStatus(state.DoneStatus) 302 ts.AddTask(t1) 303 t2 := s.state.NewTask("dummy2", "...") 304 t2.Set("snap-setup", snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "two"}}) 305 t2.WaitFor(t1) 306 ts.AddTask(t2) 307 t2.SetStatus(state.DoneStatus) 308 ts.JoinLane(s.state.NewLane()) 309 chg := s.state.NewChange("testing", "...") 310 chg.AddAll(ts) 311 312 task := s.state.NewTask("check-rerefresh", "...") 313 chg.AddTask(task) 314 315 c.Check(refreshedSnaps(task), Equals, "one") 316 } 317 318 func (s *reRefreshSuite) TestLaneSnapsBadSetup(c *C) { 319 // check that a bad SnapSetup doesn't make the thing fail 320 s.state.Lock() 321 defer s.state.Unlock() 322 323 ts := state.NewTaskSet() 324 t1 := s.state.NewTask("dummy1", "...") 325 t1.Set("snap-setup", "what is this") 326 t1.SetStatus(state.DoneStatus) 327 ts.AddTask(t1) 328 t2 := s.state.NewTask("dummy2", "...") 329 t2.Set("snap-setup", snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "two"}}) 330 t2.WaitFor(t1) 331 ts.AddTask(t2) 332 t2.SetStatus(state.DoneStatus) 333 ts.JoinLane(s.state.NewLane()) 334 chg := s.state.NewChange("testing", "...") 335 chg.AddAll(ts) 336 337 task := s.state.NewTask("check-rerefresh", "...") 338 chg.AddTask(task) 339 340 c.Check(refreshedSnaps(task), Equals, "two") 341 } 342 343 func (*reRefreshSuite) TestFilterReturnsFalseIfEpochEqual(c *C) { 344 // these work because we're mocking ReadInfo 345 snapst := &snapstate.SnapState{ 346 Active: true, 347 Sequence: []*snap.SideInfo{ 348 {RealName: "some-snap", Revision: snap.R(7)}, 349 }, 350 Current: snap.R(7), 351 SnapType: "app", 352 } 353 354 c.Check(snapstate.ReRefreshFilter(&snap.Info{Epoch: snap.E("0")}, snapst), Equals, true) 355 c.Check(snapstate.ReRefreshFilter(&snap.Info{Epoch: snap.E("1*")}, snapst), Equals, false) 356 c.Check(snapstate.ReRefreshFilter(&snap.Info{Epoch: snap.E("1")}, snapst), Equals, true) 357 } 358 359 func (s *reRefreshSuite) TestFilterReturnsFalseIfEpochEqualZero(c *C) { 360 // these work because we're mocking ReadInfo 361 snapst := &snapstate.SnapState{ 362 Active: true, 363 Sequence: []*snap.SideInfo{ 364 {RealName: "snap-with-empty-epoch", Revision: snap.R(7)}, 365 }, 366 Current: snap.R(7), 367 SnapType: "app", 368 } 369 c.Check(snapstate.ReRefreshFilter(&snap.Info{Epoch: snap.E("0")}, snapst), Equals, false) 370 c.Check(snapstate.ReRefreshFilter(&snap.Info{Epoch: snap.Epoch{}}, snapst), Equals, false) 371 }