Merge lp:~benji/juju-gui/go-sandbox into lp:juju-gui/experimental
- go-sandbox
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Diogo Matsubara |
Approved revision: | 721 |
Merged at revision: | 728 |
Proposed branch: | lp:~benji/juju-gui/go-sandbox |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
675 lines (+449/-32) 8 files modified
app/app.js (+13/-6) app/models/models.js (+1/-1) app/store/env/sandbox.js (+203/-1) lib/deploy_charm_for_testing.py (+0/-1) test/test_app.js (+11/-2) test/test_deploy_charm_for_testing.py (+24/-14) test/test_env_go.js (+2/-0) test/test_sandbox.js (+195/-7) |
To merge this branch: | bzr merge lp:~benji/juju-gui/go-sandbox |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diogo Matsubara (community) | Approve | ||
Review via email: mp+169036@code.launchpad.net |
Commit message
The beginings of a Go-flavored sandbox.
Logging in, deploying a service, and moving it around all work.
Description of the change
The beginings of a Go-flavored sandbox.
Logging in, deploying a service, and moving it around all work.
Gary Poster (gary) wrote : | # |
Jeff Pihach (hatch) wrote : | # |
LGTM Thanks this looks good. I am not sure about the method casing
senario :-)
https:/
File app/store/
https:/
app/store/
camel_Snake_Case? ;-)
https:/
File test/test_
https:/
test/test_
async failures hard to detect.
this technique has caused false failures on our CI before because of a
slow instance so I would suggest increasing it or removing all together.
https:/
test/test_
cleanups is never used.
Jujugui Lander (jujugui-lander) wrote : | # |
No commit message specified.
Jujugui Lander (jujugui-lander) wrote : | # |
Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0. Got: 1 Pending.
Diogo Matsubara (matsubara) : | # |
Jujugui Lander (jujugui-lander) wrote : | # |
The Jenkins job https:/
Not merging it.
Preview Diff
1 | === modified file 'app/app.js' | |||
2 | --- app/app.js 2013-06-12 17:55:05 +0000 | |||
3 | +++ app/app.js 2013-06-13 12:44:26 +0000 | |||
4 | @@ -360,8 +360,7 @@ | |||
5 | 360 | } | 360 | } |
6 | 361 | } | 361 | } |
7 | 362 | 362 | ||
10 | 363 | 363 | if (window.flags && window.flags.websocket_capture) { | |
9 | 364 | if (window.flags.websocket_capture) { | ||
11 | 365 | this.websocketLogging = new Y.juju.WebsocketLogging(); | 364 | this.websocketLogging = new Y.juju.WebsocketLogging(); |
12 | 366 | } | 365 | } |
13 | 367 | 366 | ||
14 | @@ -448,7 +447,7 @@ | |||
15 | 448 | }; | 447 | }; |
16 | 449 | var apiBackend = this.get('apiBackend'); | 448 | var apiBackend = this.get('apiBackend'); |
17 | 450 | // The sandbox mode does not support the Go API (yet?). | 449 | // The sandbox mode does not support the Go API (yet?). |
19 | 451 | if (this.get('sandbox') && apiBackend === 'python') { | 450 | if (this.get('sandbox')) { |
20 | 452 | var sandboxModule = Y.namespace('juju.environments.sandbox'); | 451 | var sandboxModule = Y.namespace('juju.environments.sandbox'); |
21 | 453 | var State = Y.namespace('juju.environments').FakeBackend; | 452 | var State = Y.namespace('juju.environments').FakeBackend; |
22 | 454 | var state = new State({charmStore: this.charm_store}); | 453 | var state = new State({charmStore: this.charm_store}); |
23 | @@ -457,8 +456,16 @@ | |||
24 | 457 | credentials[envOptions.user] = envOptions.password; | 456 | credentials[envOptions.user] = envOptions.password; |
25 | 458 | state.set('authorizedUsers', credentials); | 457 | state.set('authorizedUsers', credentials); |
26 | 459 | } | 458 | } |
29 | 460 | envOptions.conn = new sandboxModule.ClientConnection( | 459 | if (apiBackend === 'python') { |
30 | 461 | {juju: new sandboxModule.PyJujuAPI({state: state})}); | 460 | envOptions.conn = new sandboxModule.ClientConnection( |
31 | 461 | {juju: new sandboxModule.PyJujuAPI({state: state})}); | ||
32 | 462 | } else if (apiBackend === 'go') { | ||
33 | 463 | envOptions.conn = new sandboxModule.ClientConnection( | ||
34 | 464 | {juju: new sandboxModule.GoJujuAPI({state: state})}); | ||
35 | 465 | } else { | ||
36 | 466 | throw 'unrecognized backend type: ' + apiBackend; | ||
37 | 467 | } | ||
38 | 468 | |||
39 | 462 | } | 469 | } |
40 | 463 | this.env = juju.newEnvironment(envOptions, apiBackend); | 470 | this.env = juju.newEnvironment(envOptions, apiBackend); |
41 | 464 | } | 471 | } |
42 | @@ -938,7 +945,7 @@ | |||
43 | 938 | // If the Juju environment is not connected, exit without letting the | 945 | // If the Juju environment is not connected, exit without letting the |
44 | 939 | // route dispatch proceed. On env connection change, the app will | 946 | // route dispatch proceed. On env connection change, the app will |
45 | 940 | // re-dispatch and this route callback will be executed again. | 947 | // re-dispatch and this route callback will be executed again. |
47 | 941 | if (!this.env.get('connected')) { | 948 | if (!this.env || !this.env.get('connected')) { |
48 | 942 | return; | 949 | return; |
49 | 943 | } | 950 | } |
50 | 944 | var credentials = this.env.getCredentials(); | 951 | var credentials = this.env.getCredentials(); |
51 | 945 | 952 | ||
52 | === modified file 'app/models/models.js' | |||
53 | --- app/models/models.js 2013-06-12 15:41:45 +0000 | |||
54 | +++ app/models/models.js 2013-06-13 12:44:26 +0000 | |||
55 | @@ -344,7 +344,7 @@ | |||
56 | 344 | service.set('aggregated_status', aggregate); | 344 | service.set('aggregated_status', aggregate); |
57 | 345 | 345 | ||
58 | 346 | // Set Google Analytics tracking event. | 346 | // Set Google Analytics tracking event. |
60 | 347 | if (previous_unit_count !== sum) { | 347 | if (previous_unit_count !== sum && window._gaq) { |
61 | 348 | window._gaq.push(['_trackEvent', 'Service Stats', 'Update', | 348 | window._gaq.push(['_trackEvent', 'Service Stats', 'Update', |
62 | 349 | service.get('id'), sum]); | 349 | service.get('id'), sum]); |
63 | 350 | } | 350 | } |
64 | 351 | 351 | ||
65 | === modified file 'app/store/env/sandbox.js' | |||
66 | --- app/store/env/sandbox.js 2013-05-17 14:51:05 +0000 | |||
67 | +++ app/store/env/sandbox.js 2013-06-13 12:44:26 +0000 | |||
68 | @@ -19,7 +19,7 @@ | |||
69 | 19 | 'use strict'; | 19 | 'use strict'; |
70 | 20 | 20 | ||
71 | 21 | /** | 21 | /** |
73 | 22 | Sandbox APIs mimicking communications with the Go and Juju backends. | 22 | Sandbox APIs mimicking communications with the Go and Python backends. |
74 | 23 | 23 | ||
75 | 24 | @module env | 24 | @module env |
76 | 25 | @submodule env.sandbox | 25 | @submodule env.sandbox |
77 | @@ -682,6 +682,208 @@ | |||
78 | 682 | }); | 682 | }); |
79 | 683 | 683 | ||
80 | 684 | sandboxModule.PyJujuAPI = PyJujuAPI; | 684 | sandboxModule.PyJujuAPI = PyJujuAPI; |
81 | 685 | |||
82 | 686 | /** | ||
83 | 687 | A sandbox Juju environment using the Go API. | ||
84 | 688 | |||
85 | 689 | @class GoJujuAPI | ||
86 | 690 | */ | ||
87 | 691 | function GoJujuAPI(config) { | ||
88 | 692 | GoJujuAPI.superclass.constructor.apply(this, arguments); | ||
89 | 693 | } | ||
90 | 694 | |||
91 | 695 | GoJujuAPI.NAME = 'sandbox-py-juju-api'; | ||
92 | 696 | GoJujuAPI.ATTRS = { | ||
93 | 697 | state: {}, | ||
94 | 698 | client: {} | ||
95 | 699 | }; | ||
96 | 700 | |||
97 | 701 | Y.extend(GoJujuAPI, Y.Base, { | ||
98 | 702 | |||
99 | 703 | /** | ||
100 | 704 | Initializes. | ||
101 | 705 | |||
102 | 706 | @method initializer | ||
103 | 707 | @return {undefined} Nothing. | ||
104 | 708 | */ | ||
105 | 709 | initializer: function() { | ||
106 | 710 | this.connected = false; | ||
107 | 711 | }, | ||
108 | 712 | |||
109 | 713 | /** | ||
110 | 714 | Opens the connection to the sandbox Juju environment. | ||
111 | 715 | Called by ClientConnection, which sends itself. | ||
112 | 716 | |||
113 | 717 | @method open | ||
114 | 718 | @param {Object} client A ClientConnection. | ||
115 | 719 | @return {undefined} Nothing. | ||
116 | 720 | */ | ||
117 | 721 | open: function(client) { | ||
118 | 722 | if (!this.connected) { | ||
119 | 723 | this.connected = true; | ||
120 | 724 | this.set('client', client); | ||
121 | 725 | } else if (this.get('client') !== client) { | ||
122 | 726 | throw 'INVALID_STATE_ERR : Connection is open to another client.'; | ||
123 | 727 | } | ||
124 | 728 | }, | ||
125 | 729 | |||
126 | 730 | /** | ||
127 | 731 | Closes the connection to the sandbox Juju environment. | ||
128 | 732 | Called by ClientConnection. | ||
129 | 733 | |||
130 | 734 | @method close | ||
131 | 735 | @return {undefined} Nothing. | ||
132 | 736 | */ | ||
133 | 737 | close: function() { | ||
134 | 738 | if (this.connected) { | ||
135 | 739 | this.connected = false; | ||
136 | 740 | this.set('client', undefined); | ||
137 | 741 | } | ||
138 | 742 | }, | ||
139 | 743 | |||
140 | 744 | /** | ||
141 | 745 | Do any extra work to destroy the object. | ||
142 | 746 | |||
143 | 747 | @method destructor | ||
144 | 748 | @return {undefined} Nothing. | ||
145 | 749 | */ | ||
146 | 750 | destructor: function() { | ||
147 | 751 | this.close(); | ||
148 | 752 | }, | ||
149 | 753 | |||
150 | 754 | /** | ||
151 | 755 | Receives messages from the client and dispatches them. | ||
152 | 756 | |||
153 | 757 | @method receive | ||
154 | 758 | @param {Object} data A hash of data sent from the client. | ||
155 | 759 | @return {undefined} Nothing. | ||
156 | 760 | */ | ||
157 | 761 | receive: function(data) { | ||
158 | 762 | console.log('client message', data); | ||
159 | 763 | if (this.connected) { | ||
160 | 764 | this['handle' + data.Type + data.Request](data, | ||
161 | 765 | this.get('client'), this.get('state')); | ||
162 | 766 | } else { | ||
163 | 767 | throw CLOSEDERROR; | ||
164 | 768 | } | ||
165 | 769 | }, | ||
166 | 770 | |||
167 | 771 | /** | ||
168 | 772 | Handle Login messages to the state object. | ||
169 | 773 | |||
170 | 774 | @method handleAdminLogin | ||
171 | 775 | @param {Object} data The contents of the API arguments. | ||
172 | 776 | @param {Object} client The active ClientConnection. | ||
173 | 777 | @param {Object} state An instance of FakeBackend. | ||
174 | 778 | */ | ||
175 | 779 | handleAdminLogin: function(data, client, state) { | ||
176 | 780 | data.Error = !state.login(data.Params.AuthTag, data.Params.Password); | ||
177 | 781 | client.receive(data); | ||
178 | 782 | }, | ||
179 | 783 | |||
180 | 784 | /** | ||
181 | 785 | Handle EnvironmentView messages. | ||
182 | 786 | |||
183 | 787 | @method handleClientServiceGet | ||
184 | 788 | @param {Object} data The contents of the API arguments. | ||
185 | 789 | @param {Object} client The active ClientConnection. | ||
186 | 790 | @param {Object} state An instance of FakeBackend. | ||
187 | 791 | */ | ||
188 | 792 | handleClientEnvironmentInfo: function(data, client, state) { | ||
189 | 793 | client.receive({ | ||
190 | 794 | ProviderType: state.get('providerType'), | ||
191 | 795 | DefaultSeries: state.get('defaultSeries'), | ||
192 | 796 | Name: 'Sandbox' | ||
193 | 797 | }); | ||
194 | 798 | }, | ||
195 | 799 | |||
196 | 800 | /** | ||
197 | 801 | Handle WatchAll messages. | ||
198 | 802 | |||
199 | 803 | @method handleClientWatchAll | ||
200 | 804 | @param {Object} data The contents of the API arguments. | ||
201 | 805 | @param {Object} client The active ClientConnection. | ||
202 | 806 | @param {Object} state An instance of FakeBackend. | ||
203 | 807 | */ | ||
204 | 808 | handleClientWatchAll: function(data, client, state) { | ||
205 | 809 | // TODO wire up delta stream to "Next" responses here. | ||
206 | 810 | client.receive({Response: {AllWatcherId: '42'}}); | ||
207 | 811 | }, | ||
208 | 812 | |||
209 | 813 | /** | ||
210 | 814 | Handle AllWatcher Next messages. | ||
211 | 815 | |||
212 | 816 | @method handleAllWatcherNext | ||
213 | 817 | @param {Object} data The contents of the API arguments. | ||
214 | 818 | @param {Object} client The active ClientConnection. | ||
215 | 819 | @param {Object} state An instance of FakeBackend. | ||
216 | 820 | */ | ||
217 | 821 | handleAllWatcherNext: function(data, client, state) { | ||
218 | 822 | // This is a noop for the moment because it must exist but the current | ||
219 | 823 | // functionality does not depend on it having any effect. | ||
220 | 824 | // TODO See if there are any changes and if so, send them. | ||
221 | 825 | }, | ||
222 | 826 | |||
223 | 827 | /** | ||
224 | 828 | Handle ServiceDeploy messages | ||
225 | 829 | |||
226 | 830 | @method handleClientServiceDeploy | ||
227 | 831 | @param {Object} data The contents of the API arguments. | ||
228 | 832 | @param {Object} client The active ClientConnection. | ||
229 | 833 | @param {Object} state An instance of FakeBackend. | ||
230 | 834 | */ | ||
231 | 835 | handleClientServiceDeploy: function(data, client, state) { | ||
232 | 836 | var callback = function(result) { | ||
233 | 837 | var response = {RequestId: data.RequestId}; | ||
234 | 838 | if (result.error) { | ||
235 | 839 | response.Error = result.error; | ||
236 | 840 | } | ||
237 | 841 | client.receive(response); | ||
238 | 842 | }; | ||
239 | 843 | state.deploy(data.Params.CharmUrl, callback, { | ||
240 | 844 | name: data.Params.ServiceName, | ||
241 | 845 | config: data.Params.Config, | ||
242 | 846 | configYAML: data.Params.ConfigYAML, | ||
243 | 847 | unitCount: data.Params.NumUnits | ||
244 | 848 | }); | ||
245 | 849 | }, | ||
246 | 850 | |||
247 | 851 | /** | ||
248 | 852 | Handle SetAnnotations messages | ||
249 | 853 | |||
250 | 854 | @method handleClientSetAnnotations | ||
251 | 855 | @param {Object} data The contents of the API arguments. | ||
252 | 856 | @param {Object} client The active ClientConnection. | ||
253 | 857 | @param {Object} state An instance of FakeBackend. | ||
254 | 858 | */ | ||
255 | 859 | handleClientSetAnnotations: function(data, client, state) { | ||
256 | 860 | var serviceId = /service-([^ ]*)$/.exec(data.Params.Tag)[1]; | ||
257 | 861 | var reply = state.updateAnnotations(serviceId, data.Params.Pairs); | ||
258 | 862 | client.receive({ | ||
259 | 863 | RequestId: data.RequestId, | ||
260 | 864 | Error: reply.error}); | ||
261 | 865 | }, | ||
262 | 866 | |||
263 | 867 | /** | ||
264 | 868 | Handle ServiceGet messages | ||
265 | 869 | |||
266 | 870 | @method handleClientServiceGet | ||
267 | 871 | @param {Object} data The contents of the API arguments. | ||
268 | 872 | @param {Object} client The active ClientConnection. | ||
269 | 873 | @param {Object} state An instance of FakeBackend. | ||
270 | 874 | */ | ||
271 | 875 | handleClientServiceGet: function(data, client, state) { | ||
272 | 876 | var reply = state.getService(data.Params.ServiceName); | ||
273 | 877 | // TODO Include the optional Config or Constraints in the response. | ||
274 | 878 | client.receive({ | ||
275 | 879 | RequestId: data.RequestId, | ||
276 | 880 | Error: reply.error, | ||
277 | 881 | Response: {Service: data.Params.ServiceName}}); | ||
278 | 882 | } | ||
279 | 883 | |||
280 | 884 | }); | ||
281 | 885 | |||
282 | 886 | sandboxModule.GoJujuAPI = GoJujuAPI; | ||
283 | 685 | }, '0.1.0', { | 887 | }, '0.1.0', { |
284 | 686 | requires: [ | 888 | requires: [ |
285 | 687 | 'base', | 889 | 'base', |
286 | 688 | 890 | ||
287 | === modified file 'lib/deploy_charm_for_testing.py' | |||
288 | --- lib/deploy_charm_for_testing.py 2013-05-22 11:23:34 +0000 | |||
289 | +++ lib/deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000 | |||
290 | @@ -88,7 +88,6 @@ | |||
291 | 88 | while True: | 88 | while True: |
292 | 89 | state = get_state() | 89 | state = get_state() |
293 | 90 | if 'error' in state: | 90 | if 'error' in state: |
294 | 91 | print(get_status()) | ||
295 | 92 | raise RuntimeError('error deploying service') | 91 | raise RuntimeError('error deploying service') |
296 | 93 | if state == 'started': | 92 | if state == 'started': |
297 | 94 | break | 93 | break |
298 | 95 | 94 | ||
299 | === modified file 'test/test_app.js' | |||
300 | --- test/test_app.js 2013-06-11 12:23:12 +0000 | |||
301 | +++ test/test_app.js 2013-06-13 12:44:26 +0000 | |||
302 | @@ -112,6 +112,15 @@ | |||
303 | 112 | assert.equal(app.env.get('password'), undefined); | 112 | assert.equal(app.env.get('password'), undefined); |
304 | 113 | }); | 113 | }); |
305 | 114 | 114 | ||
306 | 115 | it('should report backend misconfiguration', function() { | ||
307 | 116 | var config = { | ||
308 | 117 | sandbox: true, | ||
309 | 118 | apiBackend: 'not really a backend'}; | ||
310 | 119 | assert.throws( | ||
311 | 120 | function() {var stupidLinter = new Y.juju.App(config);}, | ||
312 | 121 | 'unrecognized backend type: not really a backend'); | ||
313 | 122 | }); | ||
314 | 123 | |||
315 | 115 | it('should propagate login credentials from the configuration', | 124 | it('should propagate login credentials from the configuration', |
316 | 116 | function(done) { | 125 | function(done) { |
317 | 117 | var the_username = 'nehi'; | 126 | var the_username = 'nehi'; |
318 | @@ -487,7 +496,7 @@ | |||
319 | 487 | })(); | 496 | })(); |
320 | 488 | 497 | ||
321 | 489 | (function() { | 498 | (function() { |
323 | 490 | describe('Application sandbox', function() { | 499 | describe('Application sandbox mode', function() { |
324 | 491 | var Y, app, container, utils; | 500 | var Y, app, container, utils; |
325 | 492 | 501 | ||
326 | 493 | before(function(done) { | 502 | before(function(done) { |
327 | @@ -507,7 +516,7 @@ | |||
328 | 507 | } | 516 | } |
329 | 508 | }); | 517 | }); |
330 | 509 | 518 | ||
332 | 510 | it('app instantiates correctly in sandbox mode.', function() { | 519 | it('instantiates correctly', function() { |
333 | 511 | app = new Y.juju.App( | 520 | app = new Y.juju.App( |
334 | 512 | { container: container, | 521 | { container: container, |
335 | 513 | viewContainer: container, | 522 | viewContainer: container, |
336 | 514 | 523 | ||
337 | === modified file 'test/test_deploy_charm_for_testing.py' | |||
338 | --- test/test_deploy_charm_for_testing.py 2013-05-28 14:00:30 +0000 | |||
339 | +++ test/test_deploy_charm_for_testing.py 2013-06-13 12:44:26 +0000 | |||
340 | @@ -102,7 +102,7 @@ | |||
341 | 102 | self.__class__.options = options | 102 | self.__class__.options = options |
342 | 103 | 103 | ||
343 | 104 | 104 | ||
345 | 105 | # Mock argparse | 105 | # Fake argparse output. |
346 | 106 | def _options(**kwargs): | 106 | def _options(**kwargs): |
347 | 107 | def _wrapper(): | 107 | def _wrapper(): |
348 | 108 | class _o(dict): | 108 | class _o(dict): |
349 | @@ -113,14 +113,15 @@ | |||
350 | 113 | return result | 113 | return result |
351 | 114 | return _wrapper | 114 | return _wrapper |
352 | 115 | 115 | ||
353 | 116 | |||
354 | 116 | class TestScript(unittest.TestCase): | 117 | class TestScript(unittest.TestCase): |
355 | 117 | """The main() function is the entry point when run as a script.""" | 118 | """The main() function is the entry point when run as a script.""" |
356 | 118 | 119 | ||
357 | 119 | def test_status_messages_are_displayed(self): | 120 | def test_status_messages_are_displayed(self): |
358 | 120 | # While running, the script tells the user what is happening. | 121 | # While running, the script tells the user what is happening. |
359 | 121 | printed = [] | 122 | printed = [] |
362 | 122 | main(options=_options(), print=printed.append, juju=noop, wait_for_service=noop, | 123 | main(options=_options(), print=printed.append, juju=noop, |
363 | 123 | wait_for_machine=noop) | 124 | wait_for_service=noop, wait_for_machine=noop) |
364 | 124 | self.assertSequenceEqual( | 125 | self.assertSequenceEqual( |
365 | 125 | printed, | 126 | printed, |
366 | 126 | ['Bootstrapping...', | 127 | ['Bootstrapping...', |
367 | @@ -136,22 +137,26 @@ | |||
368 | 136 | def juju(s): | 137 | def juju(s): |
369 | 137 | juju_commands.append(s) | 138 | juju_commands.append(s) |
370 | 138 | 139 | ||
374 | 139 | main(options=_options(origin='lp:foo'), print=noop, juju=juju, wait_for_service=noop, | 140 | main(options=_options(origin='lp:foo'), print=noop, juju=juju, |
375 | 140 | make_config_file=MakeConfigFile, wait_for_machine=noop, | 141 | wait_for_service=noop, make_config_file=MakeConfigFile, |
376 | 141 | make_environments_yaml=noop) | 142 | wait_for_machine=noop, make_environments_yaml=noop) |
377 | 142 | options = MakeConfigFile.options | 143 | options = MakeConfigFile.options |
378 | 143 | deploy_command = juju_commands[1] | 144 | deploy_command = juju_commands[1] |
379 | 144 | self.assertIn('--config my-config-file.yaml', deploy_command) | 145 | self.assertIn('--config my-config-file.yaml', deploy_command) |
382 | 145 | self.assertDictEqual({'serve-tests': True, 'staging': True, | 146 | self.assertDictEqual( |
383 | 146 | 'juju-gui-source': 'lp:foo', 'secure': False}, options) | 147 | {'serve-tests': True, |
384 | 148 | 'staging': True, | ||
385 | 149 | 'juju-gui-source': 'lp:foo', | ||
386 | 150 | 'secure': False}, | ||
387 | 151 | options) | ||
388 | 147 | 152 | ||
389 | 148 | def test_providing_a_branch(self): | 153 | def test_providing_a_branch(self): |
390 | 149 | # If the user provides a branch name on the command line, it will be | 154 | # If the user provides a branch name on the command line, it will be |
391 | 150 | # passed to the charm. | 155 | # passed to the charm. |
392 | 151 | printed = [] | 156 | printed = [] |
393 | 152 | 157 | ||
396 | 153 | main(options=_options(origin='lp:foo'), print=printed.append, juju=noop, | 158 | main(options=_options(origin='lp:foo'), print=printed.append, |
397 | 154 | wait_for_service=noop, make_config_file=MakeConfigFile, | 159 | juju=noop, wait_for_service=noop, make_config_file=MakeConfigFile, |
398 | 155 | wait_for_machine=noop, make_environments_yaml=noop) | 160 | wait_for_machine=noop, make_environments_yaml=noop) |
399 | 156 | options = MakeConfigFile.options | 161 | options = MakeConfigFile.options |
400 | 157 | self.assertSequenceEqual( | 162 | self.assertSequenceEqual( |
401 | @@ -164,6 +169,7 @@ | |||
402 | 164 | self.assertIn('juju-gui-source', options) | 169 | self.assertIn('juju-gui-source', options) |
403 | 165 | self.assertEqual('lp:foo', options['juju-gui-source']) | 170 | self.assertEqual('lp:foo', options['juju-gui-source']) |
404 | 166 | 171 | ||
405 | 172 | |||
406 | 167 | class TestParseImageData(unittest.TestCase): | 173 | class TestParseImageData(unittest.TestCase): |
407 | 168 | """Test the nova machine image data parsing.""" | 174 | """Test the nova machine image data parsing.""" |
408 | 169 | 175 | ||
409 | @@ -209,13 +215,17 @@ | |||
410 | 209 | 215 | ||
411 | 210 | def test_picks_last(self): | 216 | def test_picks_last(self): |
412 | 211 | data = '\n'.join([ | 217 | data = '\n'.join([ |
416 | 212 | "| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img | ACTIVE | |", | 218 | '| abc-123 | ubuntu-released/ubuntu-precise-12.04-amd64-1.img ' + |
417 | 213 | "| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img | ACTIVE | |", | 219 | '| ACTIVE | |', |
418 | 214 | "| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img | ACTIVE | |"]) | 220 | '| def-123 | smoser-proposed/ubuntu-precise-12.04-amd64-2.img ' + |
419 | 221 | '| ACTIVE | |', | ||
420 | 222 | '| fad-123 | ubuntu-released/ubuntu-precise-12.04-amd64-3.img ' + | ||
421 | 223 | '| ACTIVE | |']) | ||
422 | 215 | img_id, desc = parse_image_data(data) | 224 | img_id, desc = parse_image_data(data) |
423 | 216 | self.assertEqual('fad-123', img_id) | 225 | self.assertEqual('fad-123', img_id) |
424 | 217 | self.assertEqual( | 226 | self.assertEqual( |
426 | 218 | 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img', desc) | 227 | 'ubuntu-released/ubuntu-precise-12.04-amd64-3.img', |
427 | 228 | desc) | ||
428 | 219 | 229 | ||
429 | 220 | 230 | ||
430 | 221 | if __name__ == '__main__': | 231 | if __name__ == '__main__': |
431 | 222 | 232 | ||
432 | === modified file 'test/test_env_go.js' | |||
433 | --- test/test_env_go.js 2013-05-21 16:30:38 +0000 | |||
434 | +++ test/test_env_go.js 2013-06-13 12:44:26 +0000 | |||
435 | @@ -107,6 +107,8 @@ | |||
436 | 107 | // In order to avoid rewriting all of these go tests we need to destroy | 107 | // In order to avoid rewriting all of these go tests we need to destroy |
437 | 108 | // the env created in the beforeEach | 108 | // the env created in the beforeEach |
438 | 109 | env.destroy(); | 109 | env.destroy(); |
439 | 110 | // We need to clear any credentials stored in sessionStorage. | ||
440 | 111 | env.setCredentials(null); | ||
441 | 110 | oldHandleLogin = Y.juju.environments.GoEnvironment.handleLogin; | 112 | oldHandleLogin = Y.juju.environments.GoEnvironment.handleLogin; |
442 | 111 | Y.juju.environments.GoEnvironment.handleLogin = function() {}; | 113 | Y.juju.environments.GoEnvironment.handleLogin = function() {}; |
443 | 112 | conn = new utils.SocketStub(); | 114 | conn = new utils.SocketStub(); |
444 | 113 | 115 | ||
445 | === modified file 'test/test_sandbox.js' | |||
446 | --- test/test_sandbox.js 2013-05-17 14:51:05 +0000 | |||
447 | +++ test/test_sandbox.js 2013-06-13 12:44:26 +0000 | |||
448 | @@ -182,10 +182,8 @@ | |||
449 | 182 | var requires = [ | 182 | var requires = [ |
450 | 183 | 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python', | 183 | 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-python', |
451 | 184 | 'juju-models', 'promise']; | 184 | 'juju-models', 'promise']; |
456 | 185 | var Y, sandboxModule, ClientConnection, PyJujuAPI, environmentsModule, | 185 | var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, |
457 | 186 | state, juju, client, env, utils, cleanups; | 186 | client, env, utils, cleanups; |
454 | 187 | |||
455 | 188 | this.timeout(2000); // Long timeouts make async failures hard to detect. | ||
458 | 189 | 187 | ||
459 | 190 | before(function(done) { | 188 | before(function(done) { |
460 | 191 | Y = YUI(GlobalConfig).use(requires, function(Y) { | 189 | Y = YUI(GlobalConfig).use(requires, function(Y) { |
461 | @@ -1430,8 +1428,6 @@ | |||
462 | 1430 | } | 1428 | } |
463 | 1431 | 1429 | ||
464 | 1432 | it('should support export', function(done) { | 1430 | it('should support export', function(done) { |
465 | 1433 | this.timeout(250); | ||
466 | 1434 | |||
467 | 1435 | client.open(); | 1431 | client.open(); |
468 | 1436 | promise(state, 'deploy', 'cs:wordpress') | 1432 | promise(state, 'deploy', 'cs:wordpress') |
469 | 1437 | .then(promise(state, 'deploy', 'cs:mysql')) | 1433 | .then(promise(state, 'deploy', 'cs:mysql')) |
470 | @@ -1447,7 +1443,6 @@ | |||
471 | 1447 | }); | 1443 | }); |
472 | 1448 | 1444 | ||
473 | 1449 | it('should support import', function(done) { | 1445 | it('should support import', function(done) { |
474 | 1450 | this.timeout(300); | ||
475 | 1451 | var fixture = utils.loadFixture('data/sample-fakebackend.json', false); | 1446 | var fixture = utils.loadFixture('data/sample-fakebackend.json', false); |
476 | 1452 | 1447 | ||
477 | 1453 | client.onmessage = function() { | 1448 | client.onmessage = function() { |
478 | @@ -1475,4 +1470,197 @@ | |||
479 | 1475 | 1470 | ||
480 | 1476 | }); | 1471 | }); |
481 | 1477 | 1472 | ||
482 | 1473 | |||
483 | 1474 | describe('sandbox.GoJujuAPI', function() { | ||
484 | 1475 | var requires = [ | ||
485 | 1476 | 'juju-env-sandbox', 'juju-tests-utils', 'juju-env-go', | ||
486 | 1477 | 'juju-models', 'promise']; | ||
487 | 1478 | var Y, sandboxModule, ClientConnection, environmentsModule, state, juju, | ||
488 | 1479 | client, env, utils; | ||
489 | 1480 | |||
490 | 1481 | before(function(done) { | ||
491 | 1482 | Y = YUI(GlobalConfig).use(requires, function(Y) { | ||
492 | 1483 | sandboxModule = Y.namespace('juju.environments.sandbox'); | ||
493 | 1484 | environmentsModule = Y.namespace('juju.environments'); | ||
494 | 1485 | utils = Y.namespace('juju-tests.utils'); | ||
495 | 1486 | done(); | ||
496 | 1487 | }); | ||
497 | 1488 | }); | ||
498 | 1489 | |||
499 | 1490 | beforeEach(function() { | ||
500 | 1491 | state = utils.makeFakeBackendWithCharmStore(); | ||
501 | 1492 | juju = new sandboxModule.GoJujuAPI({state: state}); | ||
502 | 1493 | client = new sandboxModule.ClientConnection({juju: juju}); | ||
503 | 1494 | env = new environmentsModule.GoEnvironment({conn: client}); | ||
504 | 1495 | }); | ||
505 | 1496 | |||
506 | 1497 | afterEach(function() { | ||
507 | 1498 | env.destroy(); | ||
508 | 1499 | client.destroy(); | ||
509 | 1500 | juju.destroy(); | ||
510 | 1501 | state.destroy(); | ||
511 | 1502 | }); | ||
512 | 1503 | |||
513 | 1504 | it('opens successfully.', function() { | ||
514 | 1505 | assert.isFalse(juju.connected); | ||
515 | 1506 | assert.isUndefined(juju.get('client')); | ||
516 | 1507 | client.open(); | ||
517 | 1508 | assert.isTrue(juju.connected); | ||
518 | 1509 | assert.strictEqual(juju.get('client'), client); | ||
519 | 1510 | }); | ||
520 | 1511 | |||
521 | 1512 | it('ignores "open" when already open to same client.', function() { | ||
522 | 1513 | client.receive = function() { | ||
523 | 1514 | assert.ok(false, 'The receive method should not be called.'); | ||
524 | 1515 | }; | ||
525 | 1516 | // Whitebox test: duplicate "open" state. | ||
526 | 1517 | juju.connected = true; | ||
527 | 1518 | juju.set('client', client); | ||
528 | 1519 | // This is effectively a re-open. | ||
529 | 1520 | client.open(); | ||
530 | 1521 | // The assert.ok above is the verification. | ||
531 | 1522 | }); | ||
532 | 1523 | |||
533 | 1524 | it('refuses to open if already open to another client.', function() { | ||
534 | 1525 | // This is a simple way to make sure that we don't leave multiple | ||
535 | 1526 | // setInterval calls running. If for some reason we want more | ||
536 | 1527 | // simultaneous clients, that's fine, though that will require | ||
537 | 1528 | // reworking the delta code generally. | ||
538 | 1529 | juju.connected = true; | ||
539 | 1530 | juju.set('client', {receive: function() { | ||
540 | 1531 | assert.ok(false, 'The receive method should not have been called.'); | ||
541 | 1532 | }}); | ||
542 | 1533 | assert.throws( | ||
543 | 1534 | client.open.bind(client), | ||
544 | 1535 | 'INVALID_STATE_ERR : Connection is open to another client.'); | ||
545 | 1536 | }); | ||
546 | 1537 | |||
547 | 1538 | it('closes successfully.', function() { | ||
548 | 1539 | client.open(); | ||
549 | 1540 | assert.isTrue(juju.connected); | ||
550 | 1541 | assert.notEqual(juju.get('client'), undefined); | ||
551 | 1542 | client.close(); | ||
552 | 1543 | assert.isFalse(juju.connected); | ||
553 | 1544 | assert.isUndefined(juju.get('client')); | ||
554 | 1545 | }); | ||
555 | 1546 | |||
556 | 1547 | it('ignores "close" when already closed.', function() { | ||
557 | 1548 | // This simply shows that we do not raise an error. | ||
558 | 1549 | juju.close(); | ||
559 | 1550 | }); | ||
560 | 1551 | |||
561 | 1552 | it('can dispatch on received information.', function(done) { | ||
562 | 1553 | var data = {Type: 'TheType', Request: 'TheRequest'}; | ||
563 | 1554 | juju.handleTheTypeTheRequest = function(received) { | ||
564 | 1555 | assert.notStrictEqual(received, data); | ||
565 | 1556 | assert.deepEqual(received, data); | ||
566 | 1557 | done(); | ||
567 | 1558 | }; | ||
568 | 1559 | client.open(); | ||
569 | 1560 | client.send(Y.JSON.stringify(data)); | ||
570 | 1561 | }); | ||
571 | 1562 | |||
572 | 1563 | it('refuses to dispatch when closed.', function() { | ||
573 | 1564 | assert.throws( | ||
574 | 1565 | juju.receive.bind(juju, {}), | ||
575 | 1566 | 'INVALID_STATE_ERR : Connection is closed.' | ||
576 | 1567 | ); | ||
577 | 1568 | }); | ||
578 | 1569 | |||
579 | 1570 | it('can log in.', function(done) { | ||
580 | 1571 | // See FakeBackend's authorizedUsers for these default authentication | ||
581 | 1572 | // values. | ||
582 | 1573 | var data = { | ||
583 | 1574 | Type: 'Admin', | ||
584 | 1575 | Request: 'Login', | ||
585 | 1576 | Params: { | ||
586 | 1577 | AuthTag: 'admin', | ||
587 | 1578 | Password: 'password' | ||
588 | 1579 | }, | ||
589 | 1580 | RequestId: 42 | ||
590 | 1581 | }; | ||
591 | 1582 | client.onmessage = function(received) { | ||
592 | 1583 | // Add in the error indicator so the deepEqual is comparing apples to | ||
593 | 1584 | // apples. | ||
594 | 1585 | data.Error = false; | ||
595 | 1586 | assert.deepEqual(Y.JSON.parse(received.data), data); | ||
596 | 1587 | assert.isTrue(state.get('authenticated')); | ||
597 | 1588 | done(); | ||
598 | 1589 | }; | ||
599 | 1590 | state.logout(); | ||
600 | 1591 | assert.isFalse(state.get('authenticated')); | ||
601 | 1592 | client.open(); | ||
602 | 1593 | client.send(Y.JSON.stringify(data)); | ||
603 | 1594 | }); | ||
604 | 1595 | |||
605 | 1596 | it('can log in (environment integration).', function(done) { | ||
606 | 1597 | state.logout(); | ||
607 | 1598 | env.after('login', function() { | ||
608 | 1599 | assert.isTrue(env.userIsAuthenticated); | ||
609 | 1600 | done(); | ||
610 | 1601 | }); | ||
611 | 1602 | env.connect(); | ||
612 | 1603 | env.setCredentials({user: 'admin', password: 'password'}); | ||
613 | 1604 | env.login(); | ||
614 | 1605 | }); | ||
615 | 1606 | |||
616 | 1607 | it('can deploy.', function(done) { | ||
617 | 1608 | // We begin logged in. See utils.makeFakeBackendWithCharmStore. | ||
618 | 1609 | var data = { | ||
619 | 1610 | Type: 'Client', | ||
620 | 1611 | Request: 'ServiceDeploy', | ||
621 | 1612 | Params: { | ||
622 | 1613 | CharmUrl: 'cs:wordpress', | ||
623 | 1614 | ServiceName: 'kumquat', | ||
624 | 1615 | ConfigYAML: 'funny: business', | ||
625 | 1616 | NumUnits: 2 | ||
626 | 1617 | }, | ||
627 | 1618 | RequestId: 42 | ||
628 | 1619 | }; | ||
629 | 1620 | client.onmessage = function(received) { | ||
630 | 1621 | var receivedData = Y.JSON.parse(received.data); | ||
631 | 1622 | assert.equal(receivedData.RequestId, data.RequestId); | ||
632 | 1623 | assert.isUndefined(receivedData.Error); | ||
633 | 1624 | assert.isObject( | ||
634 | 1625 | state.db.charms.getById('cs:precise/wordpress-10')); | ||
635 | 1626 | var service = state.db.services.getById('kumquat'); | ||
636 | 1627 | assert.isObject(service); | ||
637 | 1628 | assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); | ||
638 | 1629 | assert.deepEqual(service.get('config'), {funny: 'business'}); | ||
639 | 1630 | var units = state.db.units.get_units_for_service(service); | ||
640 | 1631 | assert.lengthOf(units, 2); | ||
641 | 1632 | done(); | ||
642 | 1633 | }; | ||
643 | 1634 | client.open(); | ||
644 | 1635 | client.send(Y.JSON.stringify(data)); | ||
645 | 1636 | }); | ||
646 | 1637 | |||
647 | 1638 | it('can deploy (environment integration).', function() { | ||
648 | 1639 | env.connect(); | ||
649 | 1640 | // We begin logged in. See utils.makeFakeBackendWithCharmStore. | ||
650 | 1641 | var callback = function(result) { | ||
651 | 1642 | assert.isUndefined(result.err); | ||
652 | 1643 | assert.equal(result.charm_url, 'cs:wordpress'); | ||
653 | 1644 | var service = state.db.services.getById('kumquat'); | ||
654 | 1645 | assert.equal(service.get('charm'), 'cs:precise/wordpress-10'); | ||
655 | 1646 | assert.deepEqual(service.get('config'), {llama: 'pajama'}); | ||
656 | 1647 | }; | ||
657 | 1648 | env.deploy( | ||
658 | 1649 | 'cs:wordpress', 'kumquat', {llama: 'pajama'}, null, 1, callback); | ||
659 | 1650 | }); | ||
660 | 1651 | |||
661 | 1652 | it('can communicate errors after attempting to deploy', function(done) { | ||
662 | 1653 | env.connect(); | ||
663 | 1654 | state.deploy('cs:wordpress', function() {}); | ||
664 | 1655 | var callback = function(result) { | ||
665 | 1656 | assert.equal( | ||
666 | 1657 | result.err, 'A service with this name already exists.'); | ||
667 | 1658 | done(); | ||
668 | 1659 | }; | ||
669 | 1660 | env.deploy('cs:wordpress', undefined, undefined, undefined, 1, | ||
670 | 1661 | callback); | ||
671 | 1662 | }); | ||
672 | 1663 | |||
673 | 1664 | }); | ||
674 | 1665 | |||
675 | 1478 | })(); | 1666 | })(); |
LGTM with questions
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js env/sandbox. js (right):
File app/store/
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode812 env/sandbox. js:812: client. receive( {Response: {AllWatcherId:
app/store/
'42'}});
Heh. :-)
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode839 env/sandbox. js:839: var response = {RequestId:
app/store/
data.RequestId};
I guess we make a new response because Juju Core does not send back the
whole original request the way PyJuju does, right? If so, cool...though
I wonder if we should therefore remove the copying code in line 766
("Y.merge(data)" and the preceding comment).
https:/ /codereview. appspot. com/10235045/ diff/1/ app/store/ env/sandbox. js#newcode879 env/sandbox. js:879: // We do not provide the optional Config
app/store/
or Constraints in the response.
How do you get it then? Or do you mean that this will be in a future
branch? (If so, let's use TODO as you did elsewhere)
https:/ /codereview. appspot. com/10235045/