mirror of https://github.com/docker/docs.git
Moved log data to LogStore
This commit is contained in:
parent
33a93f6a30
commit
5110b5c144
|
@ -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 <div></div>;
|
||||
}
|
||||
|
||||
var logs = this.state.logs.map(function (l, i) {
|
||||
return <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
||||
});
|
||||
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var button;
|
||||
if (this.state.progress === 1) {
|
||||
button = <a className="btn btn-primary" onClick={this.handleClick}>View</a>;
|
||||
} else {
|
||||
button = <a className="btn btn-primary disabled" onClick={this.handleClick}>View</a>;
|
||||
}
|
||||
|
||||
var envVars = _.map(this.state.env, function (val, key) {
|
||||
return (
|
||||
<div key={key} className="keyval-row">
|
||||
<input type="text" className="key line" defaultValue={key}></input>
|
||||
<input type="text" className="val line" defaultValue={val}></input>
|
||||
<a onClick={self.handleRemoveEnvVar.bind(self, key)} className="only-icon btn btn-action small"><span className="icon icon-cross"></span></a>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
var pendingEnvVars = _.map(this.state.pendingEnv, function (val, key) {
|
||||
return (
|
||||
<div key={key} className="keyval-row">
|
||||
<input type="text" className="key line" defaultValue={key}></input>
|
||||
<input type="text" className="val line" defaultValue={val}></input>
|
||||
<a onClick={self.handleRemovePendingEnvVar.bind(self, key)} className="only-icon btn btn-action small"><span className="icon icon-arrow-undo"></span></a>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
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 (
|
||||
<div key={key} className="table-values">
|
||||
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span>
|
||||
<a className="value-right" onClick={self.handleViewLink.bind(self, val.url)}>{val.display}</a>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
var volumes = _.map(self.props.container.Volumes, function (val, key) {
|
||||
if (!val || val.indexOf(process.env.HOME) === -1) {
|
||||
val = 'No Host Folder';
|
||||
}
|
||||
return (
|
||||
<div key={key} className="table-values">
|
||||
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span>
|
||||
<a className="value-right">{val.replace(process.env.HOME, '~')}</a>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
var body;
|
||||
if (this.props.container.State.Downloading) {
|
||||
if (this.state.progress) {
|
||||
body = (
|
||||
<div className="details-progress">
|
||||
<h2>Downloading Image</h2>
|
||||
<Radial progress={Math.round(this.state.progress * 100)}/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
body = (
|
||||
<div className="details-progress">
|
||||
<h2>Connecting to Docker Hub</h2>
|
||||
<Radial spin="true" progress="90"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this.state.page === this.PAGE_HOME) {
|
||||
body = (
|
||||
<ContainerHome ports={this.state.ports} defaultPort={this.state.defaultPort} logs={logs} container={this.props.container} />
|
||||
);
|
||||
} else if (this.state.page === this.PAGE_LOGS) {
|
||||
body = (
|
||||
<div className="details-panel details-logs logs">
|
||||
{logs}
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.page === this.PAGE_PORTS) {
|
||||
body = (
|
||||
<div className="details-panel">
|
||||
<div className="ports">
|
||||
<h3>Configure Ports</h3>
|
||||
<div className="table">
|
||||
<div className="table-labels">
|
||||
<div className="label-left">DOCKER PORT</div>
|
||||
<div className="label-right">MAC PORT</div>
|
||||
</div>
|
||||
{ports}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.page === this.PAGE_VOLUMES) {
|
||||
body = (
|
||||
<div className="details-panel">
|
||||
<div className="volumes">
|
||||
<h3>Configure Volumes</h3>
|
||||
<div className="table">
|
||||
<div className="table-labels">
|
||||
<div className="label-left">DOCKER FOLDER</div>
|
||||
<div className="label-right">MAC FOLDER</div>
|
||||
</div>
|
||||
{volumes}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
var rename = (
|
||||
<div className="settings-section">
|
||||
<h3>Container Name</h3>
|
||||
<div className="container-name">
|
||||
<input id="input-container-name" type="text" className="line" placeholder="Container Name" defaultValue={this.props.container.Name}></input>
|
||||
</div>
|
||||
<a className="btn btn-action" onClick={this.handleSaveContainerName}>Save</a>
|
||||
</div>
|
||||
);
|
||||
body = (
|
||||
<div className="details-panel">
|
||||
<div className="settings">
|
||||
{rename}
|
||||
<div className="settings-section">
|
||||
<h3>Environment Variables</h3>
|
||||
<div className="env-vars-labels">
|
||||
<div className="label-key">KEY</div>
|
||||
<div className="label-val">VALUE</div>
|
||||
</div>
|
||||
<div className="env-vars">
|
||||
{envVars}
|
||||
{pendingEnvVars}
|
||||
<div className="keyval-row">
|
||||
<input id="new-env-key" type="text" className="key line"></input>
|
||||
<input id="new-env-val" type="text" className="val line"></input>
|
||||
<a onClick={this.handleAddPendingEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add-1"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn btn-action" onClick={this.handleSaveEnvVar}>Save</a>
|
||||
</div>
|
||||
<div className="settings-section">
|
||||
<h3>Delete Container</h3>
|
||||
<a className="btn btn-action" onClick={this.handleDeleteContainer}>Delete Container</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<div key={key} className="table-values">
|
||||
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span>
|
||||
<a className="value-right" onClick={self.handleViewLink.bind(self, val.url)}>{val.display}</a>
|
||||
<input onChange={self.handleChangeDefaultPort.bind(self, key)} type="checkbox" checked={self.state.defaultPort === key}/> <label>Default</label>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
var volumes = _.map(self.props.container.Volumes, function (val, key) {
|
||||
if (!val || val.indexOf(process.env.HOME) === -1) {
|
||||
val = <span>No folder<a className="btn btn-primary btn-xs" onClick={self.handleChooseVolumeClick.bind(self, key)}>Choose</a></span>;
|
||||
} else {
|
||||
val = <span><a className="value-right" onClick={self.handleOpenVolumeClick.bind(self, val)}>{val.replace(process.env.HOME, '~')}</a> <a className="btn btn-primary btn-xs" onClick={self.handleChooseVolumeClick.bind(self, key)}>Choose</a></span>;
|
||||
}
|
||||
return (
|
||||
<div key={key} className="table-values">
|
||||
<span className="value-left">{key}</span><span className="icon icon-arrow-right"></span>
|
||||
{val}
|
||||
</div>
|
||||
);
|
||||
});*/
|
||||
|
||||
/* var view;
|
||||
if (this.state.defaultPort) {
|
||||
view = (
|
||||
<div className="action btn-group">
|
||||
<a className={viewButtonClass} onClick={this.handleView}><span className="icon icon-preview-2"></span><span className="content">View</span></a>
|
||||
<a className={dropdownViewButtonClass} onClick={this.handleViewDropdown}><span className="icon-dropdown icon icon-arrow-37"></span></a>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
view = (
|
||||
<div className="action">
|
||||
<a className={dropdownViewButtonClass} onClick={this.handleViewDropdown}><span className="icon icon-preview-2"></span> <span className="content">Ports</span> <span className="icon-dropdown icon icon-arrow-37"></span></a>
|
||||
</div>
|
||||
);
|
||||
}*/
|
||||
|
||||
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 (
|
||||
<div className="details">
|
||||
<ContainerDetailsHeader container={this.props.container} />
|
||||
<div className="details-subheader">
|
||||
<div className="details-header-actions">
|
||||
<div className={runActionClass} onMouseEnter={this.handleItemMouseEnterRun} onMouseLeave={this.handleItemMouseLeaveRun}>
|
||||
<span className="action-icon" onClick={this.handleRun}><RetinaImage src="button-run.png"/></span>
|
||||
<span className="btn-label run">Run</span>
|
||||
</div>
|
||||
<div className={restartActionClass} onMouseEnter={this.handleItemMouseEnterRestart} onMouseLeave={this.handleItemMouseLeaveRestart}>
|
||||
<span className="action-icon" onClick={this.handleRestart}><RetinaImage src="button-restart.png"/></span>
|
||||
<span className="btn-label restart">Restart</span>
|
||||
</div>
|
||||
<div className={terminalActionClass} onMouseEnter={this.handleItemMouseEnterTerminal} onMouseLeave={this.handleItemMouseLeaveTerminal}>
|
||||
<span className="action-icon" onClick={this.handleTerminal}><RetinaImage src="button-terminal.png"/></span>
|
||||
<span className="btn-label terminal">Terminal</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="details-subheader-tabs">
|
||||
<span className={tabHomeClasses} onClick={this.showHome}>Home</span>
|
||||
<span className={tabLogsClasses} onClick={this.showLogs}>Logs</span>
|
||||
<span className={tabSettingsClasses} onClick={this.showSettings}>Settings</span>
|
||||
</div>
|
||||
</div>
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ContainerDetailsbak;
|
|
@ -90,7 +90,7 @@ var ContainerHome = React.createClass({
|
|||
<ContainerHomePreview />
|
||||
</div>
|
||||
<div className="right">
|
||||
<ContainerHomeLogs />
|
||||
<ContainerHomeLogs/>
|
||||
<ContainerHomeFolders container={this.props.container} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,7 +116,7 @@ var ContainerHome = React.createClass({
|
|||
<div className="details-panel home">
|
||||
<div className="content">
|
||||
<div className="left">
|
||||
<ContainerHomeLogs />
|
||||
<ContainerHomeLogs/>
|
||||
</div>
|
||||
{right}
|
||||
</div>
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -1,26 +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 _containers = {};
|
||||
var _progress = {};
|
||||
var _logs = {};
|
||||
var _streams = {};
|
||||
var _muted = {};
|
||||
|
||||
var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||
CLIENT_CONTAINER_EVENT: 'client_container_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) {
|
||||
|
@ -99,12 +93,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;
|
||||
|
@ -282,9 +270,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
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);
|
||||
}
|
||||
|
@ -306,48 +291,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
});
|
||||
});
|
||||
},
|
||||
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;
|
||||
|
@ -444,15 +387,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
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;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
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, 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];
|
||||
});
|
||||
});
|
||||
},
|
||||
logs: function (name) {
|
||||
if (!_streams[name]) {
|
||||
this.fetchLogs(name, () => {});
|
||||
}
|
||||
return _logs[name] || [];
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = LogStore;
|
33
src/Main.js
33
src/Main.js
|
@ -33,27 +33,14 @@ bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' :
|
|||
bugsnag.notifyReleaseStages = ['production'];
|
||||
bugsnag.appVersion = app.getVersion();
|
||||
|
||||
router.run(Handler => React.render(<Handler/>, 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.run(Handler => React.render(<Handler/>, document.body));
|
||||
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 => {
|
||||
bugsnag.notify(err);
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ var NewContainer = React.createClass({
|
|||
getInitialState: function () {
|
||||
return {
|
||||
query: '',
|
||||
results: [],
|
||||
results: _recommended,
|
||||
loading: false,
|
||||
tags: {},
|
||||
active: null,
|
||||
|
@ -26,7 +26,9 @@ var NewContainer = React.createClass({
|
|||
creating: []
|
||||
});
|
||||
this.refs.searchInput.getDOMNode().focus();
|
||||
this.recommended();
|
||||
if (!_recommended.length) {
|
||||
this.recommended();
|
||||
}
|
||||
},
|
||||
search: function (query) {
|
||||
if (this._searchRequest) {
|
||||
|
@ -67,11 +69,7 @@ var NewContainer = React.createClass({
|
|||
url: 'https://kitematic.com/recommended.json',
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
})).then(res => {
|
||||
console.log(res);
|
||||
return res.repos;
|
||||
}).map(repo => {
|
||||
console.log(repo);
|
||||
})).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) {
|
||||
|
@ -82,9 +80,11 @@ var NewContainer = React.createClass({
|
|||
}).then(results => {
|
||||
_recommended = results.filter(r => !!r);
|
||||
if (!this.state.query.length) {
|
||||
this.setState({
|
||||
results: _recommended
|
||||
});
|
||||
if (this.isMounted()) {
|
||||
this.setState({
|
||||
results: _recommended
|
||||
});
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue