github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/caasbroker/broker_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasbroker_test
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils/v3"
    14  	"github.com/juju/worker/v3/workertest"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/caas"
    18  	"github.com/juju/juju/environs"
    19  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    20  	coretesting "github.com/juju/juju/testing"
    21  	"github.com/juju/juju/worker/caasbroker"
    22  )
    23  
    24  type TrackerSuite struct {
    25  	coretesting.BaseSuite
    26  }
    27  
    28  var _ = gc.Suite(&TrackerSuite{})
    29  
    30  func (s *TrackerSuite) validConfig() caasbroker.Config {
    31  	return caasbroker.Config{
    32  		ConfigAPI: &runContext{},
    33  		NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) {
    34  			return nil, errors.NotImplementedf("test func")
    35  		},
    36  		Logger: loggo.GetLogger("test"),
    37  	}
    38  }
    39  
    40  func (s *TrackerSuite) TestValidateObserver(c *gc.C) {
    41  	config := s.validConfig()
    42  	config.ConfigAPI = nil
    43  	s.testValidate(c, config, func(err error) {
    44  		c.Check(err, jc.Satisfies, errors.IsNotValid)
    45  		c.Check(err, gc.ErrorMatches, "nil ConfigAPI not valid")
    46  	})
    47  }
    48  
    49  func (s *TrackerSuite) TestValidateNewBrokerFunc(c *gc.C) {
    50  	config := s.validConfig()
    51  	config.NewContainerBrokerFunc = nil
    52  	s.testValidate(c, config, func(err error) {
    53  		c.Check(err, jc.Satisfies, errors.IsNotValid)
    54  		c.Check(err, gc.ErrorMatches, "nil NewContainerBrokerFunc not valid")
    55  	})
    56  }
    57  
    58  func (s *TrackerSuite) TestValidateLogger(c *gc.C) {
    59  	config := s.validConfig()
    60  	config.Logger = nil
    61  	s.testValidate(c, config, func(err error) {
    62  		c.Check(err, jc.Satisfies, errors.IsNotValid)
    63  		c.Check(err, gc.ErrorMatches, "nil Logger not valid")
    64  	})
    65  }
    66  
    67  func (s *TrackerSuite) testValidate(c *gc.C, config caasbroker.Config, check func(err error)) {
    68  	err := config.Validate()
    69  	check(err)
    70  
    71  	tracker, err := caasbroker.NewTracker(config)
    72  	c.Check(tracker, gc.IsNil)
    73  	check(err)
    74  }
    75  
    76  func (s *TrackerSuite) TestCloudSpecFails(c *gc.C) {
    77  	fix := &fixture{
    78  		observerErrs: []error{
    79  			errors.New("no you"),
    80  		},
    81  	}
    82  	fix.Run(c, func(context *runContext) {
    83  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
    84  			ConfigAPI:              context,
    85  			NewContainerBrokerFunc: newMockBroker,
    86  			Logger:                 loggo.GetLogger("test"),
    87  		})
    88  		c.Check(err, gc.ErrorMatches, "cannot get cloud information: no you")
    89  		c.Check(tracker, gc.IsNil)
    90  		context.CheckCallNames(c, "CloudSpec")
    91  	})
    92  }
    93  
    94  func (s *TrackerSuite) validFixture() *fixture {
    95  	cloudSpec := environscloudspec.CloudSpec{
    96  		Name:   "foo",
    97  		Type:   "bar",
    98  		Region: "baz",
    99  	}
   100  	cfg := coretesting.FakeConfig()
   101  	cfg["type"] = "kubernetes"
   102  	cfg["uuid"] = utils.MustNewUUID().String()
   103  	return &fixture{initialSpec: cloudSpec, initialConfig: cfg}
   104  }
   105  
   106  func (s *TrackerSuite) TestSuccess(c *gc.C) {
   107  	fix := s.validFixture()
   108  	fix.Run(c, func(context *runContext) {
   109  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   110  			ConfigAPI:              context,
   111  			NewContainerBrokerFunc: newMockBroker,
   112  			Logger:                 loggo.GetLogger("test"),
   113  		})
   114  		c.Assert(err, jc.ErrorIsNil)
   115  		defer workertest.CleanKill(c, tracker)
   116  
   117  		gotBroker := tracker.Broker()
   118  		c.Assert(gotBroker, gc.NotNil)
   119  	})
   120  }
   121  
   122  func (s *TrackerSuite) TestInitialise(c *gc.C) {
   123  	fix := s.validFixture()
   124  	fix.Run(c, func(runContext *runContext) {
   125  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   126  			ConfigAPI: runContext,
   127  			NewContainerBrokerFunc: func(_ context.Context, args environs.OpenParams) (caas.Broker, error) {
   128  				c.Assert(args.Cloud, jc.DeepEquals, fix.initialSpec)
   129  				c.Assert(args.Config.Name(), jc.DeepEquals, "testmodel")
   130  				return nil, errors.NotValidf("cloud spec")
   131  			},
   132  			Logger: loggo.GetLogger("test"),
   133  		})
   134  		c.Check(err, gc.ErrorMatches, `cannot create caas broker: cloud spec not valid`)
   135  		c.Check(tracker, gc.IsNil)
   136  		runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig")
   137  	})
   138  }
   139  
   140  func (s *TrackerSuite) TestModelConfigFails(c *gc.C) {
   141  	fix := &fixture{
   142  		observerErrs: []error{
   143  			nil,
   144  			errors.New("no you"),
   145  		},
   146  	}
   147  	fix.Run(c, func(context *runContext) {
   148  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   149  			ConfigAPI:              context,
   150  			NewContainerBrokerFunc: newMockBroker,
   151  			Logger:                 loggo.GetLogger("test"),
   152  		})
   153  		c.Check(err, gc.ErrorMatches, "no you")
   154  		c.Check(tracker, gc.IsNil)
   155  		context.CheckCallNames(c, "CloudSpec", "ModelConfig")
   156  	})
   157  }
   158  
   159  func (s *TrackerSuite) TestModelConfigInvalid(c *gc.C) {
   160  	fix := &fixture{}
   161  	fix.Run(c, func(runContext *runContext) {
   162  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   163  			ConfigAPI: runContext,
   164  			NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) {
   165  				return nil, errors.NotValidf("config")
   166  			},
   167  			Logger: loggo.GetLogger("test"),
   168  		})
   169  		c.Check(err, gc.ErrorMatches, `cannot create caas broker: config not valid`)
   170  		c.Check(tracker, gc.IsNil)
   171  		runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig")
   172  	})
   173  }
   174  
   175  func (s *TrackerSuite) TestModelConfigValid(c *gc.C) {
   176  	fix := &fixture{
   177  		initialConfig: coretesting.Attrs{
   178  			"name": "this-particular-name",
   179  		},
   180  	}
   181  	fix.Run(c, func(context *runContext) {
   182  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   183  			ConfigAPI:              context,
   184  			NewContainerBrokerFunc: newMockBroker,
   185  			Logger:                 loggo.GetLogger("test"),
   186  		})
   187  		c.Assert(err, jc.ErrorIsNil)
   188  		defer workertest.CleanKill(c, tracker)
   189  
   190  		gotBroker := tracker.Broker()
   191  		c.Assert(gotBroker, gc.NotNil)
   192  		c.Check(gotBroker.Config().Name(), gc.Equals, "this-particular-name")
   193  	})
   194  }
   195  
   196  func (s *TrackerSuite) TestCloudSpecInvalid(c *gc.C) {
   197  	cloudSpec := environscloudspec.CloudSpec{
   198  		Name:   "foo",
   199  		Type:   "bar",
   200  		Region: "baz",
   201  	}
   202  	fix := &fixture{initialSpec: cloudSpec}
   203  	fix.Run(c, func(runContext *runContext) {
   204  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   205  			ConfigAPI: runContext,
   206  			NewContainerBrokerFunc: func(_ context.Context, args environs.OpenParams) (caas.Broker, error) {
   207  				c.Assert(args.Cloud, jc.DeepEquals, cloudSpec)
   208  				return nil, errors.NotValidf("cloud spec")
   209  			},
   210  			Logger: loggo.GetLogger("test"),
   211  		})
   212  		c.Check(err, gc.ErrorMatches, `cannot create caas broker: cloud spec not valid`)
   213  		c.Check(tracker, gc.IsNil)
   214  		runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig")
   215  	})
   216  }
   217  
   218  func (s *TrackerSuite) TestWatchFails(c *gc.C) {
   219  	fix := &fixture{
   220  		observerErrs: []error{
   221  			nil, nil, nil, errors.New("grrk splat"),
   222  		},
   223  	}
   224  	fix.Run(c, func(context *runContext) {
   225  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   226  			ConfigAPI:              context,
   227  			NewContainerBrokerFunc: newMockBroker,
   228  			Logger:                 loggo.GetLogger("test"),
   229  		})
   230  		c.Assert(err, jc.ErrorIsNil)
   231  		defer workertest.DirtyKill(c, tracker)
   232  
   233  		err = workertest.CheckKilled(c, tracker)
   234  		c.Check(err, gc.ErrorMatches, "cannot watch model config: grrk splat")
   235  		context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges")
   236  	})
   237  }
   238  
   239  func (s *TrackerSuite) TestModelConfigWatchCloses(c *gc.C) {
   240  	fix := &fixture{}
   241  	fix.Run(c, func(context *runContext) {
   242  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   243  			ConfigAPI:              context,
   244  			NewContainerBrokerFunc: newMockBroker,
   245  			Logger:                 loggo.GetLogger("test"),
   246  		})
   247  		c.Assert(err, jc.ErrorIsNil)
   248  		defer workertest.DirtyKill(c, tracker)
   249  
   250  		context.CloseModelConfigNotify()
   251  		err = workertest.CheckKilled(c, tracker)
   252  		c.Check(err, gc.ErrorMatches, "model config watch closed")
   253  		context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges")
   254  	})
   255  }
   256  
   257  func (s *TrackerSuite) TestCloudSpecWatchCloses(c *gc.C) {
   258  	fix := &fixture{}
   259  	fix.Run(c, func(context *runContext) {
   260  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   261  			ConfigAPI:              context,
   262  			NewContainerBrokerFunc: newMockBroker,
   263  			Logger:                 loggo.GetLogger("test"),
   264  		})
   265  		c.Assert(err, jc.ErrorIsNil)
   266  		defer workertest.DirtyKill(c, tracker)
   267  
   268  		context.CloseCloudSpecNotify()
   269  		err = workertest.CheckKilled(c, tracker)
   270  		c.Check(err, gc.ErrorMatches, "cloud watch closed")
   271  		context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges")
   272  	})
   273  }
   274  
   275  func (s *TrackerSuite) TestWatchedModelConfigFails(c *gc.C) {
   276  	fix := &fixture{
   277  		observerErrs: []error{
   278  			nil, nil, nil, nil, nil, errors.New("blam ouch"),
   279  		},
   280  	}
   281  	fix.Run(c, func(context *runContext) {
   282  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   283  			ConfigAPI:              context,
   284  			NewContainerBrokerFunc: newMockBroker,
   285  			Logger:                 loggo.GetLogger("test"),
   286  		})
   287  		c.Check(err, jc.ErrorIsNil)
   288  		defer workertest.DirtyKill(c, tracker)
   289  
   290  		context.SendModelConfigNotify()
   291  		context.SendCloudSpecNotify()
   292  		err = workertest.CheckKilled(c, tracker)
   293  		c.Check(err, gc.ErrorMatches, "cannot read model config: blam ouch")
   294  	})
   295  }
   296  
   297  func (s *TrackerSuite) TestWatchedModelConfigIncompatible(c *gc.C) {
   298  	fix := &fixture{}
   299  	fix.Run(c, func(runContext *runContext) {
   300  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   301  			ConfigAPI: runContext,
   302  			NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) {
   303  				broker := &mockBroker{}
   304  				broker.SetErrors(errors.New("SetConfig is broken"))
   305  				return broker, nil
   306  			},
   307  			Logger: loggo.GetLogger("test"),
   308  		})
   309  		c.Check(err, jc.ErrorIsNil)
   310  		defer workertest.DirtyKill(c, tracker)
   311  
   312  		runContext.SendModelConfigNotify()
   313  		err = workertest.CheckKilled(c, tracker)
   314  		c.Check(err, gc.ErrorMatches, "cannot update model config: SetConfig is broken")
   315  		runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges", "ModelConfig")
   316  	})
   317  }
   318  
   319  func (s *TrackerSuite) TestWatchedModelConfigUpdates(c *gc.C) {
   320  	fix := &fixture{
   321  		initialConfig: coretesting.Attrs{
   322  			"name": "original-name",
   323  		},
   324  	}
   325  	fix.Run(c, func(context *runContext) {
   326  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   327  			ConfigAPI:              context,
   328  			NewContainerBrokerFunc: newMockBroker,
   329  			Logger:                 loggo.GetLogger("test"),
   330  		})
   331  		c.Check(err, jc.ErrorIsNil)
   332  		defer workertest.CleanKill(c, tracker)
   333  
   334  		context.SetConfig(c, coretesting.Attrs{
   335  			"name": "updated-name",
   336  		})
   337  		gotBroker := tracker.Broker()
   338  		c.Assert(gotBroker.Config().Name(), gc.Equals, "original-name")
   339  
   340  		timeout := time.After(coretesting.LongWait)
   341  		attempt := time.After(0)
   342  		context.SendModelConfigNotify()
   343  		for {
   344  			select {
   345  			case <-attempt:
   346  				name := gotBroker.Config().Name()
   347  				if name == "original-name" {
   348  					attempt = time.After(coretesting.ShortWait)
   349  					continue
   350  				}
   351  				c.Check(name, gc.Equals, "updated-name")
   352  			case <-timeout:
   353  				c.Fatalf("timed out waiting for broker to be updated")
   354  			}
   355  			break
   356  		}
   357  	})
   358  }
   359  
   360  func (s *TrackerSuite) TestWatchedCloudSpecUpdates(c *gc.C) {
   361  	fix := &fixture{
   362  		initialSpec: environscloudspec.CloudSpec{Name: "cloud", Type: "lxd"},
   363  	}
   364  	fix.Run(c, func(context *runContext) {
   365  		tracker, err := caasbroker.NewTracker(caasbroker.Config{
   366  			ConfigAPI:              context,
   367  			NewContainerBrokerFunc: newMockBroker,
   368  			Logger:                 loggo.GetLogger("test"),
   369  		})
   370  		c.Check(err, jc.ErrorIsNil)
   371  		defer workertest.CleanKill(c, tracker)
   372  
   373  		context.SetCloudSpec(c, environscloudspec.CloudSpec{Name: "lxd", Type: "lxd", Endpoint: "http://api"})
   374  		gotBroker := tracker.Broker().(*mockBroker)
   375  		c.Assert(gotBroker.CloudSpec(), jc.DeepEquals, fix.initialSpec)
   376  
   377  		timeout := time.After(coretesting.LongWait)
   378  		attempt := time.After(0)
   379  		context.SendCloudSpecNotify()
   380  		for {
   381  			select {
   382  			case <-attempt:
   383  				ep := gotBroker.CloudSpec().Endpoint
   384  				if ep == "" {
   385  					attempt = time.After(coretesting.ShortWait)
   386  					continue
   387  				}
   388  				c.Check(ep, gc.Equals, "http://api")
   389  			case <-timeout:
   390  				c.Fatalf("timed out waiting for environ to be updated")
   391  			}
   392  			break
   393  		}
   394  	})
   395  }