github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/machiner/machiner_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machiner_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"net"
     9  	"path/filepath"
    10  	stdtesting "testing"
    11  	"time"
    12  
    13  	"github.com/juju/names"
    14  	gitjujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/agent"
    19  	"github.com/juju/juju/api"
    20  	apimachiner "github.com/juju/juju/api/machiner"
    21  	"github.com/juju/juju/apiserver/params"
    22  	"github.com/juju/juju/juju/testing"
    23  	"github.com/juju/juju/network"
    24  	"github.com/juju/juju/state"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker"
    27  	"github.com/juju/juju/worker/machiner"
    28  )
    29  
    30  type MachinerSuite struct {
    31  	coretesting.BaseSuite
    32  	accessor    *mockMachineAccessor
    33  	agentConfig agent.Config
    34  	addresses   []net.Addr
    35  }
    36  
    37  var _ = gc.Suite(&MachinerSuite{})
    38  
    39  func (s *MachinerSuite) SetUpTest(c *gc.C) {
    40  	s.BaseSuite.SetUpTest(c)
    41  	s.accessor = &mockMachineAccessor{}
    42  	s.accessor.machine.watcher.changes = make(chan struct{})
    43  	s.accessor.machine.life = params.Alive
    44  	s.agentConfig = agentConfig(names.NewMachineTag("123"))
    45  	s.addresses = []net.Addr{ // anything will do
    46  		&net.IPAddr{IP: net.IPv4bcast},
    47  		&net.IPAddr{IP: net.IPv4zero},
    48  	}
    49  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
    50  		return s.addresses, nil
    51  	})
    52  }
    53  
    54  func (s *MachinerSuite) TestMachinerStorageAttached(c *gc.C) {
    55  	// Machine is dying. We'll respond to "EnsureDead" by
    56  	// saying that there are still storage attachments;
    57  	// this should not cause an error.
    58  	s.accessor.machine.life = params.Dying
    59  	s.accessor.machine.SetErrors(
    60  		nil, // SetMachineAddresses
    61  		nil, // SetStatus
    62  		nil, // Watch
    63  		nil, // Refresh
    64  		nil, // SetStatus
    65  		&params.Error{Code: params.CodeMachineHasAttachedStorage},
    66  	)
    67  
    68  	worker := machiner.NewMachiner(s.accessor, s.agentConfig, false)
    69  	s.accessor.machine.watcher.changes <- struct{}{}
    70  	worker.Kill()
    71  	c.Check(worker.Wait(), jc.ErrorIsNil)
    72  
    73  	s.accessor.CheckCalls(c, []gitjujutesting.StubCall{{
    74  		FuncName: "Machine",
    75  		Args:     []interface{}{s.agentConfig.Tag()},
    76  	}})
    77  
    78  	s.accessor.machine.watcher.CheckCalls(c, []gitjujutesting.StubCall{
    79  		{FuncName: "Changes"}, {FuncName: "Changes"}, {FuncName: "Stop"},
    80  	})
    81  
    82  	s.accessor.machine.CheckCalls(c, []gitjujutesting.StubCall{{
    83  		FuncName: "SetMachineAddresses",
    84  		Args: []interface{}{
    85  			network.NewAddresses(
    86  				"255.255.255.255",
    87  				"0.0.0.0",
    88  			),
    89  		},
    90  	}, {
    91  		FuncName: "SetStatus",
    92  		Args: []interface{}{
    93  			params.StatusStarted,
    94  			"",
    95  			map[string]interface{}(nil),
    96  		},
    97  	}, {
    98  		FuncName: "Watch",
    99  	}, {
   100  		FuncName: "Refresh",
   101  	}, {
   102  		FuncName: "Life",
   103  	}, {
   104  		FuncName: "SetStatus",
   105  		Args: []interface{}{
   106  			params.StatusStopped,
   107  			"",
   108  			map[string]interface{}(nil),
   109  		},
   110  	}, {
   111  		FuncName: "EnsureDead",
   112  	}})
   113  }
   114  
   115  // worstCase is used for timeouts when timing out
   116  // will fail the test. Raising this value should
   117  // not affect the overall running time of the tests
   118  // unless they fail.
   119  const worstCase = 5 * time.Second
   120  
   121  func TestPackage(t *stdtesting.T) {
   122  	coretesting.MgoTestPackage(t)
   123  }
   124  
   125  type MachinerStateSuite struct {
   126  	testing.JujuConnSuite
   127  
   128  	st            api.Connection
   129  	machinerState *apimachiner.State
   130  	machine       *state.Machine
   131  	apiMachine    *apimachiner.Machine
   132  }
   133  
   134  var _ = gc.Suite(&MachinerStateSuite{})
   135  
   136  func (s *MachinerStateSuite) SetUpTest(c *gc.C) {
   137  	s.JujuConnSuite.SetUpTest(c)
   138  	s.st, s.machine = s.OpenAPIAsNewMachine(c)
   139  
   140  	// Create the machiner API facade.
   141  	s.machinerState = s.st.Machiner()
   142  	c.Assert(s.machinerState, gc.NotNil)
   143  
   144  	// Get the machine through the facade.
   145  	var err error
   146  	s.apiMachine, err = s.machinerState.Machine(s.machine.Tag().(names.MachineTag))
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	c.Assert(s.apiMachine.Tag(), gc.Equals, s.machine.Tag())
   149  	// Isolate tests better by not using real interface addresses.
   150  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
   151  		return nil, nil
   152  	})
   153  	s.PatchValue(&network.InterfaceByNameAddrs, func(string) ([]net.Addr, error) {
   154  		return nil, nil
   155  	})
   156  	s.PatchValue(&network.LXCNetDefaultConfig, "")
   157  
   158  }
   159  
   160  func (s *MachinerStateSuite) waitMachineStatus(c *gc.C, m *state.Machine, expectStatus state.Status) {
   161  	timeout := time.After(worstCase)
   162  	for {
   163  		select {
   164  		case <-timeout:
   165  			c.Fatalf("timeout while waiting for machine status to change")
   166  		case <-time.After(10 * time.Millisecond):
   167  			statusInfo, err := m.Status()
   168  			c.Assert(err, jc.ErrorIsNil)
   169  			if statusInfo.Status != expectStatus {
   170  				c.Logf("machine %q status is %s, still waiting", m, statusInfo.Status)
   171  				continue
   172  			}
   173  			return
   174  		}
   175  	}
   176  }
   177  
   178  var _ worker.NotifyWatchHandler = (*machiner.Machiner)(nil)
   179  
   180  type mockConfig struct {
   181  	agent.Config
   182  	tag names.Tag
   183  }
   184  
   185  func (mock *mockConfig) Tag() names.Tag {
   186  	return mock.tag
   187  }
   188  
   189  func agentConfig(tag names.Tag) agent.Config {
   190  	return &mockConfig{tag: tag}
   191  }
   192  
   193  func (s *MachinerStateSuite) TestNotFoundOrUnauthorized(c *gc.C) {
   194  	mr := machiner.NewMachiner(
   195  		machiner.APIMachineAccessor{s.machinerState},
   196  		agentConfig(names.NewMachineTag("99")),
   197  		false,
   198  	)
   199  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   200  }
   201  
   202  func (s *MachinerStateSuite) makeMachiner(ignoreAddresses bool) worker.Worker {
   203  	return machiner.NewMachiner(
   204  		machiner.APIMachineAccessor{s.machinerState},
   205  		agentConfig(s.apiMachine.Tag()),
   206  		ignoreAddresses,
   207  	)
   208  }
   209  
   210  func (s *MachinerStateSuite) TestRunStop(c *gc.C) {
   211  	mr := s.makeMachiner(false)
   212  	c.Assert(worker.Stop(mr), gc.IsNil)
   213  	c.Assert(s.apiMachine.Refresh(), gc.IsNil)
   214  	c.Assert(s.apiMachine.Life(), gc.Equals, params.Alive)
   215  }
   216  
   217  func (s *MachinerStateSuite) TestStartSetsStatus(c *gc.C) {
   218  	statusInfo, err := s.machine.Status()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	c.Assert(statusInfo.Status, gc.Equals, state.StatusPending)
   221  	c.Assert(statusInfo.Message, gc.Equals, "")
   222  
   223  	mr := s.makeMachiner(false)
   224  	defer worker.Stop(mr)
   225  
   226  	s.waitMachineStatus(c, s.machine, state.StatusStarted)
   227  }
   228  
   229  func (s *MachinerStateSuite) TestSetsStatusWhenDying(c *gc.C) {
   230  	mr := s.makeMachiner(false)
   231  	defer worker.Stop(mr)
   232  	c.Assert(s.machine.Destroy(), gc.IsNil)
   233  	s.waitMachineStatus(c, s.machine, state.StatusStopped)
   234  }
   235  
   236  func (s *MachinerStateSuite) TestSetDead(c *gc.C) {
   237  	mr := s.makeMachiner(false)
   238  	defer worker.Stop(mr)
   239  	c.Assert(s.machine.Destroy(), gc.IsNil)
   240  	s.State.StartSync()
   241  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   242  	c.Assert(s.machine.Refresh(), gc.IsNil)
   243  	c.Assert(s.machine.Life(), gc.Equals, state.Dead)
   244  }
   245  
   246  func (s *MachinerStateSuite) TestSetDeadWithDyingUnit(c *gc.C) {
   247  	mr := s.makeMachiner(false)
   248  	defer worker.Stop(mr)
   249  
   250  	// Add a service, assign to machine.
   251  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   252  	unit, err := wordpress.AddUnit()
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	err = unit.AssignToMachine(s.machine)
   255  	c.Assert(err, jc.ErrorIsNil)
   256  
   257  	// Service alive, can't destroy machine.
   258  	err = s.machine.Destroy()
   259  	c.Assert(err, jc.Satisfies, state.IsHasAssignedUnitsError)
   260  
   261  	err = wordpress.Destroy()
   262  	c.Assert(err, jc.ErrorIsNil)
   263  
   264  	// With dying unit, machine can now be marked as dying.
   265  	c.Assert(s.machine.Destroy(), gc.IsNil)
   266  	s.State.StartSync()
   267  	c.Assert(s.machine.Refresh(), gc.IsNil)
   268  	c.Assert(s.machine.Life(), gc.Equals, state.Dying)
   269  
   270  	// When the unit is ultimately destroyed, the machine becomes dead.
   271  	err = unit.Destroy()
   272  	c.Assert(err, jc.ErrorIsNil)
   273  	s.State.StartSync()
   274  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   275  
   276  }
   277  
   278  func (s *MachinerStateSuite) setupSetMachineAddresses(c *gc.C, ignore bool) {
   279  	lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
   280  	netConf := []byte(`
   281    # comments ignored
   282  LXC_BR= ignored
   283  LXC_ADDR = "fooo"
   284  LXC_BRIDGE="foobar" # detected
   285  anything else ignored
   286  LXC_BRIDGE="ignored"`[1:])
   287  	err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
   290  		addrs := []net.Addr{
   291  			&net.IPAddr{IP: net.IPv4(10, 0, 0, 1)},
   292  			&net.IPAddr{IP: net.IPv4(127, 0, 0, 1)},
   293  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, // lxc bridge address ignored
   294  			&net.IPAddr{IP: net.IPv6loopback},
   295  			&net.UnixAddr{},                        // not IP, ignored
   296  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, // lxc bridge address ignored
   297  			&net.IPNet{IP: net.ParseIP("2001:db8::1")},
   298  			&net.IPAddr{IP: net.IPv4(169, 254, 1, 20)}, // LinkLocal Ignored
   299  			&net.IPNet{IP: net.ParseIP("fe80::1")},     // LinkLocal Ignored
   300  		}
   301  		return addrs, nil
   302  	})
   303  	s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
   304  		c.Assert(name, gc.Equals, "foobar")
   305  		return []net.Addr{
   306  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
   307  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
   308  		}, nil
   309  	})
   310  	s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
   311  
   312  	mr := s.makeMachiner(ignore)
   313  	defer worker.Stop(mr)
   314  	c.Assert(s.machine.Destroy(), gc.IsNil)
   315  	s.State.StartSync()
   316  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   317  	c.Assert(s.machine.Refresh(), gc.IsNil)
   318  }
   319  
   320  func (s *MachinerStateSuite) TestMachineAddresses(c *gc.C) {
   321  	s.setupSetMachineAddresses(c, false)
   322  	c.Assert(s.machine.MachineAddresses(), jc.DeepEquals, []network.Address{
   323  		network.NewAddress("2001:db8::1"),
   324  		network.NewScopedAddress("10.0.0.1", network.ScopeCloudLocal),
   325  		network.NewScopedAddress("::1", network.ScopeMachineLocal),
   326  		network.NewScopedAddress("127.0.0.1", network.ScopeMachineLocal),
   327  	})
   328  }
   329  
   330  func (s *MachinerStateSuite) TestMachineAddressesWithIgnoreFlag(c *gc.C) {
   331  	s.setupSetMachineAddresses(c, true)
   332  	c.Assert(s.machine.MachineAddresses(), gc.HasLen, 0)
   333  }