github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	now := time.Now()
    81  	sInfo := status.StatusInfo{
    82  		Status:  status.Idle,
    83  		Message: "",
    84  		Since:   &now,
    85  	}
    86  	err = u1.SetAgentStatus(sInfo)
    87  	c.Assert(err, jc.ErrorIsNil)
    88  	err = u1.Destroy()
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name()))
    91  
    92  	// Cause a unit to become Dead, and check that it is both recalled and
    93  	// removed from state.
    94  	err = u0.EnsureDead()
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	s.waitFor(c, isRemoved(s.State, u0.Name()))
    97  	s.waitFor(c, isDeployed(ctx, u1.Name()))
    98  
    99  	// Remove the Dying unit from the machine, and check that it is recalled...
   100  	err = u1.UnassignFromMachine()
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	s.waitFor(c, isDeployed(ctx))
   103  
   104  	// ...and that the deployer, no longer bearing any responsibility for the
   105  	// Dying unit, does nothing further to it.
   106  	err = u1.Refresh()
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	c.Assert(u1.Life(), gc.Equals, state.Dying)
   109  }
   110  
   111  func (s *deployerSuite) TestInitialStatusMessages(c *gc.C) {
   112  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   113  	u0, err := svc.AddUnit()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	dep, _ := s.makeDeployerAndContext(c)
   117  	defer stop(c, dep)
   118  	err = u0.AssignToMachine(s.machine)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	s.waitFor(c, unitStatus(u0, status.StatusInfo{
   121  		Status:  status.Waiting,
   122  		Message: "installing agent",
   123  	}))
   124  }
   125  
   126  func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) {
   127  	// Create a service, and a couple of units.
   128  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   129  	u0, err := svc.AddUnit()
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	u1, err := svc.AddUnit()
   132  	c.Assert(err, jc.ErrorIsNil)
   133  
   134  	// Assign the units to the machine, and set them to Dying/Dead.
   135  	err = u0.AssignToMachine(s.machine)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	err = u0.EnsureDead()
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	err = u1.AssignToMachine(s.machine)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	// note: this is not a sane state; for the unit to have a status it must
   142  	// have been deployed. But it's instructive to check that the right thing
   143  	// would happen if it were possible to have a dying unit in this situation.
   144  	now := time.Now()
   145  	sInfo := status.StatusInfo{
   146  		Status:  status.Idle,
   147  		Message: "",
   148  		Since:   &now,
   149  	}
   150  	err = u1.SetAgentStatus(sInfo)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	err = u1.Destroy()
   153  	c.Assert(err, jc.ErrorIsNil)
   154  
   155  	// When the deployer is started, in each case (1) no unit agent is deployed
   156  	// and (2) the non-Alive unit is been removed from state.
   157  	dep, ctx := s.makeDeployerAndContext(c)
   158  	defer stop(c, dep)
   159  	s.waitFor(c, isRemoved(s.State, u0.Name()))
   160  	s.waitFor(c, isRemoved(s.State, u1.Name()))
   161  	s.waitFor(c, isDeployed(ctx))
   162  }
   163  
   164  func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) {
   165  	svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   166  	u, err := svc.AddUnit()
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	err = u.AssignToMachine(s.machine)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	rus := []*state.RelationUnit{}
   171  	logging := s.AddTestingCharm(c, "logging")
   172  	for _, name := range []string{"subsvc0", "subsvc1"} {
   173  		s.AddTestingService(c, name, logging)
   174  		eps, err := s.State.InferEndpoints("wordpress", name)
   175  		c.Assert(err, jc.ErrorIsNil)
   176  		rel, err := s.State.AddRelation(eps...)
   177  		c.Assert(err, jc.ErrorIsNil)
   178  		ru, err := rel.Unit(u)
   179  		c.Assert(err, jc.ErrorIsNil)
   180  		rus = append(rus, ru)
   181  	}
   182  	return u, rus
   183  }
   184  
   185  func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) {
   186  	// Create a deployer acting on behalf of the principal.
   187  	u, rus := s.prepareSubordinates(c)
   188  	dep, ctx := s.makeDeployerAndContext(c)
   189  	defer stop(c, dep)
   190  
   191  	// Add a subordinate, and wait for it to be deployed.
   192  	err := rus[0].EnterScope(nil)
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	sub0, err := s.State.Unit("subsvc0/0")
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	// Make sure the principal is deployed first, then the subordinate
   197  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name()))
   198  
   199  	// And another.
   200  	err = rus[1].EnterScope(nil)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	sub1, err := s.State.Unit("subsvc1/0")
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
   205  
   206  	// Set one to Dying; check nothing happens.
   207  	err = sub1.Destroy()
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	s.State.StartSync()
   210  	c.Assert(isRemoved(s.State, sub1.Name())(c), jc.IsFalse)
   211  	s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
   212  
   213  	// Set the other to Dead; check it's recalled and removed.
   214  	err = sub0.EnsureDead()
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name()))
   217  	s.waitFor(c, isRemoved(s.State, sub0.Name()))
   218  }
   219  
   220  func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) {
   221  	// Add two subordinate units and set them to Dead/Dying respectively.
   222  	_, rus := s.prepareSubordinates(c)
   223  	err := rus[0].EnterScope(nil)
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	sub0, err := s.State.Unit("subsvc0/0")
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	err = sub0.EnsureDead()
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	err = rus[1].EnterScope(nil)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	sub1, err := s.State.Unit("subsvc1/0")
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	err = sub1.Destroy()
   234  	c.Assert(err, jc.ErrorIsNil)
   235  
   236  	// When we start a new deployer, neither unit will be deployed and
   237  	// both will be removed.
   238  	dep, _ := s.makeDeployerAndContext(c)
   239  	defer stop(c, dep)
   240  	s.waitFor(c, isRemoved(s.State, sub0.Name()))
   241  	s.waitFor(c, isRemoved(s.State, sub1.Name()))
   242  }
   243  
   244  func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) {
   245  	s.BackingState.StartSync()
   246  	if t(c) {
   247  		return
   248  	}
   249  	timeout := time.After(coretesting.LongWait)
   250  	for {
   251  		select {
   252  		case <-timeout:
   253  			c.Fatalf("timeout")
   254  		case <-time.After(coretesting.ShortWait):
   255  			if t(c) {
   256  				return
   257  			}
   258  		}
   259  	}
   260  }
   261  
   262  func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool {
   263  	return func(c *gc.C) bool {
   264  		sort.Strings(expected)
   265  		current, err := ctx.DeployedUnits()
   266  		c.Assert(err, jc.ErrorIsNil)
   267  		sort.Strings(current)
   268  		return strings.Join(expected, ":") == strings.Join(current, ":")
   269  	}
   270  }
   271  
   272  func isRemoved(st *state.State, name string) func(*gc.C) bool {
   273  	return func(c *gc.C) bool {
   274  		_, err := st.Unit(name)
   275  		if errors.IsNotFound(err) {
   276  			return true
   277  		}
   278  		c.Assert(err, jc.ErrorIsNil)
   279  		return false
   280  	}
   281  }
   282  
   283  func unitStatus(u *state.Unit, statusInfo status.StatusInfo) func(*gc.C) bool {
   284  	return func(c *gc.C) bool {
   285  		sInfo, err := u.Status()
   286  		c.Assert(err, jc.ErrorIsNil)
   287  		return sInfo.Status == statusInfo.Status && sInfo.Message == statusInfo.Message
   288  	}
   289  }
   290  
   291  func stop(c *gc.C, w worker.Worker) {
   292  	c.Assert(worker.Stop(w), gc.IsNil)
   293  }