Merge lp:~ericsnowcurrently/fake-juju/juju-2.0-support into lp:~landscape/fake-juju/trunk-old

Proposed by Eric Snow
Status: Merged
Approved by: Eric Snow
Approved revision: 86
Merged at revision: 47
Proposed branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Merge into: lp:~landscape/fake-juju/trunk-old
Prerequisite: lp:~ericsnowcurrently/fake-juju/testing-fixes
Diff against target: 1595 lines (+345/-735)
8 files modified
1.24.7/fake-juju.go (+0/-514)
1.25.6/fake-juju.go (+93/-57)
2.0.0/fake-juju.go (+239/-104)
Makefile (+2/-2)
patches/juju-core_1.24.7.patch (+0/-47)
patches/juju-core_2.0.0.patch (+6/-6)
python/fakejuju/testing.py (+2/-2)
tests/test_fake.py (+3/-3)
To merge this branch: bzr merge lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Reviewer Review Type Date Requested Status
🤖 Landscape Builder test results Approve
Simon Poirier (community) Approve
Review via email: mp+309285@code.launchpad.net

This proposal supersedes a proposal from 2016-10-25.

Commit message

Update to juju 2.0.0.

Also drop support for 1.24.x.

Description of the change

Update to juju 2.0.0.

Also drop support for 1.24.x.

Testing instructions:

Run make test

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) : Posted in a previous version of this proposal
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote : Posted in a previous version of this proposal

Command: make ci-test
Result: Success
Revno: 63
Branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Jenkins: https://ci.lscape.net/job/latch-test-xenial/336/

review: Approve (test results)
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 63
Branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Jenkins: https://ci.lscape.net/job/latch-test-xenial/337/

review: Approve (test results)
Revision history for this message
Simon Poirier (simpoir) wrote :

See inline question

review: Needs Information
Revision history for this message
Simon Poirier (simpoir) wrote :

+1 Never mind. I just misread. Looks all good.

review: Approve
64. By Eric Snow

Add some comments about bootstrap.

65. By Eric Snow

Factor out waitForBootstrapCompletion().

66. By Eric Snow

Rename the command handler functions.

67. By Eric Snow

Factor out destroyController().

68. By Eric Snow

Destroy the controller if bootstrap fails.

69. By Eric Snow

Factor out updateBootstrapResult().

70. By Eric Snow

Call copyConfig() before updating the bootstrap result.

71. By Eric Snow

Handle bootstrap error cleanup centrally.

72. By Eric Snow

Capture error output from the daemon.

73. By Eric Snow

Minor touch-ups to SetUpTest().

74. By Eric Snow

Use constants for the environment variable names.

75. By Eric Snow

Add a log file for jujud logs.

76. By Eric Snow

Factor out reportInfo().

77. By Eric Snow

Ensure that the daemon runs with FAKE_JUJU_DATA_DIR set.

78. By Eric Snow

Parse all the supported "juju bootstrap" args.

79. By Eric Snow

Always ensure that the Juju cfg dir exists.

80. By Eric Snow

Support a -v bootstrap arg.

81. By Eric Snow

Set up logging right *after* calling JujuConnSuite.SetUpTest().

82. By Eric Snow

Fix bootstrap arg order in a test.

83. By Eric Snow

Merge from trunk.

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 83
Branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Jenkins: https://ci.lscape.net/job/latch-test-xenial/927/

review: Approve (test results)
84. By Eric Snow

Copy some of the 2.0.0 tweaks over to 1.25.6.

85. By Eric Snow

Default to Juju 2.x in tests.

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 85
Branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Jenkins: https://ci.lscape.net/job/latch-test-xenial/930/

review: Approve (test results)
86. By Eric Snow

