Merge branch 'release/1.4.0'

pull/75/head
Anthony Lapenna 2016-07-13 14:54:03 +12:00
commit 1eae1c03f0
14 changed files with 191 additions and 45 deletions

View File

@ -26,11 +26,16 @@ The `--privileged` flag is required for hosts using SELinux.
By default UI For Docker connects to the Docker daemon with`/var/run/docker.sock`. For this to work you need to bind mount the unix socket into the container with `-v /var/run/docker.sock:/var/run/docker.sock`. By default UI For Docker connects to the Docker daemon with`/var/run/docker.sock`. For this to work you need to bind mount the unix socket into the container with `-v /var/run/docker.sock:/var/run/docker.sock`.
You can use the `-e` flag to change this socket: You can use the `--host`, `-H` flags to change this socket:
``` ```
# Connect to a tcp socket: # Connect to a tcp socket:
$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://127.0.0.1:2375 $ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H tcp://127.0.0.1:2375
```
```
# Connect to another unix socket:
$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H unix:///path/to/docker.sock
``` ```
### Swarm support ### Swarm support
@ -41,7 +46,7 @@ You can access a specific view for you Swarm cluster by defining the `--swarm` f
``` ```
# Connect to a tcp socket and enable Swarm: # Connect to a tcp socket and enable Swarm:
$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://<SWARM_HOST>:<SWARM_PORT> --swarm $ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H tcp://<SWARM_HOST>:<SWARM_PORT> --swarm
``` ```
*NOTE*: Due to Swarm not exposing information in a machine readable way, the app is bound to a specific version of Swarm at the moment. *NOTE*: Due to Swarm not exposing information in a machine readable way, the app is bound to a specific version of Swarm at the moment.
@ -54,6 +59,24 @@ UI For Docker listens on port 9000 by default. If you run UI For Docker inside a
$ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui $ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui
``` ```
### Access a Docker engine protected via TLS
Ensure that you have access to the CA, the cert and the public key used to access your Docker engine.
These files will need to be named `ca.pem`, `cert.pem` and `key.pem` respectively. Store them somewhere on your disk and mount a volume containing these files inside the UI container:
```
$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/certs -H https://my-docker-host.domain:2376 --tlsverify
```
You can also use the `--tlscacert`, `--tlscert` and `--tlskey` flags if you want to change the default path to the CA, certificate and key file respectively:
```
$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/certs -H https://my-docker-host.domain:2376 --tlsverify --tlscacert /certs/myCa.pem --tlscert /certs/myCert.pem --tlskey /certs/myKey.pem
```
*Note*: Replace `/path/to/certs` to the path to the certificate files on your disk.
### Hide containers with specific labels ### Hide containers with specific labels
You can hide specific containers in the containers view by using the `-hide-label` or `-l` options and specifying a label. You can hide specific containers in the containers view by using the `-hide-label` or `-l` options and specifying a label.
@ -74,9 +97,13 @@ $ docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docke
The following options are available for the `ui-for-docker` binary: The following options are available for the `ui-for-docker` binary:
* `--endpoint`, `-e`: Docker deamon endpoint (default: *"/var/run/docker.sock"*) * `--host`, `-H`: Docker daemon endpoint (default: `"unix:///var/run/docker.sock"`)
* `--bind`, `-p`: Address and port to serve UI For Docker (default: *":9000"*) * `--bind`, `-p`: Address and port to serve UI For Docker (default: `":9000"`)
* `--data`, `-d`: Path to the data folder (default: *"."*) * `--data`, `-d`: Path to the data folder (default: `"."`)
* `--assets`, `-a`: Path to the assets (default: *"."*) * `--assets`, `-a`: Path to the assets (default: `"."`)
* `--swarm`, `-s`: Swarm cluster support (default: *false*) * `--swarm`, `-s`: Swarm cluster support (default: `false`)
* `--hide-label`, `-l`: Hide containers with a specific label in the UI * `--hide-label`, `-l`: Hide containers with a specific label in the UI
* `--tlsverify`: TLS support (default: `false`)
* `--tlscacert`: Path to the CA (default `/certs/ca.pem`)
* `--tlscert`: Path to the TLS certificate file (default `/certs/cert.pem`)
* `--tlskey`: Path to the TLS key (default `/certs/key.pem`)

View File

