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

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasmodelconfigmanager_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/names/v5"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/worker/v3"
    15  	"github.com/juju/worker/v3/workertest"
    16  	"go.uber.org/mock/gomock"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/controller"
    20  	"github.com/juju/juju/core/watcher"
    21  	"github.com/juju/juju/core/watcher/watchertest"
    22  	"github.com/juju/juju/docker"
    23  	"github.com/juju/juju/docker/registry"
    24  	registrymocks "github.com/juju/juju/docker/registry/mocks"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker/caasmodelconfigmanager"
    27  	"github.com/juju/juju/worker/caasmodelconfigmanager/mocks"
    28  )
    29  
    30  var _ = gc.Suite(&workerSuite{})
    31  
    32  type workerSuite struct {
    33  	testing.IsolationSuite
    34  
    35  	modelTag names.ModelTag
    36  	logger   loggo.Logger
    37  
    38  	facade           *mocks.MockFacade
    39  	broker           *mocks.MockCAASBroker
    40  	reg              *registrymocks.MockRegistry
    41  	clock            testclock.AdvanceableClock
    42  	controllerConfig controller.Config
    43  }
    44  
    45  func (s *workerSuite) SetUpTest(c *gc.C) {
    46  	s.IsolationSuite.SetUpTest(c)
    47  	s.modelTag = names.NewModelTag("ffffffff-ffff-ffff-ffff-ffffffffffff")
    48  	s.logger = loggo.GetLogger("test")
    49  	s.controllerConfig = coretesting.FakeControllerConfig()
    50  	s.clock = testclock.NewDilatedWallClock(testing.ShortWait)
    51  }
    52  
    53  func (s *workerSuite) TearDownTest(c *gc.C) {
    54  	s.IsolationSuite.TearDownTest(c)
    55  	s.facade = nil
    56  }
    57  
    58  func (s *workerSuite) TestConfigValidate(c *gc.C) {
    59  	ctrl := gomock.NewController(c)
    60  	defer ctrl.Finish()
    61  
    62  	cfg := caasmodelconfigmanager.Config{}
    63  	c.Check(cfg.Validate(), gc.ErrorMatches, `ModelTag is missing not valid`)
    64  
    65  	cfg = caasmodelconfigmanager.Config{
    66  		ModelTag: s.modelTag,
    67  	}
    68  	c.Check(cfg.Validate(), gc.ErrorMatches, `Facade is missing not valid`)
    69  
    70  	cfg = caasmodelconfigmanager.Config{
    71  		ModelTag: s.modelTag,
    72  		Facade:   mocks.NewMockFacade(ctrl),
    73  	}
    74  	c.Check(cfg.Validate(), gc.ErrorMatches, `Broker is missing not valid`)
    75  
    76  	cfg = caasmodelconfigmanager.Config{
    77  		ModelTag: s.modelTag,
    78  		Facade:   mocks.NewMockFacade(ctrl),
    79  		Broker:   mocks.NewMockCAASBroker(ctrl),
    80  	}
    81  	c.Check(cfg.Validate(), gc.ErrorMatches, `Logger is missing not valid`)
    82  
    83  	cfg = caasmodelconfigmanager.Config{
    84  		ModelTag: s.modelTag,
    85  		Facade:   mocks.NewMockFacade(ctrl),
    86  		Broker:   mocks.NewMockCAASBroker(ctrl),
    87  		Logger:   s.logger,
    88  	}
    89  	c.Check(cfg.Validate(), gc.ErrorMatches, `Clock is missing not valid`)
    90  
    91  	cfg = caasmodelconfigmanager.Config{
    92  		ModelTag: s.modelTag,
    93  		Facade:   mocks.NewMockFacade(ctrl),
    94  		Broker:   mocks.NewMockCAASBroker(ctrl),
    95  		Logger:   s.logger,
    96  		Clock:    s.clock,
    97  	}
    98  	c.Check(cfg.Validate(), gc.ErrorMatches, `RegistryFunc is missing not valid`)
    99  
   100  	cfg = caasmodelconfigmanager.Config{
   101  		ModelTag:     s.modelTag,
   102  		Facade:       mocks.NewMockFacade(ctrl),
   103  		Broker:       mocks.NewMockCAASBroker(ctrl),
   104  		Logger:       s.logger,
   105  		Clock:        s.clock,
   106  		RegistryFunc: func(i docker.ImageRepoDetails) (registry.Registry, error) { return nil, nil },
   107  	}
   108  	c.Check(cfg.Validate(), jc.ErrorIsNil)
   109  }
   110  
   111  func (s *workerSuite) getWorkerStarter(c *gc.C) (func(...any) worker.Worker, *gomock.Controller) {
   112  	ctrl := gomock.NewController(c)
   113  
   114  	s.facade = mocks.NewMockFacade(ctrl)
   115  	s.broker = mocks.NewMockCAASBroker(ctrl)
   116  	s.reg = registrymocks.NewMockRegistry(ctrl)
   117  
   118  	cfg := caasmodelconfigmanager.Config{
   119  		ModelTag: s.modelTag,
   120  		Logger:   s.logger,
   121  		Facade:   s.facade,
   122  		Broker:   s.broker,
   123  		Clock:    s.clock,
   124  		RegistryFunc: func(i docker.ImageRepoDetails) (registry.Registry, error) {
   125  			c.Check(i, gc.DeepEquals, s.CAASImageRepo(c))
   126  			return s.reg, nil
   127  		},
   128  	}
   129  	return func(calls ...any) worker.Worker {
   130  		gomock.InOrder(calls...)
   131  		w, err := caasmodelconfigmanager.NewWorker(cfg)
   132  		c.Assert(err, jc.ErrorIsNil)
   133  		return w
   134  	}, ctrl
   135  }
   136  
   137  func (s *workerSuite) TestWorkerTokenRefreshRequired(c *gc.C) {
   138  	s.controllerConfig[controller.CAASImageRepo] = `
   139  {
   140      "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   141      "username": "aws_access_key_id",
   142      "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   143      "password": "aws_secret_access_key",
   144      "region": "ap-southeast-2"
   145  }`[1:]
   146  
   147  	refreshed := s.CAASImageRepo(c)
   148  	refreshed.Auth = docker.NewToken(`refreshed===`)
   149  
   150  	done := make(chan struct{}, 1)
   151  	startWorker, ctrl := s.getWorkerStarter(c)
   152  	defer ctrl.Finish()
   153  
   154  	controllerConfigChangedChan := make(chan struct{}, 1)
   155  	w := startWorker(
   156  		s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) {
   157  			controllerConfigChangedChan <- struct{}{}
   158  			return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil
   159  		}),
   160  		// 1st round.
   161  		s.facade.EXPECT().ControllerConfig().Return(s.controllerConfig, nil),
   162  		s.reg.EXPECT().Ping().Return(nil),
   163  		s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)),
   164  		s.reg.EXPECT().RefreshAuth().Return(nil),
   165  		s.reg.EXPECT().ImageRepoDetails().DoAndReturn(func() docker.ImageRepoDetails {
   166  			o := s.CAASImageRepo(c)
   167  			c.Check(o, gc.DeepEquals, docker.ImageRepoDetails{
   168  				ServerAddress: "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   169  				Repository:    "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   170  				Region:        "ap-southeast-2",
   171  				BasicAuthConfig: docker.BasicAuthConfig{
   172  					Username: "aws_access_key_id",
   173  					Password: "aws_secret_access_key",
   174  				},
   175  			})
   176  			return o
   177  		}),
   178  		s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error {
   179  			c.Check(i, gc.DeepEquals, s.CAASImageRepo(c))
   180  			return nil
   181  		}),
   182  		// 2nd round.
   183  		s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)),
   184  		s.reg.EXPECT().RefreshAuth().Return(nil),
   185  		s.reg.EXPECT().ImageRepoDetails().Return(refreshed),
   186  		s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error {
   187  			c.Check(i, gc.DeepEquals, refreshed)
   188  			close(done)
   189  			return nil
   190  		}),
   191  		s.reg.EXPECT().Close().Return(nil),
   192  	)
   193  
   194  	select {
   195  	case <-done:
   196  	case <-time.After(coretesting.LongWait):
   197  		c.Fatalf("timed out waiting for worker to start")
   198  	}
   199  
   200  	workertest.CleanKill(c, w)
   201  }
   202  
   203  func (s *workerSuite) TestWorkerTokenRefreshNotRequiredThenRetry(c *gc.C) {
   204  	s.controllerConfig[controller.CAASImageRepo] = `
   205  {
   206      "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   207      "username": "aws_access_key_id",
   208      "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   209      "password": "aws_secret_access_key",
   210      "region": "ap-southeast-2"
   211  }`[1:]
   212  
   213  	done := make(chan struct{}, 1)
   214  	startWorker, ctrl := s.getWorkerStarter(c)
   215  	defer ctrl.Finish()
   216  
   217  	controllerConfigChangedChan := make(chan struct{}, 1)
   218  	w := startWorker(
   219  		s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) {
   220  			controllerConfigChangedChan <- struct{}{}
   221  			return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil
   222  		}),
   223  		// 1st round.
   224  		s.facade.EXPECT().ControllerConfig().Return(s.controllerConfig, nil),
   225  		s.reg.EXPECT().Ping().Return(nil),
   226  		s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)),
   227  		s.reg.EXPECT().RefreshAuth().Return(nil),
   228  		s.reg.EXPECT().ImageRepoDetails().DoAndReturn(func() docker.ImageRepoDetails {
   229  			o := s.CAASImageRepo(c)
   230  			c.Check(o, gc.DeepEquals, docker.ImageRepoDetails{
   231  				ServerAddress: "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   232  				Repository:    "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   233  				Region:        "ap-southeast-2",
   234  				BasicAuthConfig: docker.BasicAuthConfig{
   235  					Username: "aws_access_key_id",
   236  					Password: "aws_secret_access_key",
   237  				},
   238  			})
   239  			return o
   240  		}),
   241  		s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error {
   242  			c.Check(i, gc.DeepEquals, s.CAASImageRepo(c))
   243  			return nil
   244  		}),
   245  		// 2nd round.
   246  		s.reg.EXPECT().ShouldRefreshAuth().DoAndReturn(func() (bool, time.Duration) {
   247  			return false, 1 * time.Second
   248  		}),
   249  		// 3rd round.
   250  		s.reg.EXPECT().ShouldRefreshAuth().DoAndReturn(func() (bool, time.Duration) {
   251  			return true, time.Duration(0)
   252  		}),
   253  		s.reg.EXPECT().RefreshAuth().Return(nil),
   254  		s.reg.EXPECT().ImageRepoDetails().Return(s.CAASImageRepo(c)),
   255  		s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error {
   256  			c.Check(i, gc.DeepEquals, s.CAASImageRepo(c))
   257  			close(done)
   258  			return nil
   259  		}),
   260  		s.reg.EXPECT().Close().Return(nil),
   261  	)
   262  
   263  	select {
   264  	case <-done:
   265  	case <-time.After(coretesting.LongWait):
   266  		c.Fatalf("timed out waiting for worker to start")
   267  	}
   268  
   269  	workertest.CleanKill(c, w)
   270  }
   271  
   272  func (s *workerSuite) TestWorkerNoOpsForPublicRepo(c *gc.C) {
   273  	s.controllerConfig[controller.CAASImageRepo] = `
   274  {
   275      "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   276      "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com",
   277      "region": "ap-southeast-2",
   278  }`[1:]
   279  
   280  	done := make(chan struct{}, 1)
   281  	startWorker, ctrl := s.getWorkerStarter(c)
   282  	defer ctrl.Finish()
   283  
   284  	controllerConfigChangedChan := make(chan struct{}, 1)
   285  	w := startWorker(
   286  		s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) {
   287  			controllerConfigChangedChan <- struct{}{}
   288  			return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil
   289  		}),
   290  		s.facade.EXPECT().ControllerConfig().DoAndReturn(func() (controller.Config, error) {
   291  			close(done)
   292  			return s.controllerConfig, nil
   293  		}),
   294  	)
   295  
   296  	select {
   297  	case <-done:
   298  	case <-time.After(coretesting.LongWait):
   299  		c.Fatalf("timed out waiting for worker to start")
   300  	}
   301  
   302  	workertest.CleanKill(c, w)
   303  }
   304  
   305  func (s *workerSuite) CAASImageRepo(c *gc.C) docker.ImageRepoDetails {
   306  	r, err := docker.NewImageRepoDetails(s.controllerConfig.CAASImageRepo())
   307  	c.Assert(err, jc.ErrorIsNil)
   308  	return r
   309  }