Merge lp:~rharding/juju-gui/bundle-progress into lp:juju-gui/experimental
- bundle-progress
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1201 |
Proposed branch: | lp:~rharding/juju-gui/bundle-progress |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
1033 lines (+677/-129) 12 files modified
app/app.js (+27/-24) app/assets/javascripts/bundle-import-helpers.js (+202/-30) app/modules-debug.js (+2/-2) app/store/env/go.js (+91/-0) app/views/topology/service.js (+13/-13) app/views/utils.js (+0/-18) bin/merge-files (+1/-1) test/index.html (+1/-0) test/test_bundle_import_helpers.js (+303/-0) test/test_env_go.js (+30/-0) test/test_service_module.js (+7/-2) test/test_utils.js (+0/-39) |
To merge this branch: | bzr merge lp:~rharding/juju-gui/bundle-progress |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+195084@code.launchpad.net |
Commit message
Description of the change
Add support for watching progress of bundle import
- Change the bundle-
mixed in. It didn't need any state info.
- Move everyone to going through the single deployBundle function in the
helpers vs their own calls to the env.deployerImport
- Provide the ability to start a watch on a deployment.
- Add the methods to update with new notifications on each change to the watch
status of the import.
- Start a new test file for the bundle-
- Remove the dupe idea of adding import notifications from the view utils,
it's part of the helpers.
QA
----
There are four QA methods that need testing as there are four ways to deploy
a bundle in the code. In order to QA you need to bring up a live environment.
I suggest that you use `juju quickstart` without any parameters to bring up
the environment.
Once it's up, you need to change the source to this branch with:
juju set juju-gui juju-gui-source='lp:~rharding/juju-gui/bundle-progress'
Now this takes a while. I found the best way to monitor it was to
juju ssh juju-gui/0
watch 'ps aux | grep config'
You then need to wait for the config-changed hook to finish running.
Once the config change hook is done, you can start to QA the four methods of
deploying a bundle.
1. Search for 'jorge' in the browser and drag-n-drop the discource bundle onto
the canvas.
2. Search for 'jorge' and click on the discourse bundle to view the details.
Then click on the deploy button and press "Yes I'm sure".
3. Use the import icon to import a local bundle file.
4. Drag-n-Drop a bundle file onto the canvas.
In each test case, what you're watching for are notifications that the bundle
has been requested, started, and completed. If the bundle is large the final
step might take a while.
Let me know if you've got any questions or issues qa'ing.
Richard Harding (rharding) wrote : | # |
- 1203. By Richard Harding
-
Add some debugging info
- 1204. By Richard Harding
-
Bind to the right callbacks dummy
- 1205. By Richard Harding
-
Update the deployBundle helper passed to subapp/views
- 1206. By Richard Harding
-
doh
- 1207. By Richard Harding
-
Update the deploy helper from the app.js
Richard Harding (rharding) wrote : | # |
Please take a look.
- 1208. By Richard Harding
-
Remove alerts used for debuggin
Richard Harding (rharding) wrote : | # |
Please take a look.
- 1209. By Richard Harding
-
Add error case for the watcher change
Richard Harding (rharding) wrote : | # |
Please take a look.
- 1210. By Richard Harding
-
Add a test for the failure
Richard Harding (rharding) wrote : | # |
Please take a look.
- 1211. By Richard Harding
-
Move the watch to the right section
Richard Harding (rharding) wrote : | # |
Please take a look.
Jeff Pihach (hatch) wrote : | # |
This is looking really good I have some small comments mostly related to
naming but I think that we need some integration tests for the go
sandbox.
Do we have a story for the fakebackend for this functionality or if it's
simply supposed to go unanswered?
I'm curious as to why we can't initiate a watch rpc call and then have
the guiserver send fake deltas down for us to react to, this would get
rid of calling 'next' and essentially long-polling for updates. I'm sure
there is a good reason for it I just don't know what it is :)
https:/
File app/app.js (right):
https:/
app/app.js:43: importHelpers = juju.BundleImport;
I'd probably change this to be something like `bundleHelpers`
https:/
app/app.js:583: env,
this.env and this.db are available here because the callback is scoped
to app.js so you don't need the references above
https:/
app/app.js:598: cfg.deployBundle = function(bundle) {
if you use .bind() for this function you do not need to maintain the
reference above for env, db
https:/
File app/assets/
https:/
app/assets/
ns.BundleImport
When I see utility methods that are directly tied together this it makes
me think this is a class trying really hard to be a utility object.
Everything in this object is directly related to a single instance but
instead of relying on that instance everything needs to be passed
around. I don't see the advantage to this structure vs relying on the
primary instance.
For testing you could unit test the class simply by creating an instance
of it with stubbed env/db like you do now. If you didn't want to create
a new class this would be handled by converting it to an extension and
then testing would be the same technique as prev mentioned.
https:/
app/assets/
ns.BundleImport
I know this is repurposed but it feels like we are duplicating work here
just to supply different notification messages. I'd try to move these to
be handled in the deployBundle method, or offload both notifications to
a separate showNotificatio
https:/
app/assets/
_processWatchDe
this is really a watchDeployment
https:/
File app/store/env/go.js (right):
https:/
Richard Harding (rharding) wrote : | # |
On 2013/11/14 21:30:52, jeff.pihach wrote:
> This is looking really good I have some small comments mostly related
to naming
> but I think that we need some integration tests for the go sandbox.
Thanks, I should have brought this up. The next card is to figure out
how to implement this in the sandbox. This branch is purely about making
it work for users in a live environment.
> Do we have a story for the fakebackend for this functionality or if
it's simply
> supposed to go unanswered?
That is for the follow up card. My first instinct is to not show all of
these notifications to the sandbox user, but only the final "Competed"
one.
> I'm curious as to why we can't initiate a watch rpc call and then have
the
> guiserver send fake deltas down for us to react to, this would get rid
of
> calling 'next' and essentially long-polling for updates. I'm sure
there is a
> good reason for it I just don't know what it is :)
I'll ask around on this one. Not sure about the delta stuff myself.
> https:/
> File app/app.js (right):
https:/
> app/app.js:43: importHelpers = juju.BundleImport;
> I'd probably change this to be something like `bundleHelpers`
Sure thing, thanks.
https:/
> app/app.js:583: env,
> this.env and this.db are available here because the callback is scoped
to app.js
> so you don't need the references above
The trouble is that these callbacks get bound in the go env wrappings
and I preferred to be explicit. I'll test it out and make sure, but then
add a comment/note.
https:/
> app/app.js:598: cfg.deployBundle = function(bundle) {
> if you use .bind() for this function you do not need to maintain the
reference
> above for env, db
https:/
> File app/assets/
https:/
> app/assets/
> ns.BundleImport
> When I see utility methods that are directly tied together this it
makes me
> think this is a class trying really hard to be a utility object.
Everything in
> this object is directly related to a single instance but instead of
relying on
> that instance everything needs to be passed around. I don't see the
advantage to
> this structure vs relying on the primary instance.
> For testing you could unit test the class simply by creating an
instance of it
> with stubbed env/db like you do now. If you didn't want to create a
new class
> this would be handled by converting it to an extension and then
testing would be
> the same technique as prev mentioned.
I thought about a class but then I stopped since this makes testing
really easy. Passing in arguments ftw which allowed me to write a
specific test case for the update method without dealing with anything
else. They al...
- 1212. By Richard Harding
-
Sync from laptop
Richard Harding (rharding) wrote : | # |
Please take a look.
Jeff Pihach (hatch) wrote : | # |
LGTM no qa yet
- 1213. By Richard Harding
-
Add debug info
- 1214. By Richard Harding
-
Fix debugging code
- 1215. By Richard Harding
-
Make the error check more explicit
Jeff Pihach (hatch) wrote : | # |
QA OK Let-r-rip!
Madison Scott-Clary (makyo) wrote : | # |
LGTM! Thanks!
Richard Harding (rharding) wrote : | # |
*** Submitted:
Add support for watching progress of bundle import
- Change the bundle-
to be
mixed in. It didn't need any state info.
- Move everyone to going through the single deployBundle function in the
helpers vs their own calls to the env.deployerImport
- Provide the ability to start a watch on a deployment.
- Add the methods to update with new notifications on each change to the
watch
status of the import.
- Start a new test file for the bundle-
- Remove the dupe idea of adding import notifications from the view
utils,
it's part of the helpers.
QA
----
There are four QA methods that need testing as there are four ways to
deploy
a bundle in the code. In order to QA you need to bring up a live
environment.
I suggest that you use `juju quickstart` without any parameters to bring
up
the environment.
Once it's up, you need to change the source to this branch with:
juju set juju-gui
juju-gui-source='lp:~rharding/juju-gui/bundle-progress'
Now this takes a while. I found the best way to monitor it was to
juju ssh juju-gui/0
watch 'ps aux | grep config'
You then need to wait for the config-changed hook to finish running.
Once the config change hook is done, you can start to QA the four
methods of
deploying a bundle.
1. Search for 'jorge' in the browser and drag-n-drop the discource
bundle onto
the canvas.
2. Search for 'jorge' and click on the discourse bundle to view the
details.
Then click on the deploy button and press "Yes I'm sure".
3. Use the import icon to import a local bundle file.
4. Drag-n-Drop a bundle file onto the canvas.
In each test case, what you're watching for are notifications that the
bundle
has been requested, started, and completed. If the bundle is large the
final
step might take a while.
Let me know if you've got any questions or issues qa'ing.
R=jeff.pihach, matthew.scott
CC=
https:/
Preview Diff
1 | === modified file 'app/app.js' |
2 | --- app/app.js 2013-11-12 21:05:13 +0000 |
3 | +++ app/app.js 2013-11-15 19:04:35 +0000 |
4 | @@ -39,8 +39,8 @@ |
5 | var juju = Y.namespace('juju'), |
6 | models = Y.namespace('juju.models'), |
7 | views = Y.namespace('juju.views'), |
8 | - utils = views.utils, |
9 | - widgets = Y.namespace('juju.widgets'); |
10 | + widgets = Y.namespace('juju.widgets'), |
11 | + importHelpers = juju.BundleHelpers; |
12 | |
13 | /** |
14 | * The main app class. |
15 | @@ -51,8 +51,7 @@ |
16 | Y.juju.SubAppRegistration, |
17 | Y.juju.NSRouter, |
18 | Y.juju.Cookies, |
19 | - Y.juju.GhostDeployer, |
20 | - Y.juju.BundleImport], { |
21 | + Y.juju.GhostDeployer], { |
22 | |
23 | /* |
24 | Extension properties |
25 | @@ -566,6 +565,7 @@ |
26 | |
27 | var importNode = Y.one('#import-trigger'); |
28 | var importFileInput = Y.one('.import-export input[type=file]'); |
29 | + |
30 | // Tests won't have this node. |
31 | if (importNode && importFileInput) { |
32 | importNode.on('click', function(e) { |
33 | @@ -575,8 +575,11 @@ |
34 | }); |
35 | |
36 | importFileInput.on('change', function(e) { |
37 | - this.sendToDeployer(this.env, this.db, |
38 | - e.currentTarget.get('files')._nodes); |
39 | + importHelpers.deployBundleFiles( |
40 | + e.currentTarget.get('files')._nodes, |
41 | + this.env, |
42 | + this.db |
43 | + ); |
44 | }, this); |
45 | } |
46 | |
47 | @@ -587,7 +590,22 @@ |
48 | // from the Y.juju.GhostDeployer extension |
49 | cfg.deployService = Y.bind(this.deployService, this); |
50 | |
51 | - cfg.deployBundle = this.deployBundle.bind(this); |
52 | + // Provide the bundle deployment helper to the subapps and views to |
53 | + // access in case of an UX interaction that triggers a bundle deploy. |
54 | + // Grab a reference of these for the nested event calls below. |
55 | + var env = this.env; |
56 | + var db = this.db; |
57 | + cfg.deployBundle = function(bundle) { |
58 | + // The other views will hand us an Object vs a YAML string. The import |
59 | + // helpers want the yaml string instead. |
60 | + importHelpers.deployBundle( |
61 | + Y.JSON.stringify({ |
62 | + bundle: bundle |
63 | + }), |
64 | + env, |
65 | + db |
66 | + ); |
67 | + }; |
68 | |
69 | // Watch specific things, (add units), remove db.update above |
70 | // Note: This hides under the flag as tests don't properly clean |
71 | @@ -630,21 +648,6 @@ |
72 | }, |
73 | |
74 | /** |
75 | - Calls the deployer import method with the bundle data |
76 | - to deploy the bundle to the environment. |
77 | - |
78 | - @method deployBundle |
79 | - @param {Object} bundle Bundle data. |
80 | - */ |
81 | - deployBundle: function(bundle) { |
82 | - var notifications = this.db.notifications; |
83 | - this.env.deployerImport( |
84 | - Y.JSON.stringify({ |
85 | - bundle: bundle |
86 | - }), null, Y.bind(utils.deployBundleCallback, null, notifications)); |
87 | - }, |
88 | - |
89 | - /** |
90 | Export the YAML for this environment. |
91 | |
92 | @method exportYAML |
93 | @@ -1349,6 +1352,7 @@ |
94 | 'app-base', |
95 | 'app-transitions', |
96 | 'base', |
97 | + 'bundle-import-helpers', |
98 | 'node', |
99 | 'model', |
100 | 'app-cookies-extension', |
101 | @@ -1364,7 +1368,6 @@ |
102 | 'juju-ghost-inspector', |
103 | 'juju-view-bundle', |
104 | 'viewmode-controls', |
105 | - 'help-dropdown', |
106 | - 'bundle-import-extension' |
107 | + 'help-dropdown' |
108 | ] |
109 | }); |
110 | |
111 | === renamed file 'app/assets/javascripts/bundle-import-extension.js' => 'app/assets/javascripts/bundle-import-helpers.js' |
112 | --- app/assets/javascripts/bundle-import-extension.js 2013-11-07 15:52:10 +0000 |
113 | +++ app/assets/javascripts/bundle-import-helpers.js 2013-11-15 19:04:35 +0000 |
114 | @@ -18,13 +18,71 @@ |
115 | |
116 | 'use strict'; |
117 | |
118 | -YUI.add('bundle-import-extension', function(Y) { |
119 | - |
120 | - function BundleImport() {} |
121 | - |
122 | - BundleImport.prototype = { |
123 | - |
124 | - sendToDeployer: function(env, db, fileSources) { |
125 | +YUI.add('bundle-import-helpers', function(Y) { |
126 | + var ns = Y.namespace('juju'); |
127 | + |
128 | + ns.BundleHelpers = { |
129 | + /** |
130 | + Calls the deployer import method with the bundle data |
131 | + to deploy the bundle to the environment. |
132 | + |
133 | + @method deployBundle |
134 | + @param {Object} bundle Bundle data. |
135 | + @param {Environment} env Environment with access to the bundle back end. |
136 | + calls. |
137 | + @param {Database} db The app Database with access to the NotificationList. |
138 | + */ |
139 | + deployBundle: function(bundle, env, db, customCallback) { |
140 | + var notifications = db.notifications; |
141 | + |
142 | + var defaultCallback = function(result) { |
143 | + if (result.err) { |
144 | + console.log('bundle import failed:', result.err); |
145 | + notifications.add({ |
146 | + title: 'Bundle deployment failed', |
147 | + message: 'Unable to deploy the bundle. The server returned the ' + |
148 | + 'following error: ' + result.err, |
149 | + level: 'error' |
150 | + }); |
151 | + } else { |
152 | + notifications.add({ |
153 | + title: 'Bundle deployment requested', |
154 | + message: 'Bundle deployment request successful. The full ' + |
155 | + 'deployment can take some time to complete', |
156 | + level: 'important' |
157 | + }); |
158 | + |
159 | + ns.BundleHelpers._watchDeployment(result.DeploymentId, env, db); |
160 | + } |
161 | + }; |
162 | + |
163 | + if (Y.Lang.isFunction(env.deployerImport)) { |
164 | + env.deployerImport( |
165 | + bundle, |
166 | + null, |
167 | + customCallback ? customCallback : defaultCallback |
168 | + ); |
169 | + } else { |
170 | + notifications.add({ |
171 | + title: 'Bundle deployment not available.', |
172 | + message: 'Bundle deployments are not currently available for your' + |
173 | + 'environment.', |
174 | + level: 'error' |
175 | + }); |
176 | + } |
177 | + }, |
178 | + |
179 | + /** |
180 | + Deploy one or more files, generally from the import file picker |
181 | + utility. |
182 | + |
183 | + @method deployBundleFiles |
184 | + @param {Array} fileSources The list of files from the import control. |
185 | + @param {Environment} env Environment with access to the bundle back end. |
186 | + calls. |
187 | + @param {Database} db The app Database with access to the NotificationList. |
188 | + */ |
189 | + deployBundleFiles: function(fileSources, env, db) { |
190 | var notifications = db.notifications; |
191 | if (!Y.Lang.isFunction(env.deployerImport)) { |
192 | // Notify the user that their environment is too old and return. |
193 | @@ -37,37 +95,151 @@ |
194 | }); |
195 | return; |
196 | } |
197 | + |
198 | // Handle dropping Deployer files on the canvas. |
199 | Y.Array.each(fileSources, function(file) { |
200 | var reader = new FileReader(); |
201 | + |
202 | + // Once the file has been loaded, deploy it. |
203 | reader.onload = function(e) { |
204 | - // Import each into the environment |
205 | - env.deployerImport(e.target.result, null, function(result) { |
206 | - if (!result.err) { |
207 | - notifications.add({ |
208 | - title: 'Imported Deployer file', |
209 | - message: 'Import from "' + file.name + '" successful. This ' + |
210 | - 'can take some time to complete.', |
211 | - level: 'important' |
212 | - }); |
213 | - } else { |
214 | - console.warn('import failed', file, result); |
215 | - notifications.add({ |
216 | - title: 'Import Environment Failed', |
217 | - message: 'Import from "' + file.name + |
218 | - '" failed.<br/>' + result.err, |
219 | - level: 'error' |
220 | - }); |
221 | - } |
222 | - }); |
223 | + ns.BundleHelpers.deployBundle( |
224 | + e.target.result, |
225 | + env, |
226 | + db, |
227 | + function(result) { |
228 | + if (!result.err) { |
229 | + |
230 | + notifications.add({ |
231 | + title: 'Imported Deployer file', |
232 | + message: 'Import from "' + file.name + |
233 | + '" successful. This ' + |
234 | + 'can take some time to complete.', |
235 | + level: 'important' |
236 | + }); |
237 | + |
238 | + ns.BundleHelpers._watchDeployment( |
239 | + result.DeploymentId, env, db); |
240 | + |
241 | + } else { |
242 | + console.warn('import failed', file, result); |
243 | + notifications.add({ |
244 | + title: 'Import Environment Failed', |
245 | + message: 'Import from "' + file.name + |
246 | + '" failed.<br/>' + result.err, |
247 | + level: 'error' |
248 | + }); |
249 | + } |
250 | + } |
251 | + ); |
252 | }; |
253 | + |
254 | + // Start the loading process. |
255 | reader.readAsText(file); |
256 | }); |
257 | + }, |
258 | + |
259 | + /** |
260 | + Watch a deployment for changes and notify the user of updates. |
261 | + |
262 | + @method watchDeployment |
263 | + @param {Integer} deploymentId The ID returned from the deployment call. |
264 | + @param {Environment} env The environment so that we can call the back |
265 | + end. |
266 | + @param {Database} db The db which contains the NotificationList used |
267 | + for adding notifications to the system. |
268 | + */ |
269 | + _watchDeployment: function(deploymentId, env, db) { |
270 | + var notifications = db.notifications; |
271 | + |
272 | + // First generate a watch. |
273 | + env.deployerWatch(deploymentId, function(data) { |
274 | + if (data.err) { |
275 | + notifications.add({ |
276 | + title: 'Unable to watch status of import.', |
277 | + message: 'Attempting to watch the deployment failed: ' + |
278 | + data.err, |
279 | + level: 'error' |
280 | + }); |
281 | + } else { |
282 | + ns.BundleHelpers._watchDeploymentUpdates( |
283 | + data.WatchId, env, db); |
284 | + } |
285 | + }); |
286 | + }, |
287 | + |
288 | + /** |
289 | + Once we've got a Watch, we need to continue to call Next on it to |
290 | + receive updates. Watch the deployment until it's either completed or |
291 | + we've gotten an error. |
292 | + |
293 | + This will make nested calls upon each update from the server and could |
294 | + be a potential point for recursive callback issues. It appears that we |
295 | + should only get a couple of levels deep in the notification stack and |
296 | + will be safe. |
297 | + |
298 | + @method watchDeployment |
299 | + @param {Integer} watchId The ID of the Watcher from the watchDeployment |
300 | + call. |
301 | + @param {Environment} env The environment so that we can call the |
302 | + back end. |
303 | + @param {Database} db The db which contains the NotificationList used |
304 | + for adding notifications to the system. |
305 | + */ |
306 | + _watchDeploymentUpdates: function(watchId, env, db) { |
307 | + var notifications = db.notifications; |
308 | + |
309 | + var processUpdate = function(data) { |
310 | + if (data.err) { |
311 | + notifications.add({ |
312 | + title: 'Error watching deployment', |
313 | + message: 'The watch of the deployment errored:' + |
314 | + data.err, |
315 | + level: 'error' |
316 | + }); |
317 | + |
318 | + // Break the loop of calling for the next update from the server. |
319 | + return; |
320 | + |
321 | + } else { |
322 | + // Just grab the latest change and notify the user of the |
323 | + // status. |
324 | + var newChange = data.Changes[0]; |
325 | + // The change could be the result of an error. |
326 | + if (newChange.Error !== undefined) { |
327 | + notifications.add({ |
328 | + title: 'Updated status for deployment: ' + |
329 | + newChange.DeploymentId, |
330 | + message: 'The deployment errored: ' + |
331 | + newChange.Error, |
332 | + level: 'error' |
333 | + }); |
334 | + } else { |
335 | + notifications.add({ |
336 | + title: 'Updated status for deployment: ' + |
337 | + newChange.DeploymentId, |
338 | + message: 'The deployment is currently: ' + |
339 | + newChange.Status, |
340 | + level: 'important' |
341 | + }); |
342 | + } |
343 | + |
344 | + // If the status is 'completed' then we're done watching this. |
345 | + if (newChange.Status === 'completed') { |
346 | + // There's nothing else to see here. |
347 | + return; |
348 | + } else { |
349 | + env.deployerWatchUpdate(watchId, processUpdate); |
350 | + } |
351 | + } |
352 | + }; |
353 | + |
354 | + // Make the first call to the env and the processUpdate callback will |
355 | + // handle re-calling on each update. |
356 | + env.deployerWatchUpdate(watchId, processUpdate); |
357 | } |
358 | + |
359 | }; |
360 | |
361 | - Y.namespace('juju').BundleImport = BundleImport; |
362 | - |
363 | - |
364 | +}, '0.1.0', { |
365 | + requires: [] |
366 | }); |
367 | - |
368 | |
369 | === modified file 'app/modules-debug.js' |
370 | --- app/modules-debug.js 2013-11-12 15:53:05 +0000 |
371 | +++ app/modules-debug.js 2013-11-15 19:04:35 +0000 |
372 | @@ -181,8 +181,8 @@ |
373 | fullpath: '/juju-ui/assets/javascripts/app-cookies-extension.js' |
374 | }, |
375 | |
376 | - 'bundle-import-extension': { |
377 | - fullpath: '/juju-ui/assets/javascripts/bundle-import-extension.js' |
378 | + 'bundle-import-helpers': { |
379 | + fullpath: '/juju-ui/assets/javascripts/bundle-import-helpers.js' |
380 | }, |
381 | |
382 | 'view-dropdown-extension': { |
383 | |
384 | === modified file 'app/store/env/go.js' |
385 | --- app/store/env/go.js 2013-10-14 19:38:47 +0000 |
386 | +++ app/store/env/go.js 2013-11-15 19:04:35 +0000 |
387 | @@ -496,6 +496,97 @@ |
388 | userCallback(transformedData); |
389 | }, |
390 | |
391 | + /** |
392 | + Register a Watch with the deployment specified. |
393 | + |
394 | + The callback will receive an {Object} An object with err, and the |
395 | + generated WatchId. . |
396 | + |
397 | + @method deployerWatch |
398 | + @param {Integer} deploymentId The ID of the deployment from the |
399 | + original deployment call. |
400 | + @param {Function} callback A user callback to return the response data |
401 | + to. |
402 | + */ |
403 | + deployerWatch: function(deploymentId, callback) { |
404 | + var intermediateCallback; |
405 | + if (callback) { |
406 | + intermediateCallback = Y.bind(this.handleDeployerWatch, |
407 | + this, callback); |
408 | + } |
409 | + this._send_rpc({ |
410 | + Type: 'Deployer', |
411 | + Request: 'Watch', |
412 | + Params: { |
413 | + DeploymentId: deploymentId |
414 | + } |
415 | + }, intermediateCallback); |
416 | + |
417 | + }, |
418 | + |
419 | + /** |
420 | + Callback to process the environments response to requested a watcher for |
421 | + the deployment specified above. |
422 | + |
423 | + @method handleDeployerWatch |
424 | + @param {Function} userCallback The original callback to the |
425 | + deployerWatch function. |
426 | + @param {Object} data The servers response to the deployerWatch call. |
427 | + */ |
428 | + handleDeployerWatch: function(userCallback, data) { |
429 | + var transformedData = { |
430 | + err: data.Error, |
431 | + WatchId: data.Response.WatcherId |
432 | + }; |
433 | + userCallback(transformedData); |
434 | + }, |
435 | + |
436 | + /** |
437 | + Wait for an update to a deployer watch created earlier. |
438 | + |
439 | + Note: This returns once the server has something to update on. It might |
440 | + wait a while. |
441 | + |
442 | + The callback will receive an {Object} An object with err, and the |
443 | + list of Changes. |
444 | + |
445 | + @method deployerWatchUpdate |
446 | + @param {Integer} watchId The ID of the watcher created in depployWatch. |
447 | + @param {Function} callback The caller's callback function to process |
448 | + the response. |
449 | + */ |
450 | + deployerWatchUpdate: function(watchId, callback) { |
451 | + var intermediateCallback; |
452 | + if (callback) { |
453 | + intermediateCallback = Y.bind(this.handleDeployerWatchUpdate, |
454 | + this, callback); |
455 | + } |
456 | + |
457 | + this._send_rpc({ |
458 | + Type: 'Deployer', |
459 | + Request: 'Next', |
460 | + Params: { |
461 | + WatcherId: watchId |
462 | + } |
463 | + }, intermediateCallback); |
464 | + }, |
465 | + |
466 | + /** |
467 | + Wrapper for the deployerWatchUpdate call. |
468 | + |
469 | + @method handleDeployerWatchUpdate |
470 | + @param {Function} userCallback The original callback to the |
471 | + deployerWatchUpdate function. |
472 | + @param {Object} data The servers response to the deployerWatchUpdate |
473 | + call. |
474 | + */ |
475 | + handleDeployerWatchUpdate: function(userCallback, data) { |
476 | + var transformedData = { |
477 | + err: data.Error, |
478 | + Changes: data.Response.Changes |
479 | + }; |
480 | + userCallback(transformedData); |
481 | + }, |
482 | |
483 | /** |
484 | Deploy a charm. |
485 | |
486 | === modified file 'app/views/topology/service.js' |
487 | --- app/views/topology/service.js 2013-11-11 18:00:20 +0000 |
488 | +++ app/views/topology/service.js 2013-11-15 19:04:35 +0000 |
489 | @@ -26,12 +26,12 @@ |
490 | */ |
491 | |
492 | YUI.add('juju-topology-service', function(Y) { |
493 | - var views = Y.namespace('juju.views'), |
494 | + var d3ns = Y.namespace('d3'), |
495 | + importHelpers = Y.namespace('juju').BundleHelpers, |
496 | models = Y.namespace('juju.models'), |
497 | + topoUtils = Y.namespace('juju.topology.utils'), |
498 | utils = Y.namespace('juju.views.utils'), |
499 | - topoUtils = Y.namespace('juju.topology.utils'), |
500 | - d3ns = Y.namespace('d3'), |
501 | - Templates = views.Templates; |
502 | + views = Y.namespace('juju.views'); |
503 | |
504 | var ServiceModuleCommon = function() {}; |
505 | /** |
506 | @@ -285,8 +285,8 @@ |
507 | @class ServiceModule |
508 | */ |
509 | var ServiceModule = Y.Base.create('ServiceModule', d3ns.Module, [ |
510 | - ServiceModuleCommon, |
511 | - Y.juju.BundleImport], { |
512 | + ServiceModuleCommon |
513 | + ], { |
514 | events: { |
515 | scene: { |
516 | '.service': { |
517 | @@ -639,10 +639,8 @@ |
518 | var fileSources = dataTransfer.files; |
519 | var env = topo.get('env'); |
520 | var db = topo.get('db'); |
521 | - var notifications = db.notifications; |
522 | if (fileSources && fileSources.length) { |
523 | - // provided by bundle-import-extension.js |
524 | - this.sendToDeployer(env, db, fileSources); |
525 | + importHelpers.sendToDeployer(fileSources, env, db); |
526 | } else { |
527 | // Handle dropping charm/bundle tokens from the left side bar. |
528 | var dragData = JSON.parse(dataTransfer.getData('Text')); |
529 | @@ -673,11 +671,13 @@ |
530 | // data, so we wrap the entity data in a mapping. The deployer |
531 | // format is YAML, but JSON is a subset of YAML, so we can just |
532 | // encode it this way. |
533 | - env.deployerImport( |
534 | + importHelpers.deployBundle( |
535 | Y.JSON.stringify({ |
536 | bundle: entityData.data |
537 | - }), null, |
538 | - Y.bind(utils.deployBundleCallback, null, notifications)); |
539 | + }), |
540 | + env, |
541 | + db |
542 | + ); |
543 | } |
544 | } |
545 | } |
546 | @@ -1299,6 +1299,6 @@ |
547 | 'juju-models', |
548 | 'juju-env', |
549 | 'unscaled-pack-layout', |
550 | - 'bundle-import-extension' |
551 | + 'bundle-import-helpers' |
552 | ] |
553 | }); |
554 | |
555 | === modified file 'app/views/utils.js' |
556 | --- app/views/utils.js 2013-11-06 14:44:35 +0000 |
557 | +++ app/views/utils.js 2013-11-15 19:04:35 +0000 |
558 | @@ -1789,24 +1789,6 @@ |
559 | return charmIcons; |
560 | }; |
561 | |
562 | - utils.deployBundleCallback = function(notifications, result) { |
563 | - if (result.err) { |
564 | - console.log('bundle import failed:', result.err); |
565 | - notifications.add({ |
566 | - title: 'Bundle Deployment Failed', |
567 | - message: 'Unable to deploy the bundle. The server returned the ' + |
568 | - 'following error: ' + result.err, |
569 | - level: 'error' |
570 | - }); |
571 | - return; |
572 | - } |
573 | - notifications.add({ |
574 | - title: 'Bundle Deployment Requested', |
575 | - message: 'Bundle deployment request successful. The full ' + |
576 | - 'deployment can take some time to complete', |
577 | - level: 'important' |
578 | - }); |
579 | - }; |
580 | |
581 | }, '0.1.0', { |
582 | requires: [ |
583 | |
584 | === modified file 'bin/merge-files' |
585 | --- bin/merge-files 2013-11-12 15:53:05 +0000 |
586 | +++ bin/merge-files 2013-11-15 19:04:35 +0000 |
587 | @@ -88,7 +88,7 @@ |
588 | filesToLoad.js.push.apply(filesToLoad.js, [ |
589 | 'app/assets/javascripts/app-subapp-extension.js', |
590 | 'app/assets/javascripts/app-cookies-extension.js', |
591 | - 'app/assets/javascripts/bundle-import-extension.js', |
592 | + 'app/assets/javascripts/bundle-import-helpers.js', |
593 | 'app/assets/javascripts/view-dropdown-extension.js', |
594 | 'app/assets/javascripts/d3-components.js', |
595 | 'app/assets/javascripts/d3.min.js', |
596 | |
597 | === modified file 'test/index.html' |
598 | --- test/index.html 2013-11-12 20:28:43 +0000 |
599 | +++ test/index.html 2013-11-15 19:04:35 +0000 |
600 | @@ -58,6 +58,7 @@ |
601 | <script src="test_browser_search_widget.js"></script> |
602 | <script src="test_bundle_details_view.js"></script> |
603 | <script src="test_bundle_module.js"></script> |
604 | + <script src="test_bundle_import_helpers.js"></script> |
605 | <script src="test_charmworld.js"></script> |
606 | <script src="test_token_container.js"></script> |
607 | <script src="test_token.js"></script> |
608 | |
609 | === added file 'test/test_bundle_import_helpers.js' |
610 | --- test/test_bundle_import_helpers.js 1970-01-01 00:00:00 +0000 |
611 | +++ test/test_bundle_import_helpers.js 2013-11-15 19:04:35 +0000 |
612 | @@ -0,0 +1,303 @@ |
613 | +/* |
614 | +This file is part of the Juju GUI, which lets users view and manage Juju |
615 | +environments within a graphical interface (https://launchpad.net/juju-gui). |
616 | +Copyright (C) 2012-2013 Canonical Ltd. |
617 | + |
618 | +This program is free software: you can redistribute it and/or modify it under |
619 | +the terms of the GNU Affero General Public License version 3, as published by |
620 | +the Free Software Foundation. |
621 | + |
622 | +This program is distributed in the hope that it will be useful, but WITHOUT |
623 | +ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
624 | +SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero |
625 | +General Public License for more details. |
626 | + |
627 | +You should have received a copy of the GNU Affero General Public License along |
628 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
629 | +*/ |
630 | + |
631 | +'use strict'; |
632 | + |
633 | +(function() { |
634 | + |
635 | + describe('bundle-import-helpers', function() { |
636 | + var db, env, ns, Y; |
637 | + |
638 | + before(function(done) { |
639 | + Y = YUI(GlobalConfig).use('bundle-import-helpers', function(Y) { |
640 | + ns = Y.namespace('juju'); |
641 | + done(); |
642 | + }); |
643 | + }); |
644 | + |
645 | + beforeEach(function() { |
646 | + db = { |
647 | + notifications: { |
648 | + add: function(info) { |
649 | + |
650 | + } |
651 | + } |
652 | + }; |
653 | + |
654 | + env = {}; |
655 | + }); |
656 | + |
657 | + afterEach(function() { |
658 | + db = undefined; |
659 | + env = undefined; |
660 | + }); |
661 | + |
662 | + it('errors when the env does not support deployer', function(done) { |
663 | + db.notifications.add = function(info) { |
664 | + assert.equal(info.level, 'error'); |
665 | + assert.notEqual(info.title.indexOf('not available'), -1); |
666 | + done(); |
667 | + }; |
668 | + |
669 | + // Start the process by deploying the bundle. |
670 | + ns.BundleHelpers.deployBundle('test bundle', env, db); |
671 | + }); |
672 | + |
673 | + it('errors when the bundle import fails from the env', function(done) { |
674 | + db.notifications.add = function(info) { |
675 | + assert.equal(info.level, 'error'); |
676 | + assert.notEqual(info.title.indexOf('deployment failed'), -1); |
677 | + done(); |
678 | + }; |
679 | + |
680 | + env.deployerImport = function(bundle, name, callback) { |
681 | + callback({ |
682 | + err: 'Abort abort!' |
683 | + }); |
684 | + }; |
685 | + |
686 | + // Start the process by deploying the bundle. |
687 | + ns.BundleHelpers.deployBundle('test bundle', env, db); |
688 | + }); |
689 | + |
690 | + it('provides deployBundle helper for working through env', function(done) { |
691 | + // Watch the notification for the message that we're hitting the default |
692 | + // callback. |
693 | + var hitNotifications = false; |
694 | + db.notifications.add = function(info) { |
695 | + hitNotifications = true; |
696 | + assert.equal(info.level, 'important'); |
697 | + assert.notEqual(info.title.indexOf('requested'), -1); |
698 | + }; |
699 | + |
700 | + // Stub out the env call to make sure we check the params and call the |
701 | + // provided callback. |
702 | + env.deployerImport = function(bundle, name, callback) { |
703 | + assert.equal(bundle, 'test bundle'); |
704 | + assert.equal( |
705 | + name, null, 'The name is not currently supported or passed.'); |
706 | + // This is the default callback from the deployBundle method. |
707 | + callback({ |
708 | + err: undefined, |
709 | + DeploymentId: 10 |
710 | + }); |
711 | + }; |
712 | + |
713 | + // A watch should be created in the process of submitting the deployment |
714 | + // once there's an ID. |
715 | + var _watchDeployment = ns.BundleHelpers._watchDeployment; |
716 | + ns.BundleHelpers._watchDeployment = function(id, env, db) { |
717 | + assert.equal(id, 10); |
718 | + // At this point we've checked the env call, the notification is |
719 | + // correct, and now that we've requested a watcher with the right id. |
720 | + ns.BundleHelpers._watchDeployment = _watchDeployment; |
721 | + |
722 | + // Make sure we did in fact post our notification to the user. |
723 | + assert.equal(hitNotifications, true); |
724 | + done(); |
725 | + }; |
726 | + |
727 | + // Start the process by deploying the bundle. |
728 | + ns.BundleHelpers.deployBundle('test bundle', env, db); |
729 | + }); |
730 | + |
731 | + it('provides a notification when a deploy watch updates', function(done) { |
732 | + var watchId = 1; |
733 | + var callNumber = 0; |
734 | + |
735 | + // This will get called twice. First with an update that it was |
736 | + // scheduled, then with a second call that it's complete which stops |
737 | + // the look in the watch. |
738 | + db.notifications.add = function(info) { |
739 | + if (callNumber === 0) { |
740 | + assert.equal('Updated status for deployment: 42', info.title); |
741 | + assert.equal(info.level, 'important'); |
742 | + assert.isTrue(info.message.indexOf('scheduled') !== -1, info.message); |
743 | + } else { |
744 | + assert.equal(info.level, 'important'); |
745 | + assert.isTrue(info.message.indexOf('completed') !== -1, info.message); |
746 | + done(); |
747 | + } |
748 | + callNumber = callNumber + 1; |
749 | + }; |
750 | + |
751 | + var called = false; |
752 | + env.deployerWatchUpdate = function(watchId, callback) { |
753 | + if (!called) { |
754 | + called = true; |
755 | + callback({ |
756 | + err: undefined, |
757 | + Changes: [ |
758 | + // Copied right from the docs of the charm. |
759 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080066, |
760 | + 'Queue': 2}, |
761 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080062, |
762 | + 'Queue': 1}, |
763 | + {'DeploymentId': 42, 'Status': 'started', 'Time': 1377080000, |
764 | + 'Queue': 0} |
765 | + ] |
766 | + }); |
767 | + } else { |
768 | + // Make that this is done now. |
769 | + callback({ |
770 | + err: undefined, |
771 | + Changes: [ |
772 | + // Copied right from the docs of the charm. |
773 | + {'DeploymentId': 42, 'Status': 'completed', 'Time': 1377080066, |
774 | + 'Queue': 2}, |
775 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080062, |
776 | + 'Queue': 1}, |
777 | + {'DeploymentId': 42, 'Status': 'started', 'Time': 1377080000, |
778 | + 'Queue': 0} |
779 | + ] |
780 | + }); |
781 | + } |
782 | + }; |
783 | + |
784 | + // Testing private method is evil, but it does a decent amount of |
785 | + // work and we want the aid in debugging issues. |
786 | + ns.BundleHelpers._watchDeploymentUpdates( |
787 | + watchId, |
788 | + env, |
789 | + db |
790 | + ); |
791 | + }); |
792 | + |
793 | + it('provides a error when the deploy watch says so', function(done) { |
794 | + var watchId = 1; |
795 | + |
796 | + // This will get called twice. First with an update that it was |
797 | + // scheduled, then with a second call that it's complete which stops |
798 | + // the look in the watch. |
799 | + db.notifications.add = function(info) { |
800 | + assert.equal(info.level, 'error'); |
801 | + assert.isTrue(info.message.indexOf('boom') !== -1, info.message); |
802 | + done(); |
803 | + }; |
804 | + |
805 | + env.deployerWatchUpdate = function(watchId, callback) { |
806 | + callback({ |
807 | + err: undefined, |
808 | + Changes: [ |
809 | + // Copied right from the docs of the charm. |
810 | + {'DeploymentId': 42, 'Status': 'completed', 'Time': 1377080066, |
811 | + 'Queue': 2, 'Error': 'Deploy go boom!'}, |
812 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080062, |
813 | + 'Queue': 1}, |
814 | + {'DeploymentId': 42, 'Status': 'started', 'Time': 1377080000, |
815 | + 'Queue': 0} |
816 | + ] |
817 | + }); |
818 | + }; |
819 | + |
820 | + // Testing private method is evil, but it does a decent amount of |
821 | + // work and we want the aid in debugging issues. |
822 | + ns.BundleHelpers._watchDeploymentUpdates( |
823 | + watchId, |
824 | + env, |
825 | + db |
826 | + ); |
827 | + }); |
828 | + |
829 | + |
830 | + it('the stack of deploy to watch integrate', function(done) { |
831 | + // By stubbing only the env calls to behave properly, we should be able to |
832 | + // verify that the bundle-import-helpers stack up and call in proper |
833 | + // succession to get a deployment id, then a watcher id, then the watch |
834 | + // status updates, and then completes. |
835 | + |
836 | + // Stub out the env call to make sure we check the params and call the |
837 | + // provided callback. |
838 | + env.deployerImport = function(bundle, name, callback) { |
839 | + callback({ |
840 | + err: undefined, |
841 | + DeploymentId: 10 |
842 | + }); |
843 | + }; |
844 | + |
845 | + env.deployerWatch = function(deploymentId, callback) { |
846 | + callback({ |
847 | + WatchId: 1 |
848 | + }); |
849 | + }; |
850 | + |
851 | + var updated = false; |
852 | + env.deployerWatchUpdate = function(watchId, callback) { |
853 | + if (!updated) { |
854 | + updated = true; |
855 | + callback({ |
856 | + err: undefined, |
857 | + Changes: [ |
858 | + // Copied right from the docs of the charm. |
859 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080066, |
860 | + 'Queue': 2}, |
861 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080062, |
862 | + 'Queue': 1}, |
863 | + {'DeploymentId': 42, 'Status': 'started', 'Time': 1377080000, |
864 | + 'Queue': 0} |
865 | + ] |
866 | + }); |
867 | + } else { |
868 | + // Make that this is done now. |
869 | + callback({ |
870 | + err: undefined, |
871 | + Changes: [ |
872 | + // Copied right from the docs of the charm. |
873 | + {'DeploymentId': 42, 'Status': 'completed', 'Time': 1377080066, |
874 | + 'Queue': 2}, |
875 | + {'DeploymentId': 42, 'Status': 'scheduled', 'Time': 1377080062, |
876 | + 'Queue': 1}, |
877 | + {'DeploymentId': 42, 'Status': 'started', 'Time': 1377080000, |
878 | + 'Queue': 0} |
879 | + ] |
880 | + }); |
881 | + } |
882 | + }; |
883 | + |
884 | + // We'll be called several times. |
885 | + // 1. Deployment Requested. |
886 | + // 2. Watching will pass without a notification. |
887 | + // 3. The status will first be scheduled. |
888 | + // 4. The status will be completed. |
889 | + var called = 0; |
890 | + db.notifications.add = function(info) { |
891 | + switch (called) { |
892 | + case 0: |
893 | + assert.notEqual( |
894 | + info.title.indexOf('requested'), -1, 'not requested'); |
895 | + break; |
896 | + case 1: |
897 | + assert.notEqual( |
898 | + info.message.indexOf('scheduled'), -1, 'not scheduled'); |
899 | + break; |
900 | + case 2: |
901 | + assert.notEqual( |
902 | + info.message.indexOf('completed'), -1, 'not completed'); |
903 | + done(); |
904 | + break; |
905 | + } |
906 | + called = called + 1; |
907 | + }; |
908 | + |
909 | + // Start the process by deploying the bundle. |
910 | + ns.BundleHelpers.deployBundle('test bundle', env, db); |
911 | + |
912 | + }); |
913 | + |
914 | + }); |
915 | +})(); |
916 | |
917 | === modified file 'test/test_env_go.js' |
918 | --- test/test_env_go.js 2013-09-27 08:09:27 +0000 |
919 | +++ test/test_env_go.js 2013-11-15 19:04:35 +0000 |
920 | @@ -325,6 +325,7 @@ |
921 | }); |
922 | }); |
923 | |
924 | + |
925 | describe('Deployer support', function() { |
926 | it('sends the correct messages on deployer imports', function() { |
927 | env.deployerImport('YAML BLOB'); |
928 | @@ -351,6 +352,35 @@ |
929 | }; |
930 | assert.deepEqual(expected, last_message); |
931 | }); |
932 | + |
933 | + it('builds a proper watch request', function() { |
934 | + env.deployerWatch(2); |
935 | + var last_message = conn.last_message(); |
936 | + var expected = { |
937 | + Type: 'Deployer', |
938 | + Request: 'Watch', |
939 | + RequestId: 1, |
940 | + Params: { |
941 | + DeploymentId: 2 |
942 | + } |
943 | + }; |
944 | + assert.deepEqual(expected, last_message); |
945 | + }); |
946 | + |
947 | + it('builds a proper watch next request', function() { |
948 | + env.deployerWatchUpdate(5); |
949 | + var last_message = conn.last_message(); |
950 | + var expected = { |
951 | + Type: 'Deployer', |
952 | + Request: 'Next', |
953 | + RequestId: 1, |
954 | + Params: { |
955 | + WatcherId: 5 |
956 | + } |
957 | + }; |
958 | + assert.deepEqual(expected, last_message); |
959 | + }); |
960 | + |
961 | }); |
962 | |
963 | it('sends the correct expose message', function() { |
964 | |
965 | === modified file 'test/test_service_module.js' |
966 | --- test/test_service_module.js 2013-11-05 18:10:05 +0000 |
967 | +++ test/test_service_module.js 2013-11-15 19:04:35 +0000 |
968 | @@ -328,10 +328,15 @@ |
969 | } |
970 | }; |
971 | |
972 | - view.topo.set('env', {deployerImport: function(deployerData) { |
973 | + // mock out the Y.BundleHelpers call. |
974 | + var _deployBundle = juju.BundleHelpers.deployBundle; |
975 | + juju.BundleHelpers.deployBundle = function(deployerData, env, db) { |
976 | assert.include(deployerData, 'BUNDLE DATA'); |
977 | + // Restore the deployBundle call for future tests. |
978 | + juju.BundleHelpers.deployBundle = _deployBundle; |
979 | done(); |
980 | - }}); |
981 | + }; |
982 | + |
983 | serviceModule.set('component', view.topo); |
984 | serviceModule.canvasDropHandler(fakeEventObject); |
985 | }); |
986 | |
987 | === modified file 'test/test_utils.js' |
988 | --- test/test_utils.js 2013-11-06 14:39:56 +0000 |
989 | +++ test/test_utils.js 2013-11-15 19:04:35 +0000 |
990 | @@ -1451,43 +1451,4 @@ |
991 | }); |
992 | }); |
993 | |
994 | - describe('utils.deployBundleCallback', function() { |
995 | - var utils, Y; |
996 | - |
997 | - before(function(done) { |
998 | - Y = YUI(GlobalConfig).use('juju-view-utils', function(Y) { |
999 | - utils = Y.juju.views.utils; |
1000 | - done(); |
1001 | - }); |
1002 | - }); |
1003 | - |
1004 | - it('adds a notification if bundle import is successful', function(done) { |
1005 | - var expected = { |
1006 | - title: 'Bundle Deployment Requested', |
1007 | - message: 'Bundle deployment request successful. The full deployment ' + |
1008 | - 'can take some time to complete', |
1009 | - level: 'important' |
1010 | - }; |
1011 | - utils.deployBundleCallback({ |
1012 | - add: function(notification) { |
1013 | - assert.deepEqual(notification, expected); |
1014 | - done(); |
1015 | - }}, {}); |
1016 | - }); |
1017 | - |
1018 | - it('adds a notification if a deployment error occurs', function(done) { |
1019 | - var expected = { |
1020 | - title: 'Bundle Deployment Failed', |
1021 | - message: 'Unable to deploy the bundle. The server returned the ' + |
1022 | - 'following error: bad wolf', |
1023 | - level: 'error' |
1024 | - }; |
1025 | - utils.deployBundleCallback({ |
1026 | - add: function(notification) { |
1027 | - assert.deepEqual(notification, expected); |
1028 | - done(); |
1029 | - }}, {err: 'bad wolf'}); |
1030 | - }); |
1031 | - }); |
1032 | - |
1033 | })(); |
Reviewers: mp+195084_ code.launchpad. net,
Message:
Please take a look.
Description:
Add support for watching progress of bundle import
- Change the bundle- import- extension to a helpers lib that doesn't need import- helpers tests
to be
mixed in. It didn't need any state info.
- Move everyone to going through the single deployBundle function in the
helpers vs their own calls to the env.deployerImport
- Provide the ability to start a watch on a deployment.
- Add the methods to update with new notifications on each change to the
watch
status of the import.
- Start a new test file for the bundle-
- Remove the dupe idea of adding import notifications from the view
utils,
it's part of the helpers.
https:/ /code.launchpad .net/~rharding/ juju-gui/ bundle- progress/ +merge/ 195084
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/26290043/
Affected files (+609, -129 lines): javascripts/ bundle- import- helpers. js debug.js topology/ service. js bundle_ import_ helpers. js service_ module. js
A [revision details]
M app/app.js
M app/assets/
M app/modules-
M app/store/env/go.js
M app/views/
M app/views/utils.js
M bin/merge-files
M test/index.html
A test/test_
M test/test_env_go.js
M test/test_
M test/test_utils.js