github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/addresser/worker_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package addresser_test
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/api"
    15  	apiaddresser "github.com/juju/juju/api/addresser"
    16  	"github.com/juju/juju/feature"
    17  	"github.com/juju/juju/instance"
    18  	"github.com/juju/juju/juju/testing"
    19  	"github.com/juju/juju/network"
    20  	"github.com/juju/juju/provider/common"
    21  	"github.com/juju/juju/provider/dummy"
    22  	"github.com/juju/juju/state"
    23  	coretesting "github.com/juju/juju/testing"
    24  	"github.com/juju/juju/worker"
    25  	"github.com/juju/juju/worker/addresser"
    26  )
    27  
    28  type workerSuite struct {
    29  	testing.JujuConnSuite
    30  
    31  	Enabled  bool
    32  	MachineA *state.Machine
    33  	MachineB *state.Machine
    34  
    35  	Worker  worker.Worker
    36  	OpsChan chan dummy.Operation
    37  
    38  	APIConnection api.Connection
    39  	API           *apiaddresser.API
    40  }
    41  
    42  func (s *workerSuite) SetUpTest(c *gc.C) {
    43  	s.JujuConnSuite.SetUpTest(c)
    44  	if s.Enabled {
    45  		s.SetFeatureFlags(feature.AddressAllocation)
    46  	}
    47  
    48  	// Unbreak dummy provider methods.
    49  	s.AssertConfigParameterUpdated(c, "broken", "")
    50  
    51  	s.APIConnection, _ = s.OpenAPIAsNewMachine(c, state.JobManageModel)
    52  	s.API = s.APIConnection.Addresser()
    53  
    54  	machineA, err := s.State.AddMachine("quantal", state.JobHostUnits)
    55  	s.MachineA = machineA
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	err = s.MachineA.SetProvisioned("foo", "fake_nonce", nil)
    58  	c.Assert(err, jc.ErrorIsNil)
    59  
    60  	// This machine will be destroyed after address creation to test the
    61  	// handling of addresses for machines that have gone.
    62  	machineB, err := s.State.AddMachine("quantal", state.JobHostUnits)
    63  	s.MachineB = machineB
    64  	c.Assert(err, jc.ErrorIsNil)
    65  
    66  	s.createAddresses(c)
    67  	s.State.StartSync()
    68  
    69  	s.OpsChan = make(chan dummy.Operation, 10)
    70  	dummy.Listen(s.OpsChan)
    71  
    72  	// Start the Addresser worker.
    73  	w, err := addresser.NewWorker(s.API)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	s.Worker = w
    76  
    77  	s.waitForInitialDead(c)
    78  }
    79  
    80  func (s *workerSuite) TearDownTest(c *gc.C) {
    81  	c.Assert(worker.Stop(s.Worker), jc.ErrorIsNil)
    82  	s.JujuConnSuite.TearDownTest(c)
    83  }
    84  
    85  func (s *workerSuite) createAddresses(c *gc.C) {
    86  	addresses := []string{
    87  		"0.1.2.3", "0.1.2.4", "0.1.2.5", "0.1.2.6",
    88  	}
    89  	for i, rawAddr := range addresses {
    90  		addr := network.NewAddress(rawAddr)
    91  		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
    92  		c.Assert(err, jc.ErrorIsNil)
    93  		if i%2 == 1 {
    94  			err = ipAddr.AllocateTo(s.MachineB.Id(), "wobble", "")
    95  		} else {
    96  			err = ipAddr.AllocateTo(s.MachineA.Id(), "wobble", "")
    97  			c.Assert(err, jc.ErrorIsNil)
    98  		}
    99  	}
   100  	// Two of the addresses start out allocated to this
   101  	// machine which we destroy to test the handling of
   102  	// addresses allocated to dead machines.
   103  	err := s.MachineB.EnsureDead()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	err = s.MachineB.Remove()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  }
   108  
   109  func (s *workerSuite) waitForInitialDead(c *gc.C) {
   110  	for a := common.ShortAttempt.Start(); a.Next(); {
   111  		dead, err := s.State.DeadIPAddresses()
   112  		c.Assert(err, jc.ErrorIsNil)
   113  		if s.Enabled {
   114  			// We expect dead IP addresses to be removed with
   115  			// enabled Addresser worker.
   116  			if len(dead) == 0 {
   117  				break
   118  			}
   119  			if !a.HasNext() {
   120  				c.Fatalf("timeout waiting for initial change (dead: %#v)", dead)
   121  			}
   122  		} else {
   123  			// Without Addresser worker the dead IP addresses
   124  			// will stay.
   125  			if len(dead) == 0 {
   126  				c.Fatal("IP addresses unexpectedly removed")
   127  			}
   128  		}
   129  	}
   130  }
   131  
   132  func (s *workerSuite) waitForReleaseOp(c *gc.C) dummy.OpReleaseAddress {
   133  	var releaseOp dummy.OpReleaseAddress
   134  	var ok bool
   135  	select {
   136  	case op := <-s.OpsChan:
   137  		releaseOp, ok = op.(dummy.OpReleaseAddress)
   138  		c.Assert(ok, jc.IsTrue)
   139  	case <-time.After(coretesting.LongWait):
   140  		c.Fatalf("timeout while expecting release operation")
   141  	}
   142  	return releaseOp
   143  }
   144  
   145  func (s *workerSuite) assertNoReleaseOp(c *gc.C) {
   146  	select {
   147  	case op := <-s.OpsChan:
   148  		_, ok := op.(dummy.OpReleaseAddress)
   149  		if ok {
   150  			c.Fatalf("received unexpected release operation")
   151  		}
   152  	case <-time.After(coretesting.ShortWait):
   153  		return
   154  	}
   155  }
   156  
   157  func (s *workerSuite) makeReleaseOp(digit int) dummy.OpReleaseAddress {
   158  	return dummy.OpReleaseAddress{
   159  		Env:        "admin",
   160  		InstanceId: "foo",
   161  		SubnetId:   "foobar",
   162  		Address:    network.NewAddress(fmt.Sprintf("0.1.2.%d", digit)),
   163  	}
   164  }
   165  
   166  func (s *workerSuite) assertIPAddressLife(c *gc.C, value string, life state.Life) {
   167  	ipAddr, err := s.State.IPAddress(value)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	c.Assert(ipAddr.Life(), gc.Equals, life)
   170  }
   171  
   172  func (s *workerSuite) assertIPAddressRemoved(c *gc.C, value string) {
   173  	for a := common.ShortAttempt.Start(); a.Next(); {
   174  		_, err := s.State.IPAddress(value)
   175  		if errors.IsNotFound(err) {
   176  			break
   177  		}
   178  		if !a.HasNext() {
   179  			c.Fatalf("IP address not removed")
   180  		}
   181  	}
   182  }
   183  
   184  // workerEnabledSuite runs the test with the enabled address allocation.
   185  type workerEnabledSuite struct {
   186  	workerSuite
   187  }
   188  
   189  var _ = gc.Suite(&workerEnabledSuite{})
   190  
   191  func (s *workerEnabledSuite) SetUpTest(c *gc.C) {
   192  	s.workerSuite.Enabled = true
   193  
   194  	s.workerSuite.SetUpTest(c)
   195  }
   196  
   197  func (s *workerEnabledSuite) TestWorkerIsStringsWorker(c *gc.C) {
   198  	// In case of an environment able to allocte/deallocate
   199  	// IP addresses the addresser worker is no finished worker.
   200  	// See also TestWorkerIsFinishedWorker.
   201  	c.Assert(s.Worker, gc.Not(gc.FitsTypeOf), worker.FinishedWorker{})
   202  }
   203  
   204  func (s *workerEnabledSuite) TestWorkerReleasesAlreadyDead(c *gc.C) {
   205  	// Wait for releases of 0.1.2.4 and 0.1.2.6 first. It's
   206  	// explicitely needed for this test for the assertion.
   207  	op1 := s.waitForReleaseOp(c)
   208  	op2 := s.waitForReleaseOp(c)
   209  
   210  	expected := []dummy.OpReleaseAddress{s.makeReleaseOp(4), s.makeReleaseOp(6)}
   211  
   212  	// The machines are dead, so ReleaseAddress should be called with
   213  	// instance.UnknownId.
   214  	expected[0].InstanceId = instance.UnknownId
   215  	expected[1].InstanceId = instance.UnknownId
   216  
   217  	c.Assert([]dummy.OpReleaseAddress{op1, op2}, jc.SameContents, expected)
   218  }
   219  
   220  func (s *workerEnabledSuite) TestWorkerIgnoresAliveAddresses(c *gc.C) {
   221  	// Wait for releases of 0.1.2.4 and 0.1.2.6 first. Result is not needed.
   222  	s.waitForReleaseOp(c)
   223  	s.waitForReleaseOp(c)
   224  
   225  	// Add a new alive address.
   226  	addr := network.NewAddress("0.1.2.9")
   227  	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	err = ipAddr.AllocateTo(s.MachineA.Id(), "wobble", "")
   230  	c.Assert(err, jc.ErrorIsNil)
   231  
   232  	// Assert no ReleaseAddress call..
   233  	s.assertNoReleaseOp(c)
   234  
   235  	// The worker must not kill this address.
   236  	for a := common.ShortAttempt.Start(); a.Next(); {
   237  		s.assertIPAddressLife(c, "0.1.2.9", state.Alive)
   238  	}
   239  }
   240  
   241  func (s *workerEnabledSuite) TestWorkerRemovesDeadAddress(c *gc.C) {
   242  	// Wait for releases of 0.1.2.4 and 0.1.2.6 first. Result is not needed.
   243  	s.waitForReleaseOp(c)
   244  	s.waitForReleaseOp(c)
   245  
   246  	// Kill IP address.
   247  	addr, err := s.State.IPAddress("0.1.2.3")
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	err = addr.EnsureDead()
   250  	c.Assert(err, jc.ErrorIsNil)
   251  
   252  	// Wait for ReleaseAddress attempt.
   253  	op := s.waitForReleaseOp(c)
   254  	c.Assert(op, jc.DeepEquals, s.makeReleaseOp(3))
   255  
   256  	// The address should have been removed from state.
   257  	s.assertIPAddressRemoved(c, "0.1.2.3")
   258  }
   259  
   260  func (s *workerEnabledSuite) TestWorkerAcceptsBrokenRelease(c *gc.C) {
   261  	// Wait for releases of 0.1.2.4 and 0.1.2.6 first. Result is not needed.
   262  	s.waitForReleaseOp(c)
   263  	s.waitForReleaseOp(c)
   264  
   265  	// Break ReleaseAddress and kill IP address 0.1.2.3.
   266  	s.AssertConfigParameterUpdated(c, "broken", "ReleaseAddress")
   267  
   268  	ipAddr, err := s.State.IPAddress("0.1.2.3")
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	err = ipAddr.EnsureDead()
   271  	c.Assert(err, jc.ErrorIsNil)
   272  
   273  	// The address should stay in state.
   274  	s.assertIPAddressLife(c, "0.1.2.3", state.Dead)
   275  	s.assertNoReleaseOp(c)
   276  
   277  	// Make ReleaseAddress work again, it must be cleaned up then.
   278  	s.AssertConfigParameterUpdated(c, "broken", "")
   279  
   280  	// The address should have been removed from state.
   281  	s.assertIPAddressRemoved(c, "0.1.2.3")
   282  }
   283  
   284  func (s *workerEnabledSuite) TestMachineRemovalTriggersWorker(c *gc.C) {
   285  	// Wait for releases of 0.1.2.4 and 0.1.2.6 first. Result is not needed.
   286  	s.waitForReleaseOp(c)
   287  	s.waitForReleaseOp(c)
   288  
   289  	// Add special test machine.
   290  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	err = machine.SetProvisioned("foo", "really-fake", nil)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  
   295  	// Add a new alive address.
   296  	addr := network.NewAddress("0.1.2.9")
   297  	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	err = ipAddr.AllocateTo(machine.Id(), "foo", "")
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	c.Assert(ipAddr.InstanceId(), gc.Equals, instance.Id("foo"))
   302  
   303  	// Ensure the alive address is not changed.
   304  	for a := common.ShortAttempt.Start(); a.Next(); {
   305  		s.assertIPAddressLife(c, ipAddr.Value(), state.Alive)
   306  	}
   307  
   308  	err = machine.EnsureDead()
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	err = machine.Remove()
   311  	c.Assert(err, jc.ErrorIsNil)
   312  
   313  	s.assertIPAddressLife(c, ipAddr.Value(), state.Dead)
   314  
   315  	// Wait for ReleaseAddress attempt.
   316  	op := s.waitForReleaseOp(c)
   317  	c.Assert(op, jc.DeepEquals, s.makeReleaseOp(9))
   318  
   319  	// The address should have been removed from state.
   320  	s.assertIPAddressRemoved(c, "0.1.2.9")
   321  }
   322  
   323  // workerEnabledSuite runs the test with the disabled address allocation.
   324  type workerDisabledSuite struct {
   325  	workerSuite
   326  }
   327  
   328  var _ = gc.Suite(&workerDisabledSuite{})
   329  
   330  func (s *workerDisabledSuite) SetUpTest(c *gc.C) {
   331  	s.workerSuite.Enabled = false
   332  
   333  	s.workerSuite.SetUpTest(c)
   334  }
   335  
   336  func (s *workerDisabledSuite) TestWorkerIsFinishedWorker(c *gc.C) {
   337  	// In case of an environment not able to allocte/deallocate
   338  	// IP addresses the worker is a finished worker.
   339  	// See also TestWorkerIsStringsWorker.
   340  	c.Assert(s.Worker, gc.FitsTypeOf, worker.FinishedWorker{})
   341  }
   342  
   343  func (s *workerDisabledSuite) TestWorkerIgnoresAddresses(c *gc.C) {
   344  	// The worker must not kill these addresses.
   345  	for a := common.ShortAttempt.Start(); a.Next(); {
   346  		s.assertIPAddressLife(c, "0.1.2.3", state.Alive)
   347  		s.assertIPAddressLife(c, "0.1.2.4", state.Dead)
   348  		s.assertIPAddressLife(c, "0.1.2.5", state.Alive)
   349  		s.assertIPAddressLife(c, "0.1.2.6", state.Dead)
   350  	}
   351  }