Merge lp:~ericsnowcurrently/fake-juju/python-lib-classes into lp:~landscape/fake-juju/trunk-old
- python-lib-classes
- Merge into trunk-old
Proposed by
Eric Snow
Status: | Superseded |
---|---|
Proposed branch: | lp:~ericsnowcurrently/fake-juju/python-lib-classes |
Merge into: | lp:~landscape/fake-juju/trunk-old |
Diff against target: |
963 lines (+915/-0) 9 files modified
python/LICENSE (+191/-0) python/Makefile (+9/-0) python/README.md (+1/-0) python/fakejuju/__init__.py (+57/-0) python/fakejuju/failures.py (+65/-0) python/fakejuju/fakejuju.py (+145/-0) python/fakejuju/tests/test_failures.py (+98/-0) python/fakejuju/tests/test_fakejuju.py (+280/-0) python/setup.py (+69/-0) |
To merge this branch: | bzr merge lp:~ericsnowcurrently/fake-juju/python-lib-classes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Landscape | Pending | ||
Landscape | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2016-10-06.
Commit message
Description of the change
Add the FakeJuju and Failures classes.
Testing instructions:
Run the unit tests.
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'python' | |||
2 | === added file 'python/LICENSE' | |||
3 | --- python/LICENSE 1970-01-01 00:00:00 +0000 | |||
4 | +++ python/LICENSE 2016-10-06 22:49:20 +0000 | |||
5 | @@ -0,0 +1,191 @@ | |||
6 | 1 | All files in this repository are licensed as follows. If you contribute | ||
7 | 2 | to this repository, it is assumed that you license your contribution | ||
8 | 3 | under the same license unless you state otherwise. | ||
9 | 4 | |||
10 | 5 | All files Copyright (C) 2012-2016 Canonical Ltd. unless otherwise specified in the file. | ||
11 | 6 | |||
12 | 7 | This software is licensed under the LGPLv3, included below. | ||
13 | 8 | |||
14 | 9 | As a special exception to the GNU Lesser General Public License version 3 | ||
15 | 10 | ("LGPL3"), the copyright holders of this Library give you permission to | ||
16 | 11 | convey to a third party a Combined Work that links statically or dynamically | ||
17 | 12 | to this Library without providing any Minimal Corresponding Source or | ||
18 | 13 | Minimal Application Code as set out in 4d or providing the installation | ||
19 | 14 | information set out in section 4e, provided that you comply with the other | ||
20 | 15 | provisions of LGPL3 and provided that you meet, for the Application the | ||
21 | 16 | terms and conditions of the license(s) which apply to the Application. | ||
22 | 17 | |||
23 | 18 | Except as stated in this special exception, the provisions of LGPL3 will | ||
24 | 19 | continue to comply in full to this Library. If you modify this Library, you | ||
25 | 20 | may apply this exception to your version of this Library, but you are not | ||
26 | 21 | obliged to do so. If you do not wish to do so, delete this exception | ||
27 | 22 | statement from your version. This exception does not (and cannot) modify any | ||
28 | 23 | license terms which apply to the Application, with which you must still | ||
29 | 24 | comply. | ||
30 | 25 | |||
31 | 26 | |||
32 | 27 | GNU LESSER GENERAL PUBLIC LICENSE | ||
33 | 28 | Version 3, 29 June 2007 | ||
34 | 29 | |||
35 | 30 | Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||
36 | 31 | Everyone is permitted to copy and distribute verbatim copies | ||
37 | 32 | of this license document, but changing it is not allowed. | ||
38 | 33 | |||
39 | 34 | |||
40 | 35 | This version of the GNU Lesser General Public License incorporates | ||
41 | 36 | the terms and conditions of version 3 of the GNU General Public | ||
42 | 37 | License, supplemented by the additional permissions listed below. | ||
43 | 38 | |||
44 | 39 | 0. Additional Definitions. | ||
45 | 40 | |||
46 | 41 | As used herein, "this License" refers to version 3 of the GNU Lesser | ||
47 | 42 | General Public License, and the "GNU GPL" refers to version 3 of the GNU | ||
48 | 43 | General Public License. | ||
49 | 44 | |||
50 | 45 | "The Library" refers to a covered work governed by this License, | ||
51 | 46 | other than an Application or a Combined Work as defined below. | ||
52 | 47 | |||
53 | 48 | An "Application" is any work that makes use of an interface provided | ||
54 | 49 | by the Library, but which is not otherwise based on the Library. | ||
55 | 50 | Defining a subclass of a class defined by the Library is deemed a mode | ||
56 | 51 | of using an interface provided by the Library. | ||
57 | 52 | |||
58 | 53 | A "Combined Work" is a work produced by combining or linking an | ||
59 | 54 | Application with the Library. The particular version of the Library | ||
60 | 55 | with which the Combined Work was made is also called the "Linked | ||
61 | 56 | Version". | ||
62 | 57 | |||
63 | 58 | The "Minimal Corresponding Source" for a Combined Work means the | ||
64 | 59 | Corresponding Source for the Combined Work, excluding any source code | ||
65 | 60 | for portions of the Combined Work that, considered in isolation, are | ||
66 | 61 | based on the Application, and not on the Linked Version. | ||
67 | 62 | |||
68 | 63 | The "Corresponding Application Code" for a Combined Work means the | ||
69 | 64 | object code and/or source code for the Application, including any data | ||
70 | 65 | and utility programs needed for reproducing the Combined Work from the | ||
71 | 66 | Application, but excluding the System Libraries of the Combined Work. | ||
72 | 67 | |||
73 | 68 | 1. Exception to Section 3 of the GNU GPL. | ||
74 | 69 | |||
75 | 70 | You may convey a covered work under sections 3 and 4 of this License | ||
76 | 71 | without being bound by section 3 of the GNU GPL. | ||
77 | 72 | |||
78 | 73 | 2. Conveying Modified Versions. | ||
79 | 74 | |||
80 | 75 | If you modify a copy of the Library, and, in your modifications, a | ||
81 | 76 | facility refers to a function or data to be supplied by an Application | ||
82 | 77 | that uses the facility (other than as an argument passed when the | ||
83 | 78 | facility is invoked), then you may convey a copy of the modified | ||
84 | 79 | version: | ||
85 | 80 | |||
86 | 81 | a) under this License, provided that you make a good faith effort to | ||
87 | 82 | ensure that, in the event an Application does not supply the | ||
88 | 83 | function or data, the facility still operates, and performs | ||
89 | 84 | whatever part of its purpose remains meaningful, or | ||
90 | 85 | |||
91 | 86 | b) under the GNU GPL, with none of the additional permissions of | ||
92 | 87 | this License applicable to that copy. | ||
93 | 88 | |||
94 | 89 | 3. Object Code Incorporating Material from Library Header Files. | ||
95 | 90 | |||
96 | 91 | The object code form of an Application may incorporate material from | ||
97 | 92 | a header file that is part of the Library. You may convey such object | ||
98 | 93 | code under terms of your choice, provided that, if the incorporated | ||
99 | 94 | material is not limited to numerical parameters, data structure | ||
100 | 95 | layouts and accessors, or small macros, inline functions and templates | ||
101 | 96 | (ten or fewer lines in length), you do both of the following: | ||
102 | 97 | |||
103 | 98 | a) Give prominent notice with each copy of the object code that the | ||
104 | 99 | Library is used in it and that the Library and its use are | ||
105 | 100 | covered by this License. | ||
106 | 101 | |||
107 | 102 | b) Accompany the object code with a copy of the GNU GPL and this license | ||
108 | 103 | document. | ||
109 | 104 | |||
110 | 105 | 4. Combined Works. | ||
111 | 106 | |||
112 | 107 | You may convey a Combined Work under terms of your choice that, | ||
113 | 108 | taken together, effectively do not restrict modification of the | ||
114 | 109 | portions of the Library contained in the Combined Work and reverse | ||
115 | 110 | engineering for debugging such modifications, if you also do each of | ||
116 | 111 | the following: | ||
117 | 112 | |||
118 | 113 | a) Give prominent notice with each copy of the Combined Work that | ||
119 | 114 | the Library is used in it and that the Library and its use are | ||
120 | 115 | covered by this License. | ||
121 | 116 | |||
122 | 117 | b) Accompany the Combined Work with a copy of the GNU GPL and this license | ||
123 | 118 | document. | ||
124 | 119 | |||
125 | 120 | c) For a Combined Work that displays copyright notices during | ||
126 | 121 | execution, include the copyright notice for the Library among | ||
127 | 122 | these notices, as well as a reference directing the user to the | ||
128 | 123 | copies of the GNU GPL and this license document. | ||
129 | 124 | |||
130 | 125 | d) Do one of the following: | ||
131 | 126 | |||
132 | 127 | 0) Convey the Minimal Corresponding Source under the terms of this | ||
133 | 128 | License, and the Corresponding Application Code in a form | ||
134 | 129 | suitable for, and under terms that permit, the user to | ||
135 | 130 | recombine or relink the Application with a modified version of | ||
136 | 131 | the Linked Version to produce a modified Combined Work, in the | ||
137 | 132 | manner specified by section 6 of the GNU GPL for conveying | ||
138 | 133 | Corresponding Source. | ||
139 | 134 | |||
140 | 135 | 1) Use a suitable shared library mechanism for linking with the | ||
141 | 136 | Library. A suitable mechanism is one that (a) uses at run time | ||
142 | 137 | a copy of the Library already present on the user's computer | ||
143 | 138 | system, and (b) will operate properly with a modified version | ||
144 | 139 | of the Library that is interface-compatible with the Linked | ||
145 | 140 | Version. | ||
146 | 141 | |||
147 | 142 | e) Provide Installation Information, but only if you would otherwise | ||
148 | 143 | be required to provide such information under section 6 of the | ||
149 | 144 | GNU GPL, and only to the extent that such information is | ||
150 | 145 | necessary to install and execute a modified version of the | ||
151 | 146 | Combined Work produced by recombining or relinking the | ||
152 | 147 | Application with a modified version of the Linked Version. (If | ||
153 | 148 | you use option 4d0, the Installation Information must accompany | ||
154 | 149 | the Minimal Corresponding Source and Corresponding Application | ||
155 | 150 | Code. If you use option 4d1, you must provide the Installation | ||
156 | 151 | Information in the manner specified by section 6 of the GNU GPL | ||
157 | 152 | for conveying Corresponding Source.) | ||
158 | 153 | |||
159 | 154 | 5. Combined Libraries. | ||
160 | 155 | |||
161 | 156 | You may place library facilities that are a work based on the | ||
162 | 157 | Library side by side in a single library together with other library | ||
163 | 158 | facilities that are not Applications and are not covered by this | ||
164 | 159 | License, and convey such a combined library under terms of your | ||
165 | 160 | choice, if you do both of the following: | ||
166 | 161 | |||
167 | 162 | a) Accompany the combined library with a copy of the same work based | ||
168 | 163 | on the Library, uncombined with any other library facilities, | ||
169 | 164 | conveyed under the terms of this License. | ||
170 | 165 | |||
171 | 166 | b) Give prominent notice with the combined library that part of it | ||
172 | 167 | is a work based on the Library, and explaining where to find the | ||
173 | 168 | accompanying uncombined form of the same work. | ||
174 | 169 | |||
175 | 170 | 6. Revised Versions of the GNU Lesser General Public License. | ||
176 | 171 | |||
177 | 172 | The Free Software Foundation may publish revised and/or new versions | ||
178 | 173 | of the GNU Lesser General Public License from time to time. Such new | ||
179 | 174 | versions will be similar in spirit to the present version, but may | ||
180 | 175 | differ in detail to address new problems or concerns. | ||
181 | 176 | |||
182 | 177 | Each version is given a distinguishing version number. If the | ||
183 | 178 | Library as you received it specifies that a certain numbered version | ||
184 | 179 | of the GNU Lesser General Public License "or any later version" | ||
185 | 180 | applies to it, you have the option of following the terms and | ||
186 | 181 | conditions either of that published version or of any later version | ||
187 | 182 | published by the Free Software Foundation. If the Library as you | ||
188 | 183 | received it does not specify a version number of the GNU Lesser | ||
189 | 184 | General Public License, you may choose any version of the GNU Lesser | ||
190 | 185 | General Public License ever published by the Free Software Foundation. | ||
191 | 186 | |||
192 | 187 | If the Library as you received it specifies that a proxy can decide | ||
193 | 188 | whether future versions of the GNU Lesser General Public License shall | ||
194 | 189 | apply, that proxy's public statement of acceptance of any version is | ||
195 | 190 | permanent authorization for you to choose that version for the | ||
196 | 191 | Library. | ||
197 | 0 | 192 | ||
198 | === added file 'python/Makefile' | |||
199 | --- python/Makefile 1970-01-01 00:00:00 +0000 | |||
200 | +++ python/Makefile 2016-10-06 22:49:20 +0000 | |||
201 | @@ -0,0 +1,9 @@ | |||
202 | 1 | PYTHON = python | ||
203 | 2 | |||
204 | 3 | .PHONY: test | ||
205 | 4 | test: | ||
206 | 5 | $(PYTHON) -m unittest discover -t $(shell pwd) -s $(shell pwd)/fakejuju | ||
207 | 6 | |||
208 | 7 | .PHONY: install-dev | ||
209 | 8 | install-dev: | ||
210 | 9 | ln -s $(shell pwd)/fakejuju /usr/local/lib/python2.7/dist-packages/fakejuju | ||
211 | 0 | 10 | ||
212 | === added file 'python/README.md' | |||
213 | --- python/README.md 1970-01-01 00:00:00 +0000 | |||
214 | +++ python/README.md 2016-10-06 22:49:20 +0000 | |||
215 | @@ -0,0 +1,1 @@ | |||
216 | 1 | # fakejuju | ||
217 | 0 | 2 | ||
218 | === added directory 'python/fakejuju' | |||
219 | === added file 'python/fakejuju/__init__.py' | |||
220 | --- python/fakejuju/__init__.py 1970-01-01 00:00:00 +0000 | |||
221 | +++ python/fakejuju/__init__.py 2016-10-06 22:49:20 +0000 | |||
222 | @@ -0,0 +1,57 @@ | |||
223 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | ||
224 | 2 | |||
225 | 3 | """Support for interaction with fake-juju. | ||
226 | 4 | |||
227 | 5 | "fake-juju" is a combination of the juju and jujud commands that is | ||
228 | 6 | suitable for use in integration tests. It exposes a limited subset | ||
229 | 7 | of the standard juju subcommands (see FakeJuju in this module for | ||
230 | 8 | specifics). When called without any arguments it runs jujud (using | ||
231 | 9 | the dummy provider) with extra logging and testing hooks available to | ||
232 | 10 | control failures. See https://launchpad.net/fake-juju for the project. | ||
233 | 11 | |||
234 | 12 | The binary is named with the Juju version for which it was built. | ||
235 | 13 | For example, for version 1.25.6 the file is named "fake-juju-1.25.6". | ||
236 | 14 | |||
237 | 15 | fake-juju uses the normal Juju local config directory. This defaults | ||
238 | 16 | to ~/.local/shared/juju and may be set using the JUJU_DATA environment | ||
239 | 17 | variable (in 2.x, for 1.x it is JUJU_HOME). | ||
240 | 18 | |||
241 | 19 | In addition to all the normal Juju environment variables (e.g. | ||
242 | 20 | JUJU_DATA), fake-juju uses the following: | ||
243 | 21 | |||
244 | 22 | FAKE_JUJU_FAILURES - the path to the failures file | ||
245 | 23 | The Failures class below sets this to $JUJU_DATA/juju-failures. | ||
246 | 24 | FAKE_JUJU_LOGS_DIR - the path to the logs directory | ||
247 | 25 | This defaults to $JUJU_DATA. | ||
248 | 26 | |||
249 | 27 | fake-juju also creates several extra files: | ||
250 | 28 | |||
251 | 29 | $FAKE_JUJU_LOGS_DIR/fake-juju.log - where fake-juju logs are written | ||
252 | 30 | $JUJU_DATA/fakejuju - fake-juju's data cache | ||
253 | 31 | $JUJU_DATA/fifo - a FIFO file that triggers jujud shutdown | ||
254 | 32 | $JUJU_DATA/cert.ca - the API's CA certificate | ||
255 | 33 | |||
256 | 34 | Normal Juju logging for is written to $JUJU_DATA/fake-juju.log. | ||
257 | 35 | |||
258 | 36 | Failures may be injected into a running fake-juju (or set before | ||
259 | 37 | running). They may be injected by adding them to the file identified | ||
260 | 38 | by $FAKE_JUJU_FAILURES. The format is a single failure definition per | ||
261 | 39 | line. The syntax of the failure definition depends on the failure. | ||
262 | 40 | The currently supported failures (with their definition syntax) are | ||
263 | 41 | listed here: | ||
264 | 42 | |||
265 | 43 | * when adding a unit with a specific ID | ||
266 | 44 | format: "unit-<ID>" (e.g. unit-mysql/0) | ||
267 | 45 | |||
268 | 46 | """ | ||
269 | 47 | |||
270 | 48 | from .fakejuju import get_bootstrap_spec, get_filename, set_envvars, FakeJuju | ||
271 | 49 | |||
272 | 50 | |||
273 | 51 | __all__ = [ | ||
274 | 52 | "__version__", | ||
275 | 53 | "get_bootstrap_spec", "get_filename", "set_envvars", | ||
276 | 54 | "FakeJuju", | ||
277 | 55 | ] | ||
278 | 56 | |||
279 | 57 | __version__ = "0.9.0b1" | ||
280 | 0 | 58 | ||
281 | === added file 'python/fakejuju/failures.py' | |||
282 | --- python/fakejuju/failures.py 1970-01-01 00:00:00 +0000 | |||
283 | +++ python/fakejuju/failures.py 2016-10-06 22:49:20 +0000 | |||
284 | @@ -0,0 +1,65 @@ | |||
285 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | ||
286 | 2 | |||
287 | 3 | import errno | ||
288 | 4 | import os | ||
289 | 5 | import os.path | ||
290 | 6 | |||
291 | 7 | |||
292 | 8 | class Failures(object): | ||
293 | 9 | """The collection of injected failures to use with a fake-juju. | ||
294 | 10 | |||
295 | 11 | The failures are tracked here as well as injected into any | ||
296 | 12 | fake-juju using the initial config dir (aka "juju home"). | ||
297 | 13 | |||
298 | 14 | Note that fake-juju provides only limited capability for | ||
299 | 15 | failure injection. | ||
300 | 16 | """ | ||
301 | 17 | |||
302 | 18 | def __init__(self, cfgdir, entities=None): | ||
303 | 19 | """ | ||
304 | 20 | @param cfgdir: The "juju home" directory into which the | ||
305 | 21 | failures will be registered for injection. | ||
306 | 22 | @param entities: The entity names to start with, if any. | ||
307 | 23 | """ | ||
308 | 24 | filename = os.path.join(cfgdir, "juju-failures") | ||
309 | 25 | entities = set(unicode(tag) for tag in entities or ()) | ||
310 | 26 | |||
311 | 27 | self._filename = unicode(filename) | ||
312 | 28 | self._entities = entities | ||
313 | 29 | |||
314 | 30 | @property | ||
315 | 31 | def filename(self): | ||
316 | 32 | """The path to the failures file the fake-juju reads.""" | ||
317 | 33 | return self._filename | ||
318 | 34 | |||
319 | 35 | @property | ||
320 | 36 | def entities(self): | ||
321 | 37 | """The IDs of the failing entities.""" | ||
322 | 38 | return set(self._entities) | ||
323 | 39 | |||
324 | 40 | def _flush(self): | ||
325 | 41 | """Write the failures to disk.""" | ||
326 | 42 | data = "\n".join(self._entities) + "\n" | ||
327 | 43 | try: | ||
328 | 44 | file = open(self._filename, "w") | ||
329 | 45 | except IOError: | ||
330 | 46 | dirname = os.path.dirname(self._filename) | ||
331 | 47 | if not os.path.exists(dirname): | ||
332 | 48 | os.makedirs(dirname) | ||
333 | 49 | file = open(self._filename, "w") | ||
334 | 50 | with file: | ||
335 | 51 | file.write(data) | ||
336 | 52 | |||
337 | 53 | def fail_entity(self, tag): | ||
338 | 54 | """Inject a global failure for the identified Juju entity.""" | ||
339 | 55 | self._entities.add(tag) | ||
340 | 56 | self._flush() | ||
341 | 57 | |||
342 | 58 | def clear(self): | ||
343 | 59 | """Remove all injected failures.""" | ||
344 | 60 | try: | ||
345 | 61 | os.remove(self._filename) | ||
346 | 62 | except OSError as e: | ||
347 | 63 | if e.errno != errno.ENOENT: | ||
348 | 64 | raise | ||
349 | 65 | self._entities.clear() | ||
350 | 0 | 66 | ||
351 | === added file 'python/fakejuju/fakejuju.py' | |||
352 | --- python/fakejuju/fakejuju.py 1970-01-01 00:00:00 +0000 | |||
353 | +++ python/fakejuju/fakejuju.py 2016-10-06 22:49:20 +0000 | |||
354 | @@ -0,0 +1,145 @@ | |||
355 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | ||
356 | 2 | |||
357 | 3 | from collections import namedtuple | ||
358 | 4 | import os.path | ||
359 | 5 | |||
360 | 6 | import txjuju.cli | ||
361 | 7 | |||
362 | 8 | from .failures import Failures | ||
363 | 9 | |||
364 | 10 | |||
365 | 11 | def get_bootstrap_spec(name, admin_secret=None): | ||
366 | 12 | """Return the BootstrapSpec instance for the given controller. | ||
367 | 13 | |||
368 | 14 | @param name: The controller name to set up. | ||
369 | 15 | @param admin_secret: The admin user password to use. | ||
370 | 16 | """ | ||
371 | 17 | type = "dummy" | ||
372 | 18 | default_series = None # Use the default. | ||
373 | 19 | return txjuju.cli.BootstrapSpec(name, type, default_series, admin_secret) | ||
374 | 20 | |||
375 | 21 | |||
376 | 22 | def get_filename(version, bindir=None): | ||
377 | 23 | """Return the full path to the fake-juju binary for the given version. | ||
378 | 24 | |||
379 | 25 | @param version: The Juju version to use. | ||
380 | 26 | @param bindir: The directory containing the fake-juju binary. | ||
381 | 27 | This defaults to /usr/bin. | ||
382 | 28 | """ | ||
383 | 29 | if not version: | ||
384 | 30 | raise ValueError("version not provided") | ||
385 | 31 | filename = "fake-juju-{}".format(version) | ||
386 | 32 | if bindir is None: | ||
387 | 33 | # XXX Search $PATH. | ||
388 | 34 | bindir = "/usr/bin" | ||
389 | 35 | return os.path.join(bindir, filename) | ||
390 | 36 | |||
391 | 37 | |||
392 | 38 | def set_envvars(envvars, failures_filename=None, logsdir=None): | ||
393 | 39 | """Return the environment variables with which to run fake-juju. | ||
394 | 40 | |||
395 | 41 | @param envvars: The env dict to update. | ||
396 | 42 | @param failures_filename: The path to the failures file that | ||
397 | 43 | fake-juju will use. | ||
398 | 44 | @params logsdir: The path to the directory where fake-juju will | ||
399 | 45 | write its log files. | ||
400 | 46 | """ | ||
401 | 47 | envvars["FAKE_JUJU_FAILURES"] = failures_filename or "" | ||
402 | 48 | envvars["FAKE_JUJU_LOGS_DIR"] = logsdir or "" | ||
403 | 49 | |||
404 | 50 | |||
405 | 51 | class FakeJuju( | ||
406 | 52 | namedtuple("FakeJuju", "filename version cfgdir logsdir failures")): | ||
407 | 53 | """The fundamental details for fake-juju.""" | ||
408 | 54 | |||
409 | 55 | @classmethod | ||
410 | 56 | def from_version(cls, version, cfgdir, | ||
411 | 57 | logsdir=None, failuresdir=None, bindir=None): | ||
412 | 58 | """Return a new instance given the provided information. | ||
413 | 59 | |||
414 | 60 | @param version: The Juju version to fake. | ||
415 | 61 | @param cfgdir: The "juju home" directory to use. | ||
416 | 62 | @param logsdir: The directory where logs will be written. | ||
417 | 63 | This defaults to cfgdir. | ||
418 | 64 | @params failuresdir: The directory where failure injection | ||
419 | 65 | is managed. | ||
420 | 66 | @param bindir: The directory containing the fake-juju binary. | ||
421 | 67 | This defaults to /usr/bin. | ||
422 | 68 | """ | ||
423 | 69 | if logsdir is None: | ||
424 | 70 | logsdir = cfgdir | ||
425 | 71 | if failuresdir is None: | ||
426 | 72 | failuresdir = cfgdir | ||
427 | 73 | filename = get_filename(version, bindir=bindir) | ||
428 | 74 | failures = Failures(failuresdir) | ||
429 | 75 | return cls(filename, version, cfgdir, logsdir, failures) | ||
430 | 76 | |||
431 | 77 | def __new__(cls, filename, version, cfgdir, logsdir=None, failures=None): | ||
432 | 78 | """ | ||
433 | 79 | @param filename: The path to the fake-juju binary. | ||
434 | 80 | @param version: The Juju version to fake. | ||
435 | 81 | @param cfgdir: The "juju home" directory to use. | ||
436 | 82 | @param logsdir: The directory where logs will be written. | ||
437 | 83 | This defaults to cfgdir. | ||
438 | 84 | @param failures: The set of fake-juju failures to use. | ||
439 | 85 | """ | ||
440 | 86 | filename = unicode(filename) if filename else None | ||
441 | 87 | version = unicode(version) if version else None | ||
442 | 88 | cfgdir = unicode(cfgdir) if cfgdir else None | ||
443 | 89 | logsdir = unicode(logsdir) if logsdir is not None else cfgdir | ||
444 | 90 | if failures is None and cfgdir: | ||
445 | 91 | failures = Failures(cfgdir) | ||
446 | 92 | return super(FakeJuju, cls).__new__( | ||
447 | 93 | cls, filename, version, cfgdir, logsdir, failures) | ||
448 | 94 | |||
449 | 95 | def __init__(self, *args, **kwargs): | ||
450 | 96 | if not self.filename: | ||
451 | 97 | raise ValueError("missing filename") | ||
452 | 98 | if not self.version: | ||
453 | 99 | raise ValueError("missing version") | ||
454 | 100 | if not self.cfgdir: | ||
455 | 101 | raise ValueError("missing cfgdir") | ||
456 | 102 | if not self.logsdir: | ||
457 | 103 | raise ValueError("missing logsdir") | ||
458 | 104 | if self.failures is None: | ||
459 | 105 | raise ValueError("missing failures") | ||
460 | 106 | |||
461 | 107 | @property | ||
462 | 108 | def logfile(self): | ||
463 | 109 | """The path to fake-juju's log file.""" | ||
464 | 110 | return os.path.join(self.logsdir, "fake-juju.log") | ||
465 | 111 | |||
466 | 112 | @property | ||
467 | 113 | def infofile(self): | ||
468 | 114 | """The path to fake-juju's data cache.""" | ||
469 | 115 | return os.path.join(self.cfgdir, "fakejuju") | ||
470 | 116 | |||
471 | 117 | @property | ||
472 | 118 | def fifo(self): | ||
473 | 119 | """The path to the fifo file that triggers shutdown.""" | ||
474 | 120 | return os.path.join(self.cfgdir, "fifo") | ||
475 | 121 | |||
476 | 122 | @property | ||
477 | 123 | def cacertfile(self): | ||
478 | 124 | """The path to the API server's certificate.""" | ||
479 | 125 | return os.path.join(self.cfgdir, "cert.ca") | ||
480 | 126 | |||
481 | 127 | def cli(self, envvars=None): | ||
482 | 128 | """ | ||
483 | 129 | |||
484 | 130 | Currently only the following juju subcommands are supported: | ||
485 | 131 | |||
486 | 132 | * bootstrap | ||
487 | 133 | Not that this only supports the dummy provider and the local | ||
488 | 134 | system is only minimally impacted. | ||
489 | 135 | * api-info | ||
490 | 136 | Note that passwords are always omited, even if requested. | ||
491 | 137 | * api-endpoints | ||
492 | 138 | * destroy-environment | ||
493 | 139 | """ | ||
494 | 140 | if envvars is None: | ||
495 | 141 | envvars = os.environ | ||
496 | 142 | envvars = dict(envvars) | ||
497 | 143 | set_envvars(envvars, self.failures._filename, self.logsdir) | ||
498 | 144 | return txjuju.cli.CLI.from_version( | ||
499 | 145 | self.filename, self.version, self.cfgdir, envvars) | ||
500 | 0 | 146 | ||
501 | === added directory 'python/fakejuju/tests' | |||
502 | === added file 'python/fakejuju/tests/__init__.py' | |||
503 | === added file 'python/fakejuju/tests/test_failures.py' | |||
504 | --- python/fakejuju/tests/test_failures.py 1970-01-01 00:00:00 +0000 | |||
505 | +++ python/fakejuju/tests/test_failures.py 2016-10-06 22:49:20 +0000 | |||
506 | @@ -0,0 +1,98 @@ | |||
507 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | ||
508 | 2 | |||
509 | 3 | import os | ||
510 | 4 | import os.path | ||
511 | 5 | import shutil | ||
512 | 6 | import tempfile | ||
513 | 7 | import unittest | ||
514 | 8 | |||
515 | 9 | from fakejuju.failures import Failures | ||
516 | 10 | |||
517 | 11 | |||
518 | 12 | class FailuresTests(unittest.TestCase): | ||
519 | 13 | |||
520 | 14 | def setUp(self): | ||
521 | 15 | super(FailuresTests, self).setUp() | ||
522 | 16 | self.dirname = tempfile.mkdtemp(prefix="fakejuju-test-") | ||
523 | 17 | |||
524 | 18 | def tearDown(self): | ||
525 | 19 | shutil.rmtree(self.dirname) | ||
526 | 20 | super(FailuresTests, self).tearDown() | ||
527 | 21 | |||
528 | 22 | def test_full(self): | ||
529 | 23 | """Failures() works correctly when given all args.""" | ||
530 | 24 | entities = [u"x", u"y", u"z"] | ||
531 | 25 | failures = Failures(u"/some/dir", entities) | ||
532 | 26 | |||
533 | 27 | self.assertEqual(failures.filename, u"/some/dir/juju-failures") | ||
534 | 28 | self.assertEqual(failures.entities, set(entities)) | ||
535 | 29 | |||
536 | 30 | def test_minimal(self): | ||
537 | 31 | """Failures() works correctly when given minimal args.""" | ||
538 | 32 | failures = Failures(u"/some/dir") | ||
539 | 33 | |||
540 | 34 | self.assertEqual(failures.filename, u"/some/dir/juju-failures") | ||
541 | 35 | self.assertEqual(failures.entities, set()) | ||
542 | 36 | |||
543 | 37 | def test_conversion(self): | ||
544 | 38 | """Failures() converts str to unicode.""" | ||
545 | 39 | entities = ["x", "y", "z"] | ||
546 | 40 | failures = Failures("/some/dir", entities) | ||
547 | 41 | |||
548 | 42 | self.assertIsInstance(failures.filename, unicode) | ||
549 | 43 | for id in failures.entities: | ||
550 | 44 | self.assertIsInstance(id, unicode) | ||
551 | 45 | |||
552 | 46 | def test_file_not_created_initially(self): | ||
553 | 47 | """Failures() doesn't create a missing cfgdir until necessary.""" | ||
554 | 48 | failures = Failures(self.dirname) | ||
555 | 49 | |||
556 | 50 | self.assertFalse(os.path.exists(failures.filename)) | ||
557 | 51 | |||
558 | 52 | def test_cfgdir_created(self): | ||
559 | 53 | """Failures() creates a missing cfgdir as soon as it's needed.""" | ||
560 | 54 | dirname = os.path.join(self.dirname, "subdir") | ||
561 | 55 | self.assertFalse(os.path.exists(dirname)) | ||
562 | 56 | failures = Failures(dirname) | ||
563 | 57 | failures.fail_entity("unit-xyz") | ||
564 | 58 | |||
565 | 59 | self.assertTrue(os.path.exists(dirname)) | ||
566 | 60 | |||
567 | 61 | def test_fail_entity_one(self): | ||
568 | 62 | """Failures,fail_entity() writes an initial entry to disk.""" | ||
569 | 63 | failures = Failures(self.dirname) | ||
570 | 64 | failures.fail_entity("unit-abc") | ||
571 | 65 | with open(failures.filename) as file: | ||
572 | 66 | data = file.read() | ||
573 | 67 | |||
574 | 68 | self.assertEqual(data, "unit-abc\n") | ||
575 | 69 | |||
576 | 70 | def test_fail_entity_multiple(self): | ||
577 | 71 | """Failures.fail_entity() correctly writes multiple entries to disk.""" | ||
578 | 72 | failures = Failures(self.dirname) | ||
579 | 73 | failures.fail_entity("unit-abc") | ||
580 | 74 | failures.fail_entity("unit-xyz") | ||
581 | 75 | |||
582 | 76 | with open(failures.filename) as file: | ||
583 | 77 | data = file.read() | ||
584 | 78 | entities = set(tag for tag in data.splitlines() if tag) | ||
585 | 79 | self.assertEqual(entities, failures.entities) | ||
586 | 80 | self.assertTrue(data.endswith("\n")) | ||
587 | 81 | |||
588 | 82 | def test_clear_exists(self): | ||
589 | 83 | """Failures.clear() deletes the failures file if it exists.""" | ||
590 | 84 | failures = Failures(self.dirname) | ||
591 | 85 | failures.fail_entity("unit-abc") | ||
592 | 86 | self.assertTrue(os.path.exists(failures.filename)) | ||
593 | 87 | failures.clear() | ||
594 | 88 | |||
595 | 89 | self.assertFalse(os.path.exists(failures.filename)) | ||
596 | 90 | self.assertEqual(failures.entities, set()) | ||
597 | 91 | |||
598 | 92 | def test_clear_not_exists(self): | ||
599 | 93 | """Failures.clear() does nothing if the failures file is missing.""" | ||
600 | 94 | failures = Failures(self.dirname) | ||
601 | 95 | self.assertFalse(os.path.exists(failures.filename)) | ||
602 | 96 | failures.clear() | ||
603 | 97 | |||
604 | 98 | self.assertFalse(os.path.exists(failures.filename)) | ||
605 | 0 | 99 | ||
606 | === added file 'python/fakejuju/tests/test_fakejuju.py' | |||
607 | --- python/fakejuju/tests/test_fakejuju.py 1970-01-01 00:00:00 +0000 | |||
608 | +++ python/fakejuju/tests/test_fakejuju.py 2016-10-06 22:49:20 +0000 | |||
609 | @@ -0,0 +1,280 @@ | |||
610 | 1 | # Copyright 2016 Canonical Limited. All rights reserved. | ||
611 | 2 | |||
612 | 3 | import os | ||
613 | 4 | import unittest | ||
614 | 5 | |||
615 | 6 | from txjuju import _juju1, _juju2 | ||
616 | 7 | from txjuju._utils import Executable | ||
617 | 8 | import txjuju.cli | ||
618 | 9 | |||
619 | 10 | from fakejuju.failures import Failures | ||
620 | 11 | from fakejuju.fakejuju import ( | ||
621 | 12 | get_bootstrap_spec, get_filename, set_envvars, FakeJuju) | ||
622 | 13 | |||
623 | 14 | |||
624 | 15 | class HelperTests(unittest.TestCase): | ||
625 | 16 | |||
626 | 17 | def test_get_bootstrap_spec_full(self): | ||
627 | 18 | """get_bootstrap_spec() works correctly when given all args.""" | ||
628 | 19 | spec = get_bootstrap_spec("my-env", "pw") | ||
629 | 20 | |||
630 | 21 | self.assertEqual( | ||
631 | 22 | spec, | ||
632 | 23 | txjuju.cli.BootstrapSpec("my-env", "dummy", admin_secret="pw")) | ||
633 | 24 | |||
634 | 25 | def test_get_bootstrap_spec_minimal(self): | ||
635 | 26 | """get_bootstrap_spec() works correctly when given minimal args.""" | ||
636 | 27 | spec = get_bootstrap_spec("my-env") | ||
637 | 28 | |||
638 | 29 | self.assertEqual(spec, txjuju.cli.BootstrapSpec("my-env", "dummy")) | ||
639 | 30 | |||
640 | 31 | def test_get_filename_full(self): | ||
641 | 32 | """get_filename() works correctly when given all args.""" | ||
642 | 33 | filename = get_filename("1.25.6", "/spam") | ||
643 | 34 | |||
644 | 35 | self.assertEqual(filename, "/spam/fake-juju-1.25.6") | ||
645 | 36 | |||
646 | 37 | def test_get_filename_minimal(self): | ||
647 | 38 | """get_filename() works correctly when given minimal args.""" | ||
648 | 39 | filename = get_filename("1.25.6") | ||
649 | 40 | |||
650 | 41 | self.assertEqual(filename, "/usr/bin/fake-juju-1.25.6") | ||
651 | 42 | |||
652 | 43 | def test_get_filename_empty_bindir(self): | ||
653 | 44 | """get_filename() works correctly when given an empty string | ||
654 | 45 | for bindir.""" | ||
655 | 46 | filename = get_filename("1.25.6", "") | ||
656 | 47 | |||
657 | 48 | self.assertEqual(filename, "fake-juju-1.25.6") | ||
658 | 49 | |||
659 | 50 | def test_get_filename_missing_version(self): | ||
660 | 51 | """get_filename() fails if version is None or empty.""" | ||
661 | 52 | with self.assertRaises(ValueError): | ||
662 | 53 | get_filename(None) | ||
663 | 54 | with self.assertRaises(ValueError): | ||
664 | 55 | get_filename("") | ||
665 | 56 | |||
666 | 57 | def test_set_envvars_full(self): | ||
667 | 58 | """set_envvars() works correctly when given all args.""" | ||
668 | 59 | envvars = {} | ||
669 | 60 | set_envvars(envvars, "/spam/failures", "/eggs/logsdir") | ||
670 | 61 | |||
671 | 62 | self.assertEqual(envvars, { | ||
672 | 63 | "FAKE_JUJU_FAILURES": "/spam/failures", | ||
673 | 64 | "FAKE_JUJU_LOGS_DIR": "/eggs/logsdir", | ||
674 | 65 | }) | ||
675 | 66 | |||
676 | 67 | def test_set_envvars_minimal(self): | ||
677 | 68 | """set_envvars() works correctly when given minimal args.""" | ||
678 | 69 | envvars = {} | ||
679 | 70 | set_envvars(envvars) | ||
680 | 71 | |||
681 | 72 | self.assertEqual(envvars, { | ||
682 | 73 | "FAKE_JUJU_FAILURES": "", | ||
683 | 74 | "FAKE_JUJU_LOGS_DIR": "", | ||
684 | 75 | }) | ||
685 | 76 | |||
686 | 77 | def test_set_envvars_start_empty(self): | ||
687 | 78 | """set_envvars() sets all values on an empty dict.""" | ||
688 | 79 | envvars = {} | ||
689 | 80 | set_envvars(envvars, "x", "y") | ||
690 | 81 | |||
691 | 82 | self.assertEqual(envvars, { | ||
692 | 83 | "FAKE_JUJU_FAILURES": "x", | ||
693 | 84 | "FAKE_JUJU_LOGS_DIR": "y", | ||
694 | 85 | }) | ||
695 | 86 | |||
696 | 87 | def test_set_envvars_no_collisions(self): | ||
697 | 88 | """set_envvars() sets all values when none are set yet.""" | ||
698 | 89 | envvars = {"SPAM": "eggs"} | ||
699 | 90 | set_envvars(envvars, "x", "y") | ||
700 | 91 | |||
701 | 92 | self.assertEqual(envvars, { | ||
702 | 93 | "SPAM": "eggs", | ||
703 | 94 | "FAKE_JUJU_FAILURES": "x", | ||
704 | 95 | "FAKE_JUJU_LOGS_DIR": "y", | ||
705 | 96 | }) | ||
706 | 97 | |||
707 | 98 | def test_set_envvars_empty_to_nonempty(self): | ||
708 | 99 | """set_envvars() updates empty values.""" | ||
709 | 100 | envvars = { | ||
710 | 101 | "FAKE_JUJU_FAILURES": "", | ||
711 | 102 | "FAKE_JUJU_LOGS_DIR": "", | ||
712 | 103 | } | ||
713 | 104 | set_envvars(envvars, "x", "y") | ||
714 | 105 | |||
715 | 106 | self.assertEqual(envvars, { | ||
716 | 107 | "FAKE_JUJU_FAILURES": "x", | ||
717 | 108 | "FAKE_JUJU_LOGS_DIR": "y", | ||
718 | 109 | }) | ||
719 | 110 | |||
720 | 111 | def test_set_envvars_nonempty_to_nonempty(self): | ||
721 | 112 | """set_envvars() overwrites existing values.""" | ||
722 | 113 | envvars = { | ||
723 | 114 | "FAKE_JUJU_FAILURES": "spam", | ||
724 | 115 | "FAKE_JUJU_LOGS_DIR": "ham", | ||
725 | 116 | } | ||
726 | 117 | set_envvars(envvars, "x", "y") | ||
727 | 118 | |||
728 | 119 | self.assertEqual(envvars, { | ||
729 | 120 | "FAKE_JUJU_FAILURES": "x", | ||
730 | 121 | "FAKE_JUJU_LOGS_DIR": "y", | ||
731 | 122 | }) | ||
732 | 123 | |||
733 | 124 | def test_set_envvars_nonempty_to_empty(self): | ||
734 | 125 | """set_envvars() with no args "unsets" existing values.""" | ||
735 | 126 | envvars = { | ||
736 | 127 | "FAKE_JUJU_FAILURES": "x", | ||
737 | 128 | "FAKE_JUJU_LOGS_DIR": "y", | ||
738 | 129 | } | ||
739 | 130 | set_envvars(envvars) | ||
740 | 131 | |||
741 | 132 | self.assertEqual(envvars, { | ||
742 | 133 | "FAKE_JUJU_FAILURES": "", | ||
743 | 134 | "FAKE_JUJU_LOGS_DIR": "", | ||
744 | 135 | }) | ||
745 | 136 | |||
746 | 137 | |||
747 | 138 | class FakeJujuTests(unittest.TestCase): | ||
748 | 139 | |||
749 | 140 | def test_from_version_full(self): | ||
750 | 141 | """FakeJuju.from_version() works correctly when given all args.""" | ||
751 | 142 | juju = FakeJuju.from_version( | ||
752 | 143 | "1.25.6", "/a/juju/home", "/logs/dir", "/failures/dir", "/bin/dir") | ||
753 | 144 | |||
754 | 145 | self.assertEqual(juju.filename, "/bin/dir/fake-juju-1.25.6") | ||
755 | 146 | self.assertEqual(juju.version, "1.25.6") | ||
756 | 147 | self.assertEqual(juju.cfgdir, "/a/juju/home") | ||
757 | 148 | self.assertEqual(juju.logsdir, "/logs/dir") | ||
758 | 149 | self.assertEqual(juju.failures.filename, "/failures/dir/juju-failures") | ||
759 | 150 | |||
760 | 151 | def test_from_version_minimal(self): | ||
761 | 152 | """FakeJuju.from_version() works correctly when given minimal args.""" | ||
762 | 153 | juju = FakeJuju.from_version("1.25.6", "/my/juju/home") | ||
763 | 154 | |||
764 | 155 | self.assertEqual(juju.filename, "/usr/bin/fake-juju-1.25.6") | ||
765 | 156 | self.assertEqual(juju.version, "1.25.6") | ||
766 | 157 | self.assertEqual(juju.cfgdir, "/my/juju/home") | ||
767 | 158 | self.assertEqual(juju.logsdir, "/my/juju/home") | ||
768 | 159 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") | ||
769 | 160 | |||
770 | 161 | def test_full(self): | ||
771 | 162 | """FakeJuju() works correctly when given all args.""" | ||
772 | 163 | cfgdir = "/my/juju/home" | ||
773 | 164 | failures = Failures(cfgdir) | ||
774 | 165 | juju = FakeJuju("/fake-juju", "1.25.6", cfgdir, "/some/logs", failures) | ||
775 | 166 | |||
776 | 167 | self.assertEqual(juju.filename, "/fake-juju") | ||
777 | 168 | self.assertEqual(juju.version, "1.25.6") | ||
778 | 169 | self.assertEqual(juju.cfgdir, cfgdir) | ||
779 | 170 | self.assertEqual(juju.logsdir, "/some/logs") | ||
780 | 171 | self.assertIs(juju.failures, failures) | ||
781 | 172 | |||
782 | 173 | def test_minimal(self): | ||
783 | 174 | """FakeJuju() works correctly when given minimal args.""" | ||
784 | 175 | juju = FakeJuju("/fake-juju", "1.25.6", "/my/juju/home") | ||
785 | 176 | |||
786 | 177 | self.assertEqual(juju.filename, "/fake-juju") | ||
787 | 178 | self.assertEqual(juju.version, "1.25.6") | ||
788 | 179 | self.assertEqual(juju.cfgdir, "/my/juju/home") | ||
789 | 180 | self.assertEqual(juju.logsdir, "/my/juju/home") | ||
790 | 181 | self.assertEqual(juju.failures.filename, "/my/juju/home/juju-failures") | ||
791 | 182 | |||
792 | 183 | def test_conversions(self): | ||
793 | 184 | """FakeJuju() converts str to unicode.""" | ||
794 | 185 | juju = FakeJuju("/fake-juju", "1.25.6", "/x", "/y", Failures("/...")) | ||
795 | 186 | |||
796 | 187 | self.assertIsInstance(juju.filename, unicode) | ||
797 | 188 | self.assertIsInstance(juju.version, unicode) | ||
798 | 189 | self.assertIsInstance(juju.cfgdir, unicode) | ||
799 | 190 | self.assertIsInstance(juju.logsdir, unicode) | ||
800 | 191 | |||
801 | 192 | def test_missing_filename(self): | ||
802 | 193 | """FakeJuju() fails if filename is None or empty.""" | ||
803 | 194 | with self.assertRaises(ValueError): | ||
804 | 195 | FakeJuju(None, "1.25.6", "/my/juju/home") | ||
805 | 196 | with self.assertRaises(ValueError): | ||
806 | 197 | FakeJuju("", "1.25.6", "/my/juju/home") | ||
807 | 198 | |||
808 | 199 | def test_missing_version(self): | ||
809 | 200 | """FakeJuju() fails if version is None or empty.""" | ||
810 | 201 | with self.assertRaises(ValueError): | ||
811 | 202 | FakeJuju("/fake-juju", None, "/my/juju/home") | ||
812 | 203 | with self.assertRaises(ValueError): | ||
813 | 204 | FakeJuju("/fake-juju", "", "/my/juju/home") | ||
814 | 205 | |||
815 | 206 | def test_missing_cfgdir(self): | ||
816 | 207 | """FakeJuju() fails if cfgdir is None or empty.""" | ||
817 | 208 | with self.assertRaises(ValueError): | ||
818 | 209 | FakeJuju("/fake-juju", "1.25.6", None) | ||
819 | 210 | with self.assertRaises(ValueError): | ||
820 | 211 | FakeJuju("/fake-juju", "1.25.6", "") | ||
821 | 212 | |||
822 | 213 | def test_logfile(self): | ||
823 | 214 | """FakeJuju.logfile returns the path to the fake-juju log file.""" | ||
824 | 215 | juju = FakeJuju("/fake-juju", "1.25.6", "/x", "/some/logs") | ||
825 | 216 | |||
826 | 217 | self.assertEqual(juju.logfile, "/some/logs/fake-juju.log") | ||
827 | 218 | |||
828 | 219 | def test_infofile(self): | ||
829 | 220 | """FakeJuju.logfile returns the path to the fake-juju info file.""" | ||
830 | 221 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | ||
831 | 222 | |||
832 | 223 | self.assertEqual(juju.infofile, "/x/fakejuju") | ||
833 | 224 | |||
834 | 225 | def test_fifo(self): | ||
835 | 226 | """FakeJuju.logfile returns the path to the fake-juju fifo.""" | ||
836 | 227 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | ||
837 | 228 | |||
838 | 229 | self.assertEqual(juju.fifo, "/x/fifo") | ||
839 | 230 | |||
840 | 231 | def test_cacertfile(self): | ||
841 | 232 | """FakeJuju.cacertfile returns the path to the Juju API cert.""" | ||
842 | 233 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | ||
843 | 234 | |||
844 | 235 | self.assertEqual(juju.cacertfile, "/x/cert.ca") | ||
845 | 236 | |||
846 | 237 | def test_cli_full(self): | ||
847 | 238 | """FakeJuju.cli() works correctly when given all args.""" | ||
848 | 239 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | ||
849 | 240 | cli = juju.cli({"SPAM": "eggs"}) | ||
850 | 241 | |||
851 | 242 | self.assertEqual( | ||
852 | 243 | cli._exe, | ||
853 | 244 | Executable("/fake-juju", { | ||
854 | 245 | "SPAM": "eggs", | ||
855 | 246 | "FAKE_JUJU_FAILURES": "/x/juju-failures", | ||
856 | 247 | "FAKE_JUJU_LOGS_DIR": "/x", | ||
857 | 248 | "JUJU_HOME": "/x", | ||
858 | 249 | }), | ||
859 | 250 | ) | ||
860 | 251 | |||
861 | 252 | def test_cli_minimal(self): | ||
862 | 253 | """FakeJuju.cli() works correctly when given minimal args.""" | ||
863 | 254 | juju = FakeJuju("/fake-juju", "1.25.6", "/x") | ||
864 | 255 | cli = juju.cli() | ||
865 | 256 | |||
866 | 257 | self.assertEqual( | ||
867 | 258 | cli._exe, | ||
868 | 259 | Executable("/fake-juju", dict(os.environ, **{ | ||
869 | 260 | "FAKE_JUJU_FAILURES": "/x/juju-failures", | ||
870 | 261 | "FAKE_JUJU_LOGS_DIR": "/x", | ||
871 | 262 | "JUJU_HOME": "/x", | ||
872 | 263 | })), | ||
873 | 264 | ) | ||
874 | 265 | |||
875 | 266 | def test_cli_juju1(self): | ||
876 | 267 | """FakeJuju.cli() works correctly for Juju 1.x.""" | ||
877 | 268 | juju = FakeJuju.from_version("1.25.6", "/x") | ||
878 | 269 | cli = juju.cli() | ||
879 | 270 | |||
880 | 271 | self.assertEqual(cli._exe.envvars["JUJU_HOME"], "/x") | ||
881 | 272 | self.assertIsInstance(cli._juju, _juju1.CLIHooks) | ||
882 | 273 | |||
883 | 274 | def test_cli_juju2(self): | ||
884 | 275 | """FakeJuju.cli() works correctly for Juju 2.x.""" | ||
885 | 276 | juju = FakeJuju.from_version("2.0.0", "/x") | ||
886 | 277 | cli = juju.cli() | ||
887 | 278 | |||
888 | 279 | self.assertEqual(cli._exe.envvars["JUJU_DATA"], "/x") | ||
889 | 280 | self.assertIsInstance(cli._juju, _juju2.CLIHooks) | ||
890 | 0 | 281 | ||
891 | === added file 'python/setup.py' | |||
892 | --- python/setup.py 1970-01-01 00:00:00 +0000 | |||
893 | +++ python/setup.py 2016-10-06 22:49:20 +0000 | |||
894 | @@ -0,0 +1,69 @@ | |||
895 | 1 | import os | ||
896 | 2 | from importlib import import_module | ||
897 | 3 | try: | ||
898 | 4 | from setuptools import setup | ||
899 | 5 | except ImportError: | ||
900 | 6 | from distutils.core import setup | ||
901 | 7 | |||
902 | 8 | |||
903 | 9 | basedir = os.path.abspath(os.path.dirname(__file__) or '.') | ||
904 | 10 | |||
905 | 11 | # required data | ||
906 | 12 | |||
907 | 13 | package_name = 'fakejuju' | ||
908 | 14 | NAME = package_name | ||
909 | 15 | SUMMARY = 'A limited adaptation of Juju\'s client, with testing hooks.' | ||
910 | 16 | AUTHOR = 'Canonical Landscape team' | ||
911 | 17 | EMAIL = 'juju@lists.ubuntu.com' | ||
912 | 18 | PROJECT_URL = 'https://launchpad.net/fake-juju' | ||
913 | 19 | LICENSE = 'LGPLv3' | ||
914 | 20 | |||
915 | 21 | with open(os.path.join(basedir, 'README.md')) as readme_file: | ||
916 | 22 | DESCRIPTION = readme_file.read() | ||
917 | 23 | |||
918 | 24 | # dymanically generated data | ||
919 | 25 | |||
920 | 26 | VERSION = import_module(package_name).__version__ | ||
921 | 27 | |||
922 | 28 | # set up packages | ||
923 | 29 | |||
924 | 30 | exclude_dirs = [ | ||
925 | 31 | 'tests', | ||
926 | 32 | ] | ||
927 | 33 | |||
928 | 34 | PACKAGES = [] | ||
929 | 35 | for path, dirs, files in os.walk(package_name): | ||
930 | 36 | if "__init__.py" not in files: | ||
931 | 37 | continue | ||
932 | 38 | path = path.split(os.sep) | ||
933 | 39 | if path[-1] in exclude_dirs: | ||
934 | 40 | continue | ||
935 | 41 | PACKAGES.append(".".join(path)) | ||
936 | 42 | |||
937 | 43 | # dependencies | ||
938 | 44 | |||
939 | 45 | DEPS = ['yaml', | ||
940 | 46 | # for testing | ||
941 | 47 | 'txjuju', | ||
942 | 48 | 'fixtures', | ||
943 | 49 | 'testtools', | ||
944 | 50 | ] | ||
945 | 51 | |||
946 | 52 | |||
947 | 53 | if __name__ == "__main__": | ||
948 | 54 | setup(name=NAME, | ||
949 | 55 | version=VERSION, | ||
950 | 56 | author=AUTHOR, | ||
951 | 57 | author_email=EMAIL, | ||
952 | 58 | url=PROJECT_URL, | ||
953 | 59 | license=LICENSE, | ||
954 | 60 | description=SUMMARY, | ||
955 | 61 | long_description=DESCRIPTION, | ||
956 | 62 | packages=PACKAGES, | ||
957 | 63 | |||
958 | 64 | # for distutils | ||
959 | 65 | requires=DEPS, | ||
960 | 66 | |||
961 | 67 | # for setuptools | ||
962 | 68 | install_requires=DEPS, | ||
963 | 69 | ) |