PageRenderTime 72ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/cmd/juju/status_test.go

https://github.com/frankban/juju
Go | 2762 lines | 2452 code | 199 blank | 111 comment | 28 complexity | 7a5ec530f4cde1a45d992b2cfd6fae94 MD5 | raw file
Possible License(s): AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. // Copyright 2012, 2013 Canonical Ltd.
  2. // Licensed under the AGPLv3, see LICENCE file for details.
  3. package main
  4. import (
  5. "bytes"
  6. "encoding/json"
  7. "fmt"
  8. "strings"
  9. "github.com/juju/cmd"
  10. jc "github.com/juju/testing/checkers"
  11. gc "gopkg.in/check.v1"
  12. "gopkg.in/juju/charm.v4"
  13. goyaml "gopkg.in/yaml.v1"
  14. "github.com/juju/juju/api"
  15. "github.com/juju/juju/cmd/envcmd"
  16. "github.com/juju/juju/constraints"
  17. "github.com/juju/juju/environs"
  18. "github.com/juju/juju/instance"
  19. "github.com/juju/juju/juju/testing"
  20. "github.com/juju/juju/network"
  21. "github.com/juju/juju/state"
  22. "github.com/juju/juju/state/multiwatcher"
  23. "github.com/juju/juju/state/presence"
  24. "github.com/juju/juju/testcharms"
  25. coretesting "github.com/juju/juju/testing"
  26. "github.com/juju/juju/version"
  27. )
  28. func runStatus(c *gc.C, args ...string) (code int, stdout, stderr []byte) {
  29. ctx := coretesting.Context(c)
  30. code = cmd.Main(envcmd.Wrap(&StatusCommand{}), ctx, args)
  31. stdout = ctx.Stdout.(*bytes.Buffer).Bytes()
  32. stderr = ctx.Stderr.(*bytes.Buffer).Bytes()
  33. return
  34. }
  35. type StatusSuite struct {
  36. testing.JujuConnSuite
  37. }
  38. var _ = gc.Suite(&StatusSuite{})
  39. type M map[string]interface{}
  40. type L []interface{}
  41. type testCase struct {
  42. summary string
  43. steps []stepper
  44. }
  45. func test(summary string, steps ...stepper) testCase {
  46. return testCase{summary, steps}
  47. }
  48. type stepper interface {
  49. step(c *gc.C, ctx *context)
  50. }
  51. //
  52. // context
  53. //
  54. func newContext(c *gc.C, st *state.State, env environs.Environ, adminUserTag string) *context {
  55. // We make changes in the API server's state so that
  56. // our changes to presence are immediately noticed
  57. // in the status.
  58. return &context{
  59. st: st,
  60. env: env,
  61. charms: make(map[string]*state.Charm),
  62. pingers: make(map[string]*presence.Pinger),
  63. adminUserTag: adminUserTag,
  64. }
  65. }
  66. type context struct {
  67. st *state.State
  68. env environs.Environ
  69. charms map[string]*state.Charm
  70. pingers map[string]*presence.Pinger
  71. adminUserTag string // A string repr of the tag.
  72. }
  73. func (ctx *context) reset(c *gc.C) {
  74. for _, up := range ctx.pingers {
  75. err := up.Kill()
  76. c.Check(err, jc.ErrorIsNil)
  77. }
  78. }
  79. func (ctx *context) run(c *gc.C, steps []stepper) {
  80. for i, s := range steps {
  81. c.Logf("step %d", i)
  82. c.Logf("%#v", s)
  83. s.step(c, ctx)
  84. }
  85. }
  86. func (ctx *context) setAgentPresence(c *gc.C, p presence.Presencer) *presence.Pinger {
  87. pinger, err := p.SetAgentPresence()
  88. c.Assert(err, jc.ErrorIsNil)
  89. ctx.st.StartSync()
  90. err = p.WaitAgentPresence(coretesting.LongWait)
  91. c.Assert(err, jc.ErrorIsNil)
  92. agentPresence, err := p.AgentPresence()
  93. c.Assert(err, jc.ErrorIsNil)
  94. c.Assert(agentPresence, jc.IsTrue)
  95. return pinger
  96. }
  97. func (s *StatusSuite) newContext(c *gc.C) *context {
  98. st := s.Environ.(testing.GetStater).GetStateInAPIServer()
  99. // We make changes in the API server's state so that
  100. // our changes to presence are immediately noticed
  101. // in the status.
  102. return newContext(c, st, s.Environ, s.AdminUserTag(c).String())
  103. }
  104. func (s *StatusSuite) resetContext(c *gc.C, ctx *context) {
  105. ctx.reset(c)
  106. s.JujuConnSuite.Reset(c)
  107. }
  108. // shortcuts for expected output.
  109. var (
  110. machine0 = M{
  111. "agent-state": "started",
  112. "dns-name": "dummyenv-0.dns",
  113. "instance-id": "dummyenv-0",
  114. "series": "quantal",
  115. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  116. "state-server-member-status": "adding-vote",
  117. }
  118. machine1 = M{
  119. "agent-state": "started",
  120. "dns-name": "dummyenv-1.dns",
  121. "instance-id": "dummyenv-1",
  122. "series": "quantal",
  123. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  124. }
  125. machine2 = M{
  126. "agent-state": "started",
  127. "dns-name": "dummyenv-2.dns",
  128. "instance-id": "dummyenv-2",
  129. "series": "quantal",
  130. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  131. }
  132. machine3 = M{
  133. "agent-state": "started",
  134. "dns-name": "dummyenv-3.dns",
  135. "instance-id": "dummyenv-3",
  136. "series": "quantal",
  137. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  138. }
  139. machine4 = M{
  140. "agent-state": "started",
  141. "dns-name": "dummyenv-4.dns",
  142. "instance-id": "dummyenv-4",
  143. "series": "quantal",
  144. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  145. }
  146. machine1WithContainers = M{
  147. "agent-state": "started",
  148. "containers": M{
  149. "1/lxc/0": M{
  150. "agent-state": "started",
  151. "containers": M{
  152. "1/lxc/0/lxc/0": M{
  153. "agent-state": "started",
  154. "dns-name": "dummyenv-3.dns",
  155. "instance-id": "dummyenv-3",
  156. "series": "quantal",
  157. },
  158. },
  159. "dns-name": "dummyenv-2.dns",
  160. "instance-id": "dummyenv-2",
  161. "series": "quantal",
  162. },
  163. "1/lxc/1": M{
  164. "instance-id": "pending",
  165. "series": "quantal",
  166. },
  167. },
  168. "dns-name": "dummyenv-1.dns",
  169. "instance-id": "dummyenv-1",
  170. "series": "quantal",
  171. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  172. }
  173. machine1WithContainersScoped = M{
  174. "agent-state": "started",
  175. "containers": M{
  176. "1/lxc/0": M{
  177. "agent-state": "started",
  178. "dns-name": "dummyenv-2.dns",
  179. "instance-id": "dummyenv-2",
  180. "series": "quantal",
  181. },
  182. },
  183. "dns-name": "dummyenv-1.dns",
  184. "instance-id": "dummyenv-1",
  185. "series": "quantal",
  186. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  187. }
  188. unexposedService = M{
  189. "charm": "cs:quantal/dummy-1",
  190. "exposed": false,
  191. }
  192. exposedService = M{
  193. "charm": "cs:quantal/dummy-1",
  194. "exposed": true,
  195. }
  196. )
  197. type outputFormat struct {
  198. name string
  199. marshal func(v interface{}) ([]byte, error)
  200. unmarshal func(data []byte, v interface{}) error
  201. }
  202. // statusFormats list all output formats supported by status command.
  203. var statusFormats = []outputFormat{
  204. {"yaml", goyaml.Marshal, goyaml.Unmarshal},
  205. {"json", json.Marshal, json.Unmarshal},
  206. }
  207. var machineCons = constraints.MustParse("cpu-cores=2 mem=8G root-disk=8G")
  208. var statusTests = []testCase{
  209. // Status tests
  210. test(
  211. "bootstrap and starting a single instance",
  212. addMachine{machineId: "0", job: state.JobManageEnviron},
  213. expect{
  214. "simulate juju bootstrap by adding machine/0 to the state",
  215. M{
  216. "environment": "dummyenv",
  217. "machines": M{
  218. "0": M{
  219. "instance-id": "pending",
  220. "series": "quantal",
  221. "state-server-member-status": "adding-vote",
  222. },
  223. },
  224. "services": M{},
  225. },
  226. },
  227. startAliveMachine{"0"},
  228. setAddresses{"0", []network.Address{
  229. network.NewAddress("10.0.0.1", network.ScopeUnknown),
  230. network.NewAddress("dummyenv-0.dns", network.ScopePublic),
  231. }},
  232. expect{
  233. "simulate the PA starting an instance in response to the state change",
  234. M{
  235. "environment": "dummyenv",
  236. "machines": M{
  237. "0": M{
  238. "agent-state": "pending",
  239. "dns-name": "dummyenv-0.dns",
  240. "instance-id": "dummyenv-0",
  241. "series": "quantal",
  242. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  243. "state-server-member-status": "adding-vote",
  244. },
  245. },
  246. "services": M{},
  247. },
  248. },
  249. setMachineStatus{"0", state.StatusStarted, ""},
  250. expect{
  251. "simulate the MA started and set the machine status",
  252. M{
  253. "environment": "dummyenv",
  254. "machines": M{
  255. "0": machine0,
  256. },
  257. "services": M{},
  258. },
  259. },
  260. setTools{"0", version.MustParseBinary("1.2.3-trusty-ppc")},
  261. expect{
  262. "simulate the MA setting the version",
  263. M{
  264. "environment": "dummyenv",
  265. "machines": M{
  266. "0": M{
  267. "dns-name": "dummyenv-0.dns",
  268. "instance-id": "dummyenv-0",
  269. "agent-version": "1.2.3",
  270. "agent-state": "started",
  271. "series": "quantal",
  272. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  273. "state-server-member-status": "adding-vote",
  274. },
  275. },
  276. "services": M{},
  277. },
  278. },
  279. ), test(
  280. "deploy two services and two networks",
  281. addMachine{machineId: "0", job: state.JobManageEnviron},
  282. startAliveMachine{"0"},
  283. setMachineStatus{"0", state.StatusStarted, ""},
  284. setAddresses{"0", []network.Address{
  285. network.NewAddress("10.0.0.1", network.ScopeUnknown),
  286. network.NewAddress("dummyenv-0.dns", network.ScopePublic),
  287. }},
  288. addCharm{"dummy"},
  289. addService{
  290. name: "networks-service",
  291. charm: "dummy",
  292. networks: []string{"net1", "net2"},
  293. cons: constraints.MustParse("networks=foo,bar,^no,^good"),
  294. },
  295. addService{
  296. name: "no-networks-service",
  297. charm: "dummy",
  298. cons: constraints.MustParse("networks=^mynet"),
  299. },
  300. addNetwork{
  301. name: "net1",
  302. providerId: network.Id("provider-net1"),
  303. cidr: "0.1.2.0/24",
  304. vlanTag: 0,
  305. },
  306. addNetwork{
  307. name: "net2",
  308. providerId: network.Id("provider-vlan42"),
  309. cidr: "0.42.1.0/24",
  310. vlanTag: 42,
  311. },
  312. expect{
  313. "simulate just the two services and a bootstrap node",
  314. M{
  315. "environment": "dummyenv",
  316. "machines": M{
  317. "0": machine0,
  318. },
  319. "services": M{
  320. "networks-service": M{
  321. "charm": "cs:quantal/dummy-1",
  322. "exposed": false,
  323. "networks": M{
  324. "enabled": L{"net1", "net2"},
  325. "disabled": L{"foo", "bar", "no", "good"},
  326. },
  327. },
  328. "no-networks-service": M{
  329. "charm": "cs:quantal/dummy-1",
  330. "exposed": false,
  331. "networks": M{
  332. "disabled": L{"mynet"},
  333. },
  334. },
  335. },
  336. "networks": M{
  337. "net1": M{
  338. "provider-id": "provider-net1",
  339. "cidr": "0.1.2.0/24",
  340. },
  341. "net2": M{
  342. "provider-id": "provider-vlan42",
  343. "cidr": "0.42.1.0/24",
  344. "vlan-tag": 42,
  345. },
  346. },
  347. },
  348. },
  349. ), test(
  350. "instance with different hardware characteristics",
  351. addMachine{machineId: "0", cons: machineCons, job: state.JobManageEnviron},
  352. setAddresses{"0", []network.Address{
  353. network.NewAddress("10.0.0.1", network.ScopeUnknown),
  354. network.NewAddress("dummyenv-0.dns", network.ScopePublic),
  355. }},
  356. startAliveMachine{"0"},
  357. setMachineStatus{"0", state.StatusStarted, ""},
  358. expect{
  359. "machine 0 has specific hardware characteristics",
  360. M{
  361. "environment": "dummyenv",
  362. "machines": M{
  363. "0": M{
  364. "agent-state": "started",
  365. "dns-name": "dummyenv-0.dns",
  366. "instance-id": "dummyenv-0",
  367. "series": "quantal",
  368. "hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M",
  369. "state-server-member-status": "adding-vote",
  370. },
  371. },
  372. "services": M{},
  373. },
  374. },
  375. ), test(
  376. "instance without addresses",
  377. addMachine{machineId: "0", cons: machineCons, job: state.JobManageEnviron},
  378. startAliveMachine{"0"},
  379. setMachineStatus{"0", state.StatusStarted, ""},
  380. expect{
  381. "machine 0 has no dns-name",
  382. M{
  383. "environment": "dummyenv",
  384. "machines": M{
  385. "0": M{
  386. "agent-state": "started",
  387. "instance-id": "dummyenv-0",
  388. "series": "quantal",
  389. "hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M",
  390. "state-server-member-status": "adding-vote",
  391. },
  392. },
  393. "services": M{},
  394. },
  395. },
  396. ), test(
  397. "test pending and missing machines",
  398. addMachine{machineId: "0", job: state.JobManageEnviron},
  399. expect{
  400. "machine 0 reports pending",
  401. M{
  402. "environment": "dummyenv",
  403. "machines": M{
  404. "0": M{
  405. "instance-id": "pending",
  406. "series": "quantal",
  407. "state-server-member-status": "adding-vote",
  408. },
  409. },
  410. "services": M{},
  411. },
  412. },
  413. startMissingMachine{"0"},
  414. expect{
  415. "machine 0 reports missing",
  416. M{
  417. "environment": "dummyenv",
  418. "machines": M{
  419. "0": M{
  420. "instance-state": "missing",
  421. "instance-id": "i-missing",
  422. "agent-state": "pending",
  423. "series": "quantal",
  424. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  425. "state-server-member-status": "adding-vote",
  426. },
  427. },
  428. "services": M{},
  429. },
  430. },
  431. ), test(
  432. "add two services and expose one, then add 2 more machines and some units",
  433. addMachine{machineId: "0", job: state.JobManageEnviron},
  434. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  435. startAliveMachine{"0"},
  436. setMachineStatus{"0", state.StatusStarted, ""},
  437. addCharm{"dummy"},
  438. addService{name: "dummy-service", charm: "dummy"},
  439. addService{name: "exposed-service", charm: "dummy"},
  440. expect{
  441. "no services exposed yet",
  442. M{
  443. "environment": "dummyenv",
  444. "machines": M{
  445. "0": machine0,
  446. },
  447. "services": M{
  448. "dummy-service": unexposedService,
  449. "exposed-service": unexposedService,
  450. },
  451. },
  452. },
  453. setServiceExposed{"exposed-service", true},
  454. expect{
  455. "one exposed service",
  456. M{
  457. "environment": "dummyenv",
  458. "machines": M{
  459. "0": machine0,
  460. },
  461. "services": M{
  462. "dummy-service": unexposedService,
  463. "exposed-service": exposedService,
  464. },
  465. },
  466. },
  467. addMachine{machineId: "1", job: state.JobHostUnits},
  468. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  469. startAliveMachine{"1"},
  470. setMachineStatus{"1", state.StatusStarted, ""},
  471. addMachine{machineId: "2", job: state.JobHostUnits},
  472. setAddresses{"2", []network.Address{network.NewAddress("dummyenv-2.dns", network.ScopeUnknown)}},
  473. startAliveMachine{"2"},
  474. setMachineStatus{"2", state.StatusStarted, ""},
  475. expect{
  476. "two more machines added",
  477. M{
  478. "environment": "dummyenv",
  479. "machines": M{
  480. "0": machine0,
  481. "1": machine1,
  482. "2": machine2,
  483. },
  484. "services": M{
  485. "dummy-service": unexposedService,
  486. "exposed-service": exposedService,
  487. },
  488. },
  489. },
  490. addUnit{"dummy-service", "1"},
  491. addAliveUnit{"exposed-service", "2"},
  492. setUnitStatus{"exposed-service/0", state.StatusError, "You Require More Vespene Gas", nil},
  493. // Open multiple ports with different protocols,
  494. // ensure they're sorted on protocol, then number.
  495. openUnitPort{"exposed-service/0", "udp", 10},
  496. openUnitPort{"exposed-service/0", "udp", 2},
  497. openUnitPort{"exposed-service/0", "tcp", 3},
  498. openUnitPort{"exposed-service/0", "tcp", 2},
  499. // Simulate some status with no info, while the agent is down.
  500. setUnitStatus{"dummy-service/0", state.StatusActive, "", nil},
  501. expect{
  502. "add two units, one alive (in error state), one down",
  503. M{
  504. "environment": "dummyenv",
  505. "machines": M{
  506. "0": machine0,
  507. "1": machine1,
  508. "2": machine2,
  509. },
  510. "services": M{
  511. "exposed-service": M{
  512. "charm": "cs:quantal/dummy-1",
  513. "exposed": true,
  514. "units": M{
  515. "exposed-service/0": M{
  516. "machine": "2",
  517. "agent-state": "error",
  518. "agent-state-info": "You Require More Vespene Gas",
  519. "open-ports": L{
  520. "2/tcp", "3/tcp", "2/udp", "10/udp",
  521. },
  522. "public-address": "dummyenv-2.dns",
  523. },
  524. },
  525. },
  526. "dummy-service": M{
  527. "charm": "cs:quantal/dummy-1",
  528. "exposed": false,
  529. "units": M{
  530. "dummy-service/0": M{
  531. "machine": "1",
  532. "agent-state": "down",
  533. "agent-state-info": "(started)",
  534. "public-address": "dummyenv-1.dns",
  535. },
  536. },
  537. },
  538. },
  539. },
  540. },
  541. addMachine{machineId: "3", job: state.JobHostUnits},
  542. startMachine{"3"},
  543. // Simulate some status with info, while the agent is down.
  544. setAddresses{"3", []network.Address{network.NewAddress("dummyenv-3.dns", network.ScopeUnknown)}},
  545. setMachineStatus{"3", state.StatusStopped, "Really?"},
  546. addMachine{machineId: "4", job: state.JobHostUnits},
  547. setAddresses{"4", []network.Address{network.NewAddress("dummyenv-4.dns", network.ScopeUnknown)}},
  548. startAliveMachine{"4"},
  549. setMachineStatus{"4", state.StatusError, "Beware the red toys"},
  550. ensureDyingUnit{"dummy-service/0"},
  551. addMachine{machineId: "5", job: state.JobHostUnits},
  552. ensureDeadMachine{"5"},
  553. expect{
  554. "add three more machine, one with a dead agent, one in error state and one dead itself; also one dying unit",
  555. M{
  556. "environment": "dummyenv",
  557. "machines": M{
  558. "0": machine0,
  559. "1": machine1,
  560. "2": machine2,
  561. "3": M{
  562. "dns-name": "dummyenv-3.dns",
  563. "instance-id": "dummyenv-3",
  564. "agent-state": "down",
  565. "agent-state-info": "(stopped: Really?)",
  566. "series": "quantal",
  567. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  568. },
  569. "4": M{
  570. "dns-name": "dummyenv-4.dns",
  571. "instance-id": "dummyenv-4",
  572. "agent-state": "error",
  573. "agent-state-info": "Beware the red toys",
  574. "series": "quantal",
  575. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  576. },
  577. "5": M{
  578. "life": "dead",
  579. "instance-id": "pending",
  580. "series": "quantal",
  581. },
  582. },
  583. "services": M{
  584. "exposed-service": M{
  585. "charm": "cs:quantal/dummy-1",
  586. "exposed": true,
  587. "units": M{
  588. "exposed-service/0": M{
  589. "machine": "2",
  590. "agent-state": "error",
  591. "agent-state-info": "You Require More Vespene Gas",
  592. "open-ports": L{
  593. "2/tcp", "3/tcp", "2/udp", "10/udp",
  594. },
  595. "public-address": "dummyenv-2.dns",
  596. },
  597. },
  598. },
  599. "dummy-service": M{
  600. "charm": "cs:quantal/dummy-1",
  601. "exposed": false,
  602. "units": M{
  603. "dummy-service/0": M{
  604. "machine": "1",
  605. "life": "dying",
  606. "agent-state": "down",
  607. "agent-state-info": "(started)",
  608. "public-address": "dummyenv-1.dns",
  609. },
  610. },
  611. },
  612. },
  613. },
  614. },
  615. scopedExpect{
  616. "scope status on dummy-service/0 unit",
  617. []string{"dummy-service/0"},
  618. M{
  619. "environment": "dummyenv",
  620. "machines": M{
  621. "1": machine1,
  622. },
  623. "services": M{
  624. "dummy-service": M{
  625. "charm": "cs:quantal/dummy-1",
  626. "exposed": false,
  627. "units": M{
  628. "dummy-service/0": M{
  629. "machine": "1",
  630. "life": "dying",
  631. "agent-state": "down",
  632. "agent-state-info": "(started)",
  633. "public-address": "dummyenv-1.dns",
  634. },
  635. },
  636. },
  637. },
  638. },
  639. },
  640. scopedExpect{
  641. "scope status on exposed-service service",
  642. []string{"exposed-service"},
  643. M{
  644. "environment": "dummyenv",
  645. "machines": M{
  646. "2": machine2,
  647. },
  648. "services": M{
  649. "exposed-service": M{
  650. "charm": "cs:quantal/dummy-1",
  651. "exposed": true,
  652. "units": M{
  653. "exposed-service/0": M{
  654. "machine": "2",
  655. "agent-state": "error",
  656. "agent-state-info": "You Require More Vespene Gas",
  657. "open-ports": L{
  658. "2/tcp", "3/tcp", "2/udp", "10/udp",
  659. },
  660. "public-address": "dummyenv-2.dns",
  661. },
  662. },
  663. },
  664. },
  665. },
  666. },
  667. scopedExpect{
  668. "scope status on service pattern",
  669. []string{"d*-service"},
  670. M{
  671. "environment": "dummyenv",
  672. "machines": M{
  673. "1": machine1,
  674. },
  675. "services": M{
  676. "dummy-service": M{
  677. "charm": "cs:quantal/dummy-1",
  678. "exposed": false,
  679. "units": M{
  680. "dummy-service/0": M{
  681. "machine": "1",
  682. "life": "dying",
  683. "agent-state": "down",
  684. "agent-state-info": "(started)",
  685. "public-address": "dummyenv-1.dns",
  686. },
  687. },
  688. },
  689. },
  690. },
  691. },
  692. scopedExpect{
  693. "scope status on unit pattern",
  694. []string{"e*posed-service/*"},
  695. M{
  696. "environment": "dummyenv",
  697. "machines": M{
  698. "2": machine2,
  699. },
  700. "services": M{
  701. "exposed-service": M{
  702. "charm": "cs:quantal/dummy-1",
  703. "exposed": true,
  704. "units": M{
  705. "exposed-service/0": M{
  706. "machine": "2",
  707. "agent-state": "error",
  708. "agent-state-info": "You Require More Vespene Gas",
  709. "open-ports": L{
  710. "2/tcp", "3/tcp", "2/udp", "10/udp",
  711. },
  712. "public-address": "dummyenv-2.dns",
  713. },
  714. },
  715. },
  716. },
  717. },
  718. },
  719. scopedExpect{
  720. "scope status on combination of service and unit patterns",
  721. []string{"exposed-service", "dummy-service", "e*posed-service/*", "dummy-service/*"},
  722. M{
  723. "environment": "dummyenv",
  724. "machines": M{
  725. "1": machine1,
  726. "2": machine2,
  727. },
  728. "services": M{
  729. "dummy-service": M{
  730. "charm": "cs:quantal/dummy-1",
  731. "exposed": false,
  732. "units": M{
  733. "dummy-service/0": M{
  734. "machine": "1",
  735. "life": "dying",
  736. "agent-state": "down",
  737. "agent-state-info": "(started)",
  738. "public-address": "dummyenv-1.dns",
  739. },
  740. },
  741. },
  742. "exposed-service": M{
  743. "charm": "cs:quantal/dummy-1",
  744. "exposed": true,
  745. "units": M{
  746. "exposed-service/0": M{
  747. "machine": "2",
  748. "agent-state": "error",
  749. "agent-state-info": "You Require More Vespene Gas",
  750. "open-ports": L{
  751. "2/tcp", "3/tcp", "2/udp", "10/udp",
  752. },
  753. "public-address": "dummyenv-2.dns",
  754. },
  755. },
  756. },
  757. },
  758. },
  759. },
  760. ), test(
  761. "a unit with a hook relation error",
  762. addMachine{machineId: "0", job: state.JobManageEnviron},
  763. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  764. startAliveMachine{"0"},
  765. setMachineStatus{"0", state.StatusStarted, ""},
  766. addMachine{machineId: "1", job: state.JobHostUnits},
  767. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  768. startAliveMachine{"1"},
  769. setMachineStatus{"1", state.StatusStarted, ""},
  770. addCharm{"wordpress"},
  771. addService{name: "wordpress", charm: "wordpress"},
  772. addAliveUnit{"wordpress", "1"},
  773. addCharm{"mysql"},
  774. addService{name: "mysql", charm: "mysql"},
  775. addAliveUnit{"mysql", "1"},
  776. relateServices{"wordpress", "mysql"},
  777. setUnitStatus{"wordpress/0", state.StatusError,
  778. "hook failed: some-relation-changed",
  779. map[string]interface{}{"relation-id": 0}},
  780. expect{
  781. "a unit with a hook relation error",
  782. M{
  783. "environment": "dummyenv",
  784. "machines": M{
  785. "0": machine0,
  786. "1": machine1,
  787. },
  788. "services": M{
  789. "wordpress": M{
  790. "charm": "cs:quantal/wordpress-3",
  791. "exposed": false,
  792. "relations": M{
  793. "db": L{"mysql"},
  794. },
  795. "units": M{
  796. "wordpress/0": M{
  797. "machine": "1",
  798. "agent-state": "error",
  799. "agent-state-info": "hook failed: some-relation-changed for mysql:server",
  800. "public-address": "dummyenv-1.dns",
  801. },
  802. },
  803. },
  804. "mysql": M{
  805. "charm": "cs:quantal/mysql-1",
  806. "exposed": false,
  807. "relations": M{
  808. "server": L{"wordpress"},
  809. },
  810. "units": M{
  811. "mysql/0": M{
  812. "machine": "1",
  813. "agent-state": "allocating",
  814. "public-address": "dummyenv-1.dns",
  815. },
  816. },
  817. },
  818. },
  819. },
  820. },
  821. ), test(
  822. "a unit with a hook relation error when the agent is down",
  823. addMachine{machineId: "0", job: state.JobManageEnviron},
  824. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  825. startAliveMachine{"0"},
  826. setMachineStatus{"0", state.StatusStarted, ""},
  827. addMachine{machineId: "1", job: state.JobHostUnits},
  828. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  829. startAliveMachine{"1"},
  830. setMachineStatus{"1", state.StatusStarted, ""},
  831. addCharm{"wordpress"},
  832. addService{name: "wordpress", charm: "wordpress"},
  833. addUnit{"wordpress", "1"},
  834. addCharm{"mysql"},
  835. addService{name: "mysql", charm: "mysql"},
  836. addAliveUnit{"mysql", "1"},
  837. relateServices{"wordpress", "mysql"},
  838. setUnitStatus{"wordpress/0", state.StatusError,
  839. "hook failed: some-relation-changed",
  840. map[string]interface{}{"relation-id": 0}},
  841. expect{
  842. "a unit with a hook relation error when the agent is down",
  843. M{
  844. "environment": "dummyenv",
  845. "machines": M{
  846. "0": machine0,
  847. "1": machine1,
  848. },
  849. "services": M{
  850. "wordpress": M{
  851. "charm": "cs:quantal/wordpress-3",
  852. "exposed": false,
  853. "relations": M{
  854. "db": L{"mysql"},
  855. },
  856. "units": M{
  857. "wordpress/0": M{
  858. "machine": "1",
  859. "agent-state": "down",
  860. "agent-state-info": "(error: hook failed: some-relation-changed for mysql:server)",
  861. "public-address": "dummyenv-1.dns",
  862. },
  863. },
  864. },
  865. "mysql": M{
  866. "charm": "cs:quantal/mysql-1",
  867. "exposed": false,
  868. "relations": M{
  869. "server": L{"wordpress"},
  870. },
  871. "units": M{
  872. "mysql/0": M{
  873. "machine": "1",
  874. "agent-state": "allocating",
  875. "public-address": "dummyenv-1.dns",
  876. },
  877. },
  878. },
  879. },
  880. },
  881. },
  882. ), test(
  883. "add a dying service",
  884. addCharm{"dummy"},
  885. addService{name: "dummy-service", charm: "dummy"},
  886. addMachine{machineId: "0", job: state.JobHostUnits},
  887. addUnit{"dummy-service", "0"},
  888. ensureDyingService{"dummy-service"},
  889. expect{
  890. "service shows life==dying",
  891. M{
  892. "environment": "dummyenv",
  893. "machines": M{
  894. "0": M{
  895. "instance-id": "pending",
  896. "series": "quantal",
  897. },
  898. },
  899. "services": M{
  900. "dummy-service": M{
  901. "charm": "cs:quantal/dummy-1",
  902. "exposed": false,
  903. "life": "dying",
  904. "units": M{
  905. "dummy-service/0": M{
  906. "machine": "0",
  907. "agent-state": "allocating",
  908. },
  909. },
  910. },
  911. },
  912. },
  913. },
  914. ),
  915. // Relation tests
  916. test(
  917. "complex scenario with multiple related services",
  918. addMachine{machineId: "0", job: state.JobManageEnviron},
  919. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  920. startAliveMachine{"0"},
  921. setMachineStatus{"0", state.StatusStarted, ""},
  922. addCharm{"wordpress"},
  923. addCharm{"mysql"},
  924. addCharm{"varnish"},
  925. addService{name: "project", charm: "wordpress"},
  926. setServiceExposed{"project", true},
  927. addMachine{machineId: "1", job: state.JobHostUnits},
  928. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  929. startAliveMachine{"1"},
  930. setMachineStatus{"1", state.StatusStarted, ""},
  931. addAliveUnit{"project", "1"},
  932. setUnitStatus{"project/0", state.StatusActive, "", nil},
  933. addService{name: "mysql", charm: "mysql"},
  934. setServiceExposed{"mysql", true},
  935. addMachine{machineId: "2", job: state.JobHostUnits},
  936. setAddresses{"2", []network.Address{network.NewAddress("dummyenv-2.dns", network.ScopeUnknown)}},
  937. startAliveMachine{"2"},
  938. setMachineStatus{"2", state.StatusStarted, ""},
  939. addAliveUnit{"mysql", "2"},
  940. setUnitStatus{"mysql/0", state.StatusActive, "", nil},
  941. addService{name: "varnish", charm: "varnish"},
  942. setServiceExposed{"varnish", true},
  943. addMachine{machineId: "3", job: state.JobHostUnits},
  944. setAddresses{"3", []network.Address{network.NewAddress("dummyenv-3.dns", network.ScopeUnknown)}},
  945. startAliveMachine{"3"},
  946. setMachineStatus{"3", state.StatusStarted, ""},
  947. addUnit{"varnish", "3"},
  948. addService{name: "private", charm: "wordpress"},
  949. setServiceExposed{"private", true},
  950. addMachine{machineId: "4", job: state.JobHostUnits},
  951. setAddresses{"4", []network.Address{network.NewAddress("dummyenv-4.dns", network.ScopeUnknown)}},
  952. startAliveMachine{"4"},
  953. setMachineStatus{"4", state.StatusStarted, ""},
  954. addUnit{"private", "4"},
  955. relateServices{"project", "mysql"},
  956. relateServices{"project", "varnish"},
  957. relateServices{"private", "mysql"},
  958. expect{
  959. "multiples services with relations between some of them",
  960. M{
  961. "environment": "dummyenv",
  962. "machines": M{
  963. "0": machine0,
  964. "1": machine1,
  965. "2": machine2,
  966. "3": machine3,
  967. "4": machine4,
  968. },
  969. "services": M{
  970. "project": M{
  971. "charm": "cs:quantal/wordpress-3",
  972. "exposed": true,
  973. "units": M{
  974. "project/0": M{
  975. "machine": "1",
  976. "agent-state": "started",
  977. "public-address": "dummyenv-1.dns",
  978. },
  979. },
  980. "relations": M{
  981. "db": L{"mysql"},
  982. "cache": L{"varnish"},
  983. },
  984. },
  985. "mysql": M{
  986. "charm": "cs:quantal/mysql-1",
  987. "exposed": true,
  988. "units": M{
  989. "mysql/0": M{
  990. "machine": "2",
  991. "agent-state": "started",
  992. "public-address": "dummyenv-2.dns",
  993. },
  994. },
  995. "relations": M{
  996. "server": L{"private", "project"},
  997. },
  998. },
  999. "varnish": M{
  1000. "charm": "cs:quantal/varnish-1",
  1001. "exposed": true,
  1002. "units": M{
  1003. "varnish/0": M{
  1004. "machine": "3",
  1005. "agent-state": "allocating",
  1006. "public-address": "dummyenv-3.dns",
  1007. },
  1008. },
  1009. "relations": M{
  1010. "webcache": L{"project"},
  1011. },
  1012. },
  1013. "private": M{
  1014. "charm": "cs:quantal/wordpress-3",
  1015. "exposed": true,
  1016. "units": M{
  1017. "private/0": M{
  1018. "machine": "4",
  1019. "agent-state": "allocating",
  1020. "public-address": "dummyenv-4.dns",
  1021. },
  1022. },
  1023. "relations": M{
  1024. "db": L{"mysql"},
  1025. },
  1026. },
  1027. },
  1028. },
  1029. },
  1030. ), test(
  1031. "simple peer scenario",
  1032. addMachine{machineId: "0", job: state.JobManageEnviron},
  1033. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1034. startAliveMachine{"0"},
  1035. setMachineStatus{"0", state.StatusStarted, ""},
  1036. addCharm{"riak"},
  1037. addCharm{"wordpress"},
  1038. addService{name: "riak", charm: "riak"},
  1039. setServiceExposed{"riak", true},
  1040. addMachine{machineId: "1", job: state.JobHostUnits},
  1041. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1042. startAliveMachine{"1"},
  1043. setMachineStatus{"1", state.StatusStarted, ""},
  1044. addAliveUnit{"riak", "1"},
  1045. setUnitStatus{"riak/0", state.StatusActive, "", nil},
  1046. addMachine{machineId: "2", job: state.JobHostUnits},
  1047. setAddresses{"2", []network.Address{network.NewAddress("dummyenv-2.dns", network.ScopeUnknown)}},
  1048. startAliveMachine{"2"},
  1049. setMachineStatus{"2", state.StatusStarted, ""},
  1050. addAliveUnit{"riak", "2"},
  1051. setUnitStatus{"riak/1", state.StatusActive, "", nil},
  1052. addMachine{machineId: "3", job: state.JobHostUnits},
  1053. setAddresses{"3", []network.Address{network.NewAddress("dummyenv-3.dns", network.ScopeUnknown)}},
  1054. startAliveMachine{"3"},
  1055. setMachineStatus{"3", state.StatusStarted, ""},
  1056. addAliveUnit{"riak", "3"},
  1057. setUnitStatus{"riak/2", state.StatusActive, "", nil},
  1058. expect{
  1059. "multiples related peer units",
  1060. M{
  1061. "environment": "dummyenv",
  1062. "machines": M{
  1063. "0": machine0,
  1064. "1": machine1,
  1065. "2": machine2,
  1066. "3": machine3,
  1067. },
  1068. "services": M{
  1069. "riak": M{
  1070. "charm": "cs:quantal/riak-7",
  1071. "exposed": true,
  1072. "units": M{
  1073. "riak/0": M{
  1074. "machine": "1",
  1075. "agent-state": "started",
  1076. "public-address": "dummyenv-1.dns",
  1077. },
  1078. "riak/1": M{
  1079. "machine": "2",
  1080. "agent-state": "started",
  1081. "public-address": "dummyenv-2.dns",
  1082. },
  1083. "riak/2": M{
  1084. "machine": "3",
  1085. "agent-state": "started",
  1086. "public-address": "dummyenv-3.dns",
  1087. },
  1088. },
  1089. "relations": M{
  1090. "ring": L{"riak"},
  1091. },
  1092. },
  1093. },
  1094. },
  1095. },
  1096. ),
  1097. // Subordinate tests
  1098. test(
  1099. "one service with one subordinate service",
  1100. addMachine{machineId: "0", job: state.JobManageEnviron},
  1101. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1102. startAliveMachine{"0"},
  1103. setMachineStatus{"0", state.StatusStarted, ""},
  1104. addCharm{"wordpress"},
  1105. addCharm{"mysql"},
  1106. addCharm{"logging"},
  1107. addService{name: "wordpress", charm: "wordpress"},
  1108. setServiceExposed{"wordpress", true},
  1109. addMachine{machineId: "1", job: state.JobHostUnits},
  1110. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1111. startAliveMachine{"1"},
  1112. setMachineStatus{"1", state.StatusStarted, ""},
  1113. addAliveUnit{"wordpress", "1"},
  1114. setUnitStatus{"wordpress/0", state.StatusActive, "", nil},
  1115. addService{name: "mysql", charm: "mysql"},
  1116. setServiceExposed{"mysql", true},
  1117. addMachine{machineId: "2", job: state.JobHostUnits},
  1118. setAddresses{"2", []network.Address{network.NewAddress("dummyenv-2.dns", network.ScopeUnknown)}},
  1119. startAliveMachine{"2"},
  1120. setMachineStatus{"2", state.StatusStarted, ""},
  1121. addAliveUnit{"mysql", "2"},
  1122. setUnitStatus{"mysql/0", state.StatusActive, "", nil},
  1123. addService{name: "logging", charm: "logging"},
  1124. setServiceExposed{"logging", true},
  1125. relateServices{"wordpress", "mysql"},
  1126. relateServices{"wordpress", "logging"},
  1127. relateServices{"mysql", "logging"},
  1128. addSubordinate{"wordpress/0", "logging"},
  1129. addSubordinate{"mysql/0", "logging"},
  1130. setUnitsAlive{"logging"},
  1131. setUnitStatus{"logging/0", state.StatusActive, "", nil},
  1132. setUnitStatus{"logging/1", state.StatusError, "somehow lost in all those logs", nil},
  1133. expect{
  1134. "multiples related peer units",
  1135. M{
  1136. "environment": "dummyenv",
  1137. "machines": M{
  1138. "0": machine0,
  1139. "1": machine1,
  1140. "2": machine2,
  1141. },
  1142. "services": M{
  1143. "wordpress": M{
  1144. "charm": "cs:quantal/wordpress-3",
  1145. "exposed": true,
  1146. "units": M{
  1147. "wordpress/0": M{
  1148. "machine": "1",
  1149. "agent-state": "started",
  1150. "subordinates": M{
  1151. "logging/0": M{
  1152. "agent-state": "started",
  1153. "public-address": "dummyenv-1.dns",
  1154. },
  1155. },
  1156. "public-address": "dummyenv-1.dns",
  1157. },
  1158. },
  1159. "relations": M{
  1160. "db": L{"mysql"},
  1161. "logging-dir": L{"logging"},
  1162. },
  1163. },
  1164. "mysql": M{
  1165. "charm": "cs:quantal/mysql-1",
  1166. "exposed": true,
  1167. "units": M{
  1168. "mysql/0": M{
  1169. "machine": "2",
  1170. "agent-state": "started",
  1171. "subordinates": M{
  1172. "logging/1": M{
  1173. "agent-state": "error",
  1174. "agent-state-info": "somehow lost in all those logs",
  1175. "public-address": "dummyenv-2.dns",
  1176. },
  1177. },
  1178. "public-address": "dummyenv-2.dns",
  1179. },
  1180. },
  1181. "relations": M{
  1182. "server": L{"wordpress"},
  1183. "juju-info": L{"logging"},
  1184. },
  1185. },
  1186. "logging": M{
  1187. "charm": "cs:quantal/logging-1",
  1188. "exposed": true,
  1189. "relations": M{
  1190. "logging-directory": L{"wordpress"},
  1191. "info": L{"mysql"},
  1192. },
  1193. "subordinate-to": L{"mysql", "wordpress"},
  1194. },
  1195. },
  1196. },
  1197. },
  1198. // scoped on 'logging'
  1199. scopedExpect{
  1200. "subordinates scoped on logging",
  1201. []string{"logging"},
  1202. M{
  1203. "environment": "dummyenv",
  1204. "machines": M{
  1205. "1": machine1,
  1206. "2": machine2,
  1207. },
  1208. "services": M{
  1209. "wordpress": M{
  1210. "charm": "cs:quantal/wordpress-3",
  1211. "exposed": true,
  1212. "units": M{
  1213. "wordpress/0": M{
  1214. "machine": "1",
  1215. "agent-state": "started",
  1216. "subordinates": M{
  1217. "logging/0": M{
  1218. "agent-state": "started",
  1219. "public-address": "dummyenv-1.dns",
  1220. },
  1221. },
  1222. "public-address": "dummyenv-1.dns",
  1223. },
  1224. },
  1225. "relations": M{
  1226. "db": L{"mysql"},
  1227. "logging-dir": L{"logging"},
  1228. },
  1229. },
  1230. "mysql": M{
  1231. "charm": "cs:quantal/mysql-1",
  1232. "exposed": true,
  1233. "units": M{
  1234. "mysql/0": M{
  1235. "machine": "2",
  1236. "agent-state": "started",
  1237. "subordinates": M{
  1238. "logging/1": M{
  1239. "agent-state": "error",
  1240. "agent-state-info": "somehow lost in all those logs",
  1241. "public-address": "dummyenv-2.dns",
  1242. },
  1243. },
  1244. "public-address": "dummyenv-2.dns",
  1245. },
  1246. },
  1247. "relations": M{
  1248. "server": L{"wordpress"},
  1249. "juju-info": L{"logging"},
  1250. },
  1251. },
  1252. "logging": M{
  1253. "charm": "cs:quantal/logging-1",
  1254. "exposed": true,
  1255. "relations": M{
  1256. "logging-directory": L{"wordpress"},
  1257. "info": L{"mysql"},
  1258. },
  1259. "subordinate-to": L{"mysql", "wordpress"},
  1260. },
  1261. },
  1262. },
  1263. },
  1264. // scoped on wordpress/0
  1265. scopedExpect{
  1266. "subordinates scoped on logging",
  1267. []string{"wordpress/0"},
  1268. M{
  1269. "environment": "dummyenv",
  1270. "machines": M{
  1271. "1": machine1,
  1272. },
  1273. "services": M{
  1274. "wordpress": M{
  1275. "charm": "cs:quantal/wordpress-3",
  1276. "exposed": true,
  1277. "units": M{
  1278. "wordpress/0": M{
  1279. "machine": "1",
  1280. "agent-state": "started",
  1281. "subordinates": M{
  1282. "logging/0": M{
  1283. "agent-state": "started",
  1284. "public-address": "dummyenv-1.dns",
  1285. },
  1286. },
  1287. "public-address": "dummyenv-1.dns",
  1288. },
  1289. },
  1290. "relations": M{
  1291. "db": L{"mysql"},
  1292. "logging-dir": L{"logging"},
  1293. },
  1294. },
  1295. "logging": M{
  1296. "charm": "cs:quantal/logging-1",
  1297. "exposed": true,
  1298. "relations": M{
  1299. "logging-directory": L{"wordpress"},
  1300. "info": L{"mysql"},
  1301. },
  1302. "subordinate-to": L{"mysql", "wordpress"},
  1303. },
  1304. },
  1305. },
  1306. },
  1307. ),
  1308. test(
  1309. "machines with containers",
  1310. addMachine{machineId: "0", job: state.JobManageEnviron},
  1311. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1312. startAliveMachine{"0"},
  1313. setMachineStatus{"0", state.StatusStarted, ""},
  1314. addCharm{"mysql"},
  1315. addService{name: "mysql", charm: "mysql"},
  1316. setServiceExposed{"mysql", true},
  1317. addMachine{machineId: "1", job: state.JobHostUnits},
  1318. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1319. startAliveMachine{"1"},
  1320. setMachineStatus{"1", state.StatusStarted, ""},
  1321. addAliveUnit{"mysql", "1"},
  1322. setUnitStatus{"mysql/0", state.StatusActive, "", nil},
  1323. // A container on machine 1.
  1324. addContainer{"1", "1/lxc/0", state.JobHostUnits},
  1325. setAddresses{"1/lxc/0", []network.Address{network.NewAddress("dummyenv-2.dns", network.ScopeUnknown)}},
  1326. startAliveMachine{"1/lxc/0"},
  1327. setMachineStatus{"1/lxc/0", state.StatusStarted, ""},
  1328. addAliveUnit{"mysql", "1/lxc/0"},
  1329. setUnitStatus{"mysql/1", state.StatusActive, "", nil},
  1330. addContainer{"1", "1/lxc/1", state.JobHostUnits},
  1331. // A nested container.
  1332. addContainer{"1/lxc/0", "1/lxc/0/lxc/0", state.JobHostUnits},
  1333. setAddresses{"1/lxc/0/lxc/0", []network.Address{network.NewAddress("dummyenv-3.dns", network.ScopeUnknown)}},
  1334. startAliveMachine{"1/lxc/0/lxc/0"},
  1335. setMachineStatus{"1/lxc/0/lxc/0", state.StatusStarted, ""},
  1336. expect{
  1337. "machines with nested containers",
  1338. M{
  1339. "environment": "dummyenv",
  1340. "machines": M{
  1341. "0": machine0,
  1342. "1": machine1WithContainers,
  1343. },
  1344. "services": M{
  1345. "mysql": M{
  1346. "charm": "cs:quantal/mysql-1",
  1347. "exposed": true,
  1348. "units": M{
  1349. "mysql/0": M{
  1350. "machine": "1",
  1351. "agent-state": "started",
  1352. "public-address": "dummyenv-1.dns",
  1353. },
  1354. "mysql/1": M{
  1355. "machine": "1/lxc/0",
  1356. "agent-state": "started",
  1357. "public-address": "dummyenv-2.dns",
  1358. },
  1359. },
  1360. },
  1361. },
  1362. },
  1363. },
  1364. // once again, with a scope on mysql/1
  1365. scopedExpect{
  1366. "machines with nested containers",
  1367. []string{"mysql/1"},
  1368. M{
  1369. "environment": "dummyenv",
  1370. "machines": M{
  1371. "1": M{
  1372. "agent-state": "started",
  1373. "containers": M{
  1374. "1/lxc/0": M{
  1375. "agent-state": "started",
  1376. "dns-name": "dummyenv-2.dns",
  1377. "instance-id": "dummyenv-2",
  1378. "series": "quantal",
  1379. },
  1380. },
  1381. "dns-name": "dummyenv-1.dns",
  1382. "instance-id": "dummyenv-1",
  1383. "series": "quantal",
  1384. "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
  1385. },
  1386. },
  1387. "services": M{
  1388. "mysql": M{
  1389. "charm": "cs:quantal/mysql-1",
  1390. "exposed": true,
  1391. "units": M{
  1392. "mysql/1": M{
  1393. "machine": "1/lxc/0",
  1394. "agent-state": "started",
  1395. "public-address": "dummyenv-2.dns",
  1396. },
  1397. },
  1398. },
  1399. },
  1400. },
  1401. },
  1402. ), test(
  1403. "service with out of date charm",
  1404. addMachine{machineId: "0", job: state.JobManageEnviron},
  1405. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1406. startAliveMachine{"0"},
  1407. setMachineStatus{"0", state.StatusStarted, ""},
  1408. addMachine{machineId: "1", job: state.JobHostUnits},
  1409. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1410. startAliveMachine{"1"},
  1411. setMachineStatus{"1", state.StatusStarted, ""},
  1412. addCharm{"mysql"},
  1413. addService{name: "mysql", charm: "mysql"},
  1414. setServiceExposed{"mysql", true},
  1415. addCharmPlaceholder{"mysql", 23},
  1416. addAliveUnit{"mysql", "1"},
  1417. expect{
  1418. "services and units with correct charm status",
  1419. M{
  1420. "environment": "dummyenv",
  1421. "machines": M{
  1422. "0": machine0,
  1423. "1": machine1,
  1424. },
  1425. "services": M{
  1426. "mysql": M{
  1427. "charm": "cs:quantal/mysql-1",
  1428. "can-upgrade-to": "cs:quantal/mysql-23",
  1429. "exposed": true,
  1430. "units": M{
  1431. "mysql/0": M{
  1432. "machine": "1",
  1433. "agent-state": "allocating",
  1434. "public-address": "dummyenv-1.dns",
  1435. },
  1436. },
  1437. },
  1438. },
  1439. },
  1440. },
  1441. ), test(
  1442. "unit with out of date charm",
  1443. addMachine{machineId: "0", job: state.JobManageEnviron},
  1444. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1445. startAliveMachine{"0"},
  1446. setMachineStatus{"0", state.StatusStarted, ""},
  1447. addMachine{machineId: "1", job: state.JobHostUnits},
  1448. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1449. startAliveMachine{"1"},
  1450. setMachineStatus{"1", state.StatusStarted, ""},
  1451. addCharm{"mysql"},
  1452. addService{name: "mysql", charm: "mysql"},
  1453. setServiceExposed{"mysql", true},
  1454. addAliveUnit{"mysql", "1"},
  1455. setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
  1456. addCharmWithRevision{addCharm{"mysql"}, "local", 1},
  1457. setServiceCharm{"mysql", "local:quantal/mysql-1"},
  1458. expect{
  1459. "services and units with correct charm status",
  1460. M{
  1461. "environment": "dummyenv",
  1462. "machines": M{
  1463. "0": machine0,
  1464. "1": machine1,
  1465. },
  1466. "services": M{
  1467. "mysql": M{
  1468. "charm": "local:quantal/mysql-1",
  1469. "exposed": true,
  1470. "units": M{
  1471. "mysql/0": M{
  1472. "machine": "1",
  1473. "agent-state": "started",
  1474. "upgrading-from": "cs:quantal/mysql-1",
  1475. "public-address": "dummyenv-1.dns",
  1476. },
  1477. },
  1478. },
  1479. },
  1480. },
  1481. },
  1482. ), test(
  1483. "service and unit with out of date charms",
  1484. addMachine{machineId: "0", job: state.JobManageEnviron},
  1485. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1486. startAliveMachine{"0"},
  1487. setMachineStatus{"0", state.StatusStarted, ""},
  1488. addMachine{machineId: "1", job: state.JobHostUnits},
  1489. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1490. startAliveMachine{"1"},
  1491. setMachineStatus{"1", state.StatusStarted, ""},
  1492. addCharm{"mysql"},
  1493. addService{name: "mysql", charm: "mysql"},
  1494. setServiceExposed{"mysql", true},
  1495. addAliveUnit{"mysql", "1"},
  1496. setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
  1497. addCharmWithRevision{addCharm{"mysql"}, "cs", 2},
  1498. setServiceCharm{"mysql", "cs:quantal/mysql-2"},
  1499. addCharmPlaceholder{"mysql", 23},
  1500. expect{
  1501. "services and units with correct charm status",
  1502. M{
  1503. "environment": "dummyenv",
  1504. "machines": M{
  1505. "0": machine0,
  1506. "1": machine1,
  1507. },
  1508. "services": M{
  1509. "mysql": M{
  1510. "charm": "cs:quantal/mysql-2",
  1511. "can-upgrade-to": "cs:quantal/mysql-23",
  1512. "exposed": true,
  1513. "units": M{
  1514. "mysql/0": M{
  1515. "machine": "1",
  1516. "agent-state": "started",
  1517. "upgrading-from": "cs:quantal/mysql-1",
  1518. "public-address": "dummyenv-1.dns",
  1519. },
  1520. },
  1521. },
  1522. },
  1523. },
  1524. },
  1525. ), test(
  1526. "service with local charm not shown as out of date",
  1527. addMachine{machineId: "0", job: state.JobManageEnviron},
  1528. setAddresses{"0", []network.Address{network.NewAddress("dummyenv-0.dns", network.ScopeUnknown)}},
  1529. startAliveMachine{"0"},
  1530. setMachineStatus{"0", state.StatusStarted, ""},
  1531. addMachine{machineId: "1", job: state.JobHostUnits},
  1532. setAddresses{"1", []network.Address{network.NewAddress("dummyenv-1.dns", network.ScopeUnknown)}},
  1533. startAliveMachine{"1"},
  1534. setMachineStatus{"1", state.StatusStarted, ""},
  1535. addCharm{"mysql"},
  1536. addService{name: "mysql", charm: "mysql"},
  1537. setServiceExposed{"mysql", true},
  1538. addAliveUnit{"mysql", "1"},
  1539. setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
  1540. addCharmWithRevision{addCharm{"mysql"}, "local", 1},
  1541. setServiceCharm{"mysql", "local:quantal/mysql-1"},
  1542. addCharmPlaceholder{"mysql", 23},
  1543. expect{
  1544. "services and units with correct charm status",
  1545. M{
  1546. "environment": "dummyenv",
  1547. "machines": M{
  1548. "0": machine0,
  1549. "1": machine1,
  1550. },
  1551. "services": M{
  1552. "mysql": M{
  1553. "charm": "local:quantal/mysql-1",
  1554. "exposed": true,
  1555. "units": M{
  1556. "mysql/0": M{
  1557. "machine": "1",
  1558. "agent-state": "started",
  1559. "upgrading-from": "cs:quantal/mysql-1",
  1560. "public-address": "dummyenv-1.dns",
  1561. },
  1562. },
  1563. },
  1564. },
  1565. },
  1566. },
  1567. ),
  1568. }
  1569. // TODO(dfc) test failing components by destructively mutating the state under the hood
  1570. type addMachine struct {
  1571. machineId string
  1572. cons constraints.Value
  1573. job state.MachineJob
  1574. }
  1575. func (am addMachine) step(c *gc.C, ctx *context) {
  1576. m, err := ctx.st.AddOneMachine(state.MachineTemplate{
  1577. Series: "quantal",
  1578. Constraints: am.cons,
  1579. Jobs: []state.MachineJob{am.job},
  1580. })
  1581. c.Assert(err, jc.ErrorIsNil)
  1582. c.Assert(m.Id(), gc.Equals, am.machineId)
  1583. }
  1584. type addNetwork struct {
  1585. name string
  1586. providerId network.Id
  1587. cidr string
  1588. vlanTag int
  1589. }
  1590. func (an addNetwork) step(c *gc.C, ctx *context) {
  1591. n, err := ctx.st.AddNetwork(state.NetworkInfo{
  1592. Name: an.name,
  1593. ProviderId: an.providerId,
  1594. CIDR: an.cidr,
  1595. VLANTag: an.vlanTag,
  1596. })
  1597. c.Assert(err, jc.ErrorIsNil)
  1598. c.Assert(n.Name(), gc.Equals, an.name)
  1599. }
  1600. type addContainer struct {
  1601. parentId string
  1602. machineId string
  1603. job state.MachineJob
  1604. }
  1605. func (ac addContainer) step(c *gc.C, ctx *context) {
  1606. template := state.MachineTemplate{
  1607. Series: "quantal",
  1608. Jobs: []state.MachineJob{ac.job},
  1609. }
  1610. m, err := ctx.st.AddMachineInsideMachine(template, ac.parentId, instance.LXC)
  1611. c.Assert(err, jc.ErrorIsNil)
  1612. c.Assert(m.Id(), gc.Equals, ac.machineId)
  1613. }
  1614. type startMachine struct {
  1615. machineId string
  1616. }
  1617. func (sm startMachine) step(c *gc.C, ctx *context) {
  1618. m, err := ctx.st.Machine(sm.machineId)
  1619. c.Assert(err, jc.ErrorIsNil)
  1620. cons, err := m.Constraints()
  1621. c.Assert(err, jc.ErrorIsNil)
  1622. inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, m.Id(), cons)
  1623. err = m.SetProvisioned(inst.Id(), "fake_nonce", hc)
  1624. c.Assert(err, jc.ErrorIsNil)
  1625. }
  1626. type startMissingMachine struct {
  1627. machineId string
  1628. }
  1629. func (sm startMissingMachine) step(c *gc.C, ctx *context) {
  1630. m, err := ctx.st.Machine(sm.machineId)
  1631. c.Assert(err, jc.ErrorIsNil)
  1632. cons, err := m.Constraints()
  1633. c.Assert(err, jc.ErrorIsNil)
  1634. _, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, m.Id(), cons)
  1635. err = m.SetProvisioned("i-missing", "fake_nonce", hc)
  1636. c.Assert(err, jc.ErrorIsNil)
  1637. err = m.SetInstanceStatus("missing")
  1638. c.Assert(err, jc.ErrorIsNil)
  1639. }
  1640. type startAliveMachine struct {
  1641. machineId string
  1642. }
  1643. func (sam startAliveMachine) step(c *gc.C, ctx *context) {
  1644. m, err := ctx.st.Machine(sam.machineId)
  1645. c.Assert(err, jc.ErrorIsNil)
  1646. pinger := ctx.setAgentPresence(c, m)
  1647. cons, err := m.Constraints()
  1648. c.Assert(err, jc.ErrorIsNil)
  1649. inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, m.Id(), cons)
  1650. err = m.SetProvisioned(inst.Id(), "fake_nonce", hc)
  1651. c.Assert(err, jc.ErrorIsNil)
  1652. ctx.pingers[m.Id()] = pinger
  1653. }
  1654. type setAddresses struct {
  1655. machineId string
  1656. addresses []network.Address
  1657. }
  1658. func (sa setAddresses) step(c *gc.C, ctx *context) {
  1659. m, err := ctx.st.Machine(sa.machineId)
  1660. c.Assert(err, jc.ErrorIsNil)
  1661. err = m.SetAddresses(sa.addresses...)
  1662. c.Assert(err, jc.ErrorIsNil)
  1663. }
  1664. type setTools struct {
  1665. machineId string
  1666. version version.Binary
  1667. }
  1668. func (st setTools) step(c *gc.C, ctx *context) {
  1669. m, err := ctx.st.Machine(st.machineId)
  1670. c.Assert(err, jc.ErrorIsNil)
  1671. err = m.SetAgentVersion(st.version)
  1672. c.Assert(err, jc.ErrorIsNil)
  1673. }
  1674. type addCharm struct {
  1675. name string
  1676. }
  1677. func (ac addCharm) addCharmStep(c *gc.C, ctx *context, scheme string, rev int) {
  1678. ch := testcharms.Repo.CharmDir(ac.name)
  1679. name := ch.Meta().Name
  1680. curl := charm.MustParseURL(fmt.Sprintf("%s:quantal/%s-%d", scheme, name, rev))
  1681. dummy, err := ctx.st.AddCharm(ch, curl, "dummy-path", fmt.Sprintf("%s-%d-sha256", name, rev))
  1682. c.Assert(err, jc.ErrorIsNil)
  1683. ctx.charms[ac.name] = dummy
  1684. }
  1685. func (ac addCharm) step(c *gc.C, ctx *context) {
  1686. ch := testcharms.Repo.CharmDir(ac.name)
  1687. ac.addCharmStep(c, ctx, "cs", ch.Revision())
  1688. }
  1689. type addCharmWithRevision struct {
  1690. addCharm
  1691. scheme string
  1692. rev int
  1693. }
  1694. func (ac addCharmWithRevision) step(c *gc.C, ctx *context) {
  1695. ac.addCharmStep(c, ctx, ac.scheme, ac.rev)
  1696. }
  1697. type addService struct {
  1698. name string
  1699. charm string
  1700. networks []string
  1701. cons constraints.Value
  1702. }
  1703. func (as addService) step(c *gc.C, ctx *context) {
  1704. ch, ok := ctx.charms[as.charm]
  1705. c.Assert(ok, jc.IsTrue)
  1706. svc, err := ctx.st.AddService(as.name, ctx.adminUserTag, ch, as.networks, nil)
  1707. c.Assert(err, jc.ErrorIsNil)
  1708. if svc.IsPrincipal() {

Large files files are truncated, but you can click here to view the full file