Pull a little more code from 2.0.0.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed directory '1.24.7'
2=== removed file '1.24.7/fake-juju.go'
3--- 1.24.7/fake-juju.go 2016-06-10 17:08:28 +0000
4+++ 1.24.7/fake-juju.go 1970-01-01 00:00:00 +0000
5@@ -1,514 +0,0 @@
6-package main
7-
8-import (
9- "bufio"
10- "encoding/json"
11- "errors"
12- "fmt"
13- gc "gopkg.in/check.v1"
14- "io"
15- "io/ioutil"
16- "log"
17- "os"
18- "os/exec"
19- "path/filepath"
20- "strings"
21- "syscall"
22- "testing"
23- "time"
24-
25- "github.com/juju/juju/agent"
26- "github.com/juju/juju/api"
27- "github.com/juju/juju/environs"
28- "github.com/juju/juju/environs/configstore"
29- "github.com/juju/juju/instance"
30- "github.com/juju/juju/juju/osenv"
31- jujutesting "github.com/juju/juju/juju/testing"
32- "github.com/juju/juju/network"
33- _ "github.com/juju/juju/provider/maas"
34- "github.com/juju/juju/state"
35- coretesting "github.com/juju/juju/testing"
36- "github.com/juju/juju/testing/factory"
37- "github.com/juju/juju/version"
38- "github.com/juju/names"
39- corecharm "gopkg.in/juju/charm.v5/charmrepo"
40- goyaml "gopkg.in/yaml.v1"
41-)
42-
43-func main() {
44- if len(os.Args) > 1 {
45- code := 0
46- err := handleCommand(os.Args[1])
47- if err != nil {
48- fmt.Println(err.Error())
49- code = 1
50- }
51- os.Exit(code)
52- }
53- t := &testing.T{}
54- coretesting.MgoTestPackage(t)
55-}
56-
57-type processInfo struct {
58- WorkDir string
59- EndpointAddr string
60- Uuid string
61- CACert string
62-}
63-
64-func handleCommand(command string) error {
65- if command == "bootstrap" {
66- return bootstrap()
67- }
68- if command == "api-endpoints" {
69- return apiEndpoints()
70- }
71- if command == "api-info" {
72- return apiInfo()
73- }
74- if command == "destroy-environment" {
75- return destroyEnvironment()
76- }
77- return errors.New("command not found")
78-}
79-
80-func bootstrap() error {
81- envName, password, err := environmentNameAndPassword()
82- if err != nil {
83- return err
84- }
85- command := exec.Command(os.Args[0])
86- command.Env = os.Environ()
87- command.Env = append(command.Env, "ADMIN_PASSWORD="+password)
88- stdout, err := command.StdoutPipe()
89- if err != nil {
90- return err
91- }
92- command.Start()
93- apiInfo, err := parseApiInfo(envName, stdout)
94- if err != nil {
95- return err
96- }
97- dialOpts := api.DialOpts{
98- DialAddressInterval: 50 * time.Millisecond,
99- Timeout: 5 * time.Second,
100- RetryDelay: 2 * time.Second,
101- }
102- state, err := api.Open(apiInfo, dialOpts)
103- if err != nil {
104- return err
105- }
106- client := state.Client()
107- watcher, err := client.WatchAll()
108- if err != nil {
109- return err
110- }
111- deltas, err := watcher.Next()
112- if err != nil {
113- return err
114- }
115- for _, delta := range deltas {
116- entityId := delta.Entity.EntityId()
117- if entityId.Kind == "machine" {
118- machineId, _ := entityId.Id.(string)
119- if machineId == "0" {
120- return nil
121- }
122- }
123- }
124- return errors.New("invalid delta")
125-}
126-
127-func apiEndpoints() error {
128- info, err := readProcessInfo()
129- if err != nil {
130- return err
131- }
132- fmt.Println(info.EndpointAddr)
133- return nil
134-}
135-
136-func apiInfo() error {
137- info, err := readProcessInfo()
138- if err != nil {
139- return err
140- }
141- fmt.Printf("{\"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", info.Uuid, info.EndpointAddr)
142- return nil
143-}
144-
145-func destroyEnvironment() error {
146- info, err := readProcessInfo()
147- if err != nil {
148- return err
149- }
150- fifoPath := filepath.Join(info.WorkDir, "fifo")
151- fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)
152- if err != nil {
153- return err
154- }
155- defer fd.Close()
156- _, err = fd.WriteString("destroy\n")
157- if err != nil {
158- return err
159- }
160- return nil
161-}
162-
163-func environmentNameAndPassword() (string, string, error) {
164- jujuHome := os.Getenv("JUJU_HOME")
165- osenv.SetJujuHome(jujuHome)
166- environs, err := environs.ReadEnvirons(
167- filepath.Join(jujuHome, "environments.yaml"))
168- if err != nil {
169- return "", "", err
170- }
171- envName := environs.Names()[0]
172- config, err := environs.Config(envName)
173- if err != nil {
174- return "", "", err
175- }
176- return envName, config.AdminSecret(), nil
177-}
178-
179-func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {
180- buffer := bufio.NewReader(stdout)
181- line, _, err := buffer.ReadLine()
182- if err != nil {
183- return nil, err
184- }
185- uuid := string(line)
186- environTag := names.NewEnvironTag(uuid)
187- line, _, err = buffer.ReadLine()
188- if err != nil {
189- return nil, err
190- }
191- workDir := string(line)
192- store, err := configstore.NewDisk(workDir)
193- if err != nil {
194- return nil, err
195- }
196- info, err := store.ReadInfo("dummyenv")
197- if err != nil {
198- return nil, err
199- }
200- credentials := info.APICredentials()
201- endpoint := info.APIEndpoint()
202- addresses := endpoint.Addresses
203- apiInfo := &api.Info{
204- Addrs: addresses,
205- Tag: names.NewLocalUserTag(credentials.User),
206- Password: credentials.Password,
207- CACert: endpoint.CACert,
208- EnvironTag: environTag,
209- }
210- err = writeProcessInfo(envName, &processInfo{
211- WorkDir: workDir,
212- EndpointAddr: addresses[0],
213- Uuid: uuid,
214- CACert: endpoint.CACert,
215- })
216- if err != nil {
217- return nil, err
218- }
219- return apiInfo, nil
220-}
221-
222-func readProcessInfo() (*processInfo, error) {
223- infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju")
224- data, err := ioutil.ReadFile(infoPath)
225- if err != nil {
226- return nil, err
227- }
228- info := &processInfo{}
229- err = goyaml.Unmarshal(data, info)
230- if err != nil {
231- return nil, err
232- }
233- return info, nil
234-}
235-
236-func writeProcessInfo(envName string, info *processInfo) error {
237- jujuHome := os.Getenv("JUJU_HOME")
238- infoPath := filepath.Join(jujuHome, "fakejuju")
239- logPath := filepath.Join(jujuHome, "fake-juju.log")
240- caCertPath := filepath.Join(jujuHome, "cert.ca")
241- envPath := filepath.Join(jujuHome, "environments")
242- os.Mkdir(envPath, 0755)
243- jEnvPath := filepath.Join(envPath, envName+".jenv")
244- data, _ := goyaml.Marshal(info)
245- err := os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
246- if err != nil {
247- return err
248- }
249- err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath)
250- if err != nil {
251- return err
252- }
253- err = ioutil.WriteFile(infoPath, data, 0644)
254- if err != nil {
255- return err
256- }
257- return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
258-}
259-
260-type FakeJujuSuite struct {
261- jujutesting.JujuConnSuite
262-
263- instanceCount int
264- machineStarted map[string]bool
265- fifoPath string
266- logFile *os.File
267-}
268-
269-var _ = gc.Suite(&FakeJujuSuite{})
270-
271-func (s *FakeJujuSuite) SetUpTest(c *gc.C) {
272- var CommandOutput = (*exec.Cmd).CombinedOutput
273- s.JujuConnSuite.SetUpTest(c)
274-
275- ports := s.APIState.APIHostPorts()
276- ports[0][0].NetworkName = "dummy-provider-network"
277- err := s.State.SetAPIHostPorts(ports)
278- c.Assert(err, gc.IsNil)
279-
280- s.machineStarted = make(map[string]bool)
281- s.PatchValue(&corecharm.CacheDir, c.MkDir())
282- password := "dummy-password"
283- if os.Getenv("ADMIN_PASSWORD") != "" {
284- password = os.Getenv("ADMIN_PASSWORD")
285- }
286- _, err = s.State.AddUser("admin", "Admin", password, "dummy-admin")
287- c.Assert(err, gc.IsNil)
288- _, err = s.State.AddEnvironmentUser(
289- names.NewLocalUserTag("admin"), names.NewLocalUserTag("dummy-admin"), "Admin")
290- c.Assert(err, gc.IsNil)
291-
292- // Create a machine to manage the environment.
293- stateServer := s.Factory.MakeMachine(c, &factory.MachineParams{
294- InstanceId: s.newInstanceId(),
295- Nonce: agent.BootstrapNonce,
296- Jobs: []state.MachineJob{state.JobManageEnviron, state.JobHostUnits},
297- Series: "trusty",
298- })
299- c.Assert(stateServer.SetAgentVersion(version.Current), gc.IsNil)
300- address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal)
301- c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil)
302- c.Assert(stateServer.SetStatus(state.StatusStarted, "", nil), gc.IsNil)
303- _, err = stateServer.SetAgentPresence()
304- c.Assert(err, gc.IsNil)
305- s.State.StartSync()
306- err = stateServer.WaitAgentPresence(coretesting.LongWait)
307- c.Assert(err, gc.IsNil)
308-
309- apiInfo := s.APIInfo(c)
310- //fmt.Println(apiInfo.Addrs[0])
311- jujuHome := osenv.JujuHome()
312- fmt.Println(apiInfo.EnvironTag.Id())
313- fmt.Println(jujuHome)
314-
315- binPath := filepath.Join(jujuHome, "bin")
316- os.Mkdir(binPath, 0755)
317- fakeSSHData := []byte("#!/bin/sh\nsleep 1\n")
318- fakeSSHPath := filepath.Join(binPath, "ssh")
319- err = ioutil.WriteFile(fakeSSHPath, fakeSSHData, 0755)
320- c.Assert(err, gc.IsNil)
321- os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
322-
323- s.fifoPath = filepath.Join(jujuHome, "fifo")
324- syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)
325-
326- // Logging
327- logPath := filepath.Join(jujuHome, "fake-juju.log")
328- s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
329- c.Assert(err, gc.IsNil)
330-
331- log.SetOutput(s.logFile)
332- dpkgCmd := exec.Command(
333- "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju")
334- out, err := CommandOutput(dpkgCmd)
335- fakeJujuDebVersion := strings.Trim(string(out), "'")
336- log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome)
337-
338-}
339-
340-func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
341- s.JujuConnSuite.TearDownTest(c)
342- s.logFile.Close()
343-}
344-
345-func (s *FakeJujuSuite) TestStart(c *gc.C) {
346- watcher := s.State.Watch()
347- go func() {
348- fd, err := os.Open(s.fifoPath)
349- c.Assert(err, gc.IsNil)
350- scanner := bufio.NewScanner(fd)
351- scanner.Scan()
352- watcher.Stop()
353- }()
354- for {
355- deltas, err := watcher.Next()
356- log.Println("Got deltas")
357- if err != nil {
358- if err.Error() == "watcher was stopped" {
359- log.Println("Watcher stopped")
360- break
361- }
362- log.Println("Unexpected error", err.Error())
363- }
364- c.Assert(err, gc.IsNil)
365- for _, d := range deltas {
366-
367- entity, err := json.MarshalIndent(d.Entity, "", " ")
368- c.Assert(err, gc.IsNil)
369- verb := "change"
370- if d.Removed {
371- verb = "remove"
372- }
373- log.Println("Processing delta", verb, d.Entity.EntityId().Kind, string(entity[:]))
374-
375- entityId := d.Entity.EntityId()
376- if entityId.Kind == "machine" {
377- machineId, ok := entityId.Id.(string)
378- c.Assert(ok, gc.Equals, true)
379- c.Assert(s.handleAddMachine(machineId), gc.IsNil)
380- }
381- if entityId.Kind == "unit" {
382- unitId, ok := entityId.Id.(string)
383- c.Assert(ok, gc.Equals, true)
384- c.Assert(s.handleAddUnit(unitId), gc.IsNil)
385- }
386- log.Println("Done processing delta")
387- }
388- }
389-}
390-
391-func (s *FakeJujuSuite) handleAddMachine(id string) error {
392- machine, err := s.State.Machine(id)
393- if err != nil {
394- return err
395- }
396- if instanceId, _ := machine.InstanceId(); instanceId == "" {
397- err = machine.SetProvisioned(s.newInstanceId(), agent.BootstrapNonce, nil)
398- if err != nil {
399- log.Println("Got error with SetProvisioned", err)
400- return err
401- }
402- address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal)
403- err = machine.SetProviderAddresses(address)
404- if err != nil {
405- log.Println("Got error with SetProviderAddresses", err)
406- return err
407- }
408- }
409- status, _ := machine.Status()
410- if status.Status == state.StatusPending {
411- if err = s.startMachine(machine); err != nil {
412- log.Println("Got error with startMachine:", err)
413- return err
414- }
415- } else if status.Status == state.StatusStarted {
416- if _, ok := s.machineStarted[id]; !ok {
417- s.machineStarted[id] = true
418- if err = s.startUnits(machine); err != nil {
419- log.Println("Got error with startUnits", err)
420- return err
421- }
422- }
423- }
424- return nil
425-}
426-
427-func (s *FakeJujuSuite) handleAddUnit(id string) error {
428- unit, err := s.State.Unit(id)
429- if err != nil {
430- log.Println("Got error with get unit", err)
431- return err
432- }
433- machineId, err := unit.AssignedMachineId()
434- if err != nil {
435- return nil
436- }
437- log.Println("Got machineId", machineId)
438- machine, err := s.State.Machine(machineId)
439- if err != nil {
440- log.Println("Got error with unit AssignedMachineId", err)
441- return err
442- }
443- machineStatus, _ := machine.Status()
444- if machineStatus.Status != state.StatusStarted {
445- return nil
446- }
447- status, _ := unit.Status()
448- if status.Status != state.StatusActive {
449- if err = s.startUnit(unit); err != nil {
450- return err
451- }
452- }
453- return nil
454-}
455-
456-func (s *FakeJujuSuite) startMachine(machine *state.Machine) error {
457- time.Sleep(500 * time.Millisecond)
458- err := machine.SetStatus(state.StatusStarted, "", nil)
459- if err != nil {
460- return err
461- }
462- err = machine.SetAgentVersion(version.Current)
463- if err != nil {
464- return err
465- }
466- _, err = machine.SetAgentPresence()
467- if err != nil {
468- return err
469- }
470- s.State.StartSync()
471- err = machine.WaitAgentPresence(coretesting.LongWait)
472- if err != nil {
473- return err
474- }
475- return nil
476-}
477-
478-func (s *FakeJujuSuite) startUnits(machine *state.Machine) error {
479- units, err := machine.Units()
480- if err != nil {
481- return err
482- }
483- return nil
484- for _, unit := range units {
485- unitStatus, _ := unit.Status()
486- if unitStatus.Status != state.StatusActive {
487- if err = s.startUnit(unit); err != nil {
488- return err
489- }
490- }
491- }
492- return nil
493-}
494-
495-func (s *FakeJujuSuite) startUnit(unit *state.Unit) error {
496- err := unit.SetStatus(state.StatusActive, "", nil)
497- if err != nil {
498- return err
499- }
500- _, err = unit.SetAgentPresence()
501- if err != nil {
502- return err
503- }
504- s.State.StartSync()
505- err = unit.WaitAgentPresence(coretesting.LongWait)
506- if err != nil {
507- return err
508- }
509- err = unit.SetAgentStatus(state.StatusIdle, "", nil)
510- if err != nil {
511- return err
512- }
513- return nil
514-}
515-
516-func (s *FakeJujuSuite) newInstanceId() instance.Id {
517- s.instanceCount += 1
518- return instance.Id(fmt.Sprintf("id-%d", s.instanceCount))
519-}
520
521=== modified file '1.25.6/fake-juju.go'
522--- 1.25.6/fake-juju.go 2016-10-25 16:32:53 +0000
523+++ 1.25.6/fake-juju.go 2016-10-29 14:08:30 +0000
524@@ -31,11 +31,17 @@
525 coretesting "github.com/juju/juju/testing"
526 "github.com/juju/juju/testing/factory"
527 "github.com/juju/juju/version"
528+ "github.com/juju/loggo"
529 "github.com/juju/names"
530+ "github.com/juju/utils"
531 corecharm "gopkg.in/juju/charm.v5/charmrepo"
532 goyaml "gopkg.in/yaml.v1"
533 )
534
535+const (
536+ envDataDir = "FAKE_JUJU_DATA_DIR"
537+)
538+
539 func main() {
540 code := 0
541 if len(os.Args) > 1 {
542@@ -69,7 +75,7 @@
543 return errors.New("command not found")
544 }
545
546-func bootstrap(filenames fakejujuFilenames) error {
547+func bootstrap(filenames fakejujuFilenames) (returnedErr error) {
548 if err := filenames.ensureDirsExist(); err != nil {
549 return err
550 }
551@@ -84,14 +90,26 @@
552 command.Env = append(command.Env, "ADMIN_PASSWORD="+password)
553 defaultSeries, _ := config.DefaultSeries()
554 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)
555+ command.Env = append(command.Env, envDataDir+"="+filenames.datadir)
556 stdout, err := command.StdoutPipe()
557 if err != nil {
558 return err
559 }
560 command.Start()
561
562+ var whence string
563+ defer func() {
564+ if returnedErr != nil {
565+ if err := destroyEnvironment(filenames); err != nil {
566+ fmt.Printf("could not destroy environment when %s failed: %v\n", whence, err)
567+ }
568+ returnedErr = fmt.Errorf("bootstrap failed while %s: %v", whence, returnedErr)
569+ }
570+ }()
571+
572 result, err := parseApiInfo(stdout)
573 if err != nil {
574+ whence = "parsing bootstrap result"
575 return err
576 }
577 // Get the API info before changing it. The new values might
578@@ -103,9 +121,11 @@
579 result.password = password
580 }
581 if err := result.apply(filenames, envName); err != nil {
582+ whence = "setting up fake-juju files"
583 return err
584 }
585
586+ whence = "waiting-for-ready"
587 dialOpts := api.DialOpts{
588 DialAddressInterval: 50 * time.Millisecond,
589 Timeout: 5 * time.Second,
590@@ -154,11 +174,6 @@
591 }
592
593 func destroyEnvironment(filenames fakejujuFilenames) error {
594- info, err := readProcessInfo(filenames)
595- if err != nil {
596- return err
597- }
598- filenames = newFakeJujuFilenames("", "", info.WorkDir)
599 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)
600 if err != nil {
601 return err
602@@ -228,7 +243,7 @@
603
604 func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
605 if datadir == "" {
606- datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
607+ datadir = os.Getenv(envDataDir)
608 if datadir == "" {
609 if jujucfgdir == "" {
610 jujucfgdir = os.Getenv("JUJU_HOME")
611@@ -267,6 +282,12 @@
612 return filepath.Join(fj.logsdir, "fake-juju.log")
613 }
614
615+// jujudLogsFile() returns the path to the file where fake-juju writes
616+// the jujud logs.
617+func (fj fakejujuFilenames) jujudLogsFile() string {
618+ return filepath.Join(fj.logsdir, "jujud.log")
619+}
620+
621 // fifoFile() returns the path to the FIFO file used by fake-juju.
622 // The FIFO is used by the fake-juju subcommands to interact with
623 // the daemon.
624@@ -317,23 +338,6 @@
625 }
626 }
627
628-// logsSymlinkFilenames() determines the source and target paths for
629-// a symlink to the fake-juju logs file. Such a symlink is relevant
630-// because the fake-juju daemon may not know where the log file is
631-// meant to go. It defaults to putting the log file in the default Juju
632-// config dir. In that case, a symlink should be created from there to
633-// the user-defined Juju config dir ($JUJU_HOME).
634-func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) {
635- if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" || os.Getenv("FAKE_JUJU_DATA_DIR") != "" {
636- return "", ""
637- }
638-
639- filenames := newFakeJujuFilenames("", "", br.cfgdir)
640- source = filenames.logsFile()
641- target = targetLogsFile
642- return source, target
643-}
644-
645 // jenvSymlinkFilenames() determines the source and target paths for
646 // a symlink to the .jenv file for the identified environment.
647 func (br bootstrapResult) jenvSymlinkFilenames(jujuHome, envName string) (source, target string) {
648@@ -353,13 +357,6 @@
649 return err
650 }
651
652- logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile())
653- if logsSource != "" && logsTarget != "" {
654- if err := os.Symlink(logsSource, logsTarget); err != nil {
655- return err
656- }
657- }
658-
659 jenvSource, jenvTarget := br.jenvSymlinkFilenames(os.Getenv("JUJU_HOME"), envName)
660 if jenvSource != "" && jenvTarget != "" {
661 if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil {
662@@ -388,6 +385,10 @@
663 return nil, err
664 }
665 uuid := string(line)
666+ if !utils.IsValidUUIDString(uuid) {
667+ data, _ := ioutil.ReadAll(stdout)
668+ return nil, fmt.Errorf("%s\n%s", line, data)
669+ }
670
671 line, _, err = buffer.ReadLine()
672 if err != nil {
673@@ -395,6 +396,12 @@
674 }
675 workDir := string(line)
676
677+ result := &bootstrapResult{
678+ dummyEnvName: dummyEnvName,
679+ cfgdir: workDir,
680+ uuid: uuid,
681+ }
682+
683 store, err := configstore.NewDisk(workDir)
684 if err != nil {
685 return nil, err
686@@ -403,18 +410,13 @@
687 if err != nil {
688 return nil, err
689 }
690-
691 credentials := info.APICredentials()
692 endpoint := info.APIEndpoint()
693- result := &bootstrapResult{
694- dummyEnvName: dummyEnvName,
695- cfgdir: workDir,
696- uuid: uuid,
697- username: credentials.User,
698- password: credentials.Password,
699- addresses: endpoint.Addresses,
700- caCert: []byte(endpoint.CACert),
701- }
702+ result.username = credentials.User
703+ result.password = credentials.Password
704+ result.addresses = endpoint.Addresses
705+ result.caCert = []byte(endpoint.CACert)
706+
707 return result, nil
708 }
709
710@@ -462,10 +464,10 @@
711 type FakeJujuSuite struct {
712 jujutesting.JujuConnSuite
713
714- instanceCount int
715- machineStarted map[string]bool
716- filenames fakejujuFilenames
717- logFile *os.File
718+ instanceCount int
719+ machineStarted map[string]bool
720+ filenames fakejujuFilenames
721+ toCloseOnTearDown []io.Closer
722 }
723
724 var _ = gc.Suite(&FakeJujuSuite{})
725@@ -474,6 +476,16 @@
726 var CommandOutput = (*exec.Cmd).CombinedOutput
727 s.JujuConnSuite.SetUpTest(c)
728
729+ c.Assert(os.Getenv(envDataDir), gc.Not(gc.Equals), "")
730+ s.filenames = newFakeJujuFilenames("", "", "")
731+ // Note that LoggingSuite.SetUpTest (github.com/juju/testing/log.go),
732+ // called via s.JujuConnSuite.SetUpTest(), calls loggo.ResetLogging().
733+ // So we cannot set up logging before then, since any writer we
734+ // register will get removed. Consequently we lose any logs that get
735+ // generated in the SetUpTest() call.
736+ logFile, jujudLogFile := setUpLogging(c, s.filenames)
737+ s.toCloseOnTearDown = append(s.toCloseOnTearDown, logFile, jujudLogFile)
738+
739 ports := s.APIState.APIHostPorts()
740 ports[0][0].NetworkName = "dummy-provider-network"
741 err := s.State.SetAPIHostPorts(ports)
742@@ -517,10 +529,6 @@
743
744 apiInfo := s.APIInfo(c)
745 jujuHome := osenv.JujuHome()
746- // IMPORTANT: don't remove this logging because it's used by the
747- // bootstrap command.
748- fmt.Println(apiInfo.EnvironTag.Id())
749- fmt.Println(jujuHome)
750
751 binPath := filepath.Join(jujuHome, "bin")
752 os.Mkdir(binPath, 0755)
753@@ -530,27 +538,55 @@
754 c.Assert(err, gc.IsNil)
755 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
756
757- s.filenames = newFakeJujuFilenames("", "", jujuHome)
758+ // Once this FIFO is created, users can start sending commands.
759+ // The actual handling doesn't start until TestStart() runs.
760 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)
761
762- // Logging
763- logPath := s.filenames.logsFile()
764- s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
765- c.Assert(err, gc.IsNil)
766+ // Send the info back to the bootstrap command.
767+ reportInfo(apiInfo.EnvironTag.Id(), jujuHome)
768
769 dpkgCmd := exec.Command(
770 "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju")
771 out, err := CommandOutput(dpkgCmd)
772- log.SetOutput(s.logFile)
773 fakeJujuDebVersion := strings.Trim(string(out), "'")
774 log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome)
775 }
776
777+func setUpLogging(c *gc.C, filenames fakejujuFilenames) (*os.File, *os.File) {
778+ c.Assert(filenames.logsdir, gc.Not(gc.Equals), "")
779+
780+ // fake-juju logging
781+ logPath := filenames.logsFile()
782+ logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
783+ c.Assert(err, gc.IsNil)
784+ log.SetOutput(logFile)
785+
786+ // jujud logging
787+ logPath = filenames.jujudLogsFile()
788+ jujudLogFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
789+ c.Assert(err, gc.IsNil)
790+ err = loggo.RegisterWriter("fake-juju-jujud-logs", loggo.NewSimpleWriter(jujudLogFile, &loggo.DefaultFormatter{}), loggo.TRACE)
791+ c.Assert(err, gc.IsNil)
792+ logger := loggo.GetLogger("fake-juju")
793+ logger.Infof("--- starting logging ---")
794+
795+ return logFile, jujudLogFile
796+}
797+
798+func reportInfo(uuid, jujuCfgDir string) {
799+ // IMPORTANT: don't remove this logging because it's used by the
800+ // bootstrap command.
801+ fmt.Println(uuid)
802+ fmt.Println(jujuCfgDir)
803+}
804+
805 func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
806 log.Println("Tearing down processes")
807 s.JujuConnSuite.TearDownTest(c)
808- log.Println("Closing log file")
809- s.logFile.Close()
810+ log.Println("Closing log files")
811+ for _, closer := range s.toCloseOnTearDown {
812+ closer.Close()
813+ }
814 }
815
816 func (s *FakeJujuSuite) TestStart(c *gc.C) {
817
818=== renamed directory '2.0-beta17' => '2.0.0'
819=== modified file '2.0.0/fake-juju.go'
820--- 2.0-beta17/fake-juju.go 2016-10-25 16:32:53 +0000
821+++ 2.0.0/fake-juju.go 2016-10-29 14:08:30 +0000
822@@ -4,6 +4,7 @@
823 "bufio"
824 "encoding/json"
825 "errors"
826+ "flag"
827 "fmt"
828 gc "gopkg.in/check.v1"
829 "io"
830@@ -19,6 +20,7 @@
831
832 "github.com/juju/juju/agent"
833 "github.com/juju/juju/api"
834+ jujucmdcommon "github.com/juju/juju/cmd/juju/common"
835 "github.com/juju/juju/cmd/juju/controller"
836 "github.com/juju/juju/instance"
837 "github.com/juju/juju/juju/osenv"
838@@ -31,12 +33,20 @@
839 coretesting "github.com/juju/juju/testing"
840 "github.com/juju/juju/testing/factory"
841 "github.com/juju/juju/version"
842+ "github.com/juju/loggo"
843+ "github.com/juju/utils"
844 semversion "github.com/juju/version"
845 corecharm "gopkg.in/juju/charmrepo.v2-unstable"
846 "gopkg.in/juju/names.v2"
847 goyaml "gopkg.in/yaml.v1"
848 )
849
850+const (
851+ envDataDir = "FAKE_JUJU_DATA_DIR"
852+ envLogsDir = "FAKE_JUJU_LOGS_DIR"
853+ envFailuresFile = "FAKE_JUJU_FAILURES"
854+)
855+
856 func main() {
857 code := 0
858 if len(os.Args) > 1 {
859@@ -56,49 +66,150 @@
860 func handleCommand(command string) error {
861 filenames := newFakeJujuFilenames("", "", "")
862 if command == "bootstrap" {
863- return bootstrap(filenames)
864+ return handleBootstrap(filenames)
865 }
866 if command == "show-controller" {
867- return apiInfo(filenames)
868+ return handleAPIInfo(filenames)
869 }
870 if command == "destroy-controller" {
871- return destroyController(filenames)
872+ return handleDestroyController(filenames)
873 }
874 return errors.New("command not found")
875 }
876
877-func bootstrap(filenames fakejujuFilenames) error {
878- argc := len(os.Args)
879- if argc < 4 {
880- return errors.New(
881- "error: controller name and cloud name are required")
882+func handleBootstrap(filenames fakejujuFilenames) (returnedErr error) {
883+ var args bootstrapArgs
884+ if err := args.parse(); err != nil {
885+ return err
886 }
887 if err := filenames.ensureDirsExist(); err != nil {
888 return err
889 }
890- // XXX Swap the 2 args for juju-2.0-final.
891- controllerName := os.Args[argc-2]
892+
893+ // Start the fake-juju daemon.
894 command := exec.Command(os.Args[0])
895 command.Env = os.Environ()
896 command.Env = append(
897 command.Env, "ADMIN_PASSWORD="+"pwd")
898 defaultSeries := "trusty"
899 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)
900+ command.Env = append(command.Env, envDataDir+"="+filenames.datadir)
901 stdout, err := command.StdoutPipe()
902 if err != nil {
903 return err
904 }
905 command.Start()
906
907+ var whence string
908+ defer func() {
909+ if returnedErr != nil {
910+ if err := destroyController(filenames); err != nil {
911+ fmt.Printf("could not destroy controller when %s failed: %v\n", whence, err)
912+ }
913+ returnedErr = fmt.Errorf("bootstrap failed while %s: %v", whence, returnedErr)
914+ }
915+ }()
916+
917+ // Get the internal info from the daemon and store it.
918 result, err := parseApiInfo(stdout)
919 if err != nil {
920- return err
921- }
922- if err := result.apply(filenames, controllerName); err != nil {
923- return err
924- }
925+ whence = "parsing bootstrap result"
926+ return err
927+ }
928+ if err := result.copyConfig(os.Getenv("JUJU_DATA"), args.controllerName); err != nil {
929+ whence = "copying config"
930+ return err
931+ }
932+ if err := updateBootstrapResult(result); err != nil {
933+ whence = "updating bootstrap result"
934+ return err
935+ }
936+ if err := result.apply(filenames); err != nil {
937+ whence = "setting up fake-juju files"
938+ return err
939+ }
940+
941+ // Wait for the daemon to finish starting up.
942+ if err := waitForBootstrapCompletion(result); err != nil {
943+ whence = "waiting-for-ready"
944+ return err
945+ }
946+
947+ return nil
948+}
949+
950+// bootstrapArgs is an adaptation of bootstrapCommand from
951+// github.com/juju/juju/cmd/juju/commands/bootstrap.go.
952+type bootstrapArgs struct {
953+ config jujucmdcommon.ConfigFlag
954+ hostedModelName string
955+ credentialName string
956+
957+ controllerName string
958+ cloud string
959+ region string
960+}
961+
962+func (bsargs *bootstrapArgs) parse() error {
963+ flags := flag.NewFlagSet("bootstrap", flag.ExitOnError)
964+
965+ var ignoredStr string
966+ flags.StringVar(&ignoredStr, "constraints", "", "")
967+ flags.StringVar(&ignoredStr, "bootstrap-constraints", "", "")
968+ flags.StringVar(&ignoredStr, "bootstrap-series", "", "")
969+ flags.StringVar(&ignoredStr, "bootstrap-image", "", "")
970+ flags.StringVar(&ignoredStr, "metadata-source", "", "")
971+ flags.StringVar(&ignoredStr, "to", "", "")
972+ flags.StringVar(&ignoredStr, "agent-version", "", "")
973+ flags.StringVar(&ignoredStr, "regions", "", "")
974+
975+ var ignoredBool bool
976+ flags.BoolVar(&ignoredBool, "v", false, "")
977+ flags.BoolVar(&ignoredBool, "build-agent", false, "")
978+ flags.BoolVar(&ignoredBool, "keep-broken", false, "")
979+ flags.BoolVar(&ignoredBool, "auto-upgrade", false, "")
980+ flags.BoolVar(&ignoredBool, "no-gui", false, "")
981+ flags.BoolVar(&ignoredBool, "clouds", false, "")
982+
983+ flags.Var(&bsargs.config, "config", "")
984+ flags.StringVar(&bsargs.credentialName, "credential", "", "")
985+ flags.StringVar(&bsargs.hostedModelName, "d", "", "")
986+ flags.StringVar(&bsargs.hostedModelName, "default-model", "", "")
987+
988+ flags.Parse(os.Args[2:])
989+
990+ args := flags.Args()
991+ switch len(args) {
992+ case 0:
993+ return fmt.Errorf("expected at least one positional arg, got none")
994+ case 1:
995+ bsargs.cloud = args[0]
996+ case 2:
997+ bsargs.cloud = args[0]
998+ bsargs.controllerName = args[1]
999+ default:
1000+ return fmt.Errorf("expected at most two positional args, got %v", args)
1001+ }
1002+
1003+ if i := strings.IndexRune(bsargs.cloud, '/'); i > 0 {
1004+ bsargs.cloud, bsargs.region = bsargs.cloud[:i], bsargs.cloud[i+1:]
1005+ }
1006+ if bsargs.controllerName == "" {
1007+ bsargs.controllerName = bsargs.cloud
1008+ if bsargs.region == "" {
1009+ bsargs.controllerName += "-" + bsargs.region
1010+ }
1011+ }
1012+
1013+ if bsargs.hostedModelName == "" {
1014+ bsargs.hostedModelName = "default"
1015+ }
1016+
1017+ return nil
1018+}
1019+
1020+func waitForBootstrapCompletion(result *bootstrapResult) error {
1021 apiInfo := result.apiInfo()
1022-
1023 dialOpts := api.DialOpts{
1024 DialAddressInterval: 50 * time.Millisecond,
1025 Timeout: 5 * time.Second,
1026@@ -128,7 +239,7 @@
1027 return errors.New("invalid delta")
1028 }
1029
1030-func apiInfo(filenames fakejujuFilenames) error {
1031+func handleAPIInfo(filenames fakejujuFilenames) error {
1032 info, err := readProcessInfo(filenames)
1033 if err != nil {
1034 return err
1035@@ -137,21 +248,22 @@
1036 jujuHome := os.Getenv("JUJU_DATA")
1037 osenv.SetJujuXDGDataHome(jujuHome)
1038 cmd := controller.NewShowControllerCommand()
1039- ctx, err := coretesting.RunCommandInDir(
1040- nil, cmd, os.Args[2:], info.WorkDir)
1041- if err != nil {
1042+ if err := coretesting.InitCommand(cmd, os.Args[2:]); err != nil {
1043+ return err
1044+ }
1045+ ctx := coretesting.ContextForDir(nil, info.WorkDir)
1046+ if err := cmd.Run(ctx); err != nil {
1047 return err
1048 }
1049 fmt.Print(ctx.Stdout)
1050 return nil
1051 }
1052
1053+func handleDestroyController(filenames fakejujuFilenames) error {
1054+ return destroyController(filenames)
1055+}
1056+
1057 func destroyController(filenames fakejujuFilenames) error {
1058- info, err := readProcessInfo(filenames)
1059- if err != nil {
1060- return err
1061- }
1062- filenames = newFakeJujuFilenames("", "", info.WorkDir)
1063 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)
1064 if err != nil {
1065 return err
1066@@ -203,7 +315,7 @@
1067
1068 func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
1069 if datadir == "" {
1070- datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
1071+ datadir = os.Getenv(envDataDir)
1072 if datadir == "" {
1073 if jujucfgdir == "" {
1074 jujucfgdir = os.Getenv("JUJU_DATA")
1075@@ -212,7 +324,7 @@
1076 }
1077 }
1078 if logsdir == "" {
1079- logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")
1080+ logsdir = os.Getenv(envLogsDir)
1081 if logsdir == "" {
1082 logsdir = datadir
1083 }
1084@@ -242,6 +354,12 @@
1085 return filepath.Join(fj.logsdir, "fake-juju.log")
1086 }
1087
1088+// jujudLogsFile() returns the path to the file where fake-juju writes
1089+// the jujud logs.
1090+func (fj fakejujuFilenames) jujudLogsFile() string {
1091+ return filepath.Join(fj.logsdir, "jujud.log")
1092+}
1093+
1094 // fifoFile() returns the path to the FIFO file used by fake-juju.
1095 // The FIFO is used by the fake-juju subcommands to interact with
1096 // the daemon.
1097@@ -290,41 +408,13 @@
1098 }
1099 }
1100
1101-// logsSymlinkFilenames() determines the source and target paths for
1102-// a symlink to the fake-juju logs file. Such a symlink is relevant
1103-// because the fake-juju daemon may not know where the log file is
1104-// meant to go. It defaults to putting the log file in the default Juju
1105-// config dir. In that case, a symlink should be created from there to
1106-// the user-defined Juju config dir ($JUJU_DATA).
1107-func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) {
1108- if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" {
1109- return "", ""
1110- }
1111-
1112- filenames := newFakeJujuFilenames("", "", br.cfgdir)
1113- source = filenames.logsFile()
1114- target = targetLogsFile
1115- return source, target
1116-}
1117-
1118 // apply() writes out the information from the bootstrap result to the
1119 // various files identified by the provided filenames.
1120-func (br bootstrapResult) apply(filenames fakejujuFilenames, controllerName string) error {
1121+func (br bootstrapResult) apply(filenames fakejujuFilenames) error {
1122 if err := br.fakeJujuInfo().write(filenames.infoFile()); err != nil {
1123 return err
1124 }
1125
1126- logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile())
1127- if logsSource != "" && logsTarget != "" {
1128- if err := os.Symlink(logsSource, logsTarget); err != nil {
1129- return err
1130- }
1131- }
1132-
1133- if err := br.copyConfig(os.Getenv("JUJU_DATA"), controllerName); err != nil {
1134- return err
1135- }
1136-
1137 if err := ioutil.WriteFile(filenames.caCertFile(), br.caCert, 0644); err != nil {
1138 return err
1139 }
1140@@ -333,6 +423,9 @@
1141 }
1142
1143 func (br bootstrapResult) copyConfig(targetCfgDir, controllerName string) error {
1144+ if err := os.MkdirAll(targetCfgDir, 0755); err != nil {
1145+ return err
1146+ }
1147 for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} {
1148 source := filepath.Join(br.cfgdir, name)
1149 target := filepath.Join(targetCfgDir, name)
1150@@ -360,8 +453,7 @@
1151 return nil
1152 }
1153
1154-// See github.com/juju/juju/blob/juju/testing/conn.go.
1155-const dummyControllerName = "kontroll"
1156+const dummyControllerName = jujutesting.ControllerName
1157
1158 func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
1159 buffer := bufio.NewReader(stdout)
1160@@ -371,6 +463,10 @@
1161 return nil, err
1162 }
1163 uuid := string(line)
1164+ if !utils.IsValidUUIDString(uuid) {
1165+ data, _ := ioutil.ReadAll(stdout)
1166+ return nil, fmt.Errorf("%s\n%s", line, data)
1167+ }
1168
1169 line, _, err = buffer.ReadLine()
1170 if err != nil {
1171@@ -378,31 +474,37 @@
1172 }
1173 workDir := string(line)
1174
1175- osenv.SetJujuXDGDataHome(workDir)
1176+ result := &bootstrapResult{
1177+ dummyControllerName: dummyControllerName,
1178+ cfgdir: workDir,
1179+ uuid: uuid,
1180+ }
1181+ return result, nil
1182+}
1183+
1184+func updateBootstrapResult(result *bootstrapResult) error {
1185+ osenv.SetJujuXDGDataHome(result.cfgdir)
1186 store := jujuclient.NewFileClientStore()
1187+
1188 // hard-coded value in juju testing
1189 // This will be replaced in JUJU_DATA copy of the juju client config.
1190- currentController := dummyControllerName
1191+ currentController := result.dummyControllerName
1192+
1193 one, err := store.ControllerByName(currentController)
1194 if err != nil {
1195- return nil, err
1196+ return err
1197 }
1198+ result.addresses = one.APIEndpoints
1199+ result.caCert = []byte(one.CACert)
1200
1201 accountDetails, err := store.AccountDetails(currentController)
1202 if err != nil {
1203- return nil, err
1204+ return err
1205 }
1206+ result.username = accountDetails.User
1207+ result.password = accountDetails.Password
1208
1209- result := &bootstrapResult{
1210- dummyControllerName: dummyControllerName,
1211- cfgdir: workDir,
1212- uuid: uuid,
1213- username: accountDetails.User,
1214- password: accountDetails.Password,
1215- addresses: one.APIEndpoints,
1216- caCert: []byte(one.CACert),
1217- }
1218- return result, nil
1219+ return nil
1220 }
1221
1222 // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
1223@@ -411,9 +513,9 @@
1224 // entity transition to an error state.
1225 func readFailuresInfo() (map[string]bool, error) {
1226 log.Println("Checking for forced failures")
1227- failuresPath := os.Getenv("FAKE_JUJU_FAILURES")
1228+ failuresPath := os.Getenv(envFailuresFile)
1229 if failuresPath == "" {
1230- log.Println("No FAKE_JUJU_FAILURES env variable set")
1231+ log.Printf("No %s env variable set\n", envFailuresFile)
1232 }
1233 log.Println("Reading failures file", failuresPath)
1234 failuresInfo := map[string]bool{}
1235@@ -449,10 +551,10 @@
1236 type FakeJujuSuite struct {
1237 jujutesting.JujuConnSuite
1238
1239- instanceCount int
1240- machineStarted map[string]bool
1241- filenames fakejujuFilenames
1242- logFile *os.File
1243+ instanceCount int
1244+ machineStarted map[string]bool
1245+ filenames fakejujuFilenames
1246+ toCloseOnTearDown []io.Closer
1247 }
1248
1249 var _ = gc.Suite(&FakeJujuSuite{})
1250@@ -460,6 +562,16 @@
1251 func (s *FakeJujuSuite) SetUpTest(c *gc.C) {
1252 s.JujuConnSuite.SetUpTest(c)
1253
1254+ c.Assert(os.Getenv(envDataDir), gc.Not(gc.Equals), "")
1255+ s.filenames = newFakeJujuFilenames("", "", "")
1256+ // Note that LoggingSuite.SetUpTest (github.com/juju/testing/log.go),
1257+ // called via s.JujuConnSuite.SetUpTest(), calls loggo.ResetLogging().
1258+ // So we cannot set up logging before then, since any writer we
1259+ // register will get removed. Consequently we lose any logs that get
1260+ // generated in the SetUpTest() call.
1261+ logFile, jujudLogFile := setUpLogging(c, s.filenames)
1262+ s.toCloseOnTearDown = append(s.toCloseOnTearDown, logFile, jujudLogFile)
1263+
1264 ports := s.APIState.APIHostPorts()
1265 err := s.State.SetAPIHostPorts(ports)
1266 c.Assert(err, gc.IsNil)
1267@@ -491,7 +603,7 @@
1268 c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil)
1269 now := time.Now()
1270 sInfo := states.StatusInfo{
1271- Status: states.StatusStarted,
1272+ Status: states.Started,
1273 Message: "",
1274 Since: &now,
1275 }
1276@@ -505,10 +617,6 @@
1277 apiInfo := s.APIInfo(c)
1278 //fmt.Println(apiInfo.Addrs[0])
1279 jujuHome := osenv.JujuXDGDataHome()
1280- // IMPORTANT: don't remove this logging because it's used by the
1281- // bootstrap command.
1282- fmt.Println(apiInfo.ModelTag.Id())
1283- fmt.Println(jujuHome)
1284
1285 binPath := filepath.Join(jujuHome, "bin")
1286 os.Mkdir(binPath, 0755)
1287@@ -518,24 +626,51 @@
1288 c.Assert(err, gc.IsNil)
1289 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
1290
1291- s.filenames = newFakeJujuFilenames("", "", jujuHome)
1292+ // Once this FIFO is created, users can start sending commands.
1293+ // The actual handling doesn't start until TestStart() runs.
1294 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)
1295
1296- // Logging
1297- logPath := s.filenames.logsFile()
1298- s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
1299- c.Assert(err, gc.IsNil)
1300+ // Send the info back to the bootstrap command.
1301+ reportInfo(apiInfo.ModelTag.Id(), jujuHome)
1302
1303- log.SetOutput(s.logFile)
1304 log.Println("Started fake-juju at ", jujuHome)
1305-
1306+}
1307+
1308+func setUpLogging(c *gc.C, filenames fakejujuFilenames) (*os.File, *os.File) {
1309+ c.Assert(filenames.logsdir, gc.Not(gc.Equals), "")
1310+
1311+ // fake-juju logging
1312+ logPath := filenames.logsFile()
1313+ logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
1314+ c.Assert(err, gc.IsNil)
1315+ log.SetOutput(logFile)
1316+
1317+ // jujud logging
1318+ logPath = filenames.jujudLogsFile()
1319+ jujudLogFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
1320+ c.Assert(err, gc.IsNil)
1321+ err = loggo.RegisterWriter("fake-juju-jujud-logs", loggo.NewSimpleWriter(jujudLogFile, nil))
1322+ c.Assert(err, gc.IsNil)
1323+ logger := loggo.GetLogger("fake-juju")
1324+ logger.Infof("--- starting logging ---")
1325+
1326+ return logFile, jujudLogFile
1327+}
1328+
1329+func reportInfo(uuid, jujuCfgDir string) {
1330+ // IMPORTANT: don't remove this logging because it's used by the
1331+ // bootstrap command.
1332+ fmt.Println(uuid)
1333+ fmt.Println(jujuCfgDir)
1334 }
1335
1336 func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
1337 log.Println("Tearing down processes")
1338 s.JujuConnSuite.TearDownTest(c)
1339- log.Println("Closing log file")
1340- s.logFile.Close()
1341+ log.Println("Closing log files")
1342+ for _, closer := range s.toCloseOnTearDown {
1343+ closer.Close()
1344+ }
1345 }
1346
1347 func (s *FakeJujuSuite) TestStart(c *gc.C) {
1348@@ -625,12 +760,12 @@
1349 }
1350 status, _ := machine.Status()
1351 log.Println("Machine has status:", string(status.Status), status.Message)
1352- if status.Status == states.StatusPending {
1353+ if status.Status == states.Pending {
1354 if err = s.startMachine(machine); err != nil {
1355 log.Println("Got error with startMachine:", err)
1356 return err
1357 }
1358- } else if status.Status == states.StatusStarted {
1359+ } else if status.Status == states.Started {
1360 log.Println("Starting units on machine", id)
1361 if _, ok := s.machineStarted[id]; !ok {
1362 s.machineStarted[id] = true
1363@@ -661,19 +796,19 @@
1364 return err
1365 }
1366 machineStatus, _ := machine.Status()
1367- if machineStatus.Status != states.StatusStarted {
1368+ if machineStatus.Status != states.Started {
1369 return nil
1370 }
1371 status, _ := unit.Status()
1372 log.Println("Unit has status", string(status.Status), status.Message)
1373- if status.Status != states.StatusActive && status.Status != states.StatusError {
1374+ if status.Status != states.Active && status.Status != states.Error {
1375 log.Println("Start unit", id)
1376 err = s.startUnit(unit)
1377 if err != nil {
1378 log.Println("Got error changing unit status", id, err)
1379 return err
1380 }
1381- } else if status.Status != states.StatusError {
1382+ } else if status.Status != states.Error {
1383 failuresInfo, err := readFailuresInfo()
1384 if err != nil {
1385 return err
1386@@ -684,7 +819,7 @@
1387 log.Println("Got error checking agent status", id, err)
1388 return err
1389 }
1390- if agentStatus.Status != states.StatusError {
1391+ if agentStatus.Status != states.Error {
1392 log.Println("Error unit", id)
1393 err = s.errorUnit(unit)
1394 if err != nil {
1395@@ -701,7 +836,7 @@
1396 time.Sleep(500 * time.Millisecond)
1397 now := time.Now()
1398 sInfo := states.StatusInfo{
1399- Status: states.StatusStarted,
1400+ Status: states.Started,
1401 Message: "",
1402 Since: &now,
1403 }
1404@@ -734,7 +869,7 @@
1405 time.Sleep(500 * time.Millisecond)
1406 now := time.Now()
1407 sInfo := states.StatusInfo{
1408- Status: states.StatusError,
1409+ Status: states.Error,
1410 Message: "machine errored",
1411 Since: &now,
1412 }
1413@@ -753,7 +888,7 @@
1414 return nil
1415 for _, unit := range units {
1416 unitStatus, _ := unit.Status()
1417- if unitStatus.Status != states.StatusActive {
1418+ if unitStatus.Status != states.Active {
1419 if err = s.startUnit(unit); err != nil {
1420 return err
1421 }
1422@@ -765,7 +900,7 @@
1423 func (s *FakeJujuSuite) startUnit(unit *state.Unit) error {
1424 now := time.Now()
1425 sInfo := states.StatusInfo{
1426- Status: states.StatusStarted,
1427+ Status: states.Started,
1428 Message: "",
1429 Since: &now,
1430 }
1431@@ -783,7 +918,7 @@
1432 return err
1433 }
1434 idleInfo := states.StatusInfo{
1435- Status: states.StatusIdle,
1436+ Status: states.Idle,
1437 Message: "",
1438 Since: &now,
1439 }
1440@@ -798,7 +933,7 @@
1441 log.Println("Erroring unit", unit.Name())
1442 now := time.Now()
1443 sInfo := states.StatusInfo{
1444- Status: states.StatusIdle,
1445+ Status: states.Idle,
1446 Message: "unit errored",
1447 Since: &now,
1448 }
1449
1450=== modified file 'Makefile'
1451--- Makefile 2016-10-27 16:36:28 +0000
1452+++ Makefile 2016-10-29 14:08:30 +0000
1453@@ -63,8 +63,8 @@
1454 # for all versions
1455
1456 ifndef JUJU_VERSIONS
1457-JUJU1_VERSIONS = 1.24.7 1.25.6
1458-JUJU2_VERSIONS = 2.0-beta17
1459+JUJU1_VERSIONS = 1.25.6
1460+JUJU2_VERSIONS = 2.0.0
1461 JUJU_VERSIONS = $(JUJU1_VERSIONS) $(JUJU2_VERSIONS)
1462 endif
1463 BUILT_VERSIONS = $(foreach version,$(JUJU_VERSIONS),$(version)/$(version))
1464
1465=== removed file 'patches/juju-core_1.24.7.patch'
1466--- patches/juju-core_1.24.7.patch 2016-03-18 11:10:52 +0000
1467+++ patches/juju-core_1.24.7.patch 1970-01-01 00:00:00 +0000
1468@@ -1,47 +0,0 @@
1469---- 1.24.7/src/github.com/juju/juju/testcharms/charm.go.orig 2015-06-24 12:02:02.746416146 +0200
1470-+++ 1.24.7/src/github.com/juju/juju/testcharms/charm.go 2015-06-24 12:03:49.810418650 +0200
1471-@@ -10,4 +10,6 @@
1472- )
1473-
1474- // Repo provides access to the test charm repository.
1475--var Repo = testing.NewRepo("charm-repo", "quantal")
1476-+// XXX fake-juju: avoid crashing because the charm-repo dir is not there
1477-+//var Repo = testing.NewRepo("charm-repo", "quantal")
1478-+var Repo = &testing.Repo{}
1479-
1480---- 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go.orig 2015-07-06 15:01:14.200568258 +0200
1481-+++ 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200
1482-@@ -642,9 +642,9 @@
1483-
1484- // PrecheckInstance is specified in the state.Prechecker interface.
1485- func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
1486-- if placement != "" && placement != "valid" {
1487-- return fmt.Errorf("%s placement is invalid", placement)
1488-- }
1489-+// if placement != "" && placement != "valid" {
1490-+// return fmt.Errorf("%s placement is invalid", placement)
1491-+// }
1492- return nil
1493- }
1494-
1495---- 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000
1496-+++ 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000
1497-@@ -52,7 +52,7 @@
1498- }
1499-
1500- func mustNewCA() (string, string) {
1501-- cert.KeyBits = 512
1502-+ cert.KeyBits = 1024
1503- caCert, caKey, err := cert.NewCA("juju testing", time.Now().AddDate(10, 0, 0))
1504- if err != nil {
1505- panic(err)
1506-@@ -61,7 +61,7 @@
1507- }
1508-
1509- func mustNewServer() (string, string) {
1510-- cert.KeyBits = 512
1511-+ cert.KeyBits = 1024
1512- var hostnames []string
1513- srvCert, srvKey, err := cert.NewServer(CACert, CAKey, time.Now().AddDate(10, 0, 0), hostnames)
1514- if err != nil {
1515-
1516
1517=== renamed file 'patches/juju-core_2.0-beta17.patch' => 'patches/juju-core_2.0.0.patch'
1518--- patches/juju-core_2.0-beta17.patch 2016-09-01 22:03:22 +0000
1519+++ patches/juju-core_2.0.0.patch 2016-10-29 14:08:30 +0000
1520@@ -1,5 +1,5 @@
1521---- 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +0100
1522-+++ 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +0100
1523+--- 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +0100
1524++++ 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +0100
1525 @@ -17,7 +17,9 @@
1526 )
1527
1528@@ -11,8 +11,8 @@
1529
1530 // UploadCharm uploads a charm using the given charm store client, and returns
1531 // the resulting charm URL and charm.
1532---- 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +0200
1533-+++ 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200
1534+--- 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +0200
1535++++ 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200
1536 @@ -633,9 +633,9 @@
1537
1538 // PrecheckInstance is specified in the state.Prechecker interface.
1539@@ -26,8 +26,8 @@
1540 return nil
1541 }
1542
1543---- 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000
1544-+++ 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000
1545+--- 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000
1546++++ 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000
1547 @@ -52,7 +52,7 @@
1548 }
1549
1550
1551=== modified file 'python/fakejuju/testing.py'
1552--- python/fakejuju/testing.py 2016-10-18 20:54:27 +0000
1553+++ python/fakejuju/testing.py 2016-10-29 14:08:30 +0000
1554@@ -7,8 +7,8 @@
1555
1556
1557 JUJU1_VER = "1.25.6"
1558-JUJU2_VER = "2.0-beta17"
1559-JUJU_VER = JUJU1_VER
1560+JUJU2_VER = "2.0.0"
1561+JUJU_VER = JUJU2_VER
1562
1563
1564 class FakeJujuFixture(Fixture):
1565
1566=== modified file 'tests/test_fake.py'
1567--- tests/test_fake.py 2016-09-15 20:38:52 +0000
1568+++ tests/test_fake.py 2016-10-29 14:08:30 +0000
1569@@ -37,7 +37,7 @@
1570 super(_JujuFakeTest, self).setUp()
1571
1572 self.env = os.environ.copy()
1573- self.juju_home = cfgdir = tempfile.mkdtemp()
1574+ self.juju_home = cfgdir = tempfile.mkdtemp(prefix="fake-juju-test-")
1575 _jujuclient.prepare("dummy", "dummy", cfgdir, self.env, JUJU_VERSION)
1576
1577 endpoint, uuid, password = self.bootstrap("dummy", "dummy", self.env)
1578@@ -100,7 +100,7 @@
1579
1580 def bootstrap(self, name, type, env):
1581 """Return the API endpoint after bootstrapping the controller."""
1582- args = [JUJU_FAKE, "bootstrap", "--no-gui", name, type]
1583+ args = [JUJU_FAKE, "bootstrap", "--no-gui", type, name]
1584 subprocess.check_call(args, env=env)
1585
1586 args = [JUJU_FAKE, "show-controller", "--format", "json",
1587@@ -108,7 +108,7 @@
1588 output = subprocess.check_output(args, env=env)
1589 api_info = json.loads(output.decode())
1590 endpoint = str(api_info[name]["details"]["api-endpoints"][0])
1591- model = api_info[name]["current-model"]
1592+ model = api_info[name]["current-model"].split("/", 1)[-1]
1593 uuid = api_info[name]["models"][model]["uuid"]
1594 password = api_info[name]["account"]["password"]
1595 return endpoint, uuid, password

Subscribers

People subscribed via source and target branches

to all changes: