github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/externalcontrollerupdater/externalcontrollerupdater_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package externalcontrollerupdater_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/worker/v3/workertest"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/api"
    18  	"github.com/juju/juju/api/controller/crosscontroller"
    19  	"github.com/juju/juju/core/crossmodel"
    20  	coretesting "github.com/juju/juju/testing"
    21  	"github.com/juju/juju/worker/externalcontrollerupdater"
    22  )
    23  
    24  var _ = gc.Suite(&ExternalControllerUpdaterSuite{})
    25  
    26  type ExternalControllerUpdaterSuite struct {
    27  	coretesting.BaseSuite
    28  
    29  	updater mockExternalControllerUpdaterClient
    30  	watcher mockExternalControllerWatcherClient
    31  
    32  	clock *testclock.Clock
    33  
    34  	stub       testing.Stub
    35  	newWatcher externalcontrollerupdater.NewExternalControllerWatcherClientFunc
    36  }
    37  
    38  func (s *ExternalControllerUpdaterSuite) SetUpTest(c *gc.C) {
    39  	s.BaseSuite.SetUpTest(c)
    40  
    41  	s.updater = mockExternalControllerUpdaterClient{
    42  		watcher: newMockStringsWatcher(),
    43  		info: crossmodel.ControllerInfo{
    44  			ControllerTag: coretesting.ControllerTag,
    45  			Alias:         "foo",
    46  			Addrs:         []string{"bar"},
    47  			CACert:        "baz",
    48  		},
    49  	}
    50  	s.AddCleanup(func(*gc.C) { s.updater.watcher.Stop() })
    51  
    52  	s.watcher = mockExternalControllerWatcherClient{
    53  		watcher: newMockNotifyWatcher(),
    54  		info: crosscontroller.ControllerInfo{
    55  			Addrs:  []string{"foo"},
    56  			CACert: "bar",
    57  		},
    58  	}
    59  	s.AddCleanup(func(*gc.C) { s.watcher.watcher.Stop() })
    60  
    61  	s.clock = testclock.NewClock(time.Time{})
    62  
    63  	s.stub.ResetCalls()
    64  	s.newWatcher = func(apiInfo *api.Info) (externalcontrollerupdater.ExternalControllerWatcherClientCloser, error) {
    65  		s.stub.AddCall("NextExternalControllerWatcherClient", apiInfo)
    66  		if err := s.stub.NextErr(); err != nil {
    67  			return nil, err
    68  		}
    69  		return &s.watcher, nil
    70  	}
    71  }
    72  
    73  func (s *ExternalControllerUpdaterSuite) TestStartStop(c *gc.C) {
    74  	w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	workertest.CleanKill(c, w)
    77  }
    78  
    79  func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersCalled(c *gc.C) {
    80  	s.updater.watcher.changes = make(chan []string)
    81  
    82  	w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	defer workertest.CleanKill(c, w)
    85  
    86  	select {
    87  	case s.updater.watcher.changes <- []string{}:
    88  	case <-time.After(coretesting.LongWait):
    89  		c.Fatal("timed out waiting to send changes")
    90  	}
    91  
    92  	workertest.CleanKill(c, w)
    93  	s.updater.Stub.CheckCallNames(c, "WatchExternalControllers")
    94  }
    95  
    96  func (s *ExternalControllerUpdaterSuite) assertWatchExternalControllersStart(c *gc.C) {
    97  	s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()}
    98  
    99  	// Cause three notifications. Only the first notification is
   100  	// accompanied by API address changes, so there should be only
   101  	// one API reconnection, and one local controller update.
   102  	for i := 0; i < 3; i++ {
   103  		select {
   104  		case s.watcher.watcher.changes <- struct{}{}:
   105  		case <-time.After(coretesting.LongWait):
   106  			c.Fatal("timed out waiting to send changes")
   107  		}
   108  	}
   109  
   110  	s.stub.CheckCalls(c, []testing.StubCall{{
   111  		"NextExternalControllerWatcherClient",
   112  		[]interface{}{&api.Info{
   113  			Addrs:  s.updater.info.Addrs,
   114  			CACert: s.updater.info.CACert,
   115  			Tag:    names.NewUserTag("jujuanonymous"),
   116  		}},
   117  	}, {
   118  		"NextExternalControllerWatcherClient",
   119  		[]interface{}{&api.Info{
   120  			Addrs:  s.watcher.info.Addrs,
   121  			CACert: s.updater.info.CACert, // only addresses are updated
   122  			Tag:    names.NewUserTag("jujuanonymous"),
   123  		}},
   124  	}})
   125  	s.updater.Stub.CheckCalls(c, []testing.StubCall{{
   126  		"WatchExternalControllers",
   127  		[]interface{}{},
   128  	}, {
   129  		"ExternalControllerInfo",
   130  		[]interface{}{coretesting.ControllerTag.Id()},
   131  	}, {
   132  		"SetExternalControllerInfo",
   133  		[]interface{}{crossmodel.ControllerInfo{
   134  			ControllerTag: s.updater.info.ControllerTag,
   135  			Alias:         s.updater.info.Alias,
   136  			Addrs:         s.watcher.info.Addrs, // new addrs
   137  			CACert:        s.updater.info.CACert,
   138  		}},
   139  	}})
   140  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   141  		if len(s.watcher.Stub.Calls()) < 6 {
   142  			continue
   143  		}
   144  		s.watcher.Stub.CheckCallNames(c,
   145  			"WatchControllerInfo",
   146  			"ControllerInfo",
   147  			"Close", // close watcher and restart when a change arrives
   148  			"WatchControllerInfo",
   149  			"ControllerInfo", // no change
   150  			"ControllerInfo", // no change
   151  		)
   152  		return
   153  	}
   154  	c.Fatal("time out waiting for worker api calls")
   155  
   156  	s.updater.Stub.CheckNoCalls(c)
   157  }
   158  
   159  func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllers(c *gc.C) {
   160  	w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	defer workertest.CleanKill(c, w)
   163  
   164  	s.assertWatchExternalControllersStart(c)
   165  }
   166  
   167  func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersStop(c *gc.C) {
   168  	w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	defer workertest.CleanKill(c, w)
   171  
   172  	s.assertWatchExternalControllersStart(c)
   173  
   174  	s.updater.Stub.ResetCalls()
   175  	s.watcher.Stub.ResetCalls()
   176  
   177  	s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()}
   178  
   179  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   180  		if len(s.watcher.Stub.Calls()) < 1 {
   181  			continue
   182  		}
   183  		s.watcher.Stub.CheckCallNames(c,
   184  			"Close",
   185  		)
   186  		return
   187  	}
   188  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   189  		if len(s.updater.Stub.Calls()) < 1 {
   190  			continue
   191  		}
   192  		s.updater.Stub.CheckCallNames(c,
   193  			"Close",
   194  		)
   195  		return
   196  	}
   197  
   198  	c.Fatal("time out waiting for worker api calls")
   199  }
   200  
   201  func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersErrorsContained(c *gc.C) {
   202  	// The first time we attempt to connect to the external controller,
   203  	// the dial should fail. The runner will reschedule the worker to
   204  	// try again.
   205  	s.stub.SetErrors(errors.New("no API connection for you"))
   206  
   207  	s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()}
   208  	s.watcher.watcher.changes = make(chan struct{})
   209  	s.watcher.info.Addrs = s.updater.info.Addrs // no change
   210  
   211  	w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	defer workertest.CleanKill(c, w)
   214  
   215  	// The first run of the controller worker should fail to
   216  	// connect to the API, and should abort. The runner should
   217  	// then be waiting for a minute to restart the controller
   218  	// worker.
   219  	s.clock.WaitAdvance(time.Second, coretesting.LongWait, 1)
   220  	s.clock.WaitAdvance(59*time.Second, coretesting.LongWait, 1)
   221  
   222  	// The controller worker should have been restarted now.
   223  	select {
   224  	case s.watcher.watcher.changes <- struct{}{}:
   225  	case <-time.After(coretesting.LongWait):
   226  		c.Fatal("timed out waiting to send changes")
   227  	}
   228  
   229  	workertest.CleanKill(c, w)
   230  	s.stub.CheckCalls(c, []testing.StubCall{{
   231  		"NextExternalControllerWatcherClient",
   232  		[]interface{}{&api.Info{
   233  			Addrs:  s.updater.info.Addrs,
   234  			CACert: s.updater.info.CACert,
   235  			Tag:    names.NewUserTag("jujuanonymous"),
   236  		}},
   237  	}, {
   238  		"NextExternalControllerWatcherClient",
   239  		[]interface{}{&api.Info{
   240  			Addrs:  s.updater.info.Addrs,
   241  			CACert: s.updater.info.CACert,
   242  			Tag:    names.NewUserTag("jujuanonymous"),
   243  		}},
   244  	}})
   245  	s.updater.Stub.CheckCallNames(c,
   246  		"WatchExternalControllers",
   247  		"ExternalControllerInfo",
   248  		"ExternalControllerInfo",
   249  	)
   250  	s.watcher.Stub.CheckCallNames(c,
   251  		"WatchControllerInfo",
   252  		"ControllerInfo",
   253  		"Close",
   254  	)
   255  }