github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/pebblenotices_test.go (about) 1 // Copyright 2023 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/canonical/pebble/client" 11 "github.com/juju/clock/testclock" 12 "github.com/juju/loggo" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/worker/v3" 15 "github.com/juju/worker/v3/workertest" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/testing" 19 "github.com/juju/juju/worker/uniter" 20 "github.com/juju/juju/worker/uniter/container" 21 ) 22 23 type pebbleNoticerSuite struct { 24 clock *testclock.Clock 25 worker worker.Worker 26 clients map[string]*fakePebbleClient 27 workloadEventChan chan string 28 workloadEvents container.WorkloadEvents 29 } 30 31 var _ = gc.Suite(&pebbleNoticerSuite{}) 32 33 func (s *pebbleNoticerSuite) setUpWorker(c *gc.C, containerNames []string) { 34 s.clock = testclock.NewClock(time.Time{}) 35 s.workloadEventChan = make(chan string) 36 s.workloadEvents = container.NewWorkloadEvents() 37 s.clients = make(map[string]*fakePebbleClient) 38 for _, name := range containerNames { 39 s.clients[name] = &fakePebbleClient{ 40 clock: testclock.NewClock(time.Time{}), 41 noticeAdded: make(chan *client.Notice, 1), // buffered so AddNotice doesn't block 42 } 43 } 44 newClient := func(cfg *client.Config) (uniter.PebbleClient, error) { 45 c.Assert(cfg.Socket, gc.Matches, pebbleSocketPathRegexpString) 46 matches := pebbleSocketPathRegexp.FindAllStringSubmatch(cfg.Socket, 1) 47 return s.clients[matches[0][1]], nil 48 } 49 s.worker = uniter.NewPebbleNoticer(loggo.GetLogger("test"), s.clock, containerNames, s.workloadEventChan, s.workloadEvents, newClient) 50 } 51 52 func (s *pebbleNoticerSuite) waitWorkloadEvent(c *gc.C, expected container.WorkloadEvent) { 53 select { 54 case id := <-s.workloadEventChan: 55 event, callback, err := s.workloadEvents.GetWorkloadEvent(id) 56 c.Assert(err, jc.ErrorIsNil) 57 c.Assert(event, gc.DeepEquals, expected) 58 callback(nil) 59 case <-time.After(testing.LongWait): 60 c.Fatalf("timed out waiting for event") 61 } 62 } 63 64 func (s *pebbleNoticerSuite) TestWaitNotices(c *gc.C) { 65 s.setUpWorker(c, []string{"c1"}) 66 defer workertest.CleanKill(c, s.worker) 67 68 // Simulate a WaitNotices timeout. 69 s.clients["c1"].clock.WaitAdvance(30*time.Second, testing.ShortWait, 1) 70 71 // The first notice will always be handled. 72 lastRepeated := time.Now() 73 s.clients["c1"].AddNotice(c, &client.Notice{ 74 ID: "1", 75 Type: "custom", 76 Key: "a.b/c", 77 LastRepeated: lastRepeated, 78 }) 79 s.waitWorkloadEvent(c, container.WorkloadEvent{ 80 Type: container.CustomNoticeEvent, 81 WorkloadName: "c1", 82 NoticeID: "1", 83 NoticeType: "custom", 84 NoticeKey: "a.b/c", 85 }) 86 87 // Another notice with an earlier LastRepeated time will be skipped. 88 s.clients["c1"].AddNotice(c, &client.Notice{ 89 ID: "2", 90 Type: "custom", 91 Key: "a.b/d", 92 LastRepeated: lastRepeated.Add(-time.Second), 93 }) 94 select { 95 case <-s.workloadEventChan: 96 c.Fatalf("shouldn't see this notice") 97 case <-time.After(testing.ShortWait): 98 } 99 100 // A new notice with a later LastRepeated time will be handled. 101 s.clients["c1"].AddNotice(c, &client.Notice{ 102 ID: "3", 103 Type: "custom", 104 Key: "a.b/e", 105 LastRepeated: lastRepeated.Add(time.Second), 106 }) 107 s.waitWorkloadEvent(c, container.WorkloadEvent{ 108 Type: container.CustomNoticeEvent, 109 WorkloadName: "c1", 110 NoticeID: "3", 111 NoticeType: "custom", 112 NoticeKey: "a.b/e", 113 }) 114 } 115 116 func (s *pebbleNoticerSuite) TestWaitNoticesError(c *gc.C) { 117 s.setUpWorker(c, []string{"c1"}) 118 defer workertest.CleanKill(c, s.worker) 119 120 s.clients["c1"].AddNotice(c, &client.Notice{ 121 ID: "1", 122 Type: "error", 123 Key: "WaitNotices error!", 124 }) 125 s.clock.WaitAdvance(testing.LongWait, time.Second, 1) 126 127 s.clients["c1"].AddNotice(c, &client.Notice{ 128 ID: "2", 129 Type: "custom", 130 Key: "a.b/c", 131 }) 132 s.waitWorkloadEvent(c, container.WorkloadEvent{ 133 Type: container.CustomNoticeEvent, 134 WorkloadName: "c1", 135 NoticeID: "2", 136 NoticeType: "custom", 137 NoticeKey: "a.b/c", 138 }) 139 } 140 141 func (s *pebbleNoticerSuite) TestIgnoreUnhandledType(c *gc.C) { 142 s.setUpWorker(c, []string{"c1"}) 143 defer workertest.CleanKill(c, s.worker) 144 145 s.clients["c1"].AddNotice(c, &client.Notice{ 146 ID: "1", 147 Type: "unhandled", 148 Key: "some-key", 149 }) 150 151 select { 152 case <-s.workloadEventChan: 153 c.Fatalf("should ignore this notice") 154 case <-time.After(testing.ShortWait): 155 } 156 } 157 158 func (s *pebbleNoticerSuite) TestMultipleContainers(c *gc.C) { 159 s.setUpWorker(c, []string{"c1", "c2"}) 160 defer workertest.CleanKill(c, s.worker) 161 162 for i := 1; i <= 2; i++ { 163 name := fmt.Sprintf("c%d", i) 164 s.clients[name].AddNotice(c, &client.Notice{ 165 ID: "1", 166 Type: "custom", 167 Key: "example.com/" + name, 168 }) 169 s.waitWorkloadEvent(c, container.WorkloadEvent{ 170 Type: container.CustomNoticeEvent, 171 WorkloadName: name, 172 NoticeID: "1", 173 NoticeType: "custom", 174 NoticeKey: "example.com/" + name, 175 }) 176 } 177 }