@ -144,4 +144,4 @@ angular.module('uifordocker', [
.constant('DOCKER_ENDPOINT', 'dockerapi') .constant('DOCKER_ENDPOINT', 'dockerapi')
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243 .constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
.constant('CONFIG_ENDPOINT', '/config') .constant('CONFIG_ENDPOINT', '/config')
.constant('UI_VERSION', 'v1.3.0'); .constant('UI_VERSION', 'v1.4.0');

View File

@ -39,7 +39,7 @@
<tr> <tr>
<th></th> <th></th>
<th> <th>
<a ui-sref="containers" ng-click="order('State')"> <a ui-sref="containers" ng-click="order('Status')">
State State
<span ng-show="sortType == 'State' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span> <span ng-show="sortType == 'State' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'State' && sortReverse" class="glyphicon glyphicon-chevron-up"></span> <span ng-show="sortType == 'State' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
@ -52,7 +52,7 @@
<span ng-show="sortType == 'Names' && sortReverse" class="glyphicon glyphicon-chevron-up"></span> <span ng-show="sortType == 'Names' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a> </a>
</th> </th>
<th> <th ng-if="state.displayIP">
<a ui-sref="containers" ng-click="order('IP')"> <a ui-sref="containers" ng-click="order('IP')">
IP Address IP Address
<span ng-show="sortType == 'IP' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span> <span ng-show="sortType == 'IP' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
@ -85,10 +85,10 @@
<tbody> <tbody>
<tr ng-repeat="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse))"> <tr ng-repeat="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse))">
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td> <td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
<td><span class="label label-{{ container.State|containerstatusbadge }}">{{ container.State }}</span></td> <td><span class="label label-{{ container.Status|containerstatusbadge }}">{{ container.Status|containerstatus }}</span></td>
<td ng-if="swarm"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td> <td ng-if="swarm"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
<td ng-if="!swarm"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td> <td ng-if="!swarm"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
<td>{{ container.IP ? container.IP : '-' }}</td> <td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td>
<td ng-if="swarm">{{ container|swarmhostname}}</td> <td ng-if="swarm">{{ container|swarmhostname}}</td>
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td> <td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
<td>{{ container.Command|truncate:60 }}</td> <td>{{ container.Command|truncate:60 }}</td>

View File

@ -4,6 +4,7 @@ function ($scope, Container, Settings, Messages, Config, errorMsgFilter) {
$scope.state = {}; $scope.state = {};
$scope.state.displayAll = Settings.displayAll; $scope.state.displayAll = Settings.displayAll;
$scope.state.displayIP = false;
$scope.sortType = 'State'; $scope.sortType = 'State';
$scope.sortReverse = true; $scope.sortReverse = true;
$scope.state.selectedItemCount = 0; $scope.state.selectedItemCount = 0;
@ -22,7 +23,11 @@ function ($scope, Container, Settings, Messages, Config, errorMsgFilter) {
containers = hideContainers(d); containers = hideContainers(d);
} }
$scope.containers = containers.map(function (container) { $scope.containers = containers.map(function (container) {
return new ContainerViewModel(container); var model = new ContainerViewModel(container);
if (model.IP) {
$scope.state.displayIP = true;
}
return model;
}); });
$('#loadContainersSpinner').hide(); $('#loadContainersSpinner').hide();
}); });

View File

@ -20,7 +20,7 @@
<!-- !name-input --> <!-- !name-input -->
<!-- image-and-registry-inputs --> <!-- image-and-registry-inputs -->
<div class="form-group"> <div class="form-group">
<label for="container_image" class="col-sm-1 control-label text-left">Name</label> <label for="container_image" class="col-sm-1 control-label text-left">Image</label>
<div class="col-sm-7"> <div class="col-sm-7">
<input type="text" class="form-control" ng-model="config.Image" id="container_image" placeholder="e.g. ubuntu:trusty"> <input type="text" class="form-control" ng-model="config.Image" id="container_image" placeholder="e.g. ubuntu:trusty">
</div> </div>

View File

@ -2,13 +2,18 @@ angular.module('createNetwork', [])
.controller('CreateNetworkController', ['$scope', '$state', 'Messages', 'Network', 'errorMsgFilter', .controller('CreateNetworkController', ['$scope', '$state', 'Messages', 'Network', 'errorMsgFilter',
function ($scope, $state, Messages, Network, errorMsgFilter) { function ($scope, $state, Messages, Network, errorMsgFilter) {
$scope.formValues = { $scope.formValues = {
DriverOptions: [] DriverOptions: [],
Subnet: '',
Gateway: ''
}; };
$scope.config = { $scope.config = {
Driver: 'bridge', Driver: 'bridge',
CheckDuplicate: true, CheckDuplicate: true,
Internal: false Internal: false,
IPAM: {
Config: []
}
}; };
$scope.addDriverOption = function() { $scope.addDriverOption = function() {
@ -36,6 +41,17 @@ function ($scope, $state, Messages, Network, errorMsgFilter) {
}); });
} }
function prepareIPAMConfiguration(config) {
if ($scope.formValues.Subnet) {
var ipamConfig = {};
ipamConfig.Subnet = $scope.formValues.Subnet;
if ($scope.formValues.Gateway) {
ipamConfig.Gateway = $scope.formValues.Gateway ;
}
config.IPAM.Config.push(ipamConfig);
}
}
function prepareDriverOptions(config) { function prepareDriverOptions(config) {
var options = {}; var options = {};
$scope.formValues.DriverOptions.forEach(function (option) { $scope.formValues.DriverOptions.forEach(function (option) {
@ -46,6 +62,7 @@ function ($scope, $state, Messages, Network, errorMsgFilter) {
function prepareConfiguration() { function prepareConfiguration() {
var config = angular.copy($scope.config); var config = angular.copy($scope.config);
prepareIPAMConfiguration(config);
prepareDriverOptions(config); prepareDriverOptions(config);
return config; return config;
} }

View File

@ -18,6 +18,18 @@
</div> </div>
</div> </div>
<!-- !name-input --> <!-- !name-input -->
<!-- subnet-gateway-inputs -->
<div class="form-group">
<label for="network_subnet" class="col-sm-1 control-label text-left">Subnet</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="formValues.Subnet" id="network_subnet" placeholder="e.g. 172.20.0.0/16">
</div>
<label for="network_gateway" class="col-sm-1 control-label text-left">Gateway</label>
<div class="col-sm-5">
<input type="text" class="form-control" ng-model="formValues.Gateway" id="network_gateway" placeholder="e.g. 172.20.10.11">
</div>
</div>
<!-- !subnet-gateway-inputs -->
<!-- driver-input --> <!-- driver-input -->
<div class="form-group"> <div class="form-group">
<label for="network_driver" class="col-sm-1 control-label text-left">Driver</label> <label for="network_driver" class="col-sm-1 control-label text-left">Driver</label>

View File

@ -9,7 +9,7 @@ function ($scope, $state, Image, Messages) {
$scope.config = { $scope.config = {
Image: '', Image: '',
Registry: '', Registry: ''
}; };
$scope.order = function(sortType) { $scope.order = function(sortType) {

View File

@ -21,16 +21,31 @@ angular.module('dockerui.filters', [])
.filter('containerstatusbadge', function () { .filter('containerstatusbadge', function () {
'use strict'; 'use strict';
return function (text) { return function (text) {
if (text === 'paused') { var status = _.toLower(text);
if (status.indexOf('paused') !== -1) {
return 'warning'; return 'warning';
} else if (text === 'created') { } else if (status.indexOf('created') !== -1) {
return 'info'; return 'info';
} else if (text === 'exited') { } else if (status.indexOf('exited') !== -1) {
return 'danger'; return 'danger';
} }
return 'success'; return 'success';
}; };
}) })
.filter('containerstatus', function () {
'use strict';
return function (text) {
var status = _.toLower(text);
if (status.indexOf('paused') !== -1) {
return 'paused';
} else if (status.indexOf('created') !== -1) {
return 'created';
} else if (status.indexOf('exited') !== -1) {
return 'stopped';
}
return 'running';
};
})
.filter('nodestatusbadge', function () { .filter('nodestatusbadge', function () {
'use strict'; 'use strict';
return function (text) { return function (text) {

View File

@ -10,9 +10,12 @@ function ImageViewModel(data) {
function ContainerViewModel(data) { function ContainerViewModel(data) {
this.Id = data.Id; this.Id = data.Id;
this.State = data.State; this.Status = data.Status;
this.Names = data.Names; this.Names = data.Names;
this.IP = data.NetworkSettings.Networks[Object.keys(data.NetworkSettings.Networks)[0]].IPAddress; // Unavailable in Docker < 1.10
if (data.NetworkSettings) {
this.IP = data.NetworkSettings.Networks[Object.keys(data.NetworkSettings.Networks)[0]].IPAddress;
}
this.Image = data.Image; this.Image = data.Image;
this.Command = data.Command; this.Command = data.Command;
this.Checked = false; this.Checked = false;

View File

@ -1,6 +1,6 @@
{ {
"name": "uifordocker", "name": "uifordocker",
"version": "1.3.0", "version": "1.4.0",
"homepage": "https://github.com/kevana/ui-for-docker", "homepage": "https://github.com/kevana/ui-for-docker",
"authors": [ "authors": [
"Michael Crosby <crosbymichael@gmail.com>", "Michael Crosby <crosbymichael@gmail.com>",

View File

@ -15,15 +15,21 @@ import (
"fmt" "fmt"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"gopkg.in/alecthomas/kingpin.v2" "gopkg.in/alecthomas/kingpin.v2"
"crypto/tls"
"crypto/x509"
) )
var ( var (
endpoint = kingpin.Flag("endpoint", "Dockerd endpoint").Default("/var/run/docker.sock").Short('e').String() endpoint = kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String()
addr = kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String() addr = kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String()
assets = kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String() assets = kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String()
data = kingpin.Flag("data", "Path to the data").Default(".").Short('d').String() data = kingpin.Flag("data", "Path to the data").Default(".").Short('d').String()
swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool() swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool()
labels = LabelParser(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')) tlsverify = kingpin.Flag("tlsverify", "TLS support").Default("false").Bool()
tlscacert = kingpin.Flag("tlscacert", "Path to the CA").Default("/certs/ca.pem").String()
tlscert = kingpin.Flag("tlscert", "Path to the TLS certificate file").Default("/certs/cert.pem").String()
tlskey = kingpin.Flag("tlskey", "Path to the TLS key").Default("/certs/key.pem").String()
labels = LabelParser(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l'))
authKey []byte authKey []byte
authKeyFile = "authKey.dat" authKeyFile = "authKey.dat"
) )
@ -32,6 +38,13 @@ type UnixHandler struct {
path string path string
} }
type TlsFlags struct {
tls bool
caPath string
certPath string
keyPath string
}
type Config struct { type Config struct {
Swarm bool `json:"swarm"` Swarm bool `json:"swarm"`
HiddenLabels Labels `json:"hiddenLabels"` HiddenLabels Labels `json:"hiddenLabels"`
@ -106,35 +119,70 @@ func configurationHandler(w http.ResponseWriter, r *http.Request, c Config) {
json.NewEncoder(w).Encode(c) json.NewEncoder(w).Encode(c)
} }
func createTcpHandler(e string) http.Handler { func createTcpHandler(u *url.URL) http.Handler {
u, err := url.Parse(e) u.Scheme = "http";
return httputil.NewSingleHostReverseProxy(u)
}
func createTlsConfig(tlsFlags TlsFlags) *tls.Config {
cert, err := tls.LoadX509KeyPair(tlsFlags.certPath, tlsFlags.keyPath)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return httputil.NewSingleHostReverseProxy(u) caCert, err := ioutil.ReadFile(tlsFlags.caPath)
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
return tlsConfig;
}
func createTcpHandlerWithTLS(u *url.URL, tlsFlags TlsFlags) http.Handler {
u.Scheme = "https";
var tlsConfig = createTlsConfig(tlsFlags)
proxy := httputil.NewSingleHostReverseProxy(u)
proxy.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
return proxy;
} }
func createUnixHandler(e string) http.Handler { func createUnixHandler(e string) http.Handler {
return &UnixHandler{e} return &UnixHandler{e}
} }
func createHandler(dir string, d string, e string, c Config) http.Handler { func createHandler(dir string, d string, e string, c Config, tlsFlags TlsFlags) http.Handler {
var ( var (
mux = http.NewServeMux() mux = http.NewServeMux()
fileHandler = http.FileServer(http.Dir(dir)) fileHandler = http.FileServer(http.Dir(dir))
h http.Handler h http.Handler
) )
u, perr := url.Parse(e)
if strings.Contains(e, "http") { if perr != nil {
h = createTcpHandler(e) log.Fatal(perr)
} else { }
if _, err := os.Stat(e); err != nil { if u.Scheme == "tcp" {
if tlsFlags.tls {
h = createTcpHandlerWithTLS(u, tlsFlags)
} else {
h = createTcpHandler(u)
}
} else if u.Scheme == "unix" {
var socketPath = u.Path
if _, err := os.Stat(socketPath); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Fatalf("unix socket %s does not exist", e) log.Fatalf("unix socket %s does not exist", socketPath)
} }
log.Fatal(err) log.Fatal(err)
} }
h = createUnixHandler(e) h = createUnixHandler(socketPath)
} else {
log.Fatalf("Bad Docker enpoint: %s. Only unix:// and tcp:// are supported.", e)
} }
// Use existing csrf authKey if present or generate a new one. // Use existing csrf authKey if present or generate a new one.
@ -173,7 +221,7 @@ func csrfWrapper(h http.Handler) http.Handler {
} }
func main() { func main() {
kingpin.Version("1.3.0") kingpin.Version("1.4.0")
kingpin.Parse() kingpin.Parse()
configuration := Config{ configuration := Config{
@ -181,7 +229,14 @@ func main() {
HiddenLabels: *labels, HiddenLabels: *labels,
} }
handler := createHandler(*assets, *data, *endpoint, configuration) tlsFlags := TlsFlags{
tls: *tlsverify,
caPath: *tlscacert,
certPath: *tlscert,
keyPath: *tlskey,
}
handler := createHandler(*assets, *data, *endpoint, configuration, tlsFlags)
if err := http.ListenAndServe(*addr, handler); err != nil { if err := http.ListenAndServe(*addr, handler); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -40,6 +40,7 @@ module.exports = function (grunt) {
grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']); grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']);
grunt.registerTask('run-swarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm', 'watch:buildSwarm']); grunt.registerTask('run-swarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm', 'watch:buildSwarm']);
grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']); grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']);
grunt.registerTask('run-ssl', ['if:binaryNotExist', 'shell:buildImage', 'shell:runSsl', 'watch:buildSsl']);
grunt.registerTask('clear', ['clean:app']); grunt.registerTask('clear', ['clean:app']);
// Print a timestamp (useful for when watching) // Print a timestamp (useful for when watching)
@ -224,6 +225,10 @@ module.exports = function (grunt) {
buildSwarm: { buildSwarm: {
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'], files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'],
tasks: ['build', 'shell:buildImage', 'shell:runSwarm', 'shell:cleanImages'] tasks: ['build', 'shell:buildImage', 'shell:runSwarm', 'shell:cleanImages']
},
buildSsl: {
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'],
tasks: ['build', 'shell:buildImage', 'shell:runSsl', 'shell:cleanImages']
} }
}, },
jshint: { jshint: {
@ -267,7 +272,14 @@ module.exports = function (grunt) {
command: [ command: [
'docker stop ui-for-docker', 'docker stop ui-for-docker',
'docker rm ui-for-docker', 'docker rm ui-for-docker',
'docker run --privileged -d -p 9000:9000 -v /tmp/docker-ui:/data --name ui-for-docker ui-for-docker -e http://10.0.7.10:4000 --swarm -d /data' 'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data --name ui-for-docker ui-for-docker -H tcp://10.0.7.10:4000 --swarm -d /data'
].join(';')
},
runSsl: {
command: [
'docker stop ui-for-docker',
'docker rm ui-for-docker',
'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data -v /tmp/docker-ssl:/certs --name ui-for-docker ui-for-docker -H tcp://10.0.7.10:2376 -d /data --tlsverify'
].join(';') ].join(';')
}, },
cleanImages: { cleanImages: {

View File

@ -2,7 +2,7 @@
"author": "Michael Crosby & Kevan Ahlquist", "author": "Michael Crosby & Kevan Ahlquist",
"name": "uifordocker", "name": "uifordocker",
"homepage": "https://github.com/kevana/ui-for-docker", "homepage": "https://github.com/kevana/ui-for-docker",
"version": "1.3.0", "version": "1.4.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:kevana/ui-for-docker.git" "url": "git@github.com:kevana/ui-for-docker.git"