'
37 | },
38 |
39 | _errors: {
40 | badParam: 'Bad parameter',
41 | required: 'This field cannot be blank',
42 | requiredForFargate: 'This field is required when using FARGATE launch type',
43 | notSelected: 'Something should be selected',
44 | nonNegative: 'Must be non-negative number',
45 | nonPercentile: 'Must be a number from range 1..100'
46 | },
47 |
48 | _displayedErrors: {},
49 |
50 | initialize: function(){
51 | this.$imagesTable = $j('#ecsImagesTable');
52 | this.$imagesTableWrapper = $j('.imagesTableWrapper');
53 | this.$emptyImagesListMessage = $j('.emptyImagesListMessage'); //TODO: implement
54 | this.$showAddImageDialogButton = $j('#showAddImageDialogButton');
55 |
56 | //add / edit image dialog
57 | this.$addImageButton = $j('#ecsAddImageButton');
58 | this.$cancelAddImageButton = $j('#ecsCancelAddImageButton');
59 |
60 | this.$deleteImageButton = $j('#ecsDeleteImageButton');
61 | this.$cancelDeleteImageButton = $j('#ecsCancelDeleteImageButton');
62 |
63 | this.$launchType = $j('#launchType');
64 | this.$taskDefinition = $j('#taskDefinition');
65 | this.$agentNamePrefix = $j('#agentNamePrefix');
66 | this.$taskGroup = $j('#taskGroup');
67 | this.$subnets = $j('#subnets');
68 | this.$fargatePlatformVersion = $j('#fargatePlatformVersion');
69 | this.$securityGroups = $j('#securityGroups');
70 | this.$assignPublicIp = $j('#assignPublicIp');
71 | this.$cluster = $j('#cluster');
72 | this.$maxInstances = $j('#maxInstances');
73 | this.$cpuReservationLimit = $j('#cpuReservationLimit');
74 | this.$agentPoolId = $j('#agent_pool_id');
75 |
76 | this.$imagesDataElem = $j('#' + 'source_images_json');
77 |
78 | var self = this;
79 | var rawImagesData = this.$imagesDataElem.val() || '[]';
80 | this._imagesDataLength = 0;
81 | try {
82 | var imagesData = JSON.parse(rawImagesData);
83 | this.imagesData = imagesData.reduce(function (accumulator, imageDataStr) {
84 | accumulator[self._imagesDataLength++] = imageDataStr;
85 | return accumulator;
86 | }, {});
87 | } catch (e) {
88 | this.imagesData = {};
89 | BS.Log.error('bad images data: ' + rawImagesData);
90 | }
91 |
92 | this._bindHandlers();
93 | this._renderImagesTable();
94 |
95 | BS.Clouds.Admin.CreateProfileForm.checkIfModified();
96 | },
97 |
98 | _bindHandlers: function () {
99 | var self = this;
100 |
101 | this.$showAddImageDialogButton.on('click', this._showDialogClickHandler.bind(this));
102 | this.$addImageButton.on('click', this._submitDialogClickHandler.bind(this));
103 | this.$cancelAddImageButton.on('click', this._cancelDialogClickHandler.bind(this));
104 |
105 | this.$imagesTable.on('click', this.selectors.rmImageLink, function () {
106 | self.showDeleteImageDialog($j(this));
107 | return false;
108 | });
109 | this.$deleteImageButton.on('click', this._submitDeleteImageDialogClickHandler.bind(this));
110 | this.$cancelDeleteImageButton.on('click', this._cancelDeleteImageDialogClickHandler.bind(this));
111 |
112 | var editDelegates = this.selectors.imagesTableRow + ' .highlight, ' + this.selectors.editImageLink;
113 | var that = this;
114 | this.$imagesTable.on('click', editDelegates, function () {
115 | if (!that.$addImageButton.prop('disabled')) {
116 | self.showEditImageDialog($j(this));
117 | }
118 | return false;
119 | });
120 |
121 | this.$launchType.on('change', function (e, value) {
122 | if(value !== undefined) this.$launchType.val(value);
123 | this._image['launchType'] = this.$launchType.val();
124 | this.validateOptions(e.target.getAttribute('data-id'));
125 | var subnetsTr = $j('.fargate-only');
126 | if(this.$launchType.val() === 'FARGATE'){
127 | subnetsTr.each(function(){
128 | $j(this).removeClass("advancedSetting");
129 | $j(this).removeClass("advancedSettingHighlight");
130 | $j(this).removeClass("advanced_hidden");
131 | })
132 | } else {
133 | subnetsTr.each(function(){
134 | $j(this).addClass("advancedSetting");
135 | if($j("tr[class*='advancedSettingHighlight']")) {
136 | $j(this).addClass("advancedSettingHighlight");
137 | }
138 | if($j("tr[class*='advanced_hidden']")) {
139 | $j(this).addClass("advanced_hidden");
140 | }
141 | })
142 | }
143 | }.bind(this));
144 |
145 | this.$taskDefinition.on('change', function (e, value) {
146 | if(value !== undefined) this.$taskDefinition.val(value);
147 | this._image['taskDefinition'] = this.$taskDefinition.val();
148 | this.validateOptions(e.target.getAttribute('data-id'));
149 | }.bind(this));
150 |
151 | this.$agentNamePrefix.on('change', function (e, value) {
152 | if(value !== undefined) this.$agentNamePrefix.val(value);
153 | this._image['agentNamePrefix'] = this.$agentNamePrefix.val();
154 | this.validateOptions(e.target.getAttribute('data-id'));
155 | }.bind(this));
156 |
157 | this.$cluster.on('change', function (e, value) {
158 | if(value !== undefined) this.$cluster.val(value);
159 | this._image['cluster'] = this.$cluster.val();
160 | this.validateOptions(e.target.getAttribute('data-id'));
161 | }.bind(this));
162 |
163 | this.$taskGroup.on('change', function (e, value) {
164 | if(value !== undefined) this.$taskGroup.val(value);
165 | this._image['taskGroup'] = this.$taskGroup.val();
166 | this.validateOptions(e.target.getAttribute('data-id'));
167 | }.bind(this));
168 |
169 | this.$subnets.on('change', function (e, value) {
170 | if(value !== undefined) this.$subnets.val(value);
171 | this._image['subnets'] = this.$subnets.val();
172 | this.validateOptions(e.target.getAttribute('data-id'));
173 | }.bind(this));
174 |
175 | this.$fargatePlatformVersion.on('change', function (e, value) {
176 | if(value !== undefined) this.$fargatePlatformVersion.val(value);
177 | this._image['fargatePlatformVersion'] = this.$fargatePlatformVersion.val();
178 | this.validateOptions(e.target.getAttribute('data-id'));
179 | }.bind(this));
180 |
181 | this.$securityGroups.on('change', function (e, value) {
182 | if(value !== undefined) this.$securityGroups.val(value);
183 | this._image['securityGroups'] = this.$securityGroups.val();
184 | this.validateOptions(e.target.getAttribute('data-id'));
185 | }.bind(this));
186 |
187 | this.$assignPublicIp.click(function() {
188 | this._image['assignPublicIp'] = this.$assignPublicIp.prop('checked');
189 | }.bind(this));
190 |
191 | this.$maxInstances.on('change', function (e, value) {
192 | if(value !== undefined) this.$maxInstances.val(value);
193 | this._image['maxInstances'] = this.$maxInstances.val();
194 | this.validateOptions(e.target.getAttribute('data-id'));
195 | }.bind(this));
196 |
197 | this.$cpuReservationLimit.on('change', function (e, value) {
198 | if(value !== undefined) this.$cpuReservationLimit.val(value);
199 | this._image['cpuReservationLimit'] = this.$cpuReservationLimit.val();
200 | this.validateOptions(e.target.getAttribute('data-id'));
201 | }.bind(this));
202 |
203 | this.$agentPoolId.on('change', function (e, value) {
204 | if(value !== undefined) this.$agentPoolId.val(value);
205 | this._image['agent_pool_id'] = this.$agentPoolId.val();
206 | this.validateOptions(e.target.getAttribute('data-id'));
207 | }.bind(this));
208 | },
209 |
210 | _renderImagesTable: function () {
211 | this._clearImagesTable();
212 |
213 | if (this._imagesDataLength) {
214 | Object.keys(this.imagesData).forEach(function (imageId) {
215 | var image = this.imagesData[imageId];
216 | var src = image['source-id'];
217 | $j('#initial_images_list').val($j('#initial_images_list').val() + src + ",");
218 | this._renderImageRow(image, imageId);
219 | }.bind(this));
220 | }
221 |
222 | this._toggleImagesTable();
223 | BS.Clouds.Admin.CreateProfileForm.checkIfModified();
224 | },
225 |
226 | _clearImagesTable: function () {
227 | this.$imagesTable.find('.imagesTableRow').remove();
228 | },
229 |
230 | _toggleImagesTable: function () {
231 | var toggle = !!this._imagesDataLength;
232 | this.$imagesTableWrapper.removeClass('hidden');
233 | this.$emptyImagesListMessage.toggleClass('hidden', toggle);
234 | this.$imagesTable.toggleClass('hidden', !toggle);
235 | },
236 |
237 | _renderImageRow: function (props, id) {
238 | var $row = this.templates.imagesTableRow.clone().attr('data-image-id', id);
239 | var defaults = this.defaults;
240 |
241 | this._dataKeys.forEach(function (className) {
242 | $row.find('.' + className).text(props[className] || defaults[className]);
243 | });
244 |
245 | $row.find(this.selectors.rmImageLink).data('image-id', id);
246 | $row.find(this.selectors.editImageLink).data('image-id', id);
247 | this.$imagesTable.append($row);
248 | },
249 |
250 | _showDialogClickHandler: function () {
251 | if (! this.$showAddImageDialogButton.attr('disabled')) {
252 | this.showAddImageDialog();
253 | }
254 | return false;
255 | },
256 |
257 | _submitDialogClickHandler: function() {
258 | if (this.validateOptions()) {
259 | if (this.$addImageButton.val().toLowerCase() === 'save') {
260 | this.editImage(this.$addImageButton.data('image-id'));
261 | } else {
262 | this.addImage();
263 | }
264 | BS.Ecs.ImageDialog.close();
265 | }
266 | return false;
267 | },
268 |
269 | _cancelDialogClickHandler: function () {
270 | BS.Ecs.ImageDialog.close();
271 | return false;
272 | },
273 |
274 | selectTaskDef: function(taskDef){
275 | this.$taskDefinition.trigger('change', taskDef || '');
276 | },
277 |
278 | selectCluster: function(cluster){
279 | this.$cluster.trigger('change', cluster || '');
280 | },
281 |
282 | showAddImageDialog: function () {
283 | $j('#EcsImageDialogTitle').text('Add Amazon Elastic Container Service Cloud Image');
284 |
285 | BS.Hider.addHideFunction('EcsImageDialog', this._resetDataAndDialog.bind(this));
286 | this.$addImageButton.val('Add').data('image-id', 'undefined');
287 |
288 | this._image = {};
289 |
290 | BS.Ecs.ImageDialog.showCentered();
291 | },
292 |
293 | showEditImageDialog: function ($elem) {
294 | var imageId = $elem.parents(this.selectors.imagesTableRow).data('image-id');
295 |
296 | $j('#EcsImageDialogTitle').text('Edit Amazon Elastic Container Service Cloud Image');
297 |
298 | BS.Hider.addHideFunction('EcsImageDialog', this._resetDataAndDialog.bind(this));
299 |
300 | typeof imageId !== 'undefined' && (this._image = $j.extend({}, this.imagesData[imageId]));
301 | this.$addImageButton.val('Save').data('image-id', imageId);
302 | if (imageId === 'undefined'){
303 | this.$addImageButton.removeData('image-id');
304 | }
305 |
306 | var image = this._image;
307 |
308 | this.$launchType.trigger('change', image['launchType'] || '');
309 | this.selectTaskDef(image['taskDefinition'] || '');
310 | this.$agentNamePrefix.trigger('change', image['agentNamePrefix'] || '');
311 | this.$taskGroup.trigger('change', image['taskGroup'] || '');
312 | this.$subnets.trigger('change', image['subnets'] || '');
313 | this.$fargatePlatformVersion.trigger('change', image['fargatePlatformVersion'] || '');
314 | this.$securityGroups.trigger('change', image['securityGroups'] || '');
315 | this.$assignPublicIp.prop('checked', image['assignPublicIp'] === 'true' ? image['assignPublicIp'] : '');
316 | this.selectCluster(image['cluster'] || '');
317 | this.$maxInstances.trigger('change', image['maxInstances'] || '');
318 | this.$cpuReservationLimit.trigger('change', image['cpuReservationLimit'] || '');
319 | this.$agentPoolId.trigger('change', image['agent_pool_id'] || '');
320 |
321 | BS.Ecs.ImageDialog.showCentered();
322 | },
323 |
324 | _resetDataAndDialog: function () {
325 | this._image = {};
326 |
327 | this.$launchType.trigger('change', '');
328 | this.selectTaskDef('');
329 | this.$agentNamePrefix.trigger('change', '');
330 | this.$taskGroup.trigger('change', '');
331 | this.$subnets.trigger('change', '');
332 | this.$fargatePlatformVersion.trigger('change', 'LATEST');
333 | this.$securityGroups.trigger('change', '');
334 | this.$assignPublicIp.prop('checked', '');
335 | this.selectCluster('');
336 | this.$maxInstances.trigger('change', '');
337 | this.$cpuReservationLimit.trigger('change', '');
338 | this.$agentPoolId.trigger('change', '');
339 | },
340 |
341 | validateOptions: function (options){
342 | var isValid = true;
343 |
344 | var validators = {
345 | launchType : function () {
346 | var launchType = this._image['launchType'];
347 | if (!launchType || launchType === '' || launchType === undefined) {
348 | this.addOptionError('notSelected', 'launchType');
349 | isValid = false;
350 | }
351 | }.bind(this),
352 |
353 | taskDefinition : function () {
354 | if (!this._image['taskDefinition']) {
355 | this.addOptionError('required', 'taskDefinition');
356 | isValid = false;
357 | }
358 | }.bind(this),
359 |
360 | maxInstances: function () {
361 | var maxInstances = this._image['maxInstances'];
362 | if (maxInstances && (!$j.isNumeric(maxInstances) || maxInstances < 0 )) {
363 | this.addOptionError('nonNegative', 'maxInstances');
364 | isValid = false;
365 | }
366 | }.bind(this),
367 |
368 | cpuReservationLimit: function () {
369 | var cpuReservationLimit = this._image['cpuReservationLimit'];
370 | if (cpuReservationLimit && (!$j.isNumeric(cpuReservationLimit) || cpuReservationLimit < 0 || cpuReservationLimit > 100 )) {
371 | this.addOptionError('nonPercentile', 'cpuReservationLimit');
372 | isValid = false;
373 | }
374 | }.bind(this),
375 |
376 | agent_pool_id : function () {
377 | var agentPoolId = this._image['agent_pool_id'];
378 | if (!agentPoolId || agentPoolId === '' || agentPoolId === undefined) {
379 | this.addOptionError('notSelected', 'agent_pool_id');
380 | isValid = false;
381 | }
382 | }.bind(this)
383 | };
384 |
385 | if (options && ! $j.isArray(options)) {
386 | options = [options];
387 | }
388 |
389 | this.clearOptionsErrors(options);
390 |
391 | (options || this._dataKeys).forEach(function(option) {
392 | if(validators[option]) validators[option]();
393 | });
394 |
395 | return isValid;
396 | },
397 |
398 | addOptionError: function (errorKey, optionName) {
399 | var html;
400 |
401 | if (errorKey && optionName) {
402 | this._displayedErrors[optionName] = this._displayedErrors[optionName] || [];
403 |
404 | if (typeof errorKey !== 'string') {
405 | html = this._errors[errorKey.key];
406 | Object.keys(errorKey.props).forEach(function(key) {
407 | html = html.replace('%%'+key+'%%', errorKey.props[key]);
408 | });
409 | errorKey = errorKey.key;
410 | } else {
411 | html = this._errors[errorKey];
412 | }
413 |
414 | if (this._displayedErrors[optionName].indexOf(errorKey) === -1) {
415 | this._displayedErrors[optionName].push(errorKey);
416 | this.addError(html, $j('.option-error_' + optionName));
417 | }
418 | }
419 | },
420 |
421 | addError: function (errorHTML, target) {
422 | target.append($j('').html(errorHTML));
423 | },
424 |
425 | clearOptionsErrors: function (options) {
426 | (options || this._dataKeys).forEach(function (optionName) {
427 | this.clearErrors(optionName);
428 | }.bind(this));
429 | },
430 |
431 | clearErrors: function (errorId) {
432 | var target = $j('.option-error_' + errorId);
433 | if (errorId) {
434 | delete this._displayedErrors[errorId];
435 | }
436 | target.empty();
437 | },
438 |
439 | addImage: function () {
440 | var newImageId = this.generateNewImageId(),
441 | newImage = this._image;
442 | newImage['source-id'] = newImageId;
443 | this._renderImageRow(newImage, newImageId);
444 | this.imagesData[newImageId] = newImage;
445 | this._imagesDataLength += 1;
446 | this.saveImagesData();
447 | this._toggleImagesTable();
448 | },
449 |
450 | generateNewImageId: function () {
451 | if($j.isEmptyObject(this.imagesData)) return 1;
452 | else return Math.max.apply(Math, $j.map(this.imagesData, function callback(currentValue) {
453 | return currentValue['source-id'];
454 | })) + 1;
455 | },
456 |
457 | editImage: function (id) {
458 | this._image['source-id'] = id;
459 | this.imagesData[id] = this._image;
460 | this.saveImagesData();
461 | this.$imagesTable.find(this.selectors.imagesTableRow).remove();
462 | this._renderImagesTable();
463 | },
464 |
465 | removeImage: function (imageId) {
466 | delete this.imagesData[imageId];
467 | this._imagesDataLength -= 1;
468 | this.$imagesTable.find('tr[data-image-id=\'' + imageId + '\']').remove();
469 | this.saveImagesData();
470 | this._toggleImagesTable();
471 | },
472 |
473 | saveImagesData: function () {
474 | var imageData = Object.keys(this.imagesData).reduce(function (accumulator, id) {
475 | var _val = $j.extend({}, this.imagesData[id]);
476 |
477 | delete _val.$image;
478 | accumulator.push(_val);
479 |
480 | return accumulator;
481 | }.bind(this), []);
482 | this.$imagesDataElem.val(JSON.stringify(imageData));
483 | },
484 |
485 | testConnection: function() {
486 | BS.ajaxRequest(this.testConnectionUrl, {
487 | parameters: BS.Clouds.Admin.CreateProfileForm.serializeParameters(),
488 | onFailure: function (response) {
489 | BS.TestConnectionDialog.show(false, response, null);
490 | }.bind(this),
491 | onSuccess: function (response) {
492 | var wereErrors = BS.XMLResponse.processErrors(response.responseXML, {
493 | onConnectionError: function(elem) {
494 | BS.TestConnectionDialog.show(false, elem.firstChild.nodeValue, null);
495 | }
496 | }, BS.PluginPropertiesForm.propertiesErrorsHandler);
497 | if(!wereErrors){
498 | BS.TestConnectionDialog.show(true, "", null);
499 | }
500 | }.bind(this)
501 | });
502 | },
503 |
504 | showDeleteImageDialog: function ($elem) {
505 | var imageId = $elem.parents(this.selectors.imagesTableRow).data('image-id');
506 |
507 | BS.ajaxUpdater($("ecsDeleteImageDialogBody"), BS.Ecs.DeleteImageDialog.url + window.location.search, {
508 | method: 'get',
509 | parameters : {
510 | imageId : imageId
511 | },
512 | onComplete: function() {
513 | BS.Ecs.DeleteImageDialog.show(imageId);
514 | }
515 | });
516 | },
517 |
518 | _cancelDeleteImageDialogClickHandler: function () {
519 | BS.Ecs.DeleteImageDialog.close();
520 | return false;
521 | },
522 |
523 | _submitDeleteImageDialogClickHandler: function() {
524 | var imageId = BS.Ecs.DeleteImageDialog.currentImageId;
525 | BS.ajaxRequest(BS.Ecs.DeleteImageDialog.url + window.location.search, {
526 | method: 'post',
527 | parameters : {
528 | imageId : imageId
529 | },
530 | onComplete: function() {
531 | BS.Ecs.ProfileSettingsForm.removeImage(imageId);
532 | BS.Ecs.DeleteImageDialog.close();
533 | }
534 | });
535 | }
536 | });
537 |
538 | if(!BS.Ecs.ImageDialog) BS.Ecs.ImageDialog = OO.extend(BS.AbstractModalDialog, {
539 | getContainer: function() {
540 | return $('EcsImageDialog');
541 | }
542 | });
543 |
544 | if(!BS.Ecs.TaskDefChooser){
545 | BS.Ecs.TaskDefChooser = new BS.Popup('taskDefChooser', {
546 | hideDelay: 0,
547 | hideOnMouseOut: false,
548 | hideOnMouseClickOutside: true,
549 | loadingText: "Loading task definitions..."
550 | });
551 |
552 | BS.Ecs.TaskDefChooser.showPopup = function(nearestElement, dataLoadUrl){
553 | var serializeParameters = BS.Clouds.Admin.CreateProfileForm.serializeParameters();
554 | serializeParameters += "&launchType=" + BS.Ecs.ProfileSettingsForm.$launchType.val();
555 | this.showPopupNearElement(nearestElement, {
556 | parameters: serializeParameters,
557 | url: dataLoadUrl,
558 | shift:{x:15,y:15}
559 | });
560 | };
561 |
562 | BS.Ecs.TaskDefChooser.selectTaskDef = function (taskDef) {
563 | BS.Ecs.ProfileSettingsForm.selectTaskDef(taskDef);
564 | this.hidePopup();
565 | };
566 | }
567 |
568 | if(!BS.Ecs.ClusterChooser) {
569 | BS.Ecs.ClusterChooser = new BS.Popup('clusterChooser', {
570 | hideDelay: 0,
571 | hideOnMouseOut: false,
572 | hideOnMouseClickOutside: true,
573 | loadingText: "Loading clusters..."
574 | });
575 |
576 | BS.Ecs.ClusterChooser.showPopup = function(nearestElement, dataLoadUrl) {
577 | this.showPopupNearElement(nearestElement, {
578 | parameters: BS.Clouds.Admin.CreateProfileForm.serializeParameters(),
579 | url: dataLoadUrl,
580 | shift:{x:15,y:15}
581 | });
582 | };
583 |
584 | BS.Ecs.ClusterChooser.selectCluster = function (cluster) {
585 | BS.Ecs.ProfileSettingsForm.$cluster.trigger('change', cluster || '');
586 | this.hidePopup();
587 | };
588 | }
589 |
590 | if(!BS.Ecs.DeleteImageDialog) BS.Ecs.DeleteImageDialog = OO.extend(BS.AbstractModalDialog, {
591 | url: '',
592 | currentImageId: '',
593 |
594 | getContainer: function() {
595 | return $('EcsDeleteImageDialog');
596 | },
597 |
598 | show: function (imageId) {
599 | BS.Ecs.DeleteImageDialog.currentImageId = imageId;
600 | BS.Ecs.DeleteImageDialog.showCentered();
601 | }
602 | });
--------------------------------------------------------------------------------