Merge branch 'dev'

pull/2/head
Michael Crosby 12 years ago
commit 1b531a3079

@ -1,9 +0,0 @@
REF = HEAD
VERSION = $(shell git describe --always $(REF))
all: less
less:
less css/*.less > css/app.css
.PHONY: all less

@ -6,7 +6,7 @@ DockerUI is a web interface to interact with the Remote API. The goal is to pro
![Container](/container.png)
###Installation
Open js/app.(ts/js - js file if you don't want to compile typescript) 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');
@ -14,7 +14,6 @@ Open js/app.(ts/js - js file if you don't want to compile typescript) and change
###Stack
* Angular.js
* Less
* Flatstrap ( Flat Twitter Bootstrap )

@ -1,85 +0,0 @@
body {
padding-top: 20px;
padding-bottom: 60px;
}
/* Custom container */
.container {
margin: 0 auto;
max-width: 1000px;
}
.container > hr {
margin: 60px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
margin: 80px 0;
text-align: center;
}
.jumbotron h1 {
font-size: 100px;
line-height: 1;
}
.jumbotron .lead {
font-size: 24px;
line-height: 1.25;
}
.jumbotron .btn {
font-size: 21px;
padding: 14px 24px;
}
/* Supporting marketing content */
.marketing {
margin: 60px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Customize the navbar links to be fill the entire space of the .navbar */
.navbar .navbar-inner {
padding: 0;
}
.navbar .nav {
margin: 0;
}
.navbar .nav li {
display: table-cell;
width: 1%;
float: none;
}
.navbar .nav li a {
font-weight: bold;
text-align: center;
border-left: 1px solid rgba(255,255,255,.75);
border-right: 1px solid rgba(0,0,0,.1);
}
.navbar .nav li:first-child a {
border-left: 0;
border-radius: 3px 0 0 3px;
}
.navbar .nav li:last-child a {
border-right: 0;
border-radius: 0 3px 3px 0;
}
.btn-group button {
margin: 3px;
}
.detail {
width: 80%;
margin: 0 auto;
}
.btn-remove {
margin: 0 auto;
max-width: 70%;
}
.actions {
margin: 0 auto;
}

@ -1,4 +1,5 @@
// Controller for the top masthead
function MastheadController($scope) {
$scope.template = 'partials/masthead.html';
@ -13,6 +14,7 @@ function MastheadController($scope) {
$scope.iclass = '';
$scope.sclass = '';
//This is shitty, I need help with this crap.
switch(link) {
case 'home':
$scope.hclass = 'active';
@ -32,40 +34,124 @@ function MastheadController($scope) {
};
}
function SideBarController($scope, Container) {
$scope.template = 'partials/sidebar.html';
Container.query({}, function(d) {
$scope.containers = d;
});
}
function HomeController() {
}
function SettingsController() {
function SettingsController($scope, Auth, System, Docker, Settings) {
$scope.auth = {};
$scope.info = {};
$scope.docker = {};
$('#response').hide();
$scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.updateAuthInfo = function() {
if ($scope.auth.password != $scope.auth.cpassword) {
$scope.response = 'Your passwords do not match.';
showAndHide(true);
return;
}
Auth.update(
{username: $scope.auth.username, email: $scope.auth.email, password: $scope.auth.password}, function(d) {
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Auth information updated.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
};
Auth.get({}, function(d) {
$scope.auth = d;
});
Docker.get({}, function(d) {
$scope.docker = d;
});
System.get({}, function(d) {
$scope.info = d;
});
}
function ContainerController($scope, $routeParams, Container) {
// Controls the page that displays a single container and actions on that container.
function ContainerController($scope, $routeParams, $location, Container) {
$('#response').hide();
$scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.start = function(){
Container.start({id: $routeParams.id}, function(d) {
$scope.response = d;
});
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Container started.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
};
$scope.stop = function() {
Container.stop({id: $routeParams.id}, function(d) {
$scope.response = d;
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Container stopped.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
};
$scope.kill = function() {
Container.kill({id: $routeParams.id}, function(d) {
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Container killed.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
};
$scope.remove = function() {
if (confirm("Are you sure you want to remove the container?")) {
Container.remove({id: $routeParams.id}, function(d) {
$scope.response = d;
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Container removed.';
showAndHide(true);
}, function(e){
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
}
};
@ -80,13 +166,149 @@ function ContainerController($scope, $routeParams, Container) {
Container.get({id: $routeParams.id}, function(d) {
$scope.container = d;
}, function(e) {
console.log(e);
$location.path('/containers/');
});
$scope.getChanges();
}
function ContainersController($scope, Container) {
Container.query({}, function(d) {
$scope.containers = d;
});
// Controller for the list of containers
function ContainersController($scope, Container, Settings) {
$scope.displayAll = Settings.displayAll;
$scope.predicate = '-Created';
var update = function(data) {
Container.query(data, function(d) {
$scope.containers = d;
});
};
$scope.toggleGetAll = function() {
Settings.displayAll = $scope.displayAll;
var u = update;
var data = {all: 0};
if ($scope.displayAll) {
data.all = 1;
}
u(data);
};
update({all: $scope.displayAll ? 1 : 0});
}
// Controller for the list of images
function ImagesController($scope, Image) {
$scope.predicate = '-Created';
Image.query({}, function(d) {
$scope.images = d;
});
}
// Controller for a single image and actions on that image
function ImageController($scope, $routeParams, $location, Image) {
$scope.history = [];
$scope.tag = {repo: '', force: false};
$('#response').hide();
$scope.alertClass = 'block';
var showAndHide = function(hide) {
$('#response').show();
if (hide) {
setTimeout(function() { $('#response').hide();}, 5000);
}
};
$scope.remove = function() {
if (confirm("Are you sure you want to delete this image?")) {
Image.remove({id: $routeParams.id}, function(d) {
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Image removed.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
}
};
$scope.getHistory = function() {
Image.history({id: $routeParams.id}, function(d) {
$scope.history = d;
});
};
$scope.updateTag = function() {
var tag = $scope.tag;
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) {
console.log(d);
$scope.alertClass = 'success';
$scope.response = 'Tag added.';
showAndHide(true);
}, function(e) {
console.log(e);
$scope.alertClass = 'error';
$scope.response = e.data;
showAndHide(false);
});
};
Image.get({id: $routeParams.id}, function(d) {
$scope.image = d;
}, function(e) {
console.log(e);
$location.path('/images/');
});
$scope.getHistory();
}
function StartContainerController($scope, $routeParams, $location, Container) {
$scope.template = 'partials/startcontainer.html';
$scope.memory = 0;
$scope.memorySwap = 0;
$scope.env = '';
$scope.dns = '';
$scope.volumesFrom = '';
$scope.commands = '';
$scope.launchContainer = function() {
var cmds = null;
if ($scope.commands !== '') {
cmds = $scope.commands.split('\n');
}
var id = $routeParams.id;
var ctor = Container;
var loc = $location;
var s = $scope;
Container.create({
Image: id,
Memory: $scope.memory,
MemorySwap: $scope.memorySwap,
Cmd: cmds,
VolumesFrom: $scope.volumesFrom
}, function(d) {
console.log(d);
if (d.Id) {
ctor.start({id: d.Id}, function(cd) {
console.log(cd);
loc.path('/containers/' + d.Id + '/');
}, function(e) {
console.log(e);
s.resonse = e.data;
});
}
}, function(e) {
console.log(e);
$scope.response = e.data;
});
};
}

@ -21,7 +21,42 @@ angular.module('dockerui.filters', [])
return function(text) {
if (text === 'Ghost') {
return 'important';
} else if (text.indexOf('Exit') != -1 && text !== 'Exit 0') {
return 'warning';
}
return 'success';
};
})
.filter('getstatetext', function() {
return function(state) {
if (state == undefined) return '';
if (state.Ghost && state.Running) {
return 'Ghost';
}
if (state.Running) {
return 'Running';
}
return 'Stopped';
};
})
.filter('getstatelabel', function() {
return function(state) {
if (state == undefined) return '';
if (state.Ghost && state.Running) {
return 'label-important';
}
if (state.Running) {
return 'label-success';
}
return '';
};
})
.filter('getdate', function() {
return function(data) {
//Multiply by 1000 for the unix format
var date = new Date(data * 1000);
return date.toDateString();
};
});

@ -7,27 +7,54 @@ angular.module('dockerui.services', ['ngResource'])
return $resource(DOCKER_ENDPOINT + '/containers/:id/:action', {}, {
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
get :{method: 'GET', params: { action:'json'}},
start: {method: 'POST', params: { action: 'start'}},
stop: {method: 'POST', params: {t: 5, action: 'stop'}},
restart: {method: 'POST', params: {t: 5, action: 'restart' }},
kill :{method: 'POST', params: {action:'kill'}},
start: {method: 'POST', params: {id: '@id', action: 'start'}},
stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}},
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart' }},
kill :{method: 'POST', params: {id: '@id', action:'kill'}},
changes :{method: 'GET', params: {action:'changes'}, isArray: true},
create :{method: 'POST', params: {action:'create'}},
remove :{method: 'DELETE', params: {v:0}}
remove :{method: 'DELETE', params: {id: '@id', v:0}}
});
})
.factory('Image', function($resource, DOCKER_ENDPOINT) {
// Resource for docker images
// http://docs.docker.io/en/latest/api/docker_remote_api.html#images
return $resource(DOCKER_ENDPOINT + '/images/:name/:action', {}, {
return $resource(DOCKER_ENDPOINT + '/images/:id/:action', {}, {
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
get :{method: 'GET', params: { action:'json'}},
search :{method: 'GET', params: { action:'search'}},
history :{method: 'GET', params: { action:'history'}},
history :{method: 'GET', params: { action:'history'}, isArray: true},
create :{method: 'POST', params: {action:'create'}},
insert :{method: 'POST', params: {action:'insert'}},
push :{method: 'POST', params: {action:'push'}},
tag :{method: 'POST', params: {action:'tag'}},
delete :{method: 'DELETE'}
insert :{method: 'POST', params: {id: '@id', action:'insert'}},
push :{method: 'POST', params: {id: '@id', action:'push'}},
tag :{method: 'POST', params: {id: '@id', action:'tag'}},
delete :{id: '@id', method: 'DELETE'}
});
})
.factory('Docker', function($resource, DOCKER_ENDPOINT) {
// Information for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
return $resource(DOCKER_ENDPOINT + '/version', {}, {
get: {method: 'GET'}
});
})
.factory('Auth', function($resource, DOCKER_ENDPOINT) {
// Auto Information for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration
return $resource(DOCKER_ENDPOINT + '/auth', {}, {
get: {method: 'GET'},
update: {method: 'POST'}
});
})
.factory('System', function($resource, DOCKER_ENDPOINT) {
// System for docker
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
return $resource(DOCKER_ENDPOINT + '/info', {}, {
get: {method: 'GET'}
});
})
.factory('Settings', function() {
return {
displayAll: false
};
});

@ -1,10 +1,14 @@
<div class="detail">
<div id="response" class="alert alert-{{ alertClass }}">
{{ response }}
</div>
<h4>Container: {{ container.Id }}</h4>
<div class="btn-group detail">
<button class="btn btn-success" ng-click="start()">Start</button>
<button class="btn btn-warning" ng-click="stop()">Stop</button>
<button class="btn btn-danger" ng-click="kill()">Kill</button>
</div>
<table class="table table-striped">
@ -27,11 +31,11 @@
</tr>
<tr>
<td>Image:</td>
<td><a href="#">{{ container.Image }}</a></td>
<td><a href="/#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
</tr>
<tr>
<td>Running:</td>
<td>{{ container.State.Running }}</td>
<td>State:</td>
<td><span class="label {{ container.State|getstatelabel }}">{{ container.State|getstatetext }}</span></td>
</tr>
</tbody>
</table>

@ -1,6 +1,9 @@
<h2>Containers:</h2>
<div style="float:right;">
<input type="checkbox" ng-model="displayAll" ng-click="toggleGetAll()"/> Display All
</div>
<table class="table table-striped">
<thead>
<tr>
@ -12,11 +15,11 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="container in containers">
<tr ng-repeat="container in containers|orderBy:predicate">
<td><a href="/#/containers/{{ container.Id }}/">{{ container.Id|truncate:10}}</a></td>
<td>{{ container.Image }}</td>
<td>{{ container.Command }}</td>
<td>{{ container.Created }}</td>
<td><a href="/#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
<td>{{ container.Command|truncate:40 }}</td>
<td>{{ container.Created|getdate }}</td>
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
</tr>
</tbody>

@ -0,0 +1,89 @@
<div class="detail">
<div id="response" class="alert alert-{{ alertClass }}">
{{ response }}
</div>
<h4>Image: {{ image.id }}</h4>
<table class="table table-striped">
<tbody>
<tr>
<td>Created:</td>
<td>{{ image.created }}</td>
</tr>
<tr>
<td>Parent:</td>
<td><a href="/#/images/{{ image.parent }}/">{{ image.parent }}</a></td>
</tr>
<tr>
<td>Container:</td>
<td><a href="/#/containers/{{ image.container }}/">{{ image.container }}</a></td>
</tr>
<tr>
<td>Hostname:</td>
<td>{{ image.container_config.Hostname }}</td>
</tr>
<tr>
<td>User:</td>
<td>{{ image.container_config.User }}</td>
</tr>
<tr>
<td>Cmd:</td>
<td>{{ image.container_config.Cmd }}</td>
</tr>
<tr>
<td>Volumes:</td>
<td>{{ image.container_config.Volumes }}</td>
</tr>
<tr>
<td>Volumes from:</td>
<td>{{ image.container_config.VolumesFrom }}</td>
</tr>
</tbody>
</table>
<div class="row-fluid">
<div class="span1">
History:
</div>
<div class="span5">
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getHistory()"></i>
</div>
</div>
<div class="well well-large">
<ul>
<li ng-repeat="change in history">
<strong>{{ change.Id }}</strong>: Created: {{ change.Created|getdate }} Created by: {{ change.CreatedBy }}
</li>
</ul>
</div>
<hr />
<div class="row-fluid">
<form>
<fieldset>
<legend>Tag to Repo</legend>
<label>Repo:</label>
<input type="text" placeholder="Repo..." ng-model="tag.repo" required>
<label class="checkbox">
<input type="checkbox" ng-model="tag.force"/> Force?
</label>
<input type="button" ng-click="updateTag()" value="Update" class="btn btn-info"/>
</fieldset>
</form>
</div>
<hr />
<div ng-include="template" ng-controller="StartContainerController"></div>
<hr />
<div class="btn-remove">
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Image</button>
</div>
</div>

@ -0,0 +1,21 @@
<h2>Images:</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Id</th>
<th>Tag</th>
<th>Repository</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="image in images | orderBy:predicate">
<td><a href="/#/images/{{ image.Id }}/">{{ image.Id|truncate:10}}</a></td>
<td>{{ image.Tag }}</td>
<td>{{ image.Repository }}</td>
<td>{{ image.Created|getdate }}</td>
</tr>
</tbody>
</table>

@ -0,0 +1,67 @@
<div class="detail">
<div id="response" class="alert alert-{{ alertClass }}">
{{ response }}
</div>
<h3>Docker Information</h3>
<div>
<p class="lead">
<strong>Version</strong>{{ docker.Version }}<br />
<strong>GitCommit</strong>{{ docker.GitCommit }}<br />
<strong>GoVersion</strong>{{ docker.GoVersion }}<br />
</p>
</div>
<table class="table table-striped">
<tbody>
<tr>
<td>Containers:</td>
<td>{{ info.Containers }}</td>
</tr>
<tr>
<td>Images:</td>
<td>{{ info.Images }}</td>
</tr>
<tr>
<td>Debug:</td>
<td>{{ info.Debug }}</td>
</tr>
<tr>
<td>NFd:</td>
<td>{{ info.NFd }}</td>
</tr>
<tr>
<td>NGoroutines:</td>
<td>{{ info.NGoroutines }}</td>
</tr>
<tr>
<td>MemoryLimit:</td>
<td>{{ info.MemoryLimit }}</td>
</tr>
<tr>
<td>SwapLimit:</td>
<td>{{ info.SwapLimit }}</td>
</tr>
<tr>
<td>NFd:</td>
<td>{{ info.NFd }}</td>
</tr>
</tbody>
</table>
<form>
<fieldset>
<legend>Auth Information</legend>
<label>Username:</label>
<input type="text" ng-model="auth.username" required>
<label>Email:</label>
<input type="text" ng-model="auth.email" required>
<label>Password:</label>
<input type="password" ng-model="auth.password" required>
<label>Confirm Password:</label>
<input type="password" ng-model="auth.cpassword" required>
<br />
<input type="button" ng-click="updateAuthInfo()" value="Update"/>
</fieldset>
</form>
</div>

@ -0,0 +1,27 @@
<div>
{{ response }}
</div>
<form>
<fieldset>
<legend>Start container from Image</legend>
<label>Cmd:</label>
<textarea ng-model="commands" rows="6"></textarea>
<small>Place each command on a new line</small>
<label>Memory:</label>
<input type="number" ng-model="memory"/>
<label>Memory Swap:</label>
<input type="number" ng-model="memorySwap"/>
<label>Volumes From:</label>
<input type="text" ng-model="volumesFrom"/>
<br />
<input type="button" ng-click="launchContainer()" value="Launch" />
</fieldset>
</form>
Loading…
Cancel
Save