launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/machine_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"strings"
    11  	"time"
    12  
    13  	gc "launchpad.net/gocheck"
    14  
    15  	"launchpad.net/juju-core/agent"
    16  	"launchpad.net/juju-core/charm"
    17  	"launchpad.net/juju-core/cmd"
    18  	lxctesting "launchpad.net/juju-core/container/lxc/testing"
    19  	"launchpad.net/juju-core/environs/config"
    20  	envtesting "launchpad.net/juju-core/environs/testing"
    21  	"launchpad.net/juju-core/errors"
    22  	"launchpad.net/juju-core/instance"
    23  	"launchpad.net/juju-core/juju"
    24  	"launchpad.net/juju-core/juju/osenv"
    25  	"launchpad.net/juju-core/names"
    26  	"launchpad.net/juju-core/provider/dummy"
    27  	"launchpad.net/juju-core/state"
    28  	"launchpad.net/juju-core/state/api"
    29  	apideployer "launchpad.net/juju-core/state/api/deployer"
    30  	"launchpad.net/juju-core/state/api/params"
    31  	charmtesting "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing"
    32  	statetesting "launchpad.net/juju-core/state/testing"
    33  	"launchpad.net/juju-core/state/watcher"
    34  	"launchpad.net/juju-core/testing"
    35  	coretesting "launchpad.net/juju-core/testing"
    36  	jc "launchpad.net/juju-core/testing/checkers"
    37  	"launchpad.net/juju-core/testing/testbase"
    38  	"launchpad.net/juju-core/tools"
    39  	"launchpad.net/juju-core/utils"
    40  	"launchpad.net/juju-core/utils/ssh"
    41  	sshtesting "launchpad.net/juju-core/utils/ssh/testing"
    42  	"launchpad.net/juju-core/version"
    43  	"launchpad.net/juju-core/worker/authenticationworker"
    44  	"launchpad.net/juju-core/worker/deployer"
    45  	"launchpad.net/juju-core/worker/instancepoller"
    46  	"launchpad.net/juju-core/worker/machineenvironmentworker"
    47  )
    48  
    49  type MachineSuite struct {
    50  	agentSuite
    51  	lxctesting.TestSuite
    52  }
    53  
    54  var _ = gc.Suite(&MachineSuite{})
    55  
    56  func (s *MachineSuite) SetUpSuite(c *gc.C) {
    57  	s.agentSuite.SetUpSuite(c)
    58  	s.TestSuite.SetUpSuite(c)
    59  	restore := testbase.PatchValue(&charm.CacheDir, c.MkDir())
    60  	s.AddSuiteCleanup(func(*gc.C) { restore() })
    61  }
    62  
    63  func (s *MachineSuite) TearDownSuite(c *gc.C) {
    64  	s.TestSuite.TearDownSuite(c)
    65  	s.agentSuite.TearDownSuite(c)
    66  }
    67  
    68  func (s *MachineSuite) SetUpTest(c *gc.C) {
    69  	s.agentSuite.SetUpTest(c)
    70  	s.TestSuite.SetUpTest(c)
    71  	os.Remove(jujuRun) // ignore error; may not exist
    72  	// Fake $HOME, and ssh user to avoid touching ~ubuntu/.ssh/authorized_keys.
    73  	fakeHome := coretesting.MakeEmptyFakeHomeWithoutJuju(c)
    74  	s.AddCleanup(func(*gc.C) { fakeHome.Restore() })
    75  	s.PatchValue(&authenticationworker.SSHUser, "")
    76  }
    77  
    78  func (s *MachineSuite) TearDownTest(c *gc.C) {
    79  	s.TestSuite.TearDownTest(c)
    80  	s.agentSuite.TearDownTest(c)
    81  }
    82  
    83  const initialMachinePassword = "machine-password-1234567890"
    84  
    85  // primeAgent adds a new Machine to run the given jobs, and sets up the
    86  // machine agent's directory.  It returns the new machine, the
    87  // agent's configuration and the tools currently running.
    88  func (s *MachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (m *state.Machine, config agent.Config, tools *tools.Tools) {
    89  	m, err := s.State.AddOneMachine(state.MachineTemplate{
    90  		Series:     "quantal",
    91  		InstanceId: "ardbeg-0",
    92  		Nonce:      state.BootstrapNonce,
    93  		Jobs:       jobs,
    94  	})
    95  	c.Assert(err, gc.IsNil)
    96  	err = m.SetPassword(initialMachinePassword)
    97  	c.Assert(err, gc.IsNil)
    98  	tag := names.MachineTag(m.Id())
    99  	if m.IsManager() {
   100  		err = m.SetMongoPassword(initialMachinePassword)
   101  		c.Assert(err, gc.IsNil)
   102  		config, tools = s.agentSuite.primeStateAgent(c, tag, initialMachinePassword)
   103  	} else {
   104  		config, tools = s.agentSuite.primeAgent(c, tag, initialMachinePassword)
   105  	}
   106  	err = config.Write()
   107  	c.Assert(err, gc.IsNil)
   108  	return m, config, tools
   109  }
   110  
   111  // newAgent returns a new MachineAgent instance
   112  func (s *MachineSuite) newAgent(c *gc.C, m *state.Machine) *MachineAgent {
   113  	a := &MachineAgent{}
   114  	s.initAgent(c, a, "--machine-id", m.Id())
   115  	return a
   116  }
   117  
   118  func (s *MachineSuite) TestParseSuccess(c *gc.C) {
   119  	create := func() (cmd.Command, *AgentConf) {
   120  		a := &MachineAgent{}
   121  		return a, &a.Conf
   122  	}
   123  	a := CheckAgentCommand(c, create, []string{"--machine-id", "42"})
   124  	c.Assert(a.(*MachineAgent).MachineId, gc.Equals, "42")
   125  }
   126  
   127  func (s *MachineSuite) TestParseNonsense(c *gc.C) {
   128  	for _, args := range [][]string{
   129  		{},
   130  		{"--machine-id", "-4004"},
   131  	} {
   132  		err := ParseAgentCommand(&MachineAgent{}, args)
   133  		c.Assert(err, gc.ErrorMatches, "--machine-id option must be set, and expects a non-negative integer")
   134  	}
   135  }
   136  
   137  func (s *MachineSuite) TestParseUnknown(c *gc.C) {
   138  	a := &MachineAgent{}
   139  	err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"})
   140  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blistering barnacles"\]`)
   141  }
   142  
   143  func (s *MachineSuite) TestRunInvalidMachineId(c *gc.C) {
   144  	c.Skip("agents don't yet distinguish between temporary and permanent errors")
   145  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   146  	err := s.newAgent(c, m).Run(nil)
   147  	c.Assert(err, gc.ErrorMatches, "some error")
   148  }
   149  
   150  func (s *MachineSuite) TestRunStop(c *gc.C) {
   151  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   152  	a := s.newAgent(c, m)
   153  	done := make(chan error)
   154  	go func() {
   155  		done <- a.Run(nil)
   156  	}()
   157  	err := a.Stop()
   158  	c.Assert(err, gc.IsNil)
   159  	c.Assert(<-done, gc.IsNil)
   160  	c.Assert(charm.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache"))
   161  }
   162  
   163  func (s *MachineSuite) TestWithDeadMachine(c *gc.C) {
   164  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   165  	err := m.EnsureDead()
   166  	c.Assert(err, gc.IsNil)
   167  	a := s.newAgent(c, m)
   168  	err = runWithTimeout(a)
   169  	c.Assert(err, gc.IsNil)
   170  }
   171  
   172  func (s *MachineSuite) TestWithRemovedMachine(c *gc.C) {
   173  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   174  	err := m.EnsureDead()
   175  	c.Assert(err, gc.IsNil)
   176  	err = m.Remove()
   177  	c.Assert(err, gc.IsNil)
   178  	a := s.newAgent(c, m)
   179  	err = runWithTimeout(a)
   180  	c.Assert(err, gc.IsNil)
   181  }
   182  
   183  func (s *MachineSuite) TestDyingMachine(c *gc.C) {
   184  	c.Skip("Disabled as breaks test isolation somehow, see lp:1206195")
   185  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   186  	a := s.newAgent(c, m)
   187  	done := make(chan error)
   188  	go func() {
   189  		done <- a.Run(nil)
   190  	}()
   191  	defer func() {
   192  		c.Check(a.Stop(), gc.IsNil)
   193  	}()
   194  	err := m.Destroy()
   195  	c.Assert(err, gc.IsNil)
   196  	select {
   197  	case err := <-done:
   198  		c.Assert(err, gc.IsNil)
   199  	case <-time.After(watcher.Period * 5 / 4):
   200  		// TODO(rog) Fix this so it doesn't wait for so long.
   201  		// https://bugs.launchpad.net/juju-core/+bug/1163983
   202  		c.Fatalf("timed out waiting for agent to terminate")
   203  	}
   204  	err = m.Refresh()
   205  	c.Assert(err, gc.IsNil)
   206  	c.Assert(m.Life(), gc.Equals, state.Dead)
   207  }
   208  
   209  func (s *MachineSuite) TestHostUnits(c *gc.C) {
   210  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   211  	a := s.newAgent(c, m)
   212  	ctx, reset := patchDeployContext(c, s.BackingState)
   213  	defer reset()
   214  	go func() { c.Check(a.Run(nil), gc.IsNil) }()
   215  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   216  
   217  	// check that unassigned units don't trigger any deployments.
   218  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   219  	u0, err := svc.AddUnit()
   220  	c.Assert(err, gc.IsNil)
   221  	u1, err := svc.AddUnit()
   222  	c.Assert(err, gc.IsNil)
   223  	ctx.waitDeployed(c)
   224  
   225  	// assign u0, check it's deployed.
   226  	err = u0.AssignToMachine(m)
   227  	c.Assert(err, gc.IsNil)
   228  	ctx.waitDeployed(c, u0.Name())
   229  
   230  	// "start the agent" for u0 to prevent short-circuited remove-on-destroy;
   231  	// check that it's kept deployed despite being Dying.
   232  	err = u0.SetStatus(params.StatusStarted, "", nil)
   233  	c.Assert(err, gc.IsNil)
   234  	err = u0.Destroy()
   235  	c.Assert(err, gc.IsNil)
   236  	ctx.waitDeployed(c, u0.Name())
   237  
   238  	// add u1 to the machine, check it's deployed.
   239  	err = u1.AssignToMachine(m)
   240  	c.Assert(err, gc.IsNil)
   241  	ctx.waitDeployed(c, u0.Name(), u1.Name())
   242  
   243  	// make u0 dead; check the deployer recalls the unit and removes it from
   244  	// state.
   245  	err = u0.EnsureDead()
   246  	c.Assert(err, gc.IsNil)
   247  	ctx.waitDeployed(c, u1.Name())
   248  
   249  	// The deployer actually removes the unit just after
   250  	// removing its deployment, so we need to poll here
   251  	// until it actually happens.
   252  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   253  		err := u0.Refresh()
   254  		if err == nil && attempt.HasNext() {
   255  			continue
   256  		}
   257  		c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   258  	}
   259  
   260  	// short-circuit-remove u1 after it's been deployed; check it's recalled
   261  	// and removed from state.
   262  	err = u1.Destroy()
   263  	c.Assert(err, gc.IsNil)
   264  	err = u1.Refresh()
   265  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   266  	ctx.waitDeployed(c)
   267  }
   268  
   269  func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) {
   270  	ctx := &fakeContext{
   271  		inited: make(chan struct{}),
   272  	}
   273  	orig := newDeployContext
   274  	newDeployContext = func(dst *apideployer.State, agentConfig agent.Config) deployer.Context {
   275  		ctx.st = st
   276  		ctx.agentConfig = agentConfig
   277  		close(ctx.inited)
   278  		return ctx
   279  	}
   280  	return ctx, func() { newDeployContext = orig }
   281  }
   282  
   283  func (s *MachineSuite) TestManageEnviron(c *gc.C) {
   284  	usefulVersion := version.Current
   285  	usefulVersion.Series = "quantal" // to match the charm created below
   286  	envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion)
   287  	m, _, _ := s.primeAgent(c, state.JobManageEnviron)
   288  	err := m.SetAddresses([]instance.Address{
   289  		instance.NewAddress("0.1.2.3"),
   290  	})
   291  	c.Assert(err, gc.IsNil)
   292  	op := make(chan dummy.Operation, 200)
   293  	dummy.Listen(op)
   294  
   295  	a := s.newAgent(c, m)
   296  	// Make sure the agent is stopped even if the test fails.
   297  	defer a.Stop()
   298  	done := make(chan error)
   299  	go func() {
   300  		done <- a.Run(nil)
   301  	}()
   302  
   303  	// Check that the provisioner and firewaller are alive by doing
   304  	// a rudimentary check that it responds to state changes.
   305  
   306  	// Add one unit to a service; it should get allocated a machine
   307  	// and then its ports should be opened.
   308  	charm := s.AddTestingCharm(c, "dummy")
   309  	svc := s.AddTestingService(c, "test-service", charm)
   310  	err = svc.SetExposed()
   311  	c.Assert(err, gc.IsNil)
   312  	units, err := juju.AddUnits(s.State, svc, 1, "")
   313  	c.Assert(err, gc.IsNil)
   314  	c.Check(opRecvTimeout(c, s.State, op, dummy.OpStartInstance{}), gc.NotNil)
   315  
   316  	// Wait for the instance id to show up in the state.
   317  	s.waitProvisioned(c, units[0])
   318  	err = units[0].OpenPort("tcp", 999)
   319  	c.Assert(err, gc.IsNil)
   320  
   321  	c.Check(opRecvTimeout(c, s.State, op, dummy.OpOpenPorts{}), gc.NotNil)
   322  
   323  	err = a.Stop()
   324  	c.Assert(err, gc.IsNil)
   325  
   326  	select {
   327  	case err := <-done:
   328  		c.Assert(err, gc.IsNil)
   329  	case <-time.After(5 * time.Second):
   330  		c.Fatalf("timed out waiting for agent to terminate")
   331  	}
   332  }
   333  
   334  func (s *MachineSuite) TestManageEnvironRunsInstancePoller(c *gc.C) {
   335  	defer testbase.PatchValue(&instancepoller.ShortPoll, 500*time.Millisecond).Restore()
   336  	usefulVersion := version.Current
   337  	usefulVersion.Series = "quantal" // to match the charm created below
   338  	envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion)
   339  	m, _, _ := s.primeAgent(c, state.JobManageEnviron)
   340  	err := m.SetAddresses([]instance.Address{
   341  		instance.NewAddress("0.1.2.3"),
   342  	})
   343  	c.Assert(err, gc.IsNil)
   344  	a := s.newAgent(c, m)
   345  	defer a.Stop()
   346  	go func() {
   347  		c.Check(a.Run(nil), gc.IsNil)
   348  	}()
   349  
   350  	// Add one unit to a service;
   351  	charm := s.AddTestingCharm(c, "dummy")
   352  	svc := s.AddTestingService(c, "test-service", charm)
   353  	units, err := juju.AddUnits(s.State, svc, 1, "")
   354  	c.Assert(err, gc.IsNil)
   355  
   356  	m, instId := s.waitProvisioned(c, units[0])
   357  	insts, err := s.Conn.Environ.Instances([]instance.Id{instId})
   358  	c.Assert(err, gc.IsNil)
   359  	addrs := []instance.Address{instance.NewAddress("1.2.3.4")}
   360  	dummy.SetInstanceAddresses(insts[0], addrs)
   361  	dummy.SetInstanceStatus(insts[0], "running")
   362  
   363  	for a := coretesting.LongAttempt.Start(); a.Next(); {
   364  		if !a.HasNext() {
   365  			c.Logf("final machine addresses: %#v", m.Addresses())
   366  			c.Fatalf("timed out waiting for machine to get address")
   367  		}
   368  		err := m.Refresh()
   369  		c.Assert(err, gc.IsNil)
   370  		instStatus, err := m.InstanceStatus()
   371  		c.Assert(err, gc.IsNil)
   372  		if reflect.DeepEqual(m.Addresses(), addrs) && instStatus == "running" {
   373  			break
   374  		}
   375  	}
   376  
   377  }
   378  
   379  func (s *MachineSuite) waitProvisioned(c *gc.C, unit *state.Unit) (*state.Machine, instance.Id) {
   380  	c.Logf("waiting for unit %q to be provisioned", unit)
   381  	machineId, err := unit.AssignedMachineId()
   382  	c.Assert(err, gc.IsNil)
   383  	m, err := s.State.Machine(machineId)
   384  	c.Assert(err, gc.IsNil)
   385  	w := m.Watch()
   386  	defer w.Stop()
   387  	timeout := time.After(coretesting.LongWait)
   388  	for {
   389  		select {
   390  		case <-timeout:
   391  			c.Fatalf("timed out waiting for provisioning")
   392  		case _, ok := <-w.Changes():
   393  			c.Assert(ok, jc.IsTrue)
   394  			err := m.Refresh()
   395  			c.Assert(err, gc.IsNil)
   396  			if instId, err := m.InstanceId(); err == nil {
   397  				c.Logf("unit provisioned with instance %s", instId)
   398  				return m, instId
   399  			} else {
   400  				c.Check(err, jc.Satisfies, state.IsNotProvisionedError)
   401  			}
   402  		}
   403  	}
   404  	panic("watcher died")
   405  }
   406  
   407  func (s *MachineSuite) TestUpgrade(c *gc.C) {
   408  	m, _, currentTools := s.primeAgent(c, state.JobManageEnviron, state.JobHostUnits)
   409  	a := s.newAgent(c, m)
   410  	s.testUpgrade(c, a, m.Tag(), currentTools)
   411  }
   412  
   413  var fastDialOpts = api.DialOpts{
   414  	Timeout:    coretesting.LongWait,
   415  	RetryDelay: coretesting.ShortWait,
   416  }
   417  
   418  func (s *MachineSuite) waitStopped(c *gc.C, job state.MachineJob, a *MachineAgent, done chan error) {
   419  	err := a.Stop()
   420  	if job == state.JobManageEnviron {
   421  		// When shutting down, the API server can be shut down before
   422  		// the other workers that connect to it, so they get an error so
   423  		// they then die, causing Stop to return an error.  It's not
   424  		// easy to control the actual error that's received in this
   425  		// circumstance so we just log it rather than asserting that it
   426  		// is not nil.
   427  		if err != nil {
   428  			c.Logf("error shutting down state manager: %v", err)
   429  		}
   430  	} else {
   431  		c.Assert(err, gc.IsNil)
   432  	}
   433  
   434  	select {
   435  	case err := <-done:
   436  		c.Assert(err, gc.IsNil)
   437  	case <-time.After(5 * time.Second):
   438  		c.Fatalf("timed out waiting for agent to terminate")
   439  	}
   440  }
   441  
   442  func (s *MachineSuite) assertJobWithAPI(
   443  	c *gc.C,
   444  	job state.MachineJob,
   445  	test func(agent.Config, *api.State),
   446  ) {
   447  	stm, conf, _ := s.primeAgent(c, job)
   448  	a := s.newAgent(c, stm)
   449  	defer a.Stop()
   450  
   451  	// All state jobs currently also run an APIWorker, so no
   452  	// need to check for that here, like in assertJobWithState.
   453  
   454  	agentAPIs := make(chan *api.State, 1000)
   455  	undo := sendOpenedAPIs(agentAPIs)
   456  	defer undo()
   457  
   458  	done := make(chan error)
   459  	go func() {
   460  		done <- a.Run(nil)
   461  	}()
   462  
   463  	select {
   464  	case agentAPI := <-agentAPIs:
   465  		c.Assert(agentAPI, gc.NotNil)
   466  		test(conf, agentAPI)
   467  	case <-time.After(coretesting.LongWait):
   468  		c.Fatalf("API not opened")
   469  	}
   470  
   471  	s.waitStopped(c, job, a, done)
   472  }
   473  
   474  func (s *MachineSuite) assertJobWithState(
   475  	c *gc.C,
   476  	job state.MachineJob,
   477  	test func(agent.Config, *state.State),
   478  ) {
   479  	paramsJob := job.ToParams()
   480  	if !paramsJob.NeedsState() {
   481  		c.Fatalf("%v does not use state", paramsJob)
   482  	}
   483  	stm, conf, _ := s.primeAgent(c, job)
   484  	a := s.newAgent(c, stm)
   485  	defer a.Stop()
   486  
   487  	agentStates := make(chan *state.State, 1000)
   488  	undo := sendOpenedStates(agentStates)
   489  	defer undo()
   490  
   491  	done := make(chan error)
   492  	go func() {
   493  		done <- a.Run(nil)
   494  	}()
   495  
   496  	select {
   497  	case agentState := <-agentStates:
   498  		c.Assert(agentState, gc.NotNil)
   499  		test(conf, agentState)
   500  	case <-time.After(coretesting.LongWait):
   501  		c.Fatalf("state not opened")
   502  	}
   503  
   504  	s.waitStopped(c, job, a, done)
   505  }
   506  
   507  // TODO(jam): 2013-09-02 http://pad.lv/1219661
   508  // This test has been failing regularly on the Bot. Until someone fixes it so
   509  // it doesn't crash, it isn't worth having as we can't tell when someone
   510  // actually breaks something.
   511  func (s *MachineSuite) TestManageEnvironServesAPI(c *gc.C) {
   512  	c.Skip("does not pass reliably on the bot (http://pad.lv/1219661")
   513  	s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) {
   514  		st, _, err := conf.OpenAPI(fastDialOpts)
   515  		c.Assert(err, gc.IsNil)
   516  		defer st.Close()
   517  		m, err := st.Machiner().Machine(conf.Tag())
   518  		c.Assert(err, gc.IsNil)
   519  		c.Assert(m.Life(), gc.Equals, params.Alive)
   520  	})
   521  }
   522  
   523  func (s *MachineSuite) TestManageEnvironRunsCleaner(c *gc.C) {
   524  	s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) {
   525  		// Create a service and unit, and destroy the service.
   526  		service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   527  		unit, err := service.AddUnit()
   528  		c.Assert(err, gc.IsNil)
   529  		err = service.Destroy()
   530  		c.Assert(err, gc.IsNil)
   531  
   532  		// Check the unit was not yet removed.
   533  		err = unit.Refresh()
   534  		c.Assert(err, gc.IsNil)
   535  		w := unit.Watch()
   536  		defer w.Stop()
   537  
   538  		// Trigger a sync on the state used by the agent, and wait
   539  		// for the unit to be removed.
   540  		agentState.StartSync()
   541  		timeout := time.After(coretesting.LongWait)
   542  		for done := false; !done; {
   543  			select {
   544  			case <-timeout:
   545  				c.Fatalf("unit not cleaned up")
   546  			case <-time.After(coretesting.ShortWait):
   547  				s.State.StartSync()
   548  			case <-w.Changes():
   549  				err := unit.Refresh()
   550  				if errors.IsNotFoundError(err) {
   551  					done = true
   552  				} else {
   553  					c.Assert(err, gc.IsNil)
   554  				}
   555  			}
   556  		}
   557  	})
   558  }
   559  
   560  func (s *MachineSuite) TestJobManageEnvironRunsMinUnitsWorker(c *gc.C) {
   561  	s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) {
   562  		// Ensure that the MinUnits worker is alive by doing a simple check
   563  		// that it responds to state changes: add a service, set its minimum
   564  		// number of units to one, wait for the worker to add the missing unit.
   565  		service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   566  		err := service.SetMinUnits(1)
   567  		c.Assert(err, gc.IsNil)
   568  		w := service.Watch()
   569  		defer w.Stop()
   570  
   571  		// Trigger a sync on the state used by the agent, and wait for the unit
   572  		// to be created.
   573  		agentState.StartSync()
   574  		timeout := time.After(coretesting.LongWait)
   575  		for {
   576  			select {
   577  			case <-timeout:
   578  				c.Fatalf("unit not created")
   579  			case <-time.After(coretesting.ShortWait):
   580  				s.State.StartSync()
   581  			case <-w.Changes():
   582  				units, err := service.AllUnits()
   583  				c.Assert(err, gc.IsNil)
   584  				if len(units) == 1 {
   585  					return
   586  				}
   587  			}
   588  		}
   589  	})
   590  }
   591  
   592  func (s *MachineSuite) TestMachineAgentRunsAuthorisedKeysWorker(c *gc.C) {
   593  	// Start the machine agent.
   594  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   595  	a := s.newAgent(c, m)
   596  	go func() { c.Check(a.Run(nil), gc.IsNil) }()
   597  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   598  
   599  	// Update the keys in the environment.
   600  	sshKey := sshtesting.ValidKeyOne.Key + " user@host"
   601  	err := statetesting.UpdateConfig(s.BackingState, map[string]interface{}{"authorized-keys": sshKey})
   602  	c.Assert(err, gc.IsNil)
   603  
   604  	// Wait for ssh keys file to be updated.
   605  	s.State.StartSync()
   606  	timeout := time.After(coretesting.LongWait)
   607  	sshKeyWithCommentPrefix := sshtesting.ValidKeyOne.Key + " Juju:user@host"
   608  	for {
   609  		select {
   610  		case <-timeout:
   611  			c.Fatalf("timeout while waiting for authorised ssh keys to change")
   612  		case <-time.After(coretesting.ShortWait):
   613  			keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys)
   614  			c.Assert(err, gc.IsNil)
   615  			keysStr := strings.Join(keys, "\n")
   616  			if sshKeyWithCommentPrefix != keysStr {
   617  				continue
   618  			}
   619  			return
   620  		}
   621  	}
   622  }
   623  
   624  // opRecvTimeout waits for any of the given kinds of operation to
   625  // be received from ops, and times out if not.
   626  func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation {
   627  	st.StartSync()
   628  	for {
   629  		select {
   630  		case op := <-opc:
   631  			for _, k := range kinds {
   632  				if reflect.TypeOf(op) == reflect.TypeOf(k) {
   633  					return op
   634  				}
   635  			}
   636  			c.Logf("discarding unknown event %#v", op)
   637  		case <-time.After(15 * time.Second):
   638  			c.Fatalf("time out wating for operation")
   639  		}
   640  	}
   641  }
   642  
   643  func (s *MachineSuite) TestOpenStateFailsForJobHostUnitsButOpenAPIWorks(c *gc.C) {
   644  	m, _, _ := s.primeAgent(c, state.JobHostUnits)
   645  	s.testOpenAPIState(c, m, s.newAgent(c, m), initialMachinePassword)
   646  	s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) {
   647  		s.assertCannotOpenState(c, conf.Tag(), conf.DataDir())
   648  	})
   649  }
   650  
   651  func (s *MachineSuite) TestOpenStateWorksForJobManageEnviron(c *gc.C) {
   652  	s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) {
   653  		s.assertCanOpenState(c, conf.Tag(), conf.DataDir())
   654  	})
   655  }
   656  
   657  func (s *MachineSuite) TestMachineAgentSymlinkJujuRun(c *gc.C) {
   658  	_, err := os.Stat(jujuRun)
   659  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   660  	s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) {
   661  		// juju-run should have been created
   662  		_, err := os.Stat(jujuRun)
   663  		c.Assert(err, gc.IsNil)
   664  	})
   665  }
   666  
   667  func (s *MachineSuite) TestMachineAgentSymlinkJujuRunExists(c *gc.C) {
   668  	err := os.Symlink("/nowhere/special", jujuRun)
   669  	c.Assert(err, gc.IsNil)
   670  	_, err = os.Stat(jujuRun)
   671  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   672  	s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) {
   673  		// juju-run should have been recreated
   674  		_, err := os.Stat(jujuRun)
   675  		c.Assert(err, gc.IsNil)
   676  		link, err := os.Readlink(jujuRun)
   677  		c.Assert(err, gc.IsNil)
   678  		c.Assert(link, gc.Not(gc.Equals), "/nowhere/special")
   679  	})
   680  }
   681  
   682  func (s *MachineSuite) TestMachineEnvirnWorker(c *gc.C) {
   683  	proxyDir := c.MkDir()
   684  	s.PatchValue(&machineenvironmentworker.ProxyDirectory, proxyDir)
   685  	s.PatchValue(&utils.AptConfFile, filepath.Join(proxyDir, "juju-apt-proxy"))
   686  
   687  	s.primeAgent(c, state.JobHostUnits)
   688  	// Make sure there are some proxy settings to write.
   689  	oldConfig, err := s.State.EnvironConfig()
   690  	c.Assert(err, gc.IsNil)
   691  
   692  	proxySettings := osenv.ProxySettings{
   693  		Http:  "http proxy",
   694  		Https: "https proxy",
   695  		Ftp:   "ftp proxy",
   696  	}
   697  
   698  	envConfig, err := oldConfig.Apply(config.ProxyConfigMap(proxySettings))
   699  	c.Assert(err, gc.IsNil)
   700  
   701  	err = s.State.SetEnvironConfig(envConfig, oldConfig)
   702  	c.Assert(err, gc.IsNil)
   703  
   704  	s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) {
   705  		for {
   706  			select {
   707  			case <-time.After(testing.LongWait):
   708  				c.Fatalf("timeout while waiting for proxy settings to change")
   709  			case <-time.After(10 * time.Millisecond):
   710  				_, err := os.Stat(utils.AptConfFile)
   711  				if os.IsNotExist(err) {
   712  					continue
   713  				}
   714  				c.Assert(err, gc.IsNil)
   715  				return
   716  			}
   717  		}
   718  	})
   719  }
   720  
   721  func (s *MachineSuite) TestMachineAgentUninstall(c *gc.C) {
   722  	m, ac, _ := s.primeAgent(c, state.JobHostUnits)
   723  	err := m.EnsureDead()
   724  	c.Assert(err, gc.IsNil)
   725  	a := s.newAgent(c, m)
   726  	err = runWithTimeout(a)
   727  	c.Assert(err, gc.IsNil)
   728  	// juju-run should have been removed on termination
   729  	_, err = os.Stat(jujuRun)
   730  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   731  	// data-dir should have been removed on termination
   732  	_, err = os.Stat(ac.DataDir())
   733  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   734  }
   735  
   736  // MachineWithCharmsSuite provides infrastructure for tests which need to
   737  // work with charms.
   738  type MachineWithCharmsSuite struct {
   739  	charmtesting.CharmSuite
   740  
   741  	machine *state.Machine
   742  }
   743  
   744  var _ = gc.Suite(&MachineWithCharmsSuite{})
   745  
   746  func (s *MachineWithCharmsSuite) SetUpTest(c *gc.C) {
   747  	s.CharmSuite.SetUpTest(c)
   748  
   749  	// Create a state server machine.
   750  	var err error
   751  	s.machine, err = s.State.AddOneMachine(state.MachineTemplate{
   752  		Series:     "quantal",
   753  		InstanceId: "ardbeg-0",
   754  		Nonce:      state.BootstrapNonce,
   755  		Jobs:       []state.MachineJob{state.JobManageEnviron},
   756  	})
   757  	c.Assert(err, gc.IsNil)
   758  	err = s.machine.SetPassword(initialMachinePassword)
   759  	c.Assert(err, gc.IsNil)
   760  	tag := names.MachineTag(s.machine.Id())
   761  	err = s.machine.SetMongoPassword(initialMachinePassword)
   762  	c.Assert(err, gc.IsNil)
   763  
   764  	// Set up the agent configuration.
   765  	stateInfo := s.StateInfo(c)
   766  	writeStateAgentConfig(c, stateInfo, s.DataDir(), tag, initialMachinePassword)
   767  }
   768  
   769  func (s *MachineWithCharmsSuite) TestManageEnvironRunsCharmRevisionUpdater(c *gc.C) {
   770  	s.SetupScenario(c)
   771  
   772  	// Start the machine agent.
   773  	a := &MachineAgent{}
   774  	args := []string{"--data-dir", s.DataDir(), "--machine-id", s.machine.Id()}
   775  	err := coretesting.InitCommand(a, args)
   776  	c.Assert(err, gc.IsNil)
   777  
   778  	go func() {
   779  		c.Check(a.Run(nil), gc.IsNil)
   780  	}()
   781  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   782  
   783  	checkRevision := func() bool {
   784  		curl := charm.MustParseURL("cs:quantal/mysql")
   785  		placeholder, err := s.State.LatestPlaceholderCharm(curl)
   786  		return err == nil && placeholder.String() == curl.WithRevision(23).String()
   787  	}
   788  	success := false
   789  	for attempt := coretesting.LongAttempt.Start(); attempt.Next(); {
   790  		if success = checkRevision(); success {
   791  			break
   792  		}
   793  	}
   794  	c.Assert(success, gc.Equals, true)
   795  }