diff --git a/README.md b/README.md
index 89a0c7d31..f7d63d6a5 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@ A fork of the amazing UI for Docker by Michael Crosby and Kevan Ahlquist (https:
UI For Docker is a web interface for the Docker Remote API. The goal is to provide a pure client side implementation so it is effortless to connect and manage docker.
## Goals
+
* Minimal dependencies - I really want to keep this project a pure html/js app.
* Consistency - The web UI should be consistent with the commands found on the docker CLI.
@@ -14,7 +15,7 @@ UI For Docker is a web interface for the Docker Remote API. The goal is to prov
The current Docker version support policy is the following: `N` to `N-2` included where `N` is the latest version.
-At the moment, the following versions are supported: 1.9, 1.10 & 1.11.
+At the moment, the following versions are supported: 1.9, 1.10 & 1.11.
## Run
@@ -83,9 +84,21 @@ $ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/cer
*Note*: Replace `/path/to/certs` to the path to the certificate files on your disk.
+### Use your own logo
+
+You can use the `--logo` flag to specify an URL to your own logo.
+
+For example, using the Docker logo:
+
+```
+$ docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui --logo "https://www.docker.com/sites/all/themes/docker/assets/images/brand-full.svg"
+```
+
+The custom logo will replace the CloudInovasi logo in the UI.
+
### 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.
For example, take a container started with the label `owner=acme`:
@@ -99,6 +112,36 @@ You can hide it in the view by starting the ui with:
$ docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui -l owner=acme
```
+### Reverse proxy configuration
+
+Has been tested with Nginx 1.11.
+
+Use the following configuration to host the UI at `myhost.mydomain.com/dockerui`:
+
+```nginx
+upstream cloudinovasi-ui {
+ server ADDRESS:PORT;
+}
+
+server {
+ listen 80;
+
+ location /dockerui/ {
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_pass http://cloudinovasi-ui/;
+ }
+ location /dockerui/ws/ {
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_http_version 1.1;
+ proxy_pass http://cloudinovasi-ui/ws/;
+ }
+}
+```
+
+Replace `ADDRESS:PORT` with the CloudInovasi UI container details.
+
### Available options
The following options are available for the `ui-for-docker` binary:
@@ -108,8 +151,9 @@ The following options are available for the `ui-for-docker` binary:
* `--data`, `-d`: Path to the data folder (default: `"."`)
* `--assets`, `-a`: Path to the assets (default: `"."`)
* `--swarm`, `-s`: Swarm cluster support (default: `false`)
-* `--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`)
+* `--hide-label`, `-l`: Hide containers with a specific label in the UI
+* `--logo`: URL to a picture to be displayed as a logo in the UI
diff --git a/api/main.go b/api/main.go
index a2c5a0f15..877271926 100644
--- a/api/main.go
+++ b/api/main.go
@@ -6,7 +6,7 @@ import (
// main is the entry point of the program
func main() {
- kingpin.Version("1.6.0")
+ kingpin.Version("1.7.0")
var (
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()
@@ -18,6 +18,7 @@ func main() {
tlskey = kingpin.Flag("tlskey", "Path to the TLS key").Default("/certs/key.pem").String()
swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool()
labels = pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l'))
+ logo = kingpin.Flag("logo", "URL for the logo displayed in the UI").String()
)
kingpin.Parse()
@@ -36,6 +37,7 @@ func main() {
settings := &Settings{
Swarm: *swarm,
HiddenLabels: *labels,
+ Logo: *logo,
}
api := newAPI(apiConfig)
diff --git a/api/settings.go b/api/settings.go
index 801904f61..b707a8e69 100644
--- a/api/settings.go
+++ b/api/settings.go
@@ -9,6 +9,7 @@ import (
type Settings struct {
Swarm bool `json:"swarm"`
HiddenLabels pairList `json:"hiddenLabels"`
+ Logo string `json:"logo"`
}
// configurationHandler defines a handler function used to encode the configuration in JSON
diff --git a/app/app.js b/app/app.js
index 206161c75..9cb8457d7 100644
--- a/app/app.js
+++ b/app/app.js
@@ -155,5 +155,5 @@ angular.module('uifordocker', [
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
.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('CONFIG_ENDPOINT', 'settings')
- .constant('UI_VERSION', 'v1.6.0');
+ .constant('CONFIG_ENDPOINT', 'settings')
+ .constant('UI_VERSION', 'v1.7.0');
diff --git a/app/components/container/container.html b/app/components/container/container.html
index bc76f31cc..52e203069 100644
--- a/app/components/container/container.html
+++ b/app/components/container/container.html
@@ -84,7 +84,7 @@
Created |
- {{ container.Created | date: 'medium' }} |
+ {{ container.Created|getisodate }} |
Path |
@@ -186,7 +186,8 @@
{{key}} |
- {{ val }} |
+ {{val|getisodate}} |
+ {{val}} |
diff --git a/app/components/containerConsole/containerConsoleController.js b/app/components/containerConsole/containerConsoleController.js
index 566529458..644e5f3dd 100644
--- a/app/components/containerConsole/containerConsoleController.js
+++ b/app/components/containerConsole/containerConsoleController.js
@@ -36,7 +36,12 @@ function ($scope, $stateParams, Settings, Container, Exec, $timeout, Messages, e
if (d.Id) {
var execId = d.Id;
resizeTTY(execId, termHeight, termWidth);
- var url = window.location.href.split('#')[0].replace('http://', 'ws://') + 'ws/exec?id=' + execId;
+ var url = window.location.href.split('#')[0] + 'ws/exec?id=' + execId;
+ if (url.indexOf('https') > -1) {
+ url = url.replace('https://', 'wss://');
+ } else {
+ url = url.replace('http://', 'ws://');
+ }
initTerm(url, termHeight, termWidth);
} else {
$('#loadConsoleSpinner').hide();
diff --git a/app/components/containers/containers.html b/app/components/containers/containers.html
index 0a7ca115a..844d247ea 100644
--- a/app/components/containers/containers.html
+++ b/app/components/containers/containers.html
@@ -89,7 +89,7 @@
{{ container|swarmcontainername}} |
{{ container|containername}} |
{{ container.IP ? container.IP : '-' }} |
- {{ container|swarmhostname}} |
+ {{ container.hostIP }} |
{{ container.Image }} |
{{ container.Command|truncate:60 }} |
diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js
index b7662e243..0db7b4dc8 100644
--- a/app/components/containers/containersController.js
+++ b/app/components/containers/containersController.js
@@ -1,6 +1,6 @@
angular.module('containers', [])
-.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'Config', 'errorMsgFilter',
-function ($scope, Container, Settings, Messages, Config, errorMsgFilter) {
+.controller('ContainersController', ['$scope', 'Container', 'Info', 'Settings', 'Messages', 'Config', 'errorMsgFilter',
+function ($scope, Container, Info, Settings, Messages, Config, errorMsgFilter) {
$scope.state = {};
$scope.state.displayAll = Settings.displayAll;
@@ -27,6 +27,9 @@ function ($scope, Container, Settings, Messages, Config, errorMsgFilter) {
if (model.IP) {
$scope.state.displayIP = true;
}
+ if ($scope.swarm) {
+ model.hostIP = $scope.swarm_hosts[_.split(container.Names[0], '/')[1]];
+ }
return model;
});
$('#loadContainersSpinner').hide();
@@ -154,10 +157,32 @@ function ($scope, Container, Settings, Messages, Config, errorMsgFilter) {
});
};
+ function retrieveSwarmHostsInfo(data) {
+ var swarm_hosts = {};
+ var systemStatus = data.SystemStatus;
+ var node_count = parseInt(systemStatus[3][1], 10);
+ var node_offset = 4;
+ for (i = 0; i < node_count; i++) {
+ var host = {};
+ host.name = _.trim(systemStatus[node_offset][0]);
+ host.ip = _.split(systemStatus[node_offset][1], ':')[0];
+ swarm_hosts[host.name] = host.ip;
+ node_offset += 9;
+ }
+ return swarm_hosts;
+ }
+
$scope.swarm = false;
Config.$promise.then(function (c) {
hiddenLabels = c.hiddenLabels;
$scope.swarm = c.swarm;
- update({all: Settings.displayAll ? 1 : 0});
+ if (c.swarm) {
+ Info.get({}, function (d) {
+ $scope.swarm_hosts = retrieveSwarmHostsInfo(d);
+ update({all: Settings.displayAll ? 1 : 0});
+ });
+ } else {
+ update({all: Settings.displayAll ? 1 : 0});
+ }
});
}]);
diff --git a/app/components/createContainer/createContainerController.js b/app/components/createContainer/createContainerController.js
index 7a761c04d..623da4eb8 100644
--- a/app/components/createContainer/createContainerController.js
+++ b/app/components/createContainer/createContainerController.js
@@ -9,6 +9,7 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
$scope.formValues = {
Console: 'none',
Volumes: [],
+ AvailableRegistries: [],
Registry: ''
};
@@ -16,6 +17,7 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
$scope.config = {
Env: [],
+ ExposedPorts: {},
HostConfig: {
RestartPolicy: {
Name: 'no'
@@ -27,12 +29,8 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
}
};
- $scope.resetVolumePath = function(index) {
- $scope.formValues.Volumes[index].name = '';
- };
-
$scope.addVolume = function() {
- $scope.formValues.Volumes.push({ name: '', containerPath: '', readOnly: false, isPath: false });
+ $scope.formValues.Volumes.push({ name: '', containerPath: '' });
};
$scope.removeVolume = function(index) {
@@ -58,6 +56,8 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
Config.$promise.then(function (c) {
var swarm = c.swarm;
+ $scope.formValues.AvailableRegistries = c.registries;
+
Volume.query({}, function (d) {
$scope.availableVolumes = d.Volumes;
}, function (e) {
@@ -72,6 +72,7 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
return network;
}
});
+ $scope.globalNetworkCount = networks.length;
networks.push({Name: "bridge"});
networks.push({Name: "host"});
networks.push({Name: "none"});
@@ -149,7 +150,16 @@ function ($scope, $state, Config, Container, Image, Volume, Network, Messages, e
config.HostConfig.PortBindings.forEach(function (portBinding) {
if (portBinding.hostPort && portBinding.containerPort) {
var key = portBinding.containerPort + "/" + portBinding.protocol;
- bindings[key] = [{ HostPort: portBinding.hostPort }];
+ var binding = {};
+ if (portBinding.hostPort.indexOf(':') > -1) {
+ var hostAndPort = portBinding.hostPort.split(':');
+ binding.HostIp = hostAndPort[0];
+ binding.HostPort = hostAndPort[1];
+ } else {
+ binding.HostPort = portBinding.hostPort;
+ }
+ bindings[key] = [binding];
+ config.ExposedPorts[key] = {};
}
});
config.HostConfig.PortBindings = bindings;
diff --git a/app/components/createContainer/createcontainer.html b/app/components/createContainer/createcontainer.html
index c6bd3d04f..3eb25b6f0 100644
--- a/app/components/createContainer/createcontainer.html
+++ b/app/components/createContainer/createcontainer.html
@@ -107,7 +107,6 @@
Network
Security/Host
-