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