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
=== removed directory '1.24.7'
=== removed file '1.24.7/fake-juju.go'
--- 1.24.7/fake-juju.go 2016-06-10 17:08:28 +0000
+++ 1.24.7/fake-juju.go 1970-01-01 00:00:00 +0000
@@ -1,514 +0,0 @@
1package main
2
3import (
4 "bufio"
5 "encoding/json"
6 "errors"
7 "fmt"
8 gc "gopkg.in/check.v1"
9 "io"
10 "io/ioutil"
11 "log"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "strings"
16 "syscall"
17 "testing"
18 "time"
19
20 "github.com/juju/juju/agent"
21 "github.com/juju/juju/api"
22 "github.com/juju/juju/environs"
23 "github.com/juju/juju/environs/configstore"
24 "github.com/juju/juju/instance"
25 "github.com/juju/juju/juju/osenv"
26 jujutesting "github.com/juju/juju/juju/testing"
27 "github.com/juju/juju/network"
28 _ "github.com/juju/juju/provider/maas"
29 "github.com/juju/juju/state"
30 coretesting "github.com/juju/juju/testing"
31 "github.com/juju/juju/testing/factory"
32 "github.com/juju/juju/version"
33 "github.com/juju/names"
34 corecharm "gopkg.in/juju/charm.v5/charmrepo"
35 goyaml "gopkg.in/yaml.v1"
36)
37
38func main() {
39 if len(os.Args) > 1 {
40 code := 0
41 err := handleCommand(os.Args[1])
42 if err != nil {
43 fmt.Println(err.Error())
44 code = 1
45 }
46 os.Exit(code)
47 }
48 t := &testing.T{}
49 coretesting.MgoTestPackage(t)
50}
51
52type processInfo struct {
53 WorkDir string
54 EndpointAddr string
55 Uuid string
56 CACert string
57}
58
59func handleCommand(command string) error {
60 if command == "bootstrap" {
61 return bootstrap()
62 }
63 if command == "api-endpoints" {
64 return apiEndpoints()
65 }
66 if command == "api-info" {
67 return apiInfo()
68 }
69 if command == "destroy-environment" {
70 return destroyEnvironment()
71 }
72 return errors.New("command not found")
73}
74
75func bootstrap() error {
76 envName, password, err := environmentNameAndPassword()
77 if err != nil {
78 return err
79 }
80 command := exec.Command(os.Args[0])
81 command.Env = os.Environ()
82 command.Env = append(command.Env, "ADMIN_PASSWORD="+password)
83 stdout, err := command.StdoutPipe()
84 if err != nil {
85 return err
86 }
87 command.Start()
88 apiInfo, err := parseApiInfo(envName, stdout)
89 if err != nil {
90 return err
91 }
92 dialOpts := api.DialOpts{
93 DialAddressInterval: 50 * time.Millisecond,
94 Timeout: 5 * time.Second,
95 RetryDelay: 2 * time.Second,
96 }
97 state, err := api.Open(apiInfo, dialOpts)
98 if err != nil {
99 return err
100 }
101 client := state.Client()
102 watcher, err := client.WatchAll()
103 if err != nil {
104 return err
105 }
106 deltas, err := watcher.Next()
107 if err != nil {
108 return err
109 }
110 for _, delta := range deltas {
111 entityId := delta.Entity.EntityId()
112 if entityId.Kind == "machine" {
113 machineId, _ := entityId.Id.(string)
114 if machineId == "0" {
115 return nil
116 }
117 }
118 }
119 return errors.New("invalid delta")
120}
121
122func apiEndpoints() error {
123 info, err := readProcessInfo()
124 if err != nil {
125 return err
126 }
127 fmt.Println(info.EndpointAddr)
128 return nil
129}
130
131func apiInfo() error {
132 info, err := readProcessInfo()
133 if err != nil {
134 return err
135 }
136 fmt.Printf("{\"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", info.Uuid, info.EndpointAddr)
137 return nil
138}
139
140func destroyEnvironment() error {
141 info, err := readProcessInfo()
142 if err != nil {
143 return err
144 }
145 fifoPath := filepath.Join(info.WorkDir, "fifo")
146 fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)
147 if err != nil {
148 return err
149 }
150 defer fd.Close()
151 _, err = fd.WriteString("destroy\n")
152 if err != nil {
153 return err
154 }
155 return nil
156}
157
158func environmentNameAndPassword() (string, string, error) {
159 jujuHome := os.Getenv("JUJU_HOME")
160 osenv.SetJujuHome(jujuHome)
161 environs, err := environs.ReadEnvirons(
162 filepath.Join(jujuHome, "environments.yaml"))
163 if err != nil {
164 return "", "", err
165 }
166 envName := environs.Names()[0]
167 config, err := environs.Config(envName)
168 if err != nil {
169 return "", "", err
170 }
171 return envName, config.AdminSecret(), nil
172}
173
174func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {
175 buffer := bufio.NewReader(stdout)
176 line, _, err := buffer.ReadLine()
177 if err != nil {
178 return nil, err
179 }
180 uuid := string(line)
181 environTag := names.NewEnvironTag(uuid)
182 line, _, err = buffer.ReadLine()
183 if err != nil {
184 return nil, err
185 }
186 workDir := string(line)
187 store, err := configstore.NewDisk(workDir)
188 if err != nil {
189 return nil, err
190 }
191 info, err := store.ReadInfo("dummyenv")
192 if err != nil {
193 return nil, err
194 }
195 credentials := info.APICredentials()
196 endpoint := info.APIEndpoint()
197 addresses := endpoint.Addresses
198 apiInfo := &api.Info{
199 Addrs: addresses,
200 Tag: names.NewLocalUserTag(credentials.User),
201 Password: credentials.Password,
202 CACert: endpoint.CACert,
203 EnvironTag: environTag,
204 }
205 err = writeProcessInfo(envName, &processInfo{
206 WorkDir: workDir,
207 EndpointAddr: addresses[0],
208 Uuid: uuid,
209 CACert: endpoint.CACert,
210 })
211 if err != nil {
212 return nil, err
213 }
214 return apiInfo, nil
215}
216
217func readProcessInfo() (*processInfo, error) {
218 infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju")
219 data, err := ioutil.ReadFile(infoPath)
220 if err != nil {
221 return nil, err
222 }
223 info := &processInfo{}
224 err = goyaml.Unmarshal(data, info)
225 if err != nil {
226 return nil, err
227 }
228 return info, nil
229}
230
231func writeProcessInfo(envName string, info *processInfo) error {
232 jujuHome := os.Getenv("JUJU_HOME")
233 infoPath := filepath.Join(jujuHome, "fakejuju")
234 logPath := filepath.Join(jujuHome, "fake-juju.log")
235 caCertPath := filepath.Join(jujuHome, "cert.ca")
236 envPath := filepath.Join(jujuHome, "environments")
237 os.Mkdir(envPath, 0755)
238 jEnvPath := filepath.Join(envPath, envName+".jenv")
239 data, _ := goyaml.Marshal(info)
240 err := os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
241 if err != nil {
242 return err
243 }
244 err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath)
245 if err != nil {
246 return err
247 }
248 err = ioutil.WriteFile(infoPath, data, 0644)
249 if err != nil {
250 return err
251 }
252 return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
253}
254
255type FakeJujuSuite struct {
256 jujutesting.JujuConnSuite
257
258 instanceCount int
259 machineStarted map[string]bool
260 fifoPath string
261 logFile *os.File
262}
263
264var _ = gc.Suite(&FakeJujuSuite{})
265
266func (s *FakeJujuSuite) SetUpTest(c *gc.C) {
267 var CommandOutput = (*exec.Cmd).CombinedOutput
268 s.JujuConnSuite.SetUpTest(c)
269
270 ports := s.APIState.APIHostPorts()
271 ports[0][0].NetworkName = "dummy-provider-network"
272 err := s.State.SetAPIHostPorts(ports)
273 c.Assert(err, gc.IsNil)
274
275 s.machineStarted = make(map[string]bool)
276 s.PatchValue(&corecharm.CacheDir, c.MkDir())
277 password := "dummy-password"
278 if os.Getenv("ADMIN_PASSWORD") != "" {
279 password = os.Getenv("ADMIN_PASSWORD")
280 }
281 _, err = s.State.AddUser("admin", "Admin", password, "dummy-admin")
282 c.Assert(err, gc.IsNil)
283 _, err = s.State.AddEnvironmentUser(
284 names.NewLocalUserTag("admin"), names.NewLocalUserTag("dummy-admin"), "Admin")
285 c.Assert(err, gc.IsNil)
286
287 // Create a machine to manage the environment.
288 stateServer := s.Factory.MakeMachine(c, &factory.MachineParams{
289 InstanceId: s.newInstanceId(),
290 Nonce: agent.BootstrapNonce,
291 Jobs: []state.MachineJob{state.JobManageEnviron, state.JobHostUnits},
292 Series: "trusty",
293 })
294 c.Assert(stateServer.SetAgentVersion(version.Current), gc.IsNil)
295 address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal)
296 c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil)
297 c.Assert(stateServer.SetStatus(state.StatusStarted, "", nil), gc.IsNil)
298 _, err = stateServer.SetAgentPresence()
299 c.Assert(err, gc.IsNil)
300 s.State.StartSync()
301 err = stateServer.WaitAgentPresence(coretesting.LongWait)
302 c.Assert(err, gc.IsNil)
303
304 apiInfo := s.APIInfo(c)
305 //fmt.Println(apiInfo.Addrs[0])
306 jujuHome := osenv.JujuHome()
307 fmt.Println(apiInfo.EnvironTag.Id())
308 fmt.Println(jujuHome)
309
310 binPath := filepath.Join(jujuHome, "bin")
311 os.Mkdir(binPath, 0755)
312 fakeSSHData := []byte("#!/bin/sh\nsleep 1\n")
313 fakeSSHPath := filepath.Join(binPath, "ssh")
314 err = ioutil.WriteFile(fakeSSHPath, fakeSSHData, 0755)
315 c.Assert(err, gc.IsNil)
316 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
317
318 s.fifoPath = filepath.Join(jujuHome, "fifo")
319 syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)
320
321 // Logging
322 logPath := filepath.Join(jujuHome, "fake-juju.log")
323 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
324 c.Assert(err, gc.IsNil)
325
326 log.SetOutput(s.logFile)
327 dpkgCmd := exec.Command(
328 "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju")
329 out, err := CommandOutput(dpkgCmd)
330 fakeJujuDebVersion := strings.Trim(string(out), "'")
331 log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome)
332
333}
334
335func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
336 s.JujuConnSuite.TearDownTest(c)
337 s.logFile.Close()
338}
339
340func (s *FakeJujuSuite) TestStart(c *gc.C) {
341 watcher := s.State.Watch()
342 go func() {
343 fd, err := os.Open(s.fifoPath)
344 c.Assert(err, gc.IsNil)
345 scanner := bufio.NewScanner(fd)
346 scanner.Scan()
347 watcher.Stop()
348 }()
349 for {
350 deltas, err := watcher.Next()
351 log.Println("Got deltas")
352 if err != nil {
353 if err.Error() == "watcher was stopped" {
354 log.Println("Watcher stopped")
355 break
356 }
357 log.Println("Unexpected error", err.Error())
358 }
359 c.Assert(err, gc.IsNil)
360 for _, d := range deltas {
361
362 entity, err := json.MarshalIndent(d.Entity, "", " ")
363 c.Assert(err, gc.IsNil)
364 verb := "change"
365 if d.Removed {
366 verb = "remove"
367 }
368 log.Println("Processing delta", verb, d.Entity.EntityId().Kind, string(entity[:]))
369
370 entityId := d.Entity.EntityId()
371 if entityId.Kind == "machine" {
372 machineId, ok := entityId.Id.(string)
373 c.Assert(ok, gc.Equals, true)
374 c.Assert(s.handleAddMachine(machineId), gc.IsNil)
375 }
376 if entityId.Kind == "unit" {
377 unitId, ok := entityId.Id.(string)
378 c.Assert(ok, gc.Equals, true)
379 c.Assert(s.handleAddUnit(unitId), gc.IsNil)
380 }
381 log.Println("Done processing delta")
382 }
383 }
384}
385
386func (s *FakeJujuSuite) handleAddMachine(id string) error {
387 machine, err := s.State.Machine(id)
388 if err != nil {
389 return err
390 }
391 if instanceId, _ := machine.InstanceId(); instanceId == "" {
392 err = machine.SetProvisioned(s.newInstanceId(), agent.BootstrapNonce, nil)
393 if err != nil {
394 log.Println("Got error with SetProvisioned", err)
395 return err
396 }
397 address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal)
398 err = machine.SetProviderAddresses(address)
399 if err != nil {
400 log.Println("Got error with SetProviderAddresses", err)
401 return err
402 }
403 }
404 status, _ := machine.Status()
405 if status.Status == state.StatusPending {
406 if err = s.startMachine(machine); err != nil {
407 log.Println("Got error with startMachine:", err)
408 return err
409 }
410 } else if status.Status == state.StatusStarted {
411 if _, ok := s.machineStarted[id]; !ok {
412 s.machineStarted[id] = true
413 if err = s.startUnits(machine); err != nil {
414 log.Println("Got error with startUnits", err)
415 return err
416 }
417 }
418 }
419 return nil
420}
421
422func (s *FakeJujuSuite) handleAddUnit(id string) error {
423 unit, err := s.State.Unit(id)
424 if err != nil {
425 log.Println("Got error with get unit", err)
426 return err
427 }
428 machineId, err := unit.AssignedMachineId()
429 if err != nil {
430 return nil
431 }
432 log.Println("Got machineId", machineId)
433 machine, err := s.State.Machine(machineId)
434 if err != nil {
435 log.Println("Got error with unit AssignedMachineId", err)
436 return err
437 }
438 machineStatus, _ := machine.Status()
439 if machineStatus.Status != state.StatusStarted {
440 return nil
441 }
442 status, _ := unit.Status()
443 if status.Status != state.StatusActive {
444 if err = s.startUnit(unit); err != nil {
445 return err
446 }
447 }
448 return nil
449}
450
451func (s *FakeJujuSuite) startMachine(machine *state.Machine) error {
452 time.Sleep(500 * time.Millisecond)
453 err := machine.SetStatus(state.StatusStarted, "", nil)
454 if err != nil {
455 return err
456 }
457 err = machine.SetAgentVersion(version.Current)
458 if err != nil {
459 return err
460 }
461 _, err = machine.SetAgentPresence()
462 if err != nil {
463 return err
464 }
465 s.State.StartSync()
466 err = machine.WaitAgentPresence(coretesting.LongWait)
467 if err != nil {
468 return err
469 }
470 return nil
471}
472
473func (s *FakeJujuSuite) startUnits(machine *state.Machine) error {
474 units, err := machine.Units()
475 if err != nil {
476 return err
477 }
478 return nil
479 for _, unit := range units {
480 unitStatus, _ := unit.Status()
481 if unitStatus.Status != state.StatusActive {
482 if err = s.startUnit(unit); err != nil {
483 return err
484 }
485 }
486 }
487 return nil
488}
489
490func (s *FakeJujuSuite) startUnit(unit *state.Unit) error {
491 err := unit.SetStatus(state.StatusActive, "", nil)
492 if err != nil {
493 return err
494 }
495 _, err = unit.SetAgentPresence()
496 if err != nil {
497 return err
498 }
499 s.State.StartSync()
500 err = unit.WaitAgentPresence(coretesting.LongWait)
501 if err != nil {
502 return err
503 }
504 err = unit.SetAgentStatus(state.StatusIdle, "", nil)
505 if err != nil {
506 return err
507 }
508 return nil
509}
510
511func (s *FakeJujuSuite) newInstanceId() instance.Id {
512 s.instanceCount += 1
513 return instance.Id(fmt.Sprintf("id-%d", s.instanceCount))
514}
5150
=== modified file '1.25.6/fake-juju.go'
--- 1.25.6/fake-juju.go 2016-10-25 16:32:53 +0000
+++ 1.25.6/fake-juju.go 2016-10-29 14:08:30 +0000
@@ -31,11 +31,17 @@
31 coretesting "github.com/juju/juju/testing"31 coretesting "github.com/juju/juju/testing"
32 "github.com/juju/juju/testing/factory"32 "github.com/juju/juju/testing/factory"
33 "github.com/juju/juju/version"33 "github.com/juju/juju/version"
34 "github.com/juju/loggo"
34 "github.com/juju/names"35 "github.com/juju/names"
36 "github.com/juju/utils"
35 corecharm "gopkg.in/juju/charm.v5/charmrepo"37 corecharm "gopkg.in/juju/charm.v5/charmrepo"
36 goyaml "gopkg.in/yaml.v1"38 goyaml "gopkg.in/yaml.v1"
37)39)
3840
41const (
42 envDataDir = "FAKE_JUJU_DATA_DIR"
43)
44
39func main() {45func main() {
40 code := 046 code := 0
41 if len(os.Args) > 1 {47 if len(os.Args) > 1 {
@@ -69,7 +75,7 @@
69 return errors.New("command not found")75 return errors.New("command not found")
70}76}
7177
72func bootstrap(filenames fakejujuFilenames) error {78func bootstrap(filenames fakejujuFilenames) (returnedErr error) {
73 if err := filenames.ensureDirsExist(); err != nil {79 if err := filenames.ensureDirsExist(); err != nil {
74 return err80 return err
75 }81 }
@@ -84,14 +90,26 @@
84 command.Env = append(command.Env, "ADMIN_PASSWORD="+password)90 command.Env = append(command.Env, "ADMIN_PASSWORD="+password)
85 defaultSeries, _ := config.DefaultSeries()91 defaultSeries, _ := config.DefaultSeries()
86 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)92 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)
93 command.Env = append(command.Env, envDataDir+"="+filenames.datadir)
87 stdout, err := command.StdoutPipe()94 stdout, err := command.StdoutPipe()
88 if err != nil {95 if err != nil {
89 return err96 return err
90 }97 }
91 command.Start()98 command.Start()
9299
100 var whence string
101 defer func() {
102 if returnedErr != nil {
103 if err := destroyEnvironment(filenames); err != nil {
104 fmt.Printf("could not destroy environment when %s failed: %v\n", whence, err)
105 }
106 returnedErr = fmt.Errorf("bootstrap failed while %s: %v", whence, returnedErr)
107 }
108 }()
109
93 result, err := parseApiInfo(stdout)110 result, err := parseApiInfo(stdout)
94 if err != nil {111 if err != nil {
112 whence = "parsing bootstrap result"
95 return err113 return err
96 }114 }
97 // Get the API info before changing it. The new values might115 // Get the API info before changing it. The new values might
@@ -103,9 +121,11 @@
103 result.password = password121 result.password = password
104 }122 }
105 if err := result.apply(filenames, envName); err != nil {123 if err := result.apply(filenames, envName); err != nil {
124 whence = "setting up fake-juju files"
106 return err125 return err
107 }126 }
108127
128 whence = "waiting-for-ready"
109 dialOpts := api.DialOpts{129 dialOpts := api.DialOpts{
110 DialAddressInterval: 50 * time.Millisecond,130 DialAddressInterval: 50 * time.Millisecond,
111 Timeout: 5 * time.Second,131 Timeout: 5 * time.Second,
@@ -154,11 +174,6 @@
154}174}
155175
156func destroyEnvironment(filenames fakejujuFilenames) error {176func destroyEnvironment(filenames fakejujuFilenames) error {
157 info, err := readProcessInfo(filenames)
158 if err != nil {
159 return err
160 }
161 filenames = newFakeJujuFilenames("", "", info.WorkDir)
162 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)177 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)
163 if err != nil {178 if err != nil {
164 return err179 return err
@@ -228,7 +243,7 @@
228243
229func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {244func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
230 if datadir == "" {245 if datadir == "" {
231 datadir = os.Getenv("FAKE_JUJU_DATA_DIR")246 datadir = os.Getenv(envDataDir)
232 if datadir == "" {247 if datadir == "" {
233 if jujucfgdir == "" {248 if jujucfgdir == "" {
234 jujucfgdir = os.Getenv("JUJU_HOME")249 jujucfgdir = os.Getenv("JUJU_HOME")
@@ -267,6 +282,12 @@
267 return filepath.Join(fj.logsdir, "fake-juju.log")282 return filepath.Join(fj.logsdir, "fake-juju.log")
268}283}
269284
285// jujudLogsFile() returns the path to the file where fake-juju writes
286// the jujud logs.
287func (fj fakejujuFilenames) jujudLogsFile() string {
288 return filepath.Join(fj.logsdir, "jujud.log")
289}
290
270// fifoFile() returns the path to the FIFO file used by fake-juju.291// fifoFile() returns the path to the FIFO file used by fake-juju.
271// The FIFO is used by the fake-juju subcommands to interact with292// The FIFO is used by the fake-juju subcommands to interact with
272// the daemon.293// the daemon.
@@ -317,23 +338,6 @@
317 }338 }
318}339}
319340
320// logsSymlinkFilenames() determines the source and target paths for
321// a symlink to the fake-juju logs file. Such a symlink is relevant
322// because the fake-juju daemon may not know where the log file is
323// meant to go. It defaults to putting the log file in the default Juju
324// config dir. In that case, a symlink should be created from there to
325// the user-defined Juju config dir ($JUJU_HOME).
326func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) {
327 if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" || os.Getenv("FAKE_JUJU_DATA_DIR") != "" {
328 return "", ""
329 }
330
331 filenames := newFakeJujuFilenames("", "", br.cfgdir)
332 source = filenames.logsFile()
333 target = targetLogsFile
334 return source, target
335}
336
337// jenvSymlinkFilenames() determines the source and target paths for341// jenvSymlinkFilenames() determines the source and target paths for
338// a symlink to the .jenv file for the identified environment.342// a symlink to the .jenv file for the identified environment.
339func (br bootstrapResult) jenvSymlinkFilenames(jujuHome, envName string) (source, target string) {343func (br bootstrapResult) jenvSymlinkFilenames(jujuHome, envName string) (source, target string) {
@@ -353,13 +357,6 @@
353 return err357 return err
354 }358 }
355359
356 logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile())
357 if logsSource != "" && logsTarget != "" {
358 if err := os.Symlink(logsSource, logsTarget); err != nil {
359 return err
360 }
361 }
362
363 jenvSource, jenvTarget := br.jenvSymlinkFilenames(os.Getenv("JUJU_HOME"), envName)360 jenvSource, jenvTarget := br.jenvSymlinkFilenames(os.Getenv("JUJU_HOME"), envName)
364 if jenvSource != "" && jenvTarget != "" {361 if jenvSource != "" && jenvTarget != "" {
365 if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil {362 if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil {
@@ -388,6 +385,10 @@
388 return nil, err385 return nil, err
389 }386 }
390 uuid := string(line)387 uuid := string(line)
388 if !utils.IsValidUUIDString(uuid) {
389 data, _ := ioutil.ReadAll(stdout)
390 return nil, fmt.Errorf("%s\n%s", line, data)
391 }
391392
392 line, _, err = buffer.ReadLine()393 line, _, err = buffer.ReadLine()
393 if err != nil {394 if err != nil {
@@ -395,6 +396,12 @@
395 }396 }
396 workDir := string(line)397 workDir := string(line)
397398
399 result := &bootstrapResult{
400 dummyEnvName: dummyEnvName,
401 cfgdir: workDir,
402 uuid: uuid,
403 }
404
398 store, err := configstore.NewDisk(workDir)405 store, err := configstore.NewDisk(workDir)
399 if err != nil {406 if err != nil {
400 return nil, err407 return nil, err
@@ -403,18 +410,13 @@
403 if err != nil {410 if err != nil {
404 return nil, err411 return nil, err
405 }412 }
406
407 credentials := info.APICredentials()413 credentials := info.APICredentials()
408 endpoint := info.APIEndpoint()414 endpoint := info.APIEndpoint()
409 result := &bootstrapResult{415 result.username = credentials.User
410 dummyEnvName: dummyEnvName,416 result.password = credentials.Password
411 cfgdir: workDir,417 result.addresses = endpoint.Addresses
412 uuid: uuid,418 result.caCert = []byte(endpoint.CACert)
413 username: credentials.User,419
414 password: credentials.Password,
415 addresses: endpoint.Addresses,
416 caCert: []byte(endpoint.CACert),
417 }
418 return result, nil420 return result, nil
419}421}
420422
@@ -462,10 +464,10 @@
462type FakeJujuSuite struct {464type FakeJujuSuite struct {
463 jujutesting.JujuConnSuite465 jujutesting.JujuConnSuite
464466
465 instanceCount int467 instanceCount int
466 machineStarted map[string]bool468 machineStarted map[string]bool
467 filenames fakejujuFilenames469 filenames fakejujuFilenames
468 logFile *os.File470 toCloseOnTearDown []io.Closer
469}471}
470472
471var _ = gc.Suite(&FakeJujuSuite{})473var _ = gc.Suite(&FakeJujuSuite{})
@@ -474,6 +476,16 @@
474 var CommandOutput = (*exec.Cmd).CombinedOutput476 var CommandOutput = (*exec.Cmd).CombinedOutput
475 s.JujuConnSuite.SetUpTest(c)477 s.JujuConnSuite.SetUpTest(c)
476478
479 c.Assert(os.Getenv(envDataDir), gc.Not(gc.Equals), "")
480 s.filenames = newFakeJujuFilenames("", "", "")
481 // Note that LoggingSuite.SetUpTest (github.com/juju/testing/log.go),
482 // called via s.JujuConnSuite.SetUpTest(), calls loggo.ResetLogging().
483 // So we cannot set up logging before then, since any writer we
484 // register will get removed. Consequently we lose any logs that get
485 // generated in the SetUpTest() call.
486 logFile, jujudLogFile := setUpLogging(c, s.filenames)
487 s.toCloseOnTearDown = append(s.toCloseOnTearDown, logFile, jujudLogFile)
488
477 ports := s.APIState.APIHostPorts()489 ports := s.APIState.APIHostPorts()
478 ports[0][0].NetworkName = "dummy-provider-network"490 ports[0][0].NetworkName = "dummy-provider-network"
479 err := s.State.SetAPIHostPorts(ports)491 err := s.State.SetAPIHostPorts(ports)
@@ -517,10 +529,6 @@
517529
518 apiInfo := s.APIInfo(c)530 apiInfo := s.APIInfo(c)
519 jujuHome := osenv.JujuHome()531 jujuHome := osenv.JujuHome()
520 // IMPORTANT: don't remove this logging because it's used by the
521 // bootstrap command.
522 fmt.Println(apiInfo.EnvironTag.Id())
523 fmt.Println(jujuHome)
524532
525 binPath := filepath.Join(jujuHome, "bin")533 binPath := filepath.Join(jujuHome, "bin")
526 os.Mkdir(binPath, 0755)534 os.Mkdir(binPath, 0755)
@@ -530,27 +538,55 @@
530 c.Assert(err, gc.IsNil)538 c.Assert(err, gc.IsNil)
531 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))539 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
532540
533 s.filenames = newFakeJujuFilenames("", "", jujuHome)541 // Once this FIFO is created, users can start sending commands.
542 // The actual handling doesn't start until TestStart() runs.
534 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)543 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)
535544
536 // Logging545 // Send the info back to the bootstrap command.
537 logPath := s.filenames.logsFile()546 reportInfo(apiInfo.EnvironTag.Id(), jujuHome)
538 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
539 c.Assert(err, gc.IsNil)
540547
541 dpkgCmd := exec.Command(548 dpkgCmd := exec.Command(
542 "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju")549 "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju")
543 out, err := CommandOutput(dpkgCmd)550 out, err := CommandOutput(dpkgCmd)
544 log.SetOutput(s.logFile)
545 fakeJujuDebVersion := strings.Trim(string(out), "'")551 fakeJujuDebVersion := strings.Trim(string(out), "'")
546 log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome)552 log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome)
547}553}
548554
555func setUpLogging(c *gc.C, filenames fakejujuFilenames) (*os.File, *os.File) {
556 c.Assert(filenames.logsdir, gc.Not(gc.Equals), "")
557
558 // fake-juju logging
559 logPath := filenames.logsFile()
560 logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
561 c.Assert(err, gc.IsNil)
562 log.SetOutput(logFile)
563
564 // jujud logging
565 logPath = filenames.jujudLogsFile()
566 jujudLogFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
567 c.Assert(err, gc.IsNil)
568 err = loggo.RegisterWriter("fake-juju-jujud-logs", loggo.NewSimpleWriter(jujudLogFile, &loggo.DefaultFormatter{}), loggo.TRACE)
569 c.Assert(err, gc.IsNil)
570 logger := loggo.GetLogger("fake-juju")
571 logger.Infof("--- starting logging ---")
572
573 return logFile, jujudLogFile
574}
575
576func reportInfo(uuid, jujuCfgDir string) {
577 // IMPORTANT: don't remove this logging because it's used by the
578 // bootstrap command.
579 fmt.Println(uuid)
580 fmt.Println(jujuCfgDir)
581}
582
549func (s *FakeJujuSuite) TearDownTest(c *gc.C) {583func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
550 log.Println("Tearing down processes")584 log.Println("Tearing down processes")
551 s.JujuConnSuite.TearDownTest(c)585 s.JujuConnSuite.TearDownTest(c)
552 log.Println("Closing log file")586 log.Println("Closing log files")
553 s.logFile.Close()587 for _, closer := range s.toCloseOnTearDown {
588 closer.Close()
589 }
554}590}
555591
556func (s *FakeJujuSuite) TestStart(c *gc.C) {592func (s *FakeJujuSuite) TestStart(c *gc.C) {
557593
=== renamed directory '2.0-beta17' => '2.0.0'
=== modified file '2.0.0/fake-juju.go'
--- 2.0-beta17/fake-juju.go 2016-10-25 16:32:53 +0000
+++ 2.0.0/fake-juju.go 2016-10-29 14:08:30 +0000
@@ -4,6 +4,7 @@
4 "bufio"4 "bufio"
5 "encoding/json"5 "encoding/json"
6 "errors"6 "errors"
7 "flag"
7 "fmt"8 "fmt"
8 gc "gopkg.in/check.v1"9 gc "gopkg.in/check.v1"
9 "io"10 "io"
@@ -19,6 +20,7 @@
1920
20 "github.com/juju/juju/agent"21 "github.com/juju/juju/agent"
21 "github.com/juju/juju/api"22 "github.com/juju/juju/api"
23 jujucmdcommon "github.com/juju/juju/cmd/juju/common"
22 "github.com/juju/juju/cmd/juju/controller"24 "github.com/juju/juju/cmd/juju/controller"
23 "github.com/juju/juju/instance"25 "github.com/juju/juju/instance"
24 "github.com/juju/juju/juju/osenv"26 "github.com/juju/juju/juju/osenv"
@@ -31,12 +33,20 @@
31 coretesting "github.com/juju/juju/testing"33 coretesting "github.com/juju/juju/testing"
32 "github.com/juju/juju/testing/factory"34 "github.com/juju/juju/testing/factory"
33 "github.com/juju/juju/version"35 "github.com/juju/juju/version"
36 "github.com/juju/loggo"
37 "github.com/juju/utils"
34 semversion "github.com/juju/version"38 semversion "github.com/juju/version"
35 corecharm "gopkg.in/juju/charmrepo.v2-unstable"39 corecharm "gopkg.in/juju/charmrepo.v2-unstable"
36 "gopkg.in/juju/names.v2"40 "gopkg.in/juju/names.v2"
37 goyaml "gopkg.in/yaml.v1"41 goyaml "gopkg.in/yaml.v1"
38)42)
3943
44const (
45 envDataDir = "FAKE_JUJU_DATA_DIR"
46 envLogsDir = "FAKE_JUJU_LOGS_DIR"
47 envFailuresFile = "FAKE_JUJU_FAILURES"
48)
49
40func main() {50func main() {
41 code := 051 code := 0
42 if len(os.Args) > 1 {52 if len(os.Args) > 1 {
@@ -56,49 +66,150 @@
56func handleCommand(command string) error {66func handleCommand(command string) error {
57 filenames := newFakeJujuFilenames("", "", "")67 filenames := newFakeJujuFilenames("", "", "")
58 if command == "bootstrap" {68 if command == "bootstrap" {
59 return bootstrap(filenames)69 return handleBootstrap(filenames)
60 }70 }
61 if command == "show-controller" {71 if command == "show-controller" {
62 return apiInfo(filenames)72 return handleAPIInfo(filenames)
63 }73 }
64 if command == "destroy-controller" {74 if command == "destroy-controller" {
65 return destroyController(filenames)75 return handleDestroyController(filenames)
66 }76 }
67 return errors.New("command not found")77 return errors.New("command not found")
68}78}
6979
70func bootstrap(filenames fakejujuFilenames) error {80func handleBootstrap(filenames fakejujuFilenames) (returnedErr error) {
71 argc := len(os.Args)81 var args bootstrapArgs
72 if argc < 4 {82 if err := args.parse(); err != nil {
73 return errors.New(83 return err
74 "error: controller name and cloud name are required")
75 }84 }
76 if err := filenames.ensureDirsExist(); err != nil {85 if err := filenames.ensureDirsExist(); err != nil {
77 return err86 return err
78 }87 }
79 // XXX Swap the 2 args for juju-2.0-final.88
80 controllerName := os.Args[argc-2]89 // Start the fake-juju daemon.
81 command := exec.Command(os.Args[0])90 command := exec.Command(os.Args[0])
82 command.Env = os.Environ()91 command.Env = os.Environ()
83 command.Env = append(92 command.Env = append(
84 command.Env, "ADMIN_PASSWORD="+"pwd")93 command.Env, "ADMIN_PASSWORD="+"pwd")
85 defaultSeries := "trusty"94 defaultSeries := "trusty"
86 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)95 command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries)
96 command.Env = append(command.Env, envDataDir+"="+filenames.datadir)
87 stdout, err := command.StdoutPipe()97 stdout, err := command.StdoutPipe()
88 if err != nil {98 if err != nil {
89 return err99 return err
90 }100 }
91 command.Start()101 command.Start()
92102
103 var whence string
104 defer func() {
105 if returnedErr != nil {
106 if err := destroyController(filenames); err != nil {
107 fmt.Printf("could not destroy controller when %s failed: %v\n", whence, err)
108 }
109 returnedErr = fmt.Errorf("bootstrap failed while %s: %v", whence, returnedErr)
110 }
111 }()
112
113 // Get the internal info from the daemon and store it.
93 result, err := parseApiInfo(stdout)114 result, err := parseApiInfo(stdout)
94 if err != nil {115 if err != nil {
95 return err116 whence = "parsing bootstrap result"
96 }117 return err
97 if err := result.apply(filenames, controllerName); err != nil {118 }
98 return err119 if err := result.copyConfig(os.Getenv("JUJU_DATA"), args.controllerName); err != nil {
99 }120 whence = "copying config"
121 return err
122 }
123 if err := updateBootstrapResult(result); err != nil {
124 whence = "updating bootstrap result"
125 return err
126 }
127 if err := result.apply(filenames); err != nil {
128 whence = "setting up fake-juju files"
129 return err
130 }
131
132 // Wait for the daemon to finish starting up.
133 if err := waitForBootstrapCompletion(result); err != nil {
134 whence = "waiting-for-ready"
135 return err
136 }
137
138 return nil
139}
140
141// bootstrapArgs is an adaptation of bootstrapCommand from
142// github.com/juju/juju/cmd/juju/commands/bootstrap.go.
143type bootstrapArgs struct {
144 config jujucmdcommon.ConfigFlag
145 hostedModelName string
146 credentialName string
147
148 controllerName string
149 cloud string
150 region string
151}
152
153func (bsargs *bootstrapArgs) parse() error {
154 flags := flag.NewFlagSet("bootstrap", flag.ExitOnError)
155
156 var ignoredStr string
157 flags.StringVar(&ignoredStr, "constraints", "", "")
158 flags.StringVar(&ignoredStr, "bootstrap-constraints", "", "")
159 flags.StringVar(&ignoredStr, "bootstrap-series", "", "")
160 flags.StringVar(&ignoredStr, "bootstrap-image", "", "")
161 flags.StringVar(&ignoredStr, "metadata-source", "", "")
162 flags.StringVar(&ignoredStr, "to", "", "")
163 flags.StringVar(&ignoredStr, "agent-version", "", "")
164 flags.StringVar(&ignoredStr, "regions", "", "")
165
166 var ignoredBool bool
167 flags.BoolVar(&ignoredBool, "v", false, "")
168 flags.BoolVar(&ignoredBool, "build-agent", false, "")
169 flags.BoolVar(&ignoredBool, "keep-broken", false, "")
170 flags.BoolVar(&ignoredBool, "auto-upgrade", false, "")
171 flags.BoolVar(&ignoredBool, "no-gui", false, "")
172 flags.BoolVar(&ignoredBool, "clouds", false, "")
173
174 flags.Var(&bsargs.config, "config", "")
175 flags.StringVar(&bsargs.credentialName, "credential", "", "")
176 flags.StringVar(&bsargs.hostedModelName, "d", "", "")
177 flags.StringVar(&bsargs.hostedModelName, "default-model", "", "")
178
179 flags.Parse(os.Args[2:])
180
181 args := flags.Args()
182 switch len(args) {
183 case 0:
184 return fmt.Errorf("expected at least one positional arg, got none")
185 case 1:
186 bsargs.cloud = args[0]
187 case 2:
188 bsargs.cloud = args[0]
189 bsargs.controllerName = args[1]
190 default:
191 return fmt.Errorf("expected at most two positional args, got %v", args)
192 }
193
194 if i := strings.IndexRune(bsargs.cloud, '/'); i > 0 {
195 bsargs.cloud, bsargs.region = bsargs.cloud[:i], bsargs.cloud[i+1:]
196 }
197 if bsargs.controllerName == "" {
198 bsargs.controllerName = bsargs.cloud
199 if bsargs.region == "" {
200 bsargs.controllerName += "-" + bsargs.region
201 }
202 }
203
204 if bsargs.hostedModelName == "" {
205 bsargs.hostedModelName = "default"
206 }
207
208 return nil
209}
210
211func waitForBootstrapCompletion(result *bootstrapResult) error {
100 apiInfo := result.apiInfo()212 apiInfo := result.apiInfo()
101
102 dialOpts := api.DialOpts{213 dialOpts := api.DialOpts{
103 DialAddressInterval: 50 * time.Millisecond,214 DialAddressInterval: 50 * time.Millisecond,
104 Timeout: 5 * time.Second,215 Timeout: 5 * time.Second,
@@ -128,7 +239,7 @@
128 return errors.New("invalid delta")239 return errors.New("invalid delta")
129}240}
130241
131func apiInfo(filenames fakejujuFilenames) error {242func handleAPIInfo(filenames fakejujuFilenames) error {
132 info, err := readProcessInfo(filenames)243 info, err := readProcessInfo(filenames)
133 if err != nil {244 if err != nil {
134 return err245 return err
@@ -137,21 +248,22 @@
137 jujuHome := os.Getenv("JUJU_DATA")248 jujuHome := os.Getenv("JUJU_DATA")
138 osenv.SetJujuXDGDataHome(jujuHome)249 osenv.SetJujuXDGDataHome(jujuHome)
139 cmd := controller.NewShowControllerCommand()250 cmd := controller.NewShowControllerCommand()
140 ctx, err := coretesting.RunCommandInDir(251 if err := coretesting.InitCommand(cmd, os.Args[2:]); err != nil {
141 nil, cmd, os.Args[2:], info.WorkDir)252 return err
142 if err != nil {253 }
254 ctx := coretesting.ContextForDir(nil, info.WorkDir)
255 if err := cmd.Run(ctx); err != nil {
143 return err256 return err
144 }257 }
145 fmt.Print(ctx.Stdout)258 fmt.Print(ctx.Stdout)
146 return nil259 return nil
147}260}
148261
262func handleDestroyController(filenames fakejujuFilenames) error {
263 return destroyController(filenames)
264}
265
149func destroyController(filenames fakejujuFilenames) error {266func destroyController(filenames fakejujuFilenames) error {
150 info, err := readProcessInfo(filenames)
151 if err != nil {
152 return err
153 }
154 filenames = newFakeJujuFilenames("", "", info.WorkDir)
155 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)267 fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600)
156 if err != nil {268 if err != nil {
157 return err269 return err
@@ -203,7 +315,7 @@
203315
204func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {316func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
205 if datadir == "" {317 if datadir == "" {
206 datadir = os.Getenv("FAKE_JUJU_DATA_DIR")318 datadir = os.Getenv(envDataDir)
207 if datadir == "" {319 if datadir == "" {
208 if jujucfgdir == "" {320 if jujucfgdir == "" {
209 jujucfgdir = os.Getenv("JUJU_DATA")321 jujucfgdir = os.Getenv("JUJU_DATA")
@@ -212,7 +324,7 @@
212 }324 }
213 }325 }
214 if logsdir == "" {326 if logsdir == "" {
215 logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")327 logsdir = os.Getenv(envLogsDir)
216 if logsdir == "" {328 if logsdir == "" {
217 logsdir = datadir329 logsdir = datadir
218 }330 }
@@ -242,6 +354,12 @@
242 return filepath.Join(fj.logsdir, "fake-juju.log")354 return filepath.Join(fj.logsdir, "fake-juju.log")
243}355}
244356
357// jujudLogsFile() returns the path to the file where fake-juju writes
358// the jujud logs.
359func (fj fakejujuFilenames) jujudLogsFile() string {
360 return filepath.Join(fj.logsdir, "jujud.log")
361}
362
245// fifoFile() returns the path to the FIFO file used by fake-juju.363// fifoFile() returns the path to the FIFO file used by fake-juju.
246// The FIFO is used by the fake-juju subcommands to interact with364// The FIFO is used by the fake-juju subcommands to interact with
247// the daemon.365// the daemon.
@@ -290,41 +408,13 @@
290 }408 }
291}409}
292410
293// logsSymlinkFilenames() determines the source and target paths for
294// a symlink to the fake-juju logs file. Such a symlink is relevant
295// because the fake-juju daemon may not know where the log file is
296// meant to go. It defaults to putting the log file in the default Juju
297// config dir. In that case, a symlink should be created from there to
298// the user-defined Juju config dir ($JUJU_DATA).
299func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) {
300 if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" {
301 return "", ""
302 }
303
304 filenames := newFakeJujuFilenames("", "", br.cfgdir)
305 source = filenames.logsFile()
306 target = targetLogsFile
307 return source, target
308}
309
310// apply() writes out the information from the bootstrap result to the411// apply() writes out the information from the bootstrap result to the
311// various files identified by the provided filenames.412// various files identified by the provided filenames.
312func (br bootstrapResult) apply(filenames fakejujuFilenames, controllerName string) error {413func (br bootstrapResult) apply(filenames fakejujuFilenames) error {
313 if err := br.fakeJujuInfo().write(filenames.infoFile()); err != nil {414 if err := br.fakeJujuInfo().write(filenames.infoFile()); err != nil {
314 return err415 return err
315 }416 }
316417
317 logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile())
318 if logsSource != "" && logsTarget != "" {
319 if err := os.Symlink(logsSource, logsTarget); err != nil {
320 return err
321 }
322 }
323
324 if err := br.copyConfig(os.Getenv("JUJU_DATA"), controllerName); err != nil {
325 return err
326 }
327
328 if err := ioutil.WriteFile(filenames.caCertFile(), br.caCert, 0644); err != nil {418 if err := ioutil.WriteFile(filenames.caCertFile(), br.caCert, 0644); err != nil {
329 return err419 return err
330 }420 }
@@ -333,6 +423,9 @@
333}423}
334424
335func (br bootstrapResult) copyConfig(targetCfgDir, controllerName string) error {425func (br bootstrapResult) copyConfig(targetCfgDir, controllerName string) error {
426 if err := os.MkdirAll(targetCfgDir, 0755); err != nil {
427 return err
428 }
336 for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} {429 for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} {
337 source := filepath.Join(br.cfgdir, name)430 source := filepath.Join(br.cfgdir, name)
338 target := filepath.Join(targetCfgDir, name)431 target := filepath.Join(targetCfgDir, name)
@@ -360,8 +453,7 @@
360 return nil453 return nil
361}454}
362455
363// See github.com/juju/juju/blob/juju/testing/conn.go.456const dummyControllerName = jujutesting.ControllerName
364const dummyControllerName = "kontroll"
365457
366func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {458func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
367 buffer := bufio.NewReader(stdout)459 buffer := bufio.NewReader(stdout)
@@ -371,6 +463,10 @@
371 return nil, err463 return nil, err
372 }464 }
373 uuid := string(line)465 uuid := string(line)
466 if !utils.IsValidUUIDString(uuid) {
467 data, _ := ioutil.ReadAll(stdout)
468 return nil, fmt.Errorf("%s\n%s", line, data)
469 }
374470
375 line, _, err = buffer.ReadLine()471 line, _, err = buffer.ReadLine()
376 if err != nil {472 if err != nil {
@@ -378,31 +474,37 @@
378 }474 }
379 workDir := string(line)475 workDir := string(line)
380476
381 osenv.SetJujuXDGDataHome(workDir)477 result := &bootstrapResult{
478 dummyControllerName: dummyControllerName,
479 cfgdir: workDir,
480 uuid: uuid,
481 }
482 return result, nil
483}
484
485func updateBootstrapResult(result *bootstrapResult) error {
486 osenv.SetJujuXDGDataHome(result.cfgdir)
382 store := jujuclient.NewFileClientStore()487 store := jujuclient.NewFileClientStore()
488
383 // hard-coded value in juju testing489 // hard-coded value in juju testing
384 // This will be replaced in JUJU_DATA copy of the juju client config.490 // This will be replaced in JUJU_DATA copy of the juju client config.
385 currentController := dummyControllerName491 currentController := result.dummyControllerName
492
386 one, err := store.ControllerByName(currentController)493 one, err := store.ControllerByName(currentController)
387 if err != nil {494 if err != nil {
388 return nil, err495 return err
389 }496 }
497 result.addresses = one.APIEndpoints
498 result.caCert = []byte(one.CACert)
390499
391 accountDetails, err := store.AccountDetails(currentController)500 accountDetails, err := store.AccountDetails(currentController)
392 if err != nil {501 if err != nil {
393 return nil, err502 return err
394 }503 }
504 result.username = accountDetails.User
505 result.password = accountDetails.Password
395506
396 result := &bootstrapResult{507 return nil
397 dummyControllerName: dummyControllerName,
398 cfgdir: workDir,
399 uuid: uuid,
400 username: accountDetails.User,
401 password: accountDetails.Password,
402 addresses: one.APIEndpoints,
403 caCert: []byte(one.CACert),
404 }
405 return result, nil
406}508}
407509
408// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment510// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
@@ -411,9 +513,9 @@
411// entity transition to an error state.513// entity transition to an error state.
412func readFailuresInfo() (map[string]bool, error) {514func readFailuresInfo() (map[string]bool, error) {
413 log.Println("Checking for forced failures")515 log.Println("Checking for forced failures")
414 failuresPath := os.Getenv("FAKE_JUJU_FAILURES")516 failuresPath := os.Getenv(envFailuresFile)
415 if failuresPath == "" {517 if failuresPath == "" {
416 log.Println("No FAKE_JUJU_FAILURES env variable set")518 log.Printf("No %s env variable set\n", envFailuresFile)
417 }519 }
418 log.Println("Reading failures file", failuresPath)520 log.Println("Reading failures file", failuresPath)
419 failuresInfo := map[string]bool{}521 failuresInfo := map[string]bool{}
@@ -449,10 +551,10 @@
449type FakeJujuSuite struct {551type FakeJujuSuite struct {
450 jujutesting.JujuConnSuite552 jujutesting.JujuConnSuite
451553
452 instanceCount int554 instanceCount int
453 machineStarted map[string]bool555 machineStarted map[string]bool
454 filenames fakejujuFilenames556 filenames fakejujuFilenames
455 logFile *os.File557 toCloseOnTearDown []io.Closer
456}558}
457559
458var _ = gc.Suite(&FakeJujuSuite{})560var _ = gc.Suite(&FakeJujuSuite{})
@@ -460,6 +562,16 @@
460func (s *FakeJujuSuite) SetUpTest(c *gc.C) {562func (s *FakeJujuSuite) SetUpTest(c *gc.C) {
461 s.JujuConnSuite.SetUpTest(c)563 s.JujuConnSuite.SetUpTest(c)
462564
565 c.Assert(os.Getenv(envDataDir), gc.Not(gc.Equals), "")
566 s.filenames = newFakeJujuFilenames("", "", "")
567 // Note that LoggingSuite.SetUpTest (github.com/juju/testing/log.go),
568 // called via s.JujuConnSuite.SetUpTest(), calls loggo.ResetLogging().
569 // So we cannot set up logging before then, since any writer we
570 // register will get removed. Consequently we lose any logs that get
571 // generated in the SetUpTest() call.
572 logFile, jujudLogFile := setUpLogging(c, s.filenames)
573 s.toCloseOnTearDown = append(s.toCloseOnTearDown, logFile, jujudLogFile)
574
463 ports := s.APIState.APIHostPorts()575 ports := s.APIState.APIHostPorts()
464 err := s.State.SetAPIHostPorts(ports)576 err := s.State.SetAPIHostPorts(ports)
465 c.Assert(err, gc.IsNil)577 c.Assert(err, gc.IsNil)
@@ -491,7 +603,7 @@
491 c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil)603 c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil)
492 now := time.Now()604 now := time.Now()
493 sInfo := states.StatusInfo{605 sInfo := states.StatusInfo{
494 Status: states.StatusStarted,606 Status: states.Started,
495 Message: "",607 Message: "",
496 Since: &now,608 Since: &now,
497 }609 }
@@ -505,10 +617,6 @@
505 apiInfo := s.APIInfo(c)617 apiInfo := s.APIInfo(c)
506 //fmt.Println(apiInfo.Addrs[0])618 //fmt.Println(apiInfo.Addrs[0])
507 jujuHome := osenv.JujuXDGDataHome()619 jujuHome := osenv.JujuXDGDataHome()
508 // IMPORTANT: don't remove this logging because it's used by the
509 // bootstrap command.
510 fmt.Println(apiInfo.ModelTag.Id())
511 fmt.Println(jujuHome)
512620
513 binPath := filepath.Join(jujuHome, "bin")621 binPath := filepath.Join(jujuHome, "bin")
514 os.Mkdir(binPath, 0755)622 os.Mkdir(binPath, 0755)
@@ -518,24 +626,51 @@
518 c.Assert(err, gc.IsNil)626 c.Assert(err, gc.IsNil)
519 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))627 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
520628
521 s.filenames = newFakeJujuFilenames("", "", jujuHome)629 // Once this FIFO is created, users can start sending commands.
630 // The actual handling doesn't start until TestStart() runs.
522 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)631 syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0)
523632
524 // Logging633 // Send the info back to the bootstrap command.
525 logPath := s.filenames.logsFile()634 reportInfo(apiInfo.ModelTag.Id(), jujuHome)
526 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
527 c.Assert(err, gc.IsNil)
528635
529 log.SetOutput(s.logFile)
530 log.Println("Started fake-juju at ", jujuHome)636 log.Println("Started fake-juju at ", jujuHome)
531637}
638
639func setUpLogging(c *gc.C, filenames fakejujuFilenames) (*os.File, *os.File) {
640 c.Assert(filenames.logsdir, gc.Not(gc.Equals), "")
641
642 // fake-juju logging
643 logPath := filenames.logsFile()
644 logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
645 c.Assert(err, gc.IsNil)
646 log.SetOutput(logFile)
647
648 // jujud logging
649 logPath = filenames.jujudLogsFile()
650 jujudLogFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
651 c.Assert(err, gc.IsNil)
652 err = loggo.RegisterWriter("fake-juju-jujud-logs", loggo.NewSimpleWriter(jujudLogFile, nil))
653 c.Assert(err, gc.IsNil)
654 logger := loggo.GetLogger("fake-juju")
655 logger.Infof("--- starting logging ---")
656
657 return logFile, jujudLogFile
658}
659
660func reportInfo(uuid, jujuCfgDir string) {
661 // IMPORTANT: don't remove this logging because it's used by the
662 // bootstrap command.
663 fmt.Println(uuid)
664 fmt.Println(jujuCfgDir)
532}665}
533666
534func (s *FakeJujuSuite) TearDownTest(c *gc.C) {667func (s *FakeJujuSuite) TearDownTest(c *gc.C) {
535 log.Println("Tearing down processes")668 log.Println("Tearing down processes")
536 s.JujuConnSuite.TearDownTest(c)669 s.JujuConnSuite.TearDownTest(c)
537 log.Println("Closing log file")670 log.Println("Closing log files")
538 s.logFile.Close()671 for _, closer := range s.toCloseOnTearDown {
672 closer.Close()
673 }
539}674}
540675
541func (s *FakeJujuSuite) TestStart(c *gc.C) {676func (s *FakeJujuSuite) TestStart(c *gc.C) {
@@ -625,12 +760,12 @@
625 }760 }
626 status, _ := machine.Status()761 status, _ := machine.Status()
627 log.Println("Machine has status:", string(status.Status), status.Message)762 log.Println("Machine has status:", string(status.Status), status.Message)
628 if status.Status == states.StatusPending {763 if status.Status == states.Pending {
629 if err = s.startMachine(machine); err != nil {764 if err = s.startMachine(machine); err != nil {
630 log.Println("Got error with startMachine:", err)765 log.Println("Got error with startMachine:", err)
631 return err766 return err
632 }767 }
633 } else if status.Status == states.StatusStarted {768 } else if status.Status == states.Started {
634 log.Println("Starting units on machine", id)769 log.Println("Starting units on machine", id)
635 if _, ok := s.machineStarted[id]; !ok {770 if _, ok := s.machineStarted[id]; !ok {
636 s.machineStarted[id] = true771 s.machineStarted[id] = true
@@ -661,19 +796,19 @@
661 return err796 return err
662 }797 }
663 machineStatus, _ := machine.Status()798 machineStatus, _ := machine.Status()
664 if machineStatus.Status != states.StatusStarted {799 if machineStatus.Status != states.Started {
665 return nil800 return nil
666 }801 }
667 status, _ := unit.Status()802 status, _ := unit.Status()
668 log.Println("Unit has status", string(status.Status), status.Message)803 log.Println("Unit has status", string(status.Status), status.Message)
669 if status.Status != states.StatusActive && status.Status != states.StatusError {804 if status.Status != states.Active && status.Status != states.Error {
670 log.Println("Start unit", id)805 log.Println("Start unit", id)
671 err = s.startUnit(unit)806 err = s.startUnit(unit)
672 if err != nil {807 if err != nil {
673 log.Println("Got error changing unit status", id, err)808 log.Println("Got error changing unit status", id, err)
674 return err809 return err
675 }810 }
676 } else if status.Status != states.StatusError {811 } else if status.Status != states.Error {
677 failuresInfo, err := readFailuresInfo()812 failuresInfo, err := readFailuresInfo()
678 if err != nil {813 if err != nil {
679 return err814 return err
@@ -684,7 +819,7 @@
684 log.Println("Got error checking agent status", id, err)819 log.Println("Got error checking agent status", id, err)
685 return err820 return err
686 }821 }
687 if agentStatus.Status != states.StatusError {822 if agentStatus.Status != states.Error {
688 log.Println("Error unit", id)823 log.Println("Error unit", id)
689 err = s.errorUnit(unit)824 err = s.errorUnit(unit)
690 if err != nil {825 if err != nil {
@@ -701,7 +836,7 @@
701 time.Sleep(500 * time.Millisecond)836 time.Sleep(500 * time.Millisecond)
702 now := time.Now()837 now := time.Now()
703 sInfo := states.StatusInfo{838 sInfo := states.StatusInfo{
704 Status: states.StatusStarted,839 Status: states.Started,
705 Message: "",840 Message: "",
706 Since: &now,841 Since: &now,
707 }842 }
@@ -734,7 +869,7 @@
734 time.Sleep(500 * time.Millisecond)869 time.Sleep(500 * time.Millisecond)
735 now := time.Now()870 now := time.Now()
736 sInfo := states.StatusInfo{871 sInfo := states.StatusInfo{
737 Status: states.StatusError,872 Status: states.Error,
738 Message: "machine errored",873 Message: "machine errored",
739 Since: &now,874 Since: &now,
740 }875 }
@@ -753,7 +888,7 @@
753 return nil888 return nil
754 for _, unit := range units {889 for _, unit := range units {
755 unitStatus, _ := unit.Status()890 unitStatus, _ := unit.Status()
756 if unitStatus.Status != states.StatusActive {891 if unitStatus.Status != states.Active {
757 if err = s.startUnit(unit); err != nil {892 if err = s.startUnit(unit); err != nil {
758 return err893 return err
759 }894 }
@@ -765,7 +900,7 @@
765func (s *FakeJujuSuite) startUnit(unit *state.Unit) error {900func (s *FakeJujuSuite) startUnit(unit *state.Unit) error {
766 now := time.Now()901 now := time.Now()
767 sInfo := states.StatusInfo{902 sInfo := states.StatusInfo{
768 Status: states.StatusStarted,903 Status: states.Started,
769 Message: "",904 Message: "",
770 Since: &now,905 Since: &now,
771 }906 }
@@ -783,7 +918,7 @@
783 return err918 return err
784 }919 }
785 idleInfo := states.StatusInfo{920 idleInfo := states.StatusInfo{
786 Status: states.StatusIdle,921 Status: states.Idle,
787 Message: "",922 Message: "",
788 Since: &now,923 Since: &now,
789 }924 }
@@ -798,7 +933,7 @@
798 log.Println("Erroring unit", unit.Name())933 log.Println("Erroring unit", unit.Name())
799 now := time.Now()934 now := time.Now()
800 sInfo := states.StatusInfo{935 sInfo := states.StatusInfo{
801 Status: states.StatusIdle,936 Status: states.Idle,
802 Message: "unit errored",937 Message: "unit errored",
803 Since: &now,938 Since: &now,
804 }939 }
805940
=== modified file 'Makefile'
--- Makefile 2016-10-27 16:36:28 +0000
+++ Makefile 2016-10-29 14:08:30 +0000
@@ -63,8 +63,8 @@
63# for all versions63# for all versions
6464
65ifndef JUJU_VERSIONS65ifndef JUJU_VERSIONS
66JUJU1_VERSIONS = 1.24.7 1.25.666JUJU1_VERSIONS = 1.25.6
67JUJU2_VERSIONS = 2.0-beta1767JUJU2_VERSIONS = 2.0.0
68JUJU_VERSIONS = $(JUJU1_VERSIONS) $(JUJU2_VERSIONS)68JUJU_VERSIONS = $(JUJU1_VERSIONS) $(JUJU2_VERSIONS)
69endif69endif
70BUILT_VERSIONS = $(foreach version,$(JUJU_VERSIONS),$(version)/$(version))70BUILT_VERSIONS = $(foreach version,$(JUJU_VERSIONS),$(version)/$(version))
7171
=== removed file 'patches/juju-core_1.24.7.patch'
--- patches/juju-core_1.24.7.patch 2016-03-18 11:10:52 +0000
+++ patches/juju-core_1.24.7.patch 1970-01-01 00:00:00 +0000
@@ -1,47 +0,0 @@
1--- 1.24.7/src/github.com/juju/juju/testcharms/charm.go.orig 2015-06-24 12:02:02.746416146 +0200
2+++ 1.24.7/src/github.com/juju/juju/testcharms/charm.go 2015-06-24 12:03:49.810418650 +0200
3@@ -10,4 +10,6 @@
4 )
5
6 // Repo provides access to the test charm repository.
7-var Repo = testing.NewRepo("charm-repo", "quantal")
8+// XXX fake-juju: avoid crashing because the charm-repo dir is not there
9+//var Repo = testing.NewRepo("charm-repo", "quantal")
10+var Repo = &testing.Repo{}
11
12--- 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go.orig 2015-07-06 15:01:14.200568258 +0200
13+++ 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200
14@@ -642,9 +642,9 @@
15
16 // PrecheckInstance is specified in the state.Prechecker interface.
17 func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
18- if placement != "" && placement != "valid" {
19- return fmt.Errorf("%s placement is invalid", placement)
20- }
21+// if placement != "" && placement != "valid" {
22+// return fmt.Errorf("%s placement is invalid", placement)
23+// }
24 return nil
25 }
26
27--- 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000
28+++ 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000
29@@ -52,7 +52,7 @@
30 }
31
32 func mustNewCA() (string, string) {
33- cert.KeyBits = 512
34+ cert.KeyBits = 1024
35 caCert, caKey, err := cert.NewCA("juju testing", time.Now().AddDate(10, 0, 0))
36 if err != nil {
37 panic(err)
38@@ -61,7 +61,7 @@
39 }
40
41 func mustNewServer() (string, string) {
42- cert.KeyBits = 512
43+ cert.KeyBits = 1024
44 var hostnames []string
45 srvCert, srvKey, err := cert.NewServer(CACert, CAKey, time.Now().AddDate(10, 0, 0), hostnames)
46 if err != nil {
47
480
=== renamed file 'patches/juju-core_2.0-beta17.patch' => 'patches/juju-core_2.0.0.patch'
--- patches/juju-core_2.0-beta17.patch 2016-09-01 22:03:22 +0000
+++ patches/juju-core_2.0.0.patch 2016-10-29 14:08:30 +0000
@@ -1,5 +1,5 @@
1--- 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +01001--- 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +0100
2+++ 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +01002+++ 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +0100
3@@ -17,7 +17,9 @@3@@ -17,7 +17,9 @@
4 )4 )
5 5
@@ -11,8 +11,8 @@
11 11
12 // UploadCharm uploads a charm using the given charm store client, and returns12 // UploadCharm uploads a charm using the given charm store client, and returns
13 // the resulting charm URL and charm.13 // the resulting charm URL and charm.
14--- 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +020014--- 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +0200
15+++ 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +020015+++ 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200
16@@ -633,9 +633,9 @@16@@ -633,9 +633,9 @@
17 17
18 // PrecheckInstance is specified in the state.Prechecker interface.18 // PrecheckInstance is specified in the state.Prechecker interface.
@@ -26,8 +26,8 @@
26 return nil26 return nil
27 }27 }
28 28
29--- 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +000029--- 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000
30+++ 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +000030+++ 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000
31@@ -52,7 +52,7 @@31@@ -52,7 +52,7 @@
32 }32 }
33 33
3434
=== modified file 'python/fakejuju/testing.py'
--- python/fakejuju/testing.py 2016-10-18 20:54:27 +0000
+++ python/fakejuju/testing.py 2016-10-29 14:08:30 +0000
@@ -7,8 +7,8 @@
77
88
9JUJU1_VER = "1.25.6"9JUJU1_VER = "1.25.6"
10JUJU2_VER = "2.0-beta17"10JUJU2_VER = "2.0.0"
11JUJU_VER = JUJU1_VER11JUJU_VER = JUJU2_VER
1212
1313
14class FakeJujuFixture(Fixture):14class FakeJujuFixture(Fixture):
1515
=== modified file 'tests/test_fake.py'
--- tests/test_fake.py 2016-09-15 20:38:52 +0000
+++ tests/test_fake.py 2016-10-29 14:08:30 +0000
@@ -37,7 +37,7 @@
37 super(_JujuFakeTest, self).setUp()37 super(_JujuFakeTest, self).setUp()
3838
39 self.env = os.environ.copy()39 self.env = os.environ.copy()
40 self.juju_home = cfgdir = tempfile.mkdtemp()40 self.juju_home = cfgdir = tempfile.mkdtemp(prefix="fake-juju-test-")
41 _jujuclient.prepare("dummy", "dummy", cfgdir, self.env, JUJU_VERSION)41 _jujuclient.prepare("dummy", "dummy", cfgdir, self.env, JUJU_VERSION)
4242
43 endpoint, uuid, password = self.bootstrap("dummy", "dummy", self.env)43 endpoint, uuid, password = self.bootstrap("dummy", "dummy", self.env)
@@ -100,7 +100,7 @@
100100
101 def bootstrap(self, name, type, env):101 def bootstrap(self, name, type, env):
102 """Return the API endpoint after bootstrapping the controller."""102 """Return the API endpoint after bootstrapping the controller."""
103 args = [JUJU_FAKE, "bootstrap", "--no-gui", name, type]103 args = [JUJU_FAKE, "bootstrap", "--no-gui", type, name]
104 subprocess.check_call(args, env=env)104 subprocess.check_call(args, env=env)
105105
106 args = [JUJU_FAKE, "show-controller", "--format", "json",106 args = [JUJU_FAKE, "show-controller", "--format", "json",
@@ -108,7 +108,7 @@
108 output = subprocess.check_output(args, env=env)108 output = subprocess.check_output(args, env=env)
109 api_info = json.loads(output.decode())109 api_info = json.loads(output.decode())
110 endpoint = str(api_info[name]["details"]["api-endpoints"][0])110 endpoint = str(api_info[name]["details"]["api-endpoints"][0])
111 model = api_info[name]["current-model"]111 model = api_info[name]["current-model"].split("/", 1)[-1]
112 uuid = api_info[name]["models"][model]["uuid"]112 uuid = api_info[name]["models"][model]["uuid"]
113 password = api_info[name]["account"]["password"]113 password = api_info[name]["account"]["password"]
114 return endpoint, uuid, password114 return endpoint, uuid, password

Subscribers

People subscribed via source and target branches

to all changes: