Merge lp:~ericsnowcurrently/fake-juju/juju-2.0-support into lp:~landscape/fake-juju/trunk-old
- juju-2.0-support
- Merge into trunk-old
Status: | Superseded |
---|---|
Proposed branch: | lp:~ericsnowcurrently/fake-juju/juju-2.0-support |
Merge into: | lp:~landscape/fake-juju/trunk-old |
Diff against target: |
2443 lines (+826/-902) 11 files modified
1.24.7/fake-juju.go (+0/-514) 1.25.6/fake-juju.go (+275/-109) 2.0.0/fake-juju.go (+287/-156) Makefile (+3/-3) patches/juju-core_1.24.7.patch (+0/-47) patches/juju-core_2.0.0.patch (+6/-6) python/Makefile (+1/-1) python/fakejuju/fakejuju.py (+44/-23) python/fakejuju/testing.py (+5/-19) python/fakejuju/tests/test_fakejuju.py (+204/-23) tests/test_fake.py (+1/-1) |
To merge this branch: | bzr merge lp:~ericsnowcurrently/fake-juju/juju-2.0-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
🤖 Landscape Builder | test results | Approve | |
Landscape | Pending | ||
Landscape | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2016-10-25.
Commit message
Description of the change
Update to juju 2.0.0.
Also drop support for 1.24.x.
Testing instructions:
Run make test
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) wrote : | # |
- 64. By Eric Snow
-
Add some comments about bootstrap.
- 65. By Eric Snow
-
Factor out waitForBootstra
pCompletion( ). - 66. By Eric Snow
-
Rename the command handler functions.
- 67. By Eric Snow
-
Factor out destroyControll
er(). - 68. By Eric Snow
-
Destroy the controller if bootstrap fails.
- 69. By Eric Snow
-
Factor out updateBootstrap
Result( ). - 70. By Eric Snow
-
Call copyConfig() before updating the bootstrap result.
- 71. By Eric Snow
-
Handle bootstrap error cleanup centrally.
- 72. By Eric Snow
-
Capture error output from the daemon.
- 73. By Eric Snow
-
Minor touch-ups to SetUpTest().
- 74. By Eric Snow
-
Use constants for the environment variable names.
- 75. By Eric Snow
-
Add a log file for jujud logs.
- 76. By Eric Snow
-
Factor out reportInfo().
- 77. By Eric Snow
-
Ensure that the daemon runs with FAKE_JUJU_DATA_DIR set.
- 78. By Eric Snow
-
Parse all the supported "juju bootstrap" args.
- 79. By Eric Snow
-
Always ensure that the Juju cfg dir exists.
- 80. By Eric Snow
-
Support a -v bootstrap arg.
- 81. By Eric Snow
-
Set up logging right *after* calling JujuConnSuite.
SetUpTest( ). - 82. By Eric Snow
-
Fix bootstrap arg order in a test.
- 83. By Eric Snow
-
Merge from trunk.
- 84. By Eric Snow
-
Copy some of the 2.0.0 tweaks over to 1.25.6.
- 85. By Eric Snow
-
Default to Juju 2.x in tests.
- 86. By Eric Snow
-
Pull a little more code from 2.0.0.
Unmerged revisions
Preview Diff
1 | === removed directory '1.24.7' | |||
2 | === removed file '1.24.7/fake-juju.go' | |||
3 | --- 1.24.7/fake-juju.go 2016-06-10 17:08:28 +0000 | |||
4 | +++ 1.24.7/fake-juju.go 1970-01-01 00:00:00 +0000 | |||
5 | @@ -1,514 +0,0 @@ | |||
6 | 1 | package main | ||
7 | 2 | |||
8 | 3 | import ( | ||
9 | 4 | "bufio" | ||
10 | 5 | "encoding/json" | ||
11 | 6 | "errors" | ||
12 | 7 | "fmt" | ||
13 | 8 | gc "gopkg.in/check.v1" | ||
14 | 9 | "io" | ||
15 | 10 | "io/ioutil" | ||
16 | 11 | "log" | ||
17 | 12 | "os" | ||
18 | 13 | "os/exec" | ||
19 | 14 | "path/filepath" | ||
20 | 15 | "strings" | ||
21 | 16 | "syscall" | ||
22 | 17 | "testing" | ||
23 | 18 | "time" | ||
24 | 19 | |||
25 | 20 | "github.com/juju/juju/agent" | ||
26 | 21 | "github.com/juju/juju/api" | ||
27 | 22 | "github.com/juju/juju/environs" | ||
28 | 23 | "github.com/juju/juju/environs/configstore" | ||
29 | 24 | "github.com/juju/juju/instance" | ||
30 | 25 | "github.com/juju/juju/juju/osenv" | ||
31 | 26 | jujutesting "github.com/juju/juju/juju/testing" | ||
32 | 27 | "github.com/juju/juju/network" | ||
33 | 28 | _ "github.com/juju/juju/provider/maas" | ||
34 | 29 | "github.com/juju/juju/state" | ||
35 | 30 | coretesting "github.com/juju/juju/testing" | ||
36 | 31 | "github.com/juju/juju/testing/factory" | ||
37 | 32 | "github.com/juju/juju/version" | ||
38 | 33 | "github.com/juju/names" | ||
39 | 34 | corecharm "gopkg.in/juju/charm.v5/charmrepo" | ||
40 | 35 | goyaml "gopkg.in/yaml.v1" | ||
41 | 36 | ) | ||
42 | 37 | |||
43 | 38 | func main() { | ||
44 | 39 | if len(os.Args) > 1 { | ||
45 | 40 | code := 0 | ||
46 | 41 | err := handleCommand(os.Args[1]) | ||
47 | 42 | if err != nil { | ||
48 | 43 | fmt.Println(err.Error()) | ||
49 | 44 | code = 1 | ||
50 | 45 | } | ||
51 | 46 | os.Exit(code) | ||
52 | 47 | } | ||
53 | 48 | t := &testing.T{} | ||
54 | 49 | coretesting.MgoTestPackage(t) | ||
55 | 50 | } | ||
56 | 51 | |||
57 | 52 | type processInfo struct { | ||
58 | 53 | WorkDir string | ||
59 | 54 | EndpointAddr string | ||
60 | 55 | Uuid string | ||
61 | 56 | CACert string | ||
62 | 57 | } | ||
63 | 58 | |||
64 | 59 | func handleCommand(command string) error { | ||
65 | 60 | if command == "bootstrap" { | ||
66 | 61 | return bootstrap() | ||
67 | 62 | } | ||
68 | 63 | if command == "api-endpoints" { | ||
69 | 64 | return apiEndpoints() | ||
70 | 65 | } | ||
71 | 66 | if command == "api-info" { | ||
72 | 67 | return apiInfo() | ||
73 | 68 | } | ||
74 | 69 | if command == "destroy-environment" { | ||
75 | 70 | return destroyEnvironment() | ||
76 | 71 | } | ||
77 | 72 | return errors.New("command not found") | ||
78 | 73 | } | ||
79 | 74 | |||
80 | 75 | func bootstrap() error { | ||
81 | 76 | envName, password, err := environmentNameAndPassword() | ||
82 | 77 | if err != nil { | ||
83 | 78 | return err | ||
84 | 79 | } | ||
85 | 80 | command := exec.Command(os.Args[0]) | ||
86 | 81 | command.Env = os.Environ() | ||
87 | 82 | command.Env = append(command.Env, "ADMIN_PASSWORD="+password) | ||
88 | 83 | stdout, err := command.StdoutPipe() | ||
89 | 84 | if err != nil { | ||
90 | 85 | return err | ||
91 | 86 | } | ||
92 | 87 | command.Start() | ||
93 | 88 | apiInfo, err := parseApiInfo(envName, stdout) | ||
94 | 89 | if err != nil { | ||
95 | 90 | return err | ||
96 | 91 | } | ||
97 | 92 | dialOpts := api.DialOpts{ | ||
98 | 93 | DialAddressInterval: 50 * time.Millisecond, | ||
99 | 94 | Timeout: 5 * time.Second, | ||
100 | 95 | RetryDelay: 2 * time.Second, | ||
101 | 96 | } | ||
102 | 97 | state, err := api.Open(apiInfo, dialOpts) | ||
103 | 98 | if err != nil { | ||
104 | 99 | return err | ||
105 | 100 | } | ||
106 | 101 | client := state.Client() | ||
107 | 102 | watcher, err := client.WatchAll() | ||
108 | 103 | if err != nil { | ||
109 | 104 | return err | ||
110 | 105 | } | ||
111 | 106 | deltas, err := watcher.Next() | ||
112 | 107 | if err != nil { | ||
113 | 108 | return err | ||
114 | 109 | } | ||
115 | 110 | for _, delta := range deltas { | ||
116 | 111 | entityId := delta.Entity.EntityId() | ||
117 | 112 | if entityId.Kind == "machine" { | ||
118 | 113 | machineId, _ := entityId.Id.(string) | ||
119 | 114 | if machineId == "0" { | ||
120 | 115 | return nil | ||
121 | 116 | } | ||
122 | 117 | } | ||
123 | 118 | } | ||
124 | 119 | return errors.New("invalid delta") | ||
125 | 120 | } | ||
126 | 121 | |||
127 | 122 | func apiEndpoints() error { | ||
128 | 123 | info, err := readProcessInfo() | ||
129 | 124 | if err != nil { | ||
130 | 125 | return err | ||
131 | 126 | } | ||
132 | 127 | fmt.Println(info.EndpointAddr) | ||
133 | 128 | return nil | ||
134 | 129 | } | ||
135 | 130 | |||
136 | 131 | func apiInfo() error { | ||
137 | 132 | info, err := readProcessInfo() | ||
138 | 133 | if err != nil { | ||
139 | 134 | return err | ||
140 | 135 | } | ||
141 | 136 | fmt.Printf("{\"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", info.Uuid, info.EndpointAddr) | ||
142 | 137 | return nil | ||
143 | 138 | } | ||
144 | 139 | |||
145 | 140 | func destroyEnvironment() error { | ||
146 | 141 | info, err := readProcessInfo() | ||
147 | 142 | if err != nil { | ||
148 | 143 | return err | ||
149 | 144 | } | ||
150 | 145 | fifoPath := filepath.Join(info.WorkDir, "fifo") | ||
151 | 146 | fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600) | ||
152 | 147 | if err != nil { | ||
153 | 148 | return err | ||
154 | 149 | } | ||
155 | 150 | defer fd.Close() | ||
156 | 151 | _, err = fd.WriteString("destroy\n") | ||
157 | 152 | if err != nil { | ||
158 | 153 | return err | ||
159 | 154 | } | ||
160 | 155 | return nil | ||
161 | 156 | } | ||
162 | 157 | |||
163 | 158 | func environmentNameAndPassword() (string, string, error) { | ||
164 | 159 | jujuHome := os.Getenv("JUJU_HOME") | ||
165 | 160 | osenv.SetJujuHome(jujuHome) | ||
166 | 161 | environs, err := environs.ReadEnvirons( | ||
167 | 162 | filepath.Join(jujuHome, "environments.yaml")) | ||
168 | 163 | if err != nil { | ||
169 | 164 | return "", "", err | ||
170 | 165 | } | ||
171 | 166 | envName := environs.Names()[0] | ||
172 | 167 | config, err := environs.Config(envName) | ||
173 | 168 | if err != nil { | ||
174 | 169 | return "", "", err | ||
175 | 170 | } | ||
176 | 171 | return envName, config.AdminSecret(), nil | ||
177 | 172 | } | ||
178 | 173 | |||
179 | 174 | func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) { | ||
180 | 175 | buffer := bufio.NewReader(stdout) | ||
181 | 176 | line, _, err := buffer.ReadLine() | ||
182 | 177 | if err != nil { | ||
183 | 178 | return nil, err | ||
184 | 179 | } | ||
185 | 180 | uuid := string(line) | ||
186 | 181 | environTag := names.NewEnvironTag(uuid) | ||
187 | 182 | line, _, err = buffer.ReadLine() | ||
188 | 183 | if err != nil { | ||
189 | 184 | return nil, err | ||
190 | 185 | } | ||
191 | 186 | workDir := string(line) | ||
192 | 187 | store, err := configstore.NewDisk(workDir) | ||
193 | 188 | if err != nil { | ||
194 | 189 | return nil, err | ||
195 | 190 | } | ||
196 | 191 | info, err := store.ReadInfo("dummyenv") | ||
197 | 192 | if err != nil { | ||
198 | 193 | return nil, err | ||
199 | 194 | } | ||
200 | 195 | credentials := info.APICredentials() | ||
201 | 196 | endpoint := info.APIEndpoint() | ||
202 | 197 | addresses := endpoint.Addresses | ||
203 | 198 | apiInfo := &api.Info{ | ||
204 | 199 | Addrs: addresses, | ||
205 | 200 | Tag: names.NewLocalUserTag(credentials.User), | ||
206 | 201 | Password: credentials.Password, | ||
207 | 202 | CACert: endpoint.CACert, | ||
208 | 203 | EnvironTag: environTag, | ||
209 | 204 | } | ||
210 | 205 | err = writeProcessInfo(envName, &processInfo{ | ||
211 | 206 | WorkDir: workDir, | ||
212 | 207 | EndpointAddr: addresses[0], | ||
213 | 208 | Uuid: uuid, | ||
214 | 209 | CACert: endpoint.CACert, | ||
215 | 210 | }) | ||
216 | 211 | if err != nil { | ||
217 | 212 | return nil, err | ||
218 | 213 | } | ||
219 | 214 | return apiInfo, nil | ||
220 | 215 | } | ||
221 | 216 | |||
222 | 217 | func readProcessInfo() (*processInfo, error) { | ||
223 | 218 | infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju") | ||
224 | 219 | data, err := ioutil.ReadFile(infoPath) | ||
225 | 220 | if err != nil { | ||
226 | 221 | return nil, err | ||
227 | 222 | } | ||
228 | 223 | info := &processInfo{} | ||
229 | 224 | err = goyaml.Unmarshal(data, info) | ||
230 | 225 | if err != nil { | ||
231 | 226 | return nil, err | ||
232 | 227 | } | ||
233 | 228 | return info, nil | ||
234 | 229 | } | ||
235 | 230 | |||
236 | 231 | func writeProcessInfo(envName string, info *processInfo) error { | ||
237 | 232 | jujuHome := os.Getenv("JUJU_HOME") | ||
238 | 233 | infoPath := filepath.Join(jujuHome, "fakejuju") | ||
239 | 234 | logPath := filepath.Join(jujuHome, "fake-juju.log") | ||
240 | 235 | caCertPath := filepath.Join(jujuHome, "cert.ca") | ||
241 | 236 | envPath := filepath.Join(jujuHome, "environments") | ||
242 | 237 | os.Mkdir(envPath, 0755) | ||
243 | 238 | jEnvPath := filepath.Join(envPath, envName+".jenv") | ||
244 | 239 | data, _ := goyaml.Marshal(info) | ||
245 | 240 | err := os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath) | ||
246 | 241 | if err != nil { | ||
247 | 242 | return err | ||
248 | 243 | } | ||
249 | 244 | err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath) | ||
250 | 245 | if err != nil { | ||
251 | 246 | return err | ||
252 | 247 | } | ||
253 | 248 | err = ioutil.WriteFile(infoPath, data, 0644) | ||
254 | 249 | if err != nil { | ||
255 | 250 | return err | ||
256 | 251 | } | ||
257 | 252 | return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644) | ||
258 | 253 | } | ||
259 | 254 | |||
260 | 255 | type FakeJujuSuite struct { | ||
261 | 256 | jujutesting.JujuConnSuite | ||
262 | 257 | |||
263 | 258 | instanceCount int | ||
264 | 259 | machineStarted map[string]bool | ||
265 | 260 | fifoPath string | ||
266 | 261 | logFile *os.File | ||
267 | 262 | } | ||
268 | 263 | |||
269 | 264 | var _ = gc.Suite(&FakeJujuSuite{}) | ||
270 | 265 | |||
271 | 266 | func (s *FakeJujuSuite) SetUpTest(c *gc.C) { | ||
272 | 267 | var CommandOutput = (*exec.Cmd).CombinedOutput | ||
273 | 268 | s.JujuConnSuite.SetUpTest(c) | ||
274 | 269 | |||
275 | 270 | ports := s.APIState.APIHostPorts() | ||
276 | 271 | ports[0][0].NetworkName = "dummy-provider-network" | ||
277 | 272 | err := s.State.SetAPIHostPorts(ports) | ||
278 | 273 | c.Assert(err, gc.IsNil) | ||
279 | 274 | |||
280 | 275 | s.machineStarted = make(map[string]bool) | ||
281 | 276 | s.PatchValue(&corecharm.CacheDir, c.MkDir()) | ||
282 | 277 | password := "dummy-password" | ||
283 | 278 | if os.Getenv("ADMIN_PASSWORD") != "" { | ||
284 | 279 | password = os.Getenv("ADMIN_PASSWORD") | ||
285 | 280 | } | ||
286 | 281 | _, err = s.State.AddUser("admin", "Admin", password, "dummy-admin") | ||
287 | 282 | c.Assert(err, gc.IsNil) | ||
288 | 283 | _, err = s.State.AddEnvironmentUser( | ||
289 | 284 | names.NewLocalUserTag("admin"), names.NewLocalUserTag("dummy-admin"), "Admin") | ||
290 | 285 | c.Assert(err, gc.IsNil) | ||
291 | 286 | |||
292 | 287 | // Create a machine to manage the environment. | ||
293 | 288 | stateServer := s.Factory.MakeMachine(c, &factory.MachineParams{ | ||
294 | 289 | InstanceId: s.newInstanceId(), | ||
295 | 290 | Nonce: agent.BootstrapNonce, | ||
296 | 291 | Jobs: []state.MachineJob{state.JobManageEnviron, state.JobHostUnits}, | ||
297 | 292 | Series: "trusty", | ||
298 | 293 | }) | ||
299 | 294 | c.Assert(stateServer.SetAgentVersion(version.Current), gc.IsNil) | ||
300 | 295 | address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal) | ||
301 | 296 | c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil) | ||
302 | 297 | c.Assert(stateServer.SetStatus(state.StatusStarted, "", nil), gc.IsNil) | ||
303 | 298 | _, err = stateServer.SetAgentPresence() | ||
304 | 299 | c.Assert(err, gc.IsNil) | ||
305 | 300 | s.State.StartSync() | ||
306 | 301 | err = stateServer.WaitAgentPresence(coretesting.LongWait) | ||
307 | 302 | c.Assert(err, gc.IsNil) | ||
308 | 303 | |||
309 | 304 | apiInfo := s.APIInfo(c) | ||
310 | 305 | //fmt.Println(apiInfo.Addrs[0]) | ||
311 | 306 | jujuHome := osenv.JujuHome() | ||
312 | 307 | fmt.Println(apiInfo.EnvironTag.Id()) | ||
313 | 308 | fmt.Println(jujuHome) | ||
314 | 309 | |||
315 | 310 | binPath := filepath.Join(jujuHome, "bin") | ||
316 | 311 | os.Mkdir(binPath, 0755) | ||
317 | 312 | fakeSSHData := []byte("#!/bin/sh\nsleep 1\n") | ||
318 | 313 | fakeSSHPath := filepath.Join(binPath, "ssh") | ||
319 | 314 | err = ioutil.WriteFile(fakeSSHPath, fakeSSHData, 0755) | ||
320 | 315 | c.Assert(err, gc.IsNil) | ||
321 | 316 | os.Setenv("PATH", binPath+":"+os.Getenv("PATH")) | ||
322 | 317 | |||
323 | 318 | s.fifoPath = filepath.Join(jujuHome, "fifo") | ||
324 | 319 | syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0) | ||
325 | 320 | |||
326 | 321 | // Logging | ||
327 | 322 | logPath := filepath.Join(jujuHome, "fake-juju.log") | ||
328 | 323 | s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | ||
329 | 324 | c.Assert(err, gc.IsNil) | ||
330 | 325 | |||
331 | 326 | log.SetOutput(s.logFile) | ||
332 | 327 | dpkgCmd := exec.Command( | ||
333 | 328 | "dpkg-query", "--showformat='${Version}'", "--show", "fake-juju") | ||
334 | 329 | out, err := CommandOutput(dpkgCmd) | ||
335 | 330 | fakeJujuDebVersion := strings.Trim(string(out), "'") | ||
336 | 331 | log.Printf("Started fake-juju-%s for %s\nJUJU_HOME=%s", fakeJujuDebVersion, version.Current, jujuHome) | ||
337 | 332 | |||
338 | 333 | } | ||
339 | 334 | |||
340 | 335 | func (s *FakeJujuSuite) TearDownTest(c *gc.C) { | ||
341 | 336 | s.JujuConnSuite.TearDownTest(c) | ||
342 | 337 | s.logFile.Close() | ||
343 | 338 | } | ||
344 | 339 | |||
345 | 340 | func (s *FakeJujuSuite) TestStart(c *gc.C) { | ||
346 | 341 | watcher := s.State.Watch() | ||
347 | 342 | go func() { | ||
348 | 343 | fd, err := os.Open(s.fifoPath) | ||
349 | 344 | c.Assert(err, gc.IsNil) | ||
350 | 345 | scanner := bufio.NewScanner(fd) | ||
351 | 346 | scanner.Scan() | ||
352 | 347 | watcher.Stop() | ||
353 | 348 | }() | ||
354 | 349 | for { | ||
355 | 350 | deltas, err := watcher.Next() | ||
356 | 351 | log.Println("Got deltas") | ||
357 | 352 | if err != nil { | ||
358 | 353 | if err.Error() == "watcher was stopped" { | ||
359 | 354 | log.Println("Watcher stopped") | ||
360 | 355 | break | ||
361 | 356 | } | ||
362 | 357 | log.Println("Unexpected error", err.Error()) | ||
363 | 358 | } | ||
364 | 359 | c.Assert(err, gc.IsNil) | ||
365 | 360 | for _, d := range deltas { | ||
366 | 361 | |||
367 | 362 | entity, err := json.MarshalIndent(d.Entity, "", " ") | ||
368 | 363 | c.Assert(err, gc.IsNil) | ||
369 | 364 | verb := "change" | ||
370 | 365 | if d.Removed { | ||
371 | 366 | verb = "remove" | ||
372 | 367 | } | ||
373 | 368 | log.Println("Processing delta", verb, d.Entity.EntityId().Kind, string(entity[:])) | ||
374 | 369 | |||
375 | 370 | entityId := d.Entity.EntityId() | ||
376 | 371 | if entityId.Kind == "machine" { | ||
377 | 372 | machineId, ok := entityId.Id.(string) | ||
378 | 373 | c.Assert(ok, gc.Equals, true) | ||
379 | 374 | c.Assert(s.handleAddMachine(machineId), gc.IsNil) | ||
380 | 375 | } | ||
381 | 376 | if entityId.Kind == "unit" { | ||
382 | 377 | unitId, ok := entityId.Id.(string) | ||
383 | 378 | c.Assert(ok, gc.Equals, true) | ||
384 | 379 | c.Assert(s.handleAddUnit(unitId), gc.IsNil) | ||
385 | 380 | } | ||
386 | 381 | log.Println("Done processing delta") | ||
387 | 382 | } | ||
388 | 383 | } | ||
389 | 384 | } | ||
390 | 385 | |||
391 | 386 | func (s *FakeJujuSuite) handleAddMachine(id string) error { | ||
392 | 387 | machine, err := s.State.Machine(id) | ||
393 | 388 | if err != nil { | ||
394 | 389 | return err | ||
395 | 390 | } | ||
396 | 391 | if instanceId, _ := machine.InstanceId(); instanceId == "" { | ||
397 | 392 | err = machine.SetProvisioned(s.newInstanceId(), agent.BootstrapNonce, nil) | ||
398 | 393 | if err != nil { | ||
399 | 394 | log.Println("Got error with SetProvisioned", err) | ||
400 | 395 | return err | ||
401 | 396 | } | ||
402 | 397 | address := network.NewScopedAddress("127.0.0.1", network.ScopeCloudLocal) | ||
403 | 398 | err = machine.SetProviderAddresses(address) | ||
404 | 399 | if err != nil { | ||
405 | 400 | log.Println("Got error with SetProviderAddresses", err) | ||
406 | 401 | return err | ||
407 | 402 | } | ||
408 | 403 | } | ||
409 | 404 | status, _ := machine.Status() | ||
410 | 405 | if status.Status == state.StatusPending { | ||
411 | 406 | if err = s.startMachine(machine); err != nil { | ||
412 | 407 | log.Println("Got error with startMachine:", err) | ||
413 | 408 | return err | ||
414 | 409 | } | ||
415 | 410 | } else if status.Status == state.StatusStarted { | ||
416 | 411 | if _, ok := s.machineStarted[id]; !ok { | ||
417 | 412 | s.machineStarted[id] = true | ||
418 | 413 | if err = s.startUnits(machine); err != nil { | ||
419 | 414 | log.Println("Got error with startUnits", err) | ||
420 | 415 | return err | ||
421 | 416 | } | ||
422 | 417 | } | ||
423 | 418 | } | ||
424 | 419 | return nil | ||
425 | 420 | } | ||
426 | 421 | |||
427 | 422 | func (s *FakeJujuSuite) handleAddUnit(id string) error { | ||
428 | 423 | unit, err := s.State.Unit(id) | ||
429 | 424 | if err != nil { | ||
430 | 425 | log.Println("Got error with get unit", err) | ||
431 | 426 | return err | ||
432 | 427 | } | ||
433 | 428 | machineId, err := unit.AssignedMachineId() | ||
434 | 429 | if err != nil { | ||
435 | 430 | return nil | ||
436 | 431 | } | ||
437 | 432 | log.Println("Got machineId", machineId) | ||
438 | 433 | machine, err := s.State.Machine(machineId) | ||
439 | 434 | if err != nil { | ||
440 | 435 | log.Println("Got error with unit AssignedMachineId", err) | ||
441 | 436 | return err | ||
442 | 437 | } | ||
443 | 438 | machineStatus, _ := machine.Status() | ||
444 | 439 | if machineStatus.Status != state.StatusStarted { | ||
445 | 440 | return nil | ||
446 | 441 | } | ||
447 | 442 | status, _ := unit.Status() | ||
448 | 443 | if status.Status != state.StatusActive { | ||
449 | 444 | if err = s.startUnit(unit); err != nil { | ||
450 | 445 | return err | ||
451 | 446 | } | ||
452 | 447 | } | ||
453 | 448 | return nil | ||
454 | 449 | } | ||
455 | 450 | |||
456 | 451 | func (s *FakeJujuSuite) startMachine(machine *state.Machine) error { | ||
457 | 452 | time.Sleep(500 * time.Millisecond) | ||
458 | 453 | err := machine.SetStatus(state.StatusStarted, "", nil) | ||
459 | 454 | if err != nil { | ||
460 | 455 | return err | ||
461 | 456 | } | ||
462 | 457 | err = machine.SetAgentVersion(version.Current) | ||
463 | 458 | if err != nil { | ||
464 | 459 | return err | ||
465 | 460 | } | ||
466 | 461 | _, err = machine.SetAgentPresence() | ||
467 | 462 | if err != nil { | ||
468 | 463 | return err | ||
469 | 464 | } | ||
470 | 465 | s.State.StartSync() | ||
471 | 466 | err = machine.WaitAgentPresence(coretesting.LongWait) | ||
472 | 467 | if err != nil { | ||
473 | 468 | return err | ||
474 | 469 | } | ||
475 | 470 | return nil | ||
476 | 471 | } | ||
477 | 472 | |||
478 | 473 | func (s *FakeJujuSuite) startUnits(machine *state.Machine) error { | ||
479 | 474 | units, err := machine.Units() | ||
480 | 475 | if err != nil { | ||
481 | 476 | return err | ||
482 | 477 | } | ||
483 | 478 | return nil | ||
484 | 479 | for _, unit := range units { | ||
485 | 480 | unitStatus, _ := unit.Status() | ||
486 | 481 | if unitStatus.Status != state.StatusActive { | ||
487 | 482 | if err = s.startUnit(unit); err != nil { | ||
488 | 483 | return err | ||
489 | 484 | } | ||
490 | 485 | } | ||
491 | 486 | } | ||
492 | 487 | return nil | ||
493 | 488 | } | ||
494 | 489 | |||
495 | 490 | func (s *FakeJujuSuite) startUnit(unit *state.Unit) error { | ||
496 | 491 | err := unit.SetStatus(state.StatusActive, "", nil) | ||
497 | 492 | if err != nil { | ||
498 | 493 | return err | ||
499 | 494 | } | ||
500 | 495 | _, err = unit.SetAgentPresence() | ||
501 | 496 | if err != nil { | ||
502 | 497 | return err | ||
503 | 498 | } | ||
504 | 499 | s.State.StartSync() | ||
505 | 500 | err = unit.WaitAgentPresence(coretesting.LongWait) | ||
506 | 501 | if err != nil { | ||
507 | 502 | return err | ||
508 | 503 | } | ||
509 | 504 | err = unit.SetAgentStatus(state.StatusIdle, "", nil) | ||
510 | 505 | if err != nil { | ||
511 | 506 | return err | ||
512 | 507 | } | ||
513 | 508 | return nil | ||
514 | 509 | } | ||
515 | 510 | |||
516 | 511 | func (s *FakeJujuSuite) newInstanceId() instance.Id { | ||
517 | 512 | s.instanceCount += 1 | ||
518 | 513 | return instance.Id(fmt.Sprintf("id-%d", s.instanceCount)) | ||
519 | 514 | } | ||
520 | 515 | 0 | ||
521 | === modified file '1.25.6/fake-juju.go' | |||
522 | --- 1.25.6/fake-juju.go 2016-06-10 17:07:27 +0000 | |||
523 | +++ 1.25.6/fake-juju.go 2016-10-25 17:25:06 +0000 | |||
524 | @@ -37,52 +37,51 @@ | |||
525 | 37 | ) | 37 | ) |
526 | 38 | 38 | ||
527 | 39 | func main() { | 39 | func main() { |
528 | 40 | code := 0 | ||
529 | 40 | if len(os.Args) > 1 { | 41 | if len(os.Args) > 1 { |
530 | 41 | code := 0 | ||
531 | 42 | err := handleCommand(os.Args[1]) | 42 | err := handleCommand(os.Args[1]) |
532 | 43 | if err != nil { | 43 | if err != nil { |
533 | 44 | fmt.Println(err.Error()) | 44 | fmt.Println(err.Error()) |
534 | 45 | code = 1 | 45 | code = 1 |
535 | 46 | } | 46 | } |
537 | 47 | os.Exit(code) | 47 | } else { |
538 | 48 | // This kicks off the daemon. See FakeJujuSuite below. | ||
539 | 49 | t := &testing.T{} | ||
540 | 50 | coretesting.MgoTestPackage(t) | ||
541 | 48 | } | 51 | } |
552 | 49 | t := &testing.T{} | 52 | os.Exit(code) |
543 | 50 | coretesting.MgoTestPackage(t) | ||
544 | 51 | } | ||
545 | 52 | |||
546 | 53 | type processInfo struct { | ||
547 | 54 | Username string | ||
548 | 55 | WorkDir string | ||
549 | 56 | EndpointAddr string | ||
550 | 57 | Uuid string | ||
551 | 58 | CACert string | ||
553 | 59 | } | 53 | } |
554 | 60 | 54 | ||
555 | 61 | func handleCommand(command string) error { | 55 | func handleCommand(command string) error { |
556 | 56 | filenames := newFakeJujuFilenames("", "", "") | ||
557 | 62 | if command == "bootstrap" { | 57 | if command == "bootstrap" { |
559 | 63 | return bootstrap() | 58 | return bootstrap(filenames) |
560 | 64 | } | 59 | } |
561 | 65 | if command == "api-endpoints" { | 60 | if command == "api-endpoints" { |
563 | 66 | return apiEndpoints() | 61 | return apiEndpoints(filenames) |
564 | 67 | } | 62 | } |
565 | 68 | if command == "api-info" { | 63 | if command == "api-info" { |
567 | 69 | return apiInfo() | 64 | return apiInfo(filenames) |
568 | 70 | } | 65 | } |
569 | 71 | if command == "destroy-environment" { | 66 | if command == "destroy-environment" { |
571 | 72 | return destroyEnvironment() | 67 | return destroyEnvironment(filenames) |
572 | 73 | } | 68 | } |
573 | 74 | return errors.New("command not found") | 69 | return errors.New("command not found") |
574 | 75 | } | 70 | } |
575 | 76 | 71 | ||
577 | 77 | func bootstrap() error { | 72 | func bootstrap(filenames fakejujuFilenames) error { |
578 | 73 | if err := filenames.ensureDirsExist(); err != nil { | ||
579 | 74 | return err | ||
580 | 75 | } | ||
581 | 78 | envName, config, err := environmentNameAndConfig() | 76 | envName, config, err := environmentNameAndConfig() |
582 | 79 | if err != nil { | 77 | if err != nil { |
583 | 80 | return err | 78 | return err |
584 | 81 | } | 79 | } |
585 | 80 | password := config.AdminSecret() | ||
586 | 81 | |||
587 | 82 | command := exec.Command(os.Args[0]) | 82 | command := exec.Command(os.Args[0]) |
588 | 83 | command.Env = os.Environ() | 83 | command.Env = os.Environ() |
591 | 84 | command.Env = append( | 84 | command.Env = append(command.Env, "ADMIN_PASSWORD="+password) |
590 | 85 | command.Env, "ADMIN_PASSWORD="+config.AdminSecret()) | ||
592 | 86 | defaultSeries, _ := config.DefaultSeries() | 85 | defaultSeries, _ := config.DefaultSeries() |
593 | 87 | command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries) | 86 | command.Env = append(command.Env, "DEFAULT_SERIES="+defaultSeries) |
594 | 88 | stdout, err := command.StdoutPipe() | 87 | stdout, err := command.StdoutPipe() |
595 | @@ -90,10 +89,23 @@ | |||
596 | 90 | return err | 89 | return err |
597 | 91 | } | 90 | } |
598 | 92 | command.Start() | 91 | command.Start() |
600 | 93 | apiInfo, err := parseApiInfo(envName, stdout) | 92 | |
601 | 93 | result, err := parseApiInfo(stdout) | ||
602 | 94 | if err != nil { | 94 | if err != nil { |
603 | 95 | return err | 95 | return err |
604 | 96 | } | 96 | } |
605 | 97 | // Get the API info before changing it. The new values might | ||
606 | 98 | // not work yet. | ||
607 | 99 | apiInfo := result.apiInfo() | ||
608 | 100 | // We actually want to report the API user we added in SetUpTest(). | ||
609 | 101 | result.username = "admin" | ||
610 | 102 | if password != "" { | ||
611 | 103 | result.password = password | ||
612 | 104 | } | ||
613 | 105 | if err := result.apply(filenames, envName); err != nil { | ||
614 | 106 | return err | ||
615 | 107 | } | ||
616 | 108 | |||
617 | 97 | dialOpts := api.DialOpts{ | 109 | dialOpts := api.DialOpts{ |
618 | 98 | DialAddressInterval: 50 * time.Millisecond, | 110 | DialAddressInterval: 50 * time.Millisecond, |
619 | 99 | Timeout: 5 * time.Second, | 111 | Timeout: 5 * time.Second, |
620 | @@ -123,8 +135,8 @@ | |||
621 | 123 | return errors.New("invalid delta") | 135 | return errors.New("invalid delta") |
622 | 124 | } | 136 | } |
623 | 125 | 137 | ||
626 | 126 | func apiEndpoints() error { | 138 | func apiEndpoints(filenames fakejujuFilenames) error { |
627 | 127 | info, err := readProcessInfo() | 139 | info, err := readProcessInfo(filenames) |
628 | 128 | if err != nil { | 140 | if err != nil { |
629 | 129 | return err | 141 | return err |
630 | 130 | } | 142 | } |
631 | @@ -132,23 +144,22 @@ | |||
632 | 132 | return nil | 144 | return nil |
633 | 133 | } | 145 | } |
634 | 134 | 146 | ||
637 | 135 | func apiInfo() error { | 147 | func apiInfo(filenames fakejujuFilenames) error { |
638 | 136 | info, err := readProcessInfo() | 148 | info, err := readProcessInfo(filenames) |
639 | 137 | if err != nil { | 149 | if err != nil { |
640 | 138 | return err | 150 | return err |
641 | 139 | } | 151 | } |
644 | 140 | username := strings.Replace(string(info.Username), "dummy-", "", 1) | 152 | fmt.Printf("{\"user\": \"%s\", \"password\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", info.Username, info.Password, info.Uuid, info.EndpointAddr) |
643 | 141 | fmt.Printf("{\"user\": \"%s\", \"environ-uuid\": \"%s\", \"state-servers\": [\"%s\"]}\n", username, info.Uuid, info.EndpointAddr) | ||
645 | 142 | return nil | 153 | return nil |
646 | 143 | } | 154 | } |
647 | 144 | 155 | ||
650 | 145 | func destroyEnvironment() error { | 156 | func destroyEnvironment(filenames fakejujuFilenames) error { |
651 | 146 | info, err := readProcessInfo() | 157 | info, err := readProcessInfo(filenames) |
652 | 147 | if err != nil { | 158 | if err != nil { |
653 | 148 | return err | 159 | return err |
654 | 149 | } | 160 | } |
657 | 150 | fifoPath := filepath.Join(info.WorkDir, "fifo") | 161 | filenames = newFakeJujuFilenames("", "", info.WorkDir) |
658 | 151 | fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600) | 162 | fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600) |
659 | 152 | if err != nil { | 163 | if err != nil { |
660 | 153 | return err | 164 | return err |
661 | 154 | } | 165 | } |
662 | @@ -176,93 +187,235 @@ | |||
663 | 176 | return envName, config, nil | 187 | return envName, config, nil |
664 | 177 | } | 188 | } |
665 | 178 | 189 | ||
667 | 179 | func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) { | 190 | // processInfo holds all the information that fake-juju uses internally. |
668 | 191 | type processInfo struct { | ||
669 | 192 | Username string | ||
670 | 193 | Password string | ||
671 | 194 | WorkDir string | ||
672 | 195 | EndpointAddr string | ||
673 | 196 | Uuid string | ||
674 | 197 | CACert []byte | ||
675 | 198 | } | ||
676 | 199 | |||
677 | 200 | func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) { | ||
678 | 201 | infoPath := filenames.infoFile() | ||
679 | 202 | data, err := ioutil.ReadFile(infoPath) | ||
680 | 203 | if err != nil { | ||
681 | 204 | return nil, err | ||
682 | 205 | } | ||
683 | 206 | info := &processInfo{} | ||
684 | 207 | err = goyaml.Unmarshal(data, info) | ||
685 | 208 | if err != nil { | ||
686 | 209 | return nil, err | ||
687 | 210 | } | ||
688 | 211 | return info, nil | ||
689 | 212 | } | ||
690 | 213 | |||
691 | 214 | func (info processInfo) write(infoPath string) error { | ||
692 | 215 | data, _ := goyaml.Marshal(&info) | ||
693 | 216 | if err := ioutil.WriteFile(infoPath, data, 0644); err != nil { | ||
694 | 217 | return err | ||
695 | 218 | } | ||
696 | 219 | return nil | ||
697 | 220 | } | ||
698 | 221 | |||
699 | 222 | // fakejujuFilenames encapsulates the paths to all the directories and | ||
700 | 223 | // files that are relevant to fake-juju. | ||
701 | 224 | type fakejujuFilenames struct { | ||
702 | 225 | datadir string | ||
703 | 226 | logsdir string | ||
704 | 227 | } | ||
705 | 228 | |||
706 | 229 | func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames { | ||
707 | 230 | if datadir == "" { | ||
708 | 231 | datadir = os.Getenv("FAKE_JUJU_DATA_DIR") | ||
709 | 232 | if datadir == "" { | ||
710 | 233 | if jujucfgdir == "" { | ||
711 | 234 | jujucfgdir = os.Getenv("JUJU_HOME") | ||
712 | 235 | } | ||
713 | 236 | datadir = jujucfgdir | ||
714 | 237 | } | ||
715 | 238 | } | ||
716 | 239 | if logsdir == "" { | ||
717 | 240 | logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR") | ||
718 | 241 | if logsdir == "" { | ||
719 | 242 | logsdir = datadir | ||
720 | 243 | } | ||
721 | 244 | } | ||
722 | 245 | return fakejujuFilenames{datadir, logsdir} | ||
723 | 246 | } | ||
724 | 247 | |||
725 | 248 | func (fj fakejujuFilenames) ensureDirsExist() error { | ||
726 | 249 | if err := os.MkdirAll(fj.datadir, 0755); err != nil { | ||
727 | 250 | return err | ||
728 | 251 | } | ||
729 | 252 | if err := os.MkdirAll(fj.logsdir, 0755); err != nil { | ||
730 | 253 | return err | ||
731 | 254 | } | ||
732 | 255 | return nil | ||
733 | 256 | } | ||
734 | 257 | |||
735 | 258 | // infoFile() returns the path to the file that fake-juju uses as | ||
736 | 259 | // its persistent storage for internal data. | ||
737 | 260 | func (fj fakejujuFilenames) infoFile() string { | ||
738 | 261 | return filepath.Join(fj.datadir, "fakejuju") | ||
739 | 262 | } | ||
740 | 263 | |||
741 | 264 | // logsFile() returns the path to the file where fake-juju writes | ||
742 | 265 | // its logs. Note that the normal Juju logs are not written here. | ||
743 | 266 | func (fj fakejujuFilenames) logsFile() string { | ||
744 | 267 | return filepath.Join(fj.logsdir, "fake-juju.log") | ||
745 | 268 | } | ||
746 | 269 | |||
747 | 270 | // fifoFile() returns the path to the FIFO file used by fake-juju. | ||
748 | 271 | // The FIFO is used by the fake-juju subcommands to interact with | ||
749 | 272 | // the daemon. | ||
750 | 273 | func (fj fakejujuFilenames) fifoFile() string { | ||
751 | 274 | return filepath.Join(fj.datadir, "fifo") | ||
752 | 275 | } | ||
753 | 276 | |||
754 | 277 | // caCertFile() returns the path to the file holding the CA certificate | ||
755 | 278 | // used by the Juju API server. fake-juju writes the cert there as a | ||
756 | 279 | // convenience for users. It is not actually used for anything. | ||
757 | 280 | func (fj fakejujuFilenames) caCertFile() string { | ||
758 | 281 | return filepath.Join(fj.datadir, "cert.ca") | ||
759 | 282 | } | ||
760 | 283 | |||
761 | 284 | // bootstrapResult encapsulates all significant information that came | ||
762 | 285 | // from bootstrapping an environment. | ||
763 | 286 | type bootstrapResult struct { | ||
764 | 287 | dummyEnvName string | ||
765 | 288 | cfgdir string | ||
766 | 289 | uuid string | ||
767 | 290 | username string | ||
768 | 291 | password string | ||
769 | 292 | addresses []string | ||
770 | 293 | caCert []byte | ||
771 | 294 | } | ||
772 | 295 | |||
773 | 296 | // apiInfo() composes the Juju API info corresponding to the result. | ||
774 | 297 | func (br bootstrapResult) apiInfo() *api.Info { | ||
775 | 298 | return &api.Info{ | ||
776 | 299 | Addrs: br.addresses, | ||
777 | 300 | Tag: names.NewLocalUserTag(br.username), | ||
778 | 301 | Password: br.password, | ||
779 | 302 | CACert: string(br.caCert), | ||
780 | 303 | EnvironTag: names.NewEnvironTag(br.uuid), | ||
781 | 304 | } | ||
782 | 305 | } | ||
783 | 306 | |||
784 | 307 | // fakeJujuInfo() composes, from the result, the set of information | ||
785 | 308 | // that fake-juju should use internally. | ||
786 | 309 | func (br bootstrapResult) fakeJujuInfo() *processInfo { | ||
787 | 310 | return &processInfo{ | ||
788 | 311 | Username: br.username, | ||
789 | 312 | Password: br.password, | ||
790 | 313 | WorkDir: br.cfgdir, | ||
791 | 314 | EndpointAddr: br.addresses[0], | ||
792 | 315 | Uuid: br.uuid, | ||
793 | 316 | CACert: br.caCert, | ||
794 | 317 | } | ||
795 | 318 | } | ||
796 | 319 | |||
797 | 320 | // logsSymlinkFilenames() determines the source and target paths for | ||
798 | 321 | // a symlink to the fake-juju logs file. Such a symlink is relevant | ||
799 | 322 | // because the fake-juju daemon may not know where the log file is | ||
800 | 323 | // meant to go. It defaults to putting the log file in the default Juju | ||
801 | 324 | // config dir. In that case, a symlink should be created from there to | ||
802 | 325 | // the user-defined Juju config dir ($JUJU_HOME). | ||
803 | 326 | func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) { | ||
804 | 327 | if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" || os.Getenv("FAKE_JUJU_DATA_DIR") != "" { | ||
805 | 328 | return "", "" | ||
806 | 329 | } | ||
807 | 330 | |||
808 | 331 | filenames := newFakeJujuFilenames("", "", br.cfgdir) | ||
809 | 332 | source = filenames.logsFile() | ||
810 | 333 | target = targetLogsFile | ||
811 | 334 | return source, target | ||
812 | 335 | } | ||
813 | 336 | |||
814 | 337 | // jenvSymlinkFilenames() determines the source and target paths for | ||
815 | 338 | // a symlink to the .jenv file for the identified environment. | ||
816 | 339 | func (br bootstrapResult) jenvSymlinkFilenames(jujuHome, envName string) (source, target string) { | ||
817 | 340 | if jujuHome == "" || envName == "" { | ||
818 | 341 | return "", "" | ||
819 | 342 | } | ||
820 | 343 | |||
821 | 344 | source = filepath.Join(br.cfgdir, "environments", br.dummyEnvName+".jenv") | ||
822 | 345 | target = filepath.Join(jujuHome, "environments", envName+".jenv") | ||
823 | 346 | return source, target | ||
824 | 347 | } | ||
825 | 348 | |||
826 | 349 | // apply() writes out the information from the bootstrap result to the | ||
827 | 350 | // various files identified by the provided filenames. | ||
828 | 351 | func (br bootstrapResult) apply(filenames fakejujuFilenames, envName string) error { | ||
829 | 352 | if err := br.fakeJujuInfo().write(filenames.infoFile()); err != nil { | ||
830 | 353 | return err | ||
831 | 354 | } | ||
832 | 355 | |||
833 | 356 | logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile()) | ||
834 | 357 | if logsSource != "" && logsTarget != "" { | ||
835 | 358 | if err := os.Symlink(logsSource, logsTarget); err != nil { | ||
836 | 359 | return err | ||
837 | 360 | } | ||
838 | 361 | } | ||
839 | 362 | |||
840 | 363 | jenvSource, jenvTarget := br.jenvSymlinkFilenames(os.Getenv("JUJU_HOME"), envName) | ||
841 | 364 | if jenvSource != "" && jenvTarget != "" { | ||
842 | 365 | if err := os.MkdirAll(filepath.Dir(jenvTarget), 0755); err != nil { | ||
843 | 366 | return err | ||
844 | 367 | } | ||
845 | 368 | if err := os.Symlink(jenvSource, jenvTarget); err != nil { | ||
846 | 369 | return err | ||
847 | 370 | } | ||
848 | 371 | } | ||
849 | 372 | |||
850 | 373 | if err := ioutil.WriteFile(filenames.caCertFile(), br.caCert, 0644); err != nil { | ||
851 | 374 | return err | ||
852 | 375 | } | ||
853 | 376 | |||
854 | 377 | return nil | ||
855 | 378 | } | ||
856 | 379 | |||
857 | 380 | // See github.com/juju/juju/blob/juju/testing/conn.go. | ||
858 | 381 | const dummyEnvName = "dummyenv" | ||
859 | 382 | |||
860 | 383 | func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) { | ||
861 | 180 | buffer := bufio.NewReader(stdout) | 384 | buffer := bufio.NewReader(stdout) |
862 | 385 | |||
863 | 181 | line, _, err := buffer.ReadLine() | 386 | line, _, err := buffer.ReadLine() |
864 | 182 | if err != nil { | 387 | if err != nil { |
865 | 183 | return nil, err | 388 | return nil, err |
866 | 184 | } | 389 | } |
867 | 185 | uuid := string(line) | 390 | uuid := string(line) |
869 | 186 | environTag := names.NewEnvironTag(uuid) | 391 | |
870 | 187 | line, _, err = buffer.ReadLine() | 392 | line, _, err = buffer.ReadLine() |
871 | 188 | if err != nil { | 393 | if err != nil { |
872 | 189 | return nil, err | 394 | return nil, err |
873 | 190 | } | 395 | } |
874 | 191 | workDir := string(line) | 396 | workDir := string(line) |
875 | 397 | |||
876 | 192 | store, err := configstore.NewDisk(workDir) | 398 | store, err := configstore.NewDisk(workDir) |
877 | 193 | if err != nil { | 399 | if err != nil { |
878 | 194 | return nil, err | 400 | return nil, err |
879 | 195 | } | 401 | } |
881 | 196 | info, err := store.ReadInfo("dummyenv") | 402 | info, err := store.ReadInfo(dummyEnvName) |
882 | 197 | if err != nil { | 403 | if err != nil { |
883 | 198 | return nil, err | 404 | return nil, err |
884 | 199 | } | 405 | } |
885 | 406 | |||
886 | 200 | credentials := info.APICredentials() | 407 | credentials := info.APICredentials() |
887 | 201 | endpoint := info.APIEndpoint() | 408 | endpoint := info.APIEndpoint() |
952 | 202 | addresses := endpoint.Addresses | 409 | result := &bootstrapResult{ |
953 | 203 | apiInfo := &api.Info{ | 410 | dummyEnvName: dummyEnvName, |
954 | 204 | Addrs: addresses, | 411 | cfgdir: workDir, |
955 | 205 | Tag: names.NewLocalUserTag(credentials.User), | 412 | uuid: uuid, |
956 | 206 | Password: credentials.Password, | 413 | username: credentials.User, |
957 | 207 | CACert: endpoint.CACert, | 414 | password: credentials.Password, |
958 | 208 | EnvironTag: environTag, | 415 | addresses: endpoint.Addresses, |
959 | 209 | } | 416 | caCert: []byte(endpoint.CACert), |
960 | 210 | err = writeProcessInfo(envName, &processInfo{ | 417 | } |
961 | 211 | Username: credentials.User, | 418 | return result, nil |
898 | 212 | WorkDir: workDir, | ||
899 | 213 | EndpointAddr: addresses[0], | ||
900 | 214 | Uuid: uuid, | ||
901 | 215 | CACert: endpoint.CACert, | ||
902 | 216 | }) | ||
903 | 217 | if err != nil { | ||
904 | 218 | return nil, err | ||
905 | 219 | } | ||
906 | 220 | return apiInfo, nil | ||
907 | 221 | } | ||
908 | 222 | |||
909 | 223 | func readProcessInfo() (*processInfo, error) { | ||
910 | 224 | infoPath := filepath.Join(os.Getenv("JUJU_HOME"), "fakejuju") | ||
911 | 225 | data, err := ioutil.ReadFile(infoPath) | ||
912 | 226 | if err != nil { | ||
913 | 227 | return nil, err | ||
914 | 228 | } | ||
915 | 229 | info := &processInfo{} | ||
916 | 230 | err = goyaml.Unmarshal(data, info) | ||
917 | 231 | if err != nil { | ||
918 | 232 | return nil, err | ||
919 | 233 | } | ||
920 | 234 | return info, nil | ||
921 | 235 | } | ||
922 | 236 | |||
923 | 237 | func writeProcessInfo(envName string, info *processInfo) error { | ||
924 | 238 | var err error | ||
925 | 239 | jujuHome := os.Getenv("JUJU_HOME") | ||
926 | 240 | infoPath := filepath.Join(jujuHome, "fakejuju") | ||
927 | 241 | logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR") | ||
928 | 242 | if logsDir == "" { | ||
929 | 243 | logsDir = jujuHome | ||
930 | 244 | } | ||
931 | 245 | logPath := filepath.Join(logsDir, "fake-juju.log") | ||
932 | 246 | caCertPath := filepath.Join(jujuHome, "cert.ca") | ||
933 | 247 | envPath := filepath.Join(jujuHome, "environments") | ||
934 | 248 | os.Mkdir(envPath, 0755) | ||
935 | 249 | jEnvPath := filepath.Join(envPath, envName+".jenv") | ||
936 | 250 | data, _ := goyaml.Marshal(info) | ||
937 | 251 | if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" { | ||
938 | 252 | err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath) | ||
939 | 253 | if err != nil { | ||
940 | 254 | return err | ||
941 | 255 | } | ||
942 | 256 | } | ||
943 | 257 | err = os.Symlink(filepath.Join(info.WorkDir, "environments/dummyenv.jenv"), jEnvPath) | ||
944 | 258 | if err != nil { | ||
945 | 259 | return err | ||
946 | 260 | } | ||
947 | 261 | err = ioutil.WriteFile(infoPath, data, 0644) | ||
948 | 262 | if err != nil { | ||
949 | 263 | return err | ||
950 | 264 | } | ||
951 | 265 | return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644) | ||
962 | 266 | } | 419 | } |
963 | 267 | 420 | ||
964 | 268 | // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment | 421 | // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment |
965 | @@ -302,12 +455,16 @@ | |||
966 | 302 | return failuresInfo, nil | 455 | return failuresInfo, nil |
967 | 303 | } | 456 | } |
968 | 304 | 457 | ||
969 | 458 | //=================================================================== | ||
970 | 459 | // The fake-juju daemon (started by bootstrap) is found here. It is | ||
971 | 460 | // implemented as a test suite. | ||
972 | 461 | |||
973 | 305 | type FakeJujuSuite struct { | 462 | type FakeJujuSuite struct { |
974 | 306 | jujutesting.JujuConnSuite | 463 | jujutesting.JujuConnSuite |
975 | 307 | 464 | ||
976 | 308 | instanceCount int | 465 | instanceCount int |
977 | 309 | machineStarted map[string]bool | 466 | machineStarted map[string]bool |
979 | 310 | fifoPath string | 467 | filenames fakejujuFilenames |
980 | 311 | logFile *os.File | 468 | logFile *os.File |
981 | 312 | } | 469 | } |
982 | 313 | 470 | ||
983 | @@ -359,7 +516,6 @@ | |||
984 | 359 | c.Assert(err, gc.IsNil) | 516 | c.Assert(err, gc.IsNil) |
985 | 360 | 517 | ||
986 | 361 | apiInfo := s.APIInfo(c) | 518 | apiInfo := s.APIInfo(c) |
987 | 362 | //fmt.Println(apiInfo.Addrs[0]) | ||
988 | 363 | jujuHome := osenv.JujuHome() | 519 | jujuHome := osenv.JujuHome() |
989 | 364 | // IMPORTANT: don't remove this logging because it's used by the | 520 | // IMPORTANT: don't remove this logging because it's used by the |
990 | 365 | // bootstrap command. | 521 | // bootstrap command. |
991 | @@ -374,15 +530,11 @@ | |||
992 | 374 | c.Assert(err, gc.IsNil) | 530 | c.Assert(err, gc.IsNil) |
993 | 375 | os.Setenv("PATH", binPath+":"+os.Getenv("PATH")) | 531 | os.Setenv("PATH", binPath+":"+os.Getenv("PATH")) |
994 | 376 | 532 | ||
997 | 377 | s.fifoPath = filepath.Join(jujuHome, "fifo") | 533 | s.filenames = newFakeJujuFilenames("", "", jujuHome) |
998 | 378 | syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0) | 534 | syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0) |
999 | 379 | 535 | ||
1000 | 380 | // Logging | 536 | // Logging |
1006 | 381 | logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR") | 537 | logPath := s.filenames.logsFile() |
1002 | 382 | if logsDir == "" { | ||
1003 | 383 | logsDir = jujuHome | ||
1004 | 384 | } | ||
1005 | 385 | logPath := filepath.Join(logsDir, "fake-juju.log") | ||
1007 | 386 | s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | 538 | s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) |
1008 | 387 | c.Assert(err, gc.IsNil) | 539 | c.Assert(err, gc.IsNil) |
1009 | 388 | 540 | ||
1010 | @@ -402,16 +554,30 @@ | |||
1011 | 402 | } | 554 | } |
1012 | 403 | 555 | ||
1013 | 404 | func (s *FakeJujuSuite) TestStart(c *gc.C) { | 556 | func (s *FakeJujuSuite) TestStart(c *gc.C) { |
1014 | 557 | fifoPath := s.filenames.fifoFile() | ||
1015 | 405 | watcher := s.State.Watch() | 558 | watcher := s.State.Watch() |
1016 | 406 | go func() { | 559 | go func() { |
1019 | 407 | log.Println("Open commands FIFO", s.fifoPath) | 560 | log.Println("Open commands FIFO", fifoPath) |
1020 | 408 | fd, err := os.Open(s.fifoPath) | 561 | fd, err := os.Open(fifoPath) |
1021 | 409 | if err != nil { | 562 | if err != nil { |
1022 | 410 | log.Println("Failed to open commands FIFO") | 563 | log.Println("Failed to open commands FIFO") |
1023 | 411 | } | 564 | } |
1024 | 412 | c.Assert(err, gc.IsNil) | 565 | c.Assert(err, gc.IsNil) |
1025 | 566 | defer func() { | ||
1026 | 567 | if err := fd.Close(); err != nil { | ||
1027 | 568 | c.Logf("failed closing FIFO file: %s", err) | ||
1028 | 569 | } | ||
1029 | 570 | // Mark the controller as destroyed by renaming some files. | ||
1030 | 571 | if err := os.Rename(fifoPath, fifoPath+".destroyed"); err != nil { | ||
1031 | 572 | c.Logf("failed renaming FIFO file: %s", err) | ||
1032 | 573 | } | ||
1033 | 574 | infofile := s.filenames.infoFile() | ||
1034 | 575 | if err := os.Rename(infofile, infofile+".destroyed"); err != nil { | ||
1035 | 576 | c.Logf("failed renaming info file: %s", err) | ||
1036 | 577 | } | ||
1037 | 578 | }() | ||
1038 | 413 | scanner := bufio.NewScanner(fd) | 579 | scanner := bufio.NewScanner(fd) |
1040 | 414 | log.Println("Listen for commands on FIFO", s.fifoPath) | 580 | log.Println("Listen for commands on FIFO", fifoPath) |
1041 | 415 | scanner.Scan() | 581 | scanner.Scan() |
1042 | 416 | log.Println("Stopping fake-juju") | 582 | log.Println("Stopping fake-juju") |
1043 | 417 | watcher.Stop() | 583 | watcher.Stop() |
1044 | 418 | 584 | ||
1045 | === renamed directory '2.0-beta17' => '2.0.0' | |||
1046 | === modified file '2.0.0/fake-juju.go' | |||
1047 | --- 2.0-beta17/fake-juju.go 2016-09-15 19:05:50 +0000 | |||
1048 | +++ 2.0.0/fake-juju.go 2016-10-25 17:25:06 +0000 | |||
1049 | @@ -38,46 +38,45 @@ | |||
1050 | 38 | ) | 38 | ) |
1051 | 39 | 39 | ||
1052 | 40 | func main() { | 40 | func main() { |
1053 | 41 | code := 0 | ||
1054 | 41 | if len(os.Args) > 1 { | 42 | if len(os.Args) > 1 { |
1055 | 42 | code := 0 | ||
1056 | 43 | err := handleCommand(os.Args[1]) | 43 | err := handleCommand(os.Args[1]) |
1057 | 44 | if err != nil { | 44 | if err != nil { |
1058 | 45 | fmt.Println(err.Error()) | 45 | fmt.Println(err.Error()) |
1059 | 46 | code = 1 | 46 | code = 1 |
1060 | 47 | } | 47 | } |
1062 | 48 | os.Exit(code) | 48 | } else { |
1063 | 49 | // This kicks off the daemon. See FakeJujuSuite below. | ||
1064 | 50 | t := &testing.T{} | ||
1065 | 51 | coretesting.MgoTestPackage(t) | ||
1066 | 49 | } | 52 | } |
1076 | 50 | t := &testing.T{} | 53 | os.Exit(code) |
1068 | 51 | coretesting.MgoTestPackage(t) | ||
1069 | 52 | } | ||
1070 | 53 | |||
1071 | 54 | type processInfo struct { | ||
1072 | 55 | WorkDir string | ||
1073 | 56 | EndpointAddr string | ||
1074 | 57 | Uuid string | ||
1075 | 58 | CACert string | ||
1077 | 59 | } | 54 | } |
1078 | 60 | 55 | ||
1079 | 61 | func handleCommand(command string) error { | 56 | func handleCommand(command string) error { |
1080 | 57 | filenames := newFakeJujuFilenames("", "", "") | ||
1081 | 62 | if command == "bootstrap" { | 58 | if command == "bootstrap" { |
1083 | 63 | return bootstrap() | 59 | return bootstrap(filenames) |
1084 | 64 | } | 60 | } |
1085 | 65 | if command == "show-controller" { | 61 | if command == "show-controller" { |
1087 | 66 | return apiInfo() | 62 | return apiInfo(filenames) |
1088 | 67 | } | 63 | } |
1089 | 68 | if command == "destroy-controller" { | 64 | if command == "destroy-controller" { |
1091 | 69 | return destroyEnvironment() | 65 | return destroyController(filenames) |
1092 | 70 | } | 66 | } |
1093 | 71 | return errors.New("command not found") | 67 | return errors.New("command not found") |
1094 | 72 | } | 68 | } |
1095 | 73 | 69 | ||
1097 | 74 | func bootstrap() error { | 70 | func bootstrap(filenames fakejujuFilenames) error { |
1098 | 75 | argc := len(os.Args) | 71 | argc := len(os.Args) |
1099 | 76 | if argc < 4 { | 72 | if argc < 4 { |
1100 | 77 | return errors.New( | 73 | return errors.New( |
1101 | 78 | "error: controller name and cloud name are required") | 74 | "error: controller name and cloud name are required") |
1102 | 79 | } | 75 | } |
1104 | 80 | envName := os.Args[argc-2] | 76 | if err := filenames.ensureDirsExist(); err != nil { |
1105 | 77 | return err | ||
1106 | 78 | } | ||
1107 | 79 | controllerName := os.Args[argc-1] | ||
1108 | 81 | command := exec.Command(os.Args[0]) | 80 | command := exec.Command(os.Args[0]) |
1109 | 82 | command.Env = os.Environ() | 81 | command.Env = os.Environ() |
1110 | 83 | command.Env = append( | 82 | command.Env = append( |
1111 | @@ -89,10 +88,16 @@ | |||
1112 | 89 | return err | 88 | return err |
1113 | 90 | } | 89 | } |
1114 | 91 | command.Start() | 90 | command.Start() |
1116 | 92 | apiInfo, err := parseApiInfo(envName, stdout) | 91 | |
1117 | 92 | result, err := parseApiInfo(stdout) | ||
1118 | 93 | if err != nil { | 93 | if err != nil { |
1119 | 94 | return err | 94 | return err |
1120 | 95 | } | 95 | } |
1121 | 96 | if err := result.apply(filenames, controllerName); err != nil { | ||
1122 | 97 | return err | ||
1123 | 98 | } | ||
1124 | 99 | apiInfo := result.apiInfo() | ||
1125 | 100 | |||
1126 | 96 | dialOpts := api.DialOpts{ | 101 | dialOpts := api.DialOpts{ |
1127 | 97 | DialAddressInterval: 50 * time.Millisecond, | 102 | DialAddressInterval: 50 * time.Millisecond, |
1128 | 98 | Timeout: 5 * time.Second, | 103 | Timeout: 5 * time.Second, |
1129 | @@ -122,8 +127,8 @@ | |||
1130 | 122 | return errors.New("invalid delta") | 127 | return errors.New("invalid delta") |
1131 | 123 | } | 128 | } |
1132 | 124 | 129 | ||
1135 | 125 | func apiInfo() error { | 130 | func apiInfo(filenames fakejujuFilenames) error { |
1136 | 126 | info, err := readProcessInfo() | 131 | info, err := readProcessInfo(filenames) |
1137 | 127 | if err != nil { | 132 | if err != nil { |
1138 | 128 | return err | 133 | return err |
1139 | 129 | } | 134 | } |
1140 | @@ -131,22 +136,24 @@ | |||
1141 | 131 | jujuHome := os.Getenv("JUJU_DATA") | 136 | jujuHome := os.Getenv("JUJU_DATA") |
1142 | 132 | osenv.SetJujuXDGDataHome(jujuHome) | 137 | osenv.SetJujuXDGDataHome(jujuHome) |
1143 | 133 | cmd := controller.NewShowControllerCommand() | 138 | cmd := controller.NewShowControllerCommand() |
1147 | 134 | ctx, err := coretesting.RunCommandInDir( | 139 | if err := coretesting.InitCommand(cmd, os.Args[2:]); err != nil { |
1148 | 135 | nil, cmd, os.Args[2:], info.WorkDir) | 140 | return err |
1149 | 136 | if err != nil { | 141 | } |
1150 | 142 | ctx := coretesting.ContextForDir(nil, info.WorkDir) | ||
1151 | 143 | if err := cmd.Run(ctx); err != nil { | ||
1152 | 137 | return err | 144 | return err |
1153 | 138 | } | 145 | } |
1154 | 139 | fmt.Print(ctx.Stdout) | 146 | fmt.Print(ctx.Stdout) |
1155 | 140 | return nil | 147 | return nil |
1156 | 141 | } | 148 | } |
1157 | 142 | 149 | ||
1160 | 143 | func destroyEnvironment() error { | 150 | func destroyController(filenames fakejujuFilenames) error { |
1161 | 144 | info, err := readProcessInfo() | 151 | info, err := readProcessInfo(filenames) |
1162 | 145 | if err != nil { | 152 | if err != nil { |
1163 | 146 | return err | 153 | return err |
1164 | 147 | } | 154 | } |
1167 | 148 | fifoPath := filepath.Join(info.WorkDir, "fifo") | 155 | filenames = newFakeJujuFilenames("", "", info.WorkDir) |
1168 | 149 | fd, err := os.OpenFile(fifoPath, os.O_APPEND|os.O_WRONLY, 0600) | 156 | fd, err := os.OpenFile(filenames.fifoFile(), os.O_APPEND|os.O_WRONLY, 0600) |
1169 | 150 | if err != nil { | 157 | if err != nil { |
1170 | 151 | return err | 158 | return err |
1171 | 152 | } | 159 | } |
1172 | @@ -158,13 +165,214 @@ | |||
1173 | 158 | return nil | 165 | return nil |
1174 | 159 | } | 166 | } |
1175 | 160 | 167 | ||
1177 | 161 | func parseApiInfo(envName string, stdout io.ReadCloser) (*api.Info, error) { | 168 | // processInfo holds all the information that fake-juju uses internally. |
1178 | 169 | type processInfo struct { | ||
1179 | 170 | WorkDir string | ||
1180 | 171 | EndpointAddr string | ||
1181 | 172 | Uuid string | ||
1182 | 173 | CACert []byte | ||
1183 | 174 | } | ||
1184 | 175 | |||
1185 | 176 | func readProcessInfo(filenames fakejujuFilenames) (*processInfo, error) { | ||
1186 | 177 | infoPath := filenames.infoFile() | ||
1187 | 178 | data, err := ioutil.ReadFile(infoPath) | ||
1188 | 179 | if err != nil { | ||
1189 | 180 | return nil, err | ||
1190 | 181 | } | ||
1191 | 182 | info := &processInfo{} | ||
1192 | 183 | err = goyaml.Unmarshal(data, info) | ||
1193 | 184 | if err != nil { | ||
1194 | 185 | return nil, err | ||
1195 | 186 | } | ||
1196 | 187 | return info, nil | ||
1197 | 188 | } | ||
1198 | 189 | |||
1199 | 190 | func (info processInfo) write(infoPath string) error { | ||
1200 | 191 | data, _ := goyaml.Marshal(&info) | ||
1201 | 192 | if err := ioutil.WriteFile(infoPath, data, 0644); err != nil { | ||
1202 | 193 | return err | ||
1203 | 194 | } | ||
1204 | 195 | return nil | ||
1205 | 196 | } | ||
1206 | 197 | |||
1207 | 198 | // fakejujuFilenames encapsulates the paths to all the directories and | ||
1208 | 199 | // files that are relevant to fake-juju. | ||
1209 | 200 | type fakejujuFilenames struct { | ||
1210 | 201 | datadir string | ||
1211 | 202 | logsdir string | ||
1212 | 203 | } | ||
1213 | 204 | |||
1214 | 205 | func newFakeJujuFilenames(datadir, logsdir, jujucfgdir string) fakejujuFilenames { | ||
1215 | 206 | if datadir == "" { | ||
1216 | 207 | datadir = os.Getenv("FAKE_JUJU_DATA_DIR") | ||
1217 | 208 | if datadir == "" { | ||
1218 | 209 | if jujucfgdir == "" { | ||
1219 | 210 | jujucfgdir = os.Getenv("JUJU_DATA") | ||
1220 | 211 | } | ||
1221 | 212 | datadir = jujucfgdir | ||
1222 | 213 | } | ||
1223 | 214 | } | ||
1224 | 215 | if logsdir == "" { | ||
1225 | 216 | logsdir = os.Getenv("FAKE_JUJU_LOGS_DIR") | ||
1226 | 217 | if logsdir == "" { | ||
1227 | 218 | logsdir = datadir | ||
1228 | 219 | } | ||
1229 | 220 | } | ||
1230 | 221 | return fakejujuFilenames{datadir, logsdir} | ||
1231 | 222 | } | ||
1232 | 223 | |||
1233 | 224 | func (fj fakejujuFilenames) ensureDirsExist() error { | ||
1234 | 225 | if err := os.MkdirAll(fj.datadir, 0755); err != nil { | ||
1235 | 226 | return err | ||
1236 | 227 | } | ||
1237 | 228 | if err := os.MkdirAll(fj.logsdir, 0755); err != nil { | ||
1238 | 229 | return err | ||
1239 | 230 | } | ||
1240 | 231 | return nil | ||
1241 | 232 | } | ||
1242 | 233 | |||
1243 | 234 | // infoFile() returns the path to the file that fake-juju uses as | ||
1244 | 235 | // its persistent storage for internal data. | ||
1245 | 236 | func (fj fakejujuFilenames) infoFile() string { | ||
1246 | 237 | return filepath.Join(fj.datadir, "fakejuju") | ||
1247 | 238 | } | ||
1248 | 239 | |||
1249 | 240 | // logsFile() returns the path to the file where fake-juju writes | ||
1250 | 241 | // its logs. Note that the normal Juju logs are not written here. | ||
1251 | 242 | func (fj fakejujuFilenames) logsFile() string { | ||
1252 | 243 | return filepath.Join(fj.logsdir, "fake-juju.log") | ||
1253 | 244 | } | ||
1254 | 245 | |||
1255 | 246 | // fifoFile() returns the path to the FIFO file used by fake-juju. | ||
1256 | 247 | // The FIFO is used by the fake-juju subcommands to interact with | ||
1257 | 248 | // the daemon. | ||
1258 | 249 | func (fj fakejujuFilenames) fifoFile() string { | ||
1259 | 250 | return filepath.Join(fj.datadir, "fifo") | ||
1260 | 251 | } | ||
1261 | 252 | |||
1262 | 253 | // caCertFile() returns the path to the file holding the CA certificate | ||
1263 | 254 | // used by the Juju API server. fake-juju writes the cert there as a | ||
1264 | 255 | // convenience for users. It is not actually used for anything. | ||
1265 | 256 | func (fj fakejujuFilenames) caCertFile() string { | ||
1266 | 257 | return filepath.Join(fj.datadir, "cert.ca") | ||
1267 | 258 | } | ||
1268 | 259 | |||
1269 | 260 | // bootstrapResult encapsulates all significant information that came | ||
1270 | 261 | // from bootstrapping a controller. | ||
1271 | 262 | type bootstrapResult struct { | ||
1272 | 263 | dummyControllerName string | ||
1273 | 264 | cfgdir string | ||
1274 | 265 | uuid string | ||
1275 | 266 | username string | ||
1276 | 267 | password string | ||
1277 | 268 | addresses []string | ||
1278 | 269 | caCert []byte | ||
1279 | 270 | } | ||
1280 | 271 | |||
1281 | 272 | // apiInfo() composes the Juju API info corresponding to the result. | ||
1282 | 273 | func (br bootstrapResult) apiInfo() *api.Info { | ||
1283 | 274 | return &api.Info{ | ||
1284 | 275 | Addrs: br.addresses, | ||
1285 | 276 | Tag: names.NewUserTag(br.username), | ||
1286 | 277 | Password: br.password, | ||
1287 | 278 | CACert: string(br.caCert), | ||
1288 | 279 | ModelTag: names.NewModelTag(br.uuid), | ||
1289 | 280 | } | ||
1290 | 281 | } | ||
1291 | 282 | |||
1292 | 283 | // fakeJujuInfo() composes, from the result, the set of information | ||
1293 | 284 | // that fake-juju should use internally. | ||
1294 | 285 | func (br bootstrapResult) fakeJujuInfo() *processInfo { | ||
1295 | 286 | return &processInfo{ | ||
1296 | 287 | WorkDir: br.cfgdir, | ||
1297 | 288 | EndpointAddr: br.addresses[0], | ||
1298 | 289 | Uuid: br.uuid, | ||
1299 | 290 | CACert: br.caCert, | ||
1300 | 291 | } | ||
1301 | 292 | } | ||
1302 | 293 | |||
1303 | 294 | // logsSymlinkFilenames() determines the source and target paths for | ||
1304 | 295 | // a symlink to the fake-juju logs file. Such a symlink is relevant | ||
1305 | 296 | // because the fake-juju daemon may not know where the log file is | ||
1306 | 297 | // meant to go. It defaults to putting the log file in the default Juju | ||
1307 | 298 | // config dir. In that case, a symlink should be created from there to | ||
1308 | 299 | // the user-defined Juju config dir ($JUJU_DATA). | ||
1309 | 300 | func (br bootstrapResult) logsSymlinkFilenames(targetLogsFile string) (source, target string) { | ||
1310 | 301 | if os.Getenv("FAKE_JUJU_LOGS_DIR") != "" { | ||
1311 | 302 | return "", "" | ||
1312 | 303 | } | ||
1313 | 304 | |||
1314 | 305 | filenames := newFakeJujuFilenames("", "", br.cfgdir) | ||
1315 | 306 | source = filenames.logsFile() | ||
1316 | 307 | target = targetLogsFile | ||
1317 | 308 | return source, target | ||
1318 | 309 | } | ||
1319 | 310 | |||
1320 | 311 | // apply() writes out the information from the bootstrap result to the | ||
1321 | 312 | // various files identified by the provided filenames. | ||
1322 | 313 | func (br bootstrapResult) apply(filenames fakejujuFilenames, controllerName string) error { | ||
1323 | 314 | if err := br.fakeJujuInfo().write(filenames.infoFile()); err != nil { | ||
1324 | 315 | return err | ||
1325 | 316 | } | ||
1326 | 317 | |||
1327 | 318 | logsSource, logsTarget := br.logsSymlinkFilenames(filenames.logsFile()) | ||
1328 | 319 | if logsSource != "" && logsTarget != "" { | ||
1329 | 320 | if err := os.Symlink(logsSource, logsTarget); err != nil { | ||
1330 | 321 | return err | ||
1331 | 322 | } | ||
1332 | 323 | } | ||
1333 | 324 | |||
1334 | 325 | if err := br.copyConfig(os.Getenv("JUJU_DATA"), controllerName); err != nil { | ||
1335 | 326 | return err | ||
1336 | 327 | } | ||
1337 | 328 | |||
1338 | 329 | if err := ioutil.WriteFile(filenames.caCertFile(), br.caCert, 0644); err != nil { | ||
1339 | 330 | return err | ||
1340 | 331 | } | ||
1341 | 332 | |||
1342 | 333 | return nil | ||
1343 | 334 | } | ||
1344 | 335 | |||
1345 | 336 | func (br bootstrapResult) copyConfig(targetCfgDir, controllerName string) error { | ||
1346 | 337 | for _, name := range []string{"controllers.yaml", "models.yaml", "accounts.yaml"} { | ||
1347 | 338 | source := filepath.Join(br.cfgdir, name) | ||
1348 | 339 | target := filepath.Join(targetCfgDir, name) | ||
1349 | 340 | |||
1350 | 341 | input, err := ioutil.ReadFile(source) | ||
1351 | 342 | if err != nil { | ||
1352 | 343 | return err | ||
1353 | 344 | } | ||
1354 | 345 | // Generated configuration by test fixtures has the controller name | ||
1355 | 346 | // hard-coded to "kontroll". A simple replace should fix this for | ||
1356 | 347 | // clients using this config and expecting a specific controller | ||
1357 | 348 | // name. | ||
1358 | 349 | output := strings.Replace(string(input), dummyControllerName, controllerName, -1) | ||
1359 | 350 | err = ioutil.WriteFile(target, []byte(output), 0644) | ||
1360 | 351 | if err != nil { | ||
1361 | 352 | return err | ||
1362 | 353 | } | ||
1363 | 354 | } | ||
1364 | 355 | |||
1365 | 356 | current := filepath.Join(targetCfgDir, "current-controller") | ||
1366 | 357 | if err := ioutil.WriteFile(current, []byte(controllerName), 0644); err != nil { | ||
1367 | 358 | return err | ||
1368 | 359 | } | ||
1369 | 360 | |||
1370 | 361 | return nil | ||
1371 | 362 | } | ||
1372 | 363 | |||
1373 | 364 | // See github.com/juju/juju/blob/juju/testing/conn.go. | ||
1374 | 365 | const dummyControllerName = "kontroll" | ||
1375 | 366 | |||
1376 | 367 | func parseApiInfo(stdout io.ReadCloser) (*bootstrapResult, error) { | ||
1377 | 162 | buffer := bufio.NewReader(stdout) | 368 | buffer := bufio.NewReader(stdout) |
1378 | 369 | |||
1379 | 163 | line, _, err := buffer.ReadLine() | 370 | line, _, err := buffer.ReadLine() |
1380 | 164 | if err != nil { | 371 | if err != nil { |
1381 | 165 | return nil, err | 372 | return nil, err |
1382 | 166 | } | 373 | } |
1383 | 167 | uuid := string(line) | 374 | uuid := string(line) |
1384 | 375 | |||
1385 | 168 | line, _, err = buffer.ReadLine() | 376 | line, _, err = buffer.ReadLine() |
1386 | 169 | if err != nil { | 377 | if err != nil { |
1387 | 170 | return nil, err | 378 | return nil, err |
1388 | @@ -175,8 +383,8 @@ | |||
1389 | 175 | store := jujuclient.NewFileClientStore() | 383 | store := jujuclient.NewFileClientStore() |
1390 | 176 | // hard-coded value in juju testing | 384 | // hard-coded value in juju testing |
1391 | 177 | // This will be replaced in JUJU_DATA copy of the juju client config. | 385 | // This will be replaced in JUJU_DATA copy of the juju client config. |
1394 | 178 | currentController := "kontroll" | 386 | currentController := dummyControllerName |
1395 | 179 | one, err := store.ControllerByName("kontroll") | 387 | one, err := store.ControllerByName(currentController) |
1396 | 180 | if err != nil { | 388 | if err != nil { |
1397 | 181 | return nil, err | 389 | return nil, err |
1398 | 182 | } | 390 | } |
1399 | @@ -185,108 +393,17 @@ | |||
1400 | 185 | if err != nil { | 393 | if err != nil { |
1401 | 186 | return nil, err | 394 | return nil, err |
1402 | 187 | } | 395 | } |
1505 | 188 | apiInfo := &api.Info{ | 396 | |
1506 | 189 | Addrs: one.APIEndpoints, | 397 | result := &bootstrapResult{ |
1507 | 190 | Tag: names.NewUserTag(accountDetails.User), | 398 | dummyControllerName: dummyControllerName, |
1508 | 191 | Password: accountDetails.Password, | 399 | cfgdir: workDir, |
1509 | 192 | CACert: one.CACert, | 400 | uuid: uuid, |
1510 | 193 | ModelTag: names.NewModelTag(uuid), | 401 | username: accountDetails.User, |
1511 | 194 | } | 402 | password: accountDetails.Password, |
1512 | 195 | 403 | addresses: one.APIEndpoints, | |
1513 | 196 | err = writeProcessInfo(envName, &processInfo{ | 404 | caCert: []byte(one.CACert), |
1514 | 197 | WorkDir: workDir, | 405 | } |
1515 | 198 | EndpointAddr: one.APIEndpoints[0], | 406 | return result, nil |
1414 | 199 | Uuid: uuid, | ||
1415 | 200 | CACert: one.CACert, | ||
1416 | 201 | }) | ||
1417 | 202 | if err != nil { | ||
1418 | 203 | return nil, err | ||
1419 | 204 | } | ||
1420 | 205 | return apiInfo, nil | ||
1421 | 206 | } | ||
1422 | 207 | |||
1423 | 208 | func readProcessInfo() (*processInfo, error) { | ||
1424 | 209 | infoPath := filepath.Join(os.Getenv("JUJU_DATA"), "fakejuju") | ||
1425 | 210 | data, err := ioutil.ReadFile(infoPath) | ||
1426 | 211 | if err != nil { | ||
1427 | 212 | return nil, err | ||
1428 | 213 | } | ||
1429 | 214 | info := &processInfo{} | ||
1430 | 215 | err = goyaml.Unmarshal(data, info) | ||
1431 | 216 | if err != nil { | ||
1432 | 217 | return nil, err | ||
1433 | 218 | } | ||
1434 | 219 | return info, nil | ||
1435 | 220 | } | ||
1436 | 221 | |||
1437 | 222 | func writeProcessInfo(envName string, info *processInfo) error { | ||
1438 | 223 | var err error | ||
1439 | 224 | jujuHome := os.Getenv("JUJU_DATA") | ||
1440 | 225 | infoPath := filepath.Join(jujuHome, "fakejuju") | ||
1441 | 226 | logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR") | ||
1442 | 227 | if logsDir == "" { | ||
1443 | 228 | logsDir = jujuHome | ||
1444 | 229 | } | ||
1445 | 230 | logPath := filepath.Join(logsDir, "fake-juju.log") | ||
1446 | 231 | caCertPath := filepath.Join(jujuHome, "cert.ca") | ||
1447 | 232 | data, _ := goyaml.Marshal(info) | ||
1448 | 233 | if os.Getenv("FAKE_JUJU_LOGS_DIR") == "" { | ||
1449 | 234 | err = os.Symlink(filepath.Join(info.WorkDir, "fake-juju.log"), logPath) | ||
1450 | 235 | if err != nil { | ||
1451 | 236 | return err | ||
1452 | 237 | } | ||
1453 | 238 | } | ||
1454 | 239 | |||
1455 | 240 | err = copyClientConfig( | ||
1456 | 241 | filepath.Join(info.WorkDir, "controllers.yaml"), | ||
1457 | 242 | filepath.Join(jujuHome, "controllers.yaml"), | ||
1458 | 243 | envName) | ||
1459 | 244 | if err != nil { | ||
1460 | 245 | return err | ||
1461 | 246 | } | ||
1462 | 247 | err = copyClientConfig( | ||
1463 | 248 | filepath.Join(info.WorkDir, "models.yaml"), | ||
1464 | 249 | filepath.Join(jujuHome, "models.yaml"), | ||
1465 | 250 | envName) | ||
1466 | 251 | if err != nil { | ||
1467 | 252 | return err | ||
1468 | 253 | } | ||
1469 | 254 | err = copyClientConfig( | ||
1470 | 255 | filepath.Join(info.WorkDir, "accounts.yaml"), | ||
1471 | 256 | filepath.Join(jujuHome, "accounts.yaml"), | ||
1472 | 257 | envName) | ||
1473 | 258 | if err != nil { | ||
1474 | 259 | return err | ||
1475 | 260 | } | ||
1476 | 261 | err = ioutil.WriteFile( | ||
1477 | 262 | filepath.Join(jujuHome, "current-controller"), | ||
1478 | 263 | []byte(envName), 0644) | ||
1479 | 264 | if err != nil { | ||
1480 | 265 | return err | ||
1481 | 266 | } | ||
1482 | 267 | |||
1483 | 268 | err = ioutil.WriteFile(infoPath, data, 0644) | ||
1484 | 269 | if err != nil { | ||
1485 | 270 | return err | ||
1486 | 271 | } | ||
1487 | 272 | return ioutil.WriteFile(caCertPath, []byte(info.CACert), 0644) | ||
1488 | 273 | } | ||
1489 | 274 | |||
1490 | 275 | func copyClientConfig(src string, dst string, envName string) error { | ||
1491 | 276 | input, err := ioutil.ReadFile(src) | ||
1492 | 277 | if err != nil { | ||
1493 | 278 | return err | ||
1494 | 279 | } | ||
1495 | 280 | // Generated configuration by test fixtures has the controller name | ||
1496 | 281 | // hard-coded to "kontroll". A simple replace should fix this for | ||
1497 | 282 | // clients using this config and expecting a specific controller | ||
1498 | 283 | // name. | ||
1499 | 284 | output := strings.Replace(string(input), "kontroll", envName, -1) | ||
1500 | 285 | err = ioutil.WriteFile(dst, []byte(output), 0644) | ||
1501 | 286 | if err != nil { | ||
1502 | 287 | return err | ||
1503 | 288 | } | ||
1504 | 289 | return nil | ||
1516 | 290 | } | 407 | } |
1517 | 291 | 408 | ||
1518 | 292 | // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment | 409 | // Read the failures info file pointed by the FAKE_JUJU_FAILURES environment |
1519 | @@ -326,12 +443,16 @@ | |||
1520 | 326 | return failuresInfo, nil | 443 | return failuresInfo, nil |
1521 | 327 | } | 444 | } |
1522 | 328 | 445 | ||
1523 | 446 | //=================================================================== | ||
1524 | 447 | // The fake-juju daemon (started by bootstrap) is found here. It is | ||
1525 | 448 | // implemented as a test suite. | ||
1526 | 449 | |||
1527 | 329 | type FakeJujuSuite struct { | 450 | type FakeJujuSuite struct { |
1528 | 330 | jujutesting.JujuConnSuite | 451 | jujutesting.JujuConnSuite |
1529 | 331 | 452 | ||
1530 | 332 | instanceCount int | 453 | instanceCount int |
1531 | 333 | machineStarted map[string]bool | 454 | machineStarted map[string]bool |
1533 | 334 | fifoPath string | 455 | filenames fakejujuFilenames |
1534 | 335 | logFile *os.File | 456 | logFile *os.File |
1535 | 336 | } | 457 | } |
1536 | 337 | 458 | ||
1537 | @@ -371,7 +492,7 @@ | |||
1538 | 371 | c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil) | 492 | c.Assert(stateServer.SetProviderAddresses(address), gc.IsNil) |
1539 | 372 | now := time.Now() | 493 | now := time.Now() |
1540 | 373 | sInfo := states.StatusInfo{ | 494 | sInfo := states.StatusInfo{ |
1542 | 374 | Status: states.StatusStarted, | 495 | Status: states.Started, |
1543 | 375 | Message: "", | 496 | Message: "", |
1544 | 376 | Since: &now, | 497 | Since: &now, |
1545 | 377 | } | 498 | } |
1546 | @@ -398,20 +519,16 @@ | |||
1547 | 398 | c.Assert(err, gc.IsNil) | 519 | c.Assert(err, gc.IsNil) |
1548 | 399 | os.Setenv("PATH", binPath+":"+os.Getenv("PATH")) | 520 | os.Setenv("PATH", binPath+":"+os.Getenv("PATH")) |
1549 | 400 | 521 | ||
1552 | 401 | s.fifoPath = filepath.Join(jujuHome, "fifo") | 522 | s.filenames = newFakeJujuFilenames("", "", jujuHome) |
1553 | 402 | syscall.Mknod(s.fifoPath, syscall.S_IFIFO|0666, 0) | 523 | syscall.Mknod(s.filenames.fifoFile(), syscall.S_IFIFO|0666, 0) |
1554 | 403 | 524 | ||
1555 | 404 | // Logging | 525 | // Logging |
1561 | 405 | logsDir := os.Getenv("FAKE_JUJU_LOGS_DIR") | 526 | logPath := s.filenames.logsFile() |
1557 | 406 | if logsDir == "" { | ||
1558 | 407 | logsDir = jujuHome | ||
1559 | 408 | } | ||
1560 | 409 | logPath := filepath.Join(logsDir, "fake-juju.log") | ||
1562 | 410 | s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | 527 | s.logFile, err = os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) |
1563 | 411 | c.Assert(err, gc.IsNil) | 528 | c.Assert(err, gc.IsNil) |
1564 | 412 | 529 | ||
1565 | 413 | log.SetOutput(s.logFile) | 530 | log.SetOutput(s.logFile) |
1567 | 414 | log.Println("Started fake-juju at", jujuHome) | 531 | log.Println("Started fake-juju at ", jujuHome) |
1568 | 415 | 532 | ||
1569 | 416 | } | 533 | } |
1570 | 417 | 534 | ||
1571 | @@ -423,16 +540,30 @@ | |||
1572 | 423 | } | 540 | } |
1573 | 424 | 541 | ||
1574 | 425 | func (s *FakeJujuSuite) TestStart(c *gc.C) { | 542 | func (s *FakeJujuSuite) TestStart(c *gc.C) { |
1575 | 543 | fifoPath := s.filenames.fifoFile() | ||
1576 | 426 | watcher := s.State.Watch() | 544 | watcher := s.State.Watch() |
1577 | 427 | go func() { | 545 | go func() { |
1580 | 428 | log.Println("Open commands FIFO", s.fifoPath) | 546 | log.Println("Open commands FIFO", fifoPath) |
1581 | 429 | fd, err := os.Open(s.fifoPath) | 547 | fd, err := os.Open(fifoPath) |
1582 | 430 | if err != nil { | 548 | if err != nil { |
1583 | 431 | log.Println("Failed to open commands FIFO") | 549 | log.Println("Failed to open commands FIFO") |
1584 | 432 | } | 550 | } |
1585 | 433 | c.Assert(err, gc.IsNil) | 551 | c.Assert(err, gc.IsNil) |
1586 | 552 | defer func() { | ||
1587 | 553 | if err := fd.Close(); err != nil { | ||
1588 | 554 | c.Logf("failed closing FIFO file: %s", err) | ||
1589 | 555 | } | ||
1590 | 556 | // Mark the controller as destroyed by renaming some files. | ||
1591 | 557 | if err := os.Rename(fifoPath, fifoPath+".destroyed"); err != nil { | ||
1592 | 558 | c.Logf("failed renaming FIFO file: %s", err) | ||
1593 | 559 | } | ||
1594 | 560 | infofile := s.filenames.infoFile() | ||
1595 | 561 | if err := os.Rename(infofile, infofile+".destroyed"); err != nil { | ||
1596 | 562 | c.Logf("failed renaming info file: %s", err) | ||
1597 | 563 | } | ||
1598 | 564 | }() | ||
1599 | 434 | scanner := bufio.NewScanner(fd) | 565 | scanner := bufio.NewScanner(fd) |
1601 | 435 | log.Println("Listen for commands on FIFO", s.fifoPath) | 566 | log.Println("Listen for commands on FIFO", fifoPath) |
1602 | 436 | scanner.Scan() | 567 | scanner.Scan() |
1603 | 437 | log.Println("Stopping fake-juju") | 568 | log.Println("Stopping fake-juju") |
1604 | 438 | watcher.Stop() | 569 | watcher.Stop() |
1605 | @@ -495,12 +626,12 @@ | |||
1606 | 495 | } | 626 | } |
1607 | 496 | status, _ := machine.Status() | 627 | status, _ := machine.Status() |
1608 | 497 | log.Println("Machine has status:", string(status.Status), status.Message) | 628 | log.Println("Machine has status:", string(status.Status), status.Message) |
1610 | 498 | if status.Status == states.StatusPending { | 629 | if status.Status == states.Pending { |
1611 | 499 | if err = s.startMachine(machine); err != nil { | 630 | if err = s.startMachine(machine); err != nil { |
1612 | 500 | log.Println("Got error with startMachine:", err) | 631 | log.Println("Got error with startMachine:", err) |
1613 | 501 | return err | 632 | return err |
1614 | 502 | } | 633 | } |
1616 | 503 | } else if status.Status == states.StatusStarted { | 634 | } else if status.Status == states.Started { |
1617 | 504 | log.Println("Starting units on machine", id) | 635 | log.Println("Starting units on machine", id) |
1618 | 505 | if _, ok := s.machineStarted[id]; !ok { | 636 | if _, ok := s.machineStarted[id]; !ok { |
1619 | 506 | s.machineStarted[id] = true | 637 | s.machineStarted[id] = true |
1620 | @@ -531,19 +662,19 @@ | |||
1621 | 531 | return err | 662 | return err |
1622 | 532 | } | 663 | } |
1623 | 533 | machineStatus, _ := machine.Status() | 664 | machineStatus, _ := machine.Status() |
1625 | 534 | if machineStatus.Status != states.StatusStarted { | 665 | if machineStatus.Status != states.Started { |
1626 | 535 | return nil | 666 | return nil |
1627 | 536 | } | 667 | } |
1628 | 537 | status, _ := unit.Status() | 668 | status, _ := unit.Status() |
1629 | 538 | log.Println("Unit has status", string(status.Status), status.Message) | 669 | log.Println("Unit has status", string(status.Status), status.Message) |
1631 | 539 | if status.Status != states.StatusActive && status.Status != states.StatusError { | 670 | if status.Status != states.Active && status.Status != states.Error { |
1632 | 540 | log.Println("Start unit", id) | 671 | log.Println("Start unit", id) |
1633 | 541 | err = s.startUnit(unit) | 672 | err = s.startUnit(unit) |
1634 | 542 | if err != nil { | 673 | if err != nil { |
1635 | 543 | log.Println("Got error changing unit status", id, err) | 674 | log.Println("Got error changing unit status", id, err) |
1636 | 544 | return err | 675 | return err |
1637 | 545 | } | 676 | } |
1639 | 546 | } else if status.Status != states.StatusError { | 677 | } else if status.Status != states.Error { |
1640 | 547 | failuresInfo, err := readFailuresInfo() | 678 | failuresInfo, err := readFailuresInfo() |
1641 | 548 | if err != nil { | 679 | if err != nil { |
1642 | 549 | return err | 680 | return err |
1643 | @@ -554,7 +685,7 @@ | |||
1644 | 554 | log.Println("Got error checking agent status", id, err) | 685 | log.Println("Got error checking agent status", id, err) |
1645 | 555 | return err | 686 | return err |
1646 | 556 | } | 687 | } |
1648 | 557 | if agentStatus.Status != states.StatusError { | 688 | if agentStatus.Status != states.Error { |
1649 | 558 | log.Println("Error unit", id) | 689 | log.Println("Error unit", id) |
1650 | 559 | err = s.errorUnit(unit) | 690 | err = s.errorUnit(unit) |
1651 | 560 | if err != nil { | 691 | if err != nil { |
1652 | @@ -571,7 +702,7 @@ | |||
1653 | 571 | time.Sleep(500 * time.Millisecond) | 702 | time.Sleep(500 * time.Millisecond) |
1654 | 572 | now := time.Now() | 703 | now := time.Now() |
1655 | 573 | sInfo := states.StatusInfo{ | 704 | sInfo := states.StatusInfo{ |
1657 | 574 | Status: states.StatusStarted, | 705 | Status: states.Started, |
1658 | 575 | Message: "", | 706 | Message: "", |
1659 | 576 | Since: &now, | 707 | Since: &now, |
1660 | 577 | } | 708 | } |
1661 | @@ -604,7 +735,7 @@ | |||
1662 | 604 | time.Sleep(500 * time.Millisecond) | 735 | time.Sleep(500 * time.Millisecond) |
1663 | 605 | now := time.Now() | 736 | now := time.Now() |
1664 | 606 | sInfo := states.StatusInfo{ | 737 | sInfo := states.StatusInfo{ |
1666 | 607 | Status: states.StatusError, | 738 | Status: states.Error, |
1667 | 608 | Message: "machine errored", | 739 | Message: "machine errored", |
1668 | 609 | Since: &now, | 740 | Since: &now, |
1669 | 610 | } | 741 | } |
1670 | @@ -623,7 +754,7 @@ | |||
1671 | 623 | return nil | 754 | return nil |
1672 | 624 | for _, unit := range units { | 755 | for _, unit := range units { |
1673 | 625 | unitStatus, _ := unit.Status() | 756 | unitStatus, _ := unit.Status() |
1675 | 626 | if unitStatus.Status != states.StatusActive { | 757 | if unitStatus.Status != states.Active { |
1676 | 627 | if err = s.startUnit(unit); err != nil { | 758 | if err = s.startUnit(unit); err != nil { |
1677 | 628 | return err | 759 | return err |
1678 | 629 | } | 760 | } |
1679 | @@ -635,7 +766,7 @@ | |||
1680 | 635 | func (s *FakeJujuSuite) startUnit(unit *state.Unit) error { | 766 | func (s *FakeJujuSuite) startUnit(unit *state.Unit) error { |
1681 | 636 | now := time.Now() | 767 | now := time.Now() |
1682 | 637 | sInfo := states.StatusInfo{ | 768 | sInfo := states.StatusInfo{ |
1684 | 638 | Status: states.StatusStarted, | 769 | Status: states.Started, |
1685 | 639 | Message: "", | 770 | Message: "", |
1686 | 640 | Since: &now, | 771 | Since: &now, |
1687 | 641 | } | 772 | } |
1688 | @@ -653,7 +784,7 @@ | |||
1689 | 653 | return err | 784 | return err |
1690 | 654 | } | 785 | } |
1691 | 655 | idleInfo := states.StatusInfo{ | 786 | idleInfo := states.StatusInfo{ |
1693 | 656 | Status: states.StatusIdle, | 787 | Status: states.Idle, |
1694 | 657 | Message: "", | 788 | Message: "", |
1695 | 658 | Since: &now, | 789 | Since: &now, |
1696 | 659 | } | 790 | } |
1697 | @@ -668,7 +799,7 @@ | |||
1698 | 668 | log.Println("Erroring unit", unit.Name()) | 799 | log.Println("Erroring unit", unit.Name()) |
1699 | 669 | now := time.Now() | 800 | now := time.Now() |
1700 | 670 | sInfo := states.StatusInfo{ | 801 | sInfo := states.StatusInfo{ |
1702 | 671 | Status: states.StatusIdle, | 802 | Status: states.Idle, |
1703 | 672 | Message: "unit errored", | 803 | Message: "unit errored", |
1704 | 673 | Since: &now, | 804 | Since: &now, |
1705 | 674 | } | 805 | } |
1706 | 675 | 806 | ||
1707 | === modified file 'Makefile' | |||
1708 | --- Makefile 2016-09-20 18:26:47 +0000 | |||
1709 | +++ Makefile 2016-10-25 17:25:06 +0000 | |||
1710 | @@ -11,7 +11,7 @@ | |||
1711 | 11 | INSTALLDIR = $(DESTDIR)/usr/bin | 11 | INSTALLDIR = $(DESTDIR)/usr/bin |
1712 | 12 | INSTALLED = $(INSTALLDIR)/fake-juju-$(JUJU_VERSION) | 12 | INSTALLED = $(INSTALLDIR)/fake-juju-$(JUJU_VERSION) |
1713 | 13 | 13 | ||
1715 | 14 | $(JUJU_VERSION)/$(JUJU_VERSION): | 14 | $(JUJU_VERSION)/$(JUJU_VERSION): $(JUJU_VERSION)/fake-juju.go |
1716 | 15 | case $(JUJU_VERSION) in \ | 15 | case $(JUJU_VERSION) in \ |
1717 | 16 | 1.*) $(MAKE) build-common PATH=$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\ | 16 | 1.*) $(MAKE) build-common PATH=$$PATH JUJU_VERSION=$(JUJU_VERSION) ;;\ |
1718 | 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) ;;\ |
1719 | @@ -59,8 +59,8 @@ | |||
1720 | 59 | else ########################################### | 59 | else ########################################### |
1721 | 60 | # for all versions | 60 | # for all versions |
1722 | 61 | 61 | ||
1725 | 62 | JUJU1_VERSIONS = 1.24.7 1.25.6 | 62 | JUJU1_VERSIONS = 1.25.6 |
1726 | 63 | JUJU2_VERSIONS = 2.0-beta17 | 63 | JUJU2_VERSIONS = 2.0.0 |
1727 | 64 | VERSIONS = $(JUJU1_VERSIONS) $(JUJU2_VERSIONS) | 64 | VERSIONS = $(JUJU1_VERSIONS) $(JUJU2_VERSIONS) |
1728 | 65 | BUILT_VERSIONS = $(foreach version,$(VERSIONS),$(version)/$(version)) | 65 | BUILT_VERSIONS = $(foreach version,$(VERSIONS),$(version)/$(version)) |
1729 | 66 | 66 | ||
1730 | 67 | 67 | ||
1731 | === removed file 'patches/juju-core_1.24.7.patch' | |||
1732 | --- patches/juju-core_1.24.7.patch 2016-03-18 11:10:52 +0000 | |||
1733 | +++ patches/juju-core_1.24.7.patch 1970-01-01 00:00:00 +0000 | |||
1734 | @@ -1,47 +0,0 @@ | |||
1735 | 1 | --- 1.24.7/src/github.com/juju/juju/testcharms/charm.go.orig 2015-06-24 12:02:02.746416146 +0200 | ||
1736 | 2 | +++ 1.24.7/src/github.com/juju/juju/testcharms/charm.go 2015-06-24 12:03:49.810418650 +0200 | ||
1737 | 3 | @@ -10,4 +10,6 @@ | ||
1738 | 4 | ) | ||
1739 | 5 | |||
1740 | 6 | // Repo provides access to the test charm repository. | ||
1741 | 7 | -var Repo = testing.NewRepo("charm-repo", "quantal") | ||
1742 | 8 | +// XXX fake-juju: avoid crashing because the charm-repo dir is not there | ||
1743 | 9 | +//var Repo = testing.NewRepo("charm-repo", "quantal") | ||
1744 | 10 | +var Repo = &testing.Repo{} | ||
1745 | 11 | |||
1746 | 12 | --- 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go.orig 2015-07-06 15:01:14.200568258 +0200 | ||
1747 | 13 | +++ 1.24.7/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200 | ||
1748 | 14 | @@ -642,9 +642,9 @@ | ||
1749 | 15 | |||
1750 | 16 | // PrecheckInstance is specified in the state.Prechecker interface. | ||
1751 | 17 | func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error { | ||
1752 | 18 | - if placement != "" && placement != "valid" { | ||
1753 | 19 | - return fmt.Errorf("%s placement is invalid", placement) | ||
1754 | 20 | - } | ||
1755 | 21 | +// if placement != "" && placement != "valid" { | ||
1756 | 22 | +// return fmt.Errorf("%s placement is invalid", placement) | ||
1757 | 23 | +// } | ||
1758 | 24 | return nil | ||
1759 | 25 | } | ||
1760 | 26 | |||
1761 | 27 | --- 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000 | ||
1762 | 28 | +++ 1.24.7/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000 | ||
1763 | 29 | @@ -52,7 +52,7 @@ | ||
1764 | 30 | } | ||
1765 | 31 | |||
1766 | 32 | func mustNewCA() (string, string) { | ||
1767 | 33 | - cert.KeyBits = 512 | ||
1768 | 34 | + cert.KeyBits = 1024 | ||
1769 | 35 | caCert, caKey, err := cert.NewCA("juju testing", time.Now().AddDate(10, 0, 0)) | ||
1770 | 36 | if err != nil { | ||
1771 | 37 | panic(err) | ||
1772 | 38 | @@ -61,7 +61,7 @@ | ||
1773 | 39 | } | ||
1774 | 40 | |||
1775 | 41 | func mustNewServer() (string, string) { | ||
1776 | 42 | - cert.KeyBits = 512 | ||
1777 | 43 | + cert.KeyBits = 1024 | ||
1778 | 44 | var hostnames []string | ||
1779 | 45 | srvCert, srvKey, err := cert.NewServer(CACert, CAKey, time.Now().AddDate(10, 0, 0), hostnames) | ||
1780 | 46 | if err != nil { | ||
1781 | 47 | |||
1782 | 48 | 0 | ||
1783 | === renamed file 'patches/juju-core_2.0-beta17.patch' => 'patches/juju-core_2.0.0.patch' | |||
1784 | --- patches/juju-core_2.0-beta17.patch 2016-09-01 22:03:22 +0000 | |||
1785 | +++ patches/juju-core_2.0.0.patch 2016-10-25 17:25:06 +0000 | |||
1786 | @@ -1,5 +1,5 @@ | |||
1789 | 1 | --- 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +0100 | 1 | --- 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-10 13:45:57.000000000 +0100 |
1790 | 2 | +++ 2.0-beta17/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +0100 | 2 | +++ 2.0.0/src/github.com/juju/juju/testcharms/charm.go 2016-03-21 10:46:24.312966629 +0100 |
1791 | 3 | @@ -17,7 +17,9 @@ | 3 | @@ -17,7 +17,9 @@ |
1792 | 4 | ) | 4 | ) |
1793 | 5 | 5 | ||
1794 | @@ -11,8 +11,8 @@ | |||
1795 | 11 | 11 | ||
1796 | 12 | // UploadCharm uploads a charm using the given charm store client, and returns | 12 | // UploadCharm uploads a charm using the given charm store client, and returns |
1797 | 13 | // the resulting charm URL and charm. | 13 | // the resulting charm URL and charm. |
1800 | 14 | --- 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +0200 | 14 | --- 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:01:14.200568258 +0200 |
1801 | 15 | +++ 2.0-beta17/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200 | 15 | +++ 2.0.0/src/github.com/juju/juju/provider/dummy/environs.go 2015-07-06 15:18:32.648549661 +0200 |
1802 | 16 | @@ -633,9 +633,9 @@ | 16 | @@ -633,9 +633,9 @@ |
1803 | 17 | 17 | ||
1804 | 18 | // PrecheckInstance is specified in the state.Prechecker interface. | 18 | // PrecheckInstance is specified in the state.Prechecker interface. |
1805 | @@ -26,8 +26,8 @@ | |||
1806 | 26 | return nil | 26 | return nil |
1807 | 27 | } | 27 | } |
1808 | 28 | 28 | ||
1811 | 29 | --- 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000 | 29 | --- 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:25:34 +0000 |
1812 | 30 | +++ 2.0-beta17/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000 | 30 | +++ 2.0.0/src/github.com/juju/juju/testing/cert.go 2016-03-18 09:26:04 +0000 |
1813 | 31 | @@ -52,7 +52,7 @@ | 31 | @@ -52,7 +52,7 @@ |
1814 | 32 | } | 32 | } |
1815 | 33 | 33 | ||
1816 | 34 | 34 | ||
1817 | === modified file 'python/Makefile' | |||
1818 | --- python/Makefile 2016-10-06 21:44:31 +0000 | |||
1819 | +++ python/Makefile 2016-10-25 17:25:06 +0000 | |||
1820 | @@ -6,4 +6,4 @@ | |||
1821 | 6 | 6 | ||
1822 | 7 | .PHONY: install-dev | 7 | .PHONY: install-dev |
1823 | 8 | install-dev: | 8 | install-dev: |
1825 | 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 |
1826 | 10 | 10 | ||
1827 | === modified file 'python/fakejuju/fakejuju.py' | |||
1828 | --- python/fakejuju/fakejuju.py 2016-10-17 15:54:59 +0000 | |||
1829 | +++ python/fakejuju/fakejuju.py 2016-10-25 17:25:06 +0000 | |||
1830 | @@ -2,6 +2,7 @@ | |||
1831 | 2 | 2 | ||
1832 | 3 | import os.path | 3 | import os.path |
1833 | 4 | 4 | ||
1834 | 5 | import txjuju | ||
1835 | 5 | import txjuju.cli | 6 | import txjuju.cli |
1836 | 6 | 7 | ||
1837 | 7 | from .failures import Failures | 8 | from .failures import Failures |
1838 | @@ -23,15 +24,17 @@ | |||
1839 | 23 | return os.path.join(bindir, filename) | 24 | return os.path.join(bindir, filename) |
1840 | 24 | 25 | ||
1841 | 25 | 26 | ||
1843 | 26 | def set_envvars(envvars, failures_filename=None, logsdir=None): | 27 | def set_envvars(envvars, datadir=None, failures_filename=None, logsdir=None): |
1844 | 27 | """Return the environment variables with which to run fake-juju. | 28 | """Return the environment variables with which to run fake-juju. |
1845 | 28 | 29 | ||
1846 | 29 | @param envvars: The env dict to update. | 30 | @param envvars: The env dict to update. |
1847 | 31 | @param datadir: The fake-juju data directory. | ||
1848 | 30 | @param failures_filename: The path to the failures file that | 32 | @param failures_filename: The path to the failures file that |
1849 | 31 | fake-juju will use. | 33 | fake-juju will use. |
1850 | 32 | @params logsdir: The path to the directory where fake-juju will | 34 | @params logsdir: The path to the directory where fake-juju will |
1851 | 33 | write its log files. | 35 | write its log files. |
1852 | 34 | """ | 36 | """ |
1853 | 37 | envvars["FAKE_JUJU_DATA_DIR"] = datadir or "" | ||
1854 | 35 | envvars["FAKE_JUJU_FAILURES"] = failures_filename or "" | 38 | envvars["FAKE_JUJU_FAILURES"] = failures_filename or "" |
1855 | 36 | envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or "" | 39 | envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or "" |
1856 | 37 | 40 | ||
1857 | @@ -40,46 +43,47 @@ | |||
1858 | 40 | """The fundamental details for fake-juju.""" | 43 | """The fundamental details for fake-juju.""" |
1859 | 41 | 44 | ||
1860 | 42 | @classmethod | 45 | @classmethod |
1862 | 43 | def from_version(cls, version, cfgdir, | 46 | def from_version(cls, version, datadir, |
1863 | 44 | logsdir=None, failuresdir=None, bindir=None): | 47 | logsdir=None, failuresdir=None, bindir=None): |
1864 | 45 | """Return a new instance given the provided information. | 48 | """Return a new instance given the provided information. |
1865 | 46 | 49 | ||
1866 | 47 | @param version: The Juju version to fake. | 50 | @param version: The Juju version to fake. |
1868 | 48 | @param cfgdir: The "juju home" directory to use. | 51 | @param datadir: The directory in which to store files specific |
1869 | 52 | to fake-juju. | ||
1870 | 49 | @param logsdir: The directory where logs will be written. | 53 | @param logsdir: The directory where logs will be written. |
1872 | 50 | This defaults to cfgdir. | 54 | This defaults to datadir. |
1873 | 51 | @params failuresdir: The directory where failure injection | 55 | @params failuresdir: The directory where failure injection |
1874 | 52 | is managed. | 56 | is managed. |
1875 | 53 | @param bindir: The directory containing the fake-juju binary. | 57 | @param bindir: The directory containing the fake-juju binary. |
1876 | 54 | This defaults to /usr/bin. | 58 | This defaults to /usr/bin. |
1877 | 55 | """ | 59 | """ |
1878 | 56 | if logsdir is None: | ||
1879 | 57 | logsdir = cfgdir | ||
1880 | 58 | if failuresdir is None: | 60 | if failuresdir is None: |
1882 | 59 | failuresdir = cfgdir | 61 | failuresdir = datadir |
1883 | 60 | filename = get_filename(version, bindir=bindir) | 62 | filename = get_filename(version, bindir=bindir) |
1884 | 61 | failures = Failures(failuresdir) | 63 | failures = Failures(failuresdir) |
1886 | 62 | return cls(filename, version, cfgdir, logsdir, failures) | 64 | return cls(filename, version, datadir, logsdir, failures) |
1887 | 63 | 65 | ||
1889 | 64 | def __init__(self, filename, version, cfgdir, logsdir=None, failures=None): | 66 | def __init__(self, filename, version, datadir, |
1890 | 67 | logsdir=None, failures=None): | ||
1891 | 65 | """ | 68 | """ |
1892 | 66 | @param filename: The path to the fake-juju binary. | 69 | @param filename: The path to the fake-juju binary. |
1893 | 67 | @param version: The Juju version to fake. | 70 | @param version: The Juju version to fake. |
1895 | 68 | @param cfgdir: The "juju home" directory to use. | 71 | @param datadir: The directory in which to store files specific |
1896 | 72 | to fake-juju. | ||
1897 | 69 | @param logsdir: The directory where logs will be written. | 73 | @param logsdir: The directory where logs will be written. |
1899 | 70 | This defaults to cfgdir. | 74 | This defaults to datadir. |
1900 | 71 | @param failures: The set of fake-juju failures to use. | 75 | @param failures: The set of fake-juju failures to use. |
1901 | 72 | """ | 76 | """ |
1905 | 73 | logsdir = logsdir if logsdir is not None else cfgdir | 77 | logsdir = logsdir if logsdir is not None else datadir |
1906 | 74 | if failures is None and cfgdir: | 78 | if failures is None and datadir: |
1907 | 75 | failures = Failures(cfgdir) | 79 | failures = Failures(datadir) |
1908 | 76 | 80 | ||
1909 | 77 | if not filename: | 81 | if not filename: |
1910 | 78 | raise ValueError("missing filename") | 82 | raise ValueError("missing filename") |
1911 | 79 | if not version: | 83 | if not version: |
1912 | 80 | raise ValueError("missing version") | 84 | raise ValueError("missing version") |
1915 | 81 | if not cfgdir: | 85 | if not datadir: |
1916 | 82 | raise ValueError("missing cfgdir") | 86 | raise ValueError("missing datadir") |
1917 | 83 | if not logsdir: | 87 | if not logsdir: |
1918 | 84 | raise ValueError("missing logsdir") | 88 | raise ValueError("missing logsdir") |
1919 | 85 | if failures is None: | 89 | if failures is None: |
1920 | @@ -87,7 +91,7 @@ | |||
1921 | 87 | 91 | ||
1922 | 88 | self.filename = filename | 92 | self.filename = filename |
1923 | 89 | self.version = version | 93 | self.version = version |
1925 | 90 | self.cfgdir = cfgdir | 94 | self.datadir = datadir |
1926 | 91 | self.logsdir = logsdir | 95 | self.logsdir = logsdir |
1927 | 92 | self.failures = failures | 96 | self.failures = failures |
1928 | 93 | 97 | ||
1929 | @@ -99,19 +103,19 @@ | |||
1930 | 99 | @property | 103 | @property |
1931 | 100 | def infofile(self): | 104 | def infofile(self): |
1932 | 101 | """The path to fake-juju's data cache.""" | 105 | """The path to fake-juju's data cache.""" |
1934 | 102 | return os.path.join(self.cfgdir, "fakejuju") | 106 | return os.path.join(self.datadir, "fakejuju") |
1935 | 103 | 107 | ||
1936 | 104 | @property | 108 | @property |
1937 | 105 | def fifo(self): | 109 | def fifo(self): |
1938 | 106 | """The path to the fifo file that triggers shutdown.""" | 110 | """The path to the fifo file that triggers shutdown.""" |
1940 | 107 | return os.path.join(self.cfgdir, "fifo") | 111 | return os.path.join(self.datadir, "fifo") |
1941 | 108 | 112 | ||
1942 | 109 | @property | 113 | @property |
1943 | 110 | def cacertfile(self): | 114 | def cacertfile(self): |
1944 | 111 | """The path to the API server's certificate.""" | 115 | """The path to the API server's certificate.""" |
1946 | 112 | return os.path.join(self.cfgdir, "cert.ca") | 116 | return os.path.join(self.datadir, "cert.ca") |
1947 | 113 | 117 | ||
1949 | 114 | def cli(self, envvars=None): | 118 | def cli(self, cfgdir, envvars=None): |
1950 | 115 | """Return the txjuju.cli.CLI for this fake-juju. | 119 | """Return the txjuju.cli.CLI for this fake-juju. |
1951 | 116 | 120 | ||
1952 | 117 | Currently fake-juju supports only the following juju subcommands: | 121 | Currently fake-juju supports only the following juju subcommands: |
1953 | @@ -123,10 +127,27 @@ | |||
1954 | 123 | Note that passwords are always omited, even if requested. | 127 | Note that passwords are always omited, even if requested. |
1955 | 124 | * api-endpoints | 128 | * api-endpoints |
1956 | 125 | * destroy-environment | 129 | * destroy-environment |
1957 | 130 | |||
1958 | 131 | Note that fake-juju ignores local config files. | ||
1959 | 126 | """ | 132 | """ |
1960 | 127 | if envvars is None: | 133 | if envvars is None: |
1961 | 128 | envvars = os.environ | 134 | envvars = os.environ |
1962 | 129 | envvars = dict(envvars) | 135 | envvars = dict(envvars) |
1964 | 130 | set_envvars(envvars, self.failures._filename, self.logsdir) | 136 | set_envvars( |
1965 | 137 | envvars, self.datadir, self.failures._filename, self.logsdir) | ||
1966 | 131 | return txjuju.cli.CLI.from_version( | 138 | return txjuju.cli.CLI.from_version( |
1968 | 132 | self.filename, self.version, self.cfgdir, envvars) | 139 | self.filename, self.version, cfgdir, envvars) |
1969 | 140 | |||
1970 | 141 | def bootstrap(self, name, cfgdir, admin_secret=None): | ||
1971 | 142 | """Return the CLI and APIInfo after bootstrapping from scratch.""" | ||
1972 | 143 | from . import get_bootstrap_spec | ||
1973 | 144 | spec = get_bootstrap_spec(name, admin_secret) | ||
1974 | 145 | cfgfile = txjuju.prepare_for_bootstrap(spec, self.version, cfgdir) | ||
1975 | 146 | cli = self.cli(cfgdir) | ||
1976 | 147 | cli.bootstrap(spec, cfgfile=cfgfile) | ||
1977 | 148 | api_info = cli.api_info(spec.name) | ||
1978 | 149 | return cli, api_info | ||
1979 | 150 | |||
1980 | 151 | def is_bootstrapped(self): | ||
1981 | 152 | """Return True if a fake-juju controller is running.""" | ||
1982 | 153 | return os.path.exists(self.fifo) | ||
1983 | 133 | 154 | ||
1984 | === modified file 'python/fakejuju/testing.py' | |||
1985 | --- python/fakejuju/testing.py 2016-10-06 22:51:41 +0000 | |||
1986 | +++ python/fakejuju/testing.py 2016-10-25 17:25:06 +0000 | |||
1987 | @@ -1,6 +1,5 @@ | |||
1988 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | 1 | # Copyright 2016 Canonical Limited. All rights reserved. |
1989 | 2 | 2 | ||
1990 | 3 | import txjuju | ||
1991 | 4 | from fixtures import Fixture, TempDir | 3 | from fixtures import Fixture, TempDir |
1992 | 5 | from testtools.content import content_from_file | 4 | from testtools.content import content_from_file |
1993 | 6 | 5 | ||
1994 | @@ -8,7 +7,7 @@ | |||
1995 | 8 | 7 | ||
1996 | 9 | 8 | ||
1997 | 10 | JUJU1_VER = "1.25.6" | 9 | JUJU1_VER = "1.25.6" |
1999 | 11 | JUJU2_VER = "2.0-beta17" | 10 | JUJU2_VER = "2.0.0" |
2000 | 12 | JUJU_VER = JUJU1_VER | 11 | JUJU_VER = JUJU1_VER |
2001 | 13 | 12 | ||
2002 | 14 | 13 | ||
2003 | @@ -40,29 +39,16 @@ | |||
2004 | 40 | def setUp(self): | 39 | def setUp(self): |
2005 | 41 | super(FakeJujuFixture, self).setUp() | 40 | super(FakeJujuFixture, self).setUp() |
2006 | 42 | self._juju_home = self.useFixture(TempDir()) | 41 | self._juju_home = self.useFixture(TempDir()) |
2009 | 43 | self._juju = fakejuju.FakeJuju.make( | 42 | self.fakejuju = fakejuju.FakeJuju.from_version( |
2010 | 44 | self._juju_home.path, self._version, self._logs_dir) | 43 | self._version, self._juju_home.path, self._logs_dir) |
2011 | 45 | 44 | ||
2012 | 46 | if not self._logs_dir: | 45 | if not self._logs_dir: |
2013 | 47 | # Attach logs as testtools details. | 46 | # Attach logs as testtools details. |
2014 | 48 | self.addDetail("log-file", content_from_file(self._juju.logfile)) | 47 | self.addDetail("log-file", content_from_file(self._juju.logfile)) |
2015 | 49 | 48 | ||
2027 | 50 | spec = fakejuju.get_bootstrap_spec(self._controller, self._password) | 49 | self._juju, self.all_api_info = self.fakejuju.bootstrap( |
2028 | 51 | cfgfile = txjuju.prepare_for_bootstrap( | 50 | self._controller, self._password) |
2018 | 52 | spec, self._version, self._juju_home) | ||
2019 | 53 | cli = self._juju.cli() | ||
2020 | 54 | cli.bootstrap(spec, cfgfile=cfgfile) | ||
2021 | 55 | api_info = cli.api_info(spec.name) | ||
2022 | 56 | if self._version.startswith("1."): | ||
2023 | 57 | # fake-juju doesn't give us the password, so we have to | ||
2024 | 58 | # set it here. | ||
2025 | 59 | api_info = api_info._replace(password=self._password) | ||
2026 | 60 | self.api_info = api_info | ||
2029 | 61 | 51 | ||
2030 | 62 | def cleanUp(self): | 52 | def cleanUp(self): |
2031 | 63 | self._juju.destroy_controller(self._controller) | 53 | self._juju.destroy_controller(self._controller) |
2032 | 64 | super(FakeJujuFixture, self).cleanUp() | 54 | super(FakeJujuFixture, self).cleanUp() |
2033 | 65 | |||
2034 | 66 | def add_failure(self, entity): | ||
2035 | 67 | """Make the given entity fail with an error status.""" | ||
2036 | 68 | self._juju.failures.fail_entity(entity) | ||
2037 | 69 | 55 | ||
2038 | === modified file 'python/fakejuju/tests/test_fakejuju.py' | |||
2039 | --- python/fakejuju/tests/test_fakejuju.py 2016-10-17 15:36:15 +0000 | |||
2040 | +++ python/fakejuju/tests/test_fakejuju.py 2016-10-25 17:25:06 +0000 | |||
2041 | @@ -1,10 +1,16 @@ | |||
2042 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | 1 | # Copyright 2016 Canonical Limited. All rights reserved. |
2043 | 2 | 2 | ||
2044 | 3 | from contextlib import contextmanager | ||
2045 | 4 | import json | ||
2046 | 3 | import os | 5 | import os |
2047 | 6 | import shutil | ||
2048 | 7 | import tempfile | ||
2049 | 4 | import unittest | 8 | import unittest |
2050 | 5 | 9 | ||
2051 | 6 | from txjuju import _juju1, _juju2 | 10 | from txjuju import _juju1, _juju2 |
2052 | 7 | from txjuju._utils import Executable | 11 | from txjuju._utils import Executable |
2053 | 12 | import txjuju.cli | ||
2054 | 13 | import yaml | ||
2055 | 8 | 14 | ||
2056 | 9 | from fakejuju.failures import Failures | 15 | from fakejuju.failures import Failures |
2057 | 10 | from fakejuju.fakejuju import get_filename, set_envvars, FakeJuju | 16 | from fakejuju.fakejuju import get_filename, set_envvars, FakeJuju |
2058 | @@ -44,9 +50,10 @@ | |||
2059 | 44 | def test_all_args(self): | 50 | def test_all_args(self): |
2060 | 45 | """set_envvars() works correctly when given all args.""" | 51 | """set_envvars() works correctly when given all args.""" |
2061 | 46 | envvars = {} | 52 | envvars = {} |
2063 | 47 | set_envvars(envvars, "/spam/failures", "/eggs/logsdir") | 53 | set_envvars(envvars, "/spam", "/spam/failures", "/eggs/logsdir") |
2064 | 48 | 54 | ||
2065 | 49 | self.assertEqual(envvars, { | 55 | self.assertEqual(envvars, { |
2066 | 56 | "FAKE_JUJU_DATA_DIR": "/spam", | ||
2067 | 50 | "FAKE_JUJU_FAILURES": "/spam/failures", | 57 | "FAKE_JUJU_FAILURES": "/spam/failures", |
2068 | 51 | "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir", | 58 | "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir", |
2069 | 52 | }) | 59 | }) |
2070 | @@ -57,6 +64,7 @@ | |||
2071 | 57 | set_envvars(envvars) | 64 | set_envvars(envvars) |
2072 | 58 | 65 | ||
2073 | 59 | self.assertEqual(envvars, { | 66 | self.assertEqual(envvars, { |
2074 | 67 | "FAKE_JUJU_DATA_DIR": "", | ||
2075 | 60 | "FAKE_JUJU_FAILURES": "", | 68 | "FAKE_JUJU_FAILURES": "", |
2076 | 61 | "FAKE_JUJU_LOGS_DIR": "", | 69 | "FAKE_JUJU_LOGS_DIR": "", |
2077 | 62 | }) | 70 | }) |
2078 | @@ -64,9 +72,10 @@ | |||
2079 | 64 | def test_start_empty(self): | 72 | def test_start_empty(self): |
2080 | 65 | """set_envvars() sets all values on an empty dict.""" | 73 | """set_envvars() sets all values on an empty dict.""" |
2081 | 66 | envvars = {} | 74 | envvars = {} |
2083 | 67 | set_envvars(envvars, "x", "y") | 75 | set_envvars(envvars, "w", "x", "y") |
2084 | 68 | 76 | ||
2085 | 69 | self.assertEqual(envvars, { | 77 | self.assertEqual(envvars, { |
2086 | 78 | "FAKE_JUJU_DATA_DIR": "w", | ||
2087 | 70 | "FAKE_JUJU_FAILURES": "x", | 79 | "FAKE_JUJU_FAILURES": "x", |
2088 | 71 | "FAKE_JUJU_LOGS_DIR": "y", | 80 | "FAKE_JUJU_LOGS_DIR": "y", |
2089 | 72 | }) | 81 | }) |
2090 | @@ -74,10 +83,11 @@ | |||
2091 | 74 | def test_no_collisions(self): | 83 | def test_no_collisions(self): |
2092 | 75 | """set_envvars() sets all values when none are set yet.""" | 84 | """set_envvars() sets all values when none are set yet.""" |
2093 | 76 | envvars = {"SPAM": "eggs"} | 85 | envvars = {"SPAM": "eggs"} |
2095 | 77 | set_envvars(envvars, "x", "y") | 86 | set_envvars(envvars, "w", "x", "y") |
2096 | 78 | 87 | ||
2097 | 79 | self.assertEqual(envvars, { | 88 | self.assertEqual(envvars, { |
2098 | 80 | "SPAM": "eggs", | 89 | "SPAM": "eggs", |
2099 | 90 | "FAKE_JUJU_DATA_DIR": "w", | ||
2100 | 81 | "FAKE_JUJU_FAILURES": "x", | 91 | "FAKE_JUJU_FAILURES": "x", |
2101 | 82 | "FAKE_JUJU_LOGS_DIR": "y", | 92 | "FAKE_JUJU_LOGS_DIR": "y", |
2102 | 83 | }) | 93 | }) |
2103 | @@ -85,12 +95,14 @@ | |||
2104 | 85 | def test_empty_to_nonempty(self): | 95 | def test_empty_to_nonempty(self): |
2105 | 86 | """set_envvars() updates empty values.""" | 96 | """set_envvars() updates empty values.""" |
2106 | 87 | envvars = { | 97 | envvars = { |
2107 | 98 | "FAKE_JUJU_DATA_DIR": "", | ||
2108 | 88 | "FAKE_JUJU_FAILURES": "", | 99 | "FAKE_JUJU_FAILURES": "", |
2109 | 89 | "FAKE_JUJU_LOGS_DIR": "", | 100 | "FAKE_JUJU_LOGS_DIR": "", |
2110 | 90 | } | 101 | } |
2112 | 91 | set_envvars(envvars, "x", "y") | 102 | set_envvars(envvars, "w", "x", "y") |
2113 | 92 | 103 | ||
2114 | 93 | self.assertEqual(envvars, { | 104 | self.assertEqual(envvars, { |
2115 | 105 | "FAKE_JUJU_DATA_DIR": "w", | ||
2116 | 94 | "FAKE_JUJU_FAILURES": "x", | 106 | "FAKE_JUJU_FAILURES": "x", |
2117 | 95 | "FAKE_JUJU_LOGS_DIR": "y", | 107 | "FAKE_JUJU_LOGS_DIR": "y", |
2118 | 96 | }) | 108 | }) |
2119 | @@ -98,12 +110,14 @@ | |||
2120 | 98 | def test_nonempty_to_nonempty(self): | 110 | def test_nonempty_to_nonempty(self): |
2121 | 99 | """set_envvars() overwrites existing values.""" | 111 | """set_envvars() overwrites existing values.""" |
2122 | 100 | envvars = { | 112 | envvars = { |
2123 | 113 | "FAKE_JUJU_DATA_DIR": "spam", | ||
2124 | 101 | "FAKE_JUJU_FAILURES": "spam", | 114 | "FAKE_JUJU_FAILURES": "spam", |
2125 | 102 | "FAKE_JUJU_LOGS_DIR": "ham", | 115 | "FAKE_JUJU_LOGS_DIR": "ham", |
2126 | 103 | } | 116 | } |
2128 | 104 | set_envvars(envvars, "x", "y") | 117 | set_envvars(envvars, "w", "x", "y") |
2129 | 105 | 118 | ||
2130 | 106 | self.assertEqual(envvars, { | 119 | self.assertEqual(envvars, { |
2131 | 120 | "FAKE_JUJU_DATA_DIR": "w", | ||
2132 | 107 | "FAKE_JUJU_FAILURES": "x", | 121 | "FAKE_JUJU_FAILURES": "x", |
2133 | 108 | "FAKE_JUJU_LOGS_DIR": "y", | 122 | "FAKE_JUJU_LOGS_DIR": "y", |
2134 | 109 | }) | 123 | }) |
2135 | @@ -111,12 +125,14 @@ | |||
2136 | 111 | def test_nonempty_to_empty(self): | 125 | def test_nonempty_to_empty(self): |
2137 | 112 | """set_envvars() with no args "unsets" existing values.""" | 126 | """set_envvars() with no args "unsets" existing values.""" |
2138 | 113 | envvars = { | 127 | envvars = { |
2139 | 128 | "FAKE_JUJU_DATA_DIR": "w", | ||
2140 | 114 | "FAKE_JUJU_FAILURES": "x", | 129 | "FAKE_JUJU_FAILURES": "x", |
2141 | 115 | "FAKE_JUJU_LOGS_DIR": "y", | 130 | "FAKE_JUJU_LOGS_DIR": "y", |
2142 | 116 | } | 131 | } |
2143 | 117 | set_envvars(envvars) | 132 | set_envvars(envvars) |
2144 | 118 | 133 | ||
2145 | 119 | self.assertEqual(envvars, { | 134 | self.assertEqual(envvars, { |
2146 | 135 | "FAKE_JUJU_DATA_DIR": "", | ||
2147 | 120 | "FAKE_JUJU_FAILURES": "", | 136 | "FAKE_JUJU_FAILURES": "", |
2148 | 121 | "FAKE_JUJU_LOGS_DIR": "", | 137 | "FAKE_JUJU_LOGS_DIR": "", |
2149 | 122 | }) | 138 | }) |
2150 | @@ -131,7 +147,7 @@ | |||
2151 | 131 | 147 | ||
2152 | 132 | self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6") | 148 | self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6") |
2153 | 133 | self.assertEqual(juju.version, "1.25.6") | 149 | self.assertEqual(juju.version, "1.25.6") |
2155 | 134 | self.assertEqual(juju.cfgdir, "/a/juju/home") | 150 | self.assertEqual(juju.datadir, "/a/juju/home") |
2156 | 135 | self.assertEqual(juju.logsdir, "/logs/dir") | 151 | self.assertEqual(juju.logsdir, "/logs/dir") |
2157 | 136 | self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures") | 152 | self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures") |
2158 | 137 | 153 | ||
2159 | @@ -141,19 +157,20 @@ | |||
2160 | 141 | 157 | ||
2161 | 142 | self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6") | 158 | self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6") |
2162 | 143 | self.assertEqual(juju.version, "1.25.6") | 159 | self.assertEqual(juju.version, "1.25.6") |
2164 | 144 | self.assertEqual(juju.cfgdir, "/my/juju/home") | 160 | self.assertEqual(juju.datadir, "/my/juju/home") |
2165 | 145 | self.assertEqual(juju.logsdir, "/my/juju/home") | 161 | self.assertEqual(juju.logsdir, "/my/juju/home") |
2166 | 146 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") | 162 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") |
2167 | 147 | 163 | ||
2168 | 148 | def test_full(self): | 164 | def test_full(self): |
2169 | 149 | """FakeJuju() works correctly when given all args.""" | 165 | """FakeJuju() works correctly when given all args.""" |
2173 | 150 | cfgdir = "/my/juju/home" | 166 | datadir = "/my/juju/home" |
2174 | 151 | failures = Failures(cfgdir) | 167 | failures = Failures(datadir) |
2175 | 152 | juju = FakeJuju("/fake-juju", "1.25.6", cfgdir, "/some/logs", failures) | 168 | juju = FakeJuju( |
2176 | 169 | "/fake-juju", "1.25.6", datadir, "/some/logs", failures) | ||
2177 | 153 | 170 | ||
2178 | 154 | self.assertEqual(juju.filename, "/fake-juju") | 171 | self.assertEqual(juju.filename, "/fake-juju") |
2179 | 155 | self.assertEqual(juju.version, "1.25.6") | 172 | self.assertEqual(juju.version, "1.25.6") |
2181 | 156 | self.assertEqual(juju.cfgdir, cfgdir) | 173 | self.assertEqual(juju.datadir, datadir) |
2182 | 157 | self.assertEqual(juju.logsdir, "/some/logs") | 174 | self.assertEqual(juju.logsdir, "/some/logs") |
2183 | 158 | self.assertIs(juju.failures, failures) | 175 | self.assertIs(juju.failures, failures) |
2184 | 159 | 176 | ||
2185 | @@ -163,7 +180,7 @@ | |||
2186 | 163 | 180 | ||
2187 | 164 | self.assertEqual(juju.filename, "/fake-juju") | 181 | self.assertEqual(juju.filename, "/fake-juju") |
2188 | 165 | self.assertEqual(juju.version, "1.25.6") | 182 | self.assertEqual(juju.version, "1.25.6") |
2190 | 166 | self.assertEqual(juju.cfgdir, "/my/juju/home") | 183 | self.assertEqual(juju.datadir, "/my/juju/home") |
2191 | 167 | self.assertEqual(juju.logsdir, "/my/juju/home") | 184 | self.assertEqual(juju.logsdir, "/my/juju/home") |
2192 | 168 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") | 185 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") |
2193 | 169 | 186 | ||
2194 | @@ -174,7 +191,7 @@ | |||
2195 | 174 | juju_unicode = FakeJuju( | 191 | juju_unicode = FakeJuju( |
2196 | 175 | u"/fake-juju", u"1.25.6", u"/x", u"/y", Failures(u"/...")) | 192 | u"/fake-juju", u"1.25.6", u"/x", u"/y", Failures(u"/...")) |
2197 | 176 | 193 | ||
2199 | 177 | for name in ('filename version cfgdir logsdir'.split()): | 194 | for name in ('filename version datadir logsdir'.split()): |
2200 | 178 | self.assertIsInstance(getattr(juju_str, name), str) | 195 | self.assertIsInstance(getattr(juju_str, name), str) |
2201 | 179 | self.assertIsInstance(getattr(juju_unicode, name), unicode) | 196 | self.assertIsInstance(getattr(juju_unicode, name), unicode) |
2202 | 180 | 197 | ||
2203 | @@ -192,8 +209,8 @@ | |||
2204 | 192 | with self.assertRaises(ValueError): | 209 | with self.assertRaises(ValueError): |
2205 | 193 | FakeJuju("/fake-juju", "", "/my/juju/home") | 210 | FakeJuju("/fake-juju", "", "/my/juju/home") |
2206 | 194 | 211 | ||
2209 | 195 | def test_missing_cfgdir(self): | 212 | def test_missing_datadir(self): |
2210 | 196 | """FakeJuju() fails if cfgdir is None or empty.""" | 213 | """FakeJuju() fails if datadir is None or empty.""" |
2211 | 197 | with self.assertRaises(ValueError): | 214 | with self.assertRaises(ValueError): |
2212 | 198 | FakeJuju("/fake-juju", "1.25.6", None) | 215 | FakeJuju("/fake-juju", "1.25.6", None) |
2213 | 199 | with self.assertRaises(ValueError): | 216 | with self.assertRaises(ValueError): |
2214 | @@ -226,44 +243,208 @@ | |||
2215 | 226 | def test_cli_full(self): | 243 | def test_cli_full(self): |
2216 | 227 | """FakeJuju.cli() works correctly when given all args.""" | 244 | """FakeJuju.cli() works correctly when given all args.""" |
2217 | 228 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | 245 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") |
2219 | 229 | cli = juju.cli({"SPAM": "eggs"}) | 246 | cli = juju.cli("/y", {"SPAM": "eggs"}) |
2220 | 230 | 247 | ||
2221 | 231 | self.assertEqual( | 248 | self.assertEqual( |
2222 | 232 | cli._exe, | 249 | cli._exe, |
2223 | 233 | Executable("/fake-juju", { | 250 | Executable("/fake-juju", { |
2224 | 234 | "SPAM": "eggs", | 251 | "SPAM": "eggs", |
2225 | 252 | "FAKE_JUJU_DATA_DIR": "/x", | ||
2226 | 235 | "FAKE_JUJU_FAILURES": "/x/juju-failures", | 253 | "FAKE_JUJU_FAILURES": "/x/juju-failures", |
2227 | 236 | "FAKE_JUJU_LOGS_DIR": "/x", | 254 | "FAKE_JUJU_LOGS_DIR": "/x", |
2229 | 237 | "JUJU_HOME": "/x", | 255 | "JUJU_HOME": "/y", |
2230 | 238 | }), | 256 | }), |
2231 | 239 | ) | 257 | ) |
2232 | 240 | 258 | ||
2233 | 241 | def test_cli_minimal(self): | 259 | def test_cli_minimal(self): |
2234 | 242 | """FakeJuju.cli() works correctly when given minimal args.""" | 260 | """FakeJuju.cli() works correctly when given minimal args.""" |
2235 | 243 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | 261 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") |
2237 | 244 | cli = juju.cli() | 262 | cli = juju.cli("/y") |
2238 | 245 | 263 | ||
2239 | 246 | self.assertEqual( | 264 | self.assertEqual( |
2240 | 247 | cli._exe, | 265 | cli._exe, |
2241 | 248 | Executable("/fake-juju", dict(os.environ, **{ | 266 | Executable("/fake-juju", dict(os.environ, **{ |
2242 | 267 | "FAKE_JUJU_DATA_DIR": "/x", | ||
2243 | 249 | "FAKE_JUJU_FAILURES": "/x/juju-failures", | 268 | "FAKE_JUJU_FAILURES": "/x/juju-failures", |
2244 | 250 | "FAKE_JUJU_LOGS_DIR": "/x", | 269 | "FAKE_JUJU_LOGS_DIR": "/x", |
2246 | 251 | "JUJU_HOME": "/x", | 270 | "JUJU_HOME": "/y", |
2247 | 252 | })), | 271 | })), |
2248 | 253 | ) | 272 | ) |
2249 | 254 | 273 | ||
2250 | 255 | def test_cli_juju1(self): | 274 | def test_cli_juju1(self): |
2251 | 256 | """FakeJuju.cli() works correctly for Juju 1.x.""" | 275 | """FakeJuju.cli() works correctly for Juju 1.x.""" |
2252 | 257 | juju = FakeJuju.from_version("1.25.6", "/x") | 276 | juju = FakeJuju.from_version("1.25.6", "/x") |
2254 | 258 | cli = juju.cli() | 277 | cli = juju.cli("/y") |
2255 | 259 | 278 | ||
2257 | 260 | self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/x") | 279 | self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/y") |
2258 | 261 | self.assertIsInstance(cli._juju, _juju1.CLIHooks) | 280 | self.assertIsInstance(cli._juju, _juju1.CLIHooks) |
2259 | 262 | 281 | ||
2260 | 263 | def test_cli_juju2(self): | 282 | def test_cli_juju2(self): |
2261 | 264 | """FakeJuju.cli() works correctly for Juju 2.x.""" | 283 | """FakeJuju.cli() works correctly for Juju 2.x.""" |
2262 | 265 | juju = FakeJuju.from_version("2.0.0", "/x") | 284 | juju = FakeJuju.from_version("2.0.0", "/x") |
2264 | 266 | cli = juju.cli() | 285 | cli = juju.cli("/y") |
2265 | 267 | 286 | ||
2267 | 268 | self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/x") | 287 | self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/y") |
2268 | 269 | self.assertIsInstance(cli._juju, _juju2.CLIHooks) | 288 | self.assertIsInstance(cli._juju, _juju2.CLIHooks) |
2269 | 289 | |||
2270 | 290 | def test_bootstrap(self): | ||
2271 | 291 | """FakeJuju.bootstrap() bootstraps from scratch using fake-juju.""" | ||
2272 | 292 | expected = txjuju.cli.APIInfo( | ||
2273 | 293 | endpoints=['localhost:12727'], | ||
2274 | 294 | user='admin', | ||
2275 | 295 | password='dummy-secret', | ||
2276 | 296 | model_uuid='deadbeef-0bad-400d-8000-4b1d0d06f00d', | ||
2277 | 297 | ) | ||
2278 | 298 | version = "1.25.6" | ||
2279 | 299 | with tempdir() as testdir: | ||
2280 | 300 | bindir = os.path.join(testdir, "bin") | ||
2281 | 301 | datadir = os.path.join(testdir, "fakejuju") | ||
2282 | 302 | cfgdir = os.path.join(testdir, ".juju") | ||
2283 | 303 | |||
2284 | 304 | logfilename = write_fakejuju_script( | ||
2285 | 305 | version, bindir, datadir, cfgdir, expected) | ||
2286 | 306 | fakejuju = FakeJuju.from_version(version, cfgdir, bindir=bindir) | ||
2287 | 307 | |||
2288 | 308 | cli, api_info = fakejuju.bootstrap("spam", cfgdir, "secret") | ||
2289 | 309 | |||
2290 | 310 | files = [] | ||
2291 | 311 | files.extend(os.path.join(os.path.basename(datadir), name) | ||
2292 | 312 | for name in os.listdir(datadir)) | ||
2293 | 313 | files.extend(os.path.join(os.path.basename(cfgdir), name) | ||
2294 | 314 | for name in os.listdir(cfgdir)) | ||
2295 | 315 | with open(os.path.join(cfgdir, "environments.yaml")) as envfile: | ||
2296 | 316 | data = envfile.read() | ||
2297 | 317 | |||
2298 | 318 | cli.destroy_controller() | ||
2299 | 319 | with open(logfilename) as logfile: | ||
2300 | 320 | calls = [line.strip() for line in logfile] | ||
2301 | 321 | |||
2302 | 322 | self.maxDiff = None | ||
2303 | 323 | self.assertEqual(api_info, { | ||
2304 | 324 | 'controller': expected, | ||
2305 | 325 | None: expected._replace(model_uuid=None), | ||
2306 | 326 | }) | ||
2307 | 327 | subcommands = [] | ||
2308 | 328 | for call in calls: | ||
2309 | 329 | args = call.split() | ||
2310 | 330 | self.assertEqual(os.path.basename(args[0]), "fake-juju-" + version) | ||
2311 | 331 | subcommands.append(args[1]) | ||
2312 | 332 | self.assertEqual(subcommands, [ | ||
2313 | 333 | "bootstrap", | ||
2314 | 334 | "api-info", | ||
2315 | 335 | "destroy-environment", | ||
2316 | 336 | ]) | ||
2317 | 337 | self.assertItemsEqual(files, [ | ||
2318 | 338 | '.juju/environments', | ||
2319 | 339 | '.juju/environments.yaml', | ||
2320 | 340 | 'fakejuju/cert.ca', | ||
2321 | 341 | 'fakejuju/fake-juju.log', | ||
2322 | 342 | 'fakejuju/fakejuju', | ||
2323 | 343 | 'fakejuju/fifo', | ||
2324 | 344 | ]) | ||
2325 | 345 | self.assertEqual(yaml.load(data), { | ||
2326 | 346 | "environments": { | ||
2327 | 347 | "spam": { | ||
2328 | 348 | "admin-secret": "secret", | ||
2329 | 349 | "default-series": "trusty", | ||
2330 | 350 | "type": "dummy", | ||
2331 | 351 | }, | ||
2332 | 352 | }, | ||
2333 | 353 | }) | ||
2334 | 354 | |||
2335 | 355 | def test_is_bootstrapped_true(self): | ||
2336 | 356 | """FakeJuju.is_bootstrapped() returns True if the fifo file exists.""" | ||
2337 | 357 | with tempdir() as datadir: | ||
2338 | 358 | fakejuju = FakeJuju.from_version("1.25.6", datadir) | ||
2339 | 359 | with open(fakejuju.fifo, "w"): | ||
2340 | 360 | pass | ||
2341 | 361 | result = fakejuju.is_bootstrapped() | ||
2342 | 362 | |||
2343 | 363 | self.assertTrue(result) | ||
2344 | 364 | |||
2345 | 365 | def test_is_bootstrapped_false(self): | ||
2346 | 366 | """FakeJuju.is_bootstrapped() returns False if the fifo is gone.""" | ||
2347 | 367 | with tempdir() as datadir: | ||
2348 | 368 | fakejuju = FakeJuju.from_version("1.25.6", datadir) | ||
2349 | 369 | result = fakejuju.is_bootstrapped() | ||
2350 | 370 | |||
2351 | 371 | self.assertFalse(result) | ||
2352 | 372 | |||
2353 | 373 | def test_is_bootstrapped_datadir_missing(self): | ||
2354 | 374 | """FakeJuju.is_bootstrapped() returns False if the data dir is gone.""" | ||
2355 | 375 | fakejuju = FakeJuju.from_version("1.25.6", "/tmp/fakejuju-no-exist") | ||
2356 | 376 | result = fakejuju.is_bootstrapped() | ||
2357 | 377 | |||
2358 | 378 | self.assertFalse(result) | ||
2359 | 379 | |||
2360 | 380 | |||
2361 | 381 | FAKE_JUJU_SCRIPT = """\ | ||
2362 | 382 | #!/usr/bin/env python | ||
2363 | 383 | |||
2364 | 384 | import os.path | ||
2365 | 385 | import sys | ||
2366 | 386 | |||
2367 | 387 | with open("{logfile}", "a") as logfile: | ||
2368 | 388 | logfile.write(" ".join(sys.argv) + "\\n") | ||
2369 | 389 | |||
2370 | 390 | if sys.argv[1] == "bootstrap": | ||
2371 | 391 | for filename in ("cert.ca", "fake-juju.log", "fakejuju", "fifo"): | ||
2372 | 392 | with open(os.path.join("{datadir}", filename), "w"): | ||
2373 | 393 | pass # Touch the file. | ||
2374 | 394 | for filename in ("environments",): | ||
2375 | 395 | with open(os.path.join("{cfgdir}", filename), "w"): | ||
2376 | 396 | pass # Touch the file. | ||
2377 | 397 | elif sys.argv[1] in ("api-info", "show-controller"): | ||
2378 | 398 | print('''{output}''') | ||
2379 | 399 | |||
2380 | 400 | """ | ||
2381 | 401 | |||
2382 | 402 | |||
2383 | 403 | def write_fakejuju_script(version, bindir, datadir, cfgdir, api_info): | ||
2384 | 404 | if version.startswith("1."): | ||
2385 | 405 | raw_api_info = { | ||
2386 | 406 | "state-servers": api_info.endpoints, | ||
2387 | 407 | "user": api_info.user, | ||
2388 | 408 | "password": api_info.password, | ||
2389 | 409 | "environ-uuid": api_info.model_uuid, | ||
2390 | 410 | } | ||
2391 | 411 | else: | ||
2392 | 412 | raw_api_info = { | ||
2393 | 413 | "details": { | ||
2394 | 414 | "api-endpoints": api_info.endpoints, | ||
2395 | 415 | }, | ||
2396 | 416 | "account": { | ||
2397 | 417 | "user": api_info.user + "@local", | ||
2398 | 418 | "password": api_info.password, | ||
2399 | 419 | }, | ||
2400 | 420 | "models": { | ||
2401 | 421 | "controller": { | ||
2402 | 422 | "uuid": api_info.model_uuid, | ||
2403 | 423 | }, | ||
2404 | 424 | "default": { | ||
2405 | 425 | "uuid": api_info.model_uuid, | ||
2406 | 426 | }, | ||
2407 | 427 | }, | ||
2408 | 428 | } | ||
2409 | 429 | output = json.dumps(raw_api_info) | ||
2410 | 430 | |||
2411 | 431 | logfile = os.path.join(bindir, "calls.log") | ||
2412 | 432 | script = FAKE_JUJU_SCRIPT.format( | ||
2413 | 433 | datadir=datadir, cfgdir=cfgdir, logfile=logfile, output=output) | ||
2414 | 434 | filename = get_filename(version, bindir) | ||
2415 | 435 | os.makedirs(os.path.dirname(filename)) | ||
2416 | 436 | with open(filename, "w") as scriptfile: | ||
2417 | 437 | scriptfile.write(script) | ||
2418 | 438 | os.chmod(filename, 0o755) | ||
2419 | 439 | os.makedirs(datadir) | ||
2420 | 440 | |||
2421 | 441 | return logfile | ||
2422 | 442 | |||
2423 | 443 | |||
2424 | 444 | @contextmanager | ||
2425 | 445 | def tempdir(): | ||
2426 | 446 | cfgdir = tempfile.mkdtemp("fakejuju-test-") | ||
2427 | 447 | try: | ||
2428 | 448 | yield cfgdir | ||
2429 | 449 | finally: | ||
2430 | 450 | shutil.rmtree(cfgdir) | ||
2431 | 270 | 451 | ||
2432 | === modified file 'tests/test_fake.py' | |||
2433 | --- tests/test_fake.py 2016-09-15 20:38:52 +0000 | |||
2434 | +++ tests/test_fake.py 2016-10-25 17:25:06 +0000 | |||
2435 | @@ -108,7 +108,7 @@ | |||
2436 | 108 | output = subprocess.check_output(args, env=env) | 108 | output = subprocess.check_output(args, env=env) |
2437 | 109 | api_info = json.loads(output.decode()) | 109 | api_info = json.loads(output.decode()) |
2438 | 110 | endpoint = str(api_info[name]["details"]["api-endpoints"][0]) | 110 | endpoint = str(api_info[name]["details"]["api-endpoints"][0]) |
2440 | 111 | model = api_info[name]["current-model"] | 111 | model = api_info[name]["current-model"].split("/", 1)[-1] |
2441 | 112 | uuid = api_info[name]["models"][model]["uuid"] | 112 | uuid = api_info[name]["models"][model]["uuid"] |
2442 | 113 | password = api_info[name]["account"]["password"] | 113 | password = api_info[name]["account"]["password"] |
2443 | 114 | return endpoint, uuid, password | 114 | return endpoint, uuid, password |
Command: make ci-test /ci.lscape. net/job/ latch-test- xenial/ 336/
Result: Success
Revno: 63
Branch: lp:~ericsnowcurrently/fake-juju/juju-2.0-support
Jenkins: https:/