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 }