Merge branch 'dev'

pull/2/head
Michael Crosby 2013-06-12 19:08:24 -09:00
commit b77fb1ad03
11 changed files with 126 additions and 96 deletions

View File

@ -5,19 +5,40 @@ DockerUI is a web interface to interact with the Remote API. The goal is to pro
![Container](/container.png) ![Container](/container.png)
###Goals
* Little to no dependencies - I really want to keep this project a pure html/js app. You can drop the docker binary on your server run so I want to be able to drop these html files on your server and go.
###Installation ###Installation
Open js/app.js and change the DOCKER_ENDPOINT constant to your docker ip and port. Then host the site like any other html/js application. Open js/app.js and change the DOCKER_ENDPOINT constant to your docker ip and port. Then host the site like any other html/js application.
.constant('DOCKER_ENDPOINT', 'http://192.168.1.9:4243\:4243'); .constant('DOCKER_ENDPOINT', 'http://192.168.1.9:4243\:4243');
###Remote API Version
DockerUI currently supports the v1.1 Remote API
###Stack ###Stack
* Angular.js * Angular.js
* Flatstrap ( Flat Twitter Bootstrap ) * Flatstrap ( Flat Twitter Bootstrap )
###Todo:
I work fast so it will not be long before these changes are impelmented.
* Multiple endpoints
* Full repository support
* Search
* Create images via Dockerfile
* Push files to a container
* Unit tests
###License - MIT ###License - MIT
The DockerUI code is licensed under the MIT license. Flatstrap(bootstrap) is licensed under the Apache License v2.0 and Angular.js is licensed under MIT.
**DockerUI:**
Copyright (c) 2013 Michael Crosby. crosbymichael.com Copyright (c) 2013 Michael Crosby. crosbymichael.com
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person

View File

@ -70,6 +70,11 @@
margin: 0 auto; margin: 0 auto;
} }
.center {
width: 80%;
margin: 0 auto;
}
.btn-remove { .btn-remove {
margin: 0 auto; margin: 0 auto;
max-width: 70%; max-width: 70%;
@ -78,3 +83,7 @@
.actions { .actions {
margin: 0 auto; margin: 0 auto;
} }
.container-bottom {
height: 50px;
}

View File

@ -29,12 +29,13 @@
<div class="container"> <div class="container">
<div ng-include="template" ng-controller="MastheadController" ></div> <div ng-include="template" ng-controller="MastheadController" ></div>
<div id="view" ng-view> <div id="view" ng-view>
</div> </div>
<div class="footer"> <div class="container-bottom"></div>
<p>Created by:<a href="http://crosbymichael.com">Michael Crosby</a></p> <div class="footer center well">
<p>Created by: <a href="http://crosbymichael.com">crosbymichael</a></p>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
angular.module('dockerui', ['dockerui.services', 'dockerui.filters']) angular.module('dockerui', ['dockerui.services', 'dockerui.filters'])
.config(['$routeProvider', function ($routeProvider) { .config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {templateUrl: 'partials/home.html', controller: 'HomeController'}); $routeProvider.when('/', {templateUrl: 'partials/dashboard.html', controller: 'DashboardController'});
$routeProvider.when('/containers/', {templateUrl: 'partials/containers.html', controller: 'ContainersController'}); $routeProvider.when('/containers/', {templateUrl: 'partials/containers.html', controller: 'ContainersController'});
$routeProvider.when('/containers/:id/', {templateUrl: 'partials/container.html', controller: 'ContainerController'}); $routeProvider.when('/containers/:id/', {templateUrl: 'partials/container.html', controller: 'ContainerController'});
$routeProvider.when('/images/', {templateUrl: 'partials/images.html', controller: 'ImagesController'}); $routeProvider.when('/images/', {templateUrl: 'partials/images.html', controller: 'ImagesController'});
@ -10,4 +10,6 @@ angular.module('dockerui', ['dockerui.services', 'dockerui.filters'])
$routeProvider.when('/settings', {templateUrl: 'partials/settings.html', controller: 'SettingsController'}); $routeProvider.when('/settings', {templateUrl: 'partials/settings.html', controller: 'SettingsController'});
$routeProvider.otherwise({redirectTo: '/'}); $routeProvider.otherwise({redirectTo: '/'});
}]) }])
.constant('DOCKER_ENDPOINT', 'http://192.168.1.9:4243\:4243'); // This is your docker url that the api will use to make requests
.constant('DOCKER_ENDPOINT', 'http://192.168.1.9:4243\:4243')
.constant('DOCKER_API_VERSION', '/v1.1');

View File

