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  }