github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/jujud/unit_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  	"time"
     8  
     9  	gc "launchpad.net/gocheck"
    10  
    11  	"launchpad.net/juju-core/agent"
    12  	"launchpad.net/juju-core/cmd"
    13  	envtesting "launchpad.net/juju-core/environs/testing"
    14  	jujutesting "launchpad.net/juju-core/juju/testing"
    15  	"launchpad.net/juju-core/names"
    16  	"launchpad.net/juju-core/state"
    17  	"launchpad.net/juju-core/state/api/params"
    18  	apirsyslog "launchpad.net/juju-core/state/api/rsyslog"
    19  	coretesting "launchpad.net/juju-core/testing"
    20  	"launchpad.net/juju-core/tools"
    21  	"launchpad.net/juju-core/version"
    22  	"launchpad.net/juju-core/worker"
    23  	"launchpad.net/juju-core/worker/rsyslog"
    24  	"launchpad.net/juju-core/worker/upgrader"
    25  )
    26  
    27  type UnitSuite struct {
    28  	coretesting.GitSuite
    29  	agentSuite
    30  }
    31  
    32  var _ = gc.Suite(&UnitSuite{})
    33  
    34  func (s *UnitSuite) SetUpTest(c *gc.C) {
    35  	s.GitSuite.SetUpTest(c)
    36  	s.agentSuite.SetUpTest(c)
    37  }
    38  
    39  func (s *UnitSuite) TearDownTest(c *gc.C) {
    40  	s.agentSuite.TearDownTest(c)
    41  	s.GitSuite.TearDownTest(c)
    42  }
    43  
    44  const initialUnitPassword = "unit-password-1234567890"
    45  
    46  // primeAgent creates a unit, and sets up the unit agent's directory.
    47  // It returns the assigned machine, new unit and the agent's configuration.
    48  func (s *UnitSuite) primeAgent(c *gc.C) (*state.Machine, *state.Unit, agent.Config, *tools.Tools) {
    49  	jujutesting.AddStateServerMachine(c, s.State)
    50  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
    51  	unit, err := svc.AddUnit()
    52  	c.Assert(err, gc.IsNil)
    53  	err = unit.SetPassword(initialUnitPassword)
    54  	c.Assert(err, gc.IsNil)
    55  	// Assign the unit to a machine.
    56  	err = unit.AssignToNewMachine()
    57  	c.Assert(err, gc.IsNil)
    58  	id, err := unit.AssignedMachineId()
    59  	c.Assert(err, gc.IsNil)
    60  	machine, err := s.State.Machine(id)
    61  	c.Assert(err, gc.IsNil)
    62  	conf, tools := s.agentSuite.primeAgent(c, unit.Tag(), initialUnitPassword, version.Current)
    63  	return machine, unit, conf, tools
    64  }
    65  
    66  func (s *UnitSuite) newAgent(c *gc.C, unit *state.Unit) *UnitAgent {
    67  	a := &UnitAgent{}
    68  	s.initAgent(c, a, "--unit-name", unit.Name())
    69  	return a
    70  }
    71  
    72  func (s *UnitSuite) TestParseSuccess(c *gc.C) {
    73  	create := func() (cmd.Command, *AgentConf) {
    74  		a := &UnitAgent{}
    75  		return a, &a.Conf
    76  	}
    77  	uc := CheckAgentCommand(c, create, []string{"--unit-name", "w0rd-pre55/1"})
    78  	c.Assert(uc.(*UnitAgent).UnitName, gc.Equals, "w0rd-pre55/1")
    79  }
    80  
    81  func (s *UnitSuite) TestParseMissing(c *gc.C) {
    82  	uc := &UnitAgent{}
    83  	err := ParseAgentCommand(uc, []string{})
    84  	c.Assert(err, gc.ErrorMatches, "--unit-name option must be set")
    85  }
    86  
    87  func (s *UnitSuite) TestParseNonsense(c *gc.C) {
    88  	for _, args := range [][]string{
    89  		{"--unit-name", "wordpress"},
    90  		{"--unit-name", "wordpress/seventeen"},
    91  		{"--unit-name", "wordpress/-32"},
    92  		{"--unit-name", "wordpress/wild/9"},
    93  		{"--unit-name", "20/20"},
    94  	} {
    95  		err := ParseAgentCommand(&UnitAgent{}, args)
    96  		c.Assert(err, gc.ErrorMatches, `--unit-name option expects "<service>/<n>" argument`)
    97  	}
    98  }
    99  
   100  func (s *UnitSuite) TestParseUnknown(c *gc.C) {
   101  	uc := &UnitAgent{}
   102  	err := ParseAgentCommand(uc, []string{"--unit-name", "wordpress/1", "thundering typhoons"})
   103  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["thundering typhoons"\]`)
   104  }
   105  
   106  func waitForUnitStarted(stateConn *state.State, unit *state.Unit, c *gc.C) {
   107  	timeout := time.After(5 * time.Second)
   108  
   109  	for {
   110  		select {
   111  		case <-timeout:
   112  			c.Fatalf("no activity detected")
   113  		case <-time.After(coretesting.ShortWait):
   114  			err := unit.Refresh()
   115  			c.Assert(err, gc.IsNil)
   116  			st, info, data, err := unit.Status()
   117  			c.Assert(err, gc.IsNil)
   118  			switch st {
   119  			case params.StatusPending, params.StatusInstalled:
   120  				c.Logf("waiting...")
   121  				continue
   122  			case params.StatusStarted:
   123  				c.Logf("started!")
   124  				return
   125  			case params.StatusDown:
   126  				stateConn.StartSync()
   127  				c.Logf("unit is still down")
   128  			default:
   129  				c.Fatalf("unexpected status %s %s %v", st, info, data)
   130  			}
   131  		}
   132  	}
   133  }
   134  
   135  func (s *UnitSuite) TestRunStop(c *gc.C) {
   136  	_, unit, _, _ := s.primeAgent(c)
   137  	a := s.newAgent(c, unit)
   138  	go func() { c.Check(a.Run(nil), gc.IsNil) }()
   139  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   140  	waitForUnitStarted(s.State, unit, c)
   141  }
   142  
   143  func (s *UnitSuite) TestUpgrade(c *gc.C) {
   144  	machine, unit, _, currentTools := s.primeAgent(c)
   145  	agent := s.newAgent(c, unit)
   146  	newVers := version.Current
   147  	newVers.Patch++
   148  	envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), newVers)
   149  
   150  	// Set the machine agent version to trigger an upgrade.
   151  	err := machine.SetAgentVersion(newVers)
   152  	c.Assert(err, gc.IsNil)
   153  	err = runWithTimeout(agent)
   154  	envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
   155  		AgentName: unit.Tag(),
   156  		OldTools:  currentTools.Version,
   157  		NewTools:  newVers,
   158  		DataDir:   s.DataDir(),
   159  	})
   160  }
   161  
   162  func (s *UnitSuite) TestUpgradeFailsWithoutTools(c *gc.C) {
   163  	machine, unit, _, _ := s.primeAgent(c)
   164  	agent := s.newAgent(c, unit)
   165  	newVers := version.Current
   166  	newVers.Patch++
   167  	err := machine.SetAgentVersion(newVers)
   168  	c.Assert(err, gc.IsNil)
   169  	err = runWithTimeout(agent)
   170  	c.Assert(err, gc.ErrorMatches, "timed out waiting for agent to finish.*")
   171  }
   172  
   173  func (s *UnitSuite) TestWithDeadUnit(c *gc.C) {
   174  	_, unit, _, _ := s.primeAgent(c)
   175  	err := unit.EnsureDead()
   176  	c.Assert(err, gc.IsNil)
   177  	a := s.newAgent(c, unit)
   178  	err = runWithTimeout(a)
   179  	c.Assert(err, gc.IsNil)
   180  
   181  	// try again when the unit has been removed.
   182  	err = unit.Remove()
   183  	c.Assert(err, gc.IsNil)
   184  	a = s.newAgent(c, unit)
   185  	err = runWithTimeout(a)
   186  	c.Assert(err, gc.IsNil)
   187  }
   188  
   189  func (s *UnitSuite) TestOpenAPIState(c *gc.C) {
   190  	_, unit, _, _ := s.primeAgent(c)
   191  	s.testOpenAPIState(c, unit, s.newAgent(c, unit), initialUnitPassword)
   192  }
   193  
   194  func (s *UnitSuite) TestOpenAPIStateWithBadCredsTerminates(c *gc.C) {
   195  	conf, _ := s.agentSuite.primeAgent(c, "unit-missing-0", "no-password", version.Current)
   196  	_, _, err := openAPIState(conf, nil)
   197  	c.Assert(err, gc.Equals, worker.ErrTerminateAgent)
   198  }
   199  
   200  type fakeUnitAgent struct {
   201  	unitName string
   202  }
   203  
   204  func (f *fakeUnitAgent) Entity(st *state.State) (AgentState, error) {
   205  	return st.Unit(f.unitName)
   206  }
   207  
   208  func (f *fakeUnitAgent) Tag() string {
   209  	return names.UnitTag(f.unitName)
   210  }
   211  
   212  func (s *UnitSuite) TestOpenAPIStateWithDeadEntityTerminates(c *gc.C) {
   213  	_, unit, conf, _ := s.primeAgent(c)
   214  	err := unit.EnsureDead()
   215  	c.Assert(err, gc.IsNil)
   216  	_, _, err = openAPIState(conf, &fakeUnitAgent{"wordpress/0"})
   217  	c.Assert(err, gc.Equals, worker.ErrTerminateAgent)
   218  }
   219  
   220  func (s *UnitSuite) TestOpenStateFails(c *gc.C) {
   221  	// Start a unit agent and make sure it doesn't set a mongo password
   222  	// we can use to connect to state with.
   223  	_, unit, conf, _ := s.primeAgent(c)
   224  	a := s.newAgent(c, unit)
   225  	go func() { c.Check(a.Run(nil), gc.IsNil) }()
   226  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   227  	waitForUnitStarted(s.State, unit, c)
   228  
   229  	s.assertCannotOpenState(c, conf.Tag(), conf.DataDir())
   230  }
   231  
   232  func (s *UnitSuite) TestRsyslogConfigWorker(c *gc.C) {
   233  	created := make(chan rsyslog.RsyslogMode, 1)
   234  	s.PatchValue(&newRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) {
   235  		created <- mode
   236  		return worker.NewRunner(isFatal, moreImportant), nil
   237  	})
   238  
   239  	_, unit, _, _ := s.primeAgent(c)
   240  	a := s.newAgent(c, unit)
   241  	go func() { c.Check(a.Run(nil), gc.IsNil) }()
   242  	defer func() { c.Check(a.Stop(), gc.IsNil) }()
   243  
   244  	select {
   245  	case <-time.After(coretesting.LongWait):
   246  		c.Fatalf("timeout while waiting for rsyslog worker to be created")
   247  	case mode := <-created:
   248  		c.Assert(mode, gc.Equals, rsyslog.RsyslogModeForwarding)
   249  	}
   250  }