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

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package containerbroker_test
     5  
     6  import (
     7  	"github.com/juju/names/v5"
     8  	"github.com/juju/testing"
     9  	"go.uber.org/mock/gomock"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/api/agent/provisioner"
    13  	"github.com/juju/juju/api/base"
    14  	"github.com/juju/juju/container"
    15  	"github.com/juju/juju/container/broker"
    16  	"github.com/juju/juju/core/instance"
    17  	"github.com/juju/juju/core/life"
    18  	"github.com/juju/juju/core/network"
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/rpc/params"
    21  	"github.com/juju/juju/worker/containerbroker"
    22  	"github.com/juju/juju/worker/containerbroker/mocks"
    23  )
    24  
    25  type brokerConfigSuite struct {
    26  	testing.IsolationSuite
    27  }
    28  
    29  var _ = gc.Suite(&brokerConfigSuite{})
    30  
    31  func (s *brokerConfigSuite) TestInvalidConfigValidate(c *gc.C) {
    32  	ctrl := gomock.NewController(c)
    33  	defer ctrl.Finish()
    34  
    35  	testcases := []struct {
    36  		description string
    37  		config      containerbroker.Config
    38  		err         string
    39  	}{
    40  		{
    41  			description: "Test empty configuration",
    42  			config:      containerbroker.Config{},
    43  			err:         "nil APICaller not valid",
    44  		},
    45  		{
    46  			description: "Test no api caller",
    47  			config:      containerbroker.Config{},
    48  			err:         "nil APICaller not valid",
    49  		},
    50  		{
    51  			description: "Test no agent config",
    52  			config: containerbroker.Config{
    53  				APICaller: mocks.NewMockAPICaller(ctrl),
    54  			},
    55  			err: "nil AgentConfig not valid",
    56  		},
    57  		{
    58  			description: "Test no machine lock",
    59  			config: containerbroker.Config{
    60  				APICaller:   mocks.NewMockAPICaller(ctrl),
    61  				AgentConfig: mocks.NewMockConfig(ctrl),
    62  			},
    63  			err: "nil MachineLock not valid",
    64  		},
    65  		{
    66  			description: "Test no broker func",
    67  			config: containerbroker.Config{
    68  				APICaller:   mocks.NewMockAPICaller(ctrl),
    69  				AgentConfig: mocks.NewMockConfig(ctrl),
    70  				MachineLock: mocks.NewMockLock(ctrl),
    71  			},
    72  			err: "nil NewBrokerFunc not valid",
    73  		},
    74  		{
    75  			description: "Test no state func",
    76  			config: containerbroker.Config{
    77  				APICaller:   mocks.NewMockAPICaller(ctrl),
    78  				AgentConfig: mocks.NewMockConfig(ctrl),
    79  				MachineLock: mocks.NewMockLock(ctrl),
    80  				NewBrokerFunc: func(broker.Config) (environs.InstanceBroker, error) {
    81  					return mocks.NewMockInstanceBroker(ctrl), nil
    82  				},
    83  			},
    84  			err: "nil NewStateFunc not valid",
    85  		},
    86  	}
    87  	for i, test := range testcases {
    88  		c.Logf("%d %s", i, test.description)
    89  		err := test.config.Validate()
    90  		c.Assert(err, gc.ErrorMatches, test.err)
    91  	}
    92  }
    93  
    94  func (s *brokerConfigSuite) TestValidConfigValidate(c *gc.C) {
    95  	ctrl := gomock.NewController(c)
    96  	defer ctrl.Finish()
    97  
    98  	config := containerbroker.Config{
    99  		APICaller:   mocks.NewMockAPICaller(ctrl),
   100  		AgentConfig: mocks.NewMockConfig(ctrl),
   101  		MachineLock: mocks.NewMockLock(ctrl),
   102  		NewBrokerFunc: func(broker.Config) (environs.InstanceBroker, error) {
   103  			return mocks.NewMockInstanceBroker(ctrl), nil
   104  		},
   105  		NewStateFunc: func(base.APICaller) containerbroker.State {
   106  			return mocks.NewMockState(ctrl)
   107  		},
   108  	}
   109  	err := config.Validate()
   110  	c.Assert(err, gc.IsNil)
   111  }
   112  
   113  type trackerSuite struct {
   114  	testing.IsolationSuite
   115  
   116  	apiCaller   *mocks.MockAPICaller
   117  	agentConfig *mocks.MockConfig
   118  	machineLock *mocks.MockLock
   119  	broker      *mocks.MockInstanceBroker
   120  	state       *mocks.MockState
   121  	machine     *mocks.MockMachineProvisioner
   122  
   123  	machineTag names.MachineTag
   124  }
   125  
   126  var _ = gc.Suite(&trackerSuite{})
   127  
   128  func (s *trackerSuite) setup(c *gc.C) *gomock.Controller {
   129  	ctrl := gomock.NewController(c)
   130  
   131  	s.apiCaller = mocks.NewMockAPICaller(ctrl)
   132  	s.agentConfig = mocks.NewMockConfig(ctrl)
   133  	s.machineLock = mocks.NewMockLock(ctrl)
   134  	s.broker = mocks.NewMockInstanceBroker(ctrl)
   135  	s.state = mocks.NewMockState(ctrl)
   136  	s.machine = mocks.NewMockMachineProvisioner(ctrl)
   137  
   138  	s.machineTag = names.NewMachineTag("machine-0")
   139  
   140  	return ctrl
   141  }
   142  
   143  func (s *trackerSuite) TestNewTracker(c *gc.C) {
   144  	defer s.setup(c).Finish()
   145  
   146  	_, err := s.withScenario(c,
   147  		&broker.Config{
   148  			Name:          "instance-broker",
   149  			ContainerType: instance.LXD,
   150  			ManagerConfig: map[string]string{
   151  				container.ConfigAvailabilityZone: "0",
   152  			},
   153  			APICaller:    s.state,
   154  			AgentConfig:  s.agentConfig,
   155  			MachineTag:   s.machineTag,
   156  			MachineLock:  s.machineLock,
   157  			GetNetConfig: network.GetObservedNetworkConfig,
   158  		},
   159  		s.expectMachineTag,
   160  		s.expectMachines,
   161  		s.expectSupportedContainers,
   162  		s.expectContainerConfig,
   163  	)
   164  	c.Assert(err, gc.IsNil)
   165  }
   166  
   167  func (s *trackerSuite) TestNewTrackerWithNoMachines(c *gc.C) {
   168  	defer s.setup(c).Finish()
   169  
   170  	_, err := s.withScenario(c,
   171  		nil,
   172  		s.expectMachineTag,
   173  		s.expectNoMachines,
   174  	)
   175  	c.Assert(err, gc.ErrorMatches, "expected 1 result, got 0")
   176  }
   177  
   178  func (s *trackerSuite) TestNewTrackerWithDeadMachines(c *gc.C) {
   179  	defer s.setup(c).Finish()
   180  
   181  	_, err := s.withScenario(c,
   182  		nil,
   183  		s.expectMachineTag,
   184  		s.expectDeadMachines,
   185  	)
   186  	c.Assert(err, gc.ErrorMatches, "resource permanently unavailable")
   187  }
   188  
   189  func (s *trackerSuite) TestNewTrackerWithNoContainers(c *gc.C) {
   190  	defer s.setup(c).Finish()
   191  
   192  	_, err := s.withScenario(c,
   193  		nil,
   194  		s.expectMachineTag,
   195  		s.expectMachines,
   196  		s.expectNoSupportedContainers,
   197  	)
   198  	c.Assert(err, gc.ErrorMatches, "resource permanently unavailable")
   199  }
   200  
   201  func (s *trackerSuite) TestNewTrackerWithNoDeterminedContainers(c *gc.C) {
   202  	defer s.setup(c).Finish()
   203  
   204  	_, err := s.withScenario(c,
   205  		nil,
   206  		s.expectMachineTag,
   207  		s.expectMachines,
   208  		s.expectNoDeterminedSupportedContainers,
   209  	)
   210  	c.Assert(err, gc.ErrorMatches, "no container types determined")
   211  }
   212  
   213  func (s *trackerSuite) TestNewTrackerWithKVMContainers(c *gc.C) {
   214  	defer s.setup(c).Finish()
   215  
   216  	_, err := s.withScenario(c,
   217  		nil,
   218  		s.expectMachineTag,
   219  		s.expectMachines,
   220  		s.expectKVMSupportedContainers,
   221  	)
   222  	c.Assert(err, gc.ErrorMatches, "resource permanently unavailable")
   223  }
   224  
   225  func (s *trackerSuite) withScenario(c *gc.C, expected *broker.Config, behaviours ...func()) (*containerbroker.Tracker, error) {
   226  	for _, b := range behaviours {
   227  		b()
   228  	}
   229  	return containerbroker.NewTracker(containerbroker.Config{
   230  		APICaller:   s.apiCaller,
   231  		AgentConfig: s.agentConfig,
   232  		MachineLock: s.machineLock,
   233  		NewBrokerFunc: func(config broker.Config) (environs.InstanceBroker, error) {
   234  			if expected != nil {
   235  				c.Check(config.Name, gc.Equals, expected.Name)
   236  				c.Check(config.ContainerType, gc.Equals, expected.ContainerType)
   237  				c.Check(config.ManagerConfig, gc.DeepEquals, expected.ManagerConfig)
   238  				c.Check(config.MachineTag, gc.Equals, expected.MachineTag)
   239  			}
   240  			return s.broker, nil
   241  		},
   242  		NewStateFunc: func(base.APICaller) containerbroker.State {
   243  			return s.state
   244  		},
   245  	})
   246  }
   247  
   248  func (s *trackerSuite) expectMachineTag() {
   249  	s.agentConfig.EXPECT().Tag().Return(s.machineTag)
   250  }
   251  
   252  func (s *trackerSuite) expectMachines() {
   253  	s.state.EXPECT().Machines(s.machineTag).Return([]provisioner.MachineResult{{
   254  		Machine: s.machine,
   255  	}}, nil)
   256  	s.machine.EXPECT().Life().Return(life.Alive)
   257  }
   258  
   259  func (s *trackerSuite) expectNoMachines() {
   260  	s.state.EXPECT().Machines(s.machineTag).Return([]provisioner.MachineResult{}, nil)
   261  }
   262  
   263  func (s *trackerSuite) expectDeadMachines() {
   264  	s.state.EXPECT().Machines(s.machineTag).Return([]provisioner.MachineResult{{
   265  		Machine: s.machine,
   266  	}}, nil)
   267  	s.machine.EXPECT().Life().Return(life.Dead)
   268  }
   269  
   270  func (s *trackerSuite) expectSupportedContainers() {
   271  	s.machine.EXPECT().SupportedContainers().Return([]instance.ContainerType{
   272  		instance.LXD,
   273  	}, true, nil)
   274  }
   275  
   276  func (s *trackerSuite) expectNoSupportedContainers() {
   277  	s.machine.EXPECT().SupportedContainers().Return([]instance.ContainerType{}, true, nil)
   278  }
   279  
   280  func (s *trackerSuite) expectNoDeterminedSupportedContainers() {
   281  	s.machine.EXPECT().SupportedContainers().Return([]instance.ContainerType{
   282  		instance.LXD,
   283  	}, false, nil)
   284  }
   285  
   286  func (s *trackerSuite) expectKVMSupportedContainers() {
   287  	s.machine.EXPECT().SupportedContainers().Return([]instance.ContainerType{
   288  		instance.KVM,
   289  	}, true, nil)
   290  }
   291  
   292  func (s *trackerSuite) expectContainerConfig() {
   293  	s.state.EXPECT().ContainerManagerConfig(params.ContainerManagerConfigParams{
   294  		Type: instance.LXD,
   295  	}).Return(params.ContainerManagerConfig{
   296  		ManagerConfig: make(map[string]string),
   297  	}, nil)
   298  	s.machine.EXPECT().AvailabilityZone().Return("0", nil)
   299  }