- {{partial "host/add-common"}}
+ {{#unless inModal}}
+ {{partial "host/add-common"}}
+ {{/unless}}
{{t 'machine.driverVsphere.accountSection'}}
@@ -131,5 +133,5 @@
{{partial "host/add-options"}}
- {{save-cancel save="save" cancel="cancel"}}
+ {{save-cancel save=driverSaveAction cancel="cancel"}}
diff --git a/app/components/modal-catalog-host/component.js b/app/components/modal-catalog-host/component.js
new file mode 100644
index 000000000..9bbab572d
--- /dev/null
+++ b/app/components/modal-catalog-host/component.js
@@ -0,0 +1,32 @@
+import Ember from 'ember';
+import ModalBase from 'ui/mixins/modal-base';
+
+export default Ember.Component.extend(ModalBase, {
+ modalService: Ember.inject.service('modal'),
+ hostService: Ember.inject.service('host'),
+ classNames: ['full-modal'],
+ loading: true,
+ model: null,
+ hostConfig: null,
+ goBack: null,
+ actions: {
+ completed(hostConfig) {
+ this.get('modalService.modalOpts.callee').send('completed', hostConfig);
+ Ember.run.next(() => {
+ this.get('modalService').toggleModal();
+ });
+ }
+ },
+ init() {
+ this._super(...arguments);
+ var hs = this.get('hostService');
+
+ hs.loadAllDrivers().then((drivers) => {
+ this.set('machineDrivers', drivers);
+ hs.getModel().then((hash) => {
+ this.set('model', hash);
+ this.set('loading', false);
+ });
+ });
+ }
+});
diff --git a/app/components/modal-catalog-host/template.hbs b/app/components/modal-catalog-host/template.hbs
new file mode 100644
index 000000000..60a87f3a9
--- /dev/null
+++ b/app/components/modal-catalog-host/template.hbs
@@ -0,0 +1,7 @@
+{{#if loading}}
+
+ {{t 'generic.loading'}}
+
+{{else}}
+ {{add-host model=model inModal=true completed=(action 'completed')}}
+{{/if}}
\ No newline at end of file
diff --git a/app/components/schema/input-host/component.js b/app/components/schema/input-host/component.js
new file mode 100644
index 000000000..4539edbcb
--- /dev/null
+++ b/app/components/schema/input-host/component.js
@@ -0,0 +1,33 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ modalService: Ember.inject.service('modal'),
+ hostConfig: null,
+ hostName: null,
+ value: null,
+ actions: {
+ launchHost() {
+ // we should reall not kill the previous driver if they edit, fix this in the future
+ if (this.get('hostConfig')) {
+ this.setProperties({
+ hostConfig: null,
+ hostName: null
+ });
+ }
+ this.get('modalService').toggleModal('modal-catalog-host', {
+ callee: this,
+ });
+ },
+ completed(value){
+ this.setProperties({
+ hostConfig: value, // probably use this when we are sending it back up on edit
+ value: JSON.stringify(value)
+ });
+ Object.keys(value).forEach((key) => {
+ if (key.indexOf('Config') >= 0) {
+ this.set('hostName', key.slice(0, key.indexOf('Config')).capitalize());
+ }
+ });
+ }
+ }
+});
diff --git a/app/components/schema/input-host/template.hbs b/app/components/schema/input-host/template.hbs
new file mode 100644
index 000000000..6db5d4973
--- /dev/null
+++ b/app/components/schema/input-host/template.hbs
@@ -0,0 +1,5 @@
+{{#if hostConfig}}
+ {{hostName}}
{{t 'generic.remove'}}
+{{else}}
+
+{{/if}}
diff --git a/app/hosts/new/controller.js b/app/hosts/new/controller.js
index ae6abeeec..f79ae2dfb 100644
--- a/app/hosts/new/controller.js
+++ b/app/hosts/new/controller.js
@@ -1,47 +1,11 @@
import Ember from 'ember';
export default Ember.Controller.extend({
- access: Ember.inject.service(),
- projects: Ember.inject.service(),
-
queryParams : ['backTo', 'driver', 'hostId'],
backTo : null,
driver : null,
hostId : null,
-
- allowCustom : true,
- allowOther : true,
-
actions: {
- switchDriver(name) {
- if (this.get('hostId')) {
- this.set('hostId', null);
- this.set('model.clonedModel', null);
- }
- this.set('driver', name);
- },
- },
-
- driverObj: function() {
- return this.get('model.availableDrivers').filterBy('name', this.get('driver'))[0];
- }.property('driver'),
-
- hasOther: function() {
- return this.get('model.availableDrivers').filterBy('hasUi',false).length > 0;
- }.property('model.availableDrivers.@each.hasUi'),
-
- showPicker: function() {
- return !this.get('projects.current.isWindows') && (
- this.get('model.availableDrivers.length') +
- (this.get('allowOther') && this.get('hasOther') ? 1 : 0) +
- (this.get('allowCustom') ? 1 : 0)
- ) > 1;
- }.property('model.availableDrivers.length','allowOther','hasOther','allowCustom'),
-
- showManage: function() {
- return !this.get('projects.current.isWindows') && this.get('access.admin');
- }.property('access.admin','projects.current.isWindows'),
-
- sortedDrivers: Ember.computed.sort('model.availableDrivers','sortBy'),
- sortBy: ['name'],
+ completed() {}
+ }
});
diff --git a/app/hosts/new/route.js b/app/hosts/new/route.js
index a35be927b..5ceb830f0 100644
--- a/app/hosts/new/route.js
+++ b/app/hosts/new/route.js
@@ -1,25 +1,11 @@
import Ember from 'ember';
-import C from 'ui/utils/constants';
-import Util from 'ui/utils/util';
const { getOwner } = Ember;
-function proxifyUrl(url, proxyBase) {
- let parsed = Util.parseUrl(url);
-
- if ( parsed.hostname.indexOf('.') === -1 || // No dot, local name like localhost
- parsed.hostname.toLowerCase().match(/\.local$/) || // your-macbook.local
- parsed.origin.toLowerCase() === window.location.origin // You are here
- ) {
- return url;
- } else {
- return proxyBase + '/' + url;
- }
-}
-
export default Ember.Route.extend({
access : Ember.inject.service(),
projects : Ember.inject.service(),
settings : Ember.inject.service(),
+ host : Ember.inject.service(),
backTo : null,
defaultDriver: 'custom',
@@ -77,142 +63,24 @@ export default Ember.Route.extend({
beforeModel(/*transition*/) {
this._super(...arguments);
- let us = this.get('userStore');
- let drivers = [];
+ var hs = this.get('host');
- return us.find('machinedriver', null, {forceReload: true}).then((possible) => {
- let promises = [];
-
- possible.filterBy('state','active').forEach((driver) => {
- let schemaName = driver.get('name') + 'Config';
- promises.push(us.find('schema', schemaName).then(() => {
- drivers.push(driver);
- }).catch(() => {
- return Ember.RSVP.resolve();
- }));
- });
-
- return Ember.RSVP.all(promises);
- }).then(() => {
+ return hs.loadAllDrivers().then((drivers) => {
this.set('machineDrivers', drivers);
});
},
model(params) {
this.set('backTo', params.backTo);
+ var hs = this.get('host');
- let promises = {
- reloadHost: this.get('userStore').find('schema','host', {forceReload: true}),
- loadCustomUi: this.loadCustomUi(),
- schemas: this.get('userStore').find('schema'),
- typeDocumentations: this.get('userStore').findAll('typedocumentation')
- };
-
- if ( params.hostId )
- {
- promises.clonedModel = this.getHost(params.hostId);
- }
-
- if ( this.get('access.admin') ) {
- let settings = this.get('settings');
- promises.apiHostSet = settings.load(C.SETTING.API_HOST).then(() => {
- return !!settings.get(C.SETTING.API_HOST);
- });
- } else {
- promises.apiHostSet = Ember.RSVP.resolve(true);
- }
-
- return Ember.RSVP.hash(promises).then((hash) => {
- hash.availableDrivers = this.get('machineDrivers');
- if ( this.get('projects.current.isWindows') ) {
- hash.availableDrivers = [];
- }
-
- let defaultDriver = this.get('defaultDriver');
- let targetDriver = params.driver || this.get('lastDriver') || defaultDriver;
-
- if ( ['custom','other'].indexOf(targetDriver) === -1 && hash.availableDrivers.filterBy('name', targetDriver).length === 0 )
- {
- targetDriver = defaultDriver;
- }
-
- if ( params.driver !== targetDriver )
- {
- this.transitionTo('hosts.new', {queryParams: {driver: targetDriver}});
- }
- else
- {
- return Ember.Object.create(hash);
- }
- });
- },
-
- // Loads the custom UI CSS/JS for drivers that have a uiUrl,
- loadCustomUi() {
- return new Ember.RSVP.Promise((resolve, reject) => {
- let completed = 0, expected = 0;
- let timer = null;
-
- function loaded() {
- completed++;
- if ( completed === expected ) {
- resolve();
- clearTimeout(timer);
- }
- }
-
- function errored(name) {
- clearTimeout(timer);
- reject({type: 'error', message: 'Error loading custom driver UI: ' + name});
- }
-
- // machineDrivers already contains only the active ones with a schema
- this.get('machineDrivers').forEach((driver) => {
- let id = 'driver-ui-js-' + driver.name;
- if (driver.uiUrl && $(`#${id}`).length === 0 ) {
- expected++;
- let script = document.createElement('script');
- script.onload = function() { loaded(driver.name); };
- script.onerror = function() {errored(driver.name); };
- script.src = proxifyUrl(driver.uiUrl, this.get('app.proxyEndpoint'));
- script.id = id;
- document.getElementsByTagName('BODY')[0].appendChild(script);
-
- expected++;
- let link = document.createElement('link');
- link.rel = 'stylesheet';
- link.id = id;
- link.href = proxifyUrl(driver.uiUrl.replace(/\.js$/,'.css'), this.get('app.proxyEndpoint'));
- link.onload = function() { loaded(driver.name); };
- link.onerror = function() { errored(driver.name); };
- document.getElementsByTagName('HEAD')[0].appendChild(link);
- }
- });
-
- if ( expected === 0 ) {
- resolve();
+ return hs.getModel(params).then((hash) => {
+ if (hash.transition) {
+ this.transitionTo('hosts.new', {queryParams: {driver: hash.driver}});
} else {
- timer = setTimeout(function() {
- reject({type: 'error', message: 'Timeout loading custom machine drivers'});
- }, 10000);
+ return hash;
}
});
},
- getHost(hostId) {
- let store = this.get('store');
- return store.find('host', hostId).then((host) => {
-
- let hostOut = host.cloneForNew();
- let src = host[`${host.driver}Config`];
- if ( src ) {
- src.type = `${host.driver}Config`;
- let config = store.createRecord(src);
- hostOut.set(`${host.driver}Config`, config);
- }
- return hostOut;
- }).catch(() => {
- return Ember.RSVP.reject({type: 'error', message: 'Failed to retrieve cloned model'}) ;
- });
- },
});
diff --git a/app/hosts/new/template.hbs b/app/hosts/new/template.hbs
index 46d6d391b..42a85fb06 100644
--- a/app/hosts/new/template.hbs
+++ b/app/hosts/new/template.hbs
@@ -5,47 +5,4 @@
-{{#if model.apiHostSet}}
-
- {{#if showPicker}}
-
- {{#if allowCustom}}
-
- {{/if}}
-
- {{#each sortedDrivers as |choice|}}
- {{#if choice.hasUi}}
-
- {{/if}}
- {{/each}}
-
- {{#if (and allowOther hasOther)}}
-
- {{/if}}
-
- {{/if}}
- {{#if access.admin}}
-
- {{#link-to "admin-tab.machine"}}{{t 'hostsPage.new.manageLink'}}{{/link-to}}
-
- {{/if}}
-
-
- {{#if driver}}
- {{component (if (or (not driverObj) driverObj.hasUi) (concat "machine/driver-" driver) 'machine/driver-other')
- cancel=(route-action 'cancel')
- goBack=(route-action 'goBack')
- clonedModel=model.clonedModel
- driver=(concat driver 'Config')
- schemas=model.schemas
- typeDocumentations=model.typeDocumentations
- availableDrivers=model.availableDrivers
- }}
- {{/if}}
-{{else}}
-
-
- {{host-settings saved="savedHost"}}
-
-
-{{/if}}
+{{add-host model=model driver=driver hostId=hostId completed=(action 'completed') goBack=(route-action 'goBack')}}
\ No newline at end of file
diff --git a/app/ll/service.js b/app/ll/service.js
new file mode 100644
index 000000000..b9261c61d
--- /dev/null
+++ b/app/ll/service.js
@@ -0,0 +1,4 @@
+import Ember from 'ember';
+
+export default Ember.Service.extend({
+});
diff --git a/app/mixins/driver.js b/app/mixins/driver.js
index 0f104f596..b32e54ceb 100644
--- a/app/mixins/driver.js
+++ b/app/mixins/driver.js
@@ -19,6 +19,7 @@ export default Ember.Mixin.create(NewOrEdit, ManageLabels, {
multiTemplate : null,
clonedModel : null,
useHost : true,
+ hostConfig : null,
actions: {
addLabel: addAction('addLabel', '.key'),
@@ -26,7 +27,13 @@ export default Ember.Mixin.create(NewOrEdit, ManageLabels, {
this.attrs.cancel();
},
goBack() {
- this.attrs.goBack();
+ if (Ember.typeOf(this.attrs.goBack) === 'function') {
+ this.attrs.goBack();
+ }
+ },
+ passConfigBack(cb) {
+ this.sendAction('completed', this.get('model'));
+ cb(true);
},
setLabels(labels) {
let out = {};
@@ -52,6 +59,14 @@ export default Ember.Mixin.create(NewOrEdit, ManageLabels, {
}
},
+ driverSaveAction: Ember.computed('inModal', function() {
+ if (this.get('inModal')) {
+ return 'passConfigBack';
+ } else {
+ return 'save';
+ }
+ }),
+
nameParts: function() {
let input = this.get('prefix')||'';
let count = this.get('count');
diff --git a/app/new-stack/controller.js b/app/new-stack/controller.js
index 7e1aaae0b..3a6f8cb19 100644
--- a/app/new-stack/controller.js
+++ b/app/new-stack/controller.js
@@ -1,6 +1,5 @@
import Ember from 'ember';
import NewOrEdit from 'ui/mixins/new-or-edit';
-import C from 'ui/utils/constants';
import {tagChoices, tagsToArray} from 'ui/models/stack';
export default Ember.Controller.extend(NewOrEdit, {
diff --git a/app/router.js b/app/router.js
index dc2ea505a..f18843bd9 100644
--- a/app/router.js
+++ b/app/router.js
@@ -125,6 +125,9 @@ Router.map(function() {
this.route('add', {path: '/add/:cloud_id'});
});
+ this.route('new', {path: '/add'}, function() {
+ this.route('index', {path: '/'});
+ });
this.route('host', {path: '/:host_id', resetNamespace: true}, function() {
this.route('containers');
diff --git a/app/scaling-groups/index/controller.js b/app/scaling-groups/index/controller.js
index 15e6b23a2..81628351e 100644
--- a/app/scaling-groups/index/controller.js
+++ b/app/scaling-groups/index/controller.js
@@ -1,5 +1,4 @@
import Ember from 'ember';
-import C from 'ui/utils/constants';
import { tagsToArray } from 'ui/models/stack';
import { headersWithHost as containerHeaders } from 'ui/components/container-table/component';
diff --git a/app/services/host.js b/app/services/host.js
new file mode 100644
index 000000000..b07174b67
--- /dev/null
+++ b/app/services/host.js
@@ -0,0 +1,164 @@
+import Ember from 'ember';
+import C from 'ui/utils/constants';
+import Util from 'ui/utils/util';
+
+function proxifyUrl(url, proxyBase) {
+ let parsed = Util.parseUrl(url);
+
+ if ( parsed.hostname.indexOf('.') === -1 || // No dot, local name like localhost
+ parsed.hostname.toLowerCase().match(/\.local$/) || // your-macbook.local
+ parsed.origin.toLowerCase() === window.location.origin // You are here
+ ) {
+ return url;
+ } else {
+ return proxyBase + '/' + url;
+ }
+}
+
+export default Ember.Service.extend({
+ userStore: Ember.inject.service('user-store'),
+ access : Ember.inject.service(),
+ projects : Ember.inject.service(),
+ settings : Ember.inject.service(),
+ machineDrivers: null,
+ defaultDriver: 'custom',
+ loadAllDrivers() {
+ let us = this.get('userStore');
+ let drivers = [];
+
+ return new Ember.RSVP.Promise((resolve, reject) => {
+ us.find('machinedriver', null, {forceReload: true}).then((possible) => {
+ let promises = [];
+
+ possible.filterBy('state','active').forEach((driver) => {
+ let schemaName = driver.get('name') + 'Config';
+ promises.push(us.find('schema', schemaName).then(() => {
+ drivers.push(driver);
+ }).catch(() => {
+ reject();
+ }));
+ });
+
+ Ember.RSVP.all(promises);
+ }).then(() => {
+ this.set('machineDrivers', drivers);
+ resolve(drivers);
+ });
+ });
+ },
+ getHost(hostId) {
+ let store = this.get('store');
+ return store.find('host', hostId).then((host) => {
+
+ let hostOut = host.cloneForNew();
+ let src = host[`${host.driver}Config`];
+ if ( src ) {
+ src.type = `${host.driver}Config`;
+ let config = store.createRecord(src);
+ hostOut.set(`${host.driver}Config`, config);
+ }
+ return hostOut;
+ }).catch(() => {
+ return Ember.RSVP.reject({type: 'error', message: 'Failed to retrieve cloned model'}) ;
+ });
+ },
+ getModel(params=null) {
+ let promises = {
+ reloadHost: this.get('userStore').find('schema','host', {forceReload: true}),
+ loadCustomUi: this.loadCustomUi(),
+ schemas: this.get('userStore').find('schema'),
+ typeDocumentations: this.get('userStore').findAll('typedocumentation')
+ };
+
+ if (params && params.hostId )
+ {
+ promises.clonedModel = this.getHost(params.hostId);
+ }
+
+
+ if ( this.get('access.admin') ) {
+ let settings = this.get('settings');
+ promises.apiHostSet = settings.load(C.SETTING.API_HOST).then(() => {
+ return !!settings.get(C.SETTING.API_HOST);
+ });
+ } else {
+ promises.apiHostSet = Ember.RSVP.resolve(true);
+ }
+
+ return Ember.RSVP.hash(promises).then((hash) => {
+ hash.availableDrivers = this.get('machineDrivers');
+ if ( this.get('projects.current.isWindows') ) {
+ hash.availableDrivers = [];
+ }
+
+ let defaultDriver = this.get('defaultDriver');
+ let targetDriver = params && params.driver ? params.driver : defaultDriver;
+
+ if ( ['custom','other'].indexOf(targetDriver) === -1 && hash.availableDrivers.filterBy('name', targetDriver).length === 0 )
+ {
+ targetDriver = defaultDriver;
+ }
+
+ if (params && params.driver !== targetDriver )
+ {
+ return {transition: true, driver: targetDriver};
+ }
+ else
+ {
+ return Ember.Object.create(hash);
+ }
+ });
+ },
+ // Loads the custom UI CSS/JS for drivers that have a uiUrl,
+ loadCustomUi() {
+ return new Ember.RSVP.Promise((resolve, reject) => {
+ let completed = 0, expected = 0;
+ let timer = null;
+
+ function loaded() {
+ completed++;
+ if ( completed === expected ) {
+ resolve();
+ clearTimeout(timer);
+ }
+ }
+
+ function errored(name) {
+ clearTimeout(timer);
+ reject({type: 'error', message: 'Error loading custom driver UI: ' + name});
+ }
+
+ // machineDrivers already contains only the active ones with a schema
+ this.get('machineDrivers').forEach((driver) => {
+ let id = 'driver-ui-js-' + driver.name;
+ if (driver.uiUrl && $(`#${id}`).length === 0 ) {
+ expected++;
+ let script = document.createElement('script');
+ script.onload = function() { loaded(driver.name); };
+ script.onerror = function() {errored(driver.name); };
+ script.src = proxifyUrl(driver.uiUrl, this.get('app.proxyEndpoint'));
+ script.id = id;
+ document.getElementsByTagName('BODY')[0].appendChild(script);
+
+ expected++;
+ let link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.id = id;
+ link.href = proxifyUrl(driver.uiUrl.replace(/\.js$/,'.css'), this.get('app.proxyEndpoint'));
+ link.onload = function() { loaded(driver.name); };
+ link.onerror = function() { errored(driver.name); };
+ document.getElementsByTagName('HEAD')[0].appendChild(link);
+ }
+ });
+
+ if ( expected === 0 ) {
+ resolve();
+ } else {
+ timer = setTimeout(function() {
+ reject({type: 'error', message: 'Timeout loading custom machine drivers'});
+ }, 10000);
+ }
+ });
+ },
+
+});
diff --git a/app/utils/constants.js b/app/utils/constants.js
index ec59da71a..f122f3ca4 100644
--- a/app/utils/constants.js
+++ b/app/utils/constants.js
@@ -405,6 +405,7 @@ C.SUPPORTED_SCHEMA_INPUTS= [
'date',
'enum',
'float',
+ 'host',
'int',
'multiline',
'password',
diff --git a/package.json b/package.json
index 3550e523f..da87c8bf8 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"dotenv": "^4.0.0",
"ember-api-store": "2.2.0",
"ember-browserify": "^1.0.1",
- "ember-cli": "^2.9.1",
+ "ember-cli": "2.9.1",
"ember-cli-app-version": "^2.0.0",
"ember-cli-babel": "^5.1.7",
"ember-cli-clipboard": "0.4.1",
@@ -60,7 +60,7 @@
"forever-agent": "^0.6.1",
"glob": "^5.0.3",
"http-proxy": "^1.11.1",
- "liquid-fire": "0.26.4",
+ "liquid-fire": "0.27.1",
"loader.js": "^4.0.10",
"postcss-scss": "^0.4.0",
"semver": "^5.3.0",
diff --git a/tests/unit/ll/service-test.js b/tests/unit/ll/service-test.js
new file mode 100644
index 000000000..2717ff060
--- /dev/null
+++ b/tests/unit/ll/service-test.js
@@ -0,0 +1,12 @@
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('service:ll', 'Unit | Service | ll', {
+ // Specify the other units that are required for this test.
+ // needs: ['service:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+ let service = this.subject();
+ assert.ok(service);
+});
diff --git a/translations/en-us.yaml b/translations/en-us.yaml
index 95db7077e..57caba930 100644
--- a/translations/en-us.yaml
+++ b/translations/en-us.yaml
@@ -3217,6 +3217,8 @@ schema:
prompt: Choose a Certificate...
inputEnum:
option: Choose an option...
+ inputHost:
+ label: Select Host
inputService:
prompt: Choose a Service...
inputSecret: