diff --git a/README.md b/README.md index 5810918db..db5f23915 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Cloudinovasi UI for Docker +# Cloudinovasi UI for Docker A fork of the amazing UI for Docker by Michael Crosby and Kevan Ahlquist (https://github.com/kevana/ui-for-docker) using the rdash-angular theme (https://github.com/rdash/rdash-angular). @@ -6,11 +6,14 @@ 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 +## 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. +## Run + ### Quickstart + 1. Run: `docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui` 2. Open your browser to `http://:9000` @@ -25,11 +28,54 @@ By default UI For Docker connects to the Docker daemon with`/var/run/docker.sock You can use the `-e` flag to change this socket: - # Connect to a tcp socket: - $ docker run -d -p 9000:9000 --privileged cloudinovasi/cloudinovasi-ui -e http://127.0.0.1:2375 +``` +# Connect to a tcp socket: +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://127.0.0.1:2375 +``` + +### Swarm support + +**Supported Swarm version: 1.2.3** + +You can access a specific view for you Swarm cluster by defining the `-swarm` flag: + +``` +# Connect to a tcp socket and enable Swarm: +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://: -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. ### Change address/port UI For Docker is served on UI For Docker listens on port 9000 by default. If you run UI For Docker inside a container then you can bind the container's internal port to any external address and port: - # Expose UI For Docker on 10.20.30.1:80 - $ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-ui +``` +# Expose UI For Docker on 10.20.30.1:80 +$ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock cloudinovasi/cloudinovasi-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. + +For example, take a container started with the label `owner=acme`: + +``` +$ docker run -d --label owner=acme nginx +``` + +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 +``` + +### Available options + +The following options are available for the `ui-for-docker` binary: + +* `-endpoint`, `-e`: Docker deamon endpoint (default: *"/var/run/docker.sock"*) +* `-bind`, `-p`: Address and port to serve UI For Docker (default: *":9000"*) +* `-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 diff --git a/app/app.js b/app/app.js index e707b41b9..0c3158372 100644 --- a/app/app.js +++ b/app/app.js @@ -120,4 +120,4 @@ angular.module('uifordocker', [ .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', '/config') - .constant('UI_VERSION', 'v1.0.3'); + .constant('UI_VERSION', 'v1.0.4'); diff --git a/app/components/containers/containersController.js b/app/components/containers/containersController.js index 0672cda7f..c16e101f2 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', 'ViewSpinner', -function ($scope, Container, Settings, Messages, ViewSpinner) { +.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner', 'Config', +function ($scope, Container, Settings, Messages, ViewSpinner, Config) { $scope.state = {}; $scope.state.displayAll = Settings.displayAll; @@ -18,9 +18,11 @@ function ($scope, Container, Settings, Messages, ViewSpinner) { ViewSpinner.spin(); $scope.state.selectedItemCount = 0; Container.query(data, function (d) { - $scope.containers = d.filter(function (container) { - return container.Image !== 'swarm'; - }).map(function (container) { + var containers = d; + if (hiddenLabels) { + containers = hideContainers(d); + } + $scope.containers = containers.map(function (container) { return new ContainerViewModel(container); }); ViewSpinner.stop(); @@ -134,5 +136,24 @@ function ($scope, Container, Settings, Messages, ViewSpinner) { batch($scope.containers, Container.remove, "Removed"); }; - update({all: Settings.displayAll ? 1 : 0}); + var hideContainers = function (containers) { + return containers.filter(function (container) { + var filterContainer = false; + hiddenLabels.forEach(function(label, index) { + if (_.has(container.Labels, label.name) && + container.Labels[label.name] === label.value) { + filterContainer = true; + } + }); + if (!filterContainer) { + return container; + } + }); + }; + + var hiddenLabels; + Config.$promise.then(function (c) { + hiddenLabels = c.hiddenLabels; + update({all: Settings.displayAll ? 1 : 0}); + }); }]); diff --git a/app/components/dashboard/master-ctrl.js b/app/components/dashboard/master-ctrl.js index 330952b41..9d5080005 100644 --- a/app/components/dashboard/master-ctrl.js +++ b/app/components/dashboard/master-ctrl.js @@ -9,7 +9,7 @@ angular.module('dashboard') return window.innerWidth; }; - $scope.config = Config.get(); + $scope.config = Config; $scope.$watch($scope.getWidth, function(newValue, oldValue) { if (newValue >= mobileView) { diff --git a/app/components/swarm/swarm.html b/app/components/swarm/swarm.html index ee95f7115..19c621a2b 100644 --- a/app/components/swarm/swarm.html +++ b/app/components/swarm/swarm.html @@ -115,6 +115,13 @@ + + + Engine + + + + Status @@ -129,6 +136,7 @@ {{ node.name }} {{ node.ip }} {{ node.containers }} + {{ node.version }} {{ node.status }} diff --git a/app/components/swarm/swarmController.js b/app/components/swarm/swarmController.js index 3734383da..a3b096076 100644 --- a/app/components/swarm/swarmController.js +++ b/app/components/swarm/swarmController.js @@ -42,7 +42,7 @@ angular.module('swarm', []) var node_offset = 4; for (i = 0; i < node_count; i++) { extractNodeInfo(info, node_offset); - node_offset += 9; + node_offset += 10; } } @@ -50,7 +50,8 @@ angular.module('swarm', []) var node = {}; node.name = info[offset][0]; node.ip = info[offset][1]; - node.status = info[offset + 1][1]; + node.id = info[offset + 1][1]; + node.status = info[offset + 2][1]; node.containers = info[offset + 2][1]; node.cpu = info[offset + 3][1]; node.memory = info[offset + 4][1]; diff --git a/app/shared/services.js b/app/shared/services.js index 5a16cb192..1085cdd58 100644 --- a/app/shared/services.js +++ b/app/shared/services.js @@ -143,7 +143,7 @@ angular.module('dockerui.services', ['ngResource', 'ngSanitize']) }); }]) .factory('Config', ['$resource', 'CONFIG_ENDPOINT', function($resource, CONFIG_ENDPOINT) { - return $resource(CONFIG_ENDPOINT); + return $resource(CONFIG_ENDPOINT).get(); }]) .factory('Settings', ['DOCKER_ENDPOINT', 'DOCKER_PORT', 'UI_VERSION', function SettingsFactory(DOCKER_ENDPOINT, DOCKER_PORT, UI_VERSION) { 'use strict'; diff --git a/assets/css/app.css b/assets/css/app.css index 0d99e9de8..b8d40de69 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -116,8 +116,11 @@ .logo { display: inline; - width: 110px; - max-height: 38px; + width: 100%; + max-width: 160px; + height: 100%; + max-height: 55px; + margin-bottom: 5px; } .containerNameInput { diff --git a/bower.json b/bower.json index 91fd79d08..f4b19b158 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "uifordocker", - "version": "1.0.3", + "version": "1.0.4", "homepage": "https://github.com/kevana/ui-for-docker", "authors": [ "Michael Crosby ", diff --git a/dockerui.go b/dockerui.go index fd20836b7..7497436d1 100644 --- a/dockerui.go +++ b/dockerui.go @@ -1,7 +1,6 @@ package main // import "github.com/cloudinovasi/ui-for-docker" import ( - "flag" "io" "log" "net" @@ -15,13 +14,15 @@ import ( "io/ioutil" "fmt" "github.com/gorilla/securecookie" + "gopkg.in/alecthomas/kingpin.v2" ) var ( - endpoint = flag.String("e", "/var/run/docker.sock", "Dockerd endpoint") - addr = flag.String("p", ":9000", "Address and port to serve UI For Docker") - assets = flag.String("a", ".", "Path to the assets") - swarm = flag.Bool("s", false, "Swarm mode") + endpoint = kingpin.Flag("endpoint", "Dockerd endpoint").Default("/var/run/docker.sock").Short('e').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() + 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')) authKey []byte authKeyFile = "authKey.dat" ) @@ -32,6 +33,40 @@ type UnixHandler struct { type Config struct { Swarm bool `json:"swarm"` + HiddenLabels Labels `json:"hiddenLabels"` +} + +type Label struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type Labels []Label + +func (l *Labels) Set(value string) error { + parts := strings.SplitN(value, "=", 2) + if len(parts) != 2 { + return fmt.Errorf("expected HEADER=VALUE got '%s'", value) + } + label := new(Label) + label.Name = parts[0] + label.Value = parts[1] + *l = append(*l, *label) + return nil +} + +func (l *Labels) String() string { + return "" +} + +func (l *Labels) IsCumulative() bool { + return true +} + +func LabelParser(s kingpin.Settings) (target *[]Label) { + target = new([]Label) + s.SetValue((*Labels)(target)) + return } func (h *UnixHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -82,7 +117,7 @@ func createUnixHandler(e string) http.Handler { return &UnixHandler{e} } -func createHandler(dir string, e string, s bool) http.Handler { +func createHandler(dir string, e string, c Config) http.Handler { var ( mux = http.NewServeMux() fileHandler = http.FileServer(http.Dir(dir)) @@ -120,14 +155,10 @@ func createHandler(dir string, e string, s bool) http.Handler { csrf.Secure(false), ) - configuration := Config{ - Swarm: s, - } - mux.Handle("/dockerapi/", http.StripPrefix("/dockerapi", h)) mux.Handle("/", fileHandler) mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { - configurationHandler(w, r, configuration) + configurationHandler(w, r, c) }) return CSRF(csrfWrapper(mux)) } @@ -140,9 +171,15 @@ func csrfWrapper(h http.Handler) http.Handler { } func main() { - flag.Parse() + kingpin.Version("1.0.4") + kingpin.Parse() - handler := createHandler(*assets, *endpoint, *swarm) + configuration := Config{ + Swarm: *swarm, + HiddenLabels: *labels, + } + + handler := createHandler(*assets, *endpoint, configuration) if err := http.ListenAndServe(*addr, handler); err != nil { log.Fatal(err) } diff --git a/gruntFile.js b/gruntFile.js index fd6c06e01..7f54ff697 100644 --- a/gruntFile.js +++ b/gruntFile.js @@ -24,7 +24,7 @@ module.exports = function (grunt) { 'copy' ]); grunt.registerTask('release', [ - 'clean:app', + 'clean:all', 'if:binaryNotExist', 'html2js', 'uglify', @@ -267,7 +267,7 @@ module.exports = function (grunt) { command: [ 'docker stop ui-for-docker', 'docker rm ui-for-docker', - 'docker run --privileged -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --name ui-for-docker ui-for-docker -s' + 'docker run --privileged -d -p 9000:9000 --name ui-for-docker ui-for-docker -e http://10.0.7.10:4000 -swarm' ].join(';') }, cleanImages: { diff --git a/package.json b/package.json index 49649fdd4..536b45b17 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Michael Crosby & Kevan Ahlquist", "name": "uifordocker", "homepage": "https://github.com/kevana/ui-for-docker", - "version": "1.0.3", + "version": "1.0.4", "repository": { "type": "git", "url": "git@github.com:kevana/ui-for-docker.git"