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