mirror of https://github.com/docker/docs.git
Merge branch 'master' into hub-button
This commit is contained in:
commit
53cf2b696b
|
@ -1,7 +1,7 @@
|
||||||
[](https://travis-ci.org/kitematic/kitematic)
|
[](https://travis-ci.org/kitematic/kitematic)
|
||||||
[](https://coveralls.io/r/kitematic/kitematic?branch=master)
|
[](https://coveralls.io/r/kitematic/kitematic?branch=master)
|
||||||
[](https://www.bithound.io/github/kitematic/kitematic)
|
[](https://www.bithound.io/github/kitematic/kitematic)
|
||||||
[](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ Please read through our [Contributing Guidelines](https://github.com/kitematic/k
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
- For questions on how to use Kitematic, see our [user forum](https://forums.docker.com/c/kitematic).
|
- [](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
- Ask questions on our [user forum](https://forums.docker.com/c/kitematic).
|
||||||
- **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4).
|
- **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4).
|
||||||
- Join the Kitematic [Gitter Channel](https://gitter.im/kitematic/kitematic)
|
|
||||||
- Follow [@kitematic on Twitter](https://twitter.com/kitematic).
|
- Follow [@kitematic on Twitter](https://twitter.com/kitematic).
|
||||||
- Read and subscribe to [the Kitematic Blog](http://blog.kitematic.com).
|
- Read and subscribe to [the Kitematic Blog](http://blog.kitematic.com).
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ var ContainerHome = React.createClass({
|
||||||
<ContainerHomePreview />
|
<ContainerHomePreview />
|
||||||
</div>
|
</div>
|
||||||
<div className="right">
|
<div className="right">
|
||||||
<ContainerHomeLogs/>
|
<ContainerHomeLogs container={this.props.container}/>
|
||||||
<ContainerHomeFolders container={this.props.container} />
|
<ContainerHomeFolders container={this.props.container} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -138,7 +138,7 @@ var ContainerHome = React.createClass({
|
||||||
<div className="details-panel home">
|
<div className="details-panel home">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<ContainerHomeLogs/>
|
<ContainerHomeLogs container={this.props.container}/>
|
||||||
</div>
|
</div>
|
||||||
{right}
|
{right}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,51 +6,50 @@ var metrics = require('./Metrics');
|
||||||
|
|
||||||
var _prevBottom = 0;
|
var _prevBottom = 0;
|
||||||
|
|
||||||
var ContainerHomeLogs = React.createClass({
|
module.exports = React.createClass({
|
||||||
mixins: [Router.State, Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
logs: []
|
logs: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillReceiveProps: function () {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.init();
|
if (!this.props.container) {
|
||||||
LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs);
|
return;
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
this.scrollToBottom();
|
||||||
|
LogStore.on(LogStore.SERVER_LOGS_EVENT, this.update);
|
||||||
|
LogStore.fetch(this.props.container.Name);
|
||||||
},
|
},
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs);
|
LogStore.detach(this.props.container.Name);
|
||||||
|
LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.update);
|
||||||
},
|
},
|
||||||
componentDidUpdate: function () {
|
componentDidUpdate: function () {
|
||||||
// Scroll logs to bottom
|
this.scrollToBottom();
|
||||||
|
},
|
||||||
|
scrollToBottom: function () {
|
||||||
var parent = $('.logs');
|
var parent = $('.logs');
|
||||||
if (parent.scrollTop() >= _prevBottom - 50) {
|
if (parent.scrollTop() >= _prevBottom - 50) {
|
||||||
parent.scrollTop(parent[0].scrollHeight - parent.height());
|
parent.scrollTop(parent[0].scrollHeight - parent.height());
|
||||||
}
|
}
|
||||||
_prevBottom = parent[0].scrollHeight - parent.height();
|
_prevBottom = parent[0].scrollHeight - parent.height();
|
||||||
},
|
},
|
||||||
init: function () {
|
|
||||||
this.updateLogs();
|
|
||||||
},
|
|
||||||
updateLogs: function (name) {
|
|
||||||
if (name && name !== this.getParams().name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
logs: LogStore.logs(this.getParams().name)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleClickLogs: function () {
|
handleClickLogs: function () {
|
||||||
metrics.track('Viewed Logs', {
|
metrics.track('Viewed Logs', {
|
||||||
from: 'preview'
|
from: 'preview'
|
||||||
});
|
});
|
||||||
this.transitionTo('containerLogs', {name: this.getParams().name});
|
this.transitionTo('containerLogs', {name: this.props.container.Name});
|
||||||
|
},
|
||||||
|
update: function () {
|
||||||
|
this.setState({
|
||||||
|
logs: LogStore.logs(this.props.container.Name)
|
||||||
|
});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var logs = this.state.logs.map(function (l, i) {
|
var logs = this.state.logs.map(function (l, i) {
|
||||||
return <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
return <span key={i} dangerouslySetInnerHTML={{__html: l}}></span>;
|
||||||
});
|
});
|
||||||
if (logs.length === 0) {
|
if (logs.length === 0) {
|
||||||
logs = "No logs for this container.";
|
logs = "No logs for this container.";
|
||||||
|
@ -62,11 +61,8 @@ var ContainerHomeLogs = React.createClass({
|
||||||
<div className="logs">
|
<div className="logs">
|
||||||
{logs}
|
{logs}
|
||||||
</div>
|
</div>
|
||||||
<div className="mini-logs-overlay" onClick={this.handleClickLogs}><span className="icon icon-scale-spread-1"></span><div className="text">View Logs</div></div>
|
<div className="mini-logs-overlay" onClick={this.handleClickLogs}><span className="icon icon-scale-spread-1"></span><div className="text">View Logs</div></div> </div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = ContainerHomeLogs;
|
|
||||||
|
|
|
@ -1,49 +1,46 @@
|
||||||
var $ = require('jquery');
|
var $ = require('jquery');
|
||||||
var React = require('react/addons');
|
var React = require('react/addons');
|
||||||
var LogStore = require('./LogStore');
|
var LogStore = require('./LogStore');
|
||||||
var Router = require('react-router');
|
|
||||||
|
|
||||||
var _prevBottom = 0;
|
var _prevBottom = 0;
|
||||||
|
|
||||||
var ContainerLogs = React.createClass({
|
module.exports = React.createClass({
|
||||||
mixins: [Router.State],
|
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
logs: []
|
logs: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillReceiveProps: function () {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.init();
|
if (!this.props.container) {
|
||||||
LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs);
|
return;
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
this.scrollToBottom();
|
||||||
|
LogStore.on(LogStore.SERVER_LOGS_EVENT, this.update);
|
||||||
|
LogStore.fetch(this.props.container.Name);
|
||||||
},
|
},
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs);
|
LogStore.detach(this.props.container.Name);
|
||||||
|
LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.update);
|
||||||
},
|
},
|
||||||
componentDidUpdate: function () {
|
componentDidUpdate: function () {
|
||||||
// Scroll logs to bottom
|
this.scrollToBottom();
|
||||||
|
},
|
||||||
|
scrollToBottom: function () {
|
||||||
var parent = $('.details-logs');
|
var parent = $('.details-logs');
|
||||||
if (parent.scrollTop() >= _prevBottom - 50) {
|
if (parent.scrollTop() >= _prevBottom - 50) {
|
||||||
parent.scrollTop(parent[0].scrollHeight - parent.height());
|
parent.scrollTop(parent[0].scrollHeight - parent.height());
|
||||||
}
|
}
|
||||||
_prevBottom = parent[0].scrollHeight - parent.height();
|
_prevBottom = parent[0].scrollHeight - parent.height();
|
||||||
},
|
},
|
||||||
init: function () {
|
update: function () {
|
||||||
this.updateLogs();
|
|
||||||
},
|
|
||||||
updateLogs: function (name) {
|
|
||||||
if (name && name !== this.getParams().name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
this.setState({
|
||||||
logs: LogStore.logs(this.getParams().name)
|
logs: LogStore.logs(this.props.container.Name)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var logs = this.state.logs.map(function (l, i) {
|
var logs = this.state.logs.map(function (l, i) {
|
||||||
return <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
return <span key={i} dangerouslySetInnerHTML={{__html: l}}></span>;
|
||||||
});
|
});
|
||||||
if (logs.length === 0) {
|
if (logs.length === 0) {
|
||||||
logs = "No logs for this container.";
|
logs = "No logs for this container.";
|
||||||
|
@ -55,5 +52,3 @@ var ContainerLogs = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = ContainerLogs;
|
|
||||||
|
|
|
@ -2,59 +2,77 @@ var EventEmitter = require('events').EventEmitter;
|
||||||
var assign = require('object-assign');
|
var assign = require('object-assign');
|
||||||
var Convert = require('ansi-to-html');
|
var Convert = require('ansi-to-html');
|
||||||
var docker = require('./Docker');
|
var docker = require('./Docker');
|
||||||
|
var stream = require('stream');
|
||||||
|
|
||||||
var _convert = new Convert();
|
var _convert = new Convert();
|
||||||
var _logs = {};
|
var _logs = {};
|
||||||
var _streams = {};
|
var _streams = {};
|
||||||
|
|
||||||
var LogStore = assign(Object.create(EventEmitter.prototype), {
|
var MAX_LOG_SIZE = 3000;
|
||||||
|
|
||||||
|
module.exports = assign(Object.create(EventEmitter.prototype), {
|
||||||
SERVER_LOGS_EVENT: 'server_logs_event',
|
SERVER_LOGS_EVENT: 'server_logs_event',
|
||||||
_escapeHTML: function (html) {
|
_escape: function (html) {
|
||||||
var text = document.createTextNode(html);
|
var text = document.createTextNode(html);
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.appendChild(text);
|
div.appendChild(text);
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
},
|
},
|
||||||
fetchLogs: function (name) {
|
fetch: function (name) {
|
||||||
if (!name || !docker.client()) {
|
if (!name || !docker.client()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var index = 0;
|
|
||||||
var self = this;
|
|
||||||
docker.client().getContainer(name).logs({
|
docker.client().getContainer(name).logs({
|
||||||
follow: true,
|
|
||||||
stdout: true,
|
stdout: true,
|
||||||
stderr: true,
|
stderr: true,
|
||||||
timestamps: true
|
timestamps: false,
|
||||||
}, function (err, stream) {
|
tail: MAX_LOG_SIZE,
|
||||||
if (_streams[name]) {
|
follow: false
|
||||||
return;
|
}, (err, logStream) => {
|
||||||
}
|
|
||||||
_streams[name] = stream;
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return;
|
throw err;
|
||||||
}
|
}
|
||||||
_logs[name] = [];
|
var logs = [];
|
||||||
stream.setEncoding('utf8');
|
logStream.setEncoding('utf-8');
|
||||||
stream.on('data', function (buf) {
|
logStream.on('data', (chunk) => {
|
||||||
// Every other message is a header
|
logs.push(_convert.toHtml(this._escape(chunk)));
|
||||||
if (index % 2 === 1) {
|
|
||||||
//var time = buf.substr(0,buf.indexOf(' '));
|
|
||||||
var msg = buf.substr(buf.indexOf(' ')+1);
|
|
||||||
_logs[name].push(_convert.toHtml(self._escapeHTML(msg)));
|
|
||||||
self.emit(self.SERVER_LOGS_EVENT);
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
});
|
});
|
||||||
stream.on('end', function () {
|
logStream.on('end', () => {
|
||||||
delete _streams[name];
|
_logs[name] = logs;
|
||||||
|
this.emit(this.SERVER_LOGS_EVENT);
|
||||||
|
this.attach(name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
logs: function (name) {
|
attach: function (name) {
|
||||||
if (!_streams[name]) {
|
if (!name || !docker.client() || _streams[name]) {
|
||||||
this.fetchLogs(name);
|
return;
|
||||||
}
|
}
|
||||||
|
docker.client().getContainer(name).attach({
|
||||||
|
stdout: true,
|
||||||
|
stderr: true,
|
||||||
|
logs: false,
|
||||||
|
stream: true
|
||||||
|
}, (err, logStream) => {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
logStream.setEncoding('utf-8');
|
||||||
|
logStream.on('data', (chunk) => {
|
||||||
|
_logs[name].push(_convert.toHtml(this._escape(chunk)));
|
||||||
|
if (_logs[name].length > MAX_LOG_SIZE) {
|
||||||
|
_logs[name] = _logs[name].slice(_logs[name].length - MAX_LOG_SIZE, MAX_LOG_SIZE);
|
||||||
|
}
|
||||||
|
this.emit(this.SERVER_LOGS_EVENT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
detach: function (name) {
|
||||||
|
if (_streams[name]) {
|
||||||
|
_streams[name].destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logs: function (name) {
|
||||||
return _logs[name] || [];
|
return _logs[name] || [];
|
||||||
},
|
},
|
||||||
rename: function (name, newName) {
|
rename: function (name, newName) {
|
||||||
|
@ -63,5 +81,3 @@ var LogStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = LogStore;
|
|
||||||
|
|
Loading…
Reference in New Issue