github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/machine_test.go (about)

     1  // Copyright 2012-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agent
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/json"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"reflect"
    14  	"runtime"
    15  	"runtime/pprof"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/juju/cmd"
    20  	"github.com/juju/cmd/cmdtesting"
    21  	"github.com/juju/collections/set"
    22  	"github.com/juju/errors"
    23  	"github.com/juju/juju/cmd/jujud/agent/agenttest"
    24  	"github.com/juju/os/series"
    25  	jc "github.com/juju/testing/checkers"
    26  	"github.com/juju/utils"
    27  	"github.com/juju/utils/arch"
    28  	"github.com/juju/utils/cert"
    29  	"github.com/juju/utils/ssh"
    30  	sshtesting "github.com/juju/utils/ssh/testing"
    31  	"github.com/juju/utils/symlink"
    32  	"github.com/juju/version"
    33  	gc "gopkg.in/check.v1"
    34  	"gopkg.in/juju/charmrepo.v3"
    35  	"gopkg.in/juju/names.v2"
    36  	"gopkg.in/juju/worker.v1"
    37  	"gopkg.in/juju/worker.v1/dependency"
    38  	"gopkg.in/juju/worker.v1/workertest"
    39  	"gopkg.in/natefinch/lumberjack.v2"
    40  
    41  	"github.com/juju/juju/agent"
    42  	"github.com/juju/juju/api"
    43  	apimachiner "github.com/juju/juju/api/machiner"
    44  	"github.com/juju/juju/apiserver/params"
    45  	"github.com/juju/juju/cloud"
    46  	"github.com/juju/juju/cmd/jujud/agent/model"
    47  	"github.com/juju/juju/controller"
    48  	"github.com/juju/juju/core/auditlog"
    49  	"github.com/juju/juju/core/instance"
    50  	"github.com/juju/juju/core/migration"
    51  	"github.com/juju/juju/environs"
    52  	"github.com/juju/juju/environs/context"
    53  	envtesting "github.com/juju/juju/environs/testing"
    54  	"github.com/juju/juju/network"
    55  	"github.com/juju/juju/provider/dummy"
    56  	"github.com/juju/juju/state"
    57  	"github.com/juju/juju/state/multiwatcher"
    58  	"github.com/juju/juju/storage"
    59  	coretesting "github.com/juju/juju/testing"
    60  	"github.com/juju/juju/testing/factory"
    61  	"github.com/juju/juju/tools"
    62  	jujuversion "github.com/juju/juju/version"
    63  	jworker "github.com/juju/juju/worker"
    64  	"github.com/juju/juju/worker/authenticationworker"
    65  	"github.com/juju/juju/worker/diskmanager"
    66  	"github.com/juju/juju/worker/instancepoller"
    67  	"github.com/juju/juju/worker/machiner"
    68  	"github.com/juju/juju/worker/migrationmaster"
    69  	"github.com/juju/juju/worker/storageprovisioner"
    70  	"github.com/juju/juju/worker/upgrader"
    71  )
    72  
    73  type MachineLegacyLeasesSuite struct {
    74  	commonMachineSuite
    75  }
    76  
    77  var _ = gc.Suite(&MachineLegacyLeasesSuite{})
    78  
    79  func (s *MachineLegacyLeasesSuite) SetUpTest(c *gc.C) {
    80  	s.ControllerConfigAttrs = map[string]interface{}{
    81  		controller.AuditingEnabled: true,
    82  		controller.CharmStoreURL:   "staging.charmstore",
    83  		controller.Features:        []interface{}{"legacy-leases"},
    84  	}
    85  	s.commonMachineSuite.SetUpTest(c)
    86  	coretesting.DumpTestLogsAfter(time.Minute, c, s)
    87  }
    88  
    89  // TODO (manadart 2018-10-26): Tests that work with Raft leases should be
    90  // migrated to this suite.
    91  // When the Raft startup issue is resolved all tests can use this suite
    92  // and the MachineLegacyLeasesSuite can be removed.
    93  type MachineSuite struct {
    94  	commonMachineSuite
    95  }
    96  
    97  var _ = gc.Suite(&MachineSuite{})
    98  
    99  func (s *MachineSuite) SetUpTest(c *gc.C) {
   100  	s.ControllerConfigAttrs = map[string]interface{}{
   101  		controller.AuditingEnabled: true,
   102  		controller.CharmStoreURL:   "staging.charmstore",
   103  	}
   104  	s.commonMachineSuite.SetUpTest(c)
   105  	// Most of these tests normally finish sub-second on a fast machine.
   106  	// If any given test hits a minute, we have almost certainly become
   107  	// wedged, so dump the logs.
   108  	coretesting.DumpTestLogsAfter(time.Minute, c, s)
   109  }
   110  
   111  func (s *MachineLegacyLeasesSuite) TestParseNonsense(c *gc.C) {
   112  	for _, args := range [][]string{
   113  		{},
   114  		{"--machine-id", "-4004"},
   115  	} {
   116  		var agentConf agentConf
   117  		err := ParseAgentCommand(&machineAgentCmd{agentInitializer: &agentConf}, args)
   118  		c.Assert(err, gc.ErrorMatches, "--machine-id option must be set, and expects a non-negative integer")
   119  	}
   120  }
   121  
   122  func (s *MachineLegacyLeasesSuite) TestParseUnknown(c *gc.C) {
   123  	var agentConf agentConf
   124  	a := &machineAgentCmd{agentInitializer: &agentConf}
   125  	err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"})
   126  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blistering barnacles"\]`)
   127  }
   128  
   129  func (s *MachineLegacyLeasesSuite) TestParseSuccess(c *gc.C) {
   130  	create := func() (cmd.Command, AgentConf) {
   131  		agentConf := agentConf{dataDir: s.DataDir()}
   132  		logger := s.newBufferedLogWriter()
   133  		a := NewMachineAgentCmd(
   134  			nil,
   135  			NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()),
   136  			&agentConf,
   137  			&agentConf,
   138  		)
   139  		a.(*machineAgentCmd).logToStdErr = true
   140  
   141  		return a, &agentConf
   142  	}
   143  	a := CheckAgentCommand(c, create, []string{"--machine-id", "42"})
   144  	c.Assert(a.(*machineAgentCmd).machineId, gc.Equals, "42")
   145  }
   146  
   147  func (s *MachineLegacyLeasesSuite) TestRunInvalidMachineId(c *gc.C) {
   148  	c.Skip("agents don't yet distinguish between temporary and permanent errors")
   149  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   150  	err := s.newAgent(c, m).Run(nil)
   151  	c.Assert(err, gc.ErrorMatches, "some error")
   152  }
   153  
   154  func (s *MachineLegacyLeasesSuite) TestUseLumberjack(c *gc.C) {
   155  	ctx := cmdtesting.Context(c)
   156  	agentConf := FakeAgentConfig{}
   157  	logger := s.newBufferedLogWriter()
   158  
   159  	a := NewMachineAgentCmd(
   160  		ctx,
   161  		NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()),
   162  		agentConf,
   163  		agentConf,
   164  	)
   165  	// little hack to set the data that Init expects to already be set
   166  	a.(*machineAgentCmd).machineId = "42"
   167  
   168  	err := a.Init(nil)
   169  	c.Assert(err, gc.IsNil)
   170  
   171  	l, ok := ctx.Stderr.(*lumberjack.Logger)
   172  	c.Assert(ok, jc.IsTrue)
   173  	c.Check(l.MaxAge, gc.Equals, 0)
   174  	c.Check(l.MaxBackups, gc.Equals, 2)
   175  	c.Check(l.Filename, gc.Equals, filepath.FromSlash("/var/log/juju/machine-42.log"))
   176  	c.Check(l.MaxSize, gc.Equals, 300)
   177  }
   178  
   179  func (s *MachineLegacyLeasesSuite) TestDontUseLumberjack(c *gc.C) {
   180  	ctx := cmdtesting.Context(c)
   181  	agentConf := FakeAgentConfig{}
   182  	logger := s.newBufferedLogWriter()
   183  
   184  	a := NewMachineAgentCmd(
   185  		ctx,
   186  		NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()),
   187  		agentConf,
   188  		agentConf,
   189  	)
   190  	// little hack to set the data that Init expects to already be set
   191  	a.(*machineAgentCmd).machineId = "42"
   192  
   193  	// set the value that normally gets set by the flag parsing
   194  	a.(*machineAgentCmd).logToStdErr = true
   195  
   196  	err := a.Init(nil)
   197  	c.Assert(err, gc.IsNil)
   198  
   199  	_, ok := ctx.Stderr.(*lumberjack.Logger)
   200  	c.Assert(ok, jc.IsFalse)
   201  }
   202  
   203  func (s *MachineLegacyLeasesSuite) TestRunStop(c *gc.C) {
   204  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   205  	a := s.newAgent(c, m)
   206  	done := make(chan error)
   207  	go func() {
   208  		done <- a.Run(nil)
   209  	}()
   210  	err := a.Stop()
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(<-done, jc.ErrorIsNil)
   213  	c.Assert(charmrepo.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache"))
   214  }
   215  
   216  func (s *MachineLegacyLeasesSuite) TestWithDeadMachine(c *gc.C) {
   217  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   218  	err := m.EnsureDead()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	a := s.newAgent(c, m)
   221  	err = runWithTimeout(a)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  
   224  	_, err = os.Stat(ac.DataDir())
   225  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   226  }
   227  
   228  func (s *MachineLegacyLeasesSuite) TestWithRemovedMachine(c *gc.C) {
   229  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   230  	err := m.EnsureDead()
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	err = m.Remove()
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	a := s.newAgent(c, m)
   235  	err = runWithTimeout(a)
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	_, err = os.Stat(ac.DataDir())
   239  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   240  }
   241  
   242  func (s *MachineLegacyLeasesSuite) TestDyingMachine(c *gc.C) {
   243  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   244  	a := s.newAgent(c, m)
   245  	done := make(chan error)
   246  	go func() {
   247  		done <- a.Run(nil)
   248  	}()
   249  	defer func() {
   250  		c.Check(a.Stop(), jc.ErrorIsNil)
   251  	}()
   252  	// Wait for configuration to be finished
   253  	<-a.WorkersStarted()
   254  	err := m.Destroy()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	// Tearing down the dependency engine can take a non-trivial amount of
   257  	// time.
   258  	select {
   259  	case err := <-done:
   260  		c.Assert(err, jc.ErrorIsNil)
   261  	case <-time.After(coretesting.LongWait):
   262  		// This test intermittently fails and we haven't been able to determine
   263  		// why it gets wedged. So we will dump the goroutines before the fatal call.
   264  		buff := bytes.Buffer{}
   265  		err = pprof.Lookup("goroutine").WriteTo(&buff, 1)
   266  		c.Check(err, jc.ErrorIsNil)
   267  		c.Logf("\nagent didn't stop, here's what it was doing\n\n%s", buff)
   268  		c.Fatalf("timed out waiting for agent to terminate")
   269  	}
   270  	err = m.Refresh()
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	c.Assert(m.Life(), gc.Equals, state.Dead)
   273  }
   274  
   275  func (s *MachineLegacyLeasesSuite) TestManageModelRunsInstancePoller(c *gc.C) {
   276  	s.AgentSuite.PatchValue(&instancepoller.ShortPoll, 500*time.Millisecond)
   277  	usefulVersion := version.Binary{
   278  		Number: jujuversion.Current,
   279  		Arch:   arch.HostArch(),
   280  		Series: "quantal", // to match the charm created below
   281  	}
   282  	envtesting.AssertUploadFakeToolsVersions(
   283  		c, s.DefaultToolsStorage,
   284  		s.Environ.Config().AgentStream(),
   285  		s.Environ.Config().AgentStream(),
   286  		usefulVersion,
   287  	)
   288  	m, _, _ := s.primeAgent(c, state.JobManageModel)
   289  	a := s.newAgent(c, m)
   290  	defer a.Stop()
   291  	go func() {
   292  		c.Check(a.Run(nil), jc.ErrorIsNil)
   293  	}()
   294  
   295  	// Add one unit to an application;
   296  	charm := s.AddTestingCharm(c, "dummy")
   297  	app := s.AddTestingApplication(c, "test-application", charm)
   298  	unit, err := app.AddUnit(state.AddUnitParams{})
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   301  	c.Assert(err, jc.ErrorIsNil)
   302  
   303  	m, instId := s.waitProvisioned(c, unit)
   304  	insts, err := s.Environ.Instances(context.NewCloudCallContext(), []instance.Id{instId})
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	addrs := network.NewAddresses("1.2.3.4")
   307  	dummy.SetInstanceAddresses(insts[0], addrs)
   308  	dummy.SetInstanceStatus(insts[0], "running")
   309  
   310  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   311  		if !attempt.HasNext() {
   312  			c.Logf("final machine addresses: %#v", m.Addresses())
   313  			c.Fatalf("timed out waiting for machine to get address")
   314  		}
   315  		err := m.Refresh()
   316  		c.Assert(err, jc.ErrorIsNil)
   317  		instStatus, err := m.InstanceStatus()
   318  		c.Assert(err, jc.ErrorIsNil)
   319  		c.Logf("found status is %q %q", instStatus.Status, instStatus.Message)
   320  		if reflect.DeepEqual(m.Addresses(), addrs) && instStatus.Message == "running" {
   321  			c.Logf("machine %q address updated: %+v", m.Id(), addrs)
   322  			break
   323  		}
   324  		c.Logf("waiting for machine %q address to be updated", m.Id())
   325  	}
   326  }
   327  
   328  func (s *MachineLegacyLeasesSuite) TestCallsUseMultipleCPUs(c *gc.C) {
   329  	// All machine agents call UseMultipleCPUs.
   330  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   331  	calledChan := make(chan struct{}, 1)
   332  	s.AgentSuite.PatchValue(&useMultipleCPUs, func() { calledChan <- struct{}{} })
   333  	a := s.newAgent(c, m)
   334  	defer a.Stop()
   335  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   336  
   337  	// Wait for configuration to be finished
   338  	<-a.WorkersStarted()
   339  	s.assertChannelActive(c, calledChan, "UseMultipleCPUs() to be called")
   340  	c.Check(a.Stop(), jc.ErrorIsNil)
   341  }
   342  
   343  func (s *MachineLegacyLeasesSuite) waitProvisioned(c *gc.C, unit *state.Unit) (*state.Machine, instance.Id) {
   344  	c.Logf("waiting for unit %q to be provisioned", unit)
   345  	machineId, err := unit.AssignedMachineId()
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	m, err := s.State.Machine(machineId)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	w := m.Watch()
   350  	defer worker.Stop(w)
   351  	timeout := time.After(coretesting.LongWait)
   352  	for {
   353  		select {
   354  		case <-timeout:
   355  			c.Fatalf("timed out waiting for provisioning")
   356  		case <-time.After(coretesting.ShortWait):
   357  			s.State.StartSync()
   358  		case _, ok := <-w.Changes():
   359  			c.Assert(ok, jc.IsTrue)
   360  			err := m.Refresh()
   361  			c.Assert(err, jc.ErrorIsNil)
   362  			if instId, err := m.InstanceId(); err == nil {
   363  				c.Logf("unit provisioned with instance %s", instId)
   364  				return m, instId
   365  			} else {
   366  				c.Check(err, jc.Satisfies, errors.IsNotProvisioned)
   367  			}
   368  		}
   369  	}
   370  }
   371  
   372  func (s *MachineLegacyLeasesSuite) testUpgradeRequest(c *gc.C, agent runner, tag string, currentTools *tools.Tools) {
   373  	newVers := version.Binary{
   374  		Number: jujuversion.Current,
   375  		Arch:   arch.HostArch(),
   376  		Series: series.MustHostSeries(),
   377  	}
   378  	newVers.Patch++
   379  	newTools := envtesting.AssertUploadFakeToolsVersions(
   380  		c, s.DefaultToolsStorage, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), newVers)[0]
   381  	err := s.State.SetModelAgentVersion(newVers.Number, true)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	err = runWithTimeout(agent)
   384  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   385  		AgentName: tag,
   386  		OldTools:  currentTools.Version,
   387  		NewTools:  newTools.Version,
   388  		DataDir:   s.DataDir(),
   389  	})
   390  }
   391  
   392  func (s *MachineLegacyLeasesSuite) TestUpgradeRequest(c *gc.C) {
   393  	m, _, currentTools := s.primeAgent(c, state.JobManageModel, state.JobHostUnits)
   394  	a := s.newAgent(c, m)
   395  	s.testUpgradeRequest(c, a, m.Tag().String(), currentTools)
   396  	c.Assert(a.initialUpgradeCheckComplete.IsUnlocked(), jc.IsFalse)
   397  }
   398  
   399  func (s *MachineLegacyLeasesSuite) TestNoUpgradeRequired(c *gc.C) {
   400  	m, _, _ := s.primeAgent(c, state.JobManageModel, state.JobHostUnits)
   401  	a := s.newAgent(c, m)
   402  	done := make(chan error)
   403  	go func() { done <- a.Run(nil) }()
   404  	select {
   405  	case <-a.initialUpgradeCheckComplete.Unlocked():
   406  	case <-time.After(coretesting.LongWait):
   407  		c.Fatalf("timeout waiting for upgrade check")
   408  	}
   409  	defer a.Stop() // in case of failure
   410  	s.waitStopped(c, state.JobManageModel, a, done)
   411  	c.Assert(a.initialUpgradeCheckComplete.IsUnlocked(), jc.IsTrue)
   412  }
   413  
   414  func (s *MachineLegacyLeasesSuite) waitStopped(c *gc.C, job state.MachineJob, a *MachineAgent, done chan error) {
   415  	err := a.Stop()
   416  	if job == state.JobManageModel {
   417  		// When shutting down, the API server can be shut down before
   418  		// the other workers that connect to it, so they get an error so
   419  		// they then die, causing Stop to return an error.  It's not
   420  		// easy to control the actual error that's received in this
   421  		// circumstance so we just log it rather than asserting that it
   422  		// is not nil.
   423  		if err != nil {
   424  			c.Logf("error shutting down state manager: %v", err)
   425  		}
   426  	} else {
   427  		c.Assert(err, jc.ErrorIsNil)
   428  	}
   429  
   430  	select {
   431  	case err := <-done:
   432  		c.Assert(err, jc.ErrorIsNil)
   433  	case <-time.After(coretesting.LongWait):
   434  		c.Fatalf("timed out waiting for agent to terminate")
   435  	}
   436  }
   437  
   438  func (s *MachineLegacyLeasesSuite) assertJobWithState(
   439  	c *gc.C,
   440  	job state.MachineJob,
   441  	test func(agent.Config, *state.State),
   442  ) {
   443  	paramsJob := job.ToParams()
   444  	if !paramsJob.NeedsState() {
   445  		c.Fatalf("%v does not use state", paramsJob)
   446  	}
   447  	s.assertAgentOpensState(c, job, test)
   448  }
   449  
   450  // assertAgentOpensState asserts that a machine agent started with the
   451  // given job. The agent's configuration and the agent's state.State are
   452  // then passed to the test function for further checking.
   453  func (s *MachineLegacyLeasesSuite) assertAgentOpensState(c *gc.C, job state.MachineJob, test func(agent.Config, *state.State)) {
   454  	stm, conf, _ := s.primeAgent(c, job)
   455  	a := s.newAgent(c, stm)
   456  	defer a.Stop()
   457  	logger.Debugf("new agent %#v", a)
   458  
   459  	// All state jobs currently also run an APIWorker, so no
   460  	// need to check for that here, like in assertJobWithState.
   461  	st, done := s.waitForOpenState(c, a)
   462  	test(conf, st)
   463  	s.waitStopped(c, job, a, done)
   464  }
   465  
   466  func (s *MachineLegacyLeasesSuite) waitForOpenState(c *gc.C, a *MachineAgent) (*state.State, chan error) {
   467  	agentAPIs := make(chan *state.State, 1)
   468  	s.AgentSuite.PatchValue(&reportOpenedState, func(st *state.State) {
   469  		select {
   470  		case agentAPIs <- st:
   471  		default:
   472  		}
   473  	})
   474  
   475  	done := make(chan error)
   476  	go func() {
   477  		done <- a.Run(nil)
   478  	}()
   479  
   480  	select {
   481  	case agentAPI := <-agentAPIs:
   482  		c.Assert(agentAPI, gc.NotNil)
   483  		return agentAPI, done
   484  	case <-time.After(coretesting.LongWait):
   485  		c.Fatalf("API not opened")
   486  	}
   487  	panic("can't happen")
   488  }
   489  
   490  func (s *MachineLegacyLeasesSuite) TestManageModelServesAPI(c *gc.C) {
   491  	s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) {
   492  		apiInfo, ok := conf.APIInfo()
   493  		c.Assert(ok, jc.IsTrue)
   494  		st, err := api.Open(apiInfo, fastDialOpts)
   495  		c.Assert(err, jc.ErrorIsNil)
   496  		defer st.Close()
   497  		m, err := apimachiner.NewState(st).Machine(conf.Tag().(names.MachineTag))
   498  		c.Assert(err, jc.ErrorIsNil)
   499  		c.Assert(m.Life(), gc.Equals, params.Alive)
   500  	})
   501  }
   502  
   503  func (s *MachineLegacyLeasesSuite) TestManageModelAuditsAPI(c *gc.C) {
   504  	password := "shhh..."
   505  	user := s.Factory.MakeUser(c, &factory.UserParams{
   506  		Password: password,
   507  	})
   508  
   509  	err := s.State.UpdateControllerConfig(map[string]interface{}{
   510  		"audit-log-exclude-methods": []interface{}{"Client.FullStatus"},
   511  	}, nil)
   512  	c.Assert(err, jc.ErrorIsNil)
   513  
   514  	s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) {
   515  		logPath := filepath.Join(conf.LogDir(), "audit.log")
   516  
   517  		makeAPIRequest := func(doRequest func(*api.Client)) {
   518  			apiInfo, ok := conf.APIInfo()
   519  			c.Assert(ok, jc.IsTrue)
   520  			apiInfo.Tag = user.Tag()
   521  			apiInfo.Password = password
   522  			st, err := api.Open(apiInfo, fastDialOpts)
   523  			c.Assert(err, jc.ErrorIsNil)
   524  			defer st.Close()
   525  			doRequest(st.Client())
   526  		}
   527  
   528  		// Make requests in separate API connections so they're separate conversations.
   529  		makeAPIRequest(func(client *api.Client) {
   530  			_, err = client.Status(nil)
   531  			c.Assert(err, jc.ErrorIsNil)
   532  		})
   533  		makeAPIRequest(func(client *api.Client) {
   534  			_, err = client.AddMachines([]params.AddMachineParams{{
   535  				Jobs: []multiwatcher.MachineJob{"JobHostUnits"},
   536  			}})
   537  			c.Assert(err, jc.ErrorIsNil)
   538  		})
   539  
   540  		// Check that there's a call to Client.AddMachinesV2 in the
   541  		// log, but no call to Client.FullStatus.
   542  		records := readAuditLog(c, logPath)
   543  		c.Assert(records, gc.HasLen, 3)
   544  		c.Assert(records[1].Request, gc.NotNil)
   545  		c.Assert(records[1].Request.Facade, gc.Equals, "Client")
   546  		c.Assert(records[1].Request.Method, gc.Equals, "AddMachinesV2")
   547  
   548  		// Now update the controller config to remove the exclusion.
   549  		err := s.State.UpdateControllerConfig(map[string]interface{}{
   550  			"audit-log-exclude-methods": []interface{}{},
   551  		}, nil)
   552  		c.Assert(err, jc.ErrorIsNil)
   553  
   554  		prevRecords := len(records)
   555  
   556  		// We might need to wait until the controller config change is
   557  		// propagated to the apiserver.
   558  		for a := coretesting.LongAttempt.Start(); a.Next(); {
   559  			makeAPIRequest(func(client *api.Client) {
   560  				_, err = client.Status(nil)
   561  				c.Assert(err, jc.ErrorIsNil)
   562  			})
   563  			// Check to see whether there are more logged requests.
   564  			records = readAuditLog(c, logPath)
   565  			if prevRecords < len(records) {
   566  				break
   567  			}
   568  		}
   569  		// Now there should also be a call to Client.FullStatus (and a response).
   570  		lastRequest := records[len(records)-2]
   571  		c.Assert(lastRequest.Request, gc.NotNil)
   572  		c.Assert(lastRequest.Request.Facade, gc.Equals, "Client")
   573  		c.Assert(lastRequest.Request.Method, gc.Equals, "FullStatus")
   574  	})
   575  }
   576  
   577  func readAuditLog(c *gc.C, logPath string) []auditlog.Record {
   578  	file, err := os.Open(logPath)
   579  	c.Assert(err, jc.ErrorIsNil)
   580  	defer file.Close()
   581  
   582  	scanner := bufio.NewScanner(file)
   583  	var results []auditlog.Record
   584  	for scanner.Scan() {
   585  		var record auditlog.Record
   586  		err := json.Unmarshal(scanner.Bytes(), &record)
   587  		c.Assert(err, jc.ErrorIsNil)
   588  		results = append(results, record)
   589  	}
   590  	return results
   591  }
   592  
   593  func (s *MachineLegacyLeasesSuite) assertAgentSetsToolsVersion(c *gc.C, job state.MachineJob) {
   594  	vers := version.Binary{
   595  		Number: jujuversion.Current,
   596  		Arch:   arch.HostArch(),
   597  		Series: series.MustHostSeries(),
   598  	}
   599  	vers.Minor++
   600  	m, _, _ := s.primeAgentVersion(c, vers, job)
   601  	a := s.newAgent(c, m)
   602  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   603  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   604  
   605  	timeout := time.After(coretesting.LongWait)
   606  	for done := false; !done; {
   607  		select {
   608  		case <-timeout:
   609  			c.Fatalf("timeout while waiting for agent version to be set")
   610  		case <-time.After(coretesting.ShortWait):
   611  			c.Log("Refreshing")
   612  			err := m.Refresh()
   613  			c.Assert(err, jc.ErrorIsNil)
   614  			c.Log("Fetching agent tools")
   615  			agentTools, err := m.AgentTools()
   616  			c.Assert(err, jc.ErrorIsNil)
   617  			c.Logf("(%v vs. %v)", agentTools.Version, jujuversion.Current)
   618  			if agentTools.Version.Minor != jujuversion.Current.Minor {
   619  				continue
   620  			}
   621  			c.Assert(agentTools.Version.Number, gc.DeepEquals, jujuversion.Current)
   622  			done = true
   623  		}
   624  	}
   625  }
   626  
   627  func (s *MachineLegacyLeasesSuite) TestAgentSetsToolsVersionManageModel(c *gc.C) {
   628  	s.assertAgentSetsToolsVersion(c, state.JobManageModel)
   629  }
   630  
   631  func (s *MachineLegacyLeasesSuite) TestAgentSetsToolsVersionHostUnits(c *gc.C) {
   632  	s.assertAgentSetsToolsVersion(c, state.JobHostUnits)
   633  }
   634  
   635  func (s *MachineLegacyLeasesSuite) TestManageModelRunsCleaner(c *gc.C) {
   636  	s.assertJobWithState(c, state.JobManageModel, func(conf agent.Config, agentState *state.State) {
   637  		// Create an application and unit, and destroy the app.
   638  		app := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   639  		unit, err := app.AddUnit(state.AddUnitParams{})
   640  		c.Assert(err, jc.ErrorIsNil)
   641  		err = app.Destroy()
   642  		c.Assert(err, jc.ErrorIsNil)
   643  
   644  		// Check the unit was not yet removed.
   645  		err = unit.Refresh()
   646  		c.Assert(err, jc.ErrorIsNil)
   647  		w := unit.Watch()
   648  		defer worker.Stop(w)
   649  
   650  		// Trigger a sync on the state used by the agent, and wait
   651  		// for the unit to be removed.
   652  		agentState.StartSync()
   653  		timeout := time.After(coretesting.LongWait)
   654  		for done := false; !done; {
   655  			select {
   656  			case <-timeout:
   657  				c.Fatalf("unit not cleaned up")
   658  			case <-time.After(coretesting.ShortWait):
   659  				s.State.StartSync()
   660  			case <-w.Changes():
   661  				err := unit.Refresh()
   662  				if errors.IsNotFound(err) {
   663  					done = true
   664  				} else {
   665  					c.Assert(err, jc.ErrorIsNil)
   666  				}
   667  			}
   668  		}
   669  	})
   670  }
   671  
   672  func (s *MachineLegacyLeasesSuite) TestJobManageModelRunsMinUnitsWorker(c *gc.C) {
   673  	s.assertJobWithState(c, state.JobManageModel, func(_ agent.Config, agentState *state.State) {
   674  		// Ensure that the MinUnits worker is alive by doing a simple check
   675  		// that it responds to state changes: add an application, set its minimum
   676  		// number of units to one, wait for the worker to add the missing unit.
   677  		app := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   678  		err := app.SetMinUnits(1)
   679  		c.Assert(err, jc.ErrorIsNil)
   680  		w := app.Watch()
   681  		defer worker.Stop(w)
   682  
   683  		// Trigger a sync on the state used by the agent, and wait for the unit
   684  		// to be created.
   685  		agentState.StartSync()
   686  		timeout := time.After(coretesting.LongWait)
   687  		for {
   688  			select {
   689  			case <-timeout:
   690  				c.Fatalf("unit not created")
   691  			case <-time.After(coretesting.ShortWait):
   692  				s.State.StartSync()
   693  			case <-w.Changes():
   694  				units, err := app.AllUnits()
   695  				c.Assert(err, jc.ErrorIsNil)
   696  				if len(units) == 1 {
   697  					return
   698  				}
   699  			}
   700  		}
   701  	})
   702  }
   703  
   704  func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsAuthorisedKeysWorker(c *gc.C) {
   705  	//TODO(bogdanteleaga): Fix once we get authentication worker up on windows
   706  	if runtime.GOOS == "windows" {
   707  		c.Skip("bug 1403084: authentication worker not yet implemented on windows")
   708  	}
   709  	// Start the machine agent.
   710  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   711  	a := s.newAgent(c, m)
   712  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   713  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   714  
   715  	// Update the keys in the environment.
   716  	sshKey := sshtesting.ValidKeyOne.Key + " user@host"
   717  	err := s.Model.UpdateModelConfig(map[string]interface{}{"authorized-keys": sshKey}, nil)
   718  	c.Assert(err, jc.ErrorIsNil)
   719  
   720  	// Wait for ssh keys file to be updated.
   721  	s.State.StartSync()
   722  	timeout := time.After(coretesting.LongWait)
   723  	sshKeyWithCommentPrefix := sshtesting.ValidKeyOne.Key + " Juju:user@host"
   724  	for {
   725  		select {
   726  		case <-timeout:
   727  			c.Fatalf("timeout while waiting for authorised ssh keys to change")
   728  		case <-time.After(coretesting.ShortWait):
   729  			s.State.StartSync()
   730  			keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys)
   731  			c.Assert(err, jc.ErrorIsNil)
   732  			keysStr := strings.Join(keys, "\n")
   733  			if sshKeyWithCommentPrefix != keysStr {
   734  				continue
   735  			}
   736  			return
   737  		}
   738  	}
   739  }
   740  
   741  func (s *MachineLegacyLeasesSuite) TestMachineAgentSymlinks(c *gc.C) {
   742  	stm, _, _ := s.primeAgent(c, state.JobManageModel)
   743  	a := s.newAgent(c, stm)
   744  	defer a.Stop()
   745  	_, done := s.waitForOpenState(c, a)
   746  
   747  	// Symlinks should have been created
   748  	for _, link := range jujudSymlinks {
   749  		_, err := os.Stat(utils.EnsureBaseDir(a.rootDir, link))
   750  		c.Assert(err, jc.ErrorIsNil, gc.Commentf(link))
   751  	}
   752  
   753  	s.waitStopped(c, state.JobManageModel, a, done)
   754  }
   755  
   756  func (s *MachineLegacyLeasesSuite) TestMachineAgentSymlinkJujuRunExists(c *gc.C) {
   757  	if runtime.GOOS == "windows" {
   758  		// Cannot make symlink to nonexistent file on windows or
   759  		// create a file point a symlink to it then remove it
   760  		c.Skip("Cannot test this on windows")
   761  	}
   762  
   763  	stm, _, _ := s.primeAgent(c, state.JobManageModel)
   764  	a := s.newAgent(c, stm)
   765  	defer a.Stop()
   766  
   767  	// Pre-create the symlinks, but pointing to the incorrect location.
   768  	a.rootDir = c.MkDir()
   769  	for _, link := range jujudSymlinks {
   770  		fullLink := utils.EnsureBaseDir(a.rootDir, link)
   771  		c.Assert(os.MkdirAll(filepath.Dir(fullLink), os.FileMode(0755)), jc.ErrorIsNil)
   772  		c.Assert(symlink.New("/nowhere/special", fullLink), jc.ErrorIsNil, gc.Commentf(link))
   773  	}
   774  
   775  	// Start the agent and wait for it be running.
   776  	_, done := s.waitForOpenState(c, a)
   777  
   778  	// juju-run symlink should have been recreated.
   779  	for _, link := range jujudSymlinks {
   780  		fullLink := utils.EnsureBaseDir(a.rootDir, link)
   781  		linkTarget, err := symlink.Read(fullLink)
   782  		c.Assert(err, jc.ErrorIsNil)
   783  		c.Assert(linkTarget, gc.Not(gc.Equals), "/nowhere/special", gc.Commentf(link))
   784  	}
   785  
   786  	s.waitStopped(c, state.JobManageModel, a, done)
   787  }
   788  
   789  func (s *MachineLegacyLeasesSuite) TestMachineAgentUninstall(c *gc.C) {
   790  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   791  	err := m.EnsureDead()
   792  	c.Assert(err, jc.ErrorIsNil)
   793  	a := s.newAgent(c, m)
   794  	err = runWithTimeout(a)
   795  	c.Assert(err, jc.ErrorIsNil)
   796  
   797  	// juju-* symlinks should have been removed on termination.
   798  	for _, link := range []string{jujuRun, jujuDumpLogs, jujuIntrospect} {
   799  		_, err = os.Stat(utils.EnsureBaseDir(a.rootDir, link))
   800  		c.Assert(err, jc.Satisfies, os.IsNotExist)
   801  	}
   802  
   803  	// data-dir should have been removed on termination
   804  	_, err = os.Stat(ac.DataDir())
   805  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   806  }
   807  
   808  func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsAPIAddressUpdaterWorker(c *gc.C) {
   809  	// Start the machine agent.
   810  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   811  	a := s.newAgent(c, m)
   812  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   813  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   814  
   815  	// Update the API addresses.
   816  	updatedServers := [][]network.HostPort{
   817  		network.NewHostPorts(1234, "localhost"),
   818  	}
   819  	err := s.BackingState.SetAPIHostPorts(updatedServers)
   820  	c.Assert(err, jc.ErrorIsNil)
   821  
   822  	// Wait for config to be updated.
   823  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   824  		s.BackingState.StartSync()
   825  		if !attempt.HasNext() {
   826  			break
   827  		}
   828  		addrs, err := a.CurrentConfig().APIAddresses()
   829  		c.Assert(err, jc.ErrorIsNil)
   830  		if reflect.DeepEqual(addrs, []string{"localhost:1234"}) {
   831  			return
   832  		}
   833  	}
   834  	c.Fatalf("timeout while waiting for agent config to change")
   835  }
   836  
   837  func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsDiskManagerWorker(c *gc.C) {
   838  	// Patch out the worker func before starting the agent.
   839  	started := newSignal()
   840  	newWorker := func(diskmanager.ListBlockDevicesFunc, diskmanager.BlockDeviceSetter) worker.Worker {
   841  		started.trigger()
   842  		return jworker.NewNoOpWorker()
   843  	}
   844  	s.PatchValue(&diskmanager.NewWorker, newWorker)
   845  
   846  	// Start the machine agent.
   847  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   848  	a := s.newAgent(c, m)
   849  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   850  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   851  	started.assertTriggered(c, "diskmanager worker to start")
   852  }
   853  
   854  func (s *MachineLegacyLeasesSuite) TestDiskManagerWorkerUpdatesState(c *gc.C) {
   855  	expected := []storage.BlockDevice{{DeviceName: "whatever"}}
   856  	s.PatchValue(&diskmanager.DefaultListBlockDevices, func() ([]storage.BlockDevice, error) {
   857  		return expected, nil
   858  	})
   859  
   860  	// Start the machine agent.
   861  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   862  	a := s.newAgent(c, m)
   863  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   864  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   865  
   866  	sb, err := state.NewStorageBackend(s.BackingState)
   867  	c.Assert(err, jc.ErrorIsNil)
   868  
   869  	// Wait for state to be updated.
   870  	s.BackingState.StartSync()
   871  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   872  		devices, err := sb.BlockDevices(m.MachineTag())
   873  		c.Assert(err, jc.ErrorIsNil)
   874  		if len(devices) > 0 {
   875  			c.Assert(devices, gc.HasLen, 1)
   876  			c.Assert(devices[0].DeviceName, gc.Equals, expected[0].DeviceName)
   877  			return
   878  		}
   879  	}
   880  	c.Fatalf("timeout while waiting for block devices to be recorded")
   881  }
   882  
   883  func (s *MachineLegacyLeasesSuite) TestMachineAgentRunsMachineStorageWorker(c *gc.C) {
   884  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   885  
   886  	started := newSignal()
   887  	newWorker := func(config storageprovisioner.Config) (worker.Worker, error) {
   888  		c.Check(config.Scope, gc.Equals, m.Tag())
   889  		c.Check(config.Validate(), jc.ErrorIsNil)
   890  		started.trigger()
   891  		return jworker.NewNoOpWorker(), nil
   892  	}
   893  	s.PatchValue(&storageprovisioner.NewStorageProvisioner, newWorker)
   894  
   895  	// Start the machine agent.
   896  	a := s.newAgent(c, m)
   897  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   898  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   899  	started.assertTriggered(c, "storage worker to start")
   900  }
   901  
   902  func (s *MachineLegacyLeasesSuite) TestCertificateDNSUpdated(c *gc.C) {
   903  	m, _, _ := s.primeAgent(c, state.JobManageModel)
   904  	a := s.newAgent(c, m)
   905  	s.testCertificateDNSUpdated(c, a)
   906  }
   907  
   908  func (s *MachineLegacyLeasesSuite) TestCertificateDNSUpdatedInvalidPrivateKey(c *gc.C) {
   909  	m, agentConfig, _ := s.primeAgent(c, state.JobManageModel)
   910  
   911  	// Write out config with an invalid private key. This should
   912  	// cause the agent to rewrite the cert and key.
   913  	si, ok := agentConfig.StateServingInfo()
   914  	c.Assert(ok, jc.IsTrue)
   915  	si.PrivateKey = "foo"
   916  	agentConfig.SetStateServingInfo(si)
   917  	err := agentConfig.Write()
   918  	c.Assert(err, jc.ErrorIsNil)
   919  
   920  	a := s.newAgent(c, m)
   921  	s.testCertificateDNSUpdated(c, a)
   922  }
   923  
   924  func (s *MachineLegacyLeasesSuite) testCertificateDNSUpdated(c *gc.C, a *MachineAgent) {
   925  	// Set up a channel which fires when State is opened.
   926  	started := make(chan struct{}, 16)
   927  	s.PatchValue(&reportOpenedState, func(*state.State) {
   928  		started <- struct{}{}
   929  	})
   930  
   931  	// Start the agent.
   932  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
   933  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
   934  
   935  	// Wait for State to be opened. Once this occurs we know that the
   936  	// agent's initial startup has happened.
   937  	s.assertChannelActive(c, started, "agent to start up")
   938  
   939  	// Check that certificate was updated when the agent started.
   940  	stateInfo, _ := a.CurrentConfig().StateServingInfo()
   941  	srvCert, _, err := cert.ParseCertAndKey(stateInfo.Cert, stateInfo.PrivateKey)
   942  	c.Assert(err, jc.ErrorIsNil)
   943  	expectedDnsNames := set.NewStrings("localhost", "juju-apiserver", "juju-mongodb")
   944  	certDnsNames := set.NewStrings(srvCert.DNSNames...)
   945  	c.Check(expectedDnsNames.Difference(certDnsNames).IsEmpty(), jc.IsTrue)
   946  
   947  	// Check the mongo certificate file too.
   948  	pemContent, err := ioutil.ReadFile(filepath.Join(s.DataDir(), "server.pem"))
   949  	c.Assert(err, jc.ErrorIsNil)
   950  	c.Check(string(pemContent), gc.Equals, stateInfo.Cert+"\n"+stateInfo.PrivateKey)
   951  }
   952  
   953  func (s *MachineLegacyLeasesSuite) setupIgnoreAddresses(c *gc.C, expectedIgnoreValue bool) chan bool {
   954  	ignoreAddressCh := make(chan bool, 1)
   955  	s.AgentSuite.PatchValue(&machiner.NewMachiner, func(cfg machiner.Config) (worker.Worker, error) {
   956  		select {
   957  		case ignoreAddressCh <- cfg.ClearMachineAddressesOnStart:
   958  		default:
   959  		}
   960  
   961  		// The test just cares that NewMachiner is called with the correct
   962  		// value, nothing else is done with the worker.
   963  		return newDummyWorker(), nil
   964  	})
   965  
   966  	attrs := coretesting.Attrs{"ignore-machine-addresses": expectedIgnoreValue}
   967  	err := s.Model.UpdateModelConfig(attrs, nil)
   968  	c.Assert(err, jc.ErrorIsNil)
   969  	return ignoreAddressCh
   970  }
   971  
   972  func (s *MachineLegacyLeasesSuite) TestMachineAgentIgnoreAddresses(c *gc.C) {
   973  	for _, expectedIgnoreValue := range []bool{true, false} {
   974  		ignoreAddressCh := s.setupIgnoreAddresses(c, expectedIgnoreValue)
   975  
   976  		m, _, _ := s.primeAgent(c, state.JobHostUnits)
   977  		a := s.newAgent(c, m)
   978  		defer a.Stop()
   979  		doneCh := make(chan error)
   980  		go func() {
   981  			doneCh <- a.Run(nil)
   982  		}()
   983  
   984  		select {
   985  		case ignoreMachineAddresses := <-ignoreAddressCh:
   986  			if ignoreMachineAddresses != expectedIgnoreValue {
   987  				c.Fatalf("expected ignore-machine-addresses = %v, got = %v", expectedIgnoreValue, ignoreMachineAddresses)
   988  			}
   989  		case <-time.After(coretesting.LongWait):
   990  			c.Fatalf("timed out waiting for the machiner to start")
   991  		}
   992  		s.waitStopped(c, state.JobHostUnits, a, doneCh)
   993  	}
   994  }
   995  
   996  func (s *MachineLegacyLeasesSuite) TestMachineAgentIgnoreAddressesContainer(c *gc.C) {
   997  	ignoreAddressCh := s.setupIgnoreAddresses(c, true)
   998  
   999  	parent, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1000  	c.Assert(err, jc.ErrorIsNil)
  1001  	m, err := s.State.AddMachineInsideMachine(
  1002  		state.MachineTemplate{
  1003  			Series: "trusty",
  1004  			Jobs:   []state.MachineJob{state.JobHostUnits},
  1005  		},
  1006  		parent.Id(),
  1007  		instance.LXD,
  1008  	)
  1009  	c.Assert(err, jc.ErrorIsNil)
  1010  
  1011  	vers := version.Binary{
  1012  		Number: jujuversion.Current,
  1013  		Arch:   arch.HostArch(),
  1014  		Series: series.MustHostSeries(),
  1015  	}
  1016  	s.primeAgentWithMachine(c, m, vers)
  1017  	a := s.newAgent(c, m)
  1018  	defer a.Stop()
  1019  	doneCh := make(chan error)
  1020  	go func() {
  1021  		doneCh <- a.Run(nil)
  1022  	}()
  1023  
  1024  	select {
  1025  	case ignoreMachineAddresses := <-ignoreAddressCh:
  1026  		if ignoreMachineAddresses {
  1027  			c.Fatalf("expected ignore-machine-addresses = false, got = true")
  1028  		}
  1029  	case <-time.After(coretesting.LongWait):
  1030  		c.Fatalf("timed out waiting for the machiner to start")
  1031  	}
  1032  	s.waitStopped(c, state.JobHostUnits, a, doneCh)
  1033  }
  1034  
  1035  func (s *MachineSuite) TestMachineWorkers(c *gc.C) {
  1036  	s.ControllerConfigAttrs = map[string]interface{}{
  1037  		controller.AuditingEnabled: true,
  1038  		controller.CharmStoreURL:   "staging.charmstore",
  1039  	}
  1040  
  1041  	tracker := agenttest.NewEngineTracker()
  1042  	instrumented := TrackMachines(c, tracker, machineManifolds)
  1043  	s.PatchValue(&machineManifolds, instrumented)
  1044  
  1045  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
  1046  	a := s.newAgent(c, m)
  1047  	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
  1048  	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
  1049  
  1050  	// Wait for it to stabilise, running as normal.
  1051  	matcher := agenttest.NewWorkerMatcher(c, tracker, a.Tag().String(),
  1052  		append(alwaysMachineWorkers, notMigratingMachineWorkers...))
  1053  	agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync)
  1054  }
  1055  
  1056  func (s *MachineLegacyLeasesSuite) TestControllerModelWorkers(c *gc.C) {
  1057  	uuid := s.BackingState.ModelUUID()
  1058  
  1059  	tracker := agenttest.NewEngineTracker()
  1060  	instrumented := TrackModels(c, tracker, iaasModelManifolds)
  1061  	s.PatchValue(&iaasModelManifolds, instrumented)
  1062  
  1063  	expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...)
  1064  
  1065  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, expectedWorkers)
  1066  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1067  		agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync)
  1068  	})
  1069  }
  1070  
  1071  func (s *MachineLegacyLeasesSuite) TestHostedModelWorkers(c *gc.C) {
  1072  	// The dummy provider blows up in the face of multi-model
  1073  	// scenarios so patch in a minimal environs.Environ that's good
  1074  	// enough to allow the model workers to run.
  1075  	s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) {
  1076  		return &minModelWorkersEnviron{}, nil
  1077  	})
  1078  
  1079  	st, closer := s.setUpNewModel(c)
  1080  	defer closer()
  1081  
  1082  	uuid := st.ModelUUID()
  1083  
  1084  	tracker := agenttest.NewEngineTracker()
  1085  	instrumented := TrackModels(c, tracker, iaasModelManifolds)
  1086  	s.PatchValue(&iaasModelManifolds, instrumented)
  1087  
  1088  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid,
  1089  		append(alwaysModelWorkers, aliveModelWorkers...))
  1090  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1091  		agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync)
  1092  	})
  1093  }
  1094  
  1095  func (s *MachineLegacyLeasesSuite) TestWorkersForHostedModelWithInvalidCredential(c *gc.C) {
  1096  	// The dummy provider blows up in the face of multi-model
  1097  	// scenarios so patch in a minimal environs.Environ that's good
  1098  	// enough to allow the model workers to run.
  1099  	s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) {
  1100  		return &minModelWorkersEnviron{}, nil
  1101  	})
  1102  
  1103  	st := s.Factory.MakeModel(c, &factory.ModelParams{
  1104  		ConfigAttrs: coretesting.Attrs{
  1105  			"max-status-history-age":  "2h",
  1106  			"max-status-history-size": "4M",
  1107  			"max-action-results-age":  "2h",
  1108  			"max-action-results-size": "4M",
  1109  		},
  1110  		CloudCredential: names.NewCloudCredentialTag("dummy/admin/cred"),
  1111  	})
  1112  	defer func() {
  1113  		err := st.Close()
  1114  		c.Check(err, jc.ErrorIsNil)
  1115  	}()
  1116  
  1117  	uuid := st.ModelUUID()
  1118  
  1119  	// invalidate cloud credential for this model
  1120  	err := st.InvalidateModelCredential("coz i can")
  1121  	c.Assert(err, jc.ErrorIsNil)
  1122  
  1123  	tracker := agenttest.NewEngineTracker()
  1124  	instrumented := TrackModels(c, tracker, iaasModelManifolds)
  1125  	s.PatchValue(&iaasModelManifolds, instrumented)
  1126  
  1127  	expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...)
  1128  	// Since this model's cloud credential is no longer valid,
  1129  	// only the workers that don't require a valid credential should remain.
  1130  	remainingWorkers := set.NewStrings(expectedWorkers...).Difference(
  1131  		set.NewStrings(requireValidCredentialModelWorkers...))
  1132  
  1133  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, remainingWorkers.SortedValues())
  1134  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1135  		agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync)
  1136  	})
  1137  }
  1138  
  1139  func (s *MachineLegacyLeasesSuite) TestWorkersForHostedModelWithDeletedCredential(c *gc.C) {
  1140  	// The dummy provider blows up in the face of multi-model
  1141  	// scenarios so patch in a minimal environs.Environ that's good
  1142  	// enough to allow the model workers to run.
  1143  	s.PatchValue(&newEnvirons, func(environs.OpenParams) (environs.Environ, error) {
  1144  		return &minModelWorkersEnviron{}, nil
  1145  	})
  1146  
  1147  	credentialTag := names.NewCloudCredentialTag("dummy/admin/another")
  1148  	err := s.State.UpdateCloudCredential(credentialTag, cloud.NewCredential(cloud.UserPassAuthType, nil))
  1149  	c.Assert(err, jc.ErrorIsNil)
  1150  
  1151  	st := s.Factory.MakeModel(c, &factory.ModelParams{
  1152  		ConfigAttrs: coretesting.Attrs{
  1153  			"max-status-history-age":  "2h",
  1154  			"max-status-history-size": "4M",
  1155  			"max-action-results-age":  "2h",
  1156  			"max-action-results-size": "4M",
  1157  		},
  1158  		CloudCredential: credentialTag,
  1159  	})
  1160  	defer func() {
  1161  		err := st.Close()
  1162  		c.Check(err, jc.ErrorIsNil)
  1163  	}()
  1164  
  1165  	uuid := st.ModelUUID()
  1166  
  1167  	// remove cloud credential used by this model but keep model reference to it
  1168  	err = s.State.RemoveCloudCredential(credentialTag)
  1169  	c.Assert(err, jc.ErrorIsNil)
  1170  
  1171  	tracker := agenttest.NewEngineTracker()
  1172  	instrumented := TrackModels(c, tracker, iaasModelManifolds)
  1173  	s.PatchValue(&iaasModelManifolds, instrumented)
  1174  
  1175  	expectedWorkers := append(alwaysModelWorkers, aliveModelWorkers...)
  1176  	// Since this model's cloud credential is no longer valid,
  1177  	// only the workers that don't require a valid credential should remain.
  1178  	remainingWorkers := set.NewStrings(expectedWorkers...).Difference(
  1179  		set.NewStrings(requireValidCredentialModelWorkers...))
  1180  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, remainingWorkers.SortedValues())
  1181  
  1182  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1183  		agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync)
  1184  	})
  1185  }
  1186  
  1187  func (s *MachineLegacyLeasesSuite) TestMigratingModelWorkers(c *gc.C) {
  1188  	st, closer := s.setUpNewModel(c)
  1189  	defer closer()
  1190  	uuid := st.ModelUUID()
  1191  
  1192  	tracker := agenttest.NewEngineTracker()
  1193  
  1194  	// Replace the real migrationmaster worker with a fake one which
  1195  	// does nothing. This is required to make this test be reliable as
  1196  	// the environment required for the migrationmaster to operate
  1197  	// correctly is too involved to set up from here.
  1198  	//
  1199  	// TODO(mjs) - an alternative might be to provide a fake Facade
  1200  	// and api.Open to the real migrationmaster but this test is
  1201  	// awfully far away from the low level details of the worker.
  1202  	origModelManifolds := iaasModelManifolds
  1203  	modelManifoldsDisablingMigrationMaster := func(config model.ManifoldsConfig) dependency.Manifolds {
  1204  		config.NewMigrationMaster = func(config migrationmaster.Config) (worker.Worker, error) {
  1205  			return &nullWorker{dead: make(chan struct{})}, nil
  1206  		}
  1207  		return origModelManifolds(config)
  1208  	}
  1209  	instrumented := TrackModels(c, tracker, modelManifoldsDisablingMigrationMaster)
  1210  	s.PatchValue(&iaasModelManifolds, instrumented)
  1211  
  1212  	targetControllerTag := names.NewControllerTag(utils.MustNewUUID().String())
  1213  	_, err := st.CreateMigration(state.MigrationSpec{
  1214  		InitiatedBy: names.NewUserTag("admin"),
  1215  		TargetInfo: migration.TargetInfo{
  1216  			ControllerTag: targetControllerTag,
  1217  			Addrs:         []string{"1.2.3.4:5555"},
  1218  			CACert:        "cert",
  1219  			AuthTag:       names.NewUserTag("user"),
  1220  			Password:      "password",
  1221  		},
  1222  	})
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  
  1225  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid,
  1226  		append(alwaysModelWorkers, migratingModelWorkers...))
  1227  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1228  		agenttest.WaitMatch(c, matcher.Check, ReallyLongWait, st.StartSync)
  1229  	})
  1230  }
  1231  
  1232  func (s *MachineLegacyLeasesSuite) TestDyingModelCleanedUp(c *gc.C) {
  1233  	st, closer := s.setUpNewModel(c)
  1234  	defer closer()
  1235  
  1236  	timeout := time.After(ReallyLongWait)
  1237  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1238  		m, err := st.Model()
  1239  		c.Assert(err, jc.ErrorIsNil)
  1240  		watch := m.Watch()
  1241  		defer workertest.CleanKill(c, watch)
  1242  
  1243  		err = m.Destroy(state.DestroyModelParams{})
  1244  		c.Assert(err, jc.ErrorIsNil)
  1245  		for {
  1246  			select {
  1247  			case <-watch.Changes():
  1248  				err := m.Refresh()
  1249  				cause := errors.Cause(err)
  1250  				if err == nil {
  1251  					continue // still there
  1252  				} else if errors.IsNotFound(cause) {
  1253  					return // successfully removed
  1254  				}
  1255  				c.Assert(err, jc.ErrorIsNil) // guaranteed fail
  1256  			case <-time.After(coretesting.ShortWait):
  1257  				st.StartSync()
  1258  			case <-timeout:
  1259  				c.Fatalf("timed out waiting for workers")
  1260  			}
  1261  		}
  1262  	})
  1263  }
  1264  
  1265  func (s *MachineLegacyLeasesSuite) TestModelWorkersRespectSingularResponsibilityFlag(c *gc.C) {
  1266  
  1267  	// Grab responsibility for the model on behalf of another machine.
  1268  	claimer := s.BackingState.SingularClaimer()
  1269  	uuid := s.BackingState.ModelUUID()
  1270  	err := claimer.Claim(uuid, "machine-999-lxd-99", time.Hour)
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  
  1273  	// Then run a normal model-tracking test, just checking for
  1274  	// a different set of workers.
  1275  	tracker := agenttest.NewEngineTracker()
  1276  	instrumented := TrackModels(c, tracker, iaasModelManifolds)
  1277  	s.PatchValue(&iaasModelManifolds, instrumented)
  1278  
  1279  	matcher := agenttest.NewWorkerMatcher(c, tracker, uuid, alwaysModelWorkers)
  1280  	s.assertJobWithState(c, state.JobManageModel, func(agent.Config, *state.State) {
  1281  		agenttest.WaitMatch(c, matcher.Check, coretesting.LongWait, s.BackingState.StartSync)
  1282  	})
  1283  }
  1284  
  1285  func (s *MachineLegacyLeasesSuite) setUpNewModel(c *gc.C) (newSt *state.State, closer func()) {
  1286  	// Create a new environment, tests can now watch if workers start for it.
  1287  	newSt = s.Factory.MakeModel(c, &factory.ModelParams{
  1288  		ConfigAttrs: coretesting.Attrs{
  1289  			"max-status-history-age":  "2h",
  1290  			"max-status-history-size": "4M",
  1291  			"max-action-results-age":  "2h",
  1292  			"max-action-results-size": "4M",
  1293  		},
  1294  	})
  1295  	return newSt, func() {
  1296  		err := newSt.Close()
  1297  		c.Check(err, jc.ErrorIsNil)
  1298  	}
  1299  }
  1300  
  1301  func (s *MachineLegacyLeasesSuite) TestReplicasetInitForNewController(c *gc.C) {
  1302  	if runtime.GOOS == "windows" {
  1303  		c.Skip("controllers on windows aren't supported")
  1304  	}
  1305  
  1306  	s.fakeEnsureMongo.ServiceInstalled = false
  1307  
  1308  	m, _, _ := s.primeAgent(c, state.JobManageModel)
  1309  	a := s.newAgent(c, m)
  1310  	agentConfig := a.CurrentConfig()
  1311  
  1312  	err := a.ensureMongoServer(agentConfig)
  1313  	c.Assert(err, jc.ErrorIsNil)
  1314  
  1315  	c.Assert(s.fakeEnsureMongo.EnsureCount, gc.Equals, 1)
  1316  	c.Assert(s.fakeEnsureMongo.InitiateCount, gc.Equals, 0)
  1317  }
  1318  
  1319  type nullWorker struct {
  1320  	dead chan struct{}
  1321  }
  1322  
  1323  func (w *nullWorker) Kill() {
  1324  	close(w.dead)
  1325  }
  1326  
  1327  func (w *nullWorker) Wait() error {
  1328  	<-w.dead
  1329  	return nil
  1330  }