Merge pull request #370 from kitematic/jeffdm-windows
Updating Windows Branch with latest changes
|
@ -3,6 +3,7 @@
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
coverage
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
# Signing Identity
|
# Signing Identity
|
||||||
|
|
|
@ -27,5 +27,5 @@
|
||||||
"jest": true,
|
"jest": true,
|
||||||
"pit": true
|
"pit": true
|
||||||
},
|
},
|
||||||
"predef": [ "-Promise" ]
|
"predef": [ "Promise" ]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.10"
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- resources
|
||||||
|
- node_modules
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- which ./node_modules/coveralls/bin/coveralls.js && cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
|
@ -53,6 +53,10 @@ We're thrilled to receive pull requests of any kind. Anything from bug fix, test
|
||||||
|
|
||||||
That said, please let us know what you're planning to do! For large changes always create a proposal. Maintainers will love to give you advice on building it and it keeps the app's design coherent.
|
That said, please let us know what you're planning to do! For large changes always create a proposal. Maintainers will love to give you advice on building it and it keeps the app's design coherent.
|
||||||
|
|
||||||
|
### Pull Request Requirements:
|
||||||
|
- Tests
|
||||||
|
- [Signed Off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work)
|
||||||
|
|
||||||
## Code Guidelines
|
## Code Guidelines
|
||||||
|
|
||||||
### Javascript
|
### Javascript
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[](https://circleci.com/gh/kitematic/kitematic/tree/master)
|
[](https://travis-ci.org/kitematic/kitematic)
|
||||||
|
[](https://coveralls.io/r/kitematic/kitematic?branch=master)
|
||||||
[](http://app.bithound.io/kitematic/kitematic)
|
[](http://app.bithound.io/kitematic/kitematic)
|
||||||
[](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,6 @@ dependencies:
|
||||||
cache_directories:
|
cache_directories:
|
||||||
- "resources"
|
- "resources"
|
||||||
- "node_modules"
|
- "node_modules"
|
||||||
|
notify:
|
||||||
|
webhooks:
|
||||||
|
- url: https://coveralls.io/webhook
|
||||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.5 KiB |
29
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Kitematic",
|
"name": "Kitematic",
|
||||||
"version": "0.5.11",
|
"version": "0.5.13",
|
||||||
"author": "Kitematic",
|
"author": "Kitematic",
|
||||||
"description": "Simple Docker Container management for Mac OS X.",
|
"description": "Simple Docker Container management for Mac OS X.",
|
||||||
"homepage": "https://kitematic.com/",
|
"homepage": "https://kitematic.com/",
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
"bugs": "https://github.com/kitematic/kitematic/issues",
|
"bugs": "https://github.com/kitematic/kitematic/issues",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "gulp",
|
"start": "gulp",
|
||||||
"test": "jest",
|
"test": "jest --coverage",
|
||||||
"release": "gulp release",
|
"release": "gulp release",
|
||||||
"release:beta": "gulp release --beta",
|
"release:beta": "gulp release --beta",
|
||||||
"lint": "jsxhint src && jsxhint browser",
|
"lint": "jsxhint src && jsxhint browser",
|
||||||
|
@ -27,25 +27,30 @@
|
||||||
"jest": {
|
"jest": {
|
||||||
"scriptPreprocessor": "<rootDir>/util/preprocessor.js",
|
"scriptPreprocessor": "<rootDir>/util/preprocessor.js",
|
||||||
"setupEnvScriptFile": "<rootDir>/util/testenv.js",
|
"setupEnvScriptFile": "<rootDir>/util/testenv.js",
|
||||||
|
"collectCoverage": true,
|
||||||
|
"testDirectoryName": "src",
|
||||||
|
"testPathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"^((?!-test).)*$"
|
||||||
|
],
|
||||||
"unmockedModulePathPatterns": [
|
"unmockedModulePathPatterns": [
|
||||||
|
"stream",
|
||||||
"tty",
|
"tty",
|
||||||
"net",
|
"net",
|
||||||
"crypto",
|
"crypto",
|
||||||
"stream",
|
"<rootDir>/node_modules/.*JSONStream",
|
||||||
"<rootDir>/node_modules/object-assign",
|
"<rootDir>/node_modules/object-assign",
|
||||||
"<rootDir>/node_modules/underscore",
|
"<rootDir>/node_modules/underscore",
|
||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/bluebird"
|
||||||
"<rootDir>/node_modules/bluebird",
|
|
||||||
"<rootDir>/node_modules/babel"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"docker-version": "1.5.0",
|
"docker-version": "1.5.0",
|
||||||
"docker-machine-version": "0.1.0-kitematic-0.5.10",
|
"docker-machine-version": "0.1.0-kitematic-0.5.10",
|
||||||
"atom-shell-version": "0.21.3",
|
"atom-shell-version": "0.21.3",
|
||||||
"virtualbox-version": "4.3.24",
|
"virtualbox-version": "4.3.26",
|
||||||
"virtualbox-filename": "VirtualBox-4.3.24.pkg",
|
"virtualbox-filename": "VirtualBox-4.3.26.pkg",
|
||||||
"virtualbox-filename-win": "VirtualBox-4.3.26.exe",
|
"virtualbox-filename-win": "VirtualBox-4.3.26.exe",
|
||||||
"virtualbox-checksum": "100eee21df3808fc1b1e461e86f10b6d2a748935c5f31bc665f4ff0777e44a0b",
|
"virtualbox-checksum": "668f61c95efe37f8fc65cafe95b866fba64e37f2492dfc1e2b44a7ac3dcafa3b",
|
||||||
"virtualbox-checksum-win": "9cb265babf307d825f5178693af95ffca077f80ae22cf43868c3538c159123ff",
|
"virtualbox-checksum-win": "9cb265babf307d825f5178693af95ffca077f80ae22cf43868c3538c159123ff",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-to-html": "0.3.0",
|
"ansi-to-html": "0.3.0",
|
||||||
|
@ -53,7 +58,8 @@
|
||||||
"async": "^0.9.0",
|
"async": "^0.9.0",
|
||||||
"bluebird": "^2.9.12",
|
"bluebird": "^2.9.12",
|
||||||
"bugsnag-js": "^2.4.7",
|
"bugsnag-js": "^2.4.7",
|
||||||
"dockerode": "^2.0.7",
|
"coveralls": "^2.11.2",
|
||||||
|
"dockerode": "^2.1.1",
|
||||||
"exec": "0.2.0",
|
"exec": "0.2.0",
|
||||||
"fs-extra": "^0.17.0",
|
"fs-extra": "^0.17.0",
|
||||||
"fs-promise": "^0.3.1",
|
"fs-promise": "^0.3.1",
|
||||||
|
@ -81,6 +87,7 @@
|
||||||
"gulp-cssmin": "^0.1.6",
|
"gulp-cssmin": "^0.1.6",
|
||||||
"gulp-download-atom-shell": "0.0.4",
|
"gulp-download-atom-shell": "0.0.4",
|
||||||
"gulp-if": "^1.2.5",
|
"gulp-if": "^1.2.5",
|
||||||
|
"gulp-insert": "^0.4.0",
|
||||||
"gulp-less": "^3.0.1",
|
"gulp-less": "^3.0.1",
|
||||||
"gulp-livereload": "^3.8.0",
|
"gulp-livereload": "^3.8.0",
|
||||||
"gulp-plumber": "^0.6.6",
|
"gulp-plumber": "^0.6.6",
|
||||||
|
@ -89,7 +96,7 @@
|
||||||
"gulp-shell": "^0.3.0",
|
"gulp-shell": "^0.3.0",
|
||||||
"gulp-sourcemaps": "^1.5.0",
|
"gulp-sourcemaps": "^1.5.0",
|
||||||
"gulp-util": "^3.0.4",
|
"gulp-util": "^3.0.4",
|
||||||
"jest-cli": "^0.4.0",
|
"jest-cli": "kitematic/jest",
|
||||||
"jsxhint": "^0.12.1",
|
"jsxhint": "^0.12.1",
|
||||||
"react-tools": "^0.12.2",
|
"react-tools": "^0.12.2",
|
||||||
"run-sequence": "^1.0.2"
|
"run-sequence": "^1.0.2"
|
||||||
|
|
|
@ -74,7 +74,9 @@ var ContainerHome = React.createClass({
|
||||||
if (this.props.error) {
|
if (this.props.error) {
|
||||||
body = (
|
body = (
|
||||||
<div className="details-progress">
|
<div className="details-progress">
|
||||||
<h3>There was a problem connecting to the Docker Engine in the VirtualBox VM.<br/>This could be caused because this Mac is currently connected to a VPN, blocking access to the VM. If the issue persists, please <a onClick={this.handleErrorClick}>file a ticket on our GitHub repo.</a></h3>
|
<h3>An error occurred:</h3>
|
||||||
|
<h2>{this.props.error.statusCode} {this.props.error.reason} - {this.props.error.json}</h2>
|
||||||
|
<h3>If you feel that this error is invalid, please <a onClick={this.handleErrorClick}>file a ticket on our GitHub repo.</a></h3>
|
||||||
<Radial progress={100} error={true} thick={true} transparent={true}/>
|
<Radial progress={100} error={true} thick={true} transparent={true}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,20 +2,46 @@ var _ = require('underscore');
|
||||||
var React = require('react/addons');
|
var React = require('react/addons');
|
||||||
var RetinaImage = require('react-retina-image');
|
var RetinaImage = require('react-retina-image');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var exec = require('exec');
|
var shell = require('shell');
|
||||||
|
var util = require('./Util');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
var util = require('./Util');
|
var ContainerStore = require('./ContainerStore');
|
||||||
|
|
||||||
var ContainerHomeFolder = React.createClass({
|
var ContainerHomeFolder = React.createClass({
|
||||||
mixins: [Router.State, Router.Navigation],
|
mixins: [Router.State, Router.Navigation],
|
||||||
handleClickFolder: function (path) {
|
handleClickFolder: function (hostVolume, containerVolume) {
|
||||||
metrics.track('Opened Volume Directory', {
|
metrics.track('Opened Volume Directory', {
|
||||||
from: 'home'
|
from: 'home'
|
||||||
});
|
});
|
||||||
util.openPathOrUrl(path, function (err) {
|
|
||||||
if (err) { throw err; }
|
if (hostVolume.indexOf(process.env.HOME) === -1) {
|
||||||
|
var volumes = _.clone(this.props.container.Volumes);
|
||||||
|
var newHostVolume = path.join(util.home(), 'Kitematic', this.props.container.Name, containerVolume);
|
||||||
|
volumes[containerVolume] = newHostVolume;
|
||||||
|
var binds = _.pairs(volumes).map(function (pair) {
|
||||||
|
if(util.isWindows()) {
|
||||||
|
var home = util.home();
|
||||||
|
home = home.charAt(0).toLowerCase() + home.slice(1);
|
||||||
|
home = '/' + home.replace(':', '').replace(/\\/g, '/');
|
||||||
|
var fullPath = path.join(home, 'Kitematic', pair[1], pair[0]);
|
||||||
|
fullPath = fullPath.replace(/\\/g, '/');
|
||||||
|
return fullPath + ':' + pair[0];
|
||||||
|
}
|
||||||
|
return pair[1] + ':' + pair[0];
|
||||||
});
|
});
|
||||||
|
ContainerStore.updateContainer(this.props.container.Name, {
|
||||||
|
Binds: binds
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shell.showItemInFolder(newHostVolume);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
shell.showItemInFolder(hostVolume);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleClickChangeFolders: function () {
|
handleClickChangeFolders: function () {
|
||||||
metrics.track('Viewed Volume Settings', {
|
metrics.track('Viewed Volume Settings', {
|
||||||
|
@ -24,24 +50,21 @@ var ContainerHomeFolder = React.createClass({
|
||||||
this.transitionTo('containerSettingsVolumes', {name: this.getParams().name});
|
this.transitionTo('containerSettingsVolumes', {name: this.getParams().name});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var folders;
|
if (!this.props.container) {
|
||||||
if (this.props.container) {
|
return false;
|
||||||
var self = this;
|
}
|
||||||
folders = _.map(self.props.container.Volumes, function (val, key) {
|
|
||||||
|
var folders = _.map(this.props.container.Volumes, (val, key) => {
|
||||||
var firstFolder = key.split(path.sep)[1];
|
var firstFolder = key.split(path.sep)[1];
|
||||||
if (!val || val.indexOf(process.env.HOME) === -1) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return (
|
return (
|
||||||
<div key={key} className="folder" onClick={self.handleClickFolder.bind(self, val)}>
|
<div key={key} className="folder" onClick={this.handleClickFolder.bind(this, val, key)}>
|
||||||
<RetinaImage src="folder.png" />
|
<RetinaImage src="folder.png" />
|
||||||
<div className="text">{firstFolder}</div>
|
<div className="text">{firstFolder}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
if (this.props.container && this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) {
|
if (this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) {
|
||||||
return (
|
return (
|
||||||
<div className="folders wrapper">
|
<div className="folders wrapper">
|
||||||
<h4>Edit Files</h4>
|
<h4>Edit Files</h4>
|
||||||
|
@ -52,9 +75,7 @@ var ContainerHomeFolder = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return false;
|
||||||
<div></div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,6 +52,9 @@ var ContainerHomeLogs = React.createClass({
|
||||||
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 <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
||||||
});
|
});
|
||||||
|
if (logs.length === 0) {
|
||||||
|
logs = "No logs for this container.";
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mini-logs wrapper">
|
<div className="mini-logs wrapper">
|
||||||
<h4>Logs</h4>
|
<h4>Logs</h4>
|
||||||
|
|
|
@ -38,9 +38,6 @@ var ContainerHomePreview = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentDidUpdate: function () {
|
|
||||||
this.reload();
|
|
||||||
},
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,8 +9,14 @@ var ContainerList = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var containers = this.props.containers.map(function (container) {
|
var containers = this.props.containers.map(function (container) {
|
||||||
|
var containerId = container.Id;
|
||||||
|
if (!containerId && container.State.Downloading) {
|
||||||
|
// Fall back to the container image name when there is no id. (when the
|
||||||
|
// image is downloading).
|
||||||
|
containerId = container.Image;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ContainerListItem key={container.Id} container={container} start={self._start}/>
|
<ContainerListItem key={containerId} container={container} start={self._start} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
var newItem;
|
var newItem;
|
||||||
|
|
|
@ -45,6 +45,9 @@ var ContainerLogs = React.createClass({
|
||||||
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 <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
||||||
});
|
});
|
||||||
|
if (logs.length === 0) {
|
||||||
|
logs = "No logs for this container.";
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="details-panel details-logs logs">
|
<div className="details-panel details-logs logs">
|
||||||
{logs}
|
{logs}
|
||||||
|
|
|
@ -2,7 +2,6 @@ var _ = require('underscore');
|
||||||
var React = require('react/addons');
|
var React = require('react/addons');
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
var remote = require('remote');
|
var remote = require('remote');
|
||||||
var exec = require('exec');
|
|
||||||
var dialog = remote.require('dialog');
|
var dialog = remote.require('dialog');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
|
@ -32,6 +31,21 @@ var ContainerSettingsVolumes = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleRemoveVolumeClick: function (dockerVol) {
|
||||||
|
metrics.track('Removed Volume Directory', {
|
||||||
|
from: 'settings'
|
||||||
|
});
|
||||||
|
var volumes = _.clone(this.props.container.Volumes);
|
||||||
|
delete volumes[dockerVol];
|
||||||
|
var binds = _.pairs(volumes).map(function (pair) {
|
||||||
|
return pair[1] + ':' + pair[0];
|
||||||
|
});
|
||||||
|
ContainerStore.updateContainer(this.props.container.Name, {
|
||||||
|
Binds: binds
|
||||||
|
}, function (err) {
|
||||||
|
if (err) { console.log(err); }
|
||||||
|
});
|
||||||
|
},
|
||||||
handleOpenVolumeClick: function (path) {
|
handleOpenVolumeClick: function (path) {
|
||||||
metrics.track('Opened Volume Directory', {
|
metrics.track('Opened Volume Directory', {
|
||||||
from: 'settings'
|
from: 'settings'
|
||||||
|
@ -51,6 +65,7 @@ var ContainerSettingsVolumes = React.createClass({
|
||||||
<span>
|
<span>
|
||||||
<a className="value-right">No Folder</a>
|
<a className="value-right">No Folder</a>
|
||||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||||
|
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,6 +73,7 @@ var ContainerSettingsVolumes = React.createClass({
|
||||||
<span>
|
<span>
|
||||||
<a className="value-right" onClick={self.handleOpenVolumeClick.bind(self, val)}>{val.replace(process.env.HOME, '~')}</a>
|
<a className="value-right" onClick={self.handleOpenVolumeClick.bind(self, val)}>{val.replace(process.env.HOME, '~')}</a>
|
||||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||||
|
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var path = require('path');
|
|
||||||
var assign = require('object-assign');
|
var assign = require('object-assign');
|
||||||
var docker = require('./Docker');
|
var docker = require('./Docker');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var registry = require('./Registry');
|
var registry = require('./Registry');
|
||||||
var logstore = require('./LogStore');
|
var logstore = require('./LogStore');
|
||||||
var bugsnag = require('bugsnag-js');
|
var bugsnag = require('bugsnag-js');
|
||||||
var util = require('./Util');
|
|
||||||
|
|
||||||
var _placeholders = {};
|
var _placeholders = {};
|
||||||
var _containers = {};
|
var _containers = {};
|
||||||
|
@ -58,6 +56,12 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
var data = JSON.parse(str);
|
var data = JSON.parse(str);
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
_error = data.error;
|
||||||
|
callback(data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status && (data.status === 'Pulling dependent layers' || data.status.indexOf('already being pulled by another client') !== -1)) {
|
if (data.status && (data.status === 'Pulling dependent layers' || data.status.indexOf('already being pulled by another client') !== -1)) {
|
||||||
blockedCallback();
|
blockedCallback();
|
||||||
return;
|
return;
|
||||||
|
@ -84,7 +88,8 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
progressCallback(totalProgress);
|
progressCallback(totalProgress);
|
||||||
});
|
});
|
||||||
stream.on('end', function () {
|
stream.on('end', function () {
|
||||||
callback();
|
callback(_error);
|
||||||
|
_error = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -92,32 +97,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
},
|
},
|
||||||
_startContainer: function (name, containerData, callback) {
|
_startContainer: function (name, containerData, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
docker.client().getImage(containerData.Image).inspect(function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var binds = containerData.Binds || [];
|
var binds = containerData.Binds || [];
|
||||||
if (data.Config.Volumes) {
|
|
||||||
_.each(data.Config.Volumes, function (value, key) {
|
|
||||||
var existingBind = _.find(binds, b => {
|
|
||||||
return b.indexOf(':' + key) !== -1;
|
|
||||||
});
|
|
||||||
if (!existingBind) {
|
|
||||||
var home = util.home();
|
|
||||||
|
|
||||||
if(util.isWindows()) {
|
|
||||||
home = home.charAt(0).toLowerCase() + home.slice(1);
|
|
||||||
home = "/" + home.replace(':', '').replace(/\\/g, '/');
|
|
||||||
var fullPath = path.join(home, 'Kitematic', name, key);
|
|
||||||
fullPath = fullPath.replace(/\\/g, '/');
|
|
||||||
binds.push(fullPath + ':' + key);
|
|
||||||
} else {
|
|
||||||
binds.push(path.join(home, 'Kitematic', name, key) + ':' + key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var startopts = {
|
var startopts = {
|
||||||
Binds: binds
|
Binds: binds
|
||||||
};
|
};
|
||||||
|
@ -134,7 +114,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
}
|
}
|
||||||
self.fetchContainer(name, callback);
|
self.fetchContainer(name, callback);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
_createContainer: function (name, containerData, callback) {
|
_createContainer: function (name, containerData, callback) {
|
||||||
var existing = docker.client().getContainer(name);
|
var existing = docker.client().getContainer(name);
|
||||||
|
@ -147,6 +126,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
if (containerData.Config && containerData.Config.Image) {
|
if (containerData.Config && containerData.Config.Image) {
|
||||||
containerData.Image = containerData.Config.Image;
|
containerData.Image = containerData.Config.Image;
|
||||||
}
|
}
|
||||||
|
if (!containerData.Env && containerData.Config && containerData.Config.Env) {
|
||||||
|
containerData.Env = containerData.Config.Env;
|
||||||
|
}
|
||||||
existing.kill(function () {
|
existing.kill(function () {
|
||||||
existing.remove(function () {
|
existing.remove(function () {
|
||||||
docker.client().createContainer(containerData, function (err) {
|
docker.client().createContainer(containerData, function (err) {
|
||||||
|
@ -154,11 +136,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (containerData.State && !containerData.State.Running) {
|
|
||||||
self.fetchContainer(containerData.name, callback);
|
|
||||||
} else {
|
|
||||||
self._startContainer(name, containerData, callback);
|
self._startContainer(name, containerData, callback);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,6 +5,7 @@ var ContainerStore = require('./ContainerStore');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
|
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
|
||||||
var Tooltip = require('react-bootstrap').Tooltip;
|
var Tooltip = require('react-bootstrap').Tooltip;
|
||||||
|
var util = require('./Util');
|
||||||
|
|
||||||
var ImageCard = React.createClass({
|
var ImageCard = React.createClass({
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
|
@ -30,16 +31,33 @@ var ImageCard = React.createClass({
|
||||||
handleTagOverlayClick: function (name) {
|
handleTagOverlayClick: function (name) {
|
||||||
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
||||||
$tagOverlay.fadeIn(300);
|
$tagOverlay.fadeIn(300);
|
||||||
$.get('https://registry.hub.docker.com/v1/repositories/' + name + '/tags', function (result) {
|
$.get('https://registry.hub.docker.com/v1/repositories/' + name + '/tags', result => {
|
||||||
this.setState({
|
this.setState({
|
||||||
tags: result
|
tags: result
|
||||||
});
|
});
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleCloseTagOverlay: function () {
|
handleCloseTagOverlay: function () {
|
||||||
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
||||||
$tagOverlay.fadeOut(300);
|
$tagOverlay.fadeOut(300);
|
||||||
},
|
},
|
||||||
|
handleRepoClick: function () {
|
||||||
|
var $repoUri = 'https://registry.hub.docker.com/';
|
||||||
|
if (this.props.image.is_official) {
|
||||||
|
$repoUri = $repoUri + "_/";
|
||||||
|
} else {
|
||||||
|
$repoUri = $repoUri + "u/";
|
||||||
|
}
|
||||||
|
util.exec(['open', $repoUri + this.props.image.name]);
|
||||||
|
},
|
||||||
|
componentDidMount: function() {
|
||||||
|
$.get('https://registry.hub.docker.com/v1/repositories/' + this.props.image.name + '/tags', result => {
|
||||||
|
this.setState({
|
||||||
|
tags: result,
|
||||||
|
chosenTag: result[0].name
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var name;
|
var name;
|
||||||
|
@ -57,8 +75,8 @@ var ImageCard = React.createClass({
|
||||||
name = (
|
name = (
|
||||||
<div>
|
<div>
|
||||||
<div className="namespace official">{namespace}</div>
|
<div className="namespace official">{namespace}</div>
|
||||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>{this.props.image.name}</Tooltip>}>
|
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on DockerHub</Tooltip>}>
|
||||||
<span className="repo">{repo}</span>
|
<span className="repo" onClick={this.handleRepoClick}>{repo}</span>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -66,8 +84,8 @@ var ImageCard = React.createClass({
|
||||||
name = (
|
name = (
|
||||||
<div>
|
<div>
|
||||||
<div className="namespace">{namespace}</div>
|
<div className="namespace">{namespace}</div>
|
||||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>{this.props.image.name}</Tooltip>}>
|
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on DockerHub</Tooltip>}>
|
||||||
<span className="repo">{repo}</span>
|
<span className="repo" onClick={this.handleRepoClick}>{repo}</span>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
jest.dontMock('../src/SetupStore');
|
jest.dontMock('./SetupStore');
|
||||||
var setupStore = require('../src/SetupStore');
|
var setupStore = require('./SetupStore');
|
||||||
var virtualBox = require('../src/VirtualBox');
|
var virtualBox = require('./VirtualBox');
|
||||||
var util = require('../src/Util');
|
var util = require('./Util');
|
||||||
var machine = require('../src/DockerMachine');
|
var machine = require('./DockerMachine');
|
||||||
var setupUtil = require('../src/SetupUtil');
|
var setupUtil = require('./SetupUtil');
|
||||||
var Promise = require('bluebird');
|
|
||||||
|
|
||||||
describe('SetupStore', function () {
|
describe('SetupStore', function () {
|
||||||
describe('download step', function () {
|
describe('download step', function () {
|
|
@ -270,6 +270,8 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
step: _currentStep,
|
step: _currentStep,
|
||||||
message: err.message
|
message: err.message
|
||||||
});
|
});
|
||||||
|
console.log(err);
|
||||||
|
console.log(err.stack);
|
||||||
bugsnag.notify('SetupError', err.message, {
|
bugsnag.notify('SetupError', err.message, {
|
||||||
error: err,
|
error: err,
|
||||||
step: _currentStep
|
step: _currentStep
|
||||||
|
|
|
@ -33,9 +33,9 @@ var SetupUtil = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield fs.chown(util.binsPath(), process.getuid(), '80');
|
yield fs.chown(util.binsPath(), process.getuid(), 80);
|
||||||
yield fs.chown(util.dockerBinPath(), process.getuid(), '80');
|
yield fs.chown(util.dockerBinPath(), process.getuid(), 80);
|
||||||
yield fs.chown(util.dockerMachineBinPath(), process.getuid(), '80');
|
yield fs.chown(util.dockerMachineBinPath(), process.getuid(), 80);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}),
|
}),
|
||||||
installVirtualBoxCmd: Promise.coroutine(function* () {
|
installVirtualBoxCmd: Promise.coroutine(function* () {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
jest.dontMock('../src/VirtualBox');
|
jest.dontMock('./VirtualBox');
|
||||||
var virtualBox = require('../src/VirtualBox');
|
var virtualBox = require('./VirtualBox');
|
||||||
var util = require('../src/Util');
|
var util = require('./Util');
|
||||||
var Promise = require('bluebird');
|
|
||||||
|
|
||||||
describe('VirtualBox', function () {
|
describe('VirtualBox', function () {
|
||||||
it('returns the right command', function () {
|
it('returns the right command', function () {
|
|
@ -201,7 +201,7 @@
|
||||||
left: -20px;
|
left: -20px;
|
||||||
.at2x('runningwave.png', 20px, 20px);
|
.at2x('runningwave.png', 20px, 20px);
|
||||||
-webkit-animation-name: translatewave;
|
-webkit-animation-name: translatewave;
|
||||||
-webkit-animation-duration: 8.0s;
|
-webkit-animation-duration: 7.0s;
|
||||||
-webkit-animation-iteration-count: infinite;
|
-webkit-animation-iteration-count: infinite;
|
||||||
-webkit-animation-timing-function: linear;
|
-webkit-animation-timing-function: linear;
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.description {
|
.description {
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
.action {
|
.action {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 10px;
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
@ -36,11 +35,11 @@
|
||||||
.btn-label {
|
.btn-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
color: @brand-action;
|
color: @brand-action;
|
||||||
font-size: 10px;
|
font-size: 9px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
top: 30px;
|
top: 45px;
|
||||||
&.view {
|
&.view {
|
||||||
left: 6px;
|
left: 7px;
|
||||||
//left: 0px;
|
//left: 0px;
|
||||||
}
|
}
|
||||||
&.restart {
|
&.restart {
|
||||||
|
|