Change unique container key to name from Id

This commit is contained in:
Jeffrey Morgan 2015-01-19 12:19:40 -05:00
Родитель fa41982603
Коммит f34e23b7a0
8 изменённых файлов: 197 добавлений и 369 удалений

Просмотреть файл

@ -8,8 +8,8 @@ var Link = Router.Link;
var RouteHandler = Router.RouteHandler;
var Convert = require('ansi-to-html');
var convert = new Convert();
var ContainerStore = require('./ContainerStore.js');
var docker = require('./docker.js');
var ContainerStore = require('./ContainerStore');
var docker = require('./docker');
var ContainerDetails = React.createClass({
mixins: [Router.State],
@ -18,12 +18,12 @@ var ContainerDetails = React.createClass({
logs: []
};
},
componentWillMount: function () {
componentWillReceiveProps: function () {
this.update();
var self = this;
var logs = [];
var index = 0;
docker.client().getContainer(this.getParams().Id).logs({
docker.client().getContainer(this.getParams().name).logs({
follow: false,
stdout: true,
timestamps: true
@ -40,7 +40,50 @@ var ContainerDetails = React.createClass({
});
stream.on('end', function (buf) {
self.setState({logs: logs});
docker.client().getContainer(self.getParams().Id).logs({
docker.client().getContainer(self.getParams().name).logs({
follow: true,
stdout: true,
timestamps: true,
tail: 0
}, function (err, stream) {
stream.setEncoding('utf8');
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);
logs.push(convert.toHtml(self._escapeHTML(msg)));
self.setState({logs: logs});
}
index += 1;
});
});
});
});
},
componentWillMount: function () {
this.update();
var self = this;
var logs = [];
var index = 0;
docker.client().getContainer(this.getParams().name).logs({
follow: false,
stdout: true,
timestamps: true
}, function (err, stream) {
stream.setEncoding('utf8');
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);
logs.push(convert.toHtml(self._escapeHTML(msg)));
}
index += 1;
});
stream.on('end', function (buf) {
self.setState({logs: logs});
docker.client().getContainer(self.getParams().name).logs({
follow: true,
stdout: true,
timestamps: true,
@ -68,9 +111,9 @@ var ContainerDetails = React.createClass({
ContainerStore.removeChangeListener(this.update);
},
update: function () {
var containerId = this.getParams().Id;
var containerName = this.getParams().name;
this.setState({
container: ContainerStore.containers()[containerId]
container: ContainerStore.containers()[containerName]
});
},
_escapeHTML: function (html) {

Просмотреть файл

@ -3,7 +3,7 @@ var Router = require('react-router');
var Modal = require('react-bootstrap/Modal');
var RetinaImage = require('react-retina-image');
var $ = require('jquery');
var ContainerStore = require('./ContainerStore.js');
var ContainerStore = require('./ContainerStore');
var ContainerModal = React.createClass({
getInitialState: function () {
@ -28,7 +28,6 @@ var ContainerModal = React.createClass({
if (query === this.state.query) {
return;
}
clearTimeout(this.timeout);
var self = this;
this.timeout = setTimeout(function () {
@ -37,7 +36,8 @@ var ContainerModal = React.createClass({
},
handleClick: function (event) {
var name = event.target.getAttribute('name');
ContainerStore.create(name);
ContainerStore.create(name, 'latest', function (err, containerName) {
});
},
render: function () {
var top = this.state.results.splice(0, 7);

Просмотреть файл

@ -42,6 +42,7 @@ var ContainerStore = assign(EventEmitter.prototype, {
docker.client().getEvents(function (err, stream) {
stream.setEncoding('utf8');
stream.on('data', function (data) {
console.log(data);
// TODO: Make
self.update(function (err) {
@ -69,10 +70,9 @@ var ContainerStore = assign(EventEmitter.prototype, {
}
var containers = {};
results.map(function (r) {
containers[r.Id] = r;
containers[r.Name.replace('/', '')] = r;
});
self._containers = containers;
console.log(containers);
self.emit('change');
callback(null);
});
@ -157,6 +157,10 @@ var ContainerStore = assign(EventEmitter.prototype, {
}
}
},
// Returns all shoes
containers: function() {
return this._containers;
},
create: function (repository, tag, callback) {
console.log('create', repository, tag);
@ -182,6 +186,8 @@ var ContainerStore = assign(EventEmitter.prototype, {
}
console.log('Placeholder container created.');
docker.client().pull(imageName, function (err, stream) {
console.log(containerName);
callback(null, containerName);
stream.setEncoding('utf8');
stream.on('data', function (data) {
console.log(data);
@ -197,6 +203,7 @@ var ContainerStore = assign(EventEmitter.prototype, {
} else {
// If not then directly create the container
self._createContainer(imageName, containerName, function () {
callback(null, containerName);
console.log('done');
});
}
@ -205,19 +212,15 @@ var ContainerStore = assign(EventEmitter.prototype, {
// Pull image
// When image is done pulling then
},
// Returns all shoes
containers: function() {
return this._containers;
logs: function (containerName) {
return logs[containerId];
},
addChangeListener: function(callback) {
this.on('change', callback);
},
removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
},
});
module.exports = ContainerStore;

Просмотреть файл

@ -1,20 +1,18 @@
var React = require('react');
var React = require('react/addons');
var Router = require('react-router');
var Modal = require('react-bootstrap/Modal');
var RetinaImage = require('react-retina-image');
var ModalTrigger = require('react-bootstrap/ModalTrigger');
var ContainerModal = require('./ContainerModal.react.js');
var ContainerStore = require('./ContainerStore.js');
var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var ContainerModal = require('./ContainerModal.react');
var ContainerStore = require('./ContainerStore');
var Header = require('./Header.react');
var async = require('async');
var _ = require('underscore');
var docker = require('./docker');
var Link = Router.Link;
var RouteHandler = Router.RouteHandler;
var Navigation= Router.Navigation;
var Header = require('./Header.react.js');
var async = require('async');
var _ = require('underscore');
var docker = require('./docker.js');
var ContainerList = React.createClass({
mixins: [Navigation],
@ -22,15 +20,12 @@ var ContainerList = React.createClass({
return {
containers: []
};
},
handleClick: function () {
},
componentDidMount: function () {
this.update();
ContainerStore.addChangeListener(this.update);
if (this.state.containers.length > 0) {
this.transitionTo('container', {Id: this.state.containers[0].Id});
if (this.state.active) {
this.transitionTo('container', {name: this.state.active});
}
},
componentWillMount: function () {
@ -43,8 +38,16 @@ var ContainerList = React.createClass({
var containers = _.values(ContainerStore.containers()).sort(function (a, b) {
return a.Name.localeCompare(b.Name);
});
var state = {};
if (!this.state.active && containers.length > 0) {
state.active = containers[0].Name.replace('/', '');
}
state.containers = containers;
this.setState(state);
},
handleClick: function (containerId) {
this.setState({
containers: containers
active: containerId
});
},
render: function () {
@ -85,8 +88,9 @@ var ContainerList = React.createClass({
} else {
state = <div className="state state-stopped"></div>;
}
return (
<Link key={container.Id} to="container" params={{Id: container.Id}} onClick={this.handleClick}>
<Link key={container.Name.replace('/', '')} to="container" params={{name: container.Name.replace('/', '')}} onClick={self.handleClick.bind(self, container.Id)}>
<li>
{state}
<div className="info">
@ -126,9 +130,6 @@ var Containers = React.createClass({
});
}
},
handleClick: function () {
ContainerStore.create('dockerfile/ghost', 'latest', 'testghost');
},
render: function () {
var sidebarHeaderClass = 'sidebar-header';
if (this.state.sidebarOffset) {
@ -140,7 +141,7 @@ var Containers = React.createClass({
<div className="containers-body">
<div className="sidebar">
<section className={sidebarHeaderClass}>
<h3 onClick={this.handleClick}>containers</h3>
<h3>containers</h3>
<div className="create">
<ModalTrigger modal={<ContainerModal/>}>
<div className="wrapper">

Просмотреть файл

@ -1,12 +1,6 @@
var React = require('react');
var Router = require('react-router');
var RetinaImage = require('react-retina-image');
var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
var RouteHandler = Router.RouteHandler;
var Raven = require('raven');
var async = require('async');
var docker = require('./docker.js');
@ -15,7 +9,13 @@ var Setup = require('./Setup.react');
var Containers = require('./Containers.react');
var ContainerDetails = require('./ContainerDetails.react');
var ContainerStore = require('./ContainerStore.js');
var Radial = require('./Radial.react');
var Radial = require('./Radial.react.js');
var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
var RouteHandler = Router.RouteHandler;
var NoContainers = React.createClass({
render: function () {
@ -38,7 +38,7 @@ var App = React.createClass({
var routes = (
<Route name="app" path="/" handler={App}>
<Route name="containers" handler={Containers}>
<Route name="container" path=":Id" handler={ContainerDetails}>
<Route name="container" path=":name" handler={ContainerDetails}>
</Route>
<DefaultRoute handler={NoContainers}/>
</Route>
@ -48,17 +48,19 @@ var routes = (
</Route>
);
Router.run(routes, function (Handler) {
boot2docker.ip(function (err, ip) {
boot2docker.ip(function (err, ip) {
if (!err) {
docker.setHost(ip);
ContainerStore.init(function () {
Router.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
});
});
} else {
Router.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
}
});
}
});
if (process.env.NODE_ENV !== 'development') {

Просмотреть файл

@ -1,71 +1,3 @@
@import "bootstrap/bootstrap.less";
@import "clearsans.less";
@import "theme.less";
@import "icons.less";
@import "retina.less";
@import "setup.less";
@import "radial.less";
.buttons {
display: inline-block;
position: relative;
top: 16px;
left: 20px;
&:hover {
.button-minimize.enabled {
.at2x('minimize.png', 10px, 10px);
}
.button-close.enabled {
.at2x('close.png', 10px, 10px);
}
.button-fullscreen.enabled {
.at2x('fullscreen.png', 10px, 10px);
}
.button-fullscreenclose.enabled {
.at2x('fullscreenclose.png', 10px, 10px);
}
}
.button {
box-sizing: border-box;
display: inline-block;
background: white;
margin-right: 9px;
height: 12px;
width: 12px;
border: 1px solid #CCD3D5;
border-radius: 6px;
box-shadow: 0px 1px 1px 0px rgba(234,234,234,0.50);
-webkit-app-region: no-drag;
&.disabled {
border: 1px solid #E8EEEF;
}
&.enabled:hover {
box-shadow: 0px 1px 1px 0px rgba(195,198,201,0.50);
}
&.enabled:hover:active {
cursor: default;
-webkit-filter: brightness(92%);
}
}
}
.header {
min-width: 100%;
flex: 0;
min-height: 48px;
-webkit-app-region: drag;
-webkit-user-select: none;
&.no-drag {
-webkit-app-region: no-drag;
}
}
.containers {
height: 100%;
display: flex;
@ -78,20 +10,21 @@
.sidebar {
display: flex;
flex-direction: column;
min-width: 240px;
min-width: 260px;
margin: 0;
box-sizing: border-box;
border-right: 1px solid #eee;
.sidebar-header {
flex: 0 auto;
min-width: 240px;
min-width: 260px;
display: flex;
border-bottom: 1px solid transparent;
transition: border-bottom 0.25s;
&.sep {
border-bottom: 1px solid #eee;
box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.03);
}
h3 {
@ -148,7 +81,7 @@
ul {
padding: 0;
margin: 0;
min-width: 240px;
min-width: 260px;
position: absolute;
top: 0;
bottom: 0;
@ -163,23 +96,18 @@
flex-shrink: 0;
cursor: default;
/*&:hover {
&.active {
background: #eee;
border-bottom: none;
&:hover {
}
}
&:hover {
text-decoration: none;
cursor: default;
background: @brand-primary;
li {
border-bottom: none;
}
li > .info > .name {
color: #fff;
}
li > .info > .image {
color: #fff;
}
}*/
&:focus {
text-decoration: none;
}
@ -187,10 +115,8 @@
li {
vertical-align: middle;
margin: 14px 24px 0px;
border-bottom: 1px solid #efefef;
padding-bottom: 14px;
margin: 16px 24px 0px;
display: flex;
flex-direction: row;
@ -198,18 +124,18 @@
.info {
font-size: 13px;
margin-left: 12px;
margin-left: 16px;
.name {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-size: 13px;
font-size: 14px;
font-weight: 400;
color: #555;
}
.image {
color: #999;
font-size: 10px;
font-size: 12px;
font-weight: 400;
text-overflow: ellipsis;
white-space: nowrap;
@ -218,7 +144,7 @@
}
.state {
margin-top: 8px;
margin-top: 9px;
display: inline-block;
position: relative;
min-width: 20px;
@ -349,200 +275,3 @@
}
}
}
html, body {
height: 100%;
width: 100%;
overflow: hidden;
-webkit-font-smoothing: antialiased;
user-select: none;
font-family: 'Clear Sans', sans-serif;
}
::-webkit-scrollbar {
width: 13px;
}
::-webkit-scrollbar-track {
margin: 3px;
-webkit-border-radius: 5px;
border-radius: 5px;
background: none;
}
::-webkit-scrollbar-thumb {
border: 3px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
width: 7px;
border-radius: 8px;
background-color: rgba(0,0,0,0.2);
}
.create-modal {
@modal-padding: 32px;
@search-width: 372px;
@custom-width: 270px;
.modal-dialog {
margin-top: 8%;
width: calc(@modal-padding + @search-width + 2 * @modal-padding + @custom-width);
}
.modal-content {
//box-shadow: 0 3px 15px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.10);
border: none; //1px solid #ccc;
height: 610px;
}
.modal-body {
display: flex;
flex-direction: row;
padding: 32px 32px;
.title {
color: #CCD3D5;
font-weight: 400;
font-size: 13px;
}
aside.custom {
flex: 0 auto;
padding-left: 32px;
min-width: 270px;
}
section.search {
flex: 0 auto;
min-width: 404px;
padding-right: 32px;
border-right: 1px solid #eee;
.question {
a {
color: #CCD3D5;
}
font-size: 10px;
text-align: right;
}
input {
border-radius: 20px;
font-size: 13px;
height: 38px;
padding: 8px 16px;
font-weight: 400;
color: #666;
&:focus {
box-shadow: none;
border-color: #bbb;
}
&::-webkit-input-placeholder {
color: #ddd;
font-weight: 300;
}
}
.results {
overflow: auto;
.title {
margin-top: 16px;
}
ul {
list-style: none;
color: #555;
padding: 0;
li {
display: flex;
flex-direction: row;
margin: 12px;
border-bottom: 1px solid #eee;
.info {
.name {
max-width: 278px;
img {
margin-right: 6px;
margin-left: 2px;
}
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.stars {
color: #A7A7A7;
margin-top: 2px;
.star-count {
font-size: 10px;
display: inline-block;
position: relative;
top: -3px;
left: 1px;
height: 17px;
}
.icon {
overflow: hidden;
display: inline-block;
font-size: 15px;
height: 15px;
}
}
flex: 0 auto;
}
.action {
text-align: right;
flex: 1 auto;
}
}
}
}
}
}
}
.modal-backdrop.in {
background: rgba(227,230,230,0.95);
opacity: 1;
height: 100%;
}
@-webkit-keyframes translatedownload {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
@-webkit-keyframes translatewave {
from {
-webkit-transform: translateX(0px);
}
to {
-webkit-transform: translateX(20px);
}
}
@-webkit-keyframes translatedownload {
0% {
-webkit-transform: translateY(6px);
opacity: 0;
}
25% {
opacity: 1;
-webkit-transform: translateY(6px);
}
50% {
opacity: 1;
-webkit-transform: translateY(20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(20px);
}
}

61
app/styles/header.less Normal file
Просмотреть файл

@ -0,0 +1,61 @@
@import "bootstrap/bootstrap.less";
.header {
min-width: 100%;
flex: 0;
min-height: 48px;
-webkit-app-region: drag;
-webkit-user-select: none;
&.no-drag {
-webkit-app-region: no-drag;
}
.buttons {
display: inline-block;
position: relative;
top: 16px;
left: 20px;
&:hover {
.button-minimize.enabled {
.at2x('minimize.png', 10px, 10px);
}
.button-close.enabled {
.at2x('close.png', 10px, 10px);
}
.button-fullscreen.enabled {
.at2x('fullscreen.png', 10px, 10px);
}
.button-fullscreenclose.enabled {
.at2x('fullscreenclose.png', 10px, 10px);
}
}
.button {
box-sizing: border-box;
display: inline-block;
background: white;
margin-right: 9px;
height: 12px;
width: 12px;
border: 1px solid #CCD3D5;
border-radius: 6px;
box-shadow: 0px 1px 1px 0px rgba(234,234,234,0.50);
-webkit-app-region: no-drag;
&.disabled {
border: 1px solid #E8EEEF;
}
&.enabled:hover {
box-shadow: 0px 1px 1px 0px rgba(195,198,201,0.50);
}
&.enabled:hover:active {
cursor: default;
-webkit-filter: brightness(92%);
}
}
}
}

Просмотреть файл

@ -17,24 +17,11 @@
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0,0,0,.2);
@shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);
.box-shadow(@shadow);
// Reset the shadow
&:active,
&.active {
.box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
}
.badge {
text-shadow: none;
}
}
// Mixin for generating new styles
.btn-styles(@btn-color: #555) {
#gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));
.reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners
background-repeat: repeat-x;
border-color: darken(@btn-color, 14%);
@ -42,33 +29,35 @@
&:hover,
&:focus {
background-color: darken(@btn-color, 12%);
background-position: 0 -15px;
}
&:active,
&.active {
background-color: darken(@btn-color, 12%);
border-color: darken(@btn-color, 14%);
}
&:disabled,
&[disabled] {
background-color: darken(@btn-color, 12%);
background-image: none;
}
}
// Common styles
.btn {
border-radius: 25px;
box-shadow: none;
font-weight: 400;
text-shadow: none;
// Remove the gradient for the pressed/active state
&:active,
&.active {
background-image: none;
}
&:focus,
&.focus {
box-shadow: none;
outline: none !important;
}
}
// Apply the mixin to the buttons
.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }
.btn-default { .btn-styles(@btn-default-bg); }
.btn-primary { .btn-styles(@btn-primary-bg); }
.btn-success { .btn-styles(@btn-success-bg); }
.btn-info { .btn-styles(@btn-info-bg); }