github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/state/state_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2020 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 state_test 21 22 import ( 23 "bytes" 24 "errors" 25 "fmt" 26 "testing" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/overlord/state" 32 ) 33 34 func TestState(t *testing.T) { TestingT(t) } 35 36 type stateSuite struct{} 37 38 var _ = Suite(&stateSuite{}) 39 40 type mgrState1 struct { 41 A string 42 } 43 44 type Count2 struct { 45 B int 46 } 47 48 type mgrState2 struct { 49 C *Count2 50 } 51 52 func (ss *stateSuite) TestLockUnlock(c *C) { 53 st := state.New(nil) 54 st.Lock() 55 st.Unlock() 56 } 57 58 func (ss *stateSuite) TestGetAndSet(c *C) { 59 st := state.New(nil) 60 st.Lock() 61 defer st.Unlock() 62 63 mSt1 := &mgrState1{A: "foo"} 64 st.Set("mgr1", mSt1) 65 mSt2 := &mgrState2{C: &Count2{B: 42}} 66 st.Set("mgr2", mSt2) 67 68 var mSt1B mgrState1 69 err := st.Get("mgr1", &mSt1B) 70 c.Assert(err, IsNil) 71 c.Check(&mSt1B, DeepEquals, mSt1) 72 73 var mSt2B mgrState2 74 err = st.Get("mgr2", &mSt2B) 75 c.Assert(err, IsNil) 76 c.Check(&mSt2B, DeepEquals, mSt2) 77 } 78 79 func (ss *stateSuite) TestSetPanic(c *C) { 80 st := state.New(nil) 81 st.Lock() 82 defer st.Unlock() 83 84 unsupported := struct { 85 Ch chan bool 86 }{} 87 c.Check(func() { st.Set("mgr9", unsupported) }, PanicMatches, `internal error: could not marshal value for state entry "mgr9": json: unsupported type:.*`) 88 } 89 90 func (ss *stateSuite) TestGetNoState(c *C) { 91 st := state.New(nil) 92 st.Lock() 93 defer st.Unlock() 94 95 var mSt1B mgrState1 96 err := st.Get("mgr9", &mSt1B) 97 c.Check(err, Equals, state.ErrNoState) 98 } 99 100 func (ss *stateSuite) TestSetToNilDeletes(c *C) { 101 st := state.New(nil) 102 st.Lock() 103 defer st.Unlock() 104 105 st.Set("a", map[string]int{"a": 1}) 106 var v map[string]int 107 err := st.Get("a", &v) 108 c.Assert(err, IsNil) 109 c.Check(v, HasLen, 1) 110 111 st.Set("a", nil) 112 113 var v1 map[string]int 114 err = st.Get("a", &v1) 115 c.Check(err, Equals, state.ErrNoState) 116 c.Check(v1, HasLen, 0) 117 } 118 119 func (ss *stateSuite) TestNullMeansNoState(c *C) { 120 buf := bytes.NewBufferString(`{"data": {"a": null}}`) 121 st, err := state.ReadState(nil, buf) 122 c.Assert(err, IsNil) 123 124 st.Lock() 125 defer st.Unlock() 126 127 var v1 map[string]int 128 err = st.Get("a", &v1) 129 c.Check(err, Equals, state.ErrNoState) 130 c.Check(v1, HasLen, 0) 131 } 132 133 func (ss *stateSuite) TestGetUnmarshalProblem(c *C) { 134 st := state.New(nil) 135 st.Lock() 136 defer st.Unlock() 137 138 mismatched := struct { 139 A int 140 }{A: 22} 141 st.Set("mgr9", &mismatched) 142 143 var mSt1B mgrState1 144 err := st.Get("mgr9", &mSt1B) 145 c.Check(err, ErrorMatches, `internal error: could not unmarshal state entry "mgr9": json: cannot unmarshal .*`) 146 } 147 148 func (ss *stateSuite) TestCache(c *C) { 149 st := state.New(nil) 150 st.Lock() 151 defer st.Unlock() 152 153 type key1 struct{} 154 type key2 struct{} 155 156 c.Assert(st.Cached(key1{}), Equals, nil) 157 158 st.Cache(key1{}, "value1") 159 st.Cache(key2{}, "value2") 160 c.Assert(st.Cached(key1{}), Equals, "value1") 161 c.Assert(st.Cached(key2{}), Equals, "value2") 162 163 st.Cache(key1{}, nil) 164 c.Assert(st.Cached(key1{}), Equals, nil) 165 166 _, ok := st.Cached("key3").(string) 167 c.Assert(ok, Equals, false) 168 } 169 170 type fakeStateBackend struct { 171 checkpoints [][]byte 172 error func() error 173 ensureBefore time.Duration 174 restartRequested bool 175 } 176 177 func (b *fakeStateBackend) Checkpoint(data []byte) error { 178 b.checkpoints = append(b.checkpoints, data) 179 if b.error != nil { 180 return b.error() 181 } 182 return nil 183 } 184 185 func (b *fakeStateBackend) EnsureBefore(d time.Duration) { 186 b.ensureBefore = d 187 } 188 189 func (b *fakeStateBackend) RequestRestart(t state.RestartType) { 190 b.restartRequested = true 191 } 192 193 func (ss *stateSuite) TestImplicitCheckpointAndRead(c *C) { 194 b := new(fakeStateBackend) 195 st := state.New(b) 196 st.Lock() 197 198 st.Set("v", 1) 199 mSt1 := &mgrState1{A: "foo"} 200 st.Set("mgr1", mSt1) 201 mSt2 := &mgrState2{C: &Count2{B: 42}} 202 st.Set("mgr2", mSt2) 203 204 // implicit checkpoint 205 st.Unlock() 206 207 c.Assert(b.checkpoints, HasLen, 1) 208 209 buf := bytes.NewBuffer(b.checkpoints[0]) 210 211 st2, err := state.ReadState(nil, buf) 212 c.Assert(err, IsNil) 213 c.Assert(st2.Modified(), Equals, false) 214 215 st2.Lock() 216 defer st2.Unlock() 217 218 var v int 219 err = st2.Get("v", &v) 220 c.Assert(err, IsNil) 221 c.Check(v, Equals, 1) 222 223 var mSt1B mgrState1 224 err = st2.Get("mgr1", &mSt1B) 225 c.Assert(err, IsNil) 226 c.Check(&mSt1B, DeepEquals, mSt1) 227 228 var mSt2B mgrState2 229 err = st2.Get("mgr2", &mSt2B) 230 c.Assert(err, IsNil) 231 c.Check(&mSt2B, DeepEquals, mSt2) 232 } 233 234 func (ss *stateSuite) TestImplicitCheckpointRetry(c *C) { 235 restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 1*time.Second) 236 defer restore() 237 238 retries := 0 239 boom := errors.New("boom") 240 error := func() error { 241 retries++ 242 if retries == 2 { 243 return nil 244 } 245 return boom 246 } 247 b := &fakeStateBackend{error: error} 248 st := state.New(b) 249 st.Lock() 250 251 // implicit checkpoint will retry 252 st.Unlock() 253 254 c.Check(retries, Equals, 2) 255 } 256 257 func (ss *stateSuite) TestImplicitCheckpointPanicsAfterFailedRetries(c *C) { 258 restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 80*time.Millisecond) 259 defer restore() 260 261 boom := errors.New("boom") 262 retries := 0 263 errFn := func() error { 264 retries++ 265 return boom 266 } 267 b := &fakeStateBackend{error: errFn} 268 st := state.New(b) 269 st.Lock() 270 271 // implicit checkpoint will panic after all failed retries 272 t0 := time.Now() 273 c.Check(func() { st.Unlock() }, PanicMatches, "cannot checkpoint even after 80ms of retries every 2ms: boom") 274 // we did at least a couple 275 c.Check(retries > 2, Equals, true, Commentf("expected more than 2 retries got %v", retries)) 276 c.Check(time.Since(t0) > 80*time.Millisecond, Equals, true) 277 } 278 279 func (ss *stateSuite) TestImplicitCheckpointModifiedOnly(c *C) { 280 restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 1*time.Second) 281 defer restore() 282 283 b := &fakeStateBackend{} 284 st := state.New(b) 285 st.Lock() 286 st.Unlock() 287 st.Lock() 288 st.Unlock() 289 290 c.Assert(b.checkpoints, HasLen, 1) 291 292 st.Lock() 293 st.Set("foo", "bar") 294 st.Unlock() 295 296 c.Assert(b.checkpoints, HasLen, 2) 297 } 298 299 func (ss *stateSuite) TestNewChangeAndChanges(c *C) { 300 st := state.New(nil) 301 st.Lock() 302 defer st.Unlock() 303 304 chg1 := st.NewChange("install", "...") 305 chg2 := st.NewChange("remove", "...") 306 307 chgs := st.Changes() 308 c.Check(chgs, HasLen, 2) 309 310 expected := map[string]*state.Change{ 311 chg1.ID(): chg1, 312 chg2.ID(): chg2, 313 } 314 315 for _, chg := range chgs { 316 c.Check(chg, Equals, expected[chg.ID()]) 317 c.Check(st.Change(chg.ID()), Equals, chg) 318 } 319 320 c.Check(st.Change("no-such-id"), IsNil) 321 } 322 323 func (ss *stateSuite) TestNewChangeAndCheckpoint(c *C) { 324 b := new(fakeStateBackend) 325 st := state.New(b) 326 st.Lock() 327 328 chg := st.NewChange("install", "summary") 329 c.Assert(chg, NotNil) 330 chgID := chg.ID() 331 chg.Set("a", 1) 332 chg.SetStatus(state.ErrorStatus) 333 334 spawnTime := chg.SpawnTime() 335 readyTime := chg.ReadyTime() 336 337 // implicit checkpoint 338 st.Unlock() 339 340 c.Assert(b.checkpoints, HasLen, 1) 341 342 buf := bytes.NewBuffer(b.checkpoints[0]) 343 344 st2, err := state.ReadState(nil, buf) 345 c.Assert(err, IsNil) 346 c.Assert(st2, NotNil) 347 348 st2.Lock() 349 defer st2.Unlock() 350 351 chgs := st2.Changes() 352 353 c.Assert(chgs, HasLen, 1) 354 355 chg0 := chgs[0] 356 c.Check(chg0.ID(), Equals, chgID) 357 c.Check(chg0.Kind(), Equals, "install") 358 c.Check(chg0.Summary(), Equals, "summary") 359 c.Check(chg0.SpawnTime().Equal(spawnTime), Equals, true) 360 c.Check(chg0.ReadyTime().Equal(readyTime), Equals, true) 361 362 var v int 363 err = chg0.Get("a", &v) 364 c.Check(err, IsNil) 365 c.Check(v, Equals, 1) 366 367 c.Check(chg0.Status(), Equals, state.ErrorStatus) 368 369 select { 370 case <-chg0.Ready(): 371 default: 372 c.Errorf("Change didn't preserve Ready channel closed after deserialization") 373 } 374 } 375 376 func (ss *stateSuite) TestNewChangeAndCheckpointTaskDerivedStatus(c *C) { 377 b := new(fakeStateBackend) 378 st := state.New(b) 379 st.Lock() 380 381 chg := st.NewChange("install", "summary") 382 c.Assert(chg, NotNil) 383 chgID := chg.ID() 384 385 t1 := st.NewTask("download", "1...") 386 t1.SetStatus(state.DoneStatus) 387 chg.AddTask(t1) 388 389 // implicit checkpoint 390 st.Unlock() 391 392 c.Assert(b.checkpoints, HasLen, 1) 393 buf := bytes.NewBuffer(b.checkpoints[0]) 394 395 st2, err := state.ReadState(nil, buf) 396 c.Assert(err, IsNil) 397 398 st2.Lock() 399 defer st2.Unlock() 400 401 chgs := st2.Changes() 402 403 c.Assert(chgs, HasLen, 1) 404 405 chg0 := chgs[0] 406 c.Check(chg0.ID(), Equals, chgID) 407 c.Check(chg0.Status(), Equals, state.DoneStatus) 408 409 select { 410 case <-chg0.Ready(): 411 default: 412 c.Errorf("Change didn't preserve Ready channel closed after deserialization") 413 } 414 } 415 416 func (ss *stateSuite) TestNewTaskAndCheckpoint(c *C) { 417 b := new(fakeStateBackend) 418 st := state.New(b) 419 st.Lock() 420 421 chg := st.NewChange("install", "summary") 422 c.Assert(chg, NotNil) 423 424 t1 := st.NewTask("download", "1...") 425 chg.AddTask(t1) 426 t1ID := t1.ID() 427 t1.Set("a", 1) 428 t1.SetStatus(state.DoneStatus) 429 t1.SetProgress("snap", 5, 10) 430 t1.JoinLane(42) 431 t1.JoinLane(43) 432 433 t2 := st.NewTask("inst", "2...") 434 chg.AddTask(t2) 435 t2ID := t2.ID() 436 t2.WaitFor(t1) 437 schedule := time.Now().Add(time.Hour) 438 t2.At(schedule) 439 440 // implicit checkpoint 441 st.Unlock() 442 443 c.Assert(b.checkpoints, HasLen, 1) 444 445 buf := bytes.NewBuffer(b.checkpoints[0]) 446 447 st2, err := state.ReadState(nil, buf) 448 c.Assert(err, IsNil) 449 c.Assert(st2, NotNil) 450 451 st2.Lock() 452 defer st2.Unlock() 453 454 chgs := st2.Changes() 455 c.Assert(chgs, HasLen, 1) 456 chg0 := chgs[0] 457 458 tasks0 := make(map[string]*state.Task) 459 for _, t := range chg0.Tasks() { 460 tasks0[t.ID()] = t 461 } 462 c.Assert(tasks0, HasLen, 2) 463 464 task0_1 := tasks0[t1ID] 465 c.Check(task0_1.ID(), Equals, t1ID) 466 c.Check(task0_1.Kind(), Equals, "download") 467 c.Check(task0_1.Summary(), Equals, "1...") 468 c.Check(task0_1.Change(), Equals, chg0) 469 470 var v int 471 err = task0_1.Get("a", &v) 472 c.Check(err, IsNil) 473 c.Check(v, Equals, 1) 474 475 c.Check(task0_1.Status(), Equals, state.DoneStatus) 476 477 _, cur, tot := task0_1.Progress() 478 c.Check(cur, Equals, 5) 479 c.Check(tot, Equals, 10) 480 481 c.Assert(task0_1.Lanes(), DeepEquals, []int{42, 43}) 482 483 task0_2 := tasks0[t2ID] 484 c.Check(task0_2.WaitTasks(), DeepEquals, []*state.Task{task0_1}) 485 486 c.Check(task0_1.HaltTasks(), DeepEquals, []*state.Task{task0_2}) 487 488 tasks2 := make(map[string]*state.Task) 489 for _, t := range st2.Tasks() { 490 tasks2[t.ID()] = t 491 } 492 c.Assert(tasks2, HasLen, 2) 493 494 c.Check(task0_1.AtTime().IsZero(), Equals, true) 495 c.Check(task0_2.AtTime().Equal(schedule), Equals, true) 496 } 497 498 func (ss *stateSuite) TestEmptyStateDataAndCheckpointReadAndSet(c *C) { 499 b := new(fakeStateBackend) 500 st := state.New(b) 501 st.Lock() 502 503 chg := st.NewChange("install", "summary") 504 c.Assert(chg, NotNil) 505 506 // implicit checkpoint 507 st.Unlock() 508 509 c.Assert(b.checkpoints, HasLen, 1) 510 511 buf := bytes.NewBuffer(b.checkpoints[0]) 512 513 st2, err := state.ReadState(nil, buf) 514 c.Assert(err, IsNil) 515 c.Assert(st2, NotNil) 516 517 st2.Lock() 518 defer st2.Unlock() 519 520 // no crash 521 st2.Set("a", 1) 522 } 523 524 func (ss *stateSuite) TestEmptyTaskAndChangeDataAndCheckpointReadAndSet(c *C) { 525 b := new(fakeStateBackend) 526 st := state.New(b) 527 st.Lock() 528 529 t1 := st.NewTask("1...", "...") 530 t1ID := t1.ID() 531 chg := st.NewChange("chg", "...") 532 chgID := chg.ID() 533 chg.AddTask(t1) 534 535 // implicit checkpoint 536 st.Unlock() 537 538 c.Assert(b.checkpoints, HasLen, 1) 539 540 buf := bytes.NewBuffer(b.checkpoints[0]) 541 542 st2, err := state.ReadState(nil, buf) 543 c.Assert(err, IsNil) 544 c.Assert(st2, NotNil) 545 546 st2.Lock() 547 defer st2.Unlock() 548 549 chg2 := st2.Change(chgID) 550 t1_2 := st2.Task(t1ID) 551 c.Assert(t1_2, NotNil) 552 553 // no crash 554 chg2.Set("c", 1) 555 // no crash either 556 t1_2.Set("t", 1) 557 } 558 559 func (ss *stateSuite) TestEnsureBefore(c *C) { 560 b := new(fakeStateBackend) 561 st := state.New(b) 562 563 st.EnsureBefore(10 * time.Second) 564 565 c.Check(b.ensureBefore, Equals, 10*time.Second) 566 } 567 568 func (ss *stateSuite) TestCheckpointPreserveLastIds(c *C) { 569 b := new(fakeStateBackend) 570 st := state.New(b) 571 st.Lock() 572 573 st.NewChange("install", "...") 574 st.NewTask("download", "...") 575 st.NewTask("download", "...") 576 577 c.Assert(st.NewLane(), Equals, 1) 578 579 // implicit checkpoint 580 st.Unlock() 581 582 c.Assert(b.checkpoints, HasLen, 1) 583 584 buf := bytes.NewBuffer(b.checkpoints[0]) 585 586 st2, err := state.ReadState(nil, buf) 587 c.Assert(err, IsNil) 588 589 st2.Lock() 590 defer st2.Unlock() 591 592 c.Assert(st2.NewTask("download", "...").ID(), Equals, "3") 593 c.Assert(st2.NewChange("install", "...").ID(), Equals, "2") 594 595 c.Assert(st2.NewLane(), Equals, 2) 596 597 } 598 599 func (ss *stateSuite) TestCheckpointPreserveCleanStatus(c *C) { 600 b := new(fakeStateBackend) 601 st := state.New(b) 602 st.Lock() 603 604 chg := st.NewChange("install", "...") 605 t := st.NewTask("download", "...") 606 chg.AddTask(t) 607 t.SetStatus(state.DoneStatus) 608 t.SetClean() 609 610 // implicit checkpoint 611 st.Unlock() 612 613 c.Assert(b.checkpoints, HasLen, 1) 614 615 buf := bytes.NewBuffer(b.checkpoints[0]) 616 617 st2, err := state.ReadState(nil, buf) 618 c.Assert(err, IsNil) 619 620 st2.Lock() 621 defer st2.Unlock() 622 623 chg2 := st2.Change(chg.ID()) 624 t2 := st2.Task(t.ID()) 625 626 c.Assert(chg2.IsClean(), Equals, true) 627 c.Assert(t2.IsClean(), Equals, true) 628 } 629 630 func (ss *stateSuite) TestNewTaskAndTasks(c *C) { 631 st := state.New(nil) 632 st.Lock() 633 defer st.Unlock() 634 635 chg1 := st.NewChange("install", "...") 636 t11 := st.NewTask("check", "...") 637 chg1.AddTask(t11) 638 t12 := st.NewTask("inst", "...") 639 chg1.AddTask(t12) 640 641 chg2 := st.NewChange("remove", "...") 642 t21 := st.NewTask("check", "...") 643 t22 := st.NewTask("rm", "...") 644 chg2.AddTask(t21) 645 chg2.AddTask(t22) 646 647 tasks := st.Tasks() 648 c.Check(tasks, HasLen, 4) 649 650 expected := map[string]*state.Task{ 651 t11.ID(): t11, 652 t12.ID(): t12, 653 t21.ID(): t21, 654 t22.ID(): t22, 655 } 656 657 for _, t := range tasks { 658 c.Check(t, Equals, expected[t.ID()]) 659 } 660 } 661 662 func (ss *stateSuite) TestTaskNoTask(c *C) { 663 st := state.New(nil) 664 st.Lock() 665 defer st.Unlock() 666 667 c.Check(st.Task("1"), IsNil) 668 } 669 670 func (ss *stateSuite) TestNewTaskHiddenUntilLinked(c *C) { 671 st := state.New(nil) 672 st.Lock() 673 defer st.Unlock() 674 675 t1 := st.NewTask("check", "...") 676 677 tasks := st.Tasks() 678 c.Check(tasks, HasLen, 0) 679 680 c.Check(st.Task(t1.ID()), IsNil) 681 } 682 683 func (ss *stateSuite) TestMethodEntrance(c *C) { 684 st := state.New(&fakeStateBackend{}) 685 686 // Reset modified flag. 687 st.Lock() 688 st.Unlock() 689 690 writes := []func(){ 691 func() { st.Set("foo", 1) }, 692 func() { st.NewChange("install", "...") }, 693 func() { st.NewTask("download", "...") }, 694 func() { st.UnmarshalJSON(nil) }, 695 func() { st.NewLane() }, 696 func() { st.Warnf("hello") }, 697 func() { st.OkayWarnings(time.Time{}) }, 698 func() { st.UnshowAllWarnings() }, 699 } 700 701 reads := []func(){ 702 func() { st.Get("foo", nil) }, 703 func() { st.Cached("foo") }, 704 func() { st.Cache("foo", 1) }, 705 func() { st.Changes() }, 706 func() { st.Change("foo") }, 707 func() { st.Tasks() }, 708 func() { st.Task("foo") }, 709 func() { st.MarshalJSON() }, 710 func() { st.Prune(time.Now(), time.Hour, time.Hour, 100) }, 711 func() { st.TaskCount() }, 712 func() { st.AllWarnings() }, 713 func() { st.PendingWarnings() }, 714 func() { st.WarningsSummary() }, 715 } 716 717 for i, f := range reads { 718 c.Logf("Testing read function #%d", i) 719 c.Assert(f, PanicMatches, "internal error: accessing state without lock") 720 c.Assert(st.Modified(), Equals, false) 721 } 722 723 for i, f := range writes { 724 st.Lock() 725 st.Unlock() 726 c.Assert(st.Modified(), Equals, false) 727 728 c.Logf("Testing write function #%d", i) 729 c.Assert(f, PanicMatches, "internal error: accessing state without lock") 730 c.Assert(st.Modified(), Equals, true) 731 } 732 } 733 734 func (ss *stateSuite) TestPrune(c *C) { 735 st := state.New(&fakeStateBackend{}) 736 st.Lock() 737 defer st.Unlock() 738 739 now := time.Now() 740 pruneWait := 1 * time.Hour 741 abortWait := 3 * time.Hour 742 743 unset := time.Time{} 744 745 t1 := st.NewTask("foo", "...") 746 t2 := st.NewTask("foo", "...") 747 t3 := st.NewTask("foo", "...") 748 t4 := st.NewTask("foo", "...") 749 750 chg1 := st.NewChange("abort", "...") 751 chg1.AddTask(t1) 752 state.MockChangeTimes(chg1, now.Add(-abortWait), unset) 753 754 chg2 := st.NewChange("prune", "...") 755 chg2.AddTask(t2) 756 c.Assert(chg2.Status(), Equals, state.DoStatus) 757 state.MockChangeTimes(chg2, now.Add(-pruneWait), now.Add(-pruneWait)) 758 759 chg3 := st.NewChange("ready-but-recent", "...") 760 chg3.AddTask(t3) 761 state.MockChangeTimes(chg3, now.Add(-pruneWait), now.Add(-pruneWait/2)) 762 763 chg4 := st.NewChange("old-but-not-ready", "...") 764 chg4.AddTask(t4) 765 state.MockChangeTimes(chg4, now.Add(-pruneWait/2), unset) 766 767 // unlinked task 768 t5 := st.NewTask("unliked", "...") 769 c.Check(st.Task(t5.ID()), IsNil) 770 state.MockTaskTimes(t5, now.Add(-pruneWait), now.Add(-pruneWait)) 771 772 // two warnings, one expired 773 st.AddWarning("hello", now, never, time.Nanosecond, state.DefaultRepeatAfter) 774 st.Warnf("hello again") 775 776 past := time.Now().AddDate(-1, 0, 0) 777 st.Prune(past, pruneWait, abortWait, 100) 778 779 c.Assert(st.Change(chg1.ID()), Equals, chg1) 780 c.Assert(st.Change(chg2.ID()), IsNil) 781 c.Assert(st.Change(chg3.ID()), Equals, chg3) 782 c.Assert(st.Change(chg4.ID()), Equals, chg4) 783 784 c.Assert(st.Task(t1.ID()), Equals, t1) 785 c.Assert(st.Task(t2.ID()), IsNil) 786 c.Assert(st.Task(t3.ID()), Equals, t3) 787 c.Assert(st.Task(t4.ID()), Equals, t4) 788 789 c.Assert(chg1.Status(), Equals, state.HoldStatus) 790 c.Assert(chg3.Status(), Equals, state.DoStatus) 791 c.Assert(chg4.Status(), Equals, state.DoStatus) 792 793 c.Assert(t1.Status(), Equals, state.HoldStatus) 794 c.Assert(t3.Status(), Equals, state.DoStatus) 795 c.Assert(t4.Status(), Equals, state.DoStatus) 796 797 c.Check(st.TaskCount(), Equals, 3) 798 799 c.Check(st.AllWarnings(), HasLen, 1) 800 } 801 802 func (ss *stateSuite) TestPruneEmptyChange(c *C) { 803 // Empty changes are a bit special because they start out on Hold 804 // which is a Ready status, but the change itself is not considered Ready 805 // explicitly because that's how every change that will have tasks added 806 // to it starts their life. 807 st := state.New(&fakeStateBackend{}) 808 st.Lock() 809 defer st.Unlock() 810 811 now := time.Now() 812 pruneWait := 1 * time.Hour 813 abortWait := 3 * time.Hour 814 815 chg := st.NewChange("abort", "...") 816 state.MockChangeTimes(chg, now.Add(-pruneWait), time.Time{}) 817 818 past := time.Now().AddDate(-1, 0, 0) 819 st.Prune(past, pruneWait, abortWait, 100) 820 c.Assert(st.Change(chg.ID()), IsNil) 821 } 822 823 func (ss *stateSuite) TestPruneMaxChangesHappy(c *C) { 824 st := state.New(&fakeStateBackend{}) 825 st.Lock() 826 defer st.Unlock() 827 828 now := time.Now() 829 pruneWait := 1 * time.Hour 830 abortWait := 3 * time.Hour 831 832 // create 10 changes, chg0 is freshest, chg9 is oldest, but 833 // all changes are not old enough for pruneWait 834 for i := 0; i < 10; i++ { 835 chg := st.NewChange(fmt.Sprintf("chg%d", i), "...") 836 t := st.NewTask("foo", "...") 837 chg.AddTask(t) 838 t.SetStatus(state.DoneStatus) 839 840 when := time.Duration(i) * time.Second 841 state.MockChangeTimes(chg, now.Add(-when), now.Add(-when)) 842 } 843 c.Assert(st.Changes(), HasLen, 10) 844 845 // and 5 more, all not ready 846 for i := 10; i < 15; i++ { 847 chg := st.NewChange(fmt.Sprintf("chg%d", i), "...") 848 t := st.NewTask("foo", "...") 849 chg.AddTask(t) 850 } 851 852 // test that nothing is done when we are within pruneWait and 853 // maxReadyChanges 854 past := time.Now().AddDate(-1, 0, 0) 855 maxReadyChanges := 100 856 st.Prune(past, pruneWait, abortWait, maxReadyChanges) 857 c.Assert(st.Changes(), HasLen, 15) 858 859 // but with maxReadyChanges we remove the ready ones 860 maxReadyChanges = 5 861 st.Prune(past, pruneWait, abortWait, maxReadyChanges) 862 c.Assert(st.Changes(), HasLen, 10) 863 remaining := map[string]bool{} 864 for _, chg := range st.Changes() { 865 remaining[chg.Kind()] = true 866 } 867 c.Check(remaining, DeepEquals, map[string]bool{ 868 // ready and fresh 869 "chg0": true, 870 "chg1": true, 871 "chg2": true, 872 "chg3": true, 873 "chg4": true, 874 // not ready 875 "chg10": true, 876 "chg11": true, 877 "chg12": true, 878 "chg13": true, 879 "chg14": true, 880 }) 881 } 882 883 func (ss *stateSuite) TestPruneMaxChangesSomeNotReady(c *C) { 884 st := state.New(&fakeStateBackend{}) 885 st.Lock() 886 defer st.Unlock() 887 888 // 10 changes, none ready 889 for i := 0; i < 10; i++ { 890 chg := st.NewChange(fmt.Sprintf("chg%d", i), "...") 891 t := st.NewTask("foo", "...") 892 chg.AddTask(t) 893 } 894 c.Assert(st.Changes(), HasLen, 10) 895 896 // nothing can be pruned 897 past := time.Now().AddDate(-1, 0, 0) 898 maxChanges := 5 899 st.Prune(past, 1*time.Hour, 3*time.Hour, maxChanges) 900 c.Assert(st.Changes(), HasLen, 10) 901 } 902 903 func (ss *stateSuite) TestPruneMaxChangesHonored(c *C) { 904 st := state.New(&fakeStateBackend{}) 905 st.Lock() 906 defer st.Unlock() 907 908 // 10 changes, none ready 909 for i := 0; i < 10; i++ { 910 chg := st.NewChange(fmt.Sprintf("chg%d", i), "not-ready") 911 t := st.NewTask("foo", "not-readly") 912 chg.AddTask(t) 913 } 914 c.Assert(st.Changes(), HasLen, 10) 915 916 // one extra change that just now entered ready state 917 chg := st.NewChange("chg99", "so-ready") 918 t := st.NewTask("foo", "so-ready") 919 when := 1 * time.Second 920 state.MockChangeTimes(chg, time.Now().Add(-when), time.Now().Add(-when)) 921 t.SetStatus(state.DoneStatus) 922 chg.AddTask(t) 923 924 // we have 11 changes in total, 10 not-ready, 1 ready 925 // 926 // this test we do not purge the freshly ready change 927 maxChanges := 10 928 past := time.Now().AddDate(-1, 0, 0) 929 st.Prune(past, 1*time.Hour, 3*time.Hour, maxChanges) 930 c.Assert(st.Changes(), HasLen, 11) 931 } 932 933 func (ss *stateSuite) TestPruneHonorsStartOperationTime(c *C) { 934 st := state.New(&fakeStateBackend{}) 935 st.Lock() 936 defer st.Unlock() 937 938 now := time.Now() 939 940 startTime := 2 * time.Hour 941 spawnTime := 10 * time.Hour 942 pruneWait := 1 * time.Hour 943 abortWait := 3 * time.Hour 944 945 chg := st.NewChange("change", "...") 946 t := st.NewTask("foo", "") 947 chg.AddTask(t) 948 // change spawned 10h ago 949 state.MockChangeTimes(chg, now.Add(-spawnTime), time.Time{}) 950 951 // start operation time is 2h ago, change is not aborted because 952 // it's less than abortWait limit. 953 opTime := now.Add(-startTime) 954 st.Prune(opTime, pruneWait, abortWait, 100) 955 c.Assert(st.Changes(), HasLen, 1) 956 c.Check(chg.Status(), Equals, state.DoStatus) 957 958 // start operation time is 9h ago, change is aborted. 959 startTime = 9 * time.Hour 960 opTime = time.Now().Add(-startTime) 961 st.Prune(opTime, pruneWait, abortWait, 100) 962 c.Assert(st.Changes(), HasLen, 1) 963 c.Check(chg.Status(), Equals, state.HoldStatus) 964 } 965 966 func (ss *stateSuite) TestRequestRestart(c *C) { 967 b := new(fakeStateBackend) 968 st := state.New(b) 969 970 ok, t := st.Restarting() 971 c.Check(ok, Equals, false) 972 c.Check(t, Equals, state.RestartUnset) 973 974 st.RequestRestart(state.RestartDaemon) 975 976 c.Check(b.restartRequested, Equals, true) 977 978 ok, t = st.Restarting() 979 c.Check(ok, Equals, true) 980 c.Check(t, Equals, state.RestartDaemon) 981 } 982 983 func (ss *stateSuite) TestRequestRestartSystemAndVerifyReboot(c *C) { 984 b := new(fakeStateBackend) 985 st := state.New(b) 986 987 st.Lock() 988 err := st.VerifyReboot("boot-id-1") 989 st.Unlock() 990 c.Assert(err, IsNil) 991 992 ok, t := st.Restarting() 993 c.Check(ok, Equals, false) 994 c.Check(t, Equals, state.RestartUnset) 995 996 st.Lock() 997 st.RequestRestart(state.RestartSystem) 998 st.Unlock() 999 1000 c.Check(b.restartRequested, Equals, true) 1001 1002 ok, t = st.Restarting() 1003 c.Check(ok, Equals, true) 1004 c.Check(t, Equals, state.RestartSystem) 1005 1006 var fromBootID string 1007 st.Lock() 1008 c.Check(st.Get("system-restart-from-boot-id", &fromBootID), IsNil) 1009 st.Unlock() 1010 c.Check(fromBootID, Equals, "boot-id-1") 1011 1012 st.Lock() 1013 err = st.VerifyReboot("boot-id-1") 1014 st.Unlock() 1015 c.Check(err, Equals, state.ErrExpectedReboot) 1016 1017 st.Lock() 1018 err = st.VerifyReboot("boot-id-2") 1019 st.Unlock() 1020 c.Assert(err, IsNil) 1021 st.Lock() 1022 c.Check(st.Get("system-restart-from-boot-id", &fromBootID), Equals, state.ErrNoState) 1023 st.Unlock() 1024 } 1025 1026 func (ss *stateSuite) TestReadStateInitsCache(c *C) { 1027 st, err := state.ReadState(nil, bytes.NewBufferString("{}")) 1028 c.Assert(err, IsNil) 1029 st.Lock() 1030 defer st.Unlock() 1031 1032 st.Cache("key", "value") 1033 c.Assert(st.Cached("key"), Equals, "value") 1034 } 1035 1036 func (ss *stateSuite) TestTimingsSupport(c *C) { 1037 st := state.New(nil) 1038 st.Lock() 1039 defer st.Unlock() 1040 1041 var tims []int 1042 1043 err := st.GetMaybeTimings(&tims) 1044 c.Assert(err, IsNil) 1045 c.Check(tims, IsNil) 1046 1047 st.SaveTimings([]int{1, 2, 3}) 1048 1049 err = st.GetMaybeTimings(&tims) 1050 c.Assert(err, IsNil) 1051 c.Check(tims, DeepEquals, []int{1, 2, 3}) 1052 }