diff --git a/package.json b/package.json index 07ed9db51e..fc3adc32c1 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "node_modules/6to5" ] }, - "docker-version": "1.4.1", - "boot2docker-version": "1.4.1", + "docker-version": "1.5.0", + "boot2docker-version": "1.5.0", "atom-shell-version": "0.21.1", "virtualbox-version": "4.3.20", "virtualbox-filename": "VirtualBox-4.3.20.pkg", diff --git a/src/ContainerDetails.react.js b/src/ContainerDetails.react.js index 745d77ab86..1063adf9c3 100644 --- a/src/ContainerDetails.react.js +++ b/src/ContainerDetails.react.js @@ -19,7 +19,7 @@ var ContainerDetail = React.createClass({ }, init: function () { var currentRoute = _.last(this.getRoutes()).name; - if (currentRoute === 'containerDetail') { + if (currentRoute === 'containerDetails') { this.transitionTo('containerHome', {name: this.getParams().name}); } }, diff --git a/src/ContainerDetailsbak.react.js b/src/ContainerDetailsbak.react.js deleted file mode 100644 index 049f41b690..0000000000 --- a/src/ContainerDetailsbak.react.js +++ /dev/null @@ -1,640 +0,0 @@ -var _ = require('underscore'); -var $ = require('jquery'); -var React = require('react/addons'); -var Router = require('react-router'); -var exec = require('exec'); -var path = require('path'); -var remote = require('remote'); -var rimraf = require('rimraf'); -var fs = require('fs'); -var dialog = remote.require('dialog'); -var ContainerStore = require('./ContainerStore'); -var ContainerUtil = require('./ContainerUtil'); -var boot2docker = require('./Boot2Docker'); -var ContainerDetailsHeader = require('./ContainerDetailsHeader.react'); -var ContainerHome = require('./ContainerHome.react'); -var RetinaImage = require('react-retina-image'); -var Radial = require('./Radial.react'); - -var _oldHeight = 0; - -var ContainerDetailsbak = React.createClass({ - mixins: [Router.State, Router.Navigation], - PAGE_HOME: 'home', - PAGE_LOGS: 'logs', - PAGE_SETTINGS: 'settings', - PAGE_PORTS: 'ports', - PAGE_VOLUMES: 'volumes', - getInitialState: function () { - return { - logs: [], - page: this.PAGE_HOME, - env: {}, - pendingEnv: {}, - ports: {}, - volumes: {}, - defaultPort: null - }; - }, - componentWillReceiveProps: function () { - this.init(); - }, - componentDidMount: function () { - this.init(); - ContainerStore.on(ContainerStore.SERVER_PROGRESS_EVENT, this.updateProgress); - ContainerStore.on(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); - }, - componentWillUnmount: function () { - ContainerStore.removeListener(ContainerStore.SERVER_PROGRESS_EVENT, this.updateProgress); - ContainerStore.removeListener(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); - }, - componentDidUpdate: function () { - // Scroll logs to bottom - var parent = $('.details-logs'); - if (parent.length) { - if (parent.scrollTop() >= _oldHeight) { - parent.stop(); - parent.scrollTop(parent[0].scrollHeight - parent.height()); - } - _oldHeight = parent[0].scrollHeight - parent.height(); - } - }, - init: function () { - var container = ContainerStore.container(this.getParams().name); - if (!container) { - return; - } - this.setState({ - progress: ContainerStore.progress(this.getParams().name), - env: ContainerUtil.env(container), - page: this.PAGE_HOME - }); - var ports = ContainerUtil.ports(container); - var webPorts = ['80', '8000', '8080', '3000', '5000', '2368']; - this.setState({ - ports: ports, - defaultPort: _.find(_.keys(ports), function (port) { - return webPorts.indexOf(port) !== -1; - }) - }); - this.updateLogs(); - }, - updateLogs: function (name) { - if (name && name !== this.getParams().name) { - return; - } - this.setState({ - logs: ContainerStore.logs(this.getParams().name) - }); - }, - updateProgress: function (name) { - if (name === this.getParams().name) { - this.setState({ - progress: ContainerStore.progress(name) - }); - } - }, - disableRun: function () { - return (!this.props.container.State.Running || !this.state.defaultPort); - }, - disableRestart: function () { - return (this.props.container.State.Downloading || this.props.container.State.Restarting); - }, - disableTerminal: function () { - return (!this.props.container.State.Running); - }, - disableTab: function () { - return (this.props.container.State.Downloading); - }, - showHome: function () { - if (!this.disableTab()) { - /*this.setState({ - page: this.PAGE_HOME - });*/ - this.transitionTo('containerHome', {name: this.getParams().name}); - } - }, - showLogs: function () { - if (!this.disableTab()) { - this.setState({ - page: this.PAGE_LOGS - }); - } - }, - showPorts: function () { - this.setState({ - page: this.PAGE_PORTS - }); - }, - showVolumes: function () { - this.setState({ - page: this.PAGE_VOLUMES - }); - }, - showSettings: function () { - if (!this.disableTab()) { - this.setState({ - page: this.PAGE_SETTINGS - }); - } - }, - handleRun: function () { - if (this.state.defaultPort && !this.disableRun()) { - exec(['open', this.state.ports[this.state.defaultPort].url], function (err) { - if (err) { throw err; } - }); - } - }, - handleRestart: function () { - if (!this.disableRestart()) { - ContainerStore.restart(this.props.container.Name, function (err) { - console.log(err); - }); - } - }, - handleTerminal: function () { - if (!this.disableTerminal()) { - var container = this.props.container; - var terminal = path.join(process.cwd(), 'resources', 'terminal'); - var cmd = [terminal, boot2docker.command().replace(/ /g, '\\\\\\\\ ').replace(/\(/g, '\\\\\\\\(').replace(/\)/g, '\\\\\\\\)'), 'ssh', '-t', 'sudo', 'docker', 'exec', '-i', '-t', container.Name, 'sh']; - exec(cmd, function (stderr, stdout, code) { - console.log(stderr); - console.log(stdout); - if (code) { - console.log(stderr); - } - }); - } - }, - handleViewLink: function (url) { - exec(['open', url], function (err) { - if (err) { throw err; } - }); - }, - handleChangeDefaultPort: function (port, e) { - if (e.target.checked) { - this.setState({ - defaultPort: null - }); - } else { - this.setState({ - defaultPort: port - }); - } - }, - handleChooseVolumeClick: function (dockerVol) { - var self = this; - dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, function (filenames) { - if (!filenames) { - return; - } - var directory = filenames[0]; - if (directory) { - var volumes = _.clone(self.props.container.Volumes); - volumes[dockerVol] = directory; - var binds = _.pairs(volumes).map(function (pair) { - return pair[1] + ':' + pair[0]; - }); - ContainerStore.updateContainer(self.props.container.Name, { - Binds: binds - }, function (err) { - if (err) { console.log(err); } - }); - } - }); - }, - handleOpenVolumeClick: function (path) { - exec(['open', path], function (err) { - if (err) { throw err; } - }); - }, - handleSaveContainerName: function () { - var newName = $('#input-container-name').val(); - if (newName === this.props.container.Name) { - return; - } - if (fs.existsSync(path.join(process.env.HOME, 'Kitematic', this.props.container.Name))) { - fs.renameSync(path.join(process.env.HOME, 'Kitematic', this.props.container.Name), path.join(process.env.HOME, 'Kitematic', newName)); - } - ContainerStore.updateContainer(this.props.container.Name, { - name: newName - }, function (err) { - this.transitionTo('container', {name: newName}); - if (err) { - console.error(err); - } - }.bind(this)); - }, - handleSaveEnvVar: function () { - var $rows = $('.env-vars .keyval-row'); - var envVarList = []; - $rows.each(function () { - var key = $(this).find('.key').val(); - var val = $(this).find('.val').val(); - if (!key.length || !val.length) { - return; - } - envVarList.push(key + '=' + val); - }); - var self = this; - ContainerStore.updateContainer(self.props.container.Name, { - Env: envVarList - }, function (err) { - if (err) { - console.error(err); - } else { - self.setState({ - pendingEnv: {} - }); - $('#new-env-key').val(''); - $('#new-env-val').val(''); - } - }); - }, - handleAddPendingEnvVar: function () { - var newKey = $('#new-env-key').val(); - var newVal = $('#new-env-val').val(); - var newEnv = {}; - newEnv[newKey] = newVal; - this.setState({ - pendingEnv: _.extend(this.state.pendingEnv, newEnv) - }); - $('#new-env-key').val(''); - $('#new-env-val').val(''); - }, - handleRemoveEnvVar: function (key) { - var newEnv = _.omit(this.state.env, key); - this.setState({ - env: newEnv - }); - }, - handleRemovePendingEnvVar: function (key) { - var newEnv = _.omit(this.state.pendingEnv, key); - this.setState({ - pendingEnv: newEnv - }); - }, - handleDeleteContainer: function () { - dialog.showMessageBox({ - message: 'Are you sure you want to delete this container?', - buttons: ['Delete', 'Cancel'] - }, function (index) { - var volumePath = path.join(process.env.HOME, 'Kitematic', this.props.container.Name); - if (fs.existsSync(volumePath)) { - rimraf(volumePath, function (err) { - console.log(err); - }); - } - if (index === 0) { - ContainerStore.remove(this.props.container.Name, function (err) { - console.error(err); - }); - } - }.bind(this)); - }, - handleItemMouseEnterRun: function () { - var $action = $(this.getDOMNode()).find('.action .run'); - $action.css("visibility", "visible"); - }, - handleItemMouseLeaveRun: function () { - var $action = $(this.getDOMNode()).find('.action .run'); - $action.css("visibility", "hidden"); - }, - handleItemMouseEnterRestart: function () { - var $action = $(this.getDOMNode()).find('.action .restart'); - $action.css("visibility", "visible"); - }, - handleItemMouseLeaveRestart: function () { - var $action = $(this.getDOMNode()).find('.action .restart'); - $action.css("visibility", "hidden"); - }, - handleItemMouseEnterTerminal: function () { - var $action = $(this.getDOMNode()).find('.action .terminal'); - $action.css("visibility", "visible"); - }, - handleItemMouseLeaveTerminal: function () { - var $action = $(this.getDOMNode()).find('.action .terminal'); - $action.css("visibility", "hidden"); - }, - render: function () { - var self = this; - - if (!this.state) { - return
; - } - - var logs = this.state.logs.map(function (l, i) { - return

; - }); - - if (!this.props.container) { - return false; - } - - var button; - if (this.state.progress === 1) { - button = View; - } else { - button = View; - } - - var envVars = _.map(this.state.env, function (val, key) { - return ( -
- - - -
- ); - }); - var pendingEnvVars = _.map(this.state.pendingEnv, function (val, key) { - return ( -
- - - -
- ); - }); - - var disabledClass = ''; - if (!this.props.container.State.Running) { - disabledClass = 'disabled'; - } - - /*var buttonClass = React.addons.classSet({ - btn: true, - 'btn-action': true, - 'with-icon': true, - disabled: !this.props.container.State.Running - }); - - var restartButtonClass = React.addons.classSet({ - btn: true, - 'btn-action': true, - 'with-icon': true, - disabled: this.props.container.State.Downloading || this.props.container.State.Restarting - }); - - var viewButtonClass = React.addons.classSet({ - btn: true, - 'btn-action': true, - 'with-icon': true, - disabled: !this.props.container.State.Running || !this.state.defaultPort - }); - - var kitematicVolumes = _.pairs(this.props.container.Volumes).filter(function (pair) { - return pair[1].indexOf(path.join(process.env.HOME, 'Kitematic')) !== -1; - }); - - var volumesButtonClass = React.addons.classSet({ - btn: true, - 'btn-action': true, - 'with-icon': true, - disabled: !kitematicVolumes.length - }); - - var textButtonClasses = React.addons.classSet({ - 'btn': true, - 'btn-action': true, - 'only-icon': true, - 'active': this.state.page === this.PAGE_LOGS, - disabled: this.props.container.State.Downloading - }); - - var gearButtonClass = React.addons.classSet({ - 'btn': true, - 'btn-action': true, - 'only-icon': true, - 'active': this.state.page === this.PAGE_SETTINGS, - disabled: this.props.container.State.Downloading - });*/ - - var ports = _.map(_.pairs(self.state.ports), function (pair) { - var key = pair[0]; - var val = pair[1]; - return ( -
- {key} - {val.display} -
- ); - }); - - var volumes = _.map(self.props.container.Volumes, function (val, key) { - if (!val || val.indexOf(process.env.HOME) === -1) { - val = 'No Host Folder'; - } - return ( -
- {key} - {val.replace(process.env.HOME, '~')} -
- ); - }); - - var body; - if (this.props.container.State.Downloading) { - if (this.state.progress) { - body = ( -
-

Downloading Image

- -
- ); - } else { - body = ( -
-

Connecting to Docker Hub

- -
- ); - } - } else { - if (this.state.page === this.PAGE_HOME) { - body = ( - - ); - } else if (this.state.page === this.PAGE_LOGS) { - body = ( -
- {logs} -
- ); - } else if (this.state.page === this.PAGE_PORTS) { - body = ( -
-
-

Configure Ports

-
-
-
DOCKER PORT
-
MAC PORT
-
- {ports} -
-
-
- ); - } else if (this.state.page === this.PAGE_VOLUMES) { - body = ( -
-
-

Configure Volumes

-
-
-
DOCKER FOLDER
-
MAC FOLDER
-
- {volumes} -
-
-
- ); - } else { - var rename = ( -
-

Container Name

-
- -
- Save -
- ); - body = ( -
-
- {rename} -
-

Environment Variables

-
-
KEY
-
VALUE
-
-
- {envVars} - {pendingEnvVars} -
- - - -
-
- Save -
-
-

Delete Container

- Delete Container -
-
-
- ); - } - } - - var tabHomeClasses = React.addons.classSet({ - 'tab': true, - 'active': this.state.page === this.PAGE_HOME, - disabled: this.disableTab() - }); - - var tabLogsClasses = React.addons.classSet({ - 'tab': true, - 'active': this.state.page === this.PAGE_LOGS, - disabled: this.disableTab() - }); - - var tabSettingsClasses = React.addons.classSet({ - 'tab': true, - 'active': this.state.page === this.PAGE_SETTINGS, - disabled: this.disableTab() - }); - - /*var ports = _.map(_.pairs(self.state.ports), function (pair, index, list) { - var key = pair[0]; - var val = pair[1]; - return ( -
- {key} - {val.display} - -
- ); - }); - - var volumes = _.map(self.props.container.Volumes, function (val, key) { - if (!val || val.indexOf(process.env.HOME) === -1) { - val = No folderChoose; - } else { - val = {val.replace(process.env.HOME, '~')} Choose; - } - return ( -
- {key} - {val} -
- ); - });*/ - - /* var view; - if (this.state.defaultPort) { - view = ( -
- View - -
- ); - } else { - view = ( -
- Ports -
- ); - }*/ - - var runActionClass = React.addons.classSet({ - action: true, - disabled: this.disableRun() - }); - - var restartActionClass = React.addons.classSet({ - action: true, - disabled: this.disableRestart() - }); - - var terminalActionClass = React.addons.classSet({ - action: true, - disabled: this.disableTerminal() - }); - - return ( -
- -
-
-
- - Run -
-
- - Restart -
-
- - Terminal -
-
-
- Home - Logs - Settings -
-
- {body} -
- ); - } -}); - -module.exports = ContainerDetailsbak; diff --git a/src/ContainerHome.react.js b/src/ContainerHome.react.js index 394a6d692f..2126549f1a 100644 --- a/src/ContainerHome.react.js +++ b/src/ContainerHome.react.js @@ -90,7 +90,7 @@ var ContainerHome = React.createClass({
- +
@@ -116,7 +116,7 @@ var ContainerHome = React.createClass({
- +
{right}
diff --git a/src/ContainerHomeLogs.react.js b/src/ContainerHomeLogs.react.js index 2e66352ad1..d1778a36cf 100644 --- a/src/ContainerHomeLogs.react.js +++ b/src/ContainerHomeLogs.react.js @@ -1,6 +1,6 @@ var $ = require('jquery'); var React = require('react/addons'); -var ContainerStore = require('./ContainerStore'); +var LogStore = require('./LogStore'); var Router = require('react-router'); var ContainerHomeLogs = React.createClass({ @@ -15,10 +15,10 @@ var ContainerHomeLogs = React.createClass({ }, componentDidMount: function() { this.init(); - ContainerStore.on(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs); }, componentWillUnmount: function() { - ContainerStore.removeListener(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs); }, componentDidUpdate: function () { // Scroll logs to bottom @@ -39,7 +39,7 @@ var ContainerHomeLogs = React.createClass({ return; } this.setState({ - logs: ContainerStore.logs(this.getParams().name) + logs: LogStore.logs(this.getParams().name) }); }, handleClickLogs: function () { diff --git a/src/ContainerListItem.react.js b/src/ContainerListItem.react.js index bf69e31ccd..30b7becf04 100644 --- a/src/ContainerListItem.react.js +++ b/src/ContainerListItem.react.js @@ -30,19 +30,7 @@ var ContainerListItem = React.createClass({ render: function () { var self = this; var container = this.props.container; - var downloadingImage = null, downloading = false; - var env = container.Config.Env; - if (env.length) { - var obj = _.object(env.map(function (e) { - return e.split('='); - })); - if (obj.KITEMATIC_DOWNLOADING) { - downloading = true; - } - downloadingImage = obj.KITEMATIC_DOWNLOADING_IMAGE || null; - } - - var imageName = downloadingImage || container.Config.Image; + var imageName = container.Config.Image; // Synchronize all animations var style = { @@ -50,7 +38,7 @@ var ContainerListItem = React.createClass({ }; var state; - if (downloading) { + if (container.State.Downloading) { state =
; } else if (container.State.Running && !container.State.Paused) { state =
; @@ -66,7 +54,7 @@ var ContainerListItem = React.createClass({ } return ( - +
  • {state}
    diff --git a/src/ContainerLogs.react.js b/src/ContainerLogs.react.js index e95ccd9781..a08487aac0 100644 --- a/src/ContainerLogs.react.js +++ b/src/ContainerLogs.react.js @@ -1,6 +1,6 @@ var $ = require('jquery'); var React = require('react/addons'); -var ContainerStore = require('./ContainerStore'); +var LogStore = require('./LogStore'); var Router = require('react-router'); var ContainerLogs = React.createClass({ @@ -15,10 +15,10 @@ var ContainerLogs = React.createClass({ }, componentDidMount: function() { this.init(); - ContainerStore.on(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs); }, componentWillUnmount: function() { - ContainerStore.removeListener(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs); }, componentDidUpdate: function () { // Scroll logs to bottom @@ -39,7 +39,7 @@ var ContainerLogs = React.createClass({ return; } this.setState({ - logs: ContainerStore.logs(this.getParams().name) + logs: LogStore.logs(this.getParams().name) }); }, render: function () { diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 1ec6b04b87..5d4889e130 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -1,48 +1,20 @@ -var $ = require('jquery'); var _ = require('underscore'); var EventEmitter = require('events').EventEmitter; var async = require('async'); var path = require('path'); var assign = require('object-assign'); -var Convert = require('ansi-to-html'); var docker = require('./Docker'); var registry = require('./Registry'); -var ContainerUtil = require('./ContainerUtil'); -var convert = new Convert(); -var _recommended = []; +var _placeholders = {}; var _containers = {}; var _progress = {}; -var _logs = {}; -var _streams = {}; var _muted = {}; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', - CLIENT_RECOMMENDED_EVENT: 'client_recommended_event', SERVER_CONTAINER_EVENT: 'server_container_event', SERVER_PROGRESS_EVENT: 'server_progress_event', - SERVER_LOGS_EVENT: 'server_logs_event', - _pullScratchImage: function (callback) { - var image = docker.client().getImage('scratch:latest'); - image.inspect(function (err, data) { - if (!data) { - docker.client().pull('scratch:latest', function (err, stream) { - if (err) { - callback(err); - return; - } - stream.setEncoding('utf8'); - stream.on('data', function () {}); - stream.on('end', function () { - callback(); - }); - }); - } else { - callback(); - } - }); - }, _pullImage: function (repository, tag, callback, progressCallback) { registry.layers(repository, tag, function (err, layerSizes) { @@ -89,7 +61,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var totalReceived = chunks.reduce(function (pv, sv) { return pv + sv; - }); + }, 0); var totalProgress = totalReceived / totalBytes; progressCallback(totalProgress); @@ -101,12 +73,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }); }, - _escapeHTML: function (html) { - var text = document.createTextNode(html); - var div = document.createElement('div'); - div.appendChild(text); - return div.innerHTML; - }, _createContainer: function (name, containerData, callback) { var existing = docker.client().getContainer(name); var self = this; @@ -118,14 +84,8 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { if (containerData.Config && containerData.Config.Image) { containerData.Image = containerData.Config.Image; } - existing.kill(function (err) { - if (err) { - console.log(err); - } - existing.remove(function (err) { - if (err) { - console.log(err); - } + existing.kill(function () { + existing.remove(function () { docker.client().getImage(containerData.Image).inspect(function (err, data) { if (err) { callback(err); @@ -161,31 +121,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }); }, - _createPlaceholderContainer: function (imageName, name, callback) { - var self = this; - this._pullScratchImage(function (err) { - if (err) { - callback(err); - return; - } - docker.client().createContainer({ - Image: 'scratch:latest', - Tty: false, - Env: [ - 'KITEMATIC_DOWNLOADING=true', - 'KITEMATIC_DOWNLOADING_IMAGE=' + imageName - ], - Cmd: 'placeholder', - name: name - }, function (err) { - if (err) { - callback(err); - return; - } - self.fetchContainer(name, callback); - }); - }); - }, _generateName: function (repository) { var base = _.last(repository.split('/')); var count = 1; @@ -201,18 +136,22 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } }, _resumePulling: function () { - var downloading = _.filter(_.values(_containers), function (container) { + var downloading = _.filter(_.values(this.containers()), function (container) { return container.State.Downloading; }); // Recover any pulls that were happening var self = this; downloading.forEach(function (container) { - docker.client().pull(container.KitematicDownloadingImage, function (err, stream) { + docker.client().pull(container.Config.Image, function (err, stream) { + delete _placeholders[container.Name]; + localStorage.setItem('store.placeholders', JSON.stringify(_placeholders)); stream.setEncoding('utf8'); stream.on('data', function () {}); stream.on('end', function () { - self._createContainer(container.Name, {Image: container.KitematicDownloadingImage}, function () {}); + self._createContainer(container.Name, {Image: container.Config.Image}, function () { + self.emit(self.CLIENT_CONTAINER_EVENT, container.Name); + }); }); }); }); @@ -262,13 +201,18 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } else { callback(); } + var placeholderData = JSON.parse(localStorage.getItem('store.placeholders')); + console.log(placeholderData); + console.log(_.keys(_containers)); + if (placeholderData) { + _placeholders = _.omit(placeholderData, _.keys(_containers)); + localStorage.setItem('store.placeholders', JSON.stringify(_placeholders)); + } + console.log(_placeholders); this.emit(this.CLIENT_CONTAINER_EVENT); this._resumePulling(); this._startListeningToEvents(); }.bind(this)); - this.fetchRecommended(function () { - this.emit(this.CLIENT_RECOMMENDED_EVENT); - }.bind(this)); }, fetchContainer: function (id, callback) { docker.client().getContainer(id).inspect(function (err, container) { @@ -281,15 +225,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } // Fix leading slash in container names container.Name = container.Name.replace('/', ''); - - // Add Downloading State (stored in environment variables) to containers for Kitematic - var env = ContainerUtil.env(container); - container.State.Downloading = !!env.KITEMATIC_DOWNLOADING; - container.KitematicDownloadingImage = env.KITEMATIC_DOWNLOADING_IMAGE; - - this.fetchLogs(container.Name, function () { - }.bind(this)); - _containers[container.Name] = container; callback(null, container); } @@ -311,103 +246,42 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }); }, - fetchRecommended: function (callback) { - if (_recommended.length) { - return; - } - $.ajax({ - url: 'https://kitematic.com/recommended.json', - cache: false, - dataType: 'json', - success: function (res) { - var recommended = res.repos; - async.map(recommended, function (rec, callback) { - $.get('https://registry.hub.docker.com/v1/search?q=' + rec.repo, function (data) { - var results = data.results; - var result = _.find(results, function (r) { - return r.name === rec.repo; - }); - callback(null, _.extend(result, rec)); - }); - }, function (err, results) { - _recommended = results.filter(function(r) { return !!r; }); - callback(); - }); - }, - error: function (err) { - console.log(err); - } - }); - }, - fetchLogs: function (name, callback) { - var index = 0; - var self = this; - docker.client().getContainer(name).logs({ - follow: true, - stdout: true, - stderr: true, - timestamps: true - }, function (err, stream) { - callback(err); - if (_streams[name]) { - return; - } - _streams[name] = stream; - if (err) { - return; - } - _logs[name] = []; - stream.setEncoding('utf8'); - var timeout; - stream.on('data', function (buf) { - // Every other message is a header - if (index % 2 === 1) { - //var time = buf.substr(0,buf.indexOf(' ')); - var msg = buf.substr(buf.indexOf(' ')+1); - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - timeout = setTimeout(function () { - timeout = null; - self.emit(self.SERVER_LOGS_EVENT, name); - }, 100); - _logs[name].push(convert.toHtml(self._escapeHTML(msg))); - } - index += 1; - }); - stream.on('end', function () { - delete _streams[name]; - }); - }); - }, create: function (repository, tag, callback) { tag = tag || 'latest'; var self = this; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); - // Pull image - self._createPlaceholderContainer(imageName, containerName, function (err, container) { - if (err) { - callback(err); - return; + + _placeholders[containerName] = { + Name: containerName, + Image: imageName, + Config: { + Image: imageName, + }, + State: { + Downloading: true } - _containers[containerName] = container; - self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create'); - _muted[containerName] = true; - _progress[containerName] = 0; - self._pullImage(repository, tag, function () { - self._createContainer(containerName, {Image: imageName}, function () { - delete _progress[containerName]; - _muted[containerName] = false; - self.emit(self.CLIENT_CONTAINER_EVENT, containerName); - }); - }, function (progress) { - _progress[containerName] = progress; - self.emit(self.SERVER_PROGRESS_EVENT, containerName); + }; + console.log(_placeholders); + console.log(JSON.stringify(_placeholders)); + localStorage.setItem('store.placeholders', JSON.stringify(_placeholders)); + self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create'); + + _muted[containerName] = true; + _progress[containerName] = 0; + self._pullImage(repository, tag, function () { + delete _placeholders[containerName]; + localStorage.setItem('store.placeholders', JSON.stringify(_placeholders)); + self._createContainer(containerName, {Image: imageName}, function () { + delete _progress[containerName]; + _muted[containerName] = false; + self.emit(self.CLIENT_CONTAINER_EVENT, containerName); }); - callback(null, containerName); + }, function (progress) { + _progress[containerName] = progress; + self.emit(self.SERVER_PROGRESS_EVENT, containerName); }); + callback(null, containerName); }, updateContainer: function (name, data, callback) { _muted[name] = true; @@ -428,6 +302,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, remove: function (name, callback) { + if (_placeholders[name]) { + delete _placeholders[name]; + return; + } var container = docker.client().getContainer(name); if (_containers[name].State.Paused) { container.unpause(function (err) { @@ -467,25 +345,19 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } }, containers: function() { - return _containers; + return _.extend(_containers, _placeholders); }, container: function (name) { - return _containers[name]; + return this.containers()[name]; }, sorted: function () { - return _.values(_containers).sort(function (a, b) { + return _.values(this.containers()).sort(function (a, b) { return a.Name.localeCompare(b.Name); }); }, - recommended: function () { - return _recommended; - }, progress: function (name) { return _progress[name]; }, - logs: function (name) { - return _logs[name] || []; - } }); module.exports = ContainerStore; diff --git a/src/ContainerUtil.js b/src/ContainerUtil.js index 044ba1652a..9fef385b68 100644 --- a/src/ContainerUtil.js +++ b/src/ContainerUtil.js @@ -13,6 +13,9 @@ var ContainerUtil = { })); }, ports: function (container) { + if (!container.NetworkSettings) { + return {}; + } var res = {}; var ip = docker.host; _.each(container.NetworkSettings.Ports, function (value, key) { diff --git a/src/LogStore.js b/src/LogStore.js new file mode 100644 index 0000000000..27d9f2bbe2 --- /dev/null +++ b/src/LogStore.js @@ -0,0 +1,70 @@ +var EventEmitter = require('events').EventEmitter; +var assign = require('object-assign'); +var Convert = require('ansi-to-html'); +var docker = require('./Docker'); + +var _convert = new Convert(); +var _logs = {}; +var _streams = {}; + +var LogStore = assign(Object.create(EventEmitter.prototype), { + SERVER_LOGS_EVENT: 'server_logs_event', + _escapeHTML: function (html) { + var text = document.createTextNode(html); + var div = document.createElement('div'); + div.appendChild(text); + return div.innerHTML; + }, + fetchLogs: function (name) { + if (!name || !docker.client()) { + return; + } + var index = 0; + var self = this; + docker.client().getContainer(name).logs({ + follow: true, + stdout: true, + stderr: true, + timestamps: true + }, function (err, stream) { + if (_streams[name]) { + return; + } + _streams[name] = stream; + if (err) { + return; + } + _logs[name] = []; + stream.setEncoding('utf8'); + var timeout; + stream.on('data', function (buf) { + // Every other message is a header + if (index % 2 === 1) { + //var time = buf.substr(0,buf.indexOf(' ')); + var msg = buf.substr(buf.indexOf(' ')+1); + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + timeout = setTimeout(function () { + timeout = null; + self.emit(self.SERVER_LOGS_EVENT, name); + }, 100); + _logs[name].push(_convert.toHtml(self._escapeHTML(msg))); + } + index += 1; + }); + stream.on('end', function () { + delete _streams[name]; + }); + }); + }, + logs: function (name) { + if (!_streams[name]) { + this.fetchLogs(name); + } + return _logs[name] || []; + } +}); + +module.exports = LogStore; diff --git a/src/Main.js b/src/Main.js index 69beea6499..1e490973b7 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,26 +34,14 @@ bugsnag.notifyReleaseStages = ['production']; bugsnag.appVersion = app.getVersion(); router.run(Handler => React.render(, document.body)); -if (!window.location.hash.length || window.location.hash === '#/') { - SetupStore.run().then(boot2docker.ip).then(ip => { - console.log(ip); - docker.setHost(ip); - ContainerStore.init(function (err) { - if (err) { console.log(err); } - router.transitionTo('containers'); - }); - }).catch(err => { - bugsnag.notify(err); +SetupStore.run().then(boot2docker.ip).then(ip => { + console.log(ip); + docker.setHost(ip); + ContainerStore.init(function (err) { + if (err) { console.log(err); } + router.transitionTo('containers'); }); -} else { - console.log('Skipping installer.'); - router.transitionTo('containers'); - boot2docker.ip().then(ip => { - docker.setHost(ip); - ContainerStore.init(function (err) { - if (err) { console.log(err); } - }); - }).catch(err => { - bugsnag.notify(err); - }); -} +}).catch(err => { + console.log(err); + bugsnag.notify(err); +}); diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js index 02b3a17a07..44b3b8eaf0 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainer.react.js @@ -5,13 +5,16 @@ var RetinaImage = require('react-retina-image'); var ContainerStore = require('./ContainerStore'); var Radial = require('./Radial.react'); var assign = require('object-assign'); +var Promise = require('bluebird'); + +var _recommended = []; var NewContainer = React.createClass({ _searchRequest: null, getInitialState: function () { return { query: '', - results: [], + results: _recommended, loading: false, tags: {}, active: null, @@ -23,14 +26,8 @@ var NewContainer = React.createClass({ creating: [] }); this.refs.searchInput.getDOMNode().focus(); - ContainerStore.on(ContainerStore.CLIENT_RECOMMENDED_EVENT, this.update); - this.update(); - }, - update: function () { - if (!this.state.query.length) { - this.setState({ - results: ContainerStore.recommended() - }); + if (!_recommended.length) { + this.recommended(); } }, search: function (query) { @@ -59,6 +56,40 @@ var NewContainer = React.createClass({ } }); }, + recommended: function () { + if (this._searchRequest) { + this._searchRequest.abort(); + this._searchRequest = null; + } + + if (_recommended.length) { + return; + } + Promise.resolve($.ajax({ + url: 'https://kitematic.com/recommended.json', + cache: false, + dataType: 'json', + })).then(res => res.repos).map(repo => { + return $.get('https://registry.hub.docker.com/v1/search?q=' + repo.repo).then(data => { + var results = data.results; + var result = _.find(results, function (r) { + return r.name === repo.repo; + }); + return _.extend(result, repo); + }); + }).then(results => { + _recommended = results.filter(r => !!r); + if (!this.state.query.length) { + if (this.isMounted()) { + this.setState({ + results: _recommended + }); + } + } + }).catch(err => { + console.log(err); + }); + }, handleChange: function (e) { var query = e.target.value; @@ -70,7 +101,7 @@ var NewContainer = React.createClass({ if (!query.length) { this.setState({ query: query, - results: ContainerStore.recommended() + results: _recommended }); } else { var self = this; diff --git a/src/Routes.js b/src/Routes.js index 16d82a5ca2..1571a49f6b 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -27,7 +27,7 @@ var App = React.createClass({ var routes = ( - + diff --git a/src/SetupStore.js b/src/SetupStore.js index e94b39243c..b9d1314f6a 100644 --- a/src/SetupStore.js +++ b/src/SetupStore.js @@ -165,7 +165,9 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { run: Promise.coroutine(function* () { yield this.updateBinaries(); var steps = yield this.requiredSteps(); + console.log(steps); for (let step of steps) { + console.log(step.name); _currentStep = step; step.percent = 0; while (true) { @@ -181,6 +183,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { break; } catch (err) { if (err) { + console.log(err); _error = err; this.emit(this.ERROR_EVENT); } else { diff --git a/src/Util.js b/src/Util.js index 8bc3b88458..0b036ae320 100644 --- a/src/Util.js +++ b/src/Util.js @@ -7,7 +7,6 @@ module.exports = { exec: function (args, options) { options = options || {}; return new Promise((resolve, reject) => { - console.log(options); exec(args, options, (stderr, stdout, code) => { if (code) { reject(stderr);