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