github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/provisioner/container_initialisation_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 "fmt" 8 "os/exec" 9 "runtime" 10 "sync/atomic" 11 "time" 12 13 "github.com/juju/mutex" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/arch" 16 "github.com/juju/utils/clock" 17 jujuos "github.com/juju/utils/os" 18 "github.com/juju/utils/packaging/manager" 19 "github.com/juju/utils/series" 20 "github.com/juju/version" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/juju/names.v2" 23 24 "github.com/juju/juju/agent" 25 apiprovisioner "github.com/juju/juju/api/provisioner" 26 "github.com/juju/juju/container" 27 "github.com/juju/juju/environs" 28 "github.com/juju/juju/instance" 29 "github.com/juju/juju/state" 30 coretesting "github.com/juju/juju/testing" 31 "github.com/juju/juju/tools" 32 jujuversion "github.com/juju/juju/version" 33 "github.com/juju/juju/watcher" 34 "github.com/juju/juju/worker" 35 "github.com/juju/juju/worker/provisioner" 36 ) 37 38 type ContainerSetupSuite struct { 39 CommonProvisionerSuite 40 p provisioner.Provisioner 41 agentConfig agent.ConfigSetter 42 // Record the apt commands issued as part of container initialisation 43 aptCmdChan <-chan *exec.Cmd 44 lockName string 45 } 46 47 var _ = gc.Suite(&ContainerSetupSuite{}) 48 49 func (s *ContainerSetupSuite) SetUpSuite(c *gc.C) { 50 // TODO(bogdanteleaga): Fix this on windows 51 if runtime.GOOS == "windows" { 52 c.Skip("bug 1403084: Skipping container tests on windows") 53 } 54 s.CommonProvisionerSuite.SetUpSuite(c) 55 } 56 57 func (s *ContainerSetupSuite) TearDownSuite(c *gc.C) { 58 s.CommonProvisionerSuite.TearDownSuite(c) 59 } 60 61 func allFatal(error) bool { 62 return true 63 } 64 65 func noImportance(err0, err1 error) bool { 66 return false 67 } 68 69 func (s *ContainerSetupSuite) SetUpTest(c *gc.C) { 70 s.CommonProvisionerSuite.SetUpTest(c) 71 aptCmdChan := s.HookCommandOutput(&manager.CommandOutput, []byte{}, nil) 72 s.aptCmdChan = aptCmdChan 73 74 // Set up provisioner for the state machine. 75 s.agentConfig = s.AgentConfigForTag(c, names.NewMachineTag("0")) 76 var err error 77 s.p, err = provisioner.NewEnvironProvisioner(s.provisioner, s.agentConfig, s.Environ) 78 c.Assert(err, jc.ErrorIsNil) 79 s.lockName = "provisioner-test" 80 } 81 82 func (s *ContainerSetupSuite) TearDownTest(c *gc.C) { 83 if s.p != nil { 84 stop(c, s.p) 85 } 86 s.CommonProvisionerSuite.TearDownTest(c) 87 } 88 89 func (s *ContainerSetupSuite) setupContainerWorker(c *gc.C, tag names.MachineTag) (watcher.StringsHandler, worker.Runner) { 90 runner := worker.NewRunner(allFatal, noImportance, worker.RestartDelay) 91 pr := apiprovisioner.NewState(s.st) 92 machine, err := pr.Machine(tag) 93 c.Assert(err, jc.ErrorIsNil) 94 err = machine.SetSupportedContainers(instance.ContainerTypes...) 95 c.Assert(err, jc.ErrorIsNil) 96 cfg := s.AgentConfigForTag(c, tag) 97 98 watcherName := fmt.Sprintf("%s-container-watcher", machine.Id()) 99 params := provisioner.ContainerSetupParams{ 100 Runner: runner, 101 WorkerName: watcherName, 102 SupportedContainers: instance.ContainerTypes, 103 Machine: machine, 104 Provisioner: pr, 105 Config: cfg, 106 InitLockName: s.lockName, 107 } 108 handler := provisioner.NewContainerSetupHandler(params) 109 runner.StartWorker(watcherName, func() (worker.Worker, error) { 110 return watcher.NewStringsWorker(watcher.StringsConfig{ 111 Handler: handler, 112 }) 113 }) 114 return handler, runner 115 } 116 117 func (s *ContainerSetupSuite) createContainer(c *gc.C, host *state.Machine, ctype instance.ContainerType) { 118 inst := s.checkStartInstance(c, host) 119 s.setupContainerWorker(c, host.Tag().(names.MachineTag)) 120 121 // make a container on the host machine 122 template := state.MachineTemplate{ 123 Series: series.LatestLts(), 124 Jobs: []state.MachineJob{state.JobHostUnits}, 125 } 126 container, err := s.State.AddMachineInsideMachine(template, host.Id(), ctype) 127 c.Assert(err, jc.ErrorIsNil) 128 129 // the host machine agent should not attempt to create the container 130 s.checkNoOperations(c) 131 132 // cleanup 133 c.Assert(container.EnsureDead(), gc.IsNil) 134 c.Assert(container.Remove(), gc.IsNil) 135 c.Assert(host.EnsureDead(), gc.IsNil) 136 s.checkStopInstances(c, inst) 137 s.waitForRemovalMark(c, host) 138 } 139 140 func (s *ContainerSetupSuite) assertContainerProvisionerStarted( 141 c *gc.C, host *state.Machine, ctype instance.ContainerType) { 142 143 // A stub worker callback to record what happens. 144 var provisionerStarted uint32 145 startProvisionerWorker := func(runner worker.Runner, containerType instance.ContainerType, 146 pr *apiprovisioner.State, cfg agent.Config, broker environs.InstanceBroker, 147 toolsFinder provisioner.ToolsFinder) error { 148 c.Assert(containerType, gc.Equals, ctype) 149 c.Assert(cfg.Tag(), gc.Equals, host.Tag()) 150 atomic.StoreUint32(&provisionerStarted, 1) 151 return nil 152 } 153 s.PatchValue(&provisioner.StartProvisioner, startProvisionerWorker) 154 155 s.createContainer(c, host, ctype) 156 // Consume the apt command used to initialise the container. 157 select { 158 case <-s.aptCmdChan: 159 case <-time.After(coretesting.LongWait): 160 c.Fatalf("took too long to get command from channel") 161 } 162 // the container worker should have created the provisioner 163 c.Assert(atomic.LoadUint32(&provisionerStarted) > 0, jc.IsTrue) 164 } 165 166 func (s *ContainerSetupSuite) TestContainerProvisionerStarted(c *gc.C) { 167 // Specifically ignore LXD here, if present in instance.ContainerTypes. 168 containerTypes := []instance.ContainerType{instance.KVM} 169 for _, ctype := range containerTypes { 170 // create a machine to host the container. 171 m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 172 Series: series.LatestLts(), 173 Jobs: []state.MachineJob{state.JobHostUnits}, 174 Constraints: s.defaultConstraints, 175 }) 176 c.Assert(err, jc.ErrorIsNil) 177 err = m.SetSupportedContainers(containerTypes) 178 c.Assert(err, jc.ErrorIsNil) 179 current := version.Binary{ 180 Number: jujuversion.Current, 181 Arch: arch.HostArch(), 182 Series: series.HostSeries(), 183 } 184 err = m.SetAgentVersion(current) 185 c.Assert(err, jc.ErrorIsNil) 186 s.assertContainerProvisionerStarted(c, m, ctype) 187 } 188 } 189 190 func (s *ContainerSetupSuite) TestKvmContainerUsesHostArch(c *gc.C) { 191 // KVM should do what it's told, and use the architecture in 192 // constraints. 193 s.PatchValue(&arch.HostArch, func() string { return arch.PPC64EL }) 194 s.testContainerConstraintsArch(c, instance.KVM, arch.AMD64) 195 } 196 197 func (s *ContainerSetupSuite) testContainerConstraintsArch(c *gc.C, containerType instance.ContainerType, expectArch string) { 198 var called uint32 199 s.PatchValue(provisioner.GetToolsFinder, func(*apiprovisioner.State) provisioner.ToolsFinder { 200 return toolsFinderFunc(func(v version.Number, series string, arch string) (tools.List, error) { 201 atomic.StoreUint32(&called, 1) 202 c.Assert(arch, gc.Equals, expectArch) 203 result := version.Binary{ 204 Number: v, 205 Arch: arch, 206 Series: series, 207 } 208 return tools.List{{Version: result}}, nil 209 }) 210 }) 211 212 s.PatchValue(&provisioner.StartProvisioner, func(runner worker.Runner, containerType instance.ContainerType, 213 pr *apiprovisioner.State, cfg agent.Config, broker environs.InstanceBroker, 214 toolsFinder provisioner.ToolsFinder) error { 215 toolsFinder.FindTools(jujuversion.Current, series.HostSeries(), arch.AMD64) 216 return nil 217 }) 218 219 // create a machine to host the container. 220 m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 221 Series: series.LatestLts(), 222 Jobs: []state.MachineJob{state.JobHostUnits}, 223 Constraints: s.defaultConstraints, 224 }) 225 c.Assert(err, jc.ErrorIsNil) 226 err = m.SetSupportedContainers([]instance.ContainerType{containerType}) 227 c.Assert(err, jc.ErrorIsNil) 228 current := version.Binary{ 229 Number: jujuversion.Current, 230 Arch: arch.HostArch(), 231 Series: series.HostSeries(), 232 } 233 err = m.SetAgentVersion(current) 234 c.Assert(err, jc.ErrorIsNil) 235 236 s.createContainer(c, m, containerType) 237 select { 238 case <-s.aptCmdChan: 239 case <-time.After(coretesting.LongWait): 240 c.Fatalf("took too long to get command from channel") 241 } 242 c.Assert(atomic.LoadUint32(&called) > 0, jc.IsTrue) 243 } 244 245 func (s *ContainerSetupSuite) TestContainerManagerConfigName(c *gc.C) { 246 pr := apiprovisioner.NewState(s.st) 247 cfg, err := provisioner.ContainerManagerConfig(instance.KVM, pr, s.agentConfig) 248 c.Assert(err, jc.ErrorIsNil) 249 c.Assert(cfg[container.ConfigModelUUID], gc.Equals, coretesting.ModelTag.Id()) 250 } 251 252 type ContainerInstance struct { 253 ctype instance.ContainerType 254 packages [][]string 255 } 256 257 func (s *ContainerSetupSuite) assertContainerInitialised(c *gc.C, cont ContainerInstance) { 258 // A noop worker callback. 259 startProvisionerWorker := func(runner worker.Runner, containerType instance.ContainerType, 260 pr *apiprovisioner.State, cfg agent.Config, broker environs.InstanceBroker, 261 toolsFinder provisioner.ToolsFinder) error { 262 return nil 263 } 264 s.PatchValue(&provisioner.StartProvisioner, startProvisionerWorker) 265 266 current_os, err := series.GetOSFromSeries(series.HostSeries()) 267 c.Assert(err, jc.ErrorIsNil) 268 269 var ser string 270 var expected_initial []string 271 switch current_os { 272 case jujuos.CentOS: 273 ser = "centos7" 274 expected_initial = []string{ 275 "yum", "--assumeyes", "--debuglevel=1", "install"} 276 default: 277 ser = "precise" 278 expected_initial = []string{ 279 "apt-get", "--option=Dpkg::Options::=--force-confold", 280 "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", 281 "install"} 282 } 283 284 // create a machine to host the container. 285 m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 286 Series: ser, // precise requires special apt parameters, so we use that series here. 287 Jobs: []state.MachineJob{state.JobHostUnits}, 288 Constraints: s.defaultConstraints, 289 }) 290 c.Assert(err, jc.ErrorIsNil) 291 err = m.SetSupportedContainers([]instance.ContainerType{instance.LXD, instance.KVM}) 292 c.Assert(err, jc.ErrorIsNil) 293 current := version.Binary{ 294 Number: jujuversion.Current, 295 Arch: arch.HostArch(), 296 Series: series.HostSeries(), 297 } 298 err = m.SetAgentVersion(current) 299 c.Assert(err, jc.ErrorIsNil) 300 301 s.createContainer(c, m, cont.ctype) 302 303 for _, pack := range cont.packages { 304 select { 305 case cmd := <-s.aptCmdChan: 306 expected := append(expected_initial, pack...) 307 c.Assert(cmd.Args, gc.DeepEquals, expected) 308 case <-time.After(coretesting.LongWait): 309 c.Fatalf("took too long to get command from channel") 310 } 311 } 312 } 313 314 func (s *ContainerSetupSuite) TestContainerInitialised(c *gc.C) { 315 cont, err := getContainerInstance() 316 c.Assert(err, jc.ErrorIsNil) 317 318 for _, test := range cont { 319 s.assertContainerInitialised(c, test) 320 } 321 } 322 323 func (s *ContainerSetupSuite) TestContainerInitLockError(c *gc.C) { 324 spec := mutex.Spec{ 325 Name: s.lockName, 326 Clock: clock.WallClock, 327 Delay: coretesting.ShortWait, 328 } 329 releaser, err := mutex.Acquire(spec) 330 c.Assert(err, jc.ErrorIsNil) 331 defer releaser.Release() 332 333 m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 334 Series: series.LatestLts(), 335 Jobs: []state.MachineJob{state.JobHostUnits}, 336 Constraints: s.defaultConstraints, 337 }) 338 c.Assert(err, jc.ErrorIsNil) 339 current := version.Binary{ 340 Number: jujuversion.Current, 341 Arch: arch.HostArch(), 342 Series: series.HostSeries(), 343 } 344 err = m.SetAgentVersion(current) 345 c.Assert(err, jc.ErrorIsNil) 346 347 handler, runner := s.setupContainerWorker(c, m.Tag().(names.MachineTag)) 348 runner.Kill() 349 err = runner.Wait() 350 c.Assert(err, jc.ErrorIsNil) 351 352 _, err = handler.SetUp() 353 c.Assert(err, jc.ErrorIsNil) 354 abort := make(chan struct{}) 355 close(abort) 356 err = handler.Handle(abort, []string{"0/lxd/0"}) 357 c.Assert(err, gc.ErrorMatches, ".*failed to acquire initialization lock:.*") 358 359 } 360 361 type toolsFinderFunc func(v version.Number, series string, arch string) (tools.List, error) 362 363 func (t toolsFinderFunc) FindTools(v version.Number, series string, arch string) (tools.List, error) { 364 return t(v, series, arch) 365 } 366 367 func getContainerInstance() (cont []ContainerInstance, err error) { 368 cont = []ContainerInstance{ 369 {instance.KVM, [][]string{ 370 {"uvtool-libvirt"}, 371 {"uvtool"}, 372 }}, 373 } 374 return cont, nil 375 }