github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 ¶ms.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 }