github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/caasoperator/caasoperator_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperator_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"net/url"
     9  	"os"
    10  	"path/filepath"
    11  	"time"
    12  
    13  	"github.com/juju/clock/testclock"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/os/series"
    16  	"github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils"
    19  	"github.com/juju/utils/arch"
    20  	"github.com/juju/version"
    21  	gc "gopkg.in/check.v1"
    22  	"gopkg.in/juju/names.v2"
    23  	"gopkg.in/juju/worker.v1"
    24  	"gopkg.in/juju/worker.v1/workertest"
    25  
    26  	agenttools "github.com/juju/juju/agent/tools"
    27  	apiuniter "github.com/juju/juju/api/uniter"
    28  	"github.com/juju/juju/core/leadership"
    29  	"github.com/juju/juju/core/life"
    30  	"github.com/juju/juju/core/status"
    31  	"github.com/juju/juju/core/watcher"
    32  	"github.com/juju/juju/core/watcher/watchertest"
    33  	"github.com/juju/juju/downloader"
    34  	"github.com/juju/juju/testcharms"
    35  	coretesting "github.com/juju/juju/testing"
    36  	jujuversion "github.com/juju/juju/version"
    37  	"github.com/juju/juju/worker/caasoperator"
    38  	"github.com/juju/juju/worker/uniter"
    39  	runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
    40  )
    41  
    42  type WorkerSuite struct {
    43  	testing.IsolationSuite
    44  
    45  	clock                 *testclock.Clock
    46  	config                caasoperator.Config
    47  	unitsChanges          chan []string
    48  	appChanges            chan struct{}
    49  	appWatched            chan struct{}
    50  	unitRemoved           chan struct{}
    51  	client                fakeClient
    52  	charmDownloader       fakeDownloader
    53  	charmSHA256           string
    54  	uniterParams          *uniter.UniterParams
    55  	leadershipTrackerFunc func(unitTag names.UnitTag) leadership.Tracker
    56  	uniterFacadeFunc      func(unitTag names.UnitTag) *apiuniter.State
    57  }
    58  
    59  var _ = gc.Suite(&WorkerSuite{})
    60  
    61  func (s *WorkerSuite) SetUpTest(c *gc.C) {
    62  	s.IsolationSuite.SetUpTest(c)
    63  
    64  	// Create a charm archive, and compute its SHA256 hash
    65  	// for comparison in the tests.
    66  	fakeDownloadDir := c.MkDir()
    67  	s.charmDownloader = fakeDownloader{
    68  		path: testcharms.Repo.CharmArchivePath(
    69  			fakeDownloadDir,
    70  			"../kubernetes/gitlab",
    71  		),
    72  	}
    73  	charmSHA256, _, err := utils.ReadFileSHA256(s.charmDownloader.path)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	s.charmSHA256 = charmSHA256
    76  
    77  	s.clock = testclock.NewClock(time.Time{})
    78  	s.appWatched = make(chan struct{}, 1)
    79  	s.unitRemoved = make(chan struct{}, 1)
    80  	s.client = fakeClient{
    81  		applicationWatched: s.appWatched,
    82  		unitRemoved:        s.unitRemoved,
    83  		life:               life.Alive,
    84  	}
    85  	s.unitsChanges = make(chan []string)
    86  	s.appChanges = make(chan struct{})
    87  	s.client.unitsWatcher = watchertest.NewMockStringsWatcher(s.unitsChanges)
    88  	s.client.watcher = watchertest.NewMockNotifyWatcher(s.appChanges)
    89  	s.charmDownloader.ResetCalls()
    90  	s.uniterParams = &uniter.UniterParams{}
    91  	s.leadershipTrackerFunc = func(unitTag names.UnitTag) leadership.Tracker {
    92  		return &runnertesting.FakeTracker{}
    93  	}
    94  	s.uniterFacadeFunc = func(unitTag names.UnitTag) *apiuniter.State {
    95  		return &apiuniter.State{}
    96  	}
    97  	s.config = caasoperator.Config{
    98  		Application:           "gitlab",
    99  		CharmGetter:           &s.client,
   100  		Clock:                 s.clock,
   101  		PodSpecSetter:         &s.client,
   102  		DataDir:               c.MkDir(),
   103  		Downloader:            &s.charmDownloader,
   104  		StatusSetter:          &s.client,
   105  		ApplicationWatcher:    &s.client,
   106  		UnitGetter:            &s.client,
   107  		UnitRemover:           &s.client,
   108  		VersionSetter:         &s.client,
   109  		UniterParams:          s.uniterParams,
   110  		LeadershipTrackerFunc: s.leadershipTrackerFunc,
   111  		UniterFacadeFunc:      s.uniterFacadeFunc,
   112  		StartUniterFunc:       func(runner *worker.Runner, params *uniter.UniterParams) error { return nil },
   113  	}
   114  
   115  	agentBinaryDir := agenttools.ToolsDir(s.config.DataDir, "application-gitlab")
   116  	err = os.MkdirAll(agentBinaryDir, 0755)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	err = ioutil.WriteFile(filepath.Join(s.config.DataDir, "tools", "jujud"), []byte("jujud"), 0755)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  }
   121  
   122  func (s *WorkerSuite) TestValidateConfig(c *gc.C) {
   123  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   124  		config.Application = ""
   125  	}, `application name "" not valid`)
   126  
   127  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   128  		config.ApplicationWatcher = nil
   129  	}, `missing ApplicationWatcher not valid`)
   130  
   131  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   132  		config.UnitGetter = nil
   133  	}, `missing UnitGetter not valid`)
   134  
   135  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   136  		config.UnitRemover = nil
   137  	}, `missing UnitRemover not valid`)
   138  
   139  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   140  		config.LeadershipTrackerFunc = nil
   141  	}, `missing LeadershipTrackerFunc not valid`)
   142  
   143  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   144  		config.UniterFacadeFunc = nil
   145  	}, `missing UniterFacadeFunc not valid`)
   146  
   147  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   148  		config.UniterParams = nil
   149  	}, `missing UniterParams not valid`)
   150  
   151  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   152  		config.CharmGetter = nil
   153  	}, `missing CharmGetter not valid`)
   154  
   155  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   156  		config.Clock = nil
   157  	}, `missing Clock not valid`)
   158  
   159  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   160  		config.PodSpecSetter = nil
   161  	}, `missing PodSpecSetter not valid`)
   162  
   163  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   164  		config.DataDir = ""
   165  	}, `missing DataDir not valid`)
   166  
   167  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   168  		config.Downloader = nil
   169  	}, `missing Downloader not valid`)
   170  
   171  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   172  		config.StatusSetter = nil
   173  	}, `missing StatusSetter not valid`)
   174  
   175  	s.testValidateConfig(c, func(config *caasoperator.Config) {
   176  		config.VersionSetter = nil
   177  	}, `missing VersionSetter not valid`)
   178  
   179  }
   180  
   181  func (s *WorkerSuite) testValidateConfig(c *gc.C, f func(*caasoperator.Config), expect string) {
   182  	config := s.config
   183  	f(&config)
   184  	w, err := caasoperator.NewWorker(config)
   185  	if err == nil {
   186  		workertest.DirtyKill(c, w)
   187  	}
   188  	c.Check(err, gc.ErrorMatches, expect)
   189  }
   190  
   191  func (s *WorkerSuite) TestStartStop(c *gc.C) {
   192  	w, err := caasoperator.NewWorker(s.config)
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	workertest.CheckAlive(c, w)
   195  	workertest.CleanKill(c, w)
   196  }
   197  
   198  func (s *WorkerSuite) TestWorkerDownloadsCharm(c *gc.C) {
   199  	uniterStarted := make(chan struct{})
   200  	s.config.StartUniterFunc = func(runner *worker.Runner, params *uniter.UniterParams) error {
   201  		c.Assert(params.UnitTag.Id(), gc.Equals, "gitlab/0")
   202  		close(uniterStarted)
   203  		return nil
   204  	}
   205  
   206  	w, err := caasoperator.NewWorker(s.config)
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	defer workertest.CleanKill(c, w)
   209  
   210  	select {
   211  	case s.appChanges <- struct{}{}:
   212  	case <-time.After(coretesting.LongWait):
   213  		c.Fatal("timed out sending application change")
   214  	}
   215  	select {
   216  	case s.unitsChanges <- []string{"gitlab/0"}:
   217  	case <-time.After(coretesting.LongWait):
   218  		c.Fatal("timed out sending unit change")
   219  	}
   220  	select {
   221  	case <-s.appWatched:
   222  	case <-time.After(coretesting.LongWait):
   223  		c.Fatal("timed out waiting for application to be watched")
   224  	}
   225  	select {
   226  	case <-uniterStarted:
   227  	case <-time.After(coretesting.LongWait):
   228  		c.Fatalf("timeout while waiting for uniter to start")
   229  	}
   230  
   231  	s.client.CheckCallNames(c, "Charm", "SetStatus", "SetVersion", "WatchUnits", "SetStatus", "Watch", "Charm", "Life")
   232  	s.client.CheckCall(c, 0, "Charm", "gitlab")
   233  	s.client.CheckCall(c, 2, "SetVersion", "gitlab", version.Binary{
   234  		Number: jujuversion.Current,
   235  		Series: series.MustHostSeries(),
   236  		Arch:   arch.HostArch(),
   237  	})
   238  	s.client.CheckCall(c, 3, "WatchUnits", "gitlab")
   239  	s.client.CheckCall(c, 5, "Watch", "gitlab")
   240  
   241  	s.charmDownloader.CheckCallNames(c, "Download")
   242  	downloadArgs := s.charmDownloader.Calls()[0].Args
   243  	c.Assert(downloadArgs, gc.HasLen, 1)
   244  	c.Assert(downloadArgs[0], gc.FitsTypeOf, downloader.Request{})
   245  	downloadRequest := downloadArgs[0].(downloader.Request)
   246  	c.Assert(downloadRequest.Abort, gc.NotNil)
   247  	c.Assert(downloadRequest.Verify, gc.NotNil)
   248  
   249  	// fakeClient.Charm returns the SHA256 sum of fakeCharmContent.
   250  	fakeCharmPath := filepath.Join(c.MkDir(), "fake.charm")
   251  	err = ioutil.WriteFile(fakeCharmPath, fakeCharmContent, 0644)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	f, err := os.Open(fakeCharmPath)
   254  	c.Assert(err, jc.ErrorIsNil)
   255  	defer f.Close()
   256  	err = downloadRequest.Verify(f)
   257  	c.Assert(err, jc.ErrorIsNil)
   258  
   259  	downloadRequest.Abort = nil
   260  	downloadRequest.Verify = nil
   261  	agentDir := filepath.Join(s.config.DataDir, "agents", "application-gitlab")
   262  	c.Assert(downloadRequest, jc.DeepEquals, downloader.Request{
   263  		URL:       &url.URL{Scheme: "cs", Opaque: "gitlab-1"},
   264  		TargetDir: filepath.Join(agentDir, "state", "bundles", "downloads"),
   265  	})
   266  
   267  	// The download directory should have been removed.
   268  	_, err = os.Stat(downloadRequest.TargetDir)
   269  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   270  
   271  	// The charm archive should have been unpacked into <data-dir>/charm.
   272  	charmDir := filepath.Join(agentDir, "charm")
   273  	_, err = os.Stat(filepath.Join(charmDir, "metadata.yaml"))
   274  	c.Assert(err, jc.ErrorIsNil)
   275  
   276  }
   277  
   278  func (s *WorkerSuite) assertUniterStarted(c *gc.C) (worker.Worker, watcher.NotifyChannel) {
   279  	applicationChannel := make(chan watcher.NotifyChannel)
   280  	s.config.StartUniterFunc = func(runner *worker.Runner, params *uniter.UniterParams) error {
   281  		c.Assert(params.UnitTag.Id(), gc.Equals, "gitlab/0")
   282  		select {
   283  		case applicationChannel <- params.ApplicationChannel:
   284  		case <-time.After(coretesting.LongWait):
   285  			c.Fatalf("timeout sending application channel")
   286  		}
   287  		return nil
   288  	}
   289  
   290  	w, err := caasoperator.NewWorker(s.config)
   291  	c.Assert(err, jc.ErrorIsNil)
   292  
   293  	select {
   294  	case s.appChanges <- struct{}{}:
   295  	case <-time.After(coretesting.LongWait):
   296  		c.Fatal("timed out sending application change")
   297  	}
   298  	select {
   299  	case s.unitsChanges <- []string{"gitlab/0"}:
   300  	case <-time.After(coretesting.LongWait):
   301  		c.Fatal("timed out sending unit change")
   302  	}
   303  
   304  	select {
   305  	case channel := <-applicationChannel:
   306  		return w, channel
   307  	case <-time.After(coretesting.LongWait):
   308  		c.Fatalf("timeout while waiting for uniter to start")
   309  	}
   310  	panic("not reachable")
   311  }
   312  
   313  func (s *WorkerSuite) TestUpgradeCharm(c *gc.C) {
   314  	w, applicationChannel := s.assertUniterStarted(c)
   315  	defer workertest.CleanKill(c, w)
   316  
   317  	select {
   318  	case <-applicationChannel:
   319  		c.Fatal("unexpected application change")
   320  	case <-time.After(coretesting.ShortWait):
   321  	}
   322  
   323  	select {
   324  	case s.appChanges <- struct{}{}:
   325  	case <-time.After(coretesting.LongWait):
   326  		c.Fatal("timed out sending application change")
   327  	}
   328  
   329  	select {
   330  	case <-applicationChannel:
   331  	case <-time.After(coretesting.LongWait):
   332  		c.Fatal("timed out waiting for application change")
   333  	}
   334  }
   335  
   336  func (s *WorkerSuite) TestWorkerSetsStatus(c *gc.C) {
   337  	w, err := caasoperator.NewWorker(s.config)
   338  	c.Assert(err, jc.ErrorIsNil)
   339  	workertest.CleanKill(c, w)
   340  
   341  	s.client.CheckCallNames(c, "Charm", "SetStatus", "SetVersion", "WatchUnits", "SetStatus", "Watch")
   342  	s.client.CheckCall(c, 1, "SetStatus", "gitlab", status.Maintenance, "downloading charm (cs:gitlab-1)", map[string]interface{}(nil))
   343  }
   344  
   345  func (s *WorkerSuite) TestWatcherFailureStopsWorker(c *gc.C) {
   346  	w, err := caasoperator.NewWorker(s.config)
   347  	c.Assert(err, jc.ErrorIsNil)
   348  	defer workertest.DirtyKill(c, w)
   349  
   350  	s.client.unitsWatcher.KillErr(errors.New("splat"))
   351  	err = workertest.CheckKilled(c, w)
   352  	c.Assert(err, gc.ErrorMatches, "splat")
   353  }
   354  
   355  func (s *WorkerSuite) TestRemovedUnit(c *gc.C) {
   356  	w, _ := s.assertUniterStarted(c)
   357  	defer workertest.CleanKill(c, w)
   358  
   359  	s.client.ResetCalls()
   360  	s.client.life = life.Dead
   361  	select {
   362  	case s.unitsChanges <- []string{"gitlab/0"}:
   363  	case <-time.After(coretesting.LongWait):
   364  		c.Fatal("timed out sending unit change")
   365  	}
   366  	select {
   367  	case <-s.unitRemoved:
   368  	case <-time.After(coretesting.LongWait):
   369  		c.Fatal("timed out waiting for unit to be removed")
   370  	}
   371  	s.client.CheckCallNames(c, "Life", "RemoveUnit")
   372  	s.client.CheckCall(c, 0, "Life", "gitlab/0")
   373  	s.client.CheckCall(c, 1, "RemoveUnit", "gitlab/0")
   374  }