Merge lp:~ericsnowcurrently/fake-juju/fake-juju-data-dir into lp:~landscape/fake-juju/trunk-old

Proposed by Eric Snow
Status: Superseded
Proposed branch: lp:~ericsnowcurrently/fake-juju/fake-juju-data-dir
Merge into: lp:~landscape/fake-juju/trunk-old
Diff against target: 1408 lines (+579/-303)
7 files modified
1.25.6/fake-juju.go (+213/-101)
2.0-beta17/fake-juju.go (+220/-136)
Makefile (+1/-1)
python/Makefile (+1/-1)
python/fakejuju/fakejuju.py (+40/-23)
python/fakejuju/testing.py (+4/-18)
python/fakejuju/tests/test_fakejuju.py (+100/-23)
To merge this branch: bzr merge lp:~ericsnowcurrently/fake-juju/fake-juju-data-dir
Reviewer Review Type Date Requested Status
Landscape Pending
Landscape Pending
Review via email: mp+308869@code.launchpad.net

This proposal has been superseded by a proposal from 2016-10-19.

Description of the change

Add a FAKE_JUJU_DATA_DIR environment variable.

This change also does some clean-up. Note that the changes are almost
completely the same for the two Juju versions, so reviewing shouldn't
be as bad as the line-count might imply. If fake-juju had better code
re-use then this patch would be much smaller.

To post a comment you must log in.
54. By Eric Snow

Merge from parent.

55. By Eric Snow

Clarify some names.

56. By Eric Snow

Add missing doc comments.

57. By Eric Snow

Clarify some names.

58. By Eric Snow

Merge from parent.

59. By Eric Snow