@ -33,42 +33,42 @@ function MastheadController($scope) {
}; };
} }
function HomeController() { function DashboardController($scope, Container) {
} }
function SideBarController($scope, Container, Settings) {
$scope.template = 'partials/sidebar.html';
$scope.containers = [];
$scope.endpoint = Settings.endpoint;
Container.query({all: 0}, function(d) {
$scope.containers = d;
});
}
function SettingsController($scope, Auth, System, Docker, Settings) { function SettingsController($scope, Auth, System, Docker, Settings) {
$scope.auth = {}; $scope.auth = {};
$scope.info = {}; $scope.info = {};
$scope.docker = {}; $scope.docker = {};
$scope.endpoint = Settings.endpoint;
$scope.apiVersion = Settings.version;
$('#response').hide(); $('#response').hide();
$scope.alertClass = 'block'; $scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.updateAuthInfo = function() { $scope.updateAuthInfo = function() {
if ($scope.auth.password != $scope.auth.cpassword) { if ($scope.auth.password != $scope.auth.cpassword) {
$scope.response = 'Your passwords do not match.'; setSuccessfulResponse($scope, 'Your passwords do not match.', '#response');
showAndHide(true);
return; return;
} }
Auth.update( Auth.update(
{username: $scope.auth.username, email: $scope.auth.email, password: $scope.auth.password}, function(d) { {username: $scope.auth.username, email: $scope.auth.email, password: $scope.auth.password}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Auto information updated.', '#response');
$scope.response = 'Auth information updated.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
}; };
@ -89,52 +89,33 @@ function ContainerController($scope, $routeParams, $location, Container) {
$('#response').hide(); $('#response').hide();
$scope.alertClass = 'block'; $scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.start = function(){ $scope.start = function(){
Container.start({id: $routeParams.id}, function(d) { Container.start({id: $routeParams.id}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Container started.', '#response');
$scope.response = 'Container started.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
}; };
$scope.stop = function() { $scope.stop = function() {
Container.stop({id: $routeParams.id}, function(d) { Container.stop({id: $routeParams.id}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Container stopped.', '#response');
$scope.response = 'Container stopped.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
}; };
$scope.kill = function() { $scope.kill = function() {
Container.kill({id: $routeParams.id}, function(d) { Container.kill({id: $routeParams.id}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Container killed.', '#response');
$scope.response = 'Container killed.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
}; };
@ -142,14 +123,10 @@ function ContainerController($scope, $routeParams, $location, Container) {
if (confirm("Are you sure you want to remove the container?")) { if (confirm("Are you sure you want to remove the container?")) {
Container.remove({id: $routeParams.id}, function(d) { Container.remove({id: $routeParams.id}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Container removed.', '#response');
$scope.response = 'Container removed.';
showAndHide(true);
}, function(e){ }, function(e){
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
} }
}; };
@ -211,25 +188,14 @@ function ImageController($scope, $routeParams, $location, Image) {
$('#response').hide(); $('#response').hide();
$scope.alertClass = 'block'; $scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.remove = function() { $scope.remove = function() {
if (confirm("Are you sure you want to delete this image?")) { if (confirm("Are you sure you want to delete this image?")) {
Image.remove({id: $routeParams.id}, function(d) { Image.remove({id: $routeParams.id}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Image removed.', '#response');
$scope.response = 'Image removed.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
} }
}; };
@ -244,14 +210,10 @@ function ImageController($scope, $routeParams, $location, Image) {
var tag = $scope.tag; var tag = $scope.tag;
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) { Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) {
console.log(d); console.log(d);
$scope.alertClass = 'success'; setSuccessfulResponse($scope, 'Tag added.', '#response');
$scope.response = 'Tag added.';
showAndHide(true);
}, function(e) { }, function(e) {
console.log(e); console.log(e);
$scope.alertClass = 'error'; setFailedResponse($scope, e.data, '#response');
$scope.response = e.data;
showAndHide(false);
}); });
}; };
@ -307,3 +269,16 @@ function StartContainerController($scope, $routeParams, $location, Container) {
}); });
}; };
} }
function setSuccessfulResponse($scope, msg, msgId) {
$scope.alertClass = 'success';
$scope.response = msg;
$(msgId).show();
setTimeout(function() { $(msgId).hide();}, 5000);
}
function setFailedResponse($scope, msg, msgId) {
$scope.alertClass = 'error';
$scope.response = msg;
$(msgId).show();
}

View File

@ -1,10 +1,10 @@
'use strict'; 'use strict';
angular.module('dockerui.services', ['ngResource']) angular.module('dockerui.services', ['ngResource'])
.factory('Container', function($resource, DOCKER_ENDPOINT) { .factory('Container', function($resource, Settings) {
// Resource for interacting with the docker containers // Resource for interacting with the docker containers
// http://docs.docker.io/en/latest/api/docker_remote_api.html#containers // http://docs.docker.io/en/latest/api/docker_remote_api.html#containers
return $resource(DOCKER_ENDPOINT + '/containers/:id/:action', {}, { return $resource(Settings.url + '/containers/:id/:action', {}, {
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
get :{method: 'GET', params: { action:'json'}}, get :{method: 'GET', params: { action:'json'}},
start: {method: 'POST', params: {id: '@id', action: 'start'}}, start: {method: 'POST', params: {id: '@id', action: 'start'}},
@ -16,10 +16,10 @@ angular.module('dockerui.services', ['ngResource'])
remove :{method: 'DELETE', params: {id: '@id', v:0}} remove :{method: 'DELETE', params: {id: '@id', v:0}}
}); });
}) })
.factory('Image', function($resource, DOCKER_ENDPOINT) { .factory('Image', function($resource, Settings) {
// Resource for docker images // Resource for docker images
// http://docs.docker.io/en/latest/api/docker_remote_api.html#images // http://docs.docker.io/en/latest/api/docker_remote_api.html#images
return $resource(DOCKER_ENDPOINT + '/images/:id/:action', {}, { return $resource(Settings.url + '/images/:id/:action', {}, {
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
get :{method: 'GET', params: { action:'json'}}, get :{method: 'GET', params: { action:'json'}},
search :{method: 'GET', params: { action:'search'}}, search :{method: 'GET', params: { action:'search'}},
@ -31,30 +31,33 @@ angular.module('dockerui.services', ['ngResource'])
delete :{id: '@id', method: 'DELETE'} delete :{id: '@id', method: 'DELETE'}
}); });
}) })
.factory('Docker', function($resource, DOCKER_ENDPOINT) { .factory('Docker', function($resource, Settings) {
// Information for docker // Information for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
return $resource(DOCKER_ENDPOINT + '/version', {}, { return $resource(Settings.url + '/version', {}, {
get: {method: 'GET'} get: {method: 'GET'}
}); });
}) })
.factory('Auth', function($resource, DOCKER_ENDPOINT) { .factory('Auth', function($resource, Settings) {
// Auto Information for docker // Auto Information for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration // http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration
return $resource(DOCKER_ENDPOINT + '/auth', {}, { return $resource(Settings.url + '/auth', {}, {
get: {method: 'GET'}, get: {method: 'GET'},
update: {method: 'POST'} update: {method: 'POST'}
}); });
}) })
.factory('System', function($resource, DOCKER_ENDPOINT) { .factory('System', function($resource, Settings) {
// System for docker // System for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
return $resource(DOCKER_ENDPOINT + '/info', {}, { return $resource(Settings.url + '/info', {}, {
get: {method: 'GET'} get: {method: 'GET'}
}); });
}) })
.factory('Settings', function() { .factory('Settings', function(DOCKER_ENDPOINT, DOCKER_API_VERSION) {
return { return {
displayAll: false displayAll: false,
endpoint: DOCKER_ENDPOINT,
version: DOCKER_API_VERSION,
url: DOCKER_ENDPOINT + DOCKER_API_VERSION
}; };
}); });

13
partials/dashboard.html Normal file
View File

@ -0,0 +1,13 @@
<div class="row-fluid">
<!--<div class="sidebar span4">
<div ng-include="template" ng-controller="SideBarController"></div>
</div>-->
<div class="span12">
<div class="jumbotron">
<h1>DockerUI</h1>
<p class="lead">The Linux container engine</p>
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
</div>
</div>
</div>

View File

@ -1,7 +0,0 @@
<div class="jumbotron">
<h1>DockerUI</h1>
<p class="lead">The Linux container engine</p>
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
</div>
<hr>

View File

@ -4,7 +4,7 @@
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<ul class="nav"> <ul class="nav">
<li class="{{ hclass }}" ng-click="linkChange('home')"><a href="#">Home</a></li> <li class="{{ hclass }}" ng-click="linkChange('home')"><a href="#">Dashboard</a></li>
<li class="{{ cclass }}" ng-click="linkChange('containers')"><a href="/#/containers/">Containers</a></li> <li class="{{ cclass }}" ng-click="linkChange('containers')"><a href="/#/containers/">Containers</a></li>
<li class="{{ iclass }}" ng-click="linkChange('images')"><a href="/#/images/">Images</a></li> <li class="{{ iclass }}" ng-click="linkChange('images')"><a href="/#/images/">Images</a></li>
<li class="{{ sclass }}" ng-click="linkChange('settings')"><a href="/#/settings/">Settings</a></li> <li class="{{ sclass }}" ng-click="linkChange('settings')"><a href="/#/settings/">Settings</a></li>

View File

@ -6,9 +6,11 @@
<h3>Docker Information</h3> <h3>Docker Information</h3>
<div> <div>
<p class="lead"> <p class="lead">
<strong>Version</strong>{{ docker.Version }}<br /> <strong>Endpoint: </strong>{{ endpoint }}<br />
<strong>GitCommit</strong>{{ docker.GitCommit }}<br /> <strong>Api Version: </strong>{{ apiVersion }}<br />
<strong>GoVersion</strong>{{ docker.GoVersion }}<br /> <strong>Version: </strong>{{ docker.Version }}<br />
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br />
<strong>Go Version: </strong>{{ docker.GoVersion }}<br />
</p> </p>
</div> </div>

11
partials/sidebar.html Normal file
View File

@ -0,0 +1,11 @@
<div class="well">
<strong>Running containers:</strong>
<br />
<strong>Endpoint: </strong>{{ endpoint }}
<ul>
<li ng-repeat="container in containers">
<a href="/#/containers/{{ container.Id }}/">{{ container.Id|truncate:20 }}</a>
<span class="pull-right label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
</li>
</ul>
</div>