mirror of https://github.com/docker/docs.git
Implement Linux support
This commit is contained in:
parent
0ed907553b
commit
de0b2efd2a
20
Gruntfile.js
20
Gruntfile.js
|
|
@ -22,6 +22,7 @@ module.exports = function (grunt) {
|
||||||
var BASENAME = 'Kitematic';
|
var BASENAME = 'Kitematic';
|
||||||
var OSX_APPNAME = BASENAME + ' (Beta)';
|
var OSX_APPNAME = BASENAME + ' (Beta)';
|
||||||
var WINDOWS_APPNAME = BASENAME + ' (Alpha)';
|
var WINDOWS_APPNAME = BASENAME + ' (Alpha)';
|
||||||
|
var LINUX_APPNAME = BASENAME + ' (Alpha)';
|
||||||
var OSX_OUT = './dist';
|
var OSX_OUT = './dist';
|
||||||
var OSX_OUT_X64 = OSX_OUT + '/' + OSX_APPNAME + '-darwin-x64';
|
var OSX_OUT_X64 = OSX_OUT + '/' + OSX_APPNAME + '-darwin-x64';
|
||||||
var OSX_FILENAME = OSX_OUT_X64 + '/' + OSX_APPNAME + '.app';
|
var OSX_FILENAME = OSX_OUT_X64 + '/' + OSX_APPNAME + '.app';
|
||||||
|
|
@ -57,6 +58,19 @@ module.exports = function (grunt) {
|
||||||
'app-bundle-id': 'com.kitematic.kitematic',
|
'app-bundle-id': 'com.kitematic.kitematic',
|
||||||
'app-version': packagejson.version
|
'app-version': packagejson.version
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
options: {
|
||||||
|
name: LINUX_APPNAME,
|
||||||
|
dir: 'build/',
|
||||||
|
out: 'dist/linux/',
|
||||||
|
version: packagejson['electron-version'],
|
||||||
|
platform: 'linux',
|
||||||
|
arch: 'x64',
|
||||||
|
asar: true,
|
||||||
|
'app-bundle-id': 'com.kitematic.kitematic',
|
||||||
|
'app-version': packagejson.version
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -243,7 +257,11 @@ module.exports = function (grunt) {
|
||||||
});
|
});
|
||||||
|
|
||||||
grunt.registerTask('default', ['newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']);
|
grunt.registerTask('default', ['newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']);
|
||||||
grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron', 'copy:osx', 'shell:sign', 'shell:zip', 'copy:windows', 'rcedit:exes', 'compress']);
|
if(process.platform === 'linux') {
|
||||||
|
grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron:linux']);
|
||||||
|
} else {
|
||||||
|
grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron', 'copy:osx', 'shell:sign', 'shell:zip', 'copy:windows', 'rcedit:exes', 'compress']);
|
||||||
|
}
|
||||||
|
|
||||||
process.on('SIGINT', function () {
|
process.on('SIGINT', function () {
|
||||||
grunt.task.run(['shell:electron:kill']);
|
grunt.task.run(['shell:electron:kill']);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
[](https://kitematic.com)
|
[](https://kitematic.com)
|
||||||
|
|
||||||
Kitematic is a simple application for managing Docker containers on Mac and Windows.
|
Kitematic is a simple application for managing Docker containers on Mac, Linux and Windows.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import Router from 'react-router';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import routerContainer from './router';
|
import routerContainer from './router';
|
||||||
import repositoryActions from './actions/RepositoryActions';
|
import repositoryActions from './actions/RepositoryActions';
|
||||||
|
import util from './utils/Util';
|
||||||
var app = remote.require('app');
|
var app = remote.require('app');
|
||||||
|
|
||||||
hubUtil.init();
|
hubUtil.init();
|
||||||
|
|
@ -46,7 +47,8 @@ var router = Router.create({
|
||||||
router.run(Handler => React.render(<Handler/>, document.body));
|
router.run(Handler => React.render(<Handler/>, document.body));
|
||||||
routerContainer.set(router);
|
routerContainer.set(router);
|
||||||
|
|
||||||
setupUtil.setup().then(() => {
|
let setup = util.isLinux() ? setupUtil.nativeSetup : setupUtil.nonNativeSetup;
|
||||||
|
setup().then(() => {
|
||||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
||||||
docker.init();
|
docker.init();
|
||||||
if (!hub.prompted() && !hub.loggedin()) {
|
if (!hub.prompted() && !hub.loggedin()) {
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,11 @@ var Preferences = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
var vmSettings;
|
||||||
<div className="preferences">
|
|
||||||
<div className="preferences-content">
|
if (process.platform !== 'linux') {
|
||||||
<a onClick={this.handleGoBackClick}>Go Back</a>
|
vmSettings = (
|
||||||
|
<div>
|
||||||
<div className="title">VM Settings</div>
|
<div className="title">VM Settings</div>
|
||||||
<div className="option">
|
<div className="option">
|
||||||
<div className="option-name">
|
<div className="option-name">
|
||||||
|
|
@ -48,6 +49,15 @@ var Preferences = React.createClass({
|
||||||
<input type="checkbox" checked={this.state.closeVMOnQuit} onChange={this.handleChangeCloseVMOnQuit}/>
|
<input type="checkbox" checked={this.state.closeVMOnQuit} onChange={this.handleChangeCloseVMOnQuit}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="preferences">
|
||||||
|
<div className="preferences-content">
|
||||||
|
<a onClick={this.handleGoBackClick}>Go Back</a>
|
||||||
|
{vmSettings}
|
||||||
<div className="title">App Settings</div>
|
<div className="title">App Settings</div>
|
||||||
<div className="option">
|
<div className="option">
|
||||||
<div className="option-name">
|
<div className="option-name">
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import Router from 'react-router';
|
||||||
import Radial from './Radial.react.js';
|
import Radial from './Radial.react.js';
|
||||||
import RetinaImage from 'react-retina-image';
|
import RetinaImage from 'react-retina-image';
|
||||||
import Header from './Header.react';
|
import Header from './Header.react';
|
||||||
|
import Util from '../utils/Util';
|
||||||
import metrics from '../utils/MetricsUtil';
|
import metrics from '../utils/MetricsUtil';
|
||||||
import setupStore from '../stores/SetupStore';
|
import setupStore from '../stores/SetupStore';
|
||||||
import setupActions from '../actions/SetupActions';
|
import setupActions from '../actions/SetupActions';
|
||||||
|
|
@ -43,6 +44,13 @@ var Setup = React.createClass({
|
||||||
shell.openExternal('https://www.docker.com/docker-toolbox');
|
shell.openExternal('https://www.docker.com/docker-toolbox');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleLinuxDockerInstall: function () {
|
||||||
|
metrics.track('Opening Linux Docker installation instructions', {
|
||||||
|
from: 'setup'
|
||||||
|
});
|
||||||
|
shell.openExternal('http://docs.docker.com/linux/started/');
|
||||||
|
},
|
||||||
|
|
||||||
renderContents: function () {
|
renderContents: function () {
|
||||||
return (
|
return (
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
|
|
@ -74,6 +82,25 @@ var Setup = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
renderError: function () {
|
renderError: function () {
|
||||||
|
let deleteVmAndRetry;
|
||||||
|
|
||||||
|
if (Util.isLinux()) {
|
||||||
|
if (!this.state.started) {
|
||||||
|
deleteVmAndRetry = (
|
||||||
|
<button className="btn btn-action" onClick={this.handleLinuxDockerInstall}>Install Docker</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.state.started) {
|
||||||
|
deleteVmAndRetry = (
|
||||||
|
<button className="btn btn-action" onClick={this.handleErrorRemoveRetry}>Delete VM & Retry Setup</button>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
deleteVmAndRetry = (
|
||||||
|
<button className="btn btn-action" onClick={this.handleToolBox}>Get Toolbox</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="setup">
|
<div className="setup">
|
||||||
<Header hideLogin={true}/>
|
<Header hideLogin={true}/>
|
||||||
|
|
@ -93,7 +120,7 @@ var Setup = React.createClass({
|
||||||
<p className="error">{this.state.error.message || this.state.error}</p>
|
<p className="error">{this.state.error.message || this.state.error}</p>
|
||||||
<p className="setup-actions">
|
<p className="setup-actions">
|
||||||
<button className="btn btn-action" onClick={this.handleErrorRetry}>Retry Setup</button>
|
<button className="btn btn-action" onClick={this.handleErrorRetry}>Retry Setup</button>
|
||||||
{this.state.started ? <button className="btn btn-action" onClick={this.handleErrorRemoveRetry}>Delete VM & Retry Setup</button> : <button className="btn btn-action" onClick={this.handleToolBox}>Get Toolbox</button>}
|
{{deleteVmAndRetry}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,11 @@ var DockerMachine = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else if (util.isLinux()) {
|
||||||
|
cmd = cmd || process.env.SHELL;
|
||||||
|
var terminal = util.linuxTerminal();
|
||||||
|
if (terminal)
|
||||||
|
util.exec(terminal.concat([cmd])).then(() => {});
|
||||||
} else {
|
} else {
|
||||||
cmd = cmd || process.env.SHELL;
|
cmd = cmd || process.env.SHELL;
|
||||||
this.url(machineName).then(machineUrl => {
|
this.url(machineName).then(machineUrl => {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import dockerode from 'dockerode';
|
import dockerode from 'dockerode';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
import child_process from 'child_process';
|
||||||
import util from './Util';
|
import util from './Util';
|
||||||
import hubUtil from './HubUtil';
|
import hubUtil from './HubUtil';
|
||||||
import metrics from '../utils/MetricsUtil';
|
import metrics from '../utils/MetricsUtil';
|
||||||
|
|
@ -20,20 +21,25 @@ export default {
|
||||||
throw new Error('Falsy ip or name passed to docker client setup');
|
throw new Error('Falsy ip or name passed to docker client setup');
|
||||||
}
|
}
|
||||||
|
|
||||||
let certDir = path.join(util.home(), '.docker/machine/machines/', name);
|
if (util.isLinux()) {
|
||||||
if (!fs.existsSync(certDir)) {
|
this.host = 'localhost';
|
||||||
throw new Error('Certificate directory does not exist');
|
this.client = new dockerode({socketPath: '/var/run/docker.sock'});
|
||||||
}
|
} else {
|
||||||
|
let certDir = path.join(util.home(), '.docker/machine/machines/', name);
|
||||||
|
if (!fs.existsSync(certDir)) {
|
||||||
|
throw new Error('Certificate directory does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
this.host = ip;
|
this.host = ip;
|
||||||
this.client = new dockerode({
|
this.client = new dockerode({
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
host: ip,
|
host: ip,
|
||||||
port: 2376,
|
port: 2376,
|
||||||
ca: fs.readFileSync(path.join(certDir, 'ca.pem')),
|
ca: fs.readFileSync(path.join(certDir, 'ca.pem')),
|
||||||
cert: fs.readFileSync(path.join(certDir, 'cert.pem')),
|
cert: fs.readFileSync(path.join(certDir, 'cert.pem')),
|
||||||
key: fs.readFileSync(path.join(certDir, 'key.pem'))
|
key: fs.readFileSync(path.join(certDir, 'key.pem'))
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
init () {
|
init () {
|
||||||
|
|
@ -66,6 +72,14 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isDockerRunning () {
|
||||||
|
try {
|
||||||
|
child_process.execSync('ps ax | grep "docker daemon" | grep -v grep');
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Cannot connect to the Docker daemon. The daemon is not running.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
startContainer (name, containerData) {
|
startContainer (name, containerData) {
|
||||||
let startopts = {
|
let startopts = {
|
||||||
Binds: containerData.Binds || []
|
Binds: containerData.Binds || []
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import _ from 'underscore';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import util from './Util';
|
|
||||||
import bugsnag from 'bugsnag-js';
|
import bugsnag from 'bugsnag-js';
|
||||||
|
import util from './Util';
|
||||||
import virtualBox from './VirtualBoxUtil';
|
import virtualBox from './VirtualBoxUtil';
|
||||||
import setupServerActions from '../actions/SetupServerActions';
|
import setupServerActions from '../actions/SetupServerActions';
|
||||||
import metrics from './MetricsUtil';
|
import metrics from './MetricsUtil';
|
||||||
|
|
@ -51,7 +51,31 @@ export default {
|
||||||
return _retryPromise.promise;
|
return _retryPromise.promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
async setup () {
|
async nativeSetup () {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
docker.setup('localhost', machine.name());
|
||||||
|
docker.isDockerRunning();
|
||||||
|
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
router.get().transitionTo('setup');
|
||||||
|
metrics.track('Native Setup Failed');
|
||||||
|
setupServerActions.error({error});
|
||||||
|
|
||||||
|
let message = error.message.split('\n');
|
||||||
|
let lastLine = message.length > 1 ? message[message.length - 2] : 'Docker Machine encountered an error.';
|
||||||
|
bugsnag.notify('Native Setup Failed', lastLine, {
|
||||||
|
'Docker Machine Logs': error.message
|
||||||
|
}, 'info');
|
||||||
|
|
||||||
|
this.clearTimers();
|
||||||
|
await this.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async nonNativeSetup () {
|
||||||
let virtualBoxVersion = null;
|
let virtualBoxVersion = null;
|
||||||
let machineVersion = null;
|
let machineVersion = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import remote from 'remote';
|
import remote from 'remote';
|
||||||
|
var dialog = remote.require('dialog');
|
||||||
var app = remote.require('app');
|
var app = remote.require('app');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -34,6 +35,9 @@ module.exports = {
|
||||||
isWindows: function () {
|
isWindows: function () {
|
||||||
return process.platform === 'win32';
|
return process.platform === 'win32';
|
||||||
},
|
},
|
||||||
|
isLinux: function () {
|
||||||
|
return process.platform === 'linux';
|
||||||
|
},
|
||||||
binsPath: function () {
|
binsPath: function () {
|
||||||
return this.isWindows() ? path.join(this.home(), 'Kitematic-bins') : path.join('/usr/local/bin');
|
return this.isWindows() ? path.join(this.home(), 'Kitematic-bins') : path.join('/usr/local/bin');
|
||||||
},
|
},
|
||||||
|
|
@ -156,5 +160,17 @@ module.exports = {
|
||||||
linuxToWindowsPath: function (linuxAbsPath) {
|
linuxToWindowsPath: function (linuxAbsPath) {
|
||||||
return linuxAbsPath.replace('/c', 'C:').split('/').join('\\');
|
return linuxAbsPath.replace('/c', 'C:').split('/').join('\\');
|
||||||
},
|
},
|
||||||
|
linuxTerminal: function () {
|
||||||
|
if (fs.existsSync('/usr/bin/x-terminal-emulator')) {
|
||||||
|
return ['/usr/bin/x-terminal-emulator', '-e'];
|
||||||
|
} else {
|
||||||
|
dialog.showMessageBox({
|
||||||
|
type: 'warning',
|
||||||
|
buttons: ['OK'],
|
||||||
|
message: 'The terminal emulator symbolic link doesn\'t exists. Please read the Wiki at https://github.com/kitematic/kitematic/wiki/Common-Issues-and-Fixes#early-linux-support-from-zedtux.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
webPorts: ['80', '8000', '8080', '8888', '3000', '5000', '2368', '9200', '8983']
|
webPorts: ['80', '8000', '8080', '8888', '3000', '5000', '2368', '9200', '8983']
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@
|
||||||
@color-box-button: lighten(@gray-lightest, 5%);
|
@color-box-button: lighten(@gray-lightest, 5%);
|
||||||
@color-background: lighten(@gray-lightest, 4.5%);
|
@color-background: lighten(@gray-lightest, 4.5%);
|
||||||
|
|
||||||
@font-regular: "Helvetica Neue", Segoe UI, Arial, "Lucida Grande", sans-serif;
|
@font-regular: "Helvetica Neue", Segoe UI, "Ubuntu", Arial, "Lucida Grande", sans-serif;
|
||||||
@font-code: Menlo, Consolas;
|
@font-code: Menlo, Consolas, "DejaVu Sans Mono";
|
||||||
|
|
||||||
@border-radius: 0.2rem;
|
@border-radius: 0.2rem;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue