launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/deployer/deployer_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package deployer_test
     5  
     6  import (
     7  	gc "launchpad.net/gocheck"
     8  	"sort"
     9  	"strings"
    10  	stdtesting "testing"
    11  	"time"
    12  
    13  	"launchpad.net/juju-core/errors"
    14  	jujutesting "launchpad.net/juju-core/juju/testing"
    15  	"launchpad.net/juju-core/state"
    16  	"launchpad.net/juju-core/state/api"
    17  	apideployer "launchpad.net/juju-core/state/api/deployer"
    18  	"launchpad.net/juju-core/state/api/params"
    19  	coretesting "launchpad.net/juju-core/testing"
    20  	"launchpad.net/juju-core/worker"
    21  	"launchpad.net/juju-core/worker/deployer"
    22  )
    23  
    24  func TestPackage(t *stdtesting.T) {
    25  	coretesting.MgoTestPackage(t)
    26  }
    27  
    28  type deployerSuite struct {
    29  	jujutesting.JujuConnSuite
    30  	SimpleToolsFixture
    31  
    32  	machine       *state.Machine
    33  	stateAPI      *api.State
    34  	deployerState *apideployer.State
    35  }
    36  
    37  var _ = gc.Suite(&deployerSuite{})
    38  
    39  var _ worker.StringsWatchHandler = (*deployer.Deployer)(nil)
    40  
    41  func (s *deployerSuite) SetUpTest(c *gc.C) {
    42  	s.JujuConnSuite.SetUpTest(c)
    43  	s.SimpleToolsFixture.SetUp(c, s.DataDir())
    44  	s.stateAPI, s.machine = s.OpenAPIAsNewMachine(c)
    45  	// Create the deployer facade.
    46  	s.deployerState = s.stateAPI.Deployer()
    47  	c.Assert(s.deployerState, gc.NotNil)
    48  }
    49  
    50  func (s *deployerSuite) TearDownTest(c *gc.C) {
    51  	s.SimpleToolsFixture.TearDown(c)
    52  	s.JujuConnSuite.TearDownTest(c)
    53  }
    54  
    55  func (s *deployerSuite) makeDeployerAndContext(c *gc.C) (worker.Worker, deployer.Context) {
    56  	// Create a deployer acting on behalf of the machine.
    57  	ctx := s.getContextForMachine(c, s.machine.Tag())
    58  	return deployer.NewDeployer(s.deployerState, ctx), ctx
    59  }
    60  
    61  func (s *deployerSuite) TestDeployRecallRemovePrincipals(c *gc.C) {
    62  	// Create a machine, and a couple of units.
    63  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
    64  	u0, err := svc.AddUnit()
    65  	c.Assert(err, gc.IsNil)
    66  	u1, err := svc.AddUnit()
    67  	c.Assert(err, gc.IsNil)
    68  
    69  	dep, ctx := s.makeDeployerAndContext(c)
    70  	defer stop(c, dep)
    71  
    72  	// Assign one unit, and wait for it to be deployed.
    73  	err = u0.AssignToMachine(s.machine)
    74  	c.Assert(err, gc.IsNil)
    75  	s.waitFor(c, isDeployed(ctx, u0.Name()))
    76  
    77  	// Assign another unit, and wait for that to be deployed.
    78  	err = u1.AssignToMachine(s.machine)
    79  	c.Assert(err, gc.IsNil)
    80  	s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name()))
    81  
    82  	// Cause a unit to become Dying, and check no change.
    83  	err = u1.SetStatus(params.StatusInstalled, "", nil)
    84  	c.Assert(err, gc.IsNil)
    85  	err = u1.Destroy()
    86  	c.Assert(err, gc.IsNil)
    87  	s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name()))
    88  
    89  	// Cause a unit to become Dead, and check that it is both recalled and
    90  	// removed from state.
    91  	err = u0.EnsureDead()
    92  	c.Assert(err, gc.IsNil)
    93  	s.waitFor(c, isRemoved(s.State, u0.Name()))
    94  	s.waitFor(c, isDeployed(ctx, u1.Name()))
    95  
    96  	// Remove the Dying unit from the machine, and check that it is recalled...
    97  	err = u1.UnassignFromMachine()
    98  	c.Assert(err, gc.IsNil)
    99  	s.waitFor(c, isDeployed(ctx))
   100  
   101  	// ...and that the deployer, no longer bearing any responsibility for the
   102  	// Dying unit, does nothing further to it.
   103  	err = u1.Refresh()
   104  	c.Assert(err, gc.IsNil)
   105  	c.Assert(u1.Life(), gc.Equals, state.Dying)
   106  }
   107  
   108  func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) {
   109  	// Create a service, and a couple of units.
   110  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   111  	u0, err := svc.AddUnit()
   112  	c.Assert(err, gc.IsNil)
   113  	u1, err := svc.AddUnit()
   114  	c.Assert(err, gc.IsNil)
   115  
   116  	// Assign the units to the machine, and set them to Dying/Dead.
   117  	err = u0.AssignToMachine(s.machine)
   118  	c.Assert(err, gc.IsNil)
   119  	err = u0.EnsureDead()
   120  	c.Assert(err, gc.IsNil)
   121  	err = u1.AssignToMachine(s.machine)
   122  	c.Assert(err, gc.IsNil)
   123  	// note: this is not a sane state; for the unit to have a status it must
   124  	// have been deployed. But it's instructive to check that the right thing
   125  	// would happen if it were possible to have a dying unit in this situation.
   126  	err = u1.SetStatus(params.StatusInstalled, "", nil)
   127  	c.Assert(err, gc.IsNil)
   128  	err = u1.Destroy()
   129  	c.Assert(err, gc.IsNil)
   130  
   131  	// When the deployer is started, in each case (1) no unit agent is deployed
   132  	// and (2) the non-Alive unit is been removed from state.
   133  	dep, ctx := s.makeDeployerAndContext(c)
   134  	defer stop(c, dep)
   135  	s.waitFor(c, isRemoved(s.State, u0.Name()))
   136  	s.waitFor(c, isRemoved(s.State, u1.Name()))
   137  	s.waitFor(c, isDeployed(ctx))
   138  }
   139  
   140  func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) {
   141  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   142  	u, err := svc.AddUnit()
   143  	c.Assert(err, gc.IsNil)
   144  	err = u.AssignToMachine(s.machine)
   145  	c.Assert(err, gc.IsNil)
   146  	rus := []*state.RelationUnit{}
   147  	logging := s.AddTestingCharm(c, "logging")
   148  	for _, name := range []string{"subsvc0", "subsvc1"} {
   149  		s.AddTestingService(c, name, logging)
   150  		eps, err := s.State.InferEndpoints([]string{"wordpress", name})
   151  		c.Assert(err, gc.IsNil)
   152  		rel, err := s.State.AddRelation(eps...)
   153  		c.Assert(err, gc.IsNil)
   154  		ru, err := rel.Unit(u)
   155  		c.Assert(err, gc.IsNil)
   156  		rus = append(rus, ru)
   157  	}
   158  	return u, rus
   159  }
   160  
   161  func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) {
   162  	// Create a deployer acting on behalf of the principal.
   163  	u, rus := s.prepareSubordinates(c)
   164  	dep, ctx := s.makeDeployerAndContext(c)
   165  	defer stop(c, dep)
   166  
   167  	// Add a subordinate, and wait for it to be deployed.
   168  	err := rus[0].EnterScope(nil)
   169  	c.Assert(err, gc.IsNil)
   170  	sub0, err := s.State.Unit("subsvc0/0")
   171  	c.Assert(err, gc.IsNil)
   172  	// Make sure the principal is deployed first, then the subordinate
   173  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name()))
   174  
   175  	// And another.
   176  	err = rus[1].EnterScope(nil)
   177  	c.Assert(err, gc.IsNil)
   178  	sub1, err := s.State.Unit("subsvc1/0")
   179  	c.Assert(err, gc.IsNil)
   180  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
   181  
   182  	// Set one to Dying; check nothing happens.
   183  	err = sub1.Destroy()
   184  	c.Assert(err, gc.IsNil)
   185  	s.State.StartSync()
   186  	c.Assert(isRemoved(s.State, sub1.Name())(c), gc.Equals, false)
   187  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
   188  
   189  	// Set the other to Dead; check it's recalled and removed.
   190  	err = sub0.EnsureDead()
   191  	c.Assert(err, gc.IsNil)
   192  	s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name()))
   193  	s.waitFor(c, isRemoved(s.State, sub0.Name()))
   194  }
   195  
   196  func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) {
   197  	// Add two subordinate units and set them to Dead/Dying respectively.
   198  	_, rus := s.prepareSubordinates(c)
   199  	err := rus[0].EnterScope(nil)
   200  	c.Assert(err, gc.IsNil)
   201  	sub0, err := s.State.Unit("subsvc0/0")
   202  	c.Assert(err, gc.IsNil)
   203  	err = sub0.EnsureDead()
   204  	c.Assert(err, gc.IsNil)
   205  	err = rus[1].EnterScope(nil)
   206  	c.Assert(err, gc.IsNil)
   207  	sub1, err := s.State.Unit("subsvc1/0")
   208  	c.Assert(err, gc.IsNil)
   209  	err = sub1.Destroy()
   210  	c.Assert(err, gc.IsNil)
   211  
   212  	// When we start a new deployer, neither unit will be deployed and
   213  	// both will be removed.
   214  	dep, _ := s.makeDeployerAndContext(c)
   215  	defer stop(c, dep)
   216  	s.waitFor(c, isRemoved(s.State, sub0.Name()))
   217  	s.waitFor(c, isRemoved(s.State, sub1.Name()))
   218  }
   219  
   220  func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) {
   221  	s.BackingState.StartSync()
   222  	if t(c) {
   223  		return
   224  	}
   225  	timeout := time.After(coretesting.LongWait)
   226  	for {
   227  		select {
   228  		case <-timeout:
   229  			c.Fatalf("timeout")
   230  		case <-time.After(coretesting.ShortWait):
   231  			if t(c) {
   232  				return
   233  			}
   234  		}
   235  	}
   236  }
   237  
   238  func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool {
   239  	return func(c *gc.C) bool {
   240  		sort.Strings(expected)
   241  		current, err := ctx.DeployedUnits()
   242  		c.Assert(err, gc.IsNil)
   243  		sort.Strings(current)
   244  		return strings.Join(expected, ":") == strings.Join(current, ":")
   245  	}
   246  }
   247  
   248  func isRemoved(st *state.State, name string) func(*gc.C) bool {
   249  	return func(c *gc.C) bool {
   250  		_, err := st.Unit(name)
   251  		if errors.IsNotFoundError(err) {
   252  			return true
   253  		}
   254  		c.Assert(err, gc.IsNil)
   255  		return false
   256  	}
   257  }
   258  
   259  func stop(c *gc.C, w worker.Worker) {
   260  	c.Assert(worker.Stop(w), gc.IsNil)
   261  }