github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/worker/provisioner/lxc-broker_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "launchpad.net/gocheck"
    16  
    17  	"github.com/juju/juju/agent"
    18  	"github.com/juju/juju/constraints"
    19  	"github.com/juju/juju/container"
    20  	"github.com/juju/juju/container/lxc/mock"
    21  	lxctesting "github.com/juju/juju/container/lxc/testing"
    22  	"github.com/juju/juju/environs"
    23  	"github.com/juju/juju/instance"
    24  	instancetest "github.com/juju/juju/instance/testing"
    25  	jujutesting "github.com/juju/juju/juju/testing"
    26  	"github.com/juju/juju/network"
    27  	"github.com/juju/juju/state"
    28  	"github.com/juju/juju/state/api/params"
    29  	coretesting "github.com/juju/juju/testing"
    30  	coretools "github.com/juju/juju/tools"
    31  	"github.com/juju/juju/version"
    32  	"github.com/juju/juju/worker/provisioner"
    33  )
    34  
    35  type lxcSuite struct {
    36  	lxctesting.TestSuite
    37  	events chan mock.Event
    38  }
    39  
    40  type lxcBrokerSuite struct {
    41  	lxcSuite
    42  	broker      environs.InstanceBroker
    43  	agentConfig agent.ConfigSetterWriter
    44  }
    45  
    46  var _ = gc.Suite(&lxcBrokerSuite{})
    47  
    48  func (s *lxcSuite) SetUpTest(c *gc.C) {
    49  	s.TestSuite.SetUpTest(c)
    50  	s.events = make(chan mock.Event)
    51  	go func() {
    52  		for event := range s.events {
    53  			c.Output(3, fmt.Sprintf("lxc event: <%s, %s>", event.Action, event.InstanceId))
    54  		}
    55  	}()
    56  	s.TestSuite.ContainerFactory.AddListener(s.events)
    57  }
    58  
    59  func (s *lxcSuite) TearDownTest(c *gc.C) {
    60  	close(s.events)
    61  	s.TestSuite.TearDownTest(c)
    62  }
    63  
    64  func (s *lxcBrokerSuite) SetUpTest(c *gc.C) {
    65  	s.lxcSuite.SetUpTest(c)
    66  	tools := &coretools.Tools{
    67  		Version: version.MustParseBinary("2.3.4-foo-bar"),
    68  		URL:     "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
    69  	}
    70  	var err error
    71  	s.agentConfig, err = agent.NewAgentConfig(
    72  		agent.AgentConfigParams{
    73  			DataDir:           "/not/used/here",
    74  			Tag:               "tag",
    75  			UpgradedToVersion: version.Current.Number,
    76  			Password:          "dummy-secret",
    77  			Nonce:             "nonce",
    78  			APIAddresses:      []string{"10.0.0.1:1234"},
    79  			CACert:            coretesting.CACert,
    80  		})
    81  	c.Assert(err, gc.IsNil)
    82  	managerConfig := container.ManagerConfig{container.ConfigName: "juju", "use-clone": "false"}
    83  	s.broker, err = provisioner.NewLxcBroker(&fakeAPI{}, tools, s.agentConfig, managerConfig)
    84  	c.Assert(err, gc.IsNil)
    85  }
    86  
    87  func (s *lxcBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance {
    88  	machineNonce := "fake-nonce"
    89  	stateInfo := jujutesting.FakeStateInfo(machineId)
    90  	apiInfo := jujutesting.FakeAPIInfo(machineId)
    91  	machineConfig := environs.NewMachineConfig(machineId, machineNonce, nil, stateInfo, apiInfo)
    92  	cons := constraints.Value{}
    93  	possibleTools := s.broker.(coretools.HasTools).Tools("precise")
    94  	lxc, _, _, err := s.broker.StartInstance(environs.StartInstanceParams{
    95  		Constraints:   cons,
    96  		Tools:         possibleTools,
    97  		MachineConfig: machineConfig,
    98  	})
    99  	c.Assert(err, gc.IsNil)
   100  	return lxc
   101  }
   102  
   103  func (s *lxcBrokerSuite) TestStartInstance(c *gc.C) {
   104  	machineId := "1/lxc/0"
   105  	lxc := s.startInstance(c, machineId)
   106  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   107  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   108  	s.assertInstances(c, lxc)
   109  	// Uses default network config
   110  	lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf"))
   111  	c.Assert(err, gc.IsNil)
   112  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.type = veth")
   113  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = lxcbr0")
   114  }
   115  
   116  func (s *lxcBrokerSuite) TestStartInstanceWithBridgeEnviron(c *gc.C) {
   117  	s.agentConfig.SetValue(agent.LxcBridge, "br0")
   118  	machineId := "1/lxc/0"
   119  	lxc := s.startInstance(c, machineId)
   120  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   121  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   122  	s.assertInstances(c, lxc)
   123  	// Uses default network config
   124  	lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf"))
   125  	c.Assert(err, gc.IsNil)
   126  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.type = veth")
   127  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = br0")
   128  }
   129  
   130  func (s *lxcBrokerSuite) TestStopInstance(c *gc.C) {
   131  	lxc0 := s.startInstance(c, "1/lxc/0")
   132  	lxc1 := s.startInstance(c, "1/lxc/1")
   133  	lxc2 := s.startInstance(c, "1/lxc/2")
   134  
   135  	err := s.broker.StopInstances(lxc0.Id())
   136  	c.Assert(err, gc.IsNil)
   137  	s.assertInstances(c, lxc1, lxc2)
   138  	c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist)
   139  	c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory)
   140  
   141  	err = s.broker.StopInstances(lxc1.Id(), lxc2.Id())
   142  	c.Assert(err, gc.IsNil)
   143  	s.assertInstances(c)
   144  }
   145  
   146  func (s *lxcBrokerSuite) TestAllInstances(c *gc.C) {
   147  	lxc0 := s.startInstance(c, "1/lxc/0")
   148  	lxc1 := s.startInstance(c, "1/lxc/1")
   149  	s.assertInstances(c, lxc0, lxc1)
   150  
   151  	err := s.broker.StopInstances(lxc1.Id())
   152  	c.Assert(err, gc.IsNil)
   153  	lxc2 := s.startInstance(c, "1/lxc/2")
   154  	s.assertInstances(c, lxc0, lxc2)
   155  }
   156  
   157  func (s *lxcBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) {
   158  	results, err := s.broker.AllInstances()
   159  	c.Assert(err, gc.IsNil)
   160  	instancetest.MatchInstances(c, results, inst...)
   161  }
   162  
   163  func (s *lxcBrokerSuite) lxcContainerDir(inst instance.Instance) string {
   164  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   165  }
   166  
   167  func (s *lxcBrokerSuite) lxcRemovedContainerDir(inst instance.Instance) string {
   168  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   169  }
   170  
   171  type lxcProvisionerSuite struct {
   172  	CommonProvisionerSuite
   173  	lxcSuite
   174  	parentMachineId string
   175  	events          chan mock.Event
   176  }
   177  
   178  var _ = gc.Suite(&lxcProvisionerSuite{})
   179  
   180  func (s *lxcProvisionerSuite) SetUpSuite(c *gc.C) {
   181  	s.CommonProvisionerSuite.SetUpSuite(c)
   182  	s.lxcSuite.SetUpSuite(c)
   183  }
   184  
   185  func (s *lxcProvisionerSuite) TearDownSuite(c *gc.C) {
   186  	s.lxcSuite.TearDownSuite(c)
   187  	s.CommonProvisionerSuite.TearDownSuite(c)
   188  }
   189  
   190  func (s *lxcProvisionerSuite) SetUpTest(c *gc.C) {
   191  	s.CommonProvisionerSuite.SetUpTest(c)
   192  	s.lxcSuite.SetUpTest(c)
   193  
   194  	// The lxc provisioner actually needs the machine it is being created on
   195  	// to be in state, in order to get the watcher.
   196  	m, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits, state.JobManageEnviron)
   197  	c.Assert(err, gc.IsNil)
   198  	err = m.SetAddresses(network.NewAddress("0.1.2.3", network.ScopeUnknown))
   199  	c.Assert(err, gc.IsNil)
   200  
   201  	hostPorts := [][]network.HostPort{{{
   202  		Address: network.NewAddress("0.1.2.3", network.ScopeUnknown),
   203  		Port:    1234,
   204  	}}}
   205  	err = s.State.SetAPIHostPorts(hostPorts)
   206  	c.Assert(err, gc.IsNil)
   207  
   208  	c.Assert(err, gc.IsNil)
   209  	s.parentMachineId = m.Id()
   210  	s.APILogin(c, m)
   211  	err = m.SetAgentVersion(version.Current)
   212  	c.Assert(err, gc.IsNil)
   213  
   214  	s.events = make(chan mock.Event, 25)
   215  	s.ContainerFactory.AddListener(s.events)
   216  }
   217  
   218  func (s *lxcProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   219  	s.State.StartSync()
   220  	event := <-s.events
   221  	c.Assert(event.Action, gc.Equals, mock.Created)
   222  	event = <-s.events
   223  	c.Assert(event.Action, gc.Equals, mock.Started)
   224  	err := machine.Refresh()
   225  	c.Assert(err, gc.IsNil)
   226  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   227  	return event.InstanceId
   228  }
   229  
   230  func (s *lxcProvisionerSuite) expectStopped(c *gc.C, instId string) {
   231  	s.State.StartSync()
   232  	event := <-s.events
   233  	c.Assert(event.Action, gc.Equals, mock.Stopped)
   234  	event = <-s.events
   235  	c.Assert(event.Action, gc.Equals, mock.Destroyed)
   236  	c.Assert(event.InstanceId, gc.Equals, instId)
   237  }
   238  
   239  func (s *lxcProvisionerSuite) expectNoEvents(c *gc.C) {
   240  	select {
   241  	case event := <-s.events:
   242  		c.Fatalf("unexpected event %#v", event)
   243  	case <-time.After(coretesting.ShortWait):
   244  		return
   245  	}
   246  }
   247  
   248  func (s *lxcProvisionerSuite) TearDownTest(c *gc.C) {
   249  	close(s.events)
   250  	s.lxcSuite.TearDownTest(c)
   251  	s.CommonProvisionerSuite.TearDownTest(c)
   252  }
   253  
   254  func (s *lxcProvisionerSuite) newLxcProvisioner(c *gc.C) provisioner.Provisioner {
   255  	parentMachineTag := names.NewMachineTag(s.parentMachineId).String()
   256  	agentConfig := s.AgentConfigForTag(c, parentMachineTag)
   257  	tools, err := s.provisioner.Tools(agentConfig.Tag())
   258  	c.Assert(err, gc.IsNil)
   259  	managerConfig := container.ManagerConfig{container.ConfigName: "juju", "use-clone": "false"}
   260  	broker, err := provisioner.NewLxcBroker(s.provisioner, tools, agentConfig, managerConfig)
   261  	c.Assert(err, gc.IsNil)
   262  	return provisioner.NewContainerProvisioner(instance.LXC, s.provisioner, agentConfig, broker)
   263  }
   264  
   265  func (s *lxcProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   266  	p := s.newLxcProvisioner(c)
   267  	c.Assert(p.Stop(), gc.IsNil)
   268  }
   269  
   270  func (s *lxcProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
   271  	p := s.newLxcProvisioner(c)
   272  	defer stop(c, p)
   273  
   274  	// Check that an instance is not provisioned when the machine is created.
   275  	_, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits)
   276  	c.Assert(err, gc.IsNil)
   277  
   278  	s.expectNoEvents(c)
   279  }
   280  
   281  func (s *lxcProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
   282  	p := s.newLxcProvisioner(c)
   283  	defer stop(c, p)
   284  
   285  	w, err := provisioner.GetRetryWatcher(p)
   286  	c.Assert(w, gc.IsNil)
   287  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   288  }
   289  
   290  func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine {
   291  	template := state.MachineTemplate{
   292  		Series: coretesting.FakeDefaultSeries,
   293  		Jobs:   []state.MachineJob{state.JobHostUnits},
   294  	}
   295  	container, err := s.State.AddMachineInsideMachine(template, s.parentMachineId, instance.LXC)
   296  	c.Assert(err, gc.IsNil)
   297  	return container
   298  }
   299  
   300  func (s *lxcProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
   301  	p := s.newLxcProvisioner(c)
   302  	defer stop(c, p)
   303  
   304  	container := s.addContainer(c)
   305  	instId := s.expectStarted(c, container)
   306  
   307  	// ...and removed, along with the machine, when the machine is Dead.
   308  	c.Assert(container.EnsureDead(), gc.IsNil)
   309  	s.expectStopped(c, instId)
   310  	s.waitRemoved(c, container)
   311  }
   312  
   313  type fakeAPI struct{}
   314  
   315  func (*fakeAPI) ContainerConfig() (params.ContainerConfig, error) {
   316  	return params.ContainerConfig{
   317  		ProviderType:            "fake",
   318  		AuthorizedKeys:          coretesting.FakeAuthKeys,
   319  		SSLHostnameVerification: true}, nil
   320  }