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