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
=== modified file '1.25.6/fake-juju.go'
--- 1.25.6/fake-juju.go 2016-06-10 17:07:27 +0000
+++ 1.25.6/fake-juju.go 2016-10-19 23:31:13 +0000
@@ -50,31 +50,27 @@
50 coretesting.MgoTestPackage(t)50 coretesting.MgoTestPackage(t)
51}51}
5252
53type processInfo struct {
54 Username string
55 WorkDir string
56 EndpointAddr string
57 Uuid string
58 CACert string
59}
60
61func handleCommand(command string) error {53func handleCommand(command string) error {
54 filenames := newFakeJujuFilenames("", "", "")
62 if command == "bootstrap" {55 if command == "bootstrap" {
63 return bootstrap()56 return bootstrap(filenames)
64 }57 }
65 if command == "api-endpoints" {58 if command == "api-endpoints" {
66 return apiEndpoints()59 return apiEndpoints(filenames)
67 }60 }
68 if command == "api-info" {61 if command == "api-info" {
69 return apiInfo()62 return apiInfo(filenames)
70 }63 }
71 if command == "destroy-environment" {64 if command == "destroy-environment" {
72 return destroyEnvironment()65 return destroyEnvironment(filenames)
73 }66 }
74 return errors.New("command not found")67 return errors.New("command not found")
75}68}
7669
77func bootstrap() error {70func bootstrap(filenames fakejujuFilenames) error {
71 if err := filenames.ensureDirsExist(); err != nil {
72 return err
73 }
78 envName, config, err := environmentNameAndConfig()74 envName, config, err := environmentNameAndConfig()
79 if err != nil {75 if err != nil {
80 return err76 return err
@@ -90,10 +86,16 @@
90 return err86 return err
91 }87 }
92 command.Start()88 command.Start()
93 apiInfo, err := parseApiInfo(envName, stdout)89
90 result, err := parseApiInfo(stdout)
94 if err != nil {91 if err != nil {
95 return err92 return err
96 }93 }
94 if err := result.apply(filenames, envName); err != nil {
95 return err
96 }
97 apiInfo := result.apiInfo()
98
97 dialOpts := api.DialOpts{99 dialOpts := api.DialOpts{
98 DialAddressInterval: 50 * time.Millisecond,100 DialAddressInterval: 50 * time.Millisecond,
99 Timeout: 5 * time.Second,101 Timeout: 5 * time.Second,
@@ -123,8 +125,8 @@
123 return errors.New("invalid delta")125 return errors.New("invalid delta")
124}126}
125127
126func apiEndpoints() error {128func apiEndpoints(filenames fakejujuFilenames) error {
127 info, err := readProcessInfo()129 info, err := readProcessInfo(filenames)
128 if err != nil {130 if err != nil {
129 return err131 return err
130 }132 }
@@ -132,23 +134,23 @@
132 return nil134 return nil
133}135}
134136
135func apiInfo() error {137func apiInfo(filenames fakejujuFilenames) error {
136 info, err := readProcessInfo()138 info, err := readProcessInfo(filenames)
137 if err != nil {139 if err != nil {
138 return err140 return err
139 }141 }
140 username := strings.Replace(string(info.Username), "dummy-", "", 1)142 username := strings.Replace(string(info.Username), "dummy-", "", 1)
141 fmt.Printf("{\"user\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", username, info.Uuid, info.EndpointAddr)143 fmt.Printf("{\"user\": \"%s\", \"password\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", username, info.Password, info.Uuid, info.EndpointAddr)
142 return nil144 return nil
143}145}
144146
145func destroyEnvironment() error {147func destroyEnvironment(filenames fakejujuFilenames) error {
146 info, err := readProcessInfo()148 info, err := readProcessInfo(filenames)
147 if err != nil {149 if err != nil {
148 return err150 return err
149 }151 }
150 fifoPath := filepath.Join(info.WorkDir, "fifo")152 filenames = newFakeJujuFilenames("", "", info.WorkDir)
151 fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)153 fd, err := os.OpenFile(filenames.fifo(), os.O_APPEND|os.O_WRONLY, 0600)
152 if err != nil {154 if err != nil {
153 return err155 return err
154 }156 }
@@ -176,93 +178,206 @@
176 return envName, config, nil178 return envName, config, nil
177}179}
178180
179func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {181type processInfo struct {
182 Username string
183 Password string
184 WorkDir string
185 EndpointAddr string
186 Uuid string
187 CACert []byte
188}
189
190func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) {
191 infoPath := filenames.info()
192 data, err := ioutil.ReadFile(infoPath)
193 if err != nil {
194 return nil, err
195 }
196 info := &processInfo{}
197 err = goyaml.Unmarshal(data, info)
198 if err != nil {
199 return nil, err
200 }
201 return info, nil
202}
203
204func (info processInfo) write(infoPath string) error {
205 data, _ := goyaml.Marshal(&info)
206 if err := ioutil.WriteFile(infoPath, data, 0644); err != nil {
207 return err
208 }
209 return nil
210}
211
212type fakejujuFilenames struct {
213 datadir string
214 logsdir string
215}
216
217func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
218 if datadir == "" {
219 datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
220 if datadir == "" {
221 if jujucfgdir == "" {
222 jujucfgdir = os.Getenv("JUJU_HOME")
223 }
224 datadir = jujucfgdir
225 }
226 }
227 if logsdir == "" {
228 logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")
229 if logsdir == "" {
230 logsdir = datadir
231 }
232 }
233 return fakejujuFilenames{datadir, logsdir}
234}
235
236func (fj fakejujuFilenames) ensureDirsExist() error {
237 if err := os.MkdirAll(fj.datadir, 0755); err != nil {
238 return err
239 }
240 if err := os.MkdirAll(fj.logsdir, 0755); err != nil {
241 return err
242 }
243 return nil
244}
245
246func (fj fakejujuFilenames) info() string {
247 return filepath.Join(fj.datadir, "fakejuju")
248}
249
250func (fj fakejujuFilenames) logs() string {
251 return filepath.Join(fj.logsdir, "fake-juju.log")
252}
253
254func (fj fakejujuFilenames) fifo() string {
255 return filepath.Join(fj.datadir, "fifo")
256}
257
258func (fj fakejujuFilenames) cacert() string {
259 return filepath.Join(fj.datadir, "cert.ca")
260}
261
262type bootstrapResult struct {
263 dummyEnvName string
264 cfgdir string
265 uuid string
266 username string
267 password string
268 addresses []string
269 caCert []byte
270}
271
272func (br bootstrapResult) apiInfo() *api.Info {
273 return &api.Info{
274 Addrs: br.addresses,
275 Tag: names.NewLocalUserTag(br.username),
276 Password: br.password,
277 CACert: string(br.caCert),
278 EnvironTag: names.NewEnvironTag(br.uuid),
279 }
280}
281
282func (br bootstrapResult) fakeJujuInfo() *processInfo {
283 return &processInfo{
284 Username: br.username,
285 Password: br.password,
286 WorkDir: br.cfgdir,
287 EndpointAddr: br.addresses[0],
288 Uuid: br.uuid,
289 CACert: br.caCert,
290 }
291}
292
293func (br bootstrapResult) logsSymlink(target string) (string, string) {
294 if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" || os.Getenv("FAKE_JUJU_DATA_DIR") != "" {
295 return "", ""
296 }
297
298 filenames := newFakeJujuFilenames("", "", br.cfgdir)
299 source := filenames.logs()
300 return source, target
301}
302
303func (br bootstrapResult) jenvSymlink(jujuHome, envName string) (string, string) {
304 if jujuHome == "" || envName == "" {
305 return "", ""
306 }
307
308 source := filepath.Join(br.cfgdir, "environments", br.dummyEnvName+".jenv")
309 target := filepath.Join(jujuHome, "environments", envName+".jenv")
310 return source, target
311}
312
313func (br bootstrapResult) apply(filenames fakejujuFilenames, envName string) error {
314 if err := br.fakeJujuInfo().write(filenames.info()); err != nil {
315 return err
316 }
317
318 logsSource, logsTarget := br.logsSymlink(filenames.logs())
319 if logsSource != "" && logsTarget != "" {
320 if err := os.Symlink(logsSource, logsTarget); err != nil {
321 return err
322 }
323 }
324
325 jenvSource, jenvTarget := br.jenvSymlink(os.Getenv("JUJU_HOME"), envName)
326 if jenvSource != "" && jenvTarget != "" {
327 if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil {
328 return err
329 }
330 if err := os.Symlink(jenvSource, jenvTarget); err != nil {
331 return err
332 }
333 }
334
335 if err := ioutil.WriteFile(filenames.cacert(), br.caCert, 0644); err != nil {
336 return err
337 }
338
339 return nil
340}
341
342// See github.com/juju/juju/blob/juju/testing/conn.go.
343const dummyEnvName = "dummyenv"
344
345func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
180 buffer := bufio.NewReader(stdout)346 buffer := bufio.NewReader(stdout)
347
181 line, _, err := buffer.ReadLine()348 line, _, err := buffer.ReadLine()
182 if err != nil {349 if err != nil {
183 return nil, err350 return nil, err
184 }351 }
185 uuid := string(line)352 uuid := string(line)
186 environTag := names.NewEnvironTag(uuid)353
187 line, _, err = buffer.ReadLine()354 line, _, err = buffer.ReadLine()
188 if err != nil {355 if err != nil {
189 return nil, err356 return nil, err
190 }357 }
191 workDir := string(line)358 workDir := string(line)
359
192 store, err := configstore.NewDisk(workDir)360 store, err := configstore.NewDisk(workDir)
193 if err != nil {361 if err != nil {
194 return nil, err362 return nil, err
195 }363 }
196 info, err := store.ReadInfo("dummyenv")364 info, err := store.ReadInfo(dummyEnvName)
197 if err != nil {365 if err != nil {
198 return nil, err366 return nil, err
199 }367 }
368
200 credentials := info.APICredentials()369 credentials := info.APICredentials()
201 endpoint := info.APIEndpoint()370 endpoint := info.APIEndpoint()
202 addresses := endpoint.Addresses371 result := &bootstrapResult{
203 apiInfo := &api.Info{372 dummyEnvName: dummyEnvName,
204 Addrs: addresses,373 cfgdir: workDir,
205 Tag: names.NewLocalUserTag(credentials.User),374 uuid: uuid,
206 Password: credentials.Password,375 username: credentials.User,
207 CACert: endpoint.CACert,376 password: credentials.Password,
208 EnvironTag: environTag,377 addresses: endpoint.Addresses,
209 }378 caCert: []byte(endpoint.CACert),
210 err = writeProcessInfo(envName, &processInfo{379 }
211 Username: credentials.User,380 return result, nil
212 WorkDir: workDir,
213 EndpointAddr: addresses[0],
214 Uuid: uuid,
215 CACert: endpoint.CACert,
216 })
217 if err != nil {
218 return nil, err
219 }
220 return apiInfo, nil
221}
222
223func readProcessInfo() (*processInfo, error) {
224 infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju")
225 data, err := ioutil.ReadFile(infoPath)
226 if err != nil {
227 return nil, err
228 }
229 info := &processInfo{}
230 err = goyaml.Unmarshal(data, info)
231 if err != nil {
232 return nil, err
233 }
234 return info, nil
235}
236
237func writeProcessInfo(envName string, info *processInfo) error {
238 var err error
239 jujuHome := os.Getenv("JUJU_HOME")
240 infoPath := filepath.Join(jujuHome, "fakejuju")
241 logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
242 if logsDir == "" {
243 logsDir = jujuHome
244 }
245 logPath := filepath.Join(logsDir, "fake-juju.log")
246 caCertPath := filepath.Join(jujuHome, "cert.ca")
247 envPath := filepath.Join(jujuHome, "environments")
248 os.Mkdir(envPath, 0755)
249 jEnvPath := filepath.Join(envPath, envName+".jenv")
250 data, _ := goyaml.Marshal(info)
251 if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" {
252 err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
253 if err != nil {
254 return err
255 }
256 }
257 err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath)
258 if err != nil {
259 return err
260 }
261 err = ioutil.WriteFile(infoPath, data, 0644)
262 if err != nil {
263 return err
264 }
265 return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
266}381}
267382
268// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment383// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
@@ -307,7 +422,7 @@
307422
308 instanceCount int423 instanceCount int
309 machineStarted map[string]bool424 machineStarted map[string]bool
310 fifoPath string425 filenames fakejujuFilenames
311 logFile *os.File426 logFile *os.File
312}427}
313428
@@ -374,15 +489,11 @@
374 c.Assert(err, gc.IsNil)489 c.Assert(err, gc.IsNil)
375 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))490 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
376491
377 s.fifoPath = filepath.Join(jujuHome, "fifo")492 s.filenames = newFakeJujuFilenames("", "", jujuHome)
378 syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)493 syscall.Mknod(s.filenames.fifo(), syscall.S_IFIFO|0666, 0)
379494
380 // Logging495 // Logging
381 logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")496 logPath := s.filenames.logs()
382 if logsDir == "" {
383 logsDir = jujuHome
384 }
385 logPath := filepath.Join(logsDir, "fake-juju.log")
386 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)497 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
387 c.Assert(err, gc.IsNil)498 c.Assert(err, gc.IsNil)
388499
@@ -402,16 +513,17 @@
402}513}
403514
404func (s *FakeJujuSuite) TestStart(c *gc.C) {515func (s *FakeJujuSuite) TestStart(c *gc.C) {
516 fifoPath := s.filenames.fifo()
405 watcher := s.State.Watch()517 watcher := s.State.Watch()
406 go func() {518 go func() {
407 log.Println("Open commands FIFO", s.fifoPath)519 log.Println("Open commands FIFO", fifoPath)
408 fd, err := os.Open(s.fifoPath)520 fd, err := os.Open(fifoPath)
409 if err != nil {521 if err != nil {
410 log.Println("Failed to open commands FIFO")522 log.Println("Failed to open commands FIFO")
411 }523 }
412 c.Assert(err, gc.IsNil)524 c.Assert(err, gc.IsNil)
413 scanner := bufio.NewScanner(fd)525 scanner := bufio.NewScanner(fd)
414 log.Println("Listen for commands on FIFO", s.fifoPath)526 log.Println("Listen for commands on FIFO", fifoPath)
415 scanner.Scan()527 scanner.Scan()
416 log.Println("Stopping fake-juju")528 log.Println("Stopping fake-juju")
417 watcher.Stop()529 watcher.Stop()
418530
=== modified file '2.0-beta17/fake-juju.go'
--- 2.0-beta17/fake-juju.go 2016-09-15 19:05:50 +0000
+++ 2.0-beta17/fake-juju.go 2016-10-19 23:31:13 +0000
@@ -51,33 +51,31 @@
51 coretesting.MgoTestPackage(t)51 coretesting.MgoTestPackage(t)
52}52}
5353
54type processInfo struct {
55 WorkDir string
56 EndpointAddr string
57 Uuid string
58 CACert string
59}
60
61func handleCommand(command string) error {54func handleCommand(command string) error {
55 filenames := newFakeJujuFilenames("", "", "")
62 if command == "bootstrap" {56 if command == "bootstrap" {
63 return bootstrap()57 return bootstrap(filenames)
64 }58 }
65 if command == "show-controller" {59 if command == "show-controller" {
66 return apiInfo()60 return apiInfo(filenames)
67 }61 }
68 if command == "destroy-controller" {62 if command == "destroy-controller" {
69 return destroyEnvironment()63 return destroyController(filenames)
70 }64 }
71 return errors.New("command not found")65 return errors.New("command not found")
72}66}
7367
74func bootstrap() error {68func bootstrap(filenames fakejujuFilenames) error {
75 argc := len(os.Args)69 argc := len(os.Args)
76 if argc < 4 {70 if argc < 4 {
77 return errors.New(71 return errors.New(
78 "error: controller name and cloud name are required")72 "error: controller name and cloud name are required")
79 }73 }
80 envName := os.Args[argc-2]74 if err := filenames.ensureDirsExist(); err != nil {
75 return err
76 }
77 // XXX Swap the 2 args for juju-2.0-final.
78 controllerName := os.Args[argc-2]
81 command := exec.Command(os.Args[0])79 command := exec.Command(os.Args[0])
82 command.Env = os.Environ()80 command.Env = os.Environ()
83 command.Env = append(81 command.Env = append(
@@ -89,10 +87,16 @@
89 return err87 return err
90 }88 }
91 command.Start()89 command.Start()
92 apiInfo, err := parseApiInfo(envName, stdout)90
91 result, err := parseApiInfo(stdout)
93 if err != nil {92 if err != nil {
94 return err93 return err
95 }94 }
95 if err := result.apply(filenames, controllerName); err != nil {
96 return err
97 }
98 apiInfo := result.apiInfo()
99
96 dialOpts := api.DialOpts{100 dialOpts := api.DialOpts{
97 DialAddressInterval: 50 * time.Millisecond,101 DialAddressInterval: 50 * time.Millisecond,
98 Timeout: 5 * time.Second,102 Timeout: 5 * time.Second,
@@ -122,8 +126,8 @@
122 return errors.New("invalid delta")126 return errors.New("invalid delta")
123}127}
124128
125func apiInfo() error {129func apiInfo(filenames fakejujuFilenames) error {
126 info, err := readProcessInfo()130 info, err := readProcessInfo(filenames)
127 if err != nil {131 if err != nil {
128 return err132 return err
129 }133 }
@@ -140,13 +144,13 @@
140 return nil144 return nil
141}145}
142146
143func destroyEnvironment() error {147func destroyController(filenames fakejujuFilenames) error {
144 info, err := readProcessInfo()148 info, err := readProcessInfo(filenames)
145 if err != nil {149 if err != nil {
146 return err150 return err
147 }151 }
148 fifoPath := filepath.Join(info.WorkDir, "fifo")152 filenames = newFakeJujuFilenames("", "", info.WorkDir)
149 fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600)153 fd, err := os.OpenFile(filenames.fifo(), os.O_APPEND|os.O_WRONLY, 0600)
150 if err != nil {154 if err != nil {
151 return err155 return err
152 }156 }
@@ -158,13 +162,187 @@
158 return nil162 return nil
159}163}
160164
161func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) {165type processInfo struct {
166 WorkDir string
167 EndpointAddr string
168 Uuid string
169 CACert []byte
170}
171
172func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) {
173 infoPath := filenames.info()
174 data, err := ioutil.ReadFile(infoPath)
175 if err != nil {
176 return nil, err
177 }
178 info := &processInfo{}
179 err = goyaml.Unmarshal(data, info)
180 if err != nil {
181 return nil, err
182 }
183 return info, nil
184}
185
186func (info processInfo) write(infoPath string) error {
187 data, _ := goyaml.Marshal(&info)
188 if err := ioutil.WriteFile(infoPath, data, 0644); err != nil {
189 return err
190 }
191 return nil
192}
193
194type fakejujuFilenames struct {
195 datadir string
196 logsdir string
197}
198
199func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames {
200 if datadir == "" {
201 datadir = os.Getenv("FAKE_JUJU_DATA_DIR")
202 if datadir == "" {
203 if jujucfgdir == "" {
204 jujucfgdir = os.Getenv("JUJU_DATA")
205 }
206 datadir = jujucfgdir
207 }
208 }
209 if logsdir == "" {
210 logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR")
211 if logsdir == "" {
212 logsdir = datadir
213 }
214 }
215 return fakejujuFilenames{datadir, logsdir}
216}
217
218func (fj fakejujuFilenames) ensureDirsExist() error {
219 if err := os.MkdirAll(fj.datadir, 0755); err != nil {
220 return err
221 }
222 if err := os.MkdirAll(fj.logsdir, 0755); err != nil {
223 return err
224 }
225 return nil
226}
227
228func (fj fakejujuFilenames) info() string {
229 return filepath.Join(fj.datadir, "fakejuju")
230}
231
232func (fj fakejujuFilenames) logs() string {
233 return filepath.Join(fj.logsdir, "fake-juju.log")
234}
235
236func (fj fakejujuFilenames) fifo() string {
237 return filepath.Join(fj.datadir, "fifo")
238}
239
240func (fj fakejujuFilenames) cacert() string {
241 return filepath.Join(fj.datadir, "cert.ca")
242}
243
244type bootstrapResult struct {
245 dummyControllerName string
246 cfgdir string
247 uuid string
248 username string
249 password string
250 addresses []string
251 caCert []byte
252}
253
254func (br bootstrapResult) apiInfo() *api.Info {
255 return &api.Info{
256 Addrs: br.addresses,
257 Tag: names.NewUserTag(br.username),
258 Password: br.password,
259 CACert: string(br.caCert),
260 ModelTag: names.NewModelTag(br.uuid),
261 }
262}
263
264func (br bootstrapResult) fakeJujuInfo() *processInfo {
265 return &processInfo{
266 WorkDir: br.cfgdir,
267 EndpointAddr: br.addresses[0],
268 Uuid: br.uuid,
269 CACert: br.caCert,
270 }
271}
272
273func (br bootstrapResult) logsSymlink(target string) (string, string) {
274 if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" {
275 return "", ""
276 }
277
278 filenames := newFakeJujuFilenames("", "", br.cfgdir)
279 source := filenames.logs()
280 return source, target
281}
282
283func (br bootstrapResult) apply(filenames fakejujuFilenames, controllerName string) error {
284 if err := br.fakeJujuInfo().write(filenames.info()); err != nil {
285 return err
286 }
287
288 logsSource, logsTarget := br.logsSymlink(filenames.logs())
289 if logsSource != "" && logsTarget != "" {
290 if err := os.Symlink(logsSource, logsTarget); err != nil {
291 return err
292 }
293 }
294
295 if err := br.copyConfig(os.Getenv("JUJU_DATA"), controllerName); err != nil {
296 return err
297 }
298
299 if err := ioutil.WriteFile(filenames.cacert(), br.caCert, 0644); err != nil {
300 return err
301 }
302
303 return nil
304}
305
306func (br bootstrapResult) copyConfig(cfgdir, controllerName string) error {
307 for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} {
308 source := filepath.Join(br.cfgdir, name)
309 target := filepath.Join(cfgdir, name)
310
311 input, err := ioutil.ReadFile(source)
312 if err != nil {
313 return err
314 }
315 // Generated configuration by test fixtures has the controller name
316 // hard-coded to "kontroll". A simple replace should fix this for
317 // clients using this config and expecting a specific controller
318 // name.
319 output := strings.Replace(string(input), dummyControllerName, controllerName, -1)
320 err = ioutil.WriteFile(target, []byte(output), 0644)
321 if err != nil {
322 return err
323 }
324 }
325
326 current := filepath.Join(cfgdir, "current-controller")
327 if err := ioutil.WriteFile(current, []byte(controllerName), 0644); err != nil {
328 return err
329 }
330
331 return nil
332}
333
334// See github.com/juju/juju/blob/juju/testing/conn.go.
335const dummyControllerName = "kontroll"
336
337func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) {
162 buffer := bufio.NewReader(stdout)338 buffer := bufio.NewReader(stdout)
339
163 line, _, err := buffer.ReadLine()340 line, _, err := buffer.ReadLine()
164 if err != nil {341 if err != nil {
165 return nil, err342 return nil, err
166 }343 }
167 uuid := string(line)344 uuid := string(line)
345
168 line, _, err = buffer.ReadLine()346 line, _, err = buffer.ReadLine()
169 if err != nil {347 if err != nil {
170 return nil, err348 return nil, err
@@ -175,8 +353,8 @@
175 store := jujuclient.NewFileClientStore()353 store := jujuclient.NewFileClientStore()
176 // hard-coded value in juju testing354 // hard-coded value in juju testing
177 // This will be replaced in JUJU_DATA copy of the juju client config.355 // This will be replaced in JUJU_DATA copy of the juju client config.
178 currentController := "kontroll"356 currentController := dummyControllerName
179 one, err := store.ControllerByName("kontroll")357 one, err := store.ControllerByName(currentController)
180 if err != nil {358 if err != nil {
181 return nil, err359 return nil, err
182 }360 }
@@ -185,108 +363,17 @@
185 if err != nil {363 if err != nil {
186 return nil, err364 return nil, err
187 }365 }
188 apiInfo := &api.Info{366
189 Addrs: one.APIEndpoints,367 result := &bootstrapResult{
190 Tag: names.NewUserTag(accountDetails.User),368 dummyControllerName: dummyControllerName,
191 Password: accountDetails.Password,369 cfgdir: workDir,
192 CACert: one.CACert,370 uuid: uuid,
193 ModelTag: names.NewModelTag(uuid),371 username: accountDetails.User,
194 }372 password: accountDetails.Password,
195373 addresses: one.APIEndpoints,
196 err = writeProcessInfo(envName, &processInfo{374 caCert: []byte(one.CACert),
197 WorkDir: workDir,375 }
198 EndpointAddr: one.APIEndpoints[0],376 return result, nil
199 Uuid: uuid,
200 CACert: one.CACert,
201 })
202 if err != nil {
203 return nil, err
204 }
205 return apiInfo, nil
206}
207
208func readProcessInfo() (*processInfo, error) {
209 infoPath := filepath.Join(os.Getenv("JUJU_DATA"), "fakejuju")
210 data, err := ioutil.ReadFile(infoPath)
211 if err != nil {
212 return nil, err
213 }
214 info := &processInfo{}
215 err = goyaml.Unmarshal(data, info)
216 if err != nil {
217 return nil, err
218 }
219 return info, nil
220}
221
222func writeProcessInfo(envName string, info *processInfo) error {
223 var err error
224 jujuHome := os.Getenv("JUJU_DATA")
225 infoPath := filepath.Join(jujuHome, "fakejuju")
226 logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")
227 if logsDir == "" {
228 logsDir = jujuHome
229 }
230 logPath := filepath.Join(logsDir, "fake-juju.log")
231 caCertPath := filepath.Join(jujuHome, "cert.ca")
232 data, _ := goyaml.Marshal(info)
233 if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" {
234 err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath)
235 if err != nil {
236 return err
237 }
238 }
239
240 err = copyClientConfig(
241 filepath.Join(info.WorkDir, "controllers.yaml"),
242 filepath.Join(jujuHome, "controllers.yaml"),
243 envName)
244 if err != nil {
245 return err
246 }
247 err = copyClientConfig(
248 filepath.Join(info.WorkDir, "models.yaml"),
249 filepath.Join(jujuHome, "models.yaml"),
250 envName)
251 if err != nil {
252 return err
253 }
254 err = copyClientConfig(
255 filepath.Join(info.WorkDir, "accounts.yaml"),
256 filepath.Join(jujuHome, "accounts.yaml"),
257 envName)
258 if err != nil {
259 return err
260 }
261 err = ioutil.WriteFile(
262 filepath.Join(jujuHome, "current-controller"),
263 []byte(envName), 0644)
264 if err != nil {
265 return err
266 }
267
268 err = ioutil.WriteFile(infoPath, data, 0644)
269 if err != nil {
270 return err
271 }
272 return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644)
273}
274
275func copyClientConfig(src string, dst string, envName string) error {
276 input, err := ioutil.ReadFile(src)
277 if err != nil {
278 return err
279 }
280 // Generated configuration by test fixtures has the controller name
281 // hard-coded to "kontroll". A simple replace should fix this for
282 // clients using this config and expecting a specific controller
283 // name.
284 output := strings.Replace(string(input), "kontroll", envName, -1)
285 err = ioutil.WriteFile(dst, []byte(output), 0644)
286 if err != nil {
287 return err
288 }
289 return nil
290}377}
291378
292// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment379// Read the failures info file pointed by the FAKE_JUJU_FAILURES environment
@@ -331,7 +418,7 @@
331418
332 instanceCount int419 instanceCount int
333 machineStarted map[string]bool420 machineStarted map[string]bool
334 fifoPath string421 filenames fakejujuFilenames
335 logFile *os.File422 logFile *os.File
336}423}
337424
@@ -398,20 +485,16 @@
398 c.Assert(err, gc.IsNil)485 c.Assert(err, gc.IsNil)
399 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))486 os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))
400487
401 s.fifoPath = filepath.Join(jujuHome, "fifo")488 s.filenames = newFakeJujuFilenames("", "", jujuHome)
402 syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0)489 syscall.Mknod(s.filenames.fifo(), syscall.S_IFIFO|0666, 0)
403490
404 // Logging491 // Logging
405 logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR")492 logPath := s.filenames.logs()
406 if logsDir == "" {
407 logsDir = jujuHome
408 }
409 logPath := filepath.Join(logsDir, "fake-juju.log")
410 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)493 s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
411 c.Assert(err, gc.IsNil)494 c.Assert(err, gc.IsNil)
412495
413 log.SetOutput(s.logFile)496 log.SetOutput(s.logFile)
414 log.Println("Started fake-juju at", jujuHome)497 log.Println("Started fake-juju at ", jujuHome)
415498
416}499}
417500
@@ -423,16 +506,17 @@
423}506}
424507
425func (s *FakeJujuSuite) TestStart(c *gc.C) {508func (s *FakeJujuSuite) TestStart(c *gc.C) {
509 fifoPath := s.filenames.fifo()
426 watcher := s.State.Watch()510 watcher := s.State.Watch()
427 go func() {511 go func() {
428 log.Println("Open commands FIFO", s.fifoPath)512 log.Println("Open commands FIFO", fifoPath)
429 fd, err := os.Open(s.fifoPath)513 fd, err := os.Open(fifoPath)
430 if err != nil {514 if err != nil {
431 log.Println("Failed to open commands FIFO")515 log.Println("Failed to open commands FIFO")
432 }516 }
433 c.Assert(err, gc.IsNil)517 c.Assert(err, gc.IsNil)
434 scanner := bufio.NewScanner(fd)518 scanner := bufio.NewScanner(fd)
435 log.Println("Listen for commands on FIFO", s.fifoPath)519 log.Println("Listen for commands on FIFO", fifoPath)
436 scanner.Scan()520 scanner.Scan()
437 log.Println("Stopping fake-juju")521 log.Println("Stopping fake-juju")
438 watcher.Stop()522 watcher.Stop()
439523
=== modified file 'Makefile'
--- Makefile 2016-09-20 18:26:47 +0000
+++ Makefile 2016-10-19 23:31:13 +0000
@@ -11,7 +11,7 @@
11INSTALLDIR = $(DESTDIR)/usr/bin11INSTALLDIR = $(DESTDIR)/usr/bin
12INSTALLED = $(INSTALLDIR)/fake-juju-$(JUJU_VERSION)12INSTALLED = $(INSTALLDIR)/fake-juju-$(JUJU_VERSION)
1313
14$(JUJU_VERSION)/$(JUJU_VERSION):14$(JUJU_VERSION)/$(JUJU_VERSION): $(JUJU_VERSION)/fake-juju.go
15 case $(JUJU_VERSION) in \15 case $(JUJU_VERSION) in \
16 1.*) $(MAKE) build-common PATH=$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\16 1.*) $(MAKE) build-common PATH=$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\
17 2.*) $(MAKE) build-common PATH=/usr/lib/go-$(GO_VERSION)/bin:$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\17 2.*) $(MAKE) build-common PATH=/usr/lib/go-$(GO_VERSION)/bin:$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\
1818
=== modified file 'python/Makefile'
--- python/Makefile 2016-10-06 21:44:31 +0000
+++ python/Makefile 2016-10-19 23:31:13 +0000
@@ -6,4 +6,4 @@
66
7.PHONY: install-dev7.PHONY: install-dev
8install-dev:8install-dev:
9 ln -s $(shell pwd)/fakejuju /usr/local/lib/python2.7/dist-packages/fakejuju9 ln -snv $(shell pwd)/fakejuju /usr/local/lib/python2.7/dist-packages/fakejuju
1010
=== modified file 'python/fakejuju/fakejuju.py'
--- python/fakejuju/fakejuju.py 2016-10-17 15:54:59 +0000
+++ python/fakejuju/fakejuju.py 2016-10-19 23:31:13 +0000
@@ -2,6 +2,7 @@
22
3import os.path3import os.path
44
5import txjuju
5import txjuju.cli6import txjuju.cli
67
7from .failures import Failures8from .failures import Failures
@@ -23,15 +24,17 @@
23 return os.path.join(bindir, filename)24 return os.path.join(bindir, filename)
2425
2526
26def set_envvars(envvars, failures_filename=None, logsdir=None):27def set_envvars(envvars, datadir=None, failures_filename=None, logsdir=None):
27 """Return the environment variables with which to run fake-juju.28 """Return the environment variables with which to run fake-juju.
2829
29 @param envvars: The env dict to update.30 @param envvars: The env dict to update.
31 @param datadir: The fake-juju data directory.
30 @param failures_filename: The path to the failures file that32 @param failures_filename: The path to the failures file that
31 fake-juju will use.33 fake-juju will use.
32 @params logsdir: The path to the directory where fake-juju will34 @params logsdir: The path to the directory where fake-juju will
33 write its log files.35 write its log files.
34 """36 """
37 envvars["FAKE_JUJU_DATA_DIR"] = datadir or ""
35 envvars["FAKE_JUJU_FAILURES"] = failures_filename or ""38 envvars["FAKE_JUJU_FAILURES"] = failures_filename or ""
36 envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or ""39 envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or ""
3740
@@ -40,46 +43,47 @@
40 """The fundamental details for fake-juju."""43 """The fundamental details for fake-juju."""
4144
42 @classmethod45 @classmethod
43 def from_version(cls, version, cfgdir,46 def from_version(cls, version, datadir,
44 logsdir=None, failuresdir=None, bindir=None):47 logsdir=None, failuresdir=None, bindir=None):
45 """Return a new instance given the provided information.48 """Return a new instance given the provided information.
4649
47 @param version: The Juju version to fake.50 @param version: The Juju version to fake.
48 @param cfgdir: The "juju home" directory to use.51 @param datadir: The directory in which to store files specific
52 to fake-juju.
49 @param logsdir: The directory where logs will be written.53 @param logsdir: The directory where logs will be written.
50 This defaults to cfgdir.54 This defaults to datadir.
51 @params failuresdir: The directory where failure injection55 @params failuresdir: The directory where failure injection
52 is managed.56 is managed.
53 @param bindir: The directory containing the fake-juju binary.57 @param bindir: The directory containing the fake-juju binary.
54 This defaults to /usr/bin.58 This defaults to /usr/bin.
55 """59 """
56 if logsdir is None:
57 logsdir = cfgdir
58 if failuresdir is None:60 if failuresdir is None:
59 failuresdir = cfgdir61 failuresdir = datadir
60 filename = get_filename(version, bindir=bindir)62 filename = get_filename(version, bindir=bindir)
61 failures = Failures(failuresdir)63 failures = Failures(failuresdir)
62 return cls(filename, version, cfgdir, logsdir, failures)64 return cls(filename, version, datadir, logsdir, failures)
6365
64 def __init__(self, filename, version, cfgdir, logsdir=None, failures=None):66 def __init__(self, filename, version, datadir,
67 logsdir=None, failures=None):
65 """68 """
66 @param filename: The path to the fake-juju binary.69 @param filename: The path to the fake-juju binary.
67 @param version: The Juju version to fake.70 @param version: The Juju version to fake.
68 @param cfgdir: The "juju home" directory to use.71 @param datadir: The directory in which to store files specific
72 to fake-juju.
69 @param logsdir: The directory where logs will be written.73 @param logsdir: The directory where logs will be written.
70 This defaults to cfgdir.74 This defaults to datadir.
71 @param failures: The set of fake-juju failures to use.75 @param failures: The set of fake-juju failures to use.
72 """76 """
73 logsdir = logsdir if logsdir is not None else cfgdir77 logsdir = logsdir if logsdir is not None else datadir
74 if failures is None and cfgdir:78 if failures is None and datadir:
75 failures = Failures(cfgdir)79 failures = Failures(datadir)
7680
77 if not filename:81 if not filename:
78 raise ValueError("missing filename")82 raise ValueError("missing filename")
79 if not version:83 if not version:
80 raise ValueError("missing version")84 raise ValueError("missing version")
81 if not cfgdir:85 if not datadir:
82 raise ValueError("missing cfgdir")86 raise ValueError("missing datadir")
83 if not logsdir:87 if not logsdir:
84 raise ValueError("missing logsdir")88 raise ValueError("missing logsdir")
85 if failures is None:89 if failures is None:
@@ -87,7 +91,7 @@
8791
88 self.filename = filename92 self.filename = filename
89 self.version = version93 self.version = version
90 self.cfgdir = cfgdir94 self.datadir = datadir
91 self.logsdir = logsdir95 self.logsdir = logsdir
92 self.failures = failures96 self.failures = failures
9397
@@ -99,19 +103,19 @@
99 @property103 @property
100 def infofile(self):104 def infofile(self):
101 """The path to fake-juju's data cache."""105 """The path to fake-juju's data cache."""
102 return os.path.join(self.cfgdir, "fakejuju")106 return os.path.join(self.datadir, "fakejuju")
103107
104 @property108 @property
105 def fifo(self):109 def fifo(self):
106 """The path to the fifo file that triggers shutdown."""110 """The path to the fifo file that triggers shutdown."""
107 return os.path.join(self.cfgdir, "fifo")111 return os.path.join(self.datadir, "fifo")
108112
109 @property113 @property
110 def cacertfile(self):114 def cacertfile(self):
111 """The path to the API server's certificate."""115 """The path to the API server's certificate."""
112 return os.path.join(self.cfgdir, "cert.ca")116 return os.path.join(self.datadir, "cert.ca")
113117
114 def cli(self, envvars=None):118 def cli(self, cfgdir, envvars=None):
115 """Return the txjuju.cli.CLI for this fake-juju.119 """Return the txjuju.cli.CLI for this fake-juju.
116120
117 Currently fake-juju supports only the following juju subcommands:121 Currently fake-juju supports only the following juju subcommands:
@@ -123,10 +127,23 @@
123 Note that passwords are always omited, even if requested.127 Note that passwords are always omited, even if requested.
124 * api-endpoints128 * api-endpoints
125 * destroy-environment129 * destroy-environment
130
131 Note that fake-juju ignores local config files.
126 """132 """
127 if envvars is None:133 if envvars is None:
128 envvars = os.environ134 envvars = os.environ
129 envvars = dict(envvars)135 envvars = dict(envvars)
130 set_envvars(envvars, self.failures._filename, self.logsdir)136 set_envvars(
137 envvars, self.datadir, self.failures._filename, self.logsdir)
131 return txjuju.cli.CLI.from_version(138 return txjuju.cli.CLI.from_version(
132 self.filename, self.version, self.cfgdir, envvars)139 self.filename, self.version, cfgdir, envvars)
140
141 def bootstrap(self, name, cfgdir, admin_secret=None):
142 """Return the CLI and APIInfo after bootstrapping from scratch."""
143 from . import get_bootstrap_spec
144 spec = get_bootstrap_spec(name, admin_secret)
145 cfgfile = txjuju.prepare_for_bootstrap(spec, self.version, cfgdir)
146 cli = self.cli(cfgdir)
147 cli.bootstrap(spec, cfgfile=cfgfile)
148 api_info = cli.api_info(spec.name)
149 return cli, api_info
133150
=== modified file 'python/fakejuju/testing.py'
--- python/fakejuju/testing.py 2016-10-06 22:51:41 +0000
+++ python/fakejuju/testing.py 2016-10-19 23:31:13 +0000
@@ -1,6 +1,5 @@
1# Copyright 2016 Canonical Limited. All rights reserved.1# Copyright 2016 Canonical Limited. All rights reserved.
22
3import txjuju
4from fixtures import Fixture, TempDir3from fixtures import Fixture, TempDir
5from testtools.content import content_from_file4from testtools.content import content_from_file
65
@@ -40,29 +39,16 @@
40 def setUp(self):39 def setUp(self):
41 super(FakeJujuFixture, self).setUp()40 super(FakeJujuFixture, self).setUp()
42 self._juju_home = self.useFixture(TempDir())41 self._juju_home = self.useFixture(TempDir())
43 self._juju = fakejuju.FakeJuju.make(42 self.fakejuju = fakejuju.FakeJuju.from_version(
44 self._juju_home.path, self._version, self._logs_dir)43 self._version, self._juju_home.path, self._logs_dir)
4544
46 if not self._logs_dir:45 if not self._logs_dir:
47 # Attach logs as testtools details.46 # Attach logs as testtools details.
48 self.addDetail("log-file", content_from_file(self._juju.logfile))47 self.addDetail("log-file", content_from_file(self._juju.logfile))
4948
50 spec = fakejuju.get_bootstrap_spec(self._controller, self._password)49 self._juju, self.all_api_info = self.fakejuju.bootstrap(
51 cfgfile = txjuju.prepare_for_bootstrap(50 self._controller, self._password)
52 spec, self._version, self._juju_home)
53 cli = self._juju.cli()
54 cli.bootstrap(spec, cfgfile=cfgfile)
55 api_info = cli.api_info(spec.name)
56 if self._version.startswith("1."):
57 # fake-juju doesn't give us the password, so we have to
58 # set it here.
59 api_info = api_info._replace(password=self._password)
60 self.api_info = api_info
6151
62 def cleanUp(self):52 def cleanUp(self):
63 self._juju.destroy_controller(self._controller)53 self._juju.destroy_controller(self._controller)
64 super(FakeJujuFixture, self).cleanUp()54 super(FakeJujuFixture, self).cleanUp()
65
66 def add_failure(self, entity):
67 """Make the given entity fail with an error status."""
68 self._juju.failures.fail_entity(entity)
6955
=== modified file 'python/fakejuju/tests/test_fakejuju.py'
--- python/fakejuju/tests/test_fakejuju.py 2016-10-17 15:36:15 +0000
+++ python/fakejuju/tests/test_fakejuju.py 2016-10-19 23:31:13 +0000
@@ -1,10 +1,15 @@
1# Copyright 2016 Canonical Limited. All rights reserved.1# Copyright 2016 Canonical Limited. All rights reserved.
22
3from contextlib import contextmanager
3import os4import os
5import shutil
6import tempfile
4import unittest7import unittest
58
6from txjuju import _juju1, _juju29from txjuju import _juju1, _juju2
7from txjuju._utils import Executable10from txjuju._utils import Executable
11import txjuju.cli
12import yaml
813
9from fakejuju.failures import Failures14from fakejuju.failures import Failures
10from fakejuju.fakejuju import get_filename, set_envvars, FakeJuju15from fakejuju.fakejuju import get_filename, set_envvars, FakeJuju
@@ -44,9 +49,10 @@
44 def test_all_args(self):49 def test_all_args(self):
45 """set_envvars() works correctly when given all args."""50 """set_envvars() works correctly when given all args."""
46 envvars = {}51 envvars = {}
47 set_envvars(envvars, "/spam/failures", "/eggs/logsdir")52 set_envvars(envvars, "/spam", "/spam/failures", "/eggs/logsdir")
4853
49 self.assertEqual(envvars, {54 self.assertEqual(envvars, {
55 "FAKE_JUJU_DATA_DIR": "/spam",
50 "FAKE_JUJU_FAILURES": "/spam/failures",56 "FAKE_JUJU_FAILURES": "/spam/failures",
51 "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir",57 "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir",
52 })58 })
@@ -57,6 +63,7 @@
57 set_envvars(envvars)63 set_envvars(envvars)
5864
59 self.assertEqual(envvars, {65 self.assertEqual(envvars, {
66 "FAKE_JUJU_DATA_DIR": "",
60 "FAKE_JUJU_FAILURES": "",67 "FAKE_JUJU_FAILURES": "",
61 "FAKE_JUJU_LOGS_DIR": "",68 "FAKE_JUJU_LOGS_DIR": "",
62 })69 })
@@ -64,9 +71,10 @@
64 def test_start_empty(self):71 def test_start_empty(self):
65 """set_envvars() sets all values on an empty dict."""72 """set_envvars() sets all values on an empty dict."""
66 envvars = {}73 envvars = {}
67 set_envvars(envvars, "x", "y")74 set_envvars(envvars, "w", "x", "y")
6875
69 self.assertEqual(envvars, {76 self.assertEqual(envvars, {
77 "FAKE_JUJU_DATA_DIR": "w",
70 "FAKE_JUJU_FAILURES": "x",78 "FAKE_JUJU_FAILURES": "x",
71 "FAKE_JUJU_LOGS_DIR": "y",79 "FAKE_JUJU_LOGS_DIR": "y",
72 })80 })
@@ -74,10 +82,11 @@
74 def test_no_collisions(self):82 def test_no_collisions(self):
75 """set_envvars() sets all values when none are set yet."""83 """set_envvars() sets all values when none are set yet."""
76 envvars = {"SPAM": "eggs"}84 envvars = {"SPAM": "eggs"}
77 set_envvars(envvars, "x", "y")85 set_envvars(envvars, "w", "x", "y")
7886
79 self.assertEqual(envvars, {87 self.assertEqual(envvars, {
80 "SPAM": "eggs",88 "SPAM": "eggs",
89 "FAKE_JUJU_DATA_DIR": "w",
81 "FAKE_JUJU_FAILURES": "x",90 "FAKE_JUJU_FAILURES": "x",
82 "FAKE_JUJU_LOGS_DIR": "y",91 "FAKE_JUJU_LOGS_DIR": "y",
83 })92 })
@@ -85,12 +94,14 @@
85 def test_empty_to_nonempty(self):94 def test_empty_to_nonempty(self):
86 """set_envvars() updates empty values."""95 """set_envvars() updates empty values."""
87 envvars = {96 envvars = {
97 "FAKE_JUJU_DATA_DIR": "",
88 "FAKE_JUJU_FAILURES": "",98 "FAKE_JUJU_FAILURES": "",
89 "FAKE_JUJU_LOGS_DIR": "",99 "FAKE_JUJU_LOGS_DIR": "",
90 }100 }
91 set_envvars(envvars, "x", "y")101 set_envvars(envvars, "w", "x", "y")
92102
93 self.assertEqual(envvars, {103 self.assertEqual(envvars, {
104 "FAKE_JUJU_DATA_DIR": "w",
94 "FAKE_JUJU_FAILURES": "x",105 "FAKE_JUJU_FAILURES": "x",
95 "FAKE_JUJU_LOGS_DIR": "y",106 "FAKE_JUJU_LOGS_DIR": "y",
96 })107 })
@@ -98,12 +109,14 @@
98 def test_nonempty_to_nonempty(self):109 def test_nonempty_to_nonempty(self):
99 """set_envvars() overwrites existing values."""110 """set_envvars() overwrites existing values."""
100 envvars = {111 envvars = {
112 "FAKE_JUJU_DATA_DIR": "spam",
101 "FAKE_JUJU_FAILURES": "spam",113 "FAKE_JUJU_FAILURES": "spam",
102 "FAKE_JUJU_LOGS_DIR": "ham",114 "FAKE_JUJU_LOGS_DIR": "ham",
103 }115 }
104 set_envvars(envvars, "x", "y")116 set_envvars(envvars, "w", "x", "y")
105117
106 self.assertEqual(envvars, {118 self.assertEqual(envvars, {
119 "FAKE_JUJU_DATA_DIR": "w",
107 "FAKE_JUJU_FAILURES": "x",120 "FAKE_JUJU_FAILURES": "x",
108 "FAKE_JUJU_LOGS_DIR": "y",121 "FAKE_JUJU_LOGS_DIR": "y",
109 })122 })
@@ -111,12 +124,14 @@
111 def test_nonempty_to_empty(self):124 def test_nonempty_to_empty(self):
112 """set_envvars() with no args "unsets" existing values."""125 """set_envvars() with no args "unsets" existing values."""
113 envvars = {126 envvars = {
127 "FAKE_JUJU_DATA_DIR": "w",
114 "FAKE_JUJU_FAILURES": "x",128 "FAKE_JUJU_FAILURES": "x",
115 "FAKE_JUJU_LOGS_DIR": "y",129 "FAKE_JUJU_LOGS_DIR": "y",
116 }130 }
117 set_envvars(envvars)131 set_envvars(envvars)
118132
119 self.assertEqual(envvars, {133 self.assertEqual(envvars, {
134 "FAKE_JUJU_DATA_DIR": "",
120 "FAKE_JUJU_FAILURES": "",135 "FAKE_JUJU_FAILURES": "",
121 "FAKE_JUJU_LOGS_DIR": "",136 "FAKE_JUJU_LOGS_DIR": "",
122 })137 })
@@ -131,7 +146,7 @@
131146
132 self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6")147 self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6")
133 self.assertEqual(juju.version, "1.25.6")148 self.assertEqual(juju.version, "1.25.6")
134 self.assertEqual(juju.cfgdir, "/a/juju/home")149 self.assertEqual(juju.datadir, "/a/juju/home")
135 self.assertEqual(juju.logsdir, "/logs/dir")150 self.assertEqual(juju.logsdir, "/logs/dir")
136 self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures")151 self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures")
137152
@@ -141,19 +156,20 @@
141156
142 self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6")157 self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6")
143 self.assertEqual(juju.version, "1.25.6")158 self.assertEqual(juju.version, "1.25.6")
144 self.assertEqual(juju.cfgdir, "/my/juju/home")159 self.assertEqual(juju.datadir, "/my/juju/home")
145 self.assertEqual(juju.logsdir, "/my/juju/home")160 self.assertEqual(juju.logsdir, "/my/juju/home")
146 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")161 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")
147162
148 def test_full(self):163 def test_full(self):
149 """FakeJuju() works correctly when given all args."""164 """FakeJuju() works correctly when given all args."""
150 cfgdir = "/my/juju/home"165 datadir = "/my/juju/home"
151 failures = Failures(cfgdir)166 failures = Failures(datadir)
152 juju = FakeJuju("/fake-juju", "1.25.6", cfgdir, "/some/logs", failures)167 juju = FakeJuju(
168 "/fake-juju", "1.25.6", datadir, "/some/logs", failures)
153169
154 self.assertEqual(juju.filename, "/fake-juju")170 self.assertEqual(juju.filename, "/fake-juju")
155 self.assertEqual(juju.version, "1.25.6")171 self.assertEqual(juju.version, "1.25.6")
156 self.assertEqual(juju.cfgdir, cfgdir)172 self.assertEqual(juju.datadir, datadir)
157 self.assertEqual(juju.logsdir, "/some/logs")173 self.assertEqual(juju.logsdir, "/some/logs")
158 self.assertIs(juju.failures, failures)174 self.assertIs(juju.failures, failures)
159175
@@ -163,7 +179,7 @@
163179
164 self.assertEqual(juju.filename, "/fake-juju")180 self.assertEqual(juju.filename, "/fake-juju")
165 self.assertEqual(juju.version, "1.25.6")181 self.assertEqual(juju.version, "1.25.6")
166 self.assertEqual(juju.cfgdir, "/my/juju/home")182 self.assertEqual(juju.datadir, "/my/juju/home")
167 self.assertEqual(juju.logsdir, "/my/juju/home")183 self.assertEqual(juju.logsdir, "/my/juju/home")
168 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")184 self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures")
169185
@@ -174,7 +190,7 @@
174 juju_unicode = FakeJuju(190 juju_unicode = FakeJuju(
175 u"/fake-juju", u"1.25.6", u"/x", u"/y", Failures(u"/..."))191 u"/fake-juju", u"1.25.6", u"/x", u"/y", Failures(u"/..."))
176192
177 for name in ('filename version cfgdir logsdir'.split()):193 for name in ('filename version datadir logsdir'.split()):
178 self.assertIsInstance(getattr(juju_str, name), str)194 self.assertIsInstance(getattr(juju_str, name), str)
179 self.assertIsInstance(getattr(juju_unicode, name), unicode)195 self.assertIsInstance(getattr(juju_unicode, name), unicode)
180196
@@ -192,8 +208,8 @@
192 with self.assertRaises(ValueError):208 with self.assertRaises(ValueError):
193 FakeJuju("/fake-juju", "", "/my/juju/home")209 FakeJuju("/fake-juju", "", "/my/juju/home")
194210
195 def test_missing_cfgdir(self):211 def test_missing_datadir(self):
196 """FakeJuju() fails if cfgdir is None or empty."""212 """FakeJuju() fails if datadir is None or empty."""
197 with self.assertRaises(ValueError):213 with self.assertRaises(ValueError):
198 FakeJuju("/fake-juju", "1.25.6", None)214 FakeJuju("/fake-juju", "1.25.6", None)
199 with self.assertRaises(ValueError):215 with self.assertRaises(ValueError):
@@ -226,44 +242,105 @@
226 def test_cli_full(self):242 def test_cli_full(self):
227 """FakeJuju.cli() works correctly when given all args."""243 """FakeJuju.cli() works correctly when given all args."""
228 juju = FakeJuju("/fake-juju", "1.25.6", "/x")244 juju = FakeJuju("/fake-juju", "1.25.6", "/x")
229 cli = juju.cli({"SPAM": "eggs"})245 cli = juju.cli("/y", {"SPAM": "eggs"})
230246
231 self.assertEqual(247 self.assertEqual(
232 cli._exe,248 cli._exe,
233 Executable("/fake-juju", {249 Executable("/fake-juju", {
234 "SPAM": "eggs",250 "SPAM": "eggs",
251 "FAKE_JUJU_DATA_DIR": "/x",
235 "FAKE_JUJU_FAILURES": "/x/juju-failures",252 "FAKE_JUJU_FAILURES": "/x/juju-failures",
236 "FAKE_JUJU_LOGS_DIR": "/x",253 "FAKE_JUJU_LOGS_DIR": "/x",
237 "JUJU_HOME": "/x",254 "JUJU_HOME": "/y",
238 }),255 }),
239 )256 )
240257
241 def test_cli_minimal(self):258 def test_cli_minimal(self):
242 """FakeJuju.cli() works correctly when given minimal args."""259 """FakeJuju.cli() works correctly when given minimal args."""
243 juju = FakeJuju("/fake-juju", "1.25.6", "/x")260 juju = FakeJuju("/fake-juju", "1.25.6", "/x")
244 cli = juju.cli()261 cli = juju.cli("/y")
245262
246 self.assertEqual(263 self.assertEqual(
247 cli._exe,264 cli._exe,
248 Executable("/fake-juju", dict(os.environ, **{265 Executable("/fake-juju", dict(os.environ, **{
266 "FAKE_JUJU_DATA_DIR": "/x",
249 "FAKE_JUJU_FAILURES": "/x/juju-failures",267 "FAKE_JUJU_FAILURES": "/x/juju-failures",
250 "FAKE_JUJU_LOGS_DIR": "/x",268 "FAKE_JUJU_LOGS_DIR": "/x",
251 "JUJU_HOME": "/x",269 "JUJU_HOME": "/y",
252 })),270 })),
253 )271 )
254272
255 def test_cli_juju1(self):273 def test_cli_juju1(self):
256 """FakeJuju.cli() works correctly for Juju 1.x."""274 """FakeJuju.cli() works correctly for Juju 1.x."""
257 juju = FakeJuju.from_version("1.25.6", "/x")275 juju = FakeJuju.from_version("1.25.6", "/x")
258 cli = juju.cli()276 cli = juju.cli("/y")
259277
260 self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/x")278 self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/y")
261 self.assertIsInstance(cli._juju, _juju1.CLIHooks)279 self.assertIsInstance(cli._juju, _juju1.CLIHooks)
262280
263 def test_cli_juju2(self):281 def test_cli_juju2(self):
264 """FakeJuju.cli() works correctly for Juju 2.x."""282 """FakeJuju.cli() works correctly for Juju 2.x."""
265 juju = FakeJuju.from_version("2.0.0", "/x")283 juju = FakeJuju.from_version("2.0.0", "/x")
266 cli = juju.cli()284 cli = juju.cli("/y")
267285
268 self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/x")286 self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/y")
269 self.assertIsInstance(cli._juju, _juju2.CLIHooks)287 self.assertIsInstance(cli._juju, _juju2.CLIHooks)
288
289 def test_bootstrap(self):
290 """FakeJuju.bootstrap() bootstraps from scratch using fake-juju."""
291 with tempdir() as datadir:
292 fakejuju = FakeJuju.from_version("1.25.6", datadir)
293 cfgdir = os.path.join(datadir, "juju")
294 cli, api_info = fakejuju.bootstrap("spam", cfgdir, "secret")
295 port = api_info[None].address.split(":")[-1]
296 cli.destroy_controller()
297
298 files = os.listdir(datadir)
299 files.extend(os.path.join("juju", name)
300 for name in os.listdir(cfgdir))
301 files.sort()
302 with open(os.path.join(cfgdir, "environments.yaml")) as envfile:
303 data = envfile.read()
304
305 self.maxDiff = None
306 self.assertEqual(api_info, {
307 'controller': txjuju.cli.APIInfo(
308 endpoints=['localhost:' + port],
309 user='admin',
310 password='dummy-secret',
311 model_uuid='deadbeef-0bad-400d-8000-4b1d0d06f00d',
312 ),
313 None: txjuju.cli.APIInfo(
314 endpoints=['localhost:' + port],
315 user='admin',
316 password='dummy-secret',
317 model_uuid=None,
318 ),
319 })
320 self.assertEqual(sorted(files), [
321 'cert.ca',
322 'fake-juju.log',
323 'fakejuju',
324 'fifo',
325 'juju',
326 'juju/environments',
327 'juju/environments.yaml',
328 ])
329 self.assertEqual(yaml.load(data), {
330 "environments": {
331 "spam": {
332 "admin-secret": "secret",
333 "default-series": "trusty",
334 "type": "dummy",
335 },
336 },
337 })
338
339
340@contextmanager
341def tempdir():
342 cfgdir = tempfile.mkdtemp("fakejuju-test-")
343 try:
344 yield cfgdir
345 finally:
346 shutil.rmtree(cfgdir)

Subscribers

People subscribed via source and target branches

to all changes: