Merge lp:~ericsnowcurrently/fake-juju/fake-juju-data-dir into lp:~landscape/fake-juju/trunk-old
- fake-juju-data-dir
- Merge into 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 |
Related bugs: |
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.
Commit message
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.
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) |