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