Merge from trunk.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '1.25.6/fake-juju.go'
2--- 1.25.6/fake-juju.go 2016-06-10 17:07:27 +0000
3+++ 1.25.6/fake-juju.go 2016-10-19 23:31:13 +0000
4@@ -50,31 +50,27 @@
5 coretesting.MgoTestPackage(t)
6 }
7
8-type processInfo struct {
9- Username string
10- WorkDir string
11- EndpointAddr string
12- Uuid string
13- CACert string
14-}
15-
16 func handleCommand(command string) error {
17+ filenames := newFakeJujuFilenames("", "", "")
18 if command == "bootstrap" {
19- return bootstrap()
20+ return bootstrap(filenames)
21 }
22 if command == "api-endpoints" {
23- return apiEndpoints()
24+ return apiEndpoints(filenames)
25 }
26 if command == "api-info" {
27- return apiInfo()
28+ return apiInfo(filenames)
29 }
30 if command == "destroy-environment" {
31- return destroyEnvironment()
32+ return destroyEnvironment(filenames)
33 }
34 return errors.New("command not found")
35 }
36
37-func bootstrap() error {
38+func bootstrap(filenames fakejujuFilenames) error {
39+ if err := filenames.ensureDirsExist(); err != nil {
40+ return err
41+ }
42 envName, config, err := environmentNameAndConfig()
43 if err != nil {
44 return err
45@@ -90,10 +86,16 @@
46 return err
47 }
48 command.Start()
49- apiInfo, err := parseApiInfo(envName, stdout)
50+
51+ result, err := parseApiInfo(stdout)
52 if err != nil {
53 return err
54 }
55+ if err := result.apply(filenames, envName); err != nil {
56+ return err
57+ }
58+ apiInfo := result.apiInfo()
59+
60 dialOpts := api.DialOpts{
61 DialAddressInterval: 50 * time.Millisecond,
62 Timeout: 5 * time.Second,
63@@ -123,8 +125,8 @@
64 return errors.New("invalid delta")
65 }
66
67-func apiEndpoints() error {
68- info, err := readProcessInfo()
69+func apiEndpoints(filenames fakejujuFilenames) error {
70+ info, err := readProcessInfo(filenames)
71 if err != nil {
72 return err
73 }
74@@ -132,23 +134,23 @@
75 return nil
76 }
77
78-func apiInfo() error {
79- info, err := readProcessInfo()
80+func apiInfo(filenames fakejujuFilenames) error {
81+ info, err := readProcessInfo(filenames)
82 if err != nil {
83 return err
84 }
85 username := strings.Replace(string(info.Username), "dummy-", "", 1)
86- fmt.Printf("{\"user\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", username, info.Uuid, info.EndpointAddr)
87+ fmt.Printf("{\"user\": \"%s\", \"password\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", username, info.Password, info.Uuid, info.EndpointAddr)
88 return nil
89 }
90
91-func destroyEnvironment() error {
92- info, err := readProcessInfo()
93+func destroyEnvironment(filenames fakejujuFilenames) error {
94+ info, err := readProcessInfo(filenames)
95 if err != nil {
96 return err
97 }
98- fifoPath := filepath.Join(info.WorkDir, "fifo")
99- fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)
100+ filenames = newFakeJujuFilenames("", "", info.WorkDir)
101+ fd, err := os.OpenFile(filenames.fifo(), os.O_APPEND|os.O_WRONLY, 0600)
102 if err != nil {
103 return err
104 }
105@@ -176,93 +178,206 @@
106 return envName, config, nil
107 }
108
109-func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {
110+type processInfo struct {
111+ Username string
112+ Password string
113+ WorkDir string
114+ EndpointAddr string
115+ Uuid string
116+ CACert []byte
117+}
118+
119+func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) {
120+ infoPath := filenames.info()
121+ data, err := ioutil.ReadFile(infoPath)
122+ if err != nil {
123+ return nil, err
124+ }
125+ info := &processInfo{}
126+ err = goyaml.Unmarshal(data, info)
127+ if err != nil {
128+ return nil, err
129+ }
130+ return info, nil
131+}
132+
133+func (info processInfo) write(infoPath string) error {
134+ data, _ := goyaml.Marshal(&info)
135+ if err := ioutil.WriteFile(infoPath, data, 0644); err != nil {
136+ return err
137+ }
138+ return nil
139+}
140+
141+type fakejujuFilenames struct {
142+ datadir string
143+ logsdir string
144+}
145+
146+func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
147+ if datadir == "" {
148+ datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
149+ if datadir == "" {
150+ if jujucfgdir == "" {
151+ jujucfgdir = os.Getenv("JUJU_HOME")
152+ }
153+ datadir = jujucfgdir
154+ }
155+ }
156+ if logsdir == "" {
157+ logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")
158+ if logsdir == "" {
159+ logsdir = datadir
160+ }
161+ }
162+ return fakejujuFilenames{datadir, logsdir}
163+}
164+
165+func (fj fakejujuFilenames) ensureDirsExist() error {
166+ if err := os.MkdirAll(fj.datadir, 0755); err != nil {
167+ return err
168+ }
169+ if err := os.MkdirAll(fj.logsdir, 0755); err != nil {
170+ return err
171+ }
172+ return nil
173+}
174+
175+func (fj fakejujuFilenames) info() string {
176+ return filepath.Join(fj.datadir, "fakejuju")
177+}
178+
179+func (fj fakejujuFilenames) logs() string {
180+ return filepath.Join(fj.logsdir, "fake-juju.log")
181+}
182+
183+func (fj fakejujuFilenames) fifo() string {
184+ return filepath.Join(fj.datadir, "fifo")
185+}
186+
187+func (fj fakejujuFilenames) cacert() string {
188+ return filepath.Join(fj.datadir, "cert.ca")
189+}
190+
191+type bootstrapResult struct {
192+ dummyEnvName string
193+ cfgdir string
194+ uuid string
195+ username string
196+ password string
197+ addresses []string
198+ caCert []byte
199+}
200+
201+func (br bootstrapResult) apiInfo() *api.Info {
202+ return &api.Info{
203+ Addrs: br.addresses,
204+ Tag: names.NewLocalUserTag(br.username),
205+ Password: br.password,
206+ CACert: string(br.caCert),
207+ EnvironTag: names.NewEnvironTag(br.uuid),
208+ }
209+}
210+
211+func (br bootstrapResult) fakeJujuInfo() *processInfo {
212+ return &processInfo{
213+ Username: br.username,
214+ Password: br.password,
215+ WorkDir: br.cfgdir,
216+ EndpointAddr: br.addresses[0],
217+ Uuid: br.uuid,
218+ CACert: br.caCert,
219+ }
220+}
221+
222+func (br bootstrapResult) logsSymlink(target string) (string, string) {
223+ if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" || os.Getenv("FAKE_JUJU_DATA_DIR") != "" {
224+ return "", ""
225+ }
226+
227+ filenames := newFakeJujuFilenames("", "", br.cfgdir)
228+ source := filenames.logs()
229+ return source, target
230+}
231+
232+func (br bootstrapResult) jenvSymlink(jujuHome, envName string) (string, string) {
233+ if jujuHome == "" || envName == "" {
234+ return "", ""
235+ }
236+
237+ source := filepath.Join(br.cfgdir, "environments", br.dummyEnvName+".jenv")
238+ target := filepath.Join(jujuHome, "environments", envName+".jenv")
239+ return source, target
240+}
241+
242+func (br bootstrapResult) apply(filenames fakejujuFilenames, envName string) error {
243+ if err := br.fakeJujuInfo().write(filenames.info()); err != nil {
244+ return err
245+ }
246+
247+ logsSource, logsTarget := br.logsSymlink(filenames.logs())
248+ if logsSource != "" && logsTarget != "" {
249+ if err := os.Symlink(logsSource, logsTarget); err != nil {
250+ return err
251+ }
252+ }
253+
254+ jenvSource, jenvTarget := br.jenvSymlink(os.Getenv("JUJU_HOME"), envName)
255+ if jenvSource != "" && jenvTarget != "" {
256+ if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil {
257+ return err
258+ }
259+ if err := os.Symlink(jenvSource, jenvTarget); err != nil {
260+ return err
261+ }
262+ }
263+
264+ if err := ioutil.WriteFile(filenames.cacert(), br.caCert, 0644); err != nil {
265+ return err
266+ }
267+
268+ return nil
269+}
270+
271+// See github.com/juju/juju/blob/juju/testing/conn.go.
272+const dummyEnvName = "dummyenv"
273+
274+func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
275 buffer := bufio.NewReader(stdout)
276+
277 line, _, err := buffer.ReadLine()
278 if err != nil {
279 return nil, err
280 }
281 uuid := string(line)
282- environTag := names.NewEnvironTag(uuid)
283+
284 line, _, err = buffer.ReadLine()
285 if err != nil {
286 return nil, err
287 }
288 workDir := string(line)
289+
290 store, err := configstore.NewDisk(workDir)
291 if err != nil {
292 return nil, err
293 }
294- info, err := store.ReadInfo("dummyenv")
295+ info, err := store.ReadInfo(dummyEnvName)
296 if err != nil {
297 return nil, err
298 }
299+
300 credentials := info.APICredentials()
301 endpoint := info.APIEndpoint()
302- addresses := endpoint.Addresses
303- apiInfo := &api.Info{
304- Addrs: addresses,
305- Tag: names.NewLocalUserTag(credentials.User),
306- Password: credentials.Password,
307- CACert: endpoint.CACert,
308- EnvironTag: environTag,
309- }
310- err = writeProcessInfo(envName, &processInfo{
311- Username: credentials.User,
312- WorkDir: workDir,
313- EndpointAddr: addresses[0],
314- Uuid: uuid,
315- CACert: endpoint.CACert,
316- })
317- if err != nil {
318- return nil, err
319- }
320- return apiInfo, nil
321-}
322-
323-func readProcessInfo() (*processInfo, error) {
324- infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju")
325- data, err := ioutil.ReadFile(infoPath)
326- if err != nil {
327- return nil, err
328- }
329- info := &processInfo{}
330- err = goyaml.Unmarshal(data, info)
331- if err != nil {
332- return nil, err
333- }
334- return info, nil
335-}
336-
337-func writeProcessInfo(envName string, info *processInfo) error {
338- var err error
339- jujuHome := os.Getenv("JUJU_HOME")
340- infoPath := filepath.Join(jujuHome, "fakejuju")
341- logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
342- if logsDir == "" {
343- logsDir = jujuHome
344- }
345- logPath := filepath.Join(logsDir, "fake-juju.log")
346- caCertPath := filepath.Join(jujuHome, "cert.ca")
347- envPath := filepath.Join(jujuHome, "environments")
348- os.Mkdir(envPath, 0755)
349- jEnvPath := filepath.Join(envPath, envName+".jenv")
350- data, _ := goyaml.Marshal(info)
351- if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" {
352- err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
353- if err != nil {
354- return err
355- }
356- }
357- err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath)
358- if err != nil {
359- return err
360- }
361- err = ioutil.WriteFile(infoPath, data, 0644)
362- if err != nil {
363- return err
364- }
365- return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
366+ result := &bootstrapResult{
367+ dummyEnvName: dummyEnvName,
368+ cfgdir: workDir,
369+ uuid: uuid,
370+ username: credentials.User,
371+ password: credentials.Password,
372+ addresses: endpoint.Addresses,
373+ caCert: []byte(endpoint.CACert),
374+ }
375+ return result, nil
376 }
377
378 // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
379@@ -307,7 +422,7 @@
380
381 instanceCount int
382 machineStarted map[string]bool
383- fifoPath string
384+ filenames fakejujuFilenames
385 logFile *os.File
386 }
387
388@@ -374,15 +489,11 @@
389 c.Assert(err, gc.IsNil)
390 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
391
392- s.fifoPath = filepath.Join(jujuHome, "fifo")
393- syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)
394+ s.filenames = newFakeJujuFilenames("", "", jujuHome)
395+ syscall.Mknod(s.filenames.fifo(), syscall.S_IFIFO|0666, 0)
396
397 // Logging
398- logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
399- if logsDir == "" {
400- logsDir = jujuHome
401- }
402- logPath := filepath.Join(logsDir, "fake-juju.log")
403+ logPath := s.filenames.logs()
404 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
405 c.Assert(err, gc.IsNil)
406
407@@ -402,16 +513,17 @@
408 }
409
410 func (s *FakeJujuSuite) TestStart(c *gc.C) {
411+ fifoPath := s.filenames.fifo()
412 watcher := s.State.Watch()
413 go func() {
414- log.Println("Open commands FIFO", s.fifoPath)
415- fd, err := os.Open(s.fifoPath)
416+ log.Println("Open commands FIFO", fifoPath)
417+ fd, err := os.Open(fifoPath)
418 if err != nil {
419 log.Println("Failed to open commands FIFO")
420 }
421 c.Assert(err, gc.IsNil)
422 scanner := bufio.NewScanner(fd)
423- log.Println("Listen for commands on FIFO", s.fifoPath)
424+ log.Println("Listen for commands on FIFO", fifoPath)
425 scanner.Scan()
426 log.Println("Stopping fake-juju")
427 watcher.Stop()
428
429=== modified file '2.0-beta17/fake-juju.go'
430--- 2.0-beta17/fake-juju.go 2016-09-15 19:05:50 +0000
431+++ 2.0-beta17/fake-juju.go 2016-10-19 23:31:13 +0000
432@@ -51,33 +51,31 @@
433 coretesting.MgoTestPackage(t)
434 }
435
436-type processInfo struct {
437- WorkDir string
438- EndpointAddr string
439- Uuid string
440- CACert string
441-}
442-
443 func handleCommand(command string) error {
444+ filenames := newFakeJujuFilenames("", "", "")
445 if command == "bootstrap" {
446- return bootstrap()
447+ return bootstrap(filenames)
448 }
449 if command == "show-controller" {
450- return apiInfo()
451+ return apiInfo(filenames)
452 }
453 if command == "destroy-controller" {
454- return destroyEnvironment()
455+ return destroyController(filenames)
456 }
457 return errors.New("command not found")
458 }
459
460-func bootstrap() error {
461+func bootstrap(filenames fakejujuFilenames) error {
462 argc := len(os.Args)
463 if argc < 4 {
464 return errors.New(
465 "error: controller name and cloud name are required")
466 }
467- envName := os.Args[argc-2]
468+ if err := filenames.ensureDirsExist(); err != nil {
469+ return err
470+ }
471+ // XXX Swap the 2 args for juju-2.0-final.
472+ controllerName := os.Args[argc-2]
473 command := exec.Command(os.Args[0])
474 command.Env = os.Environ()
475 command.Env = append(
476@@ -89,10 +87,16 @@
477 return err
478 }
479 command.Start()
480- apiInfo, err := parseApiInfo(envName, stdout)
481+
482+ result, err := parseApiInfo(stdout)
483 if err != nil {
484 return err
485 }
486+ if err := result.apply(filenames, controllerName); err != nil {
487+ return err
488+ }
489+ apiInfo := result.apiInfo()
490+
491 dialOpts := api.DialOpts{
492 DialAddressInterval: 50 * time.Millisecond,
493 Timeout: 5 * time.Second,
494@@ -122,8 +126,8 @@
495 return errors.New("invalid delta")
496 }
497
498-func apiInfo() error {
499- info, err := readProcessInfo()
500+func apiInfo(filenames fakejujuFilenames) error {
501+ info, err := readProcessInfo(filenames)
502 if err != nil {
503 return err
504 }
505@@ -140,13 +144,13 @@
506 return nil
507 }
508
509-func destroyEnvironment() error {
510- info, err := readProcessInfo()
511+func destroyController(filenames fakejujuFilenames) error {
512+ info, err := readProcessInfo(filenames)
513 if err != nil {
514 return err
515 }
516- fifoPath := filepath.Join(info.WorkDir, "fifo")
517- fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)
518+ filenames = newFakeJujuFilenames("", "", info.WorkDir)
519+ fd, err := os.OpenFile(filenames.fifo(), os.O_APPEND|os.O_WRONLY, 0600)
520 if err != nil {
521 return err
522 }
523@@ -158,13 +162,187 @@
524 return nil
525 }
526
527-func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {
528+type processInfo struct {
529+ WorkDir string
530+ EndpointAddr string
531+ Uuid string
532+ CACert []byte
533+}
534+
535+func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) {
536+ infoPath := filenames.info()
537+ data, err := ioutil.ReadFile(infoPath)
538+ if err != nil {
539+ return nil, err
540+ }
541+ info := &processInfo{}
542+ err = goyaml.Unmarshal(data, info)
543+ if err != nil {
544+ return nil, err
545+ }
546+ return info, nil
547+}
548+
549+func (info processInfo) write(infoPath string) error {
550+ data, _ := goyaml.Marshal(&info)
551+ if err := ioutil.WriteFile(infoPath, data, 0644); err != nil {
552+ return err
553+ }
554+ return nil
555+}
556+
557+type fakejujuFilenames struct {
558+ datadir string
559+ logsdir string
560+}
561+
562+func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
563+ if datadir == "" {
564+ datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
565+ if datadir == "" {
566+ if jujucfgdir == "" {
567+ jujucfgdir = os.Getenv("JUJU_DATA")
568+ }
569+ datadir = jujucfgdir
570+ }
571+ }
572+ if logsdir == "" {
573+ logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")
574+ if logsdir == "" {
575+ logsdir = datadir
576+ }
577+ }
578+ return fakejujuFilenames{datadir, logsdir}
579+}
580+
581+func (fj fakejujuFilenames) ensureDirsExist() error {
582+ if err := os.MkdirAll(fj.datadir, 0755); err != nil {
583+ return err
584+ }
585+ if err := os.MkdirAll(fj.logsdir, 0755); err != nil {
586+ return err
587+ }
588+ return nil
589+}
590+
591+func (fj fakejujuFilenames) info() string {
592+ return filepath.Join(fj.datadir, "fakejuju")
593+}
594+
595+func (fj fakejujuFilenames) logs() string {
596+ return filepath.Join(fj.logsdir, "fake-juju.log")
597+}
598+
599+func (fj fakejujuFilenames) fifo() string {
600+ return filepath.Join(fj.datadir, "fifo")
601+}
602+
603+func (fj fakejujuFilenames) cacert() string {
604+ return filepath.Join(fj.datadir, "cert.ca")
605+}
606+
607+type bootstrapResult struct {
608+ dummyControllerName string
609+ cfgdir string
610+ uuid string
611+ username string
612+ password string
613+ addresses []string
614+ caCert []byte
615+}
616+
617+func (br bootstrapResult) apiInfo() *api.Info {
618+ return &api.Info{
619+ Addrs: br.addresses,
620+ Tag: names.NewUserTag(br.username),
621+ Password: br.password,
622+ CACert: string(br.caCert),
623+ ModelTag: names.NewModelTag(br.uuid),
624+ }
625+}
626+
627+func (br bootstrapResult) fakeJujuInfo() *processInfo {
628+ return &processInfo{
629+ WorkDir: br.cfgdir,
630+ EndpointAddr: br.addresses[0],
631+ Uuid: br.uuid,
632+ CACert: br.caCert,
633+ }
634+}
635+
636+func (br bootstrapResult) logsSymlink(target string) (string, string) {
637+ if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" {
638+ return "", ""
639+ }
640+
641+ filenames := newFakeJujuFilenames("", "", br.cfgdir)
642+ source := filenames.logs()
643+ return source, target
644+}
645+
646+func (br bootstrapResult) apply(filenames fakejujuFilenames, controllerName string) error {
647+ if err := br.fakeJujuInfo().write(filenames.info()); err != nil {
648+ return err
649+ }
650+
651+ logsSource, logsTarget := br.logsSymlink(filenames.logs())
652+ if logsSource != "" && logsTarget != "" {
653+ if err := os.Symlink(logsSource, logsTarget); err != nil {
654+ return err
655+ }
656+ }
657+
658+ if err := br.copyConfig(os.Getenv("JUJU_DATA"), controllerName); err != nil {
659+ return err
660+ }
661+
662+ if err := ioutil.WriteFile(filenames.cacert(), br.caCert, 0644); err != nil {
663+ return err
664+ }
665+
666+ return nil
667+}
668+
669+func (br bootstrapResult) copyConfig(cfgdir, controllerName string) error {
670+ for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} {
671+ source := filepath.Join(br.cfgdir, name)
672+ target := filepath.Join(cfgdir, name)
673+
674+ input, err := ioutil.ReadFile(source)
675+ if err != nil {
676+ return err
677+ }
678+ // Generated configuration by test fixtures has the controller name
679+ // hard-coded to "kontroll". A simple replace should fix this for
680+ // clients using this config and expecting a specific controller
681+ // name.
682+ output := strings.Replace(string(input), dummyControllerName, controllerName, -1)
683+ err = ioutil.WriteFile(target, []byte(output), 0644)
684+ if err != nil {
685+ return err
686+ }
687+ }
688+
689+ current := filepath.Join(cfgdir, "current-controller")
690+ if err := ioutil.WriteFile(current, []byte(controllerName), 0644); err != nil {
691+ return err
692+ }
693+
694+ return nil
695+}
696+
697+// See github.com/juju/juju/blob/juju/testing/conn.go.
698+const dummyControllerName = "kontroll"
699+
700+func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
701 buffer := bufio.NewReader(stdout)
702+
703 line, _, err := buffer.ReadLine()
704 if err != nil {
705 return nil, err
706 }
707 uuid := string(line)
708+
709 line, _, err = buffer.ReadLine()
710 if err != nil {
711 return nil, err
712@@ -175,8 +353,8 @@
713 store := jujuclient.NewFileClientStore()
714 // hard-coded value in juju testing
715 // This will be replaced in JUJU_DATA copy of the juju client config.
716- currentController := "kontroll"
717- one, err := store.ControllerByName("kontroll")
718+ currentController := dummyControllerName
719+ one, err := store.ControllerByName(currentController)
720 if err != nil {
721 return nil, err
722 }
723@@ -185,108 +363,17 @@
724 if err != nil {
725 return nil, err
726 }
727- apiInfo := &api.Info{
728- Addrs: one.APIEndpoints,
729- Tag: names.NewUserTag(accountDetails.User),
730- Password: accountDetails.Password,
731- CACert: one.CACert,
732- ModelTag: names.NewModelTag(uuid),
733- }
734-
735- err = writeProcessInfo(envName, &processInfo{
736- WorkDir: workDir,
737- EndpointAddr: one.APIEndpoints[0],
738- Uuid: uuid,
739- CACert: one.CACert,
740- })
741- if err != nil {
742- return nil, err
743- }
744- return apiInfo, nil
745-}
746-
747-func readProcessInfo() (*processInfo, error) {
748- infoPath := filepath.Join(os.Getenv("JUJU_DATA"), "fakejuju")
749- data, err := ioutil.ReadFile(infoPath)
750- if err != nil {
751- return nil, err
752- }
753- info := &processInfo{}
754- err = goyaml.Unmarshal(data, info)
755- if err != nil {
756- return nil, err
757- }
758- return info, nil
759-}
760-
761-func writeProcessInfo(envName string, info *processInfo) error {
762- var err error
763- jujuHome := os.Getenv("JUJU_DATA")
764- infoPath := filepath.Join(jujuHome, "fakejuju")
765- logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
766- if logsDir == "" {
767- logsDir = jujuHome
768- }
769- logPath := filepath.Join(logsDir, "fake-juju.log")
770- caCertPath := filepath.Join(jujuHome, "cert.ca")
771- data, _ := goyaml.Marshal(info)
772- if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" {
773- err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
774- if err != nil {
775- return err
776- }
777- }
778-
779- err = copyClientConfig(
780- filepath.Join(info.WorkDir, "controllers.yaml"),
781- filepath.Join(jujuHome, "controllers.yaml"),
782- envName)
783- if err != nil {
784- return err
785- }
786- err = copyClientConfig(
787- filepath.Join(info.WorkDir, "models.yaml"),
788- filepath.Join(jujuHome, "models.yaml"),
789- envName)
790- if err != nil {
791- return err
792- }
793- err = copyClientConfig(
794- filepath.Join(info.WorkDir, "accounts.yaml"),
795- filepath.Join(jujuHome, "accounts.yaml"),
796- envName)
797- if err != nil {
798- return err
799- }
800- err = ioutil.WriteFile(
801- filepath.Join(jujuHome, "current-controller"),
802- []byte(envName), 0644)
803- if err != nil {
804- return err
805- }
806-
807- err = ioutil.WriteFile(infoPath, data, 0644)
808- if err != nil {
809- return err
810- }
811- return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
812-}
813-
814-func copyClientConfig(src string, dst string, envName string) error {
815- input, err := ioutil.ReadFile(src)
816- if err != nil {
817- return err
818- }
819- // Generated configuration by test fixtures has the controller name
820- // hard-coded to "kontroll". A simple replace should fix this for
821- // clients using this config and expecting a specific controller
822- // name.
823- output := strings.Replace(string(input), "kontroll", envName, -1)
824- err = ioutil.WriteFile(dst, []byte(output), 0644)
825- if err != nil {
826- return err
827- }
828- return nil
829+
830+ result := &bootstrapResult{
831+ dummyControllerName: dummyControllerName,
832+ cfgdir: workDir,
833+ uuid: uuid,
834+ username: accountDetails.User,
835+ password: accountDetails.Password,
836+ addresses: one.APIEndpoints,
837+ caCert: []byte(one.CACert),
838+ }
839+ return result, nil
840 }
841
842 // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
843@@ -331,7 +418,7 @@
844
845 instanceCount int
846 machineStarted map[string]bool
847- fifoPath string
848+ filenames fakejujuFilenames
849 logFile *os.File
850 }
851
852@@ -398,20 +485,16 @@
853 c.Assert(err, gc.IsNil)
854 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
855
856- s.fifoPath = filepath.Join(jujuHome, "fifo")
857- syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)
858+ s.filenames = newFakeJujuFilenames("", "", jujuHome)
859+ syscall.Mknod(s.filenames.fifo(), syscall.S_IFIFO|0666, 0)
860
861 // Logging
862- logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
863- if logsDir == "" {
864- logsDir = jujuHome
865- }
866- logPath := filepath.Join(logsDir, "fake-juju.log")
867+ logPath := s.filenames.logs()
868 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
869 c.Assert(err, gc.IsNil)
870
871 log.SetOutput(s.logFile)
872- log.Println("Started fake-juju at", jujuHome)
873+ log.Println("Started fake-juju at ", jujuHome)
874
875 }
876
877@@ -423,16 +506,17 @@
878 }
879
880 func (s *FakeJujuSuite) TestStart(c *gc.C) {
881+ fifoPath := s.filenames.fifo()
882 watcher := s.State.Watch()
883 go func() {
884- log.Println("Open commands FIFO", s.fifoPath)
885- fd, err := os.Open(s.fifoPath)
886+ log.Println("Open commands FIFO", fifoPath)
887+ fd, err := os.Open(fifoPath)
888 if err != nil {
889 log.Println("Failed to open commands FIFO")
890 }
891 c.Assert(err, gc.IsNil)
892 scanner := bufio.NewScanner(fd)
893- log.Println("Listen for commands on FIFO", s.fifoPath)
894+ log.Println("Listen for commands on FIFO", fifoPath)
895 scanner.Scan()
896 log.Println("Stopping fake-juju")
897 watcher.Stop()
898
899=== modified file 'Makefile'
900--- Makefile 2016-09-20 18:26:47 +0000
901+++ Makefile 2016-10-19 23:31:13 +0000
902@@ -11,7 +11,7 @@
903 INSTALLDIR = $(DESTDIR)/usr/bin
904 INSTALLED = $(INSTALLDIR)/fake-juju-$(JUJU_VERSION)
905
906-$(JUJU_VERSION)/$(JUJU_VERSION):
907+$(JUJU_VERSION)/$(JUJU_VERSION): $(JUJU_VERSION)/fake-juju.go
908 case $(JUJU_VERSION) in \
909 1.*) $(MAKE) build-common PATH=$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\
910 2.*) $(MAKE) build-common PATH=/usr/lib/go-$(GO_VERSION)/bin:$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\
911
912=== modified file 'python/Makefile'
913--- python/Makefile 2016-10-06 21:44:31 +0000
914+++ python/Makefile 2016-10-19 23:31:13 +0000
915@@ -6,4 +6,4 @@
916
917 .PHONY: install-dev
918 install-dev:
919- ln -s $(shell pwd)/fakejuju /usr/local/lib/python2.7/dist-packages/fakejuju
920+ ln -snv $(shell pwd)/fakejuju /usr/local/lib/python2.7/dist-packages/fakejuju
921
922=== modified file 'python/fakejuju/fakejuju.py'
923--- python/fakejuju/fakejuju.py 2016-10-17 15:54:59 +0000
924+++ python/fakejuju/fakejuju.py 2016-10-19 23:31:13 +0000
925@@ -2,6 +2,7 @@
926
927 import os.path
928
929+import txjuju
930 import txjuju.cli
931
932 from .failures import Failures
933@@ -23,15 +24,17 @@
934 return os.path.join(bindir, filename)
935
936
937-def set_envvars(envvars, failures_filename=None, logsdir=None):
938+def set_envvars(envvars, datadir=None, failures_filename=None, logsdir=None):
939 """Return the environment variables with which to run fake-juju.
940
941 @param envvars: The env dict to update.
942+ @param datadir: The fake-juju data directory.
943 @param failures_filename: The path to the failures file that
944 fake-juju will use.
945 @params logsdir: The path to the directory where fake-juju will
946 write its log files.
947 """
948+ envvars["FAKE_JUJU_DATA_DIR"] = datadir or ""
949 envvars["FAKE_JUJU_FAILURES"] = failures_filename or ""
950 envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or ""
951
952@@ -40,46 +43,47 @@
953 """The fundamental details for fake-juju."""
954
955 @classmethod
956- def from_version(cls, version, cfgdir,
957+ def from_version(cls, version, datadir,
958 logsdir=None, failuresdir=None, bindir=None):
959 """Return a new instance given the provided information.
960
961 @param version: The Juju version to fake.
962- @param cfgdir: The "juju home" directory to use.
963+ @param datadir: The directory in which to store files specific
964+ to fake-juju.
965 @param logsdir: The directory where logs will be written.
966- This defaults to cfgdir.
967+ This defaults to datadir.
968 @params failuresdir: The directory where failure injection
969 is managed.
970 @param bindir: The directory containing the fake-juju binary.
971 This defaults to /usr/bin.
972 """
973- if logsdir is None:
974- logsdir = cfgdir
975 if failuresdir is None:
976- failuresdir = cfgdir
977+ failuresdir = datadir
978 filename = get_filename(version, bindir=bindir)
979 failures = Failures(failuresdir)
980- return cls(filename, version, cfgdir, logsdir, failures)
981+ return cls(filename, version, datadir, logsdir, failures)
982
983- def __init__(self, filename, version, cfgdir, logsdir=None, failures=None):
984+ def __init__(self, filename, version, datadir,
985+ logsdir=None, failures=None):
986 """
987 @param filename: The path to the fake-juju binary.
988 @param version: The Juju version to fake.
989- @param cfgdir: The "juju home" directory to use.
990+ @param datadir: The directory in which to store files specific
991+ to fake-juju.
992 @param logsdir: The directory where logs will be written.
993- This defaults to cfgdir.
994+ This defaults to datadir.
995 @param failures: The set of fake-juju failures to use.
996 """
997- logsdir = logsdir if logsdir is not None else cfgdir
998- if failures is None and cfgdir:
999- failures = Failures(cfgdir)
1000+ logsdir = logsdir if logsdir is not None else datadir
1001+ if failures is None and datadir:
1002+ failures = Failures(datadir)
1003
1004 if not filename:
1005 raise ValueError("missing filename")
1006 if not version:
1007 raise ValueError("missing version")
1008- if not cfgdir:
1009- raise ValueError("missing cfgdir")
1010+ if not datadir:
1011+ raise ValueError("missing datadir")
1012 if not logsdir:
1013 raise ValueError("missing logsdir")
1014 if failures is None:
1015@@ -87,7 +91,7 @@
1016
1017 self.filename = filename
1018 self.version = version
1019- self.cfgdir = cfgdir
1020+ self.datadir = datadir
1021 self.logsdir = logsdir
1022 self.failures = failures
1023
1024@@ -99,19 +103,19 @@
1025 @property
1026 def infofile(self):
1027 """The path to fake-juju's data cache."""
1028- return os.path.join(self.cfgdir, "fakejuju")
1029+ return os.path.join(self.datadir, "fakejuju")
1030
1031 @property
1032 def fifo(self):
1033 """The path to the fifo file that triggers shutdown."""
1034- return os.path.join(self.cfgdir, "fifo")
1035+ return os.path.join(self.datadir, "fifo")
1036
1037 @property
1038 def cacertfile(self):
1039 """The path to the API server's certificate."""
1040- return os.path.join(self.cfgdir, "cert.ca")
1041+ return os.path.join(self.datadir, "cert.ca")
1042
1043- def cli(self, envvars=None):
1044+ def cli(self, cfgdir, envvars=None):
1045 """Return the txjuju.cli.CLI for this fake-juju.
1046
1047 Currently fake-juju supports only the following juju subcommands:
1048@@ -123,10 +127,23 @@
1049 Note that passwords are always omited, even if requested.
1050 * api-endpoints
1051 * destroy-environment
1052+
1053+ Note that fake-juju ignores local config files.
1054 """
1055 if envvars is None:
1056 envvars = os.environ
1057 envvars = dict(envvars)
1058- set_envvars(envvars, self.failures._filename, self.logsdir)
1059+ set_envvars(
1060+ envvars, self.datadir, self.failures._filename, self.logsdir)
1061 return txjuju.cli.CLI.from_version(
1062- self.filename, self.version, self.cfgdir, envvars)
1063+ self.filename, self.version, cfgdir, envvars)
1064+
1065+ def bootstrap(self, name, cfgdir, admin_secret=None):
1066+ """Return the CLI and APIInfo after bootstrapping from scratch."""
1067+ from . import get_bootstrap_spec
1068+ spec = get_bootstrap_spec(name, admin_secret)
1069+ cfgfile = txjuju.prepare_for_bootstrap(spec, self.version, cfgdir)
1070+ cli = self.cli(cfgdir)
1071+ cli.bootstrap(spec, cfgfile=cfgfile)
1072+ api_info = cli.api_info(spec.name)
1073+ return cli, api_info
1074
1075=== modified file 'python/fakejuju/testing.py'
1076--- python/fakejuju/testing.py 2016-10-06 22:51:41 +0000
1077+++ python/fakejuju/testing.py 2016-10-19 23:31:13 +0000
1078@@ -1,6 +1,5 @@
1079 # Copyright 2016 Canonical Limited. All rights reserved.
1080
1081-import txjuju
1082 from fixtures import Fixture, TempDir
1083 from testtools.content import content_from_file
1084
1085@@ -40,29 +39,16 @@
1086 def setUp(self):
1087 super(FakeJujuFixture, self).setUp()
1088 self._juju_home = self.useFixture(TempDir())
1089- self._juju = fakejuju.FakeJuju.make(
1090- self._juju_home.path, self._version, self._logs_dir)
1091+ self.fakejuju = fakejuju.FakeJuju.from_version(
1092+ self._version, self._juju_home.path, self._logs_dir)
1093
1094 if not self._logs_dir:
1095 # Attach logs as testtools details.
1096 self.addDetail("log-file", content_from_file(self._juju.logfile))
1097
1098- spec = fakejuju.get_bootstrap_spec(self._controller, self._password)
1099- cfgfile = txjuju.prepare_for_bootstrap(
1100- spec, self._version, self._juju_home)
1101- cli = self._juju.cli()
1102- cli.bootstrap(spec, cfgfile=cfgfile)
1103- api_info = cli.api_info(spec.name)
1104- if self._version.startswith("1."):
1105- # fake-juju doesn't give us the password, so we have to
1106- # set it here.
1107- api_info = api_info._replace(password=self._password)
1108- self.api_info = api_info
1109+ self._juju, self.all_api_info = self.fakejuju.bootstrap(
1110+ self._controller, self._password)
1111
1112 def cleanUp(self):
1113 self._juju.destroy_controller(self._controller)
1114 super(FakeJujuFixture, self).cleanUp()
1115-
1116- def add_failure(self, entity):
1117- """Make the given entity fail with an error status."""
1118- self._juju.failures.fail_entity(entity)
1119
1120=== modified file 'python/fakejuju/tests/test_fakejuju.py'
1121--- python/fakejuju/tests/test_fakejuju.py 2016-10-17 15:36:15 +0000
1122+++ python/fakejuju/tests/test_fakejuju.py 2016-10-19 23:31:13 +0000
1123@@ -1,10 +1,15 @@
1124 # Copyright 2016 Canonical Limited. All rights reserved.
1125
1126+from contextlib import contextmanager
1127 import os
1128+import shutil
1129+import tempfile
1130 import unittest
1131
1132 from txjuju import _juju1, _juju2
1133 from txjuju._utils import Executable
1134+import txjuju.cli
1135+import yaml
1136
1137 from fakejuju.failures import Failures
1138 from fakejuju.fakejuju import get_filename, set_envvars, FakeJuju
1139@@ -44,9 +49,10 @@
1140 def test_all_args(self):
1141 """set_envvars() works correctly when given all args."""
1142 envvars = {}
1143- set_envvars(envvars, "/spam/failures", "/eggs/logsdir")
1144+ set_envvars(envvars, "/spam", "/spam/failures", "/eggs/logsdir")
1145
1146 self.assertEqual(envvars, {
1147+ "FAKE_JUJU_DATA_DIR": "/spam",
1148 "FAKE_JUJU_FAILURES": "/spam/failures",
1149 "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir",
1150 })
1151@@ -57,6 +63,7 @@
1152 set_envvars(envvars)
1153
1154 self.assertEqual(envvars, {
1155+ "FAKE_JUJU_DATA_DIR": "",
1156 "FAKE_JUJU_FAILURES": "",
1157 "FAKE_JUJU_LOGS_DIR": "",
1158 })
1159@@ -64,9 +71,10 @@
1160 def test_start_empty(self):
1161 """set_envvars() sets all values on an empty dict."""
1162 envvars = {}
1163- set_envvars(envvars, "x", "y")
1164+ set_envvars(envvars, "w", "x", "y")
1165
1166 self.assertEqual(envvars, {
1167+ "FAKE_JUJU_DATA_DIR": "w",
1168 "FAKE_JUJU_FAILURES": "x",
1169 "FAKE_JUJU_LOGS_DIR": "y",
1170 })
1171@@ -74,10 +82,11 @@
1172 def test_no_collisions(self):
1173 """set_envvars() sets all values when none are set yet."""
1174 envvars = {"SPAM": "eggs"}
1175- set_envvars(envvars, "x", "y")
1176+ set_envvars(envvars, "w", "x", "y")
1177
1178 self.assertEqual(envvars, {
1179 "SPAM": "eggs",
1180+ "FAKE_JUJU_DATA_DIR": "w",
1181 "FAKE_JUJU_FAILURES": "x",
1182 "FAKE_JUJU_LOGS_DIR": "y",
1183 })
1184@@ -85,12 +94,14 @@
1185 def test_empty_to_nonempty(self):
1186 """set_envvars() updates empty values."""
1187 envvars = {
1188+ "FAKE_JUJU_DATA_DIR": "",
1189 "FAKE_JUJU_FAILURES": "",
1190 "FAKE_JUJU_LOGS_DIR": "",
1191 }
1192- set_envvars(envvars, "x", "y")
1193+ set_envvars(envvars, "w", "x", "y")
1194
1195 self.assertEqual(envvars, {
1196+ "FAKE_JUJU_DATA_DIR": "w",
1197 "FAKE_JUJU_FAILURES": "x",
1198 "FAKE_JUJU_LOGS_DIR": "y",
1199 })
1200@@ -98,12 +109,14 @@
1201 def test_nonempty_to_nonempty(self):
1202 """set_envvars() overwrites existing values."""
1203 envvars = {
1204+ "FAKE_JUJU_DATA_DIR": "spam",
1205 "FAKE_JUJU_FAILURES": "spam",
1206 "FAKE_JUJU_LOGS_DIR": "ham",
1207 }
1208- set_envvars(envvars, "x", "y")
1209+ set_envvars(envvars, "w", "x", "y")
1210
1211 self.assertEqual(envvars, {
1212+ "FAKE_JUJU_DATA_DIR": "w",
1213 "FAKE_JUJU_FAILURES": "x",
1214 "FAKE_JUJU_LOGS_DIR": "y",
1215 })
1216@@ -111,12 +124,14 @@
1217 def test_nonempty_to_empty(self):
1218 """set_envvars() with no args "unsets" existing values."""
1219 envvars = {
1220+ "FAKE_JUJU_DATA_DIR": "w",
1221 "FAKE_JUJU_FAILURES": "x",
1222 "FAKE_JUJU_LOGS_DIR": "y",
1223 }
1224 set_envvars(envvars)
1225
1226 self.assertEqual(envvars, {
1227+ "FAKE_JUJU_DATA_DIR": "",
1228 "FAKE_JUJU_FAILURES": "",
1229 "FAKE_JUJU_LOGS_DIR": "",
1230 })
1231@@ -131,7 +146,7 @@
1232
1233 self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6")
1234 self.assertEqual(juju.version, "1.25.6")
1235- self.assertEqual(juju.cfgdir, "/a/juju/home")
1236+ self.assertEqual(juju.datadir, "/a/juju/home")
1237 self.assertEqual(juju.logsdir, "/logs/dir")
1238 self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures")
1239
1240@@ -141,19 +156,20 @@
1241
1242 self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6")
1243 self.assertEqual(juju.version, "1.25.6")
1244- self.assertEqual(juju.cfgdir, "/my/juju/home")
1245+ self.assertEqual(juju.datadir, "/my/juju/home")
1246 self.assertEqual(juju.logsdir, "/my/juju/home")
1247 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")
1248
1249 def test_full(self):
1250 """FakeJuju() works correctly when given all args."""
1251- cfgdir = "/my/juju/home"
1252- failures = Failures(cfgdir)
1253- juju = FakeJuju("/fake-juju", "1.25.6", cfgdir, "/some/logs", failures)
1254+ datadir = "/my/juju/home"
1255+ failures = Failures(datadir)
1256+ juju = FakeJuju(
1257+ "/fake-juju", "1.25.6", datadir, "/some/logs", failures)
1258
1259 self.assertEqual(juju.filename, "/fake-juju")
1260 self.assertEqual(juju.version, "1.25.6")
1261- self.assertEqual(juju.cfgdir, cfgdir)
1262+ self.assertEqual(juju.datadir, datadir)
1263 self.assertEqual(juju.logsdir, "/some/logs")
1264 self.assertIs(juju.failures, failures)
1265
1266@@ -163,7 +179,7 @@
1267
1268 self.assertEqual(juju.filename, "/fake-juju")
1269 self.assertEqual(juju.version, "1.25.6")
1270- self.assertEqual(juju.cfgdir, "/my/juju/home")
1271+ self.assertEqual(juju.datadir, "/my/juju/home")
1272 self.assertEqual(juju.logsdir, "/my/juju/home")
1273 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")
1274
1275@@ -174,7 +190,7 @@
1276 juju_unicode = FakeJuju(
1277 u"/fake-juju", u"1.25.6", u"/x", u"/y", Failures(u"/..."))
1278
1279- for name in ('filename version cfgdir logsdir'.split()):
1280+ for name in ('filename version datadir logsdir'.split()):
1281 self.assertIsInstance(getattr(juju_str, name), str)
1282 self.assertIsInstance(getattr(juju_unicode, name), unicode)
1283
1284@@ -192,8 +208,8 @@
1285 with self.assertRaises(ValueError):
1286 FakeJuju("/fake-juju", "", "/my/juju/home")
1287
1288- def test_missing_cfgdir(self):
1289- """FakeJuju() fails if cfgdir is None or empty."""
1290+ def test_missing_datadir(self):
1291+ """FakeJuju() fails if datadir is None or empty."""
1292 with self.assertRaises(ValueError):
1293 FakeJuju("/fake-juju", "1.25.6", None)
1294 with self.assertRaises(ValueError):
1295@@ -226,44 +242,105 @@
1296 def test_cli_full(self):
1297 """FakeJuju.cli() works correctly when given all args."""
1298 juju = FakeJuju("/fake-juju", "1.25.6", "/x")
1299- cli = juju.cli({"SPAM": "eggs"})
1300+ cli = juju.cli("/y", {"SPAM": "eggs"})
1301
1302 self.assertEqual(
1303 cli._exe,
1304 Executable("/fake-juju", {
1305 "SPAM": "eggs",
1306+ "FAKE_JUJU_DATA_DIR": "/x",
1307 "FAKE_JUJU_FAILURES": "/x/juju-failures",
1308 "FAKE_JUJU_LOGS_DIR": "/x",
1309- "JUJU_HOME": "/x",
1310+ "JUJU_HOME": "/y",
1311 }),
1312 )
1313
1314 def test_cli_minimal(self):
1315 """FakeJuju.cli() works correctly when given minimal args."""
1316 juju = FakeJuju("/fake-juju", "1.25.6", "/x")
1317- cli = juju.cli()
1318+ cli = juju.cli("/y")
1319
1320 self.assertEqual(
1321 cli._exe,
1322 Executable("/fake-juju", dict(os.environ, **{
1323+ "FAKE_JUJU_DATA_DIR": "/x",
1324 "FAKE_JUJU_FAILURES": "/x/juju-failures",
1325 "FAKE_JUJU_LOGS_DIR": "/x",
1326- "JUJU_HOME": "/x",
1327+ "JUJU_HOME": "/y",
1328 })),
1329 )
1330
1331 def test_cli_juju1(self):
1332 """FakeJuju.cli() works correctly for Juju 1.x."""
1333 juju = FakeJuju.from_version("1.25.6", "/x")
1334- cli = juju.cli()
1335+ cli = juju.cli("/y")
1336
1337- self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/x")
1338+ self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/y")
1339 self.assertIsInstance(cli._juju, _juju1.CLIHooks)
1340
1341 def test_cli_juju2(self):
1342 """FakeJuju.cli() works correctly for Juju 2.x."""
1343 juju = FakeJuju.from_version("2.0.0", "/x")
1344- cli = juju.cli()
1345+ cli = juju.cli("/y")
1346
1347- self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/x")
1348+ self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/y")
1349 self.assertIsInstance(cli._juju, _juju2.CLIHooks)
1350+
1351+ def test_bootstrap(self):
1352+ """FakeJuju.bootstrap() bootstraps from scratch using fake-juju."""
1353+ with tempdir() as datadir:
1354+ fakejuju = FakeJuju.from_version("1.25.6", datadir)
1355+ cfgdir = os.path.join(datadir, "juju")
1356+ cli, api_info = fakejuju.bootstrap("spam", cfgdir, "secret")
1357+ port = api_info[None].address.split(":")[-1]
1358+ cli.destroy_controller()
1359+
1360+ files = os.listdir(datadir)
1361+ files.extend(os.path.join("juju", name)
1362+ for name in os.listdir(cfgdir))
1363+ files.sort()
1364+ with open(os.path.join(cfgdir, "environments.yaml")) as envfile:
1365+ data = envfile.read()
1366+
1367+ self.maxDiff = None
1368+ self.assertEqual(api_info, {
1369+ 'controller': txjuju.cli.APIInfo(
1370+ endpoints=['localhost:' + port],
1371+ user='admin',
1372+ password='dummy-secret',
1373+ model_uuid='deadbeef-0bad-400d-8000-4b1d0d06f00d',
1374+ ),
1375+ None: txjuju.cli.APIInfo(
1376+ endpoints=['localhost:' + port],
1377+ user='admin',
1378+ password='dummy-secret',
1379+ model_uuid=None,
1380+ ),
1381+ })
1382+ self.assertEqual(sorted(files), [
1383+ 'cert.ca',
1384+ 'fake-juju.log',
1385+ 'fakejuju',
1386+ 'fifo',
1387+ 'juju',
1388+ 'juju/environments',
1389+ 'juju/environments.yaml',
1390+ ])
1391+ self.assertEqual(yaml.load(data), {
1392+ "environments": {
1393+ "spam": {
1394+ "admin-secret": "secret",
1395+ "default-series": "trusty",
1396+ "type": "dummy",
1397+ },
1398+ },
1399+ })
1400+
1401+
1402+@contextmanager
1403+def tempdir():
1404+ cfgdir = tempfile.mkdtemp("fakejuju-test-")
1405+ try:
1406+ yield cfgdir
1407+ finally:
1408+ shutil.rmtree(cfgdir)

Subscribers

People subscribed via source and target branches

to all changes: