merge feat66-docker-cli-compliant

pull/119/head
Anthony Lapenna 2016-07-13 13:43:58 +12:00
commit dc721f5870
11 changed files with 325 additions and 39 deletions

View File

@ -28,11 +28,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
@ -43,7 +48,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.
@ -56,6 +61,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.
@ -86,10 +109,14 @@ $ 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 (format *LABEL_NAME=LABEL_VALUE*)
* `--registries`, `-r`: Available registries in the UI (format *REGISTRY_NAME=REGISTRY_ADDRESS*) * `--registries`, `-r`: Available registries in the UI (format *REGISTRY_NAME=REGISTRY_ADDRESS*)
* `--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`)
* `--hide-label`, `-l`: Hide containers with a specific label in the UI

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

@ -14,6 +14,7 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
}; };
$scope.imageConfig = {}; $scope.imageConfig = {};
$scope.config = { $scope.config = {
Env: [], Env: [],
HostConfig: { HostConfig: {

View File

@ -0,0 +1,74 @@
angular.module('createNetwork', [])
.controller('CreateNetworkController', ['$scope', '$state', 'Messages', 'Network', 'errorMsgFilter',
function ($scope, $state, Messages, Network, errorMsgFilter) {
$scope.formValues = {
DriverOptions: [],
Subnet: '',
Gateway: ''
};
$scope.config = {
Driver: 'bridge',
CheckDuplicate: true,
Internal: false,
IPAM: {
Config: []
}
};
$scope.addDriverOption = function() {
$scope.formValues.DriverOptions.push({ name: '', value: '' });
};
$scope.removeDriverOption = function(index) {
$scope.formValues.DriverOptions.splice(index, 1);
};
function createNetwork(config) {
$('#createNetworkSpinner').show();
Network.create(config, function (d) {
if (d.Id) {
Messages.send("Network created", d.Id);
$('#createNetworkSpinner').hide();
$state.go('networks', {}, {reload: true});
} else {
$('#createNetworkSpinner').hide();
Messages.error('Unable to create network', errorMsgFilter(d));
}
}, function (e) {
$('#createNetworkSpinner').hide();
Messages.error('Unable to create network', e.data);
});
}
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) {
var options = {};
$scope.formValues.DriverOptions.forEach(function (option) {
options[option.name] = option.value;
});
config.Options = options;
}
function prepareConfiguration() {
var config = angular.copy($scope.config);
prepareIPAMConfiguration(config);
prepareDriverOptions(config);
return config;
}
$scope.create = function () {
var config = prepareConfiguration();
createNetwork(config);
};
}]);

View File

@ -0,0 +1,95 @@
<rd-header>
<rd-header-title title="Create network"></rd-header-title>
<rd-header-content>
Networks > Add network
</rd-header-content>
</rd-header>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal">
<!-- name-input -->
<div class="form-group">
<label for="network_name" class="col-sm-1 control-label text-left">Name</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="config.Name" id="network_name" placeholder="e.g. myNetwork">
</div>
</div>
<!-- !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 -->
<div class="form-group">
<label for="network_driver" class="col-sm-1 control-label text-left">Driver</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="config.Driver" id="network_driver" placeholder="e.g. driverName">
</div>
</div>
<!-- !driver-input -->
<!-- driver-options -->
<div class="form-group">
<label for="network_driveropts" class="col-sm-1 control-label text-left">Driver options</label>
<div class="col-sm-11">
<span class="label label-default clickable" ng-click="addDriverOption()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> driver option
</span>
</div>
<!-- driver-options-input-list -->
<div class="col-sm-offset-1 col-sm-11 form-inline" style="margin-top: 10px;">
<div ng-repeat="option in formValues.DriverOptions" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">name</span>
<input type="text" class="form-control" ng-model="option.name" placeholder="e.g. com.docker.network.bridge.enable_icc">
</div>
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="option.value" placeholder="e.g. true">
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="removeDriverOption($index)">
<i class="fa fa-minus" aria-hidden="true"></i>
</button>
</span>
</div>
</div>
</div>
<!-- !driver-options-input-list -->
</div>
<!-- !driver-options -->
<!-- internal -->
<div class="form-group">
<div class="col-sm-12">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="config.Internal"> Restrict external access to the network
</label>
</div>
</div>
</div>
<!-- !internal -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12" style="text-align: center;">
<div>
<i id="createNetworkSpinner" class="fa fa-cog fa-3x fa-spin" style="margin-bottom: 5px; display: none;"></i>
</div>
<button type="button" class="btn btn-default btn-lg" ng-click="create()">Create</button>
<a type="button" class="btn btn-default btn-lg" ui-sref="networks">Cancel</a>
</div>
</div>

View File

@ -4,7 +4,6 @@
<i class="fa fa-refresh" aria-hidden="true"></i> <i class="fa fa-refresh" aria-hidden="true"></i>
</a> </a>
</rd-header-title> </rd-header-title>
<rd-header-content>Images</rd-header-content> <rd-header-content>Images</rd-header-content>
</rd-header> </rd-header>

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

@ -15,16 +15,22 @@ 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'))
registries = LabelParser(kingpin.Flag("registries", "Supported Docker registries").Short('r')) registries = LabelParser(kingpin.Flag("registries", "Supported Docker registries").Short('r'))
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"
) )
@ -33,6 +39,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"`
@ -108,35 +121,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.
@ -184,7 +232,14 @@ func main() {
Registries: *registries, Registries: *registries,
} }
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 -r local=192.168.2.193:5000' '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: {