mirror of https://github.com/portainer/portainer
Rdash theme integration (#1)
* Adding latest build to dist. * Adding latest build to dist. * Bump other app version. * Build latest changes. * Bump version to 0.7.0. * Version bump to 0.9.0-beta and remote API 1.20. * Whoah there, back down to 0.8.0-beta. * Merge branch 'crosbymichael-master' into crosbymichael-dist * Add volume options in volume creation form * display swarm cluster information in Swarm tab * update LICENSE * update repository URL in status bar * remove console logs * do not display Swarm containers anywhere in the UI * update position for add/remove option on Volumes page * compliant with swarm == 1.2.0 API support * update nginx-basic-auth examples with latest nginx and swarm example * Updated .gitignore * update .gitignore * reverted entry for dist/uifordocker in .gitignore * WIP * fix linter issues * added logo * update repository URL * update .gitignore (ignore dist/*) * add lodash * add containers actions binding (start, stop...) * replace image icon * bind remove image action * bind network remove action * bind volume remove action * update logo * wip on container details * update logo scaling, favicon and page title * wip container view * add containers actions in container view * add image view * add network view * remove useless data in tables * add pull image, create network modals * add create volume modal * update style for createVolume options * add start container modal * create volume modal now use a select to display drivers * add container stats * add containerTop view in stats view * fix trimcontainername filter * add container logs view * updated .gitignore * remove useless files/modules * remove useless chart in image view * replace $location usage with $state.go * remove useless swarm examplepull/8/head
parent
1b206f223f
commit
0f51cb66e0
|
@ -4,6 +4,6 @@ logs/*
|
||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
.idea
|
.idea
|
||||||
dist
|
|
||||||
dockerui
|
|
||||||
*.iml
|
*.iml
|
||||||
|
dist
|
||||||
|
dist/*
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com)
|
UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com), Anthony Lapenna (anthonylapenna at cloudinovasi dot id)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|
97
app/app.js
97
app/app.js
|
@ -1,76 +1,92 @@
|
||||||
angular.module('uifordocker', [
|
angular.module('uifordocker', [
|
||||||
'uifordocker.templates',
|
'uifordocker.templates',
|
||||||
|
'ui.bootstrap',
|
||||||
|
'ui.router',
|
||||||
|
'ui.select',
|
||||||
|
'ngCookies',
|
||||||
'ngRoute',
|
'ngRoute',
|
||||||
|
'ngSanitize',
|
||||||
'dockerui.services',
|
'dockerui.services',
|
||||||
'dockerui.filters',
|
'dockerui.filters',
|
||||||
'masthead',
|
|
||||||
'footer',
|
|
||||||
'dashboard',
|
'dashboard',
|
||||||
'container',
|
'container',
|
||||||
'containers',
|
'containers',
|
||||||
'containersNetwork',
|
|
||||||
'images',
|
'images',
|
||||||
'image',
|
'image',
|
||||||
'pullImage',
|
'pullImage',
|
||||||
'startContainer',
|
'startContainer',
|
||||||
'sidebar',
|
|
||||||
'info',
|
|
||||||
'builder',
|
|
||||||
'containerLogs',
|
'containerLogs',
|
||||||
'containerTop',
|
|
||||||
'events',
|
|
||||||
'stats',
|
'stats',
|
||||||
|
'swarm',
|
||||||
'network',
|
'network',
|
||||||
'networks',
|
'networks',
|
||||||
'volumes'])
|
'createNetwork',
|
||||||
.config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
|
'volumes',
|
||||||
|
'createVolume'])
|
||||||
|
.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', function ($stateProvider, $urlRouterProvider, $httpProvider) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
$httpProvider.defaults.xsrfCookieName = 'csrfToken';
|
$httpProvider.defaults.xsrfCookieName = 'csrfToken';
|
||||||
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
|
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
|
||||||
|
|
||||||
$routeProvider.when('/', {
|
$urlRouterProvider.otherwise('/');
|
||||||
|
|
||||||
|
$stateProvider
|
||||||
|
.state('index', {
|
||||||
|
url: '/',
|
||||||
templateUrl: 'app/components/dashboard/dashboard.html',
|
templateUrl: 'app/components/dashboard/dashboard.html',
|
||||||
controller: 'DashboardController'
|
controller: 'DashboardController'
|
||||||
});
|
})
|
||||||
$routeProvider.when('/containers/', {
|
.state('containers', {
|
||||||
|
url: '/containers/',
|
||||||
templateUrl: 'app/components/containers/containers.html',
|
templateUrl: 'app/components/containers/containers.html',
|
||||||
controller: 'ContainersController'
|
controller: 'ContainersController'
|
||||||
});
|
})
|
||||||
$routeProvider.when('/containers/:id/', {
|
.state('container', {
|
||||||
|
url: "^/containers/:id",
|
||||||
templateUrl: 'app/components/container/container.html',
|
templateUrl: 'app/components/container/container.html',
|
||||||
controller: 'ContainerController'
|
controller: 'ContainerController'
|
||||||
});
|
})
|
||||||
$routeProvider.when('/containers/:id/logs/', {
|
.state('stats', {
|
||||||
templateUrl: 'app/components/containerLogs/containerlogs.html',
|
url: "^/containers/:id/stats",
|
||||||
controller: 'ContainerLogsController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/containers/:id/top', {
|
|
||||||
templateUrl: 'app/components/containerTop/containerTop.html',
|
|
||||||
controller: 'ContainerTopController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/containers/:id/stats', {
|
|
||||||
templateUrl: 'app/components/stats/stats.html',
|
templateUrl: 'app/components/stats/stats.html',
|
||||||
controller: 'StatsController'
|
controller: 'StatsController'
|
||||||
});
|
})
|
||||||
$routeProvider.when('/containers_network', {
|
.state('logs', {
|
||||||
templateUrl: 'app/components/containersNetwork/containersNetwork.html',
|
url: "^/containers/:id/logs",
|
||||||
controller: 'ContainersNetworkController'
|
templateUrl: 'app/components/containerLogs/containerlogs.html',
|
||||||
});
|
controller: 'ContainerLogsController'
|
||||||
$routeProvider.when('/images/', {
|
})
|
||||||
|
.state('images', {
|
||||||
|
url: '/images/',
|
||||||
templateUrl: 'app/components/images/images.html',
|
templateUrl: 'app/components/images/images.html',
|
||||||
controller: 'ImagesController'
|
controller: 'ImagesController'
|
||||||
});
|
})
|
||||||
$routeProvider.when('/images/:id*/', {
|
.state('image', {
|
||||||
|
url: '^/images/:id/',
|
||||||
templateUrl: 'app/components/image/image.html',
|
templateUrl: 'app/components/image/image.html',
|
||||||
controller: 'ImageController'
|
controller: 'ImageController'
|
||||||
|
})
|
||||||
|
.state('networks', {
|
||||||
|
url: '/networks/',
|
||||||
|
templateUrl: 'app/components/networks/networks.html',
|
||||||
|
controller: 'NetworksController'
|
||||||
|
})
|
||||||
|
.state('network', {
|
||||||
|
url: '^/networks/:id/',
|
||||||
|
templateUrl: 'app/components/network/network.html',
|
||||||
|
controller: 'NetworkController'
|
||||||
|
})
|
||||||
|
.state('volumes', {
|
||||||
|
url: '/volumes/',
|
||||||
|
templateUrl: 'app/components/volumes/volumes.html',
|
||||||
|
controller: 'VolumesController'
|
||||||
|
})
|
||||||
|
.state('swarm', {
|
||||||
|
url: '/swarm/',
|
||||||
|
templateUrl: 'app/components/swarm/swarm.html',
|
||||||
|
controller: 'SwarmController'
|
||||||
});
|
});
|
||||||
$routeProvider.when('/info', {templateUrl: 'app/components/info/info.html', controller: 'InfoController'});
|
|
||||||
$routeProvider.when('/events', {
|
|
||||||
templateUrl: 'app/components/events/events.html',
|
|
||||||
controller: 'EventsController'
|
|
||||||
});
|
|
||||||
$routeProvider.otherwise({redirectTo: '/'});
|
|
||||||
|
|
||||||
// The Docker API likes to return plaintext errors, this catches them and disp
|
// The Docker API likes to return plaintext errors, this catches them and disp
|
||||||
$httpProvider.interceptors.push(function() {
|
$httpProvider.interceptors.push(function() {
|
||||||
|
@ -92,8 +108,9 @@ angular.module('uifordocker', [
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}])
|
}])
|
||||||
|
|
||||||
// This is your docker url that the api will use to make requests
|
// This is your docker url that the api will use to make requests
|
||||||
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
// 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_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('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('UI_VERSION', 'v0.10.1-beta');
|
.constant('UI_VERSION', 'v0.11.0');
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
<div id="build-modal" class="modal fade">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h3>Build Image</h3>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div id="editor"></div>
|
|
||||||
<p>{{ messages }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="" class="btn btn-primary" ng-click="build()">Build</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,5 +0,0 @@
|
||||||
angular.module('builder', [])
|
|
||||||
.controller('BuilderController', ['$scope',
|
|
||||||
function ($scope) {
|
|
||||||
$scope.template = 'app/components/builder/builder.html';
|
|
||||||
}]);
|
|
|
@ -1,72 +1,91 @@
|
||||||
<div class="detail">
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-12 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
|
</div>
|
||||||
<div ng-if="!container.edit">
|
<div ng-if="!container.edit">
|
||||||
<h4>Container: {{ container.Name }}
|
<div class="title">{{ container.Name|trimcontainername }}</div>
|
||||||
<button class="btn btn-primary"
|
<div class="comment">
|
||||||
ng-click="container.edit = true;">Rename
|
Name <a href="" ng-click="container.edit = true;"><i class="fa fa-edit"></i></a>
|
||||||
</button>
|
</div>
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="container.edit">
|
<div ng-if="container.edit">
|
||||||
<h4>
|
<div class="title"><input type="text" class="containerNameInput" ng-model="container.newContainerName"></div>
|
||||||
Container:
|
<div class="comment">
|
||||||
<input type="text" ng-model="container.newContainerName">
|
Name
|
||||||
<button class="btn btn-success"
|
<a href="" ng-click="container.edit = false;"><i class="fa fa-times"></i></a>
|
||||||
ng-click="renameContainer()">Save
|
<a href="" ng-click="renameContainer()"><i class="fa fa-check-square-o"></i></a>
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger"
|
|
||||||
ng-click="container.edit = false;">×</button>
|
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group detail">
|
|
||||||
<button class="btn btn-success"
|
|
||||||
ng-click="start()"
|
|
||||||
ng-show="!container.State.Running">Start
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-warning"
|
|
||||||
ng-click="stop()"
|
|
||||||
ng-show="container.State.Running && !container.State.Paused">Stop
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger"
|
|
||||||
ng-click="kill()"
|
|
||||||
ng-show="container.State.Running && !container.State.Paused">Kill
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-info"
|
|
||||||
ng-click="pause()"
|
|
||||||
ng-show="container.State.Running && !container.State.Paused">Pause
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-success"
|
|
||||||
ng-click="unpause()"
|
|
||||||
ng-show="container.State.Running && container.State.Paused">Unpause
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-success"
|
|
||||||
ng-click="restart()"
|
|
||||||
ng-show="container.State.Running && !container.State.Stopped">Restart
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary"
|
|
||||||
ng-click="commit()">Commit
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-12 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div ng-class="{true: 'widget-icon green pull-left', false: 'widget-icon red pull-left'}[container.State.Running]">
|
||||||
|
<i class="fa fa-heartbeat"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ container.State|getstatetext }}</div>
|
||||||
|
<div class="comment">State</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
|
<button class="btn btn-primary" ng-click="commit()">Commit</button>
|
||||||
|
<button class="btn btn-primary" ng-click="start()" ng-disabled="container.State.Running">Start</button>
|
||||||
|
<button class="btn btn-primary" ng-click="stop()" ng-disabled="!container.State.Running">Stop</button>
|
||||||
|
<button class="btn btn-primary" ng-click="kill()" ng-disabled="!container.State.Running">Kill</button>
|
||||||
|
<button class="btn btn-primary" ng-click="restart()">Restart</button>
|
||||||
|
<button class="btn btn-primary" ng-click="pause()" ng-disabled="!container.State.Running && !container.State.Paused">Pause</button>
|
||||||
|
<button class="btn btn-primary" ng-click="unpause()" ng-disabled="!container.State.Paused">Unpause</button>
|
||||||
|
<button class="btn btn-danger" ng-click="remove()" ng-disabled="container.State.Running">Remove</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
|
<a class="btn btn-default" type="button" href="#/containers/{{ container.Id }}/stats">Stats</a>
|
||||||
|
<a class="btn btn-default" type="button" href="#/containers/{{ container.Id }}/logs">Logs</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="comment">
|
||||||
|
Actions
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped">
|
<div class="row">
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-tasks" title="Container status"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Created:</td>
|
<td>Created</td>
|
||||||
<td>{{ container.Created | date: 'medium' }}</td>
|
<td>{{ container.Created | date: 'medium' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Path:</td>
|
<td>Path</td>
|
||||||
<td>{{ container.Path }}</td>
|
<td>{{ container.Path }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Args:</td>
|
<td>Args</td>
|
||||||
<td>
|
<td>{{ container.Args.join(' ') || 'None' }}</td>
|
||||||
<pre>{{ container.Args.join(' ') || 'None' }}</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Exposed Ports:</td>
|
<td>Exposed Ports</td>
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
||||||
|
@ -74,55 +93,17 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Environment:</td>
|
<td>Environment</td>
|
||||||
<td>
|
<td>
|
||||||
<div ng-show="!editEnv">
|
|
||||||
<button class="btn btn-default btn-xs pull-right" ng-click="editEnv = true"><i class="glyphicon glyphicon-pencil"></i></button>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
<div class="form-group" ng-show="editEnv">
|
|
||||||
<label>Env:</label>
|
|
||||||
|
|
||||||
<div ng-repeat="envar in newCfg.Env">
|
|
||||||
<div class="form-group form-inline">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="sr-only">Variable Name:</label>
|
|
||||||
<input type="text" ng-model="envar.name" class="form-control input-sm"
|
|
||||||
placeholder="NAME"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="sr-only">Variable Value:</label>
|
|
||||||
<input type="text" ng-model="envar.value" class="form-control input-sm" style="width: 400px"
|
|
||||||
placeholder="value"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button class="btn btn-danger btn-sm input-sm form-control"
|
|
||||||
ng-click="rmEntry(newCfg.Env, envar)"><i class="glyphicon glyphicon-remove"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
|
||||||
ng-click="addEntry(newCfg.Env, {name: '', value: ''})"><i class="glyphicon glyphicon-plus"></i> Add
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary btn-sm"
|
|
||||||
ng-click="restartEnv()"
|
|
||||||
ng-show="!container.State.Restarting">Commit and restart</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Labels:</td>
|
<td>Labels</td>
|
||||||
<td>
|
<td>
|
||||||
<table role="table" class="table">
|
<table role="table" class="table">
|
||||||
<tr>
|
|
||||||
<th>Key</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat="(k, v) in container.Config.Labels">
|
<tr ng-repeat="(k, v) in container.Config.Labels">
|
||||||
<td>{{ k }}</td>
|
<td>{{ k }}</td>
|
||||||
<td>{{ v }}</td>
|
<td>{{ v }}</td>
|
||||||
|
@ -130,169 +111,76 @@
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Publish All:</td>
|
<td>Publish all ports</td>
|
||||||
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Ports:</td>
|
<td>Ports</td>
|
||||||
<td>
|
<td>
|
||||||
<div ng-show="!editPorts">
|
|
||||||
<button class="btn btn-default btn-xs pull-right" ng-click="editPorts = true"><i class="glyphicon glyphicon-pencil"></i></button>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="(containerport, hostports) in container.NetworkSettings.Ports">
|
<li ng-repeat="(containerport, hostports) in container.NetworkSettings.Ports">
|
||||||
{{ containerport }} =>
|
{{ containerport }} =>
|
||||||
<span class="label label-default" style="margin-right: 5px;" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
<span class="label label-default" style="margin-right: 5px;" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
<div ng-show="editPorts">
|
|
||||||
<div ng-repeat="(containerport, hostports) in newCfg.Ports" style="margin-bottom: 5px;">
|
|
||||||
<label>{{ containerport }}</label>
|
|
||||||
<div style="margin-left: 20px;">
|
|
||||||
<div ng-repeat="(k,v) in hostports" class="form-group form-inline">
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="text" ng-model="v.HostIp" class="form-control input-sm" placeholder="IP address, ex. 0.0.0.0" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="text" ng-model="v.HostPort" class="form-control input-sm"
|
|
||||||
placeholder="Port" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button class="btn btn-danger btn-sm input-sm form-control"
|
|
||||||
ng-click="rmEntry(hostports, v)"><i class="glyphicon glyphicon-remove"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
|
||||||
ng-click="addEntry(hostports, {HostIp: '0.0.0.0', HostPort: ''})"><i class="glyphicon glyphicon-plus"></i> Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary btn-sm"
|
|
||||||
ng-click="restartEnv()"
|
|
||||||
ng-show="!container.State.Restarting">Commit and restart</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Hostname:</td>
|
<td>Hostname</td>
|
||||||
<td>{{ container.Config.Hostname }}</td>
|
<td>{{ container.Config.Hostname }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>IPAddress:</td>
|
<td>IPAddress</td>
|
||||||
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Cmd:</td>
|
<td>Cmd</td>
|
||||||
<td>{{ container.Config.Cmd }}</td>
|
<td>{{ container.Config.Cmd }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Entrypoint:</td>
|
<td>Entrypoint</td>
|
||||||
<td>
|
<td>{{ container.Config.Entrypoint.join(' ') }}</td>
|
||||||
<pre>{{ container.Config.Entrypoint.join(' ') }}</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Bindings:</td>
|
<td>Bindings</td>
|
||||||
<td>
|
<td>
|
||||||
<div ng-show="!editBinds">
|
|
||||||
<button class="btn btn-default btn-xs pull-right" ng-click="editBinds = true"><i class="glyphicon glyphicon-pencil"></i></button>
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="b in container.HostConfig.Binds">{{ b }}</li>
|
<li ng-repeat="b in container.HostConfig.Binds">{{ b }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
<div ng-show="editBinds">
|
|
||||||
<div ng-repeat="(vol, b) in newCfg.Binds" class="form-group form-inline">
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="text" ng-model="b.HostPath" class="form-control input-sm"
|
|
||||||
placeholder="Host path or volume name" style="width: 250px;" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="text" ng-model="b.ContPath" ng-readonly="b.DefaultBind" class="form-control input-sm" placeholder="Container path" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label><input type="checkbox" ng-model="b.ReadOnly" /> read only</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button class="btn btn-danger btn-sm input-sm form-control"
|
|
||||||
ng-click="rmEntry(newCfg.Binds, b)"><i class="glyphicon glyphicon-remove"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
|
||||||
ng-click="addEntry(newCfg.Binds, { ContPath: '', HostPath: '', ReadOnly: false, DefaultBind: false })"><i class="glyphicon glyphicon-plus"></i> Add
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary btn-sm"
|
|
||||||
ng-click="restartEnv()"
|
|
||||||
ng-show="!container.State.Restarting">Commit and restart</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Volumes:</td>
|
<td>Volumes</td>
|
||||||
<td>{{ container.Volumes }}</td>
|
<td>{{ container.Volumes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>SysInitpath:</td>
|
<td>SysInitpath</td>
|
||||||
<td>{{ container.SysInitPath }}</td>
|
<td>{{ container.SysInitPath }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Image:</td>
|
<td>Image</td>
|
||||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>State:</td>
|
|
||||||
<td>
|
|
||||||
<accordion close-others="true">
|
|
||||||
<accordion-group heading="{{ container.State|getstatetext }}">
|
|
||||||
<ul>
|
|
||||||
<li ng-repeat="(key, val) in container.State">{{key}} : {{ val }}</li>
|
|
||||||
</ul>
|
|
||||||
</accordion-group>
|
|
||||||
</accordion>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Logs:</td>
|
|
||||||
<td><a href="#/containers/{{ container.Id }}/logs">stdout/stderr</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Stats:</td>
|
|
||||||
<td><a href="#/containers/{{ container.Id }}/stats">stats</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Top:</td>
|
|
||||||
<td><a href="#/containers/{{ container.Id }}/top">Top</a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
<div class="row-fluid">
|
</rd-widget>
|
||||||
<div class="span1">
|
|
||||||
Changes:
|
|
||||||
</div>
|
</div>
|
||||||
<div class="span5">
|
<div class="col-lg-3">
|
||||||
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getChanges()"></i>
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-tasks" title="Container state details"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="(key, val) in container.State">
|
||||||
|
<td>{{key}}</td>
|
||||||
|
<td>{{ val }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
|
|
||||||
<div class="well well-large">
|
|
||||||
<ul>
|
|
||||||
<li ng-repeat="change in changes | filter:hasContent">
|
|
||||||
<strong>{{ change.Path }}</strong> {{ change.Kind }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<div class="btn-remove">
|
|
||||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Container</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('container', [])
|
angular.module('container', [])
|
||||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Image', 'Messages', 'ViewSpinner', '$timeout',
|
.controller('ContainerController', ['$scope', '$stateParams', '$state', '$filter', 'Container', 'ContainerCommit', 'Image', 'Messages', 'ViewSpinner', '$timeout',
|
||||||
function ($scope, $routeParams, $location, Container, ContainerCommit, Image, Messages, ViewSpinner, $timeout) {
|
function ($scope, $stateParams, $state, $filter, Container, ContainerCommit, Image, Messages, ViewSpinner, $timeout) {
|
||||||
$scope.changes = [];
|
$scope.changes = [];
|
||||||
$scope.editEnv = false;
|
$scope.editEnv = false;
|
||||||
$scope.editPorts = false;
|
$scope.editPorts = false;
|
||||||
|
@ -12,10 +12,10 @@
|
||||||
|
|
||||||
var update = function () {
|
var update = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.get({id: $routeParams.id}, function (d) {
|
Container.get({id: $stateParams.id}, function (d) {
|
||||||
$scope.container = d;
|
$scope.container = d;
|
||||||
$scope.container.edit = false;
|
$scope.container.edit = false;
|
||||||
$scope.container.newContainerName = d.Name;
|
$scope.container.newContainerName = $filter('trimcontainername')(d.Name);
|
||||||
|
|
||||||
// fill up env
|
// fill up env
|
||||||
if (d.Config.Env) {
|
if (d.Config.Env) {
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
HostConfig: $scope.container.HostConfig
|
HostConfig: $scope.container.HostConfig
|
||||||
}, function (d) {
|
}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container started", $routeParams.id);
|
Messages.send("Container started", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to start." + e.data);
|
Messages.error("Failure", "Container failed to start." + e.data);
|
||||||
|
@ -90,9 +90,9 @@
|
||||||
|
|
||||||
$scope.stop = function () {
|
$scope.stop = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.stop({id: $routeParams.id}, function (d) {
|
Container.stop({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container stopped", $routeParams.id);
|
Messages.send("Container stopped", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to stop." + e.data);
|
Messages.error("Failure", "Container failed to stop." + e.data);
|
||||||
|
@ -101,123 +101,20 @@
|
||||||
|
|
||||||
$scope.kill = function () {
|
$scope.kill = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.kill({id: $routeParams.id}, function (d) {
|
Container.kill({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container killed", $routeParams.id);
|
Messages.send("Container killed", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to die." + e.data);
|
Messages.error("Failure", "Container failed to die." + e.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.restartEnv = function () {
|
|
||||||
var config = angular.copy($scope.container.Config);
|
|
||||||
|
|
||||||
config.Env = $scope.newCfg.Env.map(function(entry) {
|
|
||||||
return entry.name+"="+entry.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
var portBindings = angular.copy($scope.newCfg.Ports);
|
|
||||||
angular.forEach(portBindings, function(item, key) {
|
|
||||||
if (item.length === 0) {
|
|
||||||
delete portBindings[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var binds = [];
|
|
||||||
angular.forEach($scope.newCfg.Binds, function(b) {
|
|
||||||
if (b.ContPath !== '') {
|
|
||||||
var bindLine = '';
|
|
||||||
if (b.HostPath !== '') {
|
|
||||||
bindLine = b.HostPath + ':';
|
|
||||||
}
|
|
||||||
bindLine += b.ContPath;
|
|
||||||
if (b.ReadOnly) {
|
|
||||||
bindLine += ':ro';
|
|
||||||
}
|
|
||||||
if (b.HostPath !== '' || !b.DefaultBind) {
|
|
||||||
binds.push(bindLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ViewSpinner.spin();
|
|
||||||
ContainerCommit.commit({id: $routeParams.id, tag: $scope.container.Config.Image, config: config }, function (d) {
|
|
||||||
if ('Id' in d) {
|
|
||||||
var imageId = d.Id;
|
|
||||||
Image.inspect({id: imageId}, function(imageData) {
|
|
||||||
// Append current host config to image with new port bindings
|
|
||||||
imageData.Config.HostConfig = angular.copy($scope.container.HostConfig);
|
|
||||||
imageData.Config.HostConfig.PortBindings = portBindings;
|
|
||||||
imageData.Config.HostConfig.Binds = binds;
|
|
||||||
if (imageData.Config.HostConfig.NetworkMode === 'host') {
|
|
||||||
imageData.Config.Hostname = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
Container.create(imageData.Config, function(containerData) {
|
|
||||||
if (!('Id' in containerData)) {
|
|
||||||
Messages.error("Failure", "Container failed to create.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Stop current if running
|
|
||||||
if ($scope.container.State.Running) {
|
|
||||||
Container.stop({id: $routeParams.id}, function (d) {
|
|
||||||
Messages.send("Container stopped", $routeParams.id);
|
|
||||||
// start new
|
|
||||||
Container.start({
|
|
||||||
id: containerData.Id
|
|
||||||
}, function (d) {
|
|
||||||
$location.url('/containers/' + containerData.Id + '/');
|
|
||||||
Messages.send("Container started", $routeParams.id);
|
|
||||||
}, function (e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Container failed to start." + e.data);
|
|
||||||
});
|
|
||||||
}, function (e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Container failed to stop." + e.data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// start new
|
|
||||||
Container.start({
|
|
||||||
id: containerData.Id
|
|
||||||
}, function (d) {
|
|
||||||
$location.url('/containers/'+containerData.Id+'/');
|
|
||||||
Messages.send("Container started", $routeParams.id);
|
|
||||||
}, function (e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Container failed to start." + e.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}, function(e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Image failed to get." + e.data);
|
|
||||||
});
|
|
||||||
}, function (e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Image failed to get." + e.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Container commit failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}, function (e) {
|
|
||||||
update();
|
|
||||||
Messages.error("Failure", "Container failed to commit." + e.data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.commit = function () {
|
$scope.commit = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function (d) {
|
ContainerCommit.commit({id: $stateParams.id, repo: $scope.container.Config.Image}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container commited", $routeParams.id);
|
Messages.send("Container commited", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to commit." + e.data);
|
Messages.error("Failure", "Container failed to commit." + e.data);
|
||||||
|
@ -225,9 +122,9 @@
|
||||||
};
|
};
|
||||||
$scope.pause = function () {
|
$scope.pause = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.pause({id: $routeParams.id}, function (d) {
|
Container.pause({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container paused", $routeParams.id);
|
Messages.send("Container paused", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to pause." + e.data);
|
Messages.error("Failure", "Container failed to pause." + e.data);
|
||||||
|
@ -236,9 +133,9 @@
|
||||||
|
|
||||||
$scope.unpause = function () {
|
$scope.unpause = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.unpause({id: $routeParams.id}, function (d) {
|
Container.unpause({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container unpaused", $routeParams.id);
|
Messages.send("Container unpaused", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to unpause." + e.data);
|
Messages.error("Failure", "Container failed to unpause." + e.data);
|
||||||
|
@ -247,10 +144,10 @@
|
||||||
|
|
||||||
$scope.remove = function () {
|
$scope.remove = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.remove({id: $routeParams.id}, function (d) {
|
Container.remove({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
$location.path('/containers');
|
$state.go('containers', {}, {reload: true});
|
||||||
Messages.send("Container removed", $routeParams.id);
|
Messages.send("Container removed", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to remove." + e.data);
|
Messages.error("Failure", "Container failed to remove." + e.data);
|
||||||
|
@ -259,9 +156,9 @@
|
||||||
|
|
||||||
$scope.restart = function () {
|
$scope.restart = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.restart({id: $routeParams.id}, function (d) {
|
Container.restart({id: $stateParams.id}, function (d) {
|
||||||
update();
|
update();
|
||||||
Messages.send("Container restarted", $routeParams.id);
|
Messages.send("Container restarted", $stateParams.id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
update();
|
update();
|
||||||
Messages.error("Failure", "Container failed to restart." + e.data);
|
Messages.error("Failure", "Container failed to restart." + e.data);
|
||||||
|
@ -274,7 +171,7 @@
|
||||||
|
|
||||||
$scope.getChanges = function () {
|
$scope.getChanges = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.changes({id: $routeParams.id}, function (d) {
|
Container.changes({id: $stateParams.id}, function (d) {
|
||||||
$scope.changes = d;
|
$scope.changes = d;
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
});
|
});
|
||||||
|
@ -282,10 +179,10 @@
|
||||||
|
|
||||||
$scope.renameContainer = function () {
|
$scope.renameContainer = function () {
|
||||||
// #FIXME fix me later to handle http status to show the correct error message
|
// #FIXME fix me later to handle http status to show the correct error message
|
||||||
Container.rename({id: $routeParams.id, 'name': $scope.container.newContainerName}, function (data) {
|
Container.rename({id: $stateParams.id, 'name': $scope.container.newContainerName}, function (data) {
|
||||||
if (data.name) {
|
if (data.name) {
|
||||||
$scope.container.Name = data.name;
|
$scope.container.Name = data.name;
|
||||||
Messages.send("Container renamed", $routeParams.id);
|
Messages.send("Container renamed", $stateParams.id);
|
||||||
} else {
|
} else {
|
||||||
$scope.container.newContainerName = $scope.container.Name;
|
$scope.container.newContainerName = $scope.container.Name;
|
||||||
Messages.error("Failure", "Container failed to rename.");
|
Messages.error("Failure", "Container failed to rename.");
|
||||||
|
@ -308,5 +205,4 @@
|
||||||
|
|
||||||
update();
|
update();
|
||||||
$scope.getChanges();
|
$scope.getChanges();
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
angular.module('containerLogs', [])
|
angular.module('containerLogs', [])
|
||||||
.controller('ContainerLogsController', ['$scope', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner',
|
.controller('ContainerLogsController', ['$scope', '$stateParams', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner',
|
||||||
function ($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) {
|
function ($scope, $stateParams, $anchorScroll, ContainerLogs, Container, ViewSpinner) {
|
||||||
|
$scope.state = {};
|
||||||
|
$scope.state.displayTimestampsOut = false;
|
||||||
|
$scope.state.displayTimestampsErr = false;
|
||||||
$scope.stdout = '';
|
$scope.stdout = '';
|
||||||
$scope.stderr = '';
|
$scope.stderr = '';
|
||||||
$scope.showTimestamps = false;
|
|
||||||
$scope.tailLines = 2000;
|
$scope.tailLines = 2000;
|
||||||
|
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.get({id: $routeParams.id}, function (d) {
|
Container.get({id: $stateParams.id}, function (d) {
|
||||||
$scope.container = d;
|
$scope.container = d;
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
|
@ -21,25 +23,16 @@ angular.module('containerLogs', [])
|
||||||
|
|
||||||
function getLogs() {
|
function getLogs() {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
ContainerLogs.get($routeParams.id, {
|
getLogsStdout();
|
||||||
stdout: 1,
|
getLogsStderr();
|
||||||
stderr: 0,
|
|
||||||
timestamps: $scope.showTimestamps,
|
|
||||||
tail: $scope.tailLines
|
|
||||||
}, function (data, status, headers, config) {
|
|
||||||
// Replace carriage returns with newlines to clean up output
|
|
||||||
data = data.replace(/[\r]/g, '\n');
|
|
||||||
// Strip 8 byte header from each line of output
|
|
||||||
data = data.substring(8);
|
|
||||||
data = data.replace(/\n(.{8})/g, '\n');
|
|
||||||
$scope.stdout = data;
|
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
});
|
}
|
||||||
|
|
||||||
ContainerLogs.get($routeParams.id, {
|
function getLogsStderr() {
|
||||||
|
ContainerLogs.get($stateParams.id, {
|
||||||
stdout: 0,
|
stdout: 0,
|
||||||
stderr: 1,
|
stderr: 1,
|
||||||
timestamps: $scope.showTimestamps,
|
timestamps: $scope.state.displayTimestampsErr,
|
||||||
tail: $scope.tailLines
|
tail: $scope.tailLines
|
||||||
}, function (data, status, headers, config) {
|
}, function (data, status, headers, config) {
|
||||||
// Replace carriage returns with newlines to clean up output
|
// Replace carriage returns with newlines to clean up output
|
||||||
|
@ -48,7 +41,22 @@ angular.module('containerLogs', [])
|
||||||
data = data.substring(8);
|
data = data.substring(8);
|
||||||
data = data.replace(/\n(.{8})/g, '\n');
|
data = data.replace(/\n(.{8})/g, '\n');
|
||||||
$scope.stderr = data;
|
$scope.stderr = data;
|
||||||
ViewSpinner.stop();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogsStdout() {
|
||||||
|
ContainerLogs.get($stateParams.id, {
|
||||||
|
stdout: 1,
|
||||||
|
stderr: 0,
|
||||||
|
timestamps: $scope.state.displayTimestampsOut,
|
||||||
|
tail: $scope.tailLines
|
||||||
|
}, function (data, status, headers, config) {
|
||||||
|
// Replace carriage returns with newlines to clean up output
|
||||||
|
data = data.replace(/[\r]/g, '\n');
|
||||||
|
// Strip 8 byte header from each line of output
|
||||||
|
data = data.substring(8);
|
||||||
|
data = data.replace(/\n(.{8})/g, '\n');
|
||||||
|
$scope.stdout = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,16 +69,11 @@ angular.module('containerLogs', [])
|
||||||
clearInterval(logIntervalId);
|
clearInterval(logIntervalId);
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.scrollTo = function (id) {
|
$scope.toggleTimestampsOut = function () {
|
||||||
$location.hash(id);
|
getLogsStdout();
|
||||||
$anchorScroll();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleTimestamps = function () {
|
$scope.toggleTimestampsErr = function () {
|
||||||
getLogs();
|
getLogsStderr();
|
||||||
};
|
};
|
||||||
|
}]);
|
||||||
$scope.toggleTail = function () {
|
|
||||||
getLogs();
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|
|
@ -1,43 +1,47 @@
|
||||||
<div class="row logs">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<h4>Logs for container: <a href="#/containers/{{ container.Id }}/">{{ container.Name }}</a></td></h4>
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ container.Name|trimcontainername }}</div>
|
||||||
|
<div class="comment">Name</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="btn-group detail">
|
<div class="row">
|
||||||
<button class="btn btn-info" ng-click="scrollTo('stdout')">stdout</button>
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<button class="btn btn-warning" ng-click="scrollTo('stderr')">stderr</button>
|
<rd-widget>
|
||||||
</div>
|
<rd-widget-header icon="fa-info-circle" title="Stdout logs"></rd-widget-header>
|
||||||
<div class="pull-right col-xs-6">
|
<rd-widget-taskbar>
|
||||||
<div class="col-xs-6">
|
<input type="checkbox" ng-model="state.displayTimestampsOut" id="displayAllTsOut" ng-change="toggleTimestampsOut()"/>
|
||||||
<a class="btn btn-primary" ng-click="toggleTail()" role="button">Reload logs</a>
|
<label for="displayAllTsOut">Display timestamps</label>
|
||||||
<input id="tailLines" type="number" ng-style="{width: '45px'}"
|
</rd-widget-taskbar>
|
||||||
ng-model="tailLines" ng-keypress="($event.which === 13)? toggleTail() : 0"/>
|
<rd-widget-body classes="no-padding">
|
||||||
<label for="tailLines">lines</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-4">
|
|
||||||
<input id="timestampToggle" type="checkbox" ng-model="showTimestamps"
|
|
||||||
ng-change="toggleTimestamps()"/> <label for="timestampToggle">Timestamps</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 id="stdout" class="panel-title">STDOUT</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<pre id="stdoutLog" class="pre-scrollable pre-x-scrollable">{{stdout}}</pre>
|
<pre id="stdoutLog" class="pre-scrollable pre-x-scrollable">{{stdout}}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12">
|
|
||||||
<div class="panel panel-default">
|
<div class="row">
|
||||||
<div class="panel-heading">
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<h3 id="stderr" class="panel-title">STDERR</h3>
|
<rd-widget>
|
||||||
</div>
|
<rd-widget-header icon="fa-exclamation-triangle" title="Stderr logs"></rd-widget-header>
|
||||||
|
<rd-widget-taskbar>
|
||||||
|
<input type="checkbox" ng-model="state.displayTimestampsErr" id="displayAllTsErr" ng-change="toggleTimestampsErr()"/>
|
||||||
|
<label for="displayAllTsErr">Display timestamps</label>
|
||||||
|
</rd-widget-taskbar>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<pre id="stderrLog" class="pre-scrollable pre-x-scrollable">{{stderr}}</pre>
|
<pre id="stderrLog" class="pre-scrollable pre-x-scrollable">{{stderr}}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<div class="containerTop">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<h1>Top for: {{ containerName }}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group col-xs-2">
|
|
||||||
<input type="text" class="form-control" placeholder="[options] (aux)" ng-model="ps_args">
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-default" ng-click="getTop()">Submit</button>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th ng-repeat="title in containerTop.Titles">{{title}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="processInfos in containerTop.Processes">
|
|
||||||
<td ng-repeat="processInfo in processInfos track by $index">{{processInfo}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,25 +0,0 @@
|
||||||
angular.module('containerTop', [])
|
|
||||||
.controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'Container', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, Container, ViewSpinner) {
|
|
||||||
$scope.ps_args = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get container processes
|
|
||||||
*/
|
|
||||||
$scope.getTop = function () {
|
|
||||||
ViewSpinner.spin();
|
|
||||||
ContainerTop.get($routeParams.id, {
|
|
||||||
ps_args: $scope.ps_args
|
|
||||||
}, function (data) {
|
|
||||||
$scope.containerTop = data;
|
|
||||||
ViewSpinner.stop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Container.get({id: $routeParams.id}, function (d) {
|
|
||||||
$scope.containerName = d.Name.substring(1);
|
|
||||||
}, function (e) {
|
|
||||||
Messages.error("Failure", e.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.getTop();
|
|
||||||
}]);
|
|
|
@ -1,31 +1,33 @@
|
||||||
|
<div ng-include="template" ng-controller="StartContainerController"></div>
|
||||||
|
|
||||||
<h2>Containers:</h2>
|
<div class="col-lg-12">
|
||||||
|
<rd-widget>
|
||||||
<div>
|
<rd-widget-header icon="fa-tasks" title="Containers">
|
||||||
<ul class="nav nav-pills pull-left">
|
</rd-widget-header>
|
||||||
<li class="dropdown">
|
<rd-widget-taskbar classes="col-lg-12">
|
||||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b class="caret"></b></a>
|
<div class="pull-left">
|
||||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
<li><a tabindex="-1" href="" ng-click="startAction()">Start</a></li>
|
<button type="button" class="btn btn-primary" ng-click="startAction()" ng-disabled="!state.selectedItemCount">Start</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="stopAction()">Stop</a></li>
|
<button type="button" class="btn btn-primary" ng-click="stopAction()" ng-disabled="!state.selectedItemCount">Stop</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="restartAction()">Restart</a></li>
|
<button type="button" class="btn btn-primary" ng-click="killAction()" ng-disabled="!state.selectedItemCount">Kill</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="killAction()">Kill</a></li>
|
<button type="button" class="btn btn-primary" ng-click="restartAction()" ng-disabled="!state.selectedItemCount">Restart</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="pauseAction()">Pause</a></li>
|
<button type="button" class="btn btn-primary" ng-click="pauseAction()" ng-disabled="!state.selectedItemCount">Pause</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="unpauseAction()">Unpause</a></li>
|
<button type="button" class="btn btn-primary" ng-click="unpauseAction()" ng-disabled="!state.selectedItemCount">Unpause</button>
|
||||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount">Remove</button>
|
||||||
</ul>
|
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#create-modal">Start a new container...</button>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="pull-right form-inline">
|
|
||||||
<input type="checkbox" ng-model="displayAll" id="displayAll" ng-change="toggleGetAll()"/> <label for="displayAll">Display All</label>
|
|
||||||
<input type="text" class="form-control" style="vertical-align: center" id="filter" placeholder="Filter" ng-model="filter"/> <label class="sr-only" for="filter">Filter</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped">
|
<div class="pull-right">
|
||||||
|
<input type="checkbox" ng-model="state.displayAll" id="displayAll" ng-change="toggleGetAll()"/><label for="displayAll">Display All</label>
|
||||||
|
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||||
|
</div>
|
||||||
|
</rd-widget-taskbar>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Select</label></th>
|
<th><label><input type="checkbox" ng-model="state.toggle" ng-change="toggleSelectAll()" /> Select</label></th>
|
||||||
<th>
|
<th>
|
||||||
<a href="#/containers/" ng-click="order('Names')">
|
<a href="#/containers/" ng-click="order('Names')">
|
||||||
Name
|
Name
|
||||||
|
@ -64,13 +66,17 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="container in (filteredContainers = ( containers | filter: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" /></td>
|
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
|
||||||
<td><a href="#/containers/{{ container.Id }}/">{{ container|containername}}</a></td>
|
<td><a href="#/containers/{{ container.Id }}">{{ container|containername}}</a></td>
|
||||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||||
<td>{{ container.Command|truncate:40 }}</td>
|
<td>{{ container.Command|truncate:40 }}</td>
|
||||||
<td>{{ container.Created|getdate }}</td>
|
<td>{{ container.Created|getdate }}</td>
|
||||||
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
|
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
<rd-widget>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
angular.module('containers', [])
|
angular.module('containers', [])
|
||||||
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
|
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
|
||||||
function ($scope, Container, Settings, Messages, ViewSpinner) {
|
function ($scope, Container, Settings, Messages, ViewSpinner) {
|
||||||
|
|
||||||
|
$scope.state = {};
|
||||||
|
$scope.state.displayAll = Settings.displayAll;
|
||||||
$scope.sortType = 'Created';
|
$scope.sortType = 'Created';
|
||||||
$scope.sortReverse = true;
|
$scope.sortReverse = true;
|
||||||
$scope.toggle = false;
|
$scope.state.toggle = false;
|
||||||
$scope.displayAll = Settings.displayAll;
|
$scope.state.selectedItemCount = 0;
|
||||||
|
|
||||||
$scope.order = function (sortType) {
|
$scope.order = function (sortType) {
|
||||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||||
|
@ -13,9 +16,12 @@ angular.module('containers', [])
|
||||||
|
|
||||||
var update = function (data) {
|
var update = function (data) {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
|
$scope.state.selectedItemCount = 0;
|
||||||
Container.query(data, function (d) {
|
Container.query(data, function (d) {
|
||||||
$scope.containers = d.map(function (item) {
|
$scope.containers = d.filter(function (container) {
|
||||||
return new ContainerViewModel(item);
|
return container.Image !== 'swarm';
|
||||||
|
}).map(function (container) {
|
||||||
|
return new ContainerViewModel(container);
|
||||||
});
|
});
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
});
|
});
|
||||||
|
@ -75,14 +81,28 @@ angular.module('containers', [])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.selectItem = function (item) {
|
||||||
|
if (item.Checked) {
|
||||||
|
$scope.state.selectedItemCount++;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.toggleSelectAll = function () {
|
$scope.toggleSelectAll = function () {
|
||||||
angular.forEach($scope.filteredContainers, function (i) {
|
$scope.state.selectedItem = $scope.state.toggle;
|
||||||
i.Checked = $scope.toggle;
|
angular.forEach($scope.state.filteredContainers, function (i) {
|
||||||
|
i.Checked = $scope.state.toggle;
|
||||||
});
|
});
|
||||||
|
if ($scope.state.toggle) {
|
||||||
|
$scope.state.selectedItemCount = $scope.state.filteredContainers.length;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleGetAll = function () {
|
$scope.toggleGetAll = function () {
|
||||||
Settings.displayAll = $scope.displayAll;
|
Settings.displayAll = $scope.state.displayAll;
|
||||||
update({all: Settings.displayAll ? 1 : 0});
|
update({all: Settings.displayAll ? 1 : 0});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,4 +135,4 @@ angular.module('containers', [])
|
||||||
};
|
};
|
||||||
|
|
||||||
update({all: Settings.displayAll ? 1 : 0});
|
update({all: Settings.displayAll ? 1 : 0});
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
<div class="detail">
|
|
||||||
<h2>Containers Network</h2>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" ng-model="query" autofocus="true" class="form-control"
|
|
||||||
placeholder="Search" ng-change="network.selectContainers(query)"/>
|
|
||||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-warning" ng-click="network.hideSelected()">Hide Selected</button>
|
|
||||||
<button class="btn btn-info" ng-click="network.showSelectedDownstream()">Show Selected Downstream</button>
|
|
||||||
<button class="btn btn-info" ng-click="network.showSelectedUpstream()">Show Selected Upstream</button>
|
|
||||||
<button class="btn btn-success" ng-click="network.showAll()">Show All</button>
|
|
||||||
</div>
|
|
||||||
<input type="checkbox" ng-model="includeStopped" id="includeStopped" ng-change="toggleIncludeStopped()"/> <label
|
|
||||||
for="includeStopped">Include stopped containers</label>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<vis-network data="network.data" options="network.options" events="network.events"
|
|
||||||
component="network.component"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,271 +0,0 @@
|
||||||
angular.module('containersNetwork', ['ngVis'])
|
|
||||||
.controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function ($scope, $location, Container, Messages, VisDataSet) {
|
|
||||||
|
|
||||||
function ContainerNode(data) {
|
|
||||||
this.Id = data.Id;
|
|
||||||
// names have the following format: /Name
|
|
||||||
this.Name = data.Name.substring(1);
|
|
||||||
this.Image = data.Config.Image;
|
|
||||||
this.Running = data.State.Running;
|
|
||||||
var dataLinks = data.HostConfig.Links;
|
|
||||||
if (dataLinks != null) {
|
|
||||||
this.Links = {};
|
|
||||||
for (var i = 0; i < dataLinks.length; i++) {
|
|
||||||
// links have the following format: /TargetContainerName:/SourceContainerName/LinkAlias
|
|
||||||
var link = dataLinks[i].split(":");
|
|
||||||
var target = link[0].substring(1);
|
|
||||||
var alias = link[1].substring(link[1].lastIndexOf("/") + 1);
|
|
||||||
// only keep shortest alias
|
|
||||||
if (this.Links[target] == null || alias.length < this.Links[target].length) {
|
|
||||||
this.Links[target] = alias;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var dataVolumes = data.HostConfig.VolumesFrom;
|
|
||||||
//converting array into properties for simpler and faster access
|
|
||||||
if (dataVolumes != null) {
|
|
||||||
this.VolumesFrom = {};
|
|
||||||
for (var j = 0; j < dataVolumes.length; j++) {
|
|
||||||
this.VolumesFrom[dataVolumes[j]] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContainersNetworkData() {
|
|
||||||
this.nodes = new VisDataSet();
|
|
||||||
this.edges = new VisDataSet();
|
|
||||||
|
|
||||||
this.addContainerNode = function (container) {
|
|
||||||
this.nodes.add({
|
|
||||||
id: container.Id,
|
|
||||||
label: container.Name,
|
|
||||||
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
|
|
||||||
"<li><strong>ID:</strong> " + container.Id + "</li>" +
|
|
||||||
"<li><strong>Image:</strong> " + container.Image + "</li>" +
|
|
||||||
"</ul>",
|
|
||||||
color: (container.Running ? "#8888ff" : "#cccccc")
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hasEdge = function (from, to) {
|
|
||||||
return this.edges.getIds({
|
|
||||||
filter: function (item) {
|
|
||||||
return item.from === from.Id && item.to === to.Id;
|
|
||||||
}
|
|
||||||
}).length > 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addLinkEdgeIfExists = function (from, to) {
|
|
||||||
if (from.Links != null && from.Links[to.Name] != null && !this.hasEdge(from, to)) {
|
|
||||||
this.edges.add({
|
|
||||||
from: from.Id,
|
|
||||||
to: to.Id,
|
|
||||||
label: from.Links[to.Name]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addVolumeEdgeIfExists = function (from, to) {
|
|
||||||
if (from.VolumesFrom != null && (from.VolumesFrom[to.Id] != null || from.VolumesFrom[to.Name] != null) && !this.hasEdge(from, to)) {
|
|
||||||
this.edges.add({
|
|
||||||
from: from.Id,
|
|
||||||
to: to.Id,
|
|
||||||
color: {color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.removeContainersNodes = function (containersIds) {
|
|
||||||
this.nodes.remove(containersIds);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContainersNetwork() {
|
|
||||||
this.data = new ContainersNetworkData();
|
|
||||||
this.containers = {};
|
|
||||||
this.selectedContainersIds = [];
|
|
||||||
this.shownContainersIds = [];
|
|
||||||
this.events = {
|
|
||||||
select: function (event) {
|
|
||||||
$scope.network.selectedContainersIds = event.nodes;
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.query = '';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
doubleClick: function (event) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$location.path('/containers/' + event.nodes[0]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.options = {
|
|
||||||
navigation: true,
|
|
||||||
keyboard: true,
|
|
||||||
height: '500px', width: '700px',
|
|
||||||
nodes: {
|
|
||||||
shape: 'box'
|
|
||||||
},
|
|
||||||
edges: {
|
|
||||||
style: 'arrow'
|
|
||||||
},
|
|
||||||
physics: {
|
|
||||||
barnesHut: {
|
|
||||||
springLength: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addContainer = function (data) {
|
|
||||||
var container = new ContainerNode(data);
|
|
||||||
this.containers[container.Id] = container;
|
|
||||||
this.shownContainersIds.push(container.Id);
|
|
||||||
this.data.addContainerNode(container);
|
|
||||||
for (var otherContainerId in this.containers) {
|
|
||||||
var otherContainer = this.containers[otherContainerId];
|
|
||||||
this.data.addLinkEdgeIfExists(container, otherContainer);
|
|
||||||
this.data.addLinkEdgeIfExists(otherContainer, container);
|
|
||||||
this.data.addVolumeEdgeIfExists(container, otherContainer);
|
|
||||||
this.data.addVolumeEdgeIfExists(otherContainer, container);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.selectContainers = function (query) {
|
|
||||||
if (this.component != null) {
|
|
||||||
this.selectedContainersIds = this.searchContainers(query);
|
|
||||||
this.component.selectNodes(this.selectedContainersIds);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.searchContainers = function (query) {
|
|
||||||
if (query.trim() === "") {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
var selectedContainersIds = [];
|
|
||||||
for (var i = 0; i < this.shownContainersIds.length; i++) {
|
|
||||||
var container = this.containers[this.shownContainersIds[i]];
|
|
||||||
if (container.Name.indexOf(query) > -1 ||
|
|
||||||
container.Image.indexOf(query) > -1 ||
|
|
||||||
container.Id.indexOf(query) > -1) {
|
|
||||||
selectedContainersIds.push(container.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selectedContainersIds;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hideSelected = function () {
|
|
||||||
var i = 0;
|
|
||||||
while (i < this.shownContainersIds.length) {
|
|
||||||
if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) {
|
|
||||||
this.shownContainersIds.splice(i, 1);
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.data.removeContainersNodes(this.selectedContainersIds);
|
|
||||||
$scope.query = '';
|
|
||||||
this.selectedContainersIds = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.searchDownstream = function (containerId, downstreamContainersIds) {
|
|
||||||
if (downstreamContainersIds.indexOf(containerId) > -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
downstreamContainersIds.push(containerId);
|
|
||||||
var container = this.containers[containerId];
|
|
||||||
if (container.Links == null && container.VolumesFrom == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (var otherContainerId in this.containers) {
|
|
||||||
var otherContainer = this.containers[otherContainerId];
|
|
||||||
if (container.Links != null && container.Links[otherContainer.Name] != null) {
|
|
||||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
|
||||||
} else if (container.VolumesFrom != null &&
|
|
||||||
container.VolumesFrom[otherContainer.Id] != null) {
|
|
||||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateShownContainers = function (newShownContainersIds) {
|
|
||||||
for (var containerId in this.containers) {
|
|
||||||
if (newShownContainersIds.indexOf(containerId) > -1 &&
|
|
||||||
this.shownContainersIds.indexOf(containerId) === -1) {
|
|
||||||
this.data.addContainerNode(this.containers[containerId]);
|
|
||||||
} else if (newShownContainersIds.indexOf(containerId) === -1 &&
|
|
||||||
this.shownContainersIds.indexOf(containerId) > -1) {
|
|
||||||
this.data.removeContainersNodes(containerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.shownContainersIds = newShownContainersIds;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showSelectedDownstream = function () {
|
|
||||||
var downstreamContainersIds = [];
|
|
||||||
for (var i = 0; i < this.selectedContainersIds.length; i++) {
|
|
||||||
this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds);
|
|
||||||
}
|
|
||||||
this.updateShownContainers(downstreamContainersIds);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.searchUpstream = function (containerId, upstreamContainersIds) {
|
|
||||||
if (upstreamContainersIds.indexOf(containerId) > -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
upstreamContainersIds.push(containerId);
|
|
||||||
var container = this.containers[containerId];
|
|
||||||
for (var otherContainerId in this.containers) {
|
|
||||||
var otherContainer = this.containers[otherContainerId];
|
|
||||||
if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) {
|
|
||||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
|
||||||
} else if (otherContainer.VolumesFrom != null &&
|
|
||||||
otherContainer.VolumesFrom[container.Id] != null) {
|
|
||||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showSelectedUpstream = function () {
|
|
||||||
var upstreamContainersIds = [];
|
|
||||||
for (var i = 0; i < this.selectedContainersIds.length; i++) {
|
|
||||||
this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds);
|
|
||||||
}
|
|
||||||
this.updateShownContainers(upstreamContainersIds);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showAll = function () {
|
|
||||||
for (var containerId in this.containers) {
|
|
||||||
if (this.shownContainersIds.indexOf(containerId) === -1) {
|
|
||||||
this.data.addContainerNode(this.containers[containerId]);
|
|
||||||
this.shownContainersIds.push(containerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.network = new ContainersNetwork();
|
|
||||||
|
|
||||||
var showFailure = function (event) {
|
|
||||||
Messages.error('Failure', e.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
var addContainer = function (container) {
|
|
||||||
$scope.network.addContainer(container);
|
|
||||||
};
|
|
||||||
|
|
||||||
var update = function (data) {
|
|
||||||
Container.query(data, function (d) {
|
|
||||||
for (var i = 0; i < d.length; i++) {
|
|
||||||
Container.get({id: d[i].Id}, addContainer, showFailure);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
update({all: 0});
|
|
||||||
|
|
||||||
$scope.includeStopped = false;
|
|
||||||
$scope.toggleIncludeStopped = function () {
|
|
||||||
$scope.network.updateShownContainers([]);
|
|
||||||
update({all: $scope.includeStopped ? 1 : 0});
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<div id="create-network-modal" class="modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||||
|
<h3>Create network</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form novalidate role="form" name="createNetworkForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" placeholder='my_network'
|
||||||
|
ng-model="createNetworkConfig.Name" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Driver:</label>
|
||||||
|
<input type="text" placeholder='bridge'
|
||||||
|
ng-model="createNetworkConfig.Driver" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Subnet:</label>
|
||||||
|
<input type="text" placeholder='172.20.0.0/16'
|
||||||
|
ng-model="createNetworkConfig.IPAM.Config[0].Subnet" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>IPRange:</label>
|
||||||
|
<input type="text" placeholder='172.20.10.0/24'
|
||||||
|
ng-model="createNetworkConfig.IPAM.Config[0].IPRange" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Gateway:</label>
|
||||||
|
<input type="text" placeholder='172.20.10.11'
|
||||||
|
ng-model="createNetworkConfig.IPAM.Config[0].Gateway" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-error" id="error-message" style="display:none">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="" class="btn btn-primary" ng-click="createNetwork(createNetworkConfig)">Create</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,41 @@
|
||||||
|
angular.module('createNetwork', [])
|
||||||
|
.controller('CreateNetworkController', ['$scope', '$state', 'Messages', 'Network', 'ViewSpinner', 'errorMsgFilter',
|
||||||
|
function ($scope, $state, Messages, Network, ViewSpinner, errorMsgFilter) {
|
||||||
|
$scope.template = 'app/components/createNetwork/createNetwork.html';
|
||||||
|
|
||||||
|
$scope.init = function () {
|
||||||
|
$scope.createNetworkConfig = {
|
||||||
|
"Name": '',
|
||||||
|
"Driver": '',
|
||||||
|
"IPAM": {
|
||||||
|
"Config": [{}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.init();
|
||||||
|
|
||||||
|
$scope.createNetwork = function addNetwork(createNetworkConfig) {
|
||||||
|
if (_.isEmpty(createNetworkConfig.IPAM.Config[0])) {
|
||||||
|
delete createNetworkConfig.IPAM;
|
||||||
|
}
|
||||||
|
$('#error-message').hide();
|
||||||
|
ViewSpinner.spin();
|
||||||
|
$('#create-network-modal').modal('hide');
|
||||||
|
Network.create(createNetworkConfig, function (d) {
|
||||||
|
if (d.Id) {
|
||||||
|
Messages.send("Network created", d.Id);
|
||||||
|
} else {
|
||||||
|
Messages.error('Failure', errorMsgFilter(d));
|
||||||
|
}
|
||||||
|
ViewSpinner.stop();
|
||||||
|
$scope.init();
|
||||||
|
$state.go('networks', {}, {reload: true});
|
||||||
|
}, function (e) {
|
||||||
|
ViewSpinner.stop();
|
||||||
|
$scope.error = "Cannot pull image " + imageName + " Reason: " + e.data;
|
||||||
|
$('#create-network-modal').modal('show');
|
||||||
|
$('#error-message').show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}]);
|
|
@ -0,0 +1,40 @@
|
||||||
|
<div id="create-volume-modal" class="modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||||
|
<h3>Create volume</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form novalidate role="form" name="createVolumeForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" placeholder='my_volume'
|
||||||
|
ng-model="createVolumeConfig.Name" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Driver:</label>
|
||||||
|
<ui-select ng-model="selectedDriver.value" theme="bootstrap">
|
||||||
|
<ui-select-match>
|
||||||
|
<span ng-bind="$select.selected"></span>
|
||||||
|
</ui-select-match>
|
||||||
|
<ui-select-choices repeat="driver in availableDrivers">
|
||||||
|
<span ng-bind="driver"></span>
|
||||||
|
</ui-select-choices>
|
||||||
|
</ui-select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-show="selectedDriver.value == 'local-persist'">
|
||||||
|
<label>Mount point:</label>
|
||||||
|
<input type="text" ng-model="createVolumeConfig.DriverOpts.mountpoint" name="" placeholder="/volume/my_volume" class="form-control">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-error" id="error-message" style="display:none">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="" class="btn btn-primary" ng-click="addVolume(createVolumeConfig)">Create</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,39 @@
|
||||||
|
angular.module('createVolume', [])
|
||||||
|
.controller('CreateVolumeController', ['$scope', '$state', 'Messages', 'Volume', 'ViewSpinner', 'errorMsgFilter',
|
||||||
|
function ($scope, $state, Messages, Volume, ViewSpinner, errorMsgFilter) {
|
||||||
|
$scope.template = 'app/components/createVolume/createVolume.html';
|
||||||
|
|
||||||
|
$scope.init = function () {
|
||||||
|
$scope.createVolumeConfig = {
|
||||||
|
"Name": "",
|
||||||
|
"Driver": "",
|
||||||
|
"DriverOpts": {}
|
||||||
|
};
|
||||||
|
$scope.availableDrivers = ['local', 'local-persist'];
|
||||||
|
$scope.selectedDriver = { value: $scope.availableDrivers[0] };
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.init();
|
||||||
|
|
||||||
|
$scope.addVolume = function addVolume(createVolumeConfig) {
|
||||||
|
$('#error-message').hide();
|
||||||
|
ViewSpinner.spin();
|
||||||
|
$('#create-volume-modal').modal('hide');
|
||||||
|
createVolumeConfig.Driver = $scope.selectedDriver.value;
|
||||||
|
console.log(JSON.stringify(createVolumeConfig, null, 4));
|
||||||
|
Volume.create(createVolumeConfig, function (d) {
|
||||||
|
if (d.Name) {
|
||||||
|
Messages.send("Volume created", d.Name);
|
||||||
|
} else {
|
||||||
|
Messages.error('Failure', errorMsgFilter(d));
|
||||||
|
}
|
||||||
|
ViewSpinner.stop();
|
||||||
|
$state.go('volumes', {}, {reload: true});
|
||||||
|
}, function (e) {
|
||||||
|
ViewSpinner.stop();
|
||||||
|
$scope.error = "Cannot create volume " + createVolumeConfig.Name + " Reason: " + e.data;
|
||||||
|
$('#create-volume-modal').modal('show');
|
||||||
|
$('#error-message').show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}]);
|
|
@ -1,52 +1,71 @@
|
||||||
<div class="col-xs-offset-1">
|
<div class="row">
|
||||||
<!--<div class="sidebar span4">
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
<div ng-include="template" ng-controller="SideBarController"></div>
|
<rd-widget>
|
||||||
</div>-->
|
<rd-widget-body>
|
||||||
<div class="row">
|
<div class="widget-icon blue pull-left">
|
||||||
<div class="col-xs-10" id="masthead" style="display:none">
|
<i class="fa fa-tasks"></i>
|
||||||
<div class="jumbotron">
|
|
||||||
<h1>UI For Docker</h1>
|
|
||||||
|
|
||||||
<p class="lead">The UI for Docker container engine</p>
|
|
||||||
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">{{ containerData.total }}</div>
|
||||||
|
<div class="comment">Containers</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon green pull-left">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">{{ containerData.running }}</div>
|
||||||
<div class="row">
|
<div class="comment">Running</div>
|
||||||
<div class="col-xs-10">
|
</rd-widget-body>
|
||||||
<div class="col-xs-5">
|
</rd-widget>
|
||||||
<h3>Running Containers</h3>
|
|
||||||
<ul>
|
|
||||||
<li ng-repeat="container in containers|orderBy:predicate">
|
|
||||||
<a href="#/containers/{{ container.Id }}/">{{ container|containername }}</a>
|
|
||||||
<span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-5 text-right">
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
<h3>Status</h3>
|
<rd-widget>
|
||||||
<canvas id="containers-chart" class="pull-right">
|
<rd-widget-body>
|
||||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
<div class="widget-icon red pull-left">
|
||||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
<i class="fa fa-tasks"></i>
|
||||||
</canvas>
|
|
||||||
<div id="chart-legend"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">{{ containerData.stopped }}</div>
|
||||||
|
<div class="comment">Stopped</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon gray pull-left">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">{{ containerData.ghost }}</div>
|
||||||
<div class="row">
|
<div class="comment">Ghost</div>
|
||||||
<div class="col-xs-10" id="stats">
|
</rd-widget-body>
|
||||||
<h4>Containers created</h4>
|
</rd-widget>
|
||||||
<canvas id="containers-started-chart" width="700">
|
</div>
|
||||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
</div>
|
||||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
|
||||||
</canvas>
|
<div class="row">
|
||||||
<h4>Images created</h4>
|
<div class="col-lg-6">
|
||||||
<canvas id="images-created-chart" width="700">
|
<rd-widget>
|
||||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
<rd-widget-header icon="fa-tasks" title="Containers created"></rd-widget-header>
|
||||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
<rd-widget-body>
|
||||||
</canvas>
|
<canvas id="containers-started-chart" width="770" height="230">
|
||||||
</div>
|
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||||
|
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
</canvas>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-clone" title="Images created"></rd-widget-header>
|
||||||
|
<rd-widget-body>
|
||||||
|
<canvas id="images-created-chart" width="770" height="230">
|
||||||
|
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||||
|
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
</canvas>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
angular.module('dashboard', [])
|
angular.module('dashboard', [])
|
||||||
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function ($scope, Container, Image, Settings, LineChart) {
|
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function ($scope, Container, Image, Settings, LineChart) {
|
||||||
$scope.predicate = '-Created';
|
|
||||||
$scope.containers = [];
|
|
||||||
|
|
||||||
var getStarted = function (data) {
|
$scope.containerData = {};
|
||||||
$scope.totalContainers = data.length;
|
|
||||||
|
var buildCharts = function (data) {
|
||||||
|
$scope.containerData.total = data.length;
|
||||||
LineChart.build('#containers-started-chart', data, function (c) {
|
LineChart.build('#containers-started-chart', data, function (c) {
|
||||||
return new Date(c.Created * 1000).toLocaleDateString();
|
return new Date(c.Created * 1000).toLocaleDateString();
|
||||||
});
|
});
|
||||||
|
@ -17,59 +17,30 @@ angular.module('dashboard', [])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var opts = {animation: false};
|
|
||||||
if (Settings.firstLoad) {
|
|
||||||
opts.animation = true;
|
|
||||||
Settings.firstLoad = false;
|
|
||||||
localStorage.setItem('firstLoad', false);
|
|
||||||
$('#masthead').show();
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
$('#masthead').slideUp('slow');
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
Container.query({all: 1}, function (d) {
|
Container.query({all: 1}, function (d) {
|
||||||
var running = 0;
|
var running = 0;
|
||||||
var ghost = 0;
|
var ghost = 0;
|
||||||
var stopped = 0;
|
var stopped = 0;
|
||||||
|
|
||||||
for (var i = 0; i < d.length; i++) {
|
// TODO: centralize that
|
||||||
var item = d[i];
|
var containers = d.filter(function (container) {
|
||||||
|
return container.Image !== 'swarm';
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < containers.length; i++) {
|
||||||
|
var item = containers[i];
|
||||||
if (item.Status === "Ghost") {
|
if (item.Status === "Ghost") {
|
||||||
ghost += 1;
|
ghost += 1;
|
||||||
} else if (item.Status.indexOf('Exit') !== -1) {
|
} else if (item.Status.indexOf('Exit') !== -1) {
|
||||||
stopped += 1;
|
stopped += 1;
|
||||||
} else {
|
} else {
|
||||||
running += 1;
|
running += 1;
|
||||||
$scope.containers.push(new ContainerViewModel(item));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$scope.containerData.running = running;
|
||||||
|
$scope.containerData.stopped = stopped;
|
||||||
|
$scope.containerData.ghost = ghost;
|
||||||
|
|
||||||
getStarted(d);
|
buildCharts(containers);
|
||||||
|
|
||||||
var c = new Chart($('#containers-chart').get(0).getContext("2d"));
|
|
||||||
var data = [
|
|
||||||
{
|
|
||||||
value: running,
|
|
||||||
color: '#5bb75b',
|
|
||||||
title: 'Running'
|
|
||||||
}, // running
|
|
||||||
{
|
|
||||||
value: stopped,
|
|
||||||
color: '#C7604C',
|
|
||||||
title: 'Stopped'
|
|
||||||
}, // stopped
|
|
||||||
{
|
|
||||||
value: ghost,
|
|
||||||
color: '#E2EAE9',
|
|
||||||
title: 'Ghost'
|
|
||||||
} // ghost
|
|
||||||
];
|
|
||||||
|
|
||||||
c.Doughnut(data, opts);
|
|
||||||
var lgd = $('#chart-legend').get(0);
|
|
||||||
legend(lgd, data);
|
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
angular.module('dashboard')
|
||||||
|
.controller('MasterCtrl', ['$scope', '$cookieStore', 'Settings', function ($scope, $cookieStore, Settings) {
|
||||||
|
/**
|
||||||
|
* Sidebar Toggle & Cookie Control
|
||||||
|
*/
|
||||||
|
var mobileView = 992;
|
||||||
|
|
||||||
|
$scope.getWidth = function() {
|
||||||
|
return window.innerWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch($scope.getWidth, function(newValue, oldValue) {
|
||||||
|
if (newValue >= mobileView) {
|
||||||
|
if (angular.isDefined($cookieStore.get('toggle'))) {
|
||||||
|
$scope.toggle = ! $cookieStore.get('toggle') ? false : true;
|
||||||
|
} else {
|
||||||
|
$scope.toggle = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$scope.toggle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.toggleSidebar = function() {
|
||||||
|
$scope.toggle = !$scope.toggle;
|
||||||
|
$cookieStore.put('toggle', $scope.toggle);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onresize = function() {
|
||||||
|
$scope.$apply();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.uiVersion = Settings.uiVersion;
|
||||||
|
}]);
|
|
@ -1,34 +0,0 @@
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<h2>Events</h2>
|
|
||||||
|
|
||||||
<form class="form-inline">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="since">Since:</label>
|
|
||||||
<input id="since" type="datetime-local" ng-model="model.since" class="form-control" step="any"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="until">Until:</label>
|
|
||||||
<input id="until" type="datetime-local" ng-model="model.until" class="form-control" step="any"/>
|
|
||||||
</div>
|
|
||||||
<button ng-click="updateEvents()" class="btn btn-primary">Update</button>
|
|
||||||
</form>
|
|
||||||
<br>
|
|
||||||
<table class="table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Event</th>
|
|
||||||
<th>From</th>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Time</th>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat="event in dockerEvents">
|
|
||||||
<td ng-bind="event.status"/>
|
|
||||||
<td ng-bind="event.from"/>
|
|
||||||
<td ng-bind="event.id"/>
|
|
||||||
<td ng-bind="event.time * 1000 | date:'medium'"/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,42 +0,0 @@
|
||||||
angular.module('events', ['ngOboe'])
|
|
||||||
.controller('EventsController', ['Settings', '$scope', 'Oboe', 'Messages', '$timeout', function (Settings, $scope, oboe, Messages, $timeout) {
|
|
||||||
$scope.updateEvents = function () {
|
|
||||||
$scope.dockerEvents = [];
|
|
||||||
|
|
||||||
// TODO: Clean up URL building
|
|
||||||
var url = Settings.url + '/events?';
|
|
||||||
|
|
||||||
if ($scope.model.since) {
|
|
||||||
var sinceSecs = Math.floor($scope.model.since.getTime() / 1000);
|
|
||||||
url += 'since=' + sinceSecs + '&';
|
|
||||||
}
|
|
||||||
if ($scope.model.until) {
|
|
||||||
var untilSecs = Math.floor($scope.model.until.getTime() / 1000);
|
|
||||||
url += 'until=' + untilSecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
oboe({
|
|
||||||
url: url,
|
|
||||||
pattern: '{id status time}'
|
|
||||||
})
|
|
||||||
.then(function (node) {
|
|
||||||
// finished loading
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
}, function (error) {
|
|
||||||
// handle errors
|
|
||||||
Messages.error("Failure", error.data);
|
|
||||||
}, function (node) {
|
|
||||||
// node received
|
|
||||||
$scope.dockerEvents.push(node);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init
|
|
||||||
$scope.model = {};
|
|
||||||
$scope.model.since = new Date(Date.now() - 86400000); // 24 hours in the past
|
|
||||||
$scope.model.until = new Date();
|
|
||||||
$scope.updateEvents();
|
|
||||||
|
|
||||||
}]);
|
|
|
@ -1,9 +0,0 @@
|
||||||
angular.module('footer', [])
|
|
||||||
.controller('FooterController', ['$scope', 'Settings', 'Version', function ($scope, Settings, Version) {
|
|
||||||
$scope.template = 'app/components/footer/statusbar.html';
|
|
||||||
|
|
||||||
$scope.uiVersion = Settings.uiVersion;
|
|
||||||
Version.get({}, function (d) {
|
|
||||||
$scope.apiVersion = d.ApiVersion;
|
|
||||||
});
|
|
||||||
}]);
|
|
|
@ -1,6 +0,0 @@
|
||||||
<footer class="center well">
|
|
||||||
<p>
|
|
||||||
<small>Docker API Version: <strong>{{ apiVersion }}</strong> UI Version: <strong>{{ uiVersion }}</strong> <a
|
|
||||||
class="pull-right" href="https://github.com/kevana/ui-for-docker">UI For Docker</a></small>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
|
@ -1,29 +1,48 @@
|
||||||
<div ng-include="template" ng-controller="StartContainerController"></div>
|
<div class="row">
|
||||||
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<div class="alert alert-error" id="error-message" style="display:none">
|
<rd-widget>
|
||||||
{{ error }}
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-clone"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ id }}</div>
|
||||||
|
<div class="comment">Image ID</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="detail">
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
<h4>Image: {{ id }}</h4>
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
<div class="btn-group detail">
|
<i class="fa fa-cogs"></i>
|
||||||
<button class="btn btn-success" data-toggle="modal" data-target="#create-modal">Start Container</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">
|
||||||
<div>
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
<h4>Containers created:</h4>
|
<button class="btn btn-danger" ng-click="removeImage(id)">Remove</button>
|
||||||
<canvas id="containers-started-chart" width="750">
|
|
||||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
|
||||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
|
||||||
</canvas>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<table class="table table-striped">
|
<div class="comment">
|
||||||
|
Actions
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-clone" title="Image details"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Tags:</td>
|
<td>Created</td>
|
||||||
|
<td>{{ image.Created | date: 'medium'}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tags</td>
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="tag in RepoTags">{{ tag }}
|
<li ng-repeat="tag in RepoTags">{{ tag }}
|
||||||
|
@ -33,88 +52,41 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Created:</td>
|
<td>Parent</td>
|
||||||
<td>{{ image.Created | date: 'medium'}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Parent:</td>
|
|
||||||
<td><a href="#/images/{{ image.Parent }}/">{{ image.Parent }}</a></td>
|
<td><a href="#/images/{{ image.Parent }}/">{{ image.Parent }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Size (Virtual Size):</td>
|
<td>Size (Virtual Size)</td>
|
||||||
<td>{{ image.Size|humansize }} ({{ image.VirtualSize|humansize }})</td>
|
<td>{{ image.Size|humansize }} ({{ image.VirtualSize|humansize }})</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Hostname:</td>
|
<td>Hostname</td>
|
||||||
<td>{{ image.ContainerConfig.Hostname }}</td>
|
<td>{{ image.ContainerConfig.Hostname }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>User:</td>
|
<td>User</td>
|
||||||
<td>{{ image.ContainerConfig.User }}</td>
|
<td>{{ image.ContainerConfig.User }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Cmd:</td>
|
<td>Cmd</td>
|
||||||
<td>{{ image.ContainerConfig.Cmd }}</td>
|
<td>{{ image.ContainerConfig.Cmd }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Volumes:</td>
|
<td>Volumes</td>
|
||||||
<td>{{ image.ContainerConfig.Volumes }}</td>
|
<td>{{ image.ContainerConfig.Volumes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Volumes from:</td>
|
<td>Volumes from</td>
|
||||||
<td>{{ image.ContainerConfig.VolumesFrom }}</td>
|
<td>{{ image.ContainerConfig.VolumesFrom }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Built with:</td>
|
<td>Built with</td>
|
||||||
<td>Docker {{ image.DockerVersion }} on {{ image.Os}}, {{ image.Architecture }}</td>
|
<td>Docker {{ image.DockerVersion }} on {{ image.Os}}, {{ image.Architecture }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
<div class="row-fluid">
|
</rd-widget>
|
||||||
<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 class="form-inline" role="form">
|
|
||||||
<fieldset>
|
|
||||||
<legend>Tag image</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Tag:</label>
|
|
||||||
<input type="text" placeholder="repo" ng-model="tagInfo.repo" class="form-control">
|
|
||||||
<input type="text" placeholder="version" ng-model="tagInfo.version" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" ng-model="tagInfo.force" class="form-control"/> Force?
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<input type="button" ng-click="addTag()" value="Add Tag" class="btn btn-primary"/>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<div class="btn-remove">
|
|
||||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="removeImage(id)">Remove Image</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
angular.module('image', [])
|
angular.module('image', [])
|
||||||
.controller('ImageController', ['$scope', '$q', '$routeParams', '$location', 'Image', 'Container', 'Messages', 'LineChart',
|
.controller('ImageController', ['$scope', '$q', '$stateParams', '$state', 'Image', 'Container', 'Messages', 'LineChart',
|
||||||
function ($scope, $q, $routeParams, $location, Image, Container, Messages, LineChart) {
|
function ($scope, $q, $stateParams, $state, Image, Container, Messages, LineChart) {
|
||||||
$scope.history = [];
|
|
||||||
$scope.tagInfo = {repo: '', version: '', force: false};
|
$scope.tagInfo = {repo: '', version: '', force: false};
|
||||||
$scope.id = '';
|
$scope.id = '';
|
||||||
$scope.repoTags = [];
|
$scope.repoTags = [];
|
||||||
|
@ -14,9 +13,9 @@ angular.module('image', [])
|
||||||
});
|
});
|
||||||
// If last message key is 'Deleted' then assume the image is gone and send to images page
|
// If last message key is 'Deleted' then assume the image is gone and send to images page
|
||||||
if (d[d.length-1].Deleted) {
|
if (d[d.length-1].Deleted) {
|
||||||
$location.path('/images');
|
$state.go('images', {}, {reload: true});
|
||||||
} else {
|
} else {
|
||||||
$location.path('/images/' + $scope.id); // Refresh the current page.
|
$state.go('image', {id: $scope.id}, {reload: true});
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
$scope.error = e.data;
|
$scope.error = e.data;
|
||||||
|
@ -24,45 +23,6 @@ angular.module('image', [])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getHistory = function () {
|
|
||||||
Image.history({id: $routeParams.id}, function (d) {
|
|
||||||
$scope.history = d;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.addTag = function () {
|
|
||||||
var tag = $scope.tagInfo;
|
|
||||||
Image.tag({
|
|
||||||
id: $routeParams.id,
|
|
||||||
repo: tag.repo,
|
|
||||||
tag: tag.version,
|
|
||||||
force: tag.force ? 1 : 0
|
|
||||||
}, function (d) {
|
|
||||||
Messages.send("Tag Added", $routeParams.id);
|
|
||||||
$location.path('/images/' + $scope.id);
|
|
||||||
}, function (e) {
|
|
||||||
$scope.error = e.data;
|
|
||||||
$('#error-message').show();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function getContainersFromImage($q, Container, imageId) {
|
|
||||||
var defer = $q.defer();
|
|
||||||
|
|
||||||
Container.query({all: 1, notruc: 1}, function (d) {
|
|
||||||
var containers = [];
|
|
||||||
for (var i = 0; i < d.length; i++) {
|
|
||||||
var c = d[i];
|
|
||||||
if (c.ImageID === imageId) {
|
|
||||||
containers.push(new ContainerViewModel(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer.resolve(containers);
|
|
||||||
});
|
|
||||||
|
|
||||||
return defer.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get RepoTags from the /images/query endpoint instead of /image/json,
|
* Get RepoTags from the /images/query endpoint instead of /image/json,
|
||||||
* for backwards compatibility with Docker API versions older than 1.21
|
* for backwards compatibility with Docker API versions older than 1.21
|
||||||
|
@ -78,7 +38,7 @@ angular.module('image', [])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.get({id: $routeParams.id}, function (d) {
|
Image.get({id: $stateParams.id}, function (d) {
|
||||||
$scope.image = d;
|
$scope.image = d;
|
||||||
$scope.id = d.Id;
|
$scope.id = d.Id;
|
||||||
if (d.RepoTags) {
|
if (d.RepoTags) {
|
||||||
|
@ -86,21 +46,13 @@ angular.module('image', [])
|
||||||
} else {
|
} else {
|
||||||
getRepoTags($scope.id);
|
getRepoTags($scope.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getContainersFromImage($q, Container, $scope.id).then(function (containers) {
|
|
||||||
LineChart.build('#containers-started-chart', containers, function (c) {
|
|
||||||
return new Date(c.Created * 1000).toLocaleDateString();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
if (e.status === 404) {
|
if (e.status === 404) {
|
||||||
$('.detail').hide();
|
$('.detail').hide();
|
||||||
$scope.error = "Image not found.<br />" + $routeParams.id;
|
$scope.error = "Image not found.<br />" + $stateParams.id;
|
||||||
} else {
|
} else {
|
||||||
$scope.error = e.data;
|
$scope.error = e.data;
|
||||||
}
|
}
|
||||||
$('#error-message').show();
|
$('#error-message').show();
|
||||||
});
|
});
|
||||||
|
}]);
|
||||||
$scope.getHistory();
|
|
||||||
}]);
|
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
<div ng-include="template" ng-controller="BuilderController"></div>
|
|
||||||
<div ng-include="template" ng-controller="PullImageController"></div>
|
<div ng-include="template" ng-controller="PullImageController"></div>
|
||||||
|
|
||||||
<h2>Images:</h2>
|
<div class="col-lg-12">
|
||||||
|
<rd-widget>
|
||||||
<div>
|
<rd-widget-header icon="fa-clone" title="Images">
|
||||||
<ul class="nav nav-pills pull-left">
|
</rd-widget-header>
|
||||||
<li class="dropdown">
|
<rd-widget-taskbar classes="col-lg-12">
|
||||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b class="caret"></b></a>
|
<div class="pull-left">
|
||||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount">Remove</button>
|
||||||
</ul>
|
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#pull-modal">Pull new image...</button>
|
||||||
</li>
|
|
||||||
<li><a data-toggle="modal" data-target="#pull-modal" href="">Pull</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="pull-right form-inline">
|
|
||||||
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label class="sr-only" for="filter">Filter</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped">
|
<div class="pull-right">
|
||||||
|
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||||
|
</div>
|
||||||
|
</rd-widget-taskbar>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()" /> Select</label></th>
|
<th><label><input type="checkbox" ng-model="state.toggle" ng-change="toggleSelectAll()" /> Select</label></th>
|
||||||
<th>
|
<th>
|
||||||
<a href="#/images/" ng-click="order('Id')">
|
<a href="#/images/" ng-click="order('Id')">
|
||||||
Id
|
Id
|
||||||
|
@ -53,12 +52,16 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="image in (filteredImages = (images | filter:filter | orderBy:sortType:sortReverse))">
|
<tr ng-repeat="image in (state.filteredImages = (images | filter:state.filter | orderBy:sortType:sortReverse))">
|
||||||
<td><input type="checkbox" ng-model="image.Checked" /></td>
|
<td><input type="checkbox" ng-model="image.Checked" ng-change="selectItem(image)" /></td>
|
||||||
<td><a href="#/images/{{ image.Id }}/?tag={{ image|repotag }}">{{ image.Id|truncate:20}}</a></td>
|
<td><a href="#/images/{{ image.Id }}/?tag={{ image|repotag }}">{{ image.Id|truncate:20}}</a></td>
|
||||||
<td>{{ image|repotag }}</td>
|
<td>{{ image|repotag }}</td>
|
||||||
<td>{{ image.VirtualSize|humansize }}</td>
|
<td>{{ image.VirtualSize|humansize }}</td>
|
||||||
<td>{{ image.Created|getdate }}</td>
|
<td>{{ image.Created|getdate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
<rd-widget>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,17 +1,34 @@
|
||||||
angular.module('images', [])
|
angular.module('images', [])
|
||||||
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
|
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
|
||||||
function ($scope, Image, ViewSpinner, Messages) {
|
function ($scope, Image, ViewSpinner, Messages) {
|
||||||
|
$scope.state = {};
|
||||||
$scope.sortType = 'Created';
|
$scope.sortType = 'Created';
|
||||||
$scope.sortReverse = true;
|
$scope.sortReverse = true;
|
||||||
$scope.toggle = false;
|
$scope.state.toggle = false;
|
||||||
|
$scope.state.selectedItemCount = 0;
|
||||||
|
|
||||||
$scope.order = function(sortType) {
|
$scope.order = function(sortType) {
|
||||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||||
$scope.sortType = sortType;
|
$scope.sortType = sortType;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.showBuilder = function () {
|
$scope.toggleSelectAll = function () {
|
||||||
$('#build-modal').modal('show');
|
angular.forEach($scope.state.filteredImages, function (i) {
|
||||||
|
i.Checked = $scope.state.toggle;
|
||||||
|
});
|
||||||
|
if ($scope.state.toggle) {
|
||||||
|
$scope.state.selectedItemCount = $scope.state.filteredImages.length;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.selectItem = function (item) {
|
||||||
|
if (item.Checked) {
|
||||||
|
$scope.state.selectedItemCount++;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount--;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeAction = function () {
|
$scope.removeAction = function () {
|
||||||
|
@ -41,12 +58,7 @@ angular.module('images', [])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleSelectAll = function () {
|
function fetchImages() {
|
||||||
angular.forEach($scope.filteredImages, function (i) {
|
|
||||||
i.Checked = $scope.toggle;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Image.query({}, function (d) {
|
Image.query({}, function (d) {
|
||||||
$scope.images = d.map(function (item) {
|
$scope.images = d.map(function (item) {
|
||||||
|
@ -57,4 +69,7 @@ angular.module('images', [])
|
||||||
Messages.error("Failure", e.data);
|
Messages.error("Failure", e.data);
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
});
|
});
|
||||||
}]);
|
}
|
||||||
|
|
||||||
|
fetchImages();
|
||||||
|
}]);
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
<div class="detail">
|
|
||||||
<h2>Docker Information</h2>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p class="lead">
|
|
||||||
<strong>API Endpoint: </strong>{{ endpoint }}<br/>
|
|
||||||
<strong>API Version: </strong>{{ docker.ApiVersion }}<br/>
|
|
||||||
<strong>Docker version: </strong>{{ docker.Version }}<br/>
|
|
||||||
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br/>
|
|
||||||
<strong>Go Version: </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>CPUs:</td>
|
|
||||||
<td>{{ info.NCPU }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Total Memory:</td>
|
|
||||||
<td>{{ info.MemTotal|humansize }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Operating System:</td>
|
|
||||||
<td>{{ info.OperatingSystem }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Kernel Version:</td>
|
|
||||||
<td>{{ info.KernelVersion }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>ID:</td>
|
|
||||||
<td>{{ info.ID }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Labels:</td>
|
|
||||||
<td>{{ info.Labels }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>File Descriptors:</td>
|
|
||||||
<td>{{ info.NFd }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Goroutines:</td>
|
|
||||||
<td>{{ info.NGoroutines }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Storage Driver:</td>
|
|
||||||
<td>{{ info.Driver }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Storage Driver Status:</td>
|
|
||||||
<td>
|
|
||||||
<p ng-repeat="val in info.DriverStatus">
|
|
||||||
{{ val[0] }}: {{ val[1] }}
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Execution Driver:</td>
|
|
||||||
<td>{{ info.ExecutionDriver }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Events:</td>
|
|
||||||
<td><a href="#/events">Events</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>IPv4 Forwarding:</td>
|
|
||||||
<td>{{ info.IPv4Forwarding }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Index Server Address:</td>
|
|
||||||
<td>{{ info.IndexServerAddress }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Init Path:</td>
|
|
||||||
<td>{{ info.InitPath }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Docker Root Directory:</td>
|
|
||||||
<td>{{ info.DockerRootDir }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Init SHA1</td>
|
|
||||||
<td>{{ info.InitSha1 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Memory Limit:</td>
|
|
||||||
<td>{{ info.MemoryLimit }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Swap Limit:</td>
|
|
||||||
<td>{{ info.SwapLimit }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
|
@ -1,14 +0,0 @@
|
||||||
angular.module('info', [])
|
|
||||||
.controller('InfoController', ['$scope', 'Info', 'Version', 'Settings',
|
|
||||||
function ($scope, Info, Version, Settings) {
|
|
||||||
$scope.info = {};
|
|
||||||
$scope.docker = {};
|
|
||||||
$scope.endpoint = Settings.endpoint;
|
|
||||||
|
|
||||||
Version.get({}, function (d) {
|
|
||||||
$scope.docker = d;
|
|
||||||
});
|
|
||||||
Info.get({}, function (d) {
|
|
||||||
$scope.info = d;
|
|
||||||
});
|
|
||||||
}]);
|
|
|
@ -1,21 +0,0 @@
|
||||||
<div class="masthead">
|
|
||||||
<h3 class="text-muted">UI For Docker</h3>
|
|
||||||
|
|
||||||
<div class="col-xs-11">
|
|
||||||
<ul class="nav well">
|
|
||||||
<li><a href="#/">Dashboard</a></li>
|
|
||||||
<li><a href="#/containers/">Containers</a></li>
|
|
||||||
<li><a href="#/containers_network/">Containers Network</a></li>
|
|
||||||
<li><a href="#/images/">Images</a></li>
|
|
||||||
<li ng-if="showNetworksVolumes"><a href="#/networks/">Networks</a></li>
|
|
||||||
<li ng-if="showNetworksVolumes"><a href="#/volumes/">Volumes</a></li>
|
|
||||||
<li><a href="#/info/">Info</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-1">
|
|
||||||
<button class="btn btn-primary" ng-click="refresh()">
|
|
||||||
<span class="glyphicon glyphicon-refresh" aria-hidden="true"></span>
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,15 +0,0 @@
|
||||||
angular.module('masthead', [])
|
|
||||||
.controller('MastheadController', ['$scope', 'Version', function ($scope, Version) {
|
|
||||||
$scope.template = 'app/components/masthead/masthead.html';
|
|
||||||
$scope.showNetworksVolumes = false;
|
|
||||||
|
|
||||||
Version.get(function(d) {
|
|
||||||
if (d.ApiVersion >= 1.21) {
|
|
||||||
$scope.showNetworksVolumes = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.refresh = function() {
|
|
||||||
location.reload();
|
|
||||||
};
|
|
||||||
}]);
|
|
|
@ -1,94 +1,110 @@
|
||||||
<div class="detail">
|
<div class="row">
|
||||||
|
<div class="col-lg-9 col-md-9 col-xs-9">
|
||||||
<h4>Network: {{ network.Name }}</h4>
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
<table class="table table-striped">
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-sitemap"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ network.Name }}</div>
|
||||||
|
<div class="comment">Name</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-3 col-xs-3">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
|
<button class="btn btn-default" disabled>Connect container...</button>
|
||||||
|
<button class="btn btn-danger" ng-click="remove(id)">Remove</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="comment">
|
||||||
|
Actions
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-sitemap" title="Network details"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Name:</td>
|
<td>Id</td>
|
||||||
<td>{{ network.Name }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Id:</td>
|
|
||||||
<td>{{ network.Id }}</td>
|
<td>{{ network.Id }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Scope:</td>
|
<td>Scope</td>
|
||||||
<td>{{ network.Scope }}</td>
|
<td>{{ network.Scope }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Driver:</td>
|
<td>Driver</td>
|
||||||
<td>{{ network.Driver }}</td>
|
<td>{{ network.Driver }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>IPAM:</td>
|
<td>IPAM</td>
|
||||||
<td>
|
<td>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Driver:</td>
|
<td>Driver</td>
|
||||||
<td>{{ network.IPAM.Driver }}</td>
|
<td>{{ network.IPAM.Driver }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Subnet:</td>
|
<td>Subnet</td>
|
||||||
<td>{{ network.IPAM.Config[0].Subnet }}</td>
|
<td>{{ network.IPAM.Config[0].Subnet }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Gateway:</td>
|
<td>Gateway</td>
|
||||||
<td>{{ network.IPAM.Config[0].Gateway }}</td>
|
<td>{{ network.IPAM.Config[0].Gateway }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Containers:</td>
|
<td>Containers</td>
|
||||||
<td>
|
<td>
|
||||||
<table class="table table-striped" ng-repeat="(Id, container) in network.Containers">
|
<table class="table table-striped" ng-repeat="(Id, container) in network.Containers">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Id:</td>
|
<td>Id</td>
|
||||||
<td><a href="#/containers/{{ Id }}">{{ Id }}</a></td>
|
<td><a href="#/containers/{{ Id }}">{{ Id }}</a></td>
|
||||||
<td>
|
</tr>
|
||||||
<button ng-click="disconnect(network.Id, Id)" class="btn btn-danger btn-sm">
|
<tr>
|
||||||
|
<td>EndpointID</td>
|
||||||
|
<td>{{ container.EndpointID}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MacAddress</td>
|
||||||
|
<td>{{ container.MacAddress}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IPv4Address</td>
|
||||||
|
<td>{{ container.IPv4Address}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IPv6Address</td>
|
||||||
|
<td>{{ container.IPv6Address}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<button ng-click="disconnect(network.Id, Id)" class="btn btn-danger">
|
||||||
Disconnect from network
|
Disconnect from network
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>EndpointID:</td>
|
|
||||||
<td>{{ container.EndpointID}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>MacAddress:</td>
|
|
||||||
<td>{{ container.MacAddress}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>IPv4Address:</td>
|
|
||||||
<td>{{ container.IPv4Address}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>IPv6Address:</td>
|
|
||||||
<td>{{ container.IPv6Address}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
<form class="form-inline">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Container ID:
|
|
||||||
<input ng-model="containerId" placeholder="3613f73ba0e4" class="form-control">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button ng-click="connect(network.Id, containerId)" class="btn btn-primary">
|
|
||||||
Connect
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Options:</td>
|
<td>Options</td>
|
||||||
<td>
|
<td>
|
||||||
<table role="table" class="table table-striped">
|
<table role="table" class="table table-striped">
|
||||||
<tr>
|
|
||||||
<th>Key</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat="(k, v) in network.Options">
|
<tr ng-repeat="(k, v) in network.Options">
|
||||||
<td>{{ k }}</td>
|
<td>{{ k }}</td>
|
||||||
<td>{{ v }}</td>
|
<td>{{ v }}</td>
|
||||||
|
@ -98,13 +114,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
<hr/>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="btn-remove">
|
|
||||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="removeImage(id)">Remove Network
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,44 +1,25 @@
|
||||||
angular.module('network', []).config(['$routeProvider', function ($routeProvider) {
|
angular.module('network', []).config(['$routeProvider', function ($routeProvider) {
|
||||||
$routeProvider.when('/networks/:id/', {
|
}]).controller('NetworkController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$state', '$stateParams', 'errorMsgFilter',
|
||||||
templateUrl: 'app/components/network/network.html',
|
function ($scope, Network, ViewSpinner, Messages, $state, $stateParams, errorMsgFilter) {
|
||||||
controller: 'NetworkController'
|
|
||||||
});
|
|
||||||
}]).controller('NetworkController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$routeParams', '$location', 'errorMsgFilter',
|
|
||||||
function ($scope, Network, ViewSpinner, Messages, $routeParams, $location, errorMsgFilter) {
|
|
||||||
|
|
||||||
$scope.disconnect = function disconnect(networkId, containerId) {
|
$scope.disconnect = function disconnect(networkId, containerId) {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Network.disconnect({id: $routeParams.id}, {Container: containerId}, function (d) {
|
Network.disconnect({id: $stateParams.id}, {Container: containerId}, function (d) {
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
Messages.send("Container disconnected", containerId);
|
Messages.send("Container disconnected", containerId);
|
||||||
$location.path('/networks/' + $routeParams.id); // Refresh the current page.
|
$state.go('network', {id: $stateParams.id}, {reload: true});
|
||||||
}, function (e) {
|
|
||||||
ViewSpinner.stop();
|
|
||||||
Messages.error("Failure", e.data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$scope.connect = function connect(networkId, containerId) {
|
|
||||||
ViewSpinner.spin();
|
|
||||||
Network.connect({id: $routeParams.id}, {Container: containerId}, function (d) {
|
|
||||||
ViewSpinner.stop();
|
|
||||||
var errmsg = errorMsgFilter(d);
|
|
||||||
if (errmsg) {
|
|
||||||
Messages.error('Error', errmsg);
|
|
||||||
} else {
|
|
||||||
Messages.send("Container connected", d);
|
|
||||||
}
|
|
||||||
$location.path('/networks/' + $routeParams.id); // Refresh the current page.
|
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
Messages.error("Failure", e.data);
|
Messages.error("Failure", e.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.remove = function remove(networkId) {
|
$scope.remove = function remove(networkId) {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Network.remove({id: $routeParams.id}, function (d) {
|
Network.remove({id: $stateParams.id}, function (d) {
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
Messages.send("Network removed", d);
|
Messages.send("Network removed", "");
|
||||||
$location.path('/networks'); // Go to the networks page
|
$state.go('networks', {});
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
Messages.error("Failure", e.data);
|
Messages.error("Failure", e.data);
|
||||||
|
@ -46,11 +27,11 @@ angular.module('network', []).config(['$routeProvider', function ($routeProvider
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Network.get({id: $routeParams.id}, function (d) {
|
Network.get({id: $stateParams.id}, function (d) {
|
||||||
$scope.network = d;
|
$scope.network = d;
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
Messages.error("Failure", e.data);
|
Messages.error("Failure", e.data);
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
<h2>Networks:</h2>
|
<div ng-include="template" ng-controller="CreateNetworkController"></div>
|
||||||
|
|
||||||
<div>
|
<div class="col-lg-12">
|
||||||
<ul class="nav nav-pills pull-left">
|
<rd-widget>
|
||||||
<li class="dropdown">
|
<rd-widget-header icon="fa-sitemap" title="Networks">
|
||||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b
|
</rd-widget-header>
|
||||||
class="caret"></b></a>
|
<rd-widget-taskbar classes="col-lg-12">
|
||||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
<div class="pull-left">
|
||||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
</ul>
|
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount">Remove</button>
|
||||||
</li>
|
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#create-network-modal">Create new network...</button>
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="pull-right form-inline">
|
|
||||||
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label
|
|
||||||
class="sr-only" for="filter">Filter</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped">
|
<div class="pull-right">
|
||||||
|
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||||
|
</div>
|
||||||
|
</rd-widget-taskbar>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</label></th>
|
<th><label><input type="checkbox" ng-model="state.toggle" ng-change="toggleSelectAll()"/> Select</label></th>
|
||||||
<th>
|
<th>
|
||||||
<a href="#/networks/" ng-click="order('Name')">
|
<a href="#/networks/" ng-click="order('Name')">
|
||||||
Name
|
Name
|
||||||
|
@ -72,8 +73,8 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="network in ( filteredNetworks = (networks | filter:filter | orderBy:sortType:sortReverse))">
|
<tr ng-repeat="network in ( state.filteredNetworks = (networks | filter:state.filter | orderBy:sortType:sortReverse))">
|
||||||
<td><input type="checkbox" ng-model="network.Checked"/></td>
|
<td><input type="checkbox" ng-model="network.Checked" ng-change="selectItem(network)"/></td>
|
||||||
<td><a href="#/networks/{{ network.Id }}/">{{ network.Name|truncate:20}}</a></td>
|
<td><a href="#/networks/{{ network.Id }}/">{{ network.Name|truncate:20}}</a></td>
|
||||||
<td>{{ network.Id }}</td>
|
<td>{{ network.Id }}</td>
|
||||||
<td>{{ network.Scope }}</td>
|
<td>{{ network.Scope }}</td>
|
||||||
|
@ -83,39 +84,8 @@
|
||||||
<td>{{ network.IPAM.Config[0].Gateway }}</td>
|
<td>{{ network.IPAM.Config[0].Gateway }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-offset-3 col-xs-6">
|
|
||||||
<form role="form" class="">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" placeholder='isolated_nw'
|
|
||||||
ng-model="createNetworkConfig.Name" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Driver:</label>
|
|
||||||
<input type="text" placeholder='bridge'
|
|
||||||
ng-model="createNetworkConfig.Driver" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Subnet:</label>
|
|
||||||
<input type="text" placeholder='172.20.0.0/16'
|
|
||||||
ng-model="createNetworkConfig.IPAM.Config[0].Subnet" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>IPRange:</label>
|
|
||||||
<input type="text" placeholder='172.20.10.0/24'
|
|
||||||
ng-model="createNetworkConfig.IPAM.Config[0].IPRange" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Gateway:</label>
|
|
||||||
<input type="text" placeholder='172.20.10.11'
|
|
||||||
ng-model="createNetworkConfig.IPAM.Config[0].Gateway" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
|
||||||
ng-click="addNetwork(createNetworkConfig)">
|
|
||||||
Create Network
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
<rd-widget>
|
||||||
</div>
|
</div>
|
|
@ -1,30 +1,36 @@
|
||||||
angular.module('networks', []).config(['$routeProvider', function ($routeProvider) {
|
angular.module('networks', [])
|
||||||
$routeProvider.when('/networks/', {
|
.controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', 'errorMsgFilter',
|
||||||
templateUrl: 'app/components/networks/networks.html',
|
function ($scope, Network, ViewSpinner, Messages, errorMsgFilter) {
|
||||||
controller: 'NetworksController'
|
|
||||||
});
|
$scope.state = {};
|
||||||
}]).controller('NetworksController', ['$scope', 'Network', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
|
$scope.state.toggle = false;
|
||||||
function ($scope, Network, ViewSpinner, Messages, $route, errorMsgFilter) {
|
$scope.state.selectedItemCount = 0;
|
||||||
$scope.sortType = 'Name';
|
$scope.sortType = 'Name';
|
||||||
$scope.sortReverse = true;
|
$scope.sortReverse = true;
|
||||||
$scope.toggle = false;
|
|
||||||
$scope.order = function(sortType) {
|
$scope.order = function(sortType) {
|
||||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||||
$scope.sortType = sortType;
|
$scope.sortType = sortType;
|
||||||
};
|
};
|
||||||
$scope.createNetworkConfig = {
|
|
||||||
"Name": '',
|
$scope.toggleSelectAll = function () {
|
||||||
"Driver": '',
|
angular.forEach($scope.state.filteredNetworks, function (i) {
|
||||||
"IPAM": {
|
i.Checked = $scope.state.toggle;
|
||||||
"Config": [{
|
});
|
||||||
"Subnet": '',
|
if ($scope.state.toggle) {
|
||||||
"IPRange": '',
|
$scope.state.selectedItemCount = $scope.state.filteredNetworks.length;
|
||||||
"Gateway": ''
|
} else {
|
||||||
}]
|
$scope.state.selectedItemCount = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.selectItem = function (item) {
|
||||||
|
if (item.Checked) {
|
||||||
|
$scope.state.selectedItemCount++;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.removeAction = function () {
|
$scope.removeAction = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
|
@ -51,28 +57,6 @@ angular.module('networks', []).config(['$routeProvider', function ($routeProvide
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleSelectAll = function () {
|
|
||||||
angular.forEach($scope.filteredNetworks, function (i) {
|
|
||||||
i.Checked = $scope.toggle;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.addNetwork = function addNetwork(createNetworkConfig) {
|
|
||||||
ViewSpinner.spin();
|
|
||||||
Network.create(createNetworkConfig, function (d) {
|
|
||||||
if (d.Id) {
|
|
||||||
Messages.send("Network created", d.Id);
|
|
||||||
} else {
|
|
||||||
Messages.error('Failure', errorMsgFilter(d));
|
|
||||||
}
|
|
||||||
ViewSpinner.stop();
|
|
||||||
fetchNetworks();
|
|
||||||
}, function (e) {
|
|
||||||
Messages.error("Failure", e.data);
|
|
||||||
ViewSpinner.stop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function fetchNetworks() {
|
function fetchNetworks() {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Network.query({}, function (d) {
|
Network.query({}, function (d) {
|
||||||
|
@ -84,4 +68,4 @@ angular.module('networks', []).config(['$routeProvider', function ($routeProvide
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fetchNetworks();
|
fetchNetworks();
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -2,34 +2,25 @@
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||||
<h3>Pull Image</h3>
|
<h3>Pull image</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form novalidate role="form" name="pullForm">
|
<form novalidate role="form" name="pullForm">
|
||||||
<!--<div class="input-group">
|
|
||||||
<span class="input-group-addon" id="basic-addon1">Image name</span>
|
|
||||||
<input type="text" class="form-control" placeholder="imageName" aria-describedby="basic-addon1">
|
|
||||||
</div>-->
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Registry:</label>
|
<label>Registry:</label>
|
||||||
<input type="text" ng-model="config.registry" class="form-control"
|
<input type="text" ng-model="config.registry" class="form-control"
|
||||||
placeholder="Registry. Leave empty to user docker hub"/>
|
placeholder="Leave empty to user DockerHub"/>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Repo:</label>
|
|
||||||
<input type="text" ng-model="config.repo" class="form-control"
|
|
||||||
placeholder="Repository - usually your username."/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Image Name:</label>
|
<label>Image Name:</label>
|
||||||
<input type="text" ng-model="config.fromImage" class="form-control" placeholder="Image name"
|
<input type="text" ng-model="config.fromImage" class="form-control" placeholder="username/image"
|
||||||
required/>
|
required/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tag Name:</label>
|
<label>Tag Name:</label>
|
||||||
<input type="text" ng-model="config.tag" class="form-control"
|
<input type="text" ng-model="config.tag" class="form-control"
|
||||||
placeholder="Tag name. If empty it will download ALL tags."/>
|
placeholder="Leave empty to download ALL tags."/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
angular.module('pullImage', [])
|
angular.module('pullImage', [])
|
||||||
.controller('PullImageController', ['$scope', '$log', 'Messages', 'Image', 'ViewSpinner',
|
.controller('PullImageController', ['$scope', '$state', 'Messages', 'Image', 'ViewSpinner',
|
||||||
function ($scope, $log, Messages, Image, ViewSpinner) {
|
function ($scope, $state, Messages, Image, ViewSpinner) {
|
||||||
$scope.template = 'app/components/pullImage/pullImage.html';
|
$scope.template = 'app/components/pullImage/pullImage.html';
|
||||||
|
|
||||||
$scope.init = function () {
|
$scope.init = function () {
|
||||||
$scope.config = {
|
$scope.config = {
|
||||||
registry: '',
|
registry: '',
|
||||||
repo: '',
|
|
||||||
fromImage: '',
|
fromImage: '',
|
||||||
tag: 'latest'
|
tag: 'latest'
|
||||||
};
|
};
|
||||||
|
@ -22,7 +21,6 @@ angular.module('pullImage', [])
|
||||||
$('#error-message').hide();
|
$('#error-message').hide();
|
||||||
var config = angular.copy($scope.config);
|
var config = angular.copy($scope.config);
|
||||||
var imageName = (config.registry ? config.registry + '/' : '' ) +
|
var imageName = (config.registry ? config.registry + '/' : '' ) +
|
||||||
(config.repo ? config.repo + '/' : '') +
|
|
||||||
(config.fromImage) +
|
(config.fromImage) +
|
||||||
(config.tag ? ':' + config.tag : '');
|
(config.tag ? ':' + config.tag : '');
|
||||||
|
|
||||||
|
@ -41,10 +39,12 @@ angular.module('pullImage', [])
|
||||||
} else {
|
} else {
|
||||||
Messages.send("Image Added", imageName);
|
Messages.send("Image Added", imageName);
|
||||||
$scope.init();
|
$scope.init();
|
||||||
|
$state.go('images', {}, {reload: true});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Messages.send("Image Added", imageName);
|
Messages.send("Image Added", imageName);
|
||||||
$scope.init();
|
$scope.init();
|
||||||
|
$state.go('images', {}, {reload: true});
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
ViewSpinner.stop();
|
ViewSpinner.stop();
|
||||||
|
@ -53,4 +53,4 @@ angular.module('pullImage', [])
|
||||||
$('#error-message').show();
|
$('#error-message').show();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,11 +0,0 @@
|
||||||
angular.module('sidebar', [])
|
|
||||||
.controller('SideBarController', ['$scope', 'Container', 'Settings',
|
|
||||||
function ($scope, Container, Settings) {
|
|
||||||
$scope.template = 'partials/sidebar.html';
|
|
||||||
$scope.containers = [];
|
|
||||||
$scope.endpoint = Settings.endpoint;
|
|
||||||
|
|
||||||
Container.query({all: 0}, function (d) {
|
|
||||||
$scope.containers = d;
|
|
||||||
});
|
|
||||||
}]);
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('startContainer', ['ui.bootstrap'])
|
angular.module('startContainer', ['ui.bootstrap'])
|
||||||
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter',
|
.controller('StartContainerController', ['$scope', '$state', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter', 'ViewSpinner',
|
||||||
function ($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) {
|
function ($scope, $state, Container, Messages, containernameFilter, errorMsgFilter, ViewSpinner) {
|
||||||
$scope.template = 'app/components/startContainer/startcontainer.html';
|
$scope.template = 'app/components/startContainer/startcontainer.html';
|
||||||
|
|
||||||
Container.query({all: 1}, function (d) {
|
Container.query({all: 1}, function (d) {
|
||||||
|
@ -54,9 +54,10 @@ angular.module('startContainer', ['ui.bootstrap'])
|
||||||
|
|
||||||
$scope.create = function () {
|
$scope.create = function () {
|
||||||
// Copy the config before transforming fields to the remote API format
|
// Copy the config before transforming fields to the remote API format
|
||||||
var config = angular.copy($scope.config);
|
$('#create-modal').modal('hide');
|
||||||
|
ViewSpinner.spin();
|
||||||
|
|
||||||
config.Image = $routeParams.id;
|
var config = angular.copy($scope.config);
|
||||||
|
|
||||||
if (config.Cmd && config.Cmd[0] === "[") {
|
if (config.Cmd && config.Cmd[0] === "[") {
|
||||||
config.Cmd = angular.fromJson(config.Cmd);
|
config.Cmd = angular.fromJson(config.Cmd);
|
||||||
|
@ -121,7 +122,6 @@ angular.module('startContainer', ['ui.bootstrap'])
|
||||||
rmEmptyKeys(config);
|
rmEmptyKeys(config);
|
||||||
|
|
||||||
var ctor = Container;
|
var ctor = Container;
|
||||||
var loc = $location;
|
|
||||||
var s = $scope;
|
var s = $scope;
|
||||||
Container.create(config, function (d) {
|
Container.create(config, function (d) {
|
||||||
if (d.Id) {
|
if (d.Id) {
|
||||||
|
@ -129,22 +129,26 @@ angular.module('startContainer', ['ui.bootstrap'])
|
||||||
reqBody.id = d.Id;
|
reqBody.id = d.Id;
|
||||||
ctor.start(reqBody, function (cd) {
|
ctor.start(reqBody, function (cd) {
|
||||||
if (cd.id) {
|
if (cd.id) {
|
||||||
|
ViewSpinner.stop();
|
||||||
Messages.send('Container Started', d.Id);
|
Messages.send('Container Started', d.Id);
|
||||||
$('#create-modal').modal('hide');
|
$state.go('container', {id: d.Id}, {reload: true});
|
||||||
loc.path('/containers/' + d.Id + '/');
|
|
||||||
} else {
|
} else {
|
||||||
|
ViewSpinner.stop();
|
||||||
failedRequestHandler(cd, Messages);
|
failedRequestHandler(cd, Messages);
|
||||||
ctor.remove({id: d.Id}, function () {
|
ctor.remove({id: d.Id}, function () {
|
||||||
Messages.send('Container Removed', d.Id);
|
Messages.send('Container Removed', d.Id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
|
ViewSpinner.stop();
|
||||||
failedRequestHandler(e, Messages);
|
failedRequestHandler(e, Messages);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
ViewSpinner.stop();
|
||||||
failedRequestHandler(d, Messages);
|
failedRequestHandler(d, Messages);
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
|
ViewSpinner.stop();
|
||||||
failedRequestHandler(e, Messages);
|
failedRequestHandler(e, Messages);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -156,4 +160,4 @@ angular.module('startContainer', ['ui.bootstrap'])
|
||||||
var idx = array.indexOf(entry);
|
var idx = array.indexOf(entry);
|
||||||
array.splice(idx, 1);
|
array.splice(idx, 1);
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h3>Create And Start Container From Image</h3>
|
<h3>Start a new container</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
|
@ -12,6 +12,11 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Image:</label>
|
||||||
|
<input type="text" placeholder='ubuntu:latest'
|
||||||
|
ng-model="config.Image" class="form-control"/>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Cmd:</label>
|
<label>Cmd:</label>
|
||||||
<input type="text" placeholder='["/bin/echo", "Hello world"]'
|
<input type="text" placeholder='["/bin/echo", "Hello world"]'
|
||||||
|
@ -45,19 +50,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Volumes:</label>
|
<label>Volumes:</label>
|
||||||
|
|
||||||
<div ng-repeat="volume in config.Volumes">
|
<div ng-repeat="volume in config.Volumes">
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="volume.name" class="form-control"
|
<input type="text" ng-model="volume.name" class="form-control"
|
||||||
placeholder="/var/data"/>
|
placeholder="/var/data"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.Volumes, volume)">
|
||||||
ng-click="rmEntry(config.Volumes, volume)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.Volumes, {name: ''})">
|
||||||
ng-click="addEntry(config.Volumes, {name: ''})">Add Volume
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
@ -109,14 +113,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="opt.name" class="form-control"
|
<input type="text" ng-model="opt.name" class="form-control"
|
||||||
placeholder="label:type:svirt_apache"/>
|
placeholder="label:type:svirt_apache"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.SecurityOpts, opt)">
|
||||||
ng-click="rmEntry(config.SecurityOpts, opt)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.SecurityOpts, {name: ''})">
|
||||||
ng-click="addEntry(config.SecurityOpts, {name: ''})">Add Option
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -137,16 +141,15 @@
|
||||||
placeholder="value"/>
|
placeholder="value"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.Env, envar)">
|
||||||
ng-click="rmEntry(config.Env, envar)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.Env, {name: '', value: ''})">
|
||||||
ng-click="addEntry(config.Env, {name: '', value: ''})">Add environment
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
variable
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Labels:</label>
|
<label>Labels:</label>
|
||||||
|
@ -164,15 +167,15 @@
|
||||||
placeholder="value"/>
|
placeholder="value"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.Labels, label)">
|
||||||
ng-click="rmEntry(config.Labels, label)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.Labels, {key: '', value: ''})">
|
||||||
ng-click="addEntry(config.Labels, {key: '', value: ''})">Add Label
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</accordion-group>
|
</accordion-group>
|
||||||
|
@ -187,14 +190,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="bind.name" class="form-control"
|
<input type="text" ng-model="bind.name" class="form-control"
|
||||||
placeholder="/host:/container"/>
|
placeholder="/host:/container"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.Binds, bind)">
|
||||||
ng-click="rmEntry(config.HostConfig.Binds, bind)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.Binds, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.Binds, {name: ''})">Add Bind
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Links:</label>
|
<label>Links:</label>
|
||||||
|
@ -203,14 +206,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="link.name" class="form-control"
|
<input type="text" ng-model="link.name" class="form-control"
|
||||||
placeholder="web:db">
|
placeholder="web:db">
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.Links, link)">
|
||||||
ng-click="rmEntry(config.HostConfig.Links, link)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.Links, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.Links, {name: ''})">Add Link
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Dns:</label>
|
<label>Dns:</label>
|
||||||
|
@ -219,14 +222,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="entry.name" class="form-control"
|
<input type="text" ng-model="entry.name" class="form-control"
|
||||||
placeholder="8.8.8.8"/>
|
placeholder="8.8.8.8"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.Dns, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.Dns, entry)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.Dns, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.Dns, {name: ''})">Add entry
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>DnsSearch:</label>
|
<label>DnsSearch:</label>
|
||||||
|
@ -235,16 +238,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="entry.name" class="form-control"
|
<input type="text" ng-model="entry.name" class="form-control"
|
||||||
placeholder="example.com"/>
|
placeholder="example.com"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
Remove
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">Add
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
entry
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>CapAdd:</label>
|
<label>CapAdd:</label>
|
||||||
|
@ -253,14 +254,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="entry.name" class="form-control"
|
<input type="text" ng-model="entry.name" class="form-control"
|
||||||
placeholder="cap_sys_admin"/>
|
placeholder="cap_sys_admin"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.CapAdd, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.CapAdd, entry)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">Add entry
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>CapDrop:</label>
|
<label>CapDrop:</label>
|
||||||
|
@ -269,14 +270,14 @@
|
||||||
<div class="form-group form-inline">
|
<div class="form-group form-inline">
|
||||||
<input type="text" ng-model="entry.name" class="form-control"
|
<input type="text" ng-model="entry.name" class="form-control"
|
||||||
placeholder="cap_sys_admin"/>
|
placeholder="cap_sys_admin"/>
|
||||||
<button type="button" class="btn btn-danger btn-sm"
|
<a href="" ng-click="rmEntry(config.HostConfig.CapDrop, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.CapDrop, entry)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">Add entry
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
@ -304,16 +305,14 @@
|
||||||
ng-options="name for name in containerNames track by name"
|
ng-options="name for name in containerNames track by name"
|
||||||
class="form-control">
|
class="form-control">
|
||||||
</select>
|
</select>
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.HostConfig.VolumesFrom, volume)">
|
||||||
ng-click="rmEntry(config.HostConfig.VolumesFrom, volume)">
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
Remove
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.VolumesFrom, {name: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.VolumesFrom, {name: ''})">Add
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
volume
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -346,16 +345,15 @@
|
||||||
placeholder="127.0.0.1"/>
|
placeholder="127.0.0.1"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.HostConfig.ExtraHosts, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.ExtraHosts, entry)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.ExtraHosts, {host: '', ip: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.ExtraHosts, {host: '', ip: ''})">Add
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
extra host
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>LxcConf:</label>
|
<label>LxcConf:</label>
|
||||||
|
@ -373,16 +371,15 @@
|
||||||
placeholder="docker"/>
|
placeholder="docker"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.HostConfig.LxcConf, entry)">
|
||||||
ng-click="rmEntry(config.HostConfig.LxcConf, entry)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.LxcConf, {name: '', value: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.LxcConf, {name: '', value: ''})">Add
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
Entry
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Devices:</label>
|
<label>Devices:</label>
|
||||||
|
@ -398,15 +395,14 @@
|
||||||
<label class="sr-only">CgroupPermissions:</label>
|
<label class="sr-only">CgroupPermissions:</label>
|
||||||
<input type="text" ng-model="device.CgroupPermissions" class="form-control"
|
<input type="text" ng-model="device.CgroupPermissions" class="form-control"
|
||||||
placeholder="CgroupPermissions"/>
|
placeholder="CgroupPermissions"/>
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.HostConfig.Devices, device)">
|
||||||
ng-click="rmEntry(config.HostConfig.Devices, device)">Remove
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.Devices, { PathOnHost: '', PathInContainer: '', CgroupPermissions: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.Devices, { PathOnHost: '', PathInContainer: '', CgroupPermissions: ''})">
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
Add Device
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>PortBindings:</label>
|
<label>PortBindings:</label>
|
||||||
|
@ -426,16 +422,14 @@
|
||||||
<option value="">tcp</option>
|
<option value="">tcp</option>
|
||||||
<option value="udp">udp</option>
|
<option value="udp">udp</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="btn btn-danger btn-xs form-control"
|
<a href="" ng-click="rmEntry(config.HostConfig.PortBindings, portBinding)">
|
||||||
ng-click="rmEntry(config.HostConfig.PortBindings, portBinding)">
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
Remove
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
<a href="" ng-click="addEntry(config.HostConfig.PortBindings, {ip: '', extPort: '', intPort: ''})">
|
||||||
ng-click="addEntry(config.HostConfig.PortBindings, {ip: '', extPort: '', intPort: ''})">
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
Add Port Binding
|
</a>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</accordion-group>
|
</accordion-group>
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<h1>Stats for: {{ containerName }}</h1>
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
<h2>CPU</h2>
|
<div class="widget-icon grey pull-left">
|
||||||
|
<i class="fa fa-tasks"></i>
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<canvas id="cpu-stats-chart" width="650" height="300"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Memory</h2>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<canvas id="memory-stats-chart" width="650" height="300"></canvas>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-offset-1 col-sm-4">
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td>Max usage</td>
|
|
||||||
<td>{{ data.memory_stats.max_usage | humansize }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Limit</td>
|
|
||||||
<td>{{ data.memory_stats.limit | humansize }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Fail count</td>
|
|
||||||
<td>{{ data.memory_stats.failcnt }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<accordion>
|
|
||||||
<accordion-group heading="Other stats">
|
|
||||||
<table class="table">
|
|
||||||
<tr ng-repeat="(key, value) in data.memory_stats.stats">
|
|
||||||
<td>{{ key }}</td>
|
|
||||||
<td>{{ value }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</accordion-group>
|
|
||||||
</accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>Network {{ networkName}}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<canvas id="network-stats-chart" width="650" height="300"></canvas>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-offset-1 col-sm-4">
|
|
||||||
<div id="network-legend" style="margin-bottom: 20px;"></div>
|
|
||||||
<accordion>
|
|
||||||
<accordion-group heading="Other stats">
|
|
||||||
<table class="table">
|
|
||||||
<tr ng-repeat="(key, value) in data.network">
|
|
||||||
<td>{{ key }}</td>
|
|
||||||
<td>{{ value }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</accordion-group>
|
|
||||||
</accordion>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title">{{ containerName }}</div>
|
||||||
|
<div class="comment">
|
||||||
|
Name
|
||||||
</div>
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-area-chart" title="CPU usage"></rd-widget-header>
|
||||||
|
<rd-widget-body>
|
||||||
|
<canvas id="cpu-stats-chart" width="770" height="230"></canvas>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-area-chart" title="Memory usage"></rd-widget-header>
|
||||||
|
<rd-widget-body>
|
||||||
|
<canvas id="memory-stats-chart" width="770" height="230"></canvas>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-area-chart" title="Network usage"></rd-widget-header>
|
||||||
|
<rd-widget-body>
|
||||||
|
<canvas id="network-stats-chart" width="770" height="230"></canvas>
|
||||||
|
<div class="comment">
|
||||||
|
<div id="network-legend" style="margin-bottom: 20px;"></div>
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-tasks" title="Processes"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th ng-repeat="title in containerTop.Titles">{{title}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="processInfos in containerTop.Processes">
|
||||||
|
<td ng-repeat="processInfo in processInfos track by $index">{{processInfo}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
angular.module('stats', [])
|
angular.module('stats', [])
|
||||||
.controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', '$routeParams', 'humansizeFilter', '$sce', function (Settings, $scope, Messages, $timeout, Container, $routeParams, humansizeFilter, $sce) {
|
.controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', 'ContainerTop', '$stateParams', 'humansizeFilter', '$sce', '$document',
|
||||||
|
function (Settings, $scope, Messages, $timeout, Container, ContainerTop, $stateParams, humansizeFilter, $sce, $document) {
|
||||||
// TODO: Force scale to 0-100 for cpu, fix charts on dashboard,
|
// TODO: Force scale to 0-100 for cpu, fix charts on dashboard,
|
||||||
// TODO: Force memory scale to 0 - max memory
|
// TODO: Force memory scale to 0 - max memory
|
||||||
|
$scope.ps_args = '';
|
||||||
|
$scope.getTop = function () {
|
||||||
|
ContainerTop.get($stateParams.id, {
|
||||||
|
ps_args: $scope.ps_args
|
||||||
|
}, function (data) {
|
||||||
|
$scope.containerTop = data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$document.ready(function(){
|
||||||
var cpuLabels = [];
|
var cpuLabels = [];
|
||||||
var cpuData = [];
|
var cpuData = [];
|
||||||
var memoryLabels = [];
|
var memoryLabels = [];
|
||||||
|
@ -58,8 +67,10 @@ angular.module('stats', [])
|
||||||
{
|
{
|
||||||
//value: '',
|
//value: '',
|
||||||
color: 'rgba(255,180,174,0.5)',
|
color: 'rgba(255,180,174,0.5)',
|
||||||
title: 'Rx Data'
|
title: 'Tx Data'
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
|
|
||||||
legend($('#network-legend').get(0), networkLegendData);
|
legend($('#network-legend').get(0), networkLegendData);
|
||||||
|
|
||||||
Chart.defaults.global.animationSteps = 30; // Lower from 60 to ease CPU load.
|
Chart.defaults.global.animationSteps = 30; // Lower from 60 to ease CPU load.
|
||||||
|
@ -96,7 +107,7 @@ angular.module('stats', [])
|
||||||
$scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend());
|
$scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend());
|
||||||
|
|
||||||
function updateStats() {
|
function updateStats() {
|
||||||
Container.stats({id: $routeParams.id}, function (d) {
|
Container.stats({id: $stateParams.id}, function (d) {
|
||||||
var arr = Object.keys(d).map(function (key) {
|
var arr = Object.keys(d).map(function (key) {
|
||||||
return d[key];
|
return d[key];
|
||||||
});
|
});
|
||||||
|
@ -172,11 +183,12 @@ angular.module('stats', [])
|
||||||
}
|
}
|
||||||
return cpuPercent;
|
return cpuPercent;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Container.get({id: $routeParams.id}, function (d) {
|
Container.get({id: $stateParams.id}, function (d) {
|
||||||
$scope.containerName = d.Name.substring(1);
|
$scope.containerName = d.Name.substring(1);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
Messages.error("Failure", e.data);
|
Messages.error("Failure", e.data);
|
||||||
});
|
});
|
||||||
}])
|
$scope.getTop();
|
||||||
;
|
}]);
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon pull-left">
|
||||||
|
<i class="fa fa-code"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ docker.Version }}</div>
|
||||||
|
<div class="comment">Swarm version</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon pull-left">
|
||||||
|
<i class="fa fa-code"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ docker.ApiVersion }}</div>
|
||||||
|
<div class="comment">API version</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 col-xs-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body>
|
||||||
|
<div class="widget-icon pull-left">
|
||||||
|
<i class="fa fa-code"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{ docker.GoVersion }}</div>
|
||||||
|
<div class="comment">Go version</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-object-group" title="Cluster status"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Nodes</td>
|
||||||
|
<td>{{ swarm.Nodes }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Containers</td>
|
||||||
|
<td>{{ info.Containers }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Images</td>
|
||||||
|
<td>{{ info.Images }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Strategy</td>
|
||||||
|
<td>{{ swarm.Strategy }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CPUs</td>
|
||||||
|
<td>{{ info.NCPU }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Total Memory</td>
|
||||||
|
<td>{{ info.MemTotal|humansize }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Operating System</td>
|
||||||
|
<td>{{ info.OperatingSystem }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Kernel Version</td>
|
||||||
|
<td>{{ info.KernelVersion }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="fa-hdd-o" title="Nodes status"></rd-widget-header>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<a href="#/swarm/" ng-click="order('Name')">
|
||||||
|
Name
|
||||||
|
<span ng-show="sortType == 'Name' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||||
|
<span ng-show="sortType == 'Name' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="#/swarm/" ng-click="order('IP')">
|
||||||
|
IP
|
||||||
|
<span ng-show="sortType == 'IP' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||||
|
<span ng-show="sortType == 'IP' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="#/swarm/" ng-click="order('Containers')">
|
||||||
|
Containers
|
||||||
|
<span ng-show="sortType == 'Containers' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||||
|
<span ng-show="sortType == 'Containers' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href="#/swarm/" ng-click="order('Status')">
|
||||||
|
Status
|
||||||
|
<span ng-show="sortType == 'Status' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||||
|
<span ng-show="sortType == 'Status' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="node in (state.filteredNodes = (swarm.Status | filter:state.filter | orderBy:sortType:sortReverse))">
|
||||||
|
<td>{{ node.name }}</td>
|
||||||
|
<td>{{ node.ip }}</td>
|
||||||
|
<td>{{ node.containers }}</td>
|
||||||
|
<td><span class="label label-{{ node.status|statusbadge }}">{{ node.status }}</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,62 @@
|
||||||
|
angular.module('swarm', [])
|
||||||
|
.controller('SwarmController', ['$scope', 'Info', 'Version', 'Settings',
|
||||||
|
function ($scope, Info, Version, Settings) {
|
||||||
|
|
||||||
|
$scope.sortType = 'Name';
|
||||||
|
$scope.sortReverse = true;
|
||||||
|
$scope.info = {};
|
||||||
|
$scope.docker = {};
|
||||||
|
$scope.swarm = {};
|
||||||
|
|
||||||
|
$scope.order = function(sortType) {
|
||||||
|
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||||
|
$scope.sortType = sortType;
|
||||||
|
};
|
||||||
|
|
||||||
|
Version.get({}, function (d) {
|
||||||
|
$scope.docker = d;
|
||||||
|
});
|
||||||
|
Info.get({}, function (d) {
|
||||||
|
$scope.info = d;
|
||||||
|
extractSwarmInfo(d);
|
||||||
|
});
|
||||||
|
|
||||||
|
function extractSwarmInfo(info) {
|
||||||
|
// Swarm info is available in SystemStatus object
|
||||||
|
var systemStatus = info.SystemStatus;
|
||||||
|
// Swarm strategy
|
||||||
|
$scope.swarm[systemStatus[1][0]] = systemStatus[1][1];
|
||||||
|
// Swarm filters
|
||||||
|
$scope.swarm[systemStatus[2][0]] = systemStatus[2][1];
|
||||||
|
// Swarm node count
|
||||||
|
var node_count = parseInt(systemStatus[3][1], 10);
|
||||||
|
$scope.swarm[systemStatus[3][0]] = node_count;
|
||||||
|
|
||||||
|
$scope.swarm.Status = [];
|
||||||
|
extractNodesInfo(systemStatus, node_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractNodesInfo(info, node_count) {
|
||||||
|
// First information for node1 available at element #4 of SystemStatus
|
||||||
|
// The next 10 elements are information related to the node
|
||||||
|
var node_offset = 4;
|
||||||
|
for (i = 0; i < node_count; i++) {
|
||||||
|
extractNodeInfo(info, node_offset);
|
||||||
|
node_offset += 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractNodeInfo(info, offset) {
|
||||||
|
var node = {};
|
||||||
|
node.name = info[offset][0];
|
||||||
|
node.ip = info[offset][1];
|
||||||
|
node.status = info[offset + 1][1];
|
||||||
|
node.containers = info[offset + 2][1];
|
||||||
|
node.cpu = info[offset + 3][1];
|
||||||
|
node.memory = info[offset + 4][1];
|
||||||
|
node.labels = info[offset + 5][1];
|
||||||
|
node.error = info[offset + 6][1];
|
||||||
|
node.version = info[offset + 8][1];
|
||||||
|
$scope.swarm.Status.push(node);
|
||||||
|
}
|
||||||
|
}]);
|
|
@ -1,25 +1,26 @@
|
||||||
<h2>Volumes:</h2>
|
<div ng-include="template" ng-controller="CreateVolumeController"></div>
|
||||||
|
|
||||||
<div>
|
<div class="col-lg-12">
|
||||||
<ul class="nav nav-pills pull-left">
|
<rd-widget>
|
||||||
<li class="dropdown">
|
<rd-widget-header icon="fa-cubes" title="Volumes">
|
||||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b
|
</rd-widget-header>
|
||||||
class="caret"></b></a>
|
<rd-widget-taskbar classes="col-lg-12">
|
||||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
<div class="pull-left">
|
||||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
</ul>
|
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount">Remove</button>
|
||||||
</li>
|
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#create-volume-modal">Create new volume...</button>
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="pull-right form-inline">
|
|
||||||
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label
|
|
||||||
class="sr-only" for="filter">Filter</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped">
|
<div class="pull-right">
|
||||||
|
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||||
|
</div>
|
||||||
|
</rd-widget-taskbar>
|
||||||
|
<rd-widget-body classes="no-padding">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label><input type="checkbox" ng-model="toggle" ng-change="toggleSelectAll()"/> Select</label></th>
|
<th><label><input type="checkbox" ng-model="state.toggle" ng-change="toggleSelectAll()"/> Select</label></th>
|
||||||
<th>
|
<th>
|
||||||
<a href="#/volumes/" ng-click="order('Name')">
|
<a href="#/volumes/" ng-click="order('Name')">
|
||||||
Name
|
Name
|
||||||
|
@ -44,31 +45,15 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="volume in (filteredVolumes = (volumes | filter:filter | orderBy:sortType:sortReverse))">
|
<tr ng-repeat="volume in (state.filteredVolumes = (volumes | filter:state.filter | orderBy:sortType:sortReverse))">
|
||||||
<td><input type="checkbox" ng-model="volume.Checked"/></td>
|
<td><input type="checkbox" ng-model="volume.Checked" ng-change="selectItem(volume)"/></td>
|
||||||
<td>{{ volume.Name|truncate:20 }}</td>
|
<td>{{ volume.Name|truncate:20 }}</td>
|
||||||
<td>{{ volume.Driver }}</td>
|
<td>{{ volume.Driver }}</td>
|
||||||
<td>{{ volume.Mountpoint }}</td>
|
<td>{{ volume.Mountpoint }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-offset-3 col-xs-6">
|
|
||||||
<form role="form" class="">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" placeholder='tardis'
|
|
||||||
ng-model="createVolumeConfig.Name" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Driver:</label>
|
|
||||||
<input type="text" placeholder='local'
|
|
||||||
ng-model="createVolumeConfig.Driver" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-success btn-sm"
|
|
||||||
ng-click="addVolume(createVolumeConfig)">
|
|
||||||
Create Volume
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
<rd-widget>
|
||||||
</div>
|
</div>
|
|
@ -1,23 +1,35 @@
|
||||||
angular.module('volumes', []).config(['$routeProvider', function ($routeProvider) {
|
angular.module('volumes', [])
|
||||||
$routeProvider.when('/volumes/', {
|
.controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
|
||||||
templateUrl: 'app/components/volumes/volumes.html',
|
function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) {
|
||||||
controller: 'VolumesController'
|
$scope.state = {};
|
||||||
});
|
$scope.state.toggle = false;
|
||||||
}]).controller('VolumesController', ['$scope', 'Volume', 'ViewSpinner', 'Messages', '$route', 'errorMsgFilter',
|
$scope.state.selectedItemCount = 0;
|
||||||
function ($scope, Volume, ViewSpinner, Messages, $route, errorMsgFilter) {
|
|
||||||
$scope.sortType = 'Name';
|
$scope.sortType = 'Name';
|
||||||
$scope.sortReverse = true;
|
$scope.sortReverse = true;
|
||||||
$scope.toggle = false;
|
|
||||||
$scope.order = function(sortType) {
|
$scope.order = function(sortType) {
|
||||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||||
$scope.sortType = sortType;
|
$scope.sortType = sortType;
|
||||||
};
|
};
|
||||||
$scope.createVolumeConfig = {
|
|
||||||
"Name": "",
|
$scope.toggleSelectAll = function () {
|
||||||
"Driver": ""
|
angular.forEach($scope.state.filteredVolumes, function (i) {
|
||||||
|
i.Checked = $scope.state.toggle;
|
||||||
|
});
|
||||||
|
if ($scope.state.toggle) {
|
||||||
|
$scope.state.selectedItemCount = $scope.state.filteredVolumes.length;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.selectItem = function (item) {
|
||||||
|
if (item.Checked) {
|
||||||
|
$scope.state.selectedItemCount++;
|
||||||
|
} else {
|
||||||
|
$scope.state.selectedItemCount--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.removeAction = function () {
|
$scope.removeAction = function () {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
|
@ -44,28 +56,6 @@ angular.module('volumes', []).config(['$routeProvider', function ($routeProvider
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleSelectAll = function () {
|
|
||||||
angular.forEach($scope.filteredVolumes, function (i) {
|
|
||||||
i.Checked = $scope.toggle;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.addVolume = function addVolume(createVolumeConfig) {
|
|
||||||
ViewSpinner.spin();
|
|
||||||
Volume.create(createVolumeConfig, function (d) {
|
|
||||||
if (d.Name) {
|
|
||||||
Messages.send("Volume created", d.Name);
|
|
||||||
} else {
|
|
||||||
Messages.error('Failure', errorMsgFilter(d));
|
|
||||||
}
|
|
||||||
ViewSpinner.stop();
|
|
||||||
fetchVolumes();
|
|
||||||
}, function (e) {
|
|
||||||
Messages.error("Failure", e.data);
|
|
||||||
ViewSpinner.stop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function fetchVolumes() {
|
function fetchVolumes() {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Volume.query({}, function (d) {
|
Volume.query({}, function (d) {
|
||||||
|
@ -77,4 +67,4 @@ angular.module('volumes', []).config(['$routeProvider', function ($routeProvider
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fetchVolumes();
|
fetchVolumes();
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdLoading', function rdLoading() {
|
||||||
|
var directive = {
|
||||||
|
restrict: 'AE',
|
||||||
|
template: '<div class="loading"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdWidgetBody', function rdWidgetBody() {
|
||||||
|
var directive = {
|
||||||
|
requires: '^rdWidget',
|
||||||
|
scope: {
|
||||||
|
loading: '@?',
|
||||||
|
classes: '@?'
|
||||||
|
},
|
||||||
|
transclude: true,
|
||||||
|
template: '<div class="widget-body" ng-class="classes"><rd-loading ng-show="loading"></rd-loading><div ng-hide="loading" class="widget-content" ng-transclude></div></div>',
|
||||||
|
restrict: 'E'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdWidgetFooter', function rdWidgetFooter() {
|
||||||
|
var directive = {
|
||||||
|
requires: '^rdWidget',
|
||||||
|
transclude: true,
|
||||||
|
template: '<div class="widget-footer" ng-transclude></div>',
|
||||||
|
restrict: 'E'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdWidgetHeader', function rdWidgetTitle() {
|
||||||
|
var directive = {
|
||||||
|
requires: '^rdWidget',
|
||||||
|
scope: {
|
||||||
|
title: '@',
|
||||||
|
icon: '@'
|
||||||
|
},
|
||||||
|
transclude: true,
|
||||||
|
template: '<div class="widget-header"><div class="row"><div class="pull-left"><i class="fa" ng-class="icon"></i> {{title}} </div><div class="pull-right col-xs-6 col-sm-4" ng-transclude></div></div></div>',
|
||||||
|
restrict: 'E'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -0,0 +1,14 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdWidgetTaskbar', function rdWidgetTaskbar() {
|
||||||
|
var directive = {
|
||||||
|
requires: '^rdWidget',
|
||||||
|
scope: {
|
||||||
|
classes: '@?'
|
||||||
|
},
|
||||||
|
transclude: true,
|
||||||
|
template: '<div class="widget-header"><div class="row"><div ng-class="classes" ng-transclude></div></div></div>',
|
||||||
|
restrict: 'E'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
angular
|
||||||
|
.module('uifordocker')
|
||||||
|
.directive('rdWidget', function rdWidget() {
|
||||||
|
var directive = {
|
||||||
|
scope: {
|
||||||
|
"ngModel": "="
|
||||||
|
},
|
||||||
|
transclude: true,
|
||||||
|
template: '<div class="widget" ng-transclude></div>',
|
||||||
|
restrict: 'EA'
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
});
|
|
@ -23,12 +23,23 @@ angular.module('dockerui.filters', [])
|
||||||
return function (text) {
|
return function (text) {
|
||||||
if (text === 'Ghost') {
|
if (text === 'Ghost') {
|
||||||
return 'important';
|
return 'important';
|
||||||
|
} else if (text === 'Unhealthy') {
|
||||||
|
return 'danger';
|
||||||
} else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') {
|
} else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') {
|
||||||
return 'warning';
|
return 'warning';
|
||||||
}
|
}
|
||||||
return 'success';
|
return 'success';
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
.filter('trimcontainername', function () {
|
||||||
|
'use strict';
|
||||||
|
return function (name) {
|
||||||
|
if (name) {
|
||||||
|
return (name.indexOf('/') === 0 ? name.replace('/','') : name);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
})
|
||||||
.filter('getstatetext', function () {
|
.filter('getstatetext', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
return function (state) {
|
return function (state) {
|
||||||
|
|
|
@ -114,3 +114,28 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: inline;
|
||||||
|
width: 110px;
|
||||||
|
max-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.containerNameInput {
|
||||||
|
width: 85%;
|
||||||
|
border:none;
|
||||||
|
background:none;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.containerNameInput:active, .containerNameInput:focus {
|
||||||
|
outline:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network-legend {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network-legend span {
|
||||||
|
display: inline;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 183 KiB |
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 183 KiB |
Binary file not shown.
After Width: | Height: | Size: 180 KiB |
22
bower.json
22
bower.json
|
@ -23,20 +23,24 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Chart.js": "1.0.2",
|
"Chart.js": "1.0.2",
|
||||||
"angular": "1.3.15",
|
"angular": "~1.5.0",
|
||||||
"angular-sanitize": "1.3.15",
|
"angular-cookies": "~1.5.0",
|
||||||
"angular-bootstrap": "0.12.0",
|
"angular-bootstrap": "~1.0.3",
|
||||||
"angular-mocks": "1.3.15",
|
"angular-ui-router": "^0.2.15",
|
||||||
|
"angular-sanitize": "~1.5.0",
|
||||||
|
"angular-mocks": "~1.5.0",
|
||||||
"angular-oboe": "*",
|
"angular-oboe": "*",
|
||||||
"angular-resource": "1.3.15",
|
"angular-resource": "~1.5.0",
|
||||||
"angular-route": "1.3.15",
|
"angular-route": "~1.5.0",
|
||||||
"angular-visjs": "0.0.7",
|
"bootstrap": "~3.3.6",
|
||||||
"bootstrap": "3.3.0",
|
"font-awesome": "~4.5.0",
|
||||||
"jquery": "1.11.1",
|
"jquery": "1.11.1",
|
||||||
"jquery.gritter": "1.7.4",
|
"jquery.gritter": "1.7.4",
|
||||||
|
"lodash": "4.12.0",
|
||||||
|
"rdash-ui": "1.0.*",
|
||||||
"spin.js": "1.3"
|
"spin.js": "1.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"angular": "1.3.15"
|
"angular": "1.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
965f7ce53139cf9e75e5b8a8206a4af5791eb8f8 dockerui
|
|
@ -1,4 +1,4 @@
|
||||||
FROM nginx:1.9.9
|
FROM nginx:latest
|
||||||
|
|
||||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY users.htpasswd /etc/nginx/users.htpasswd
|
COPY users.htpasswd /etc/nginx/users.htpasswd
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
dockerui:
|
dockerui:
|
||||||
image: dockerui/dockerui
|
image: cloudinovasi/ui-for-docker
|
||||||
privileged: true
|
command: -e http://<SWARM_HOST>:<SWARM_PORT>
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
build: .
|
build: .
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
FROM debian
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y socat
|
|
|
@ -1,11 +0,0 @@
|
||||||
# UI For Docker with Swarm
|
|
||||||
|
|
||||||
This example works with swarm clusters created with docker-machine.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Make sure your client is pointed directly to the Docker daemon on the swarm-master's node (not through swarm).
|
|
||||||
|
|
||||||
```
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
|
@ -1,11 +0,0 @@
|
||||||
dockerui:
|
|
||||||
image: uifd/ui-for-docker
|
|
||||||
command: -e http://127.0.0.1:2375
|
|
||||||
net: "host"
|
|
||||||
|
|
||||||
socat:
|
|
||||||
build: .
|
|
||||||
net: "host"
|
|
||||||
command: socat -d -d TCP-L:2375,fork,bind=localhost ssl:127.0.0.1:3376,cert=/var/lib/boot2docker/server.pem,cafile=/var/lib/boot2docker/ca.pem,key=/var/lib/boot2docker/server-key.pem
|
|
||||||
volumes:
|
|
||||||
- /var/lib/boot2docker:/var/lib/boot2docker
|
|
39
gruntFile.js
39
gruntFile.js
|
@ -29,7 +29,7 @@ module.exports = function (grunt) {
|
||||||
'html2js',
|
'html2js',
|
||||||
'uglify',
|
'uglify',
|
||||||
'clean:tmpl',
|
'clean:tmpl',
|
||||||
//'jshint',
|
'jshint',
|
||||||
//'karma:unit',
|
//'karma:unit',
|
||||||
'concat:index',
|
'concat:index',
|
||||||
'recess:min',
|
'recess:min',
|
||||||
|
@ -37,8 +37,9 @@ module.exports = function (grunt) {
|
||||||
]);
|
]);
|
||||||
grunt.registerTask('test-watch', ['karma:watch']);
|
grunt.registerTask('test-watch', ['karma:watch']);
|
||||||
grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']);
|
grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']);
|
||||||
grunt.registerTask('runSwarm', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:runSwarm']);
|
grunt.registerTask('runSwarm', ['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('clear', ['clean:app']);
|
||||||
|
|
||||||
// Print a timestamp (useful for when watching)
|
// Print a timestamp (useful for when watching)
|
||||||
grunt.registerTask('timestamp', function () {
|
grunt.registerTask('timestamp', function () {
|
||||||
|
@ -68,8 +69,8 @@ module.exports = function (grunt) {
|
||||||
'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict"
|
'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict"
|
||||||
'bower_components/bootstrap/dist/js/bootstrap.js',
|
'bower_components/bootstrap/dist/js/bootstrap.js',
|
||||||
'bower_components/spin.js/spin.js',
|
'bower_components/spin.js/spin.js',
|
||||||
'bower_components/vis/dist/vis.js',
|
|
||||||
'bower_components/Chart.js/Chart.js',
|
'bower_components/Chart.js/Chart.js',
|
||||||
|
'bower_components/lodash/dist/lodash.js',
|
||||||
'bower_components/oboe/dist/oboe-browser.js',
|
'bower_components/oboe/dist/oboe-browser.js',
|
||||||
'assets/js/legend.js' // Not a bower package
|
'assets/js/legend.js' // Not a bower package
|
||||||
],
|
],
|
||||||
|
@ -81,7 +82,9 @@ module.exports = function (grunt) {
|
||||||
cssVendor: [
|
cssVendor: [
|
||||||
'bower_components/bootstrap/dist/css/bootstrap.css',
|
'bower_components/bootstrap/dist/css/bootstrap.css',
|
||||||
'bower_components/jquery.gritter/css/jquery.gritter.css',
|
'bower_components/jquery.gritter/css/jquery.gritter.css',
|
||||||
'bower_components/vis/dist/vis.css'
|
'bower_components/font-awesome/css/font-awesome.min.css',
|
||||||
|
'bower_components/rdash-ui/dist/css/rdash.css',
|
||||||
|
'bower_components/angular-ui-select/dist/select.css'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
clean: {
|
clean: {
|
||||||
|
@ -92,7 +95,9 @@ module.exports = function (grunt) {
|
||||||
copy: {
|
copy: {
|
||||||
assets: {
|
assets: {
|
||||||
files: [
|
files: [
|
||||||
{dest: '<%= distdir %>/fonts/', src: '**', expand: true, cwd: 'bower_components/bootstrap/fonts/'},
|
{dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/bootstrap/fonts/'},
|
||||||
|
{dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/font-awesome/fonts/'},
|
||||||
|
{dest: '<%= distdir %>/fonts/', src: '*.{ttf,woff,woff2,eof,svg}', expand: true, cwd: 'bower_components/rdash-ui/dist/fonts/'},
|
||||||
{
|
{
|
||||||
dest: '<%= distdir %>/images/',
|
dest: '<%= distdir %>/images/',
|
||||||
src: ['**', '!trees.jpg'],
|
src: ['**', '!trees.jpg'],
|
||||||
|
@ -100,18 +105,10 @@ module.exports = function (grunt) {
|
||||||
cwd: 'bower_components/jquery.gritter/images/'
|
cwd: 'bower_components/jquery.gritter/images/'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dest: '<%= distdir %>/img',
|
dest: '<%= distdir %>/images/',
|
||||||
src: [
|
src: ['**'],
|
||||||
'network/downArrow.png',
|
|
||||||
'network/leftArrow.png',
|
|
||||||
'network/upArrow.png',
|
|
||||||
'network/rightArrow.png',
|
|
||||||
'network/minus.png',
|
|
||||||
'network/plus.png',
|
|
||||||
'network/zoomExtends.png'
|
|
||||||
],
|
|
||||||
expand: true,
|
expand: true,
|
||||||
cwd: 'bower_components/vis/dist/img'
|
cwd: 'assets/images/'
|
||||||
},
|
},
|
||||||
{dest: '<%= distdir %>/ico', src: '**', expand: true, cwd: 'assets/ico'}
|
{dest: '<%= distdir %>/ico', src: '**', expand: true, cwd: 'assets/ico'}
|
||||||
]
|
]
|
||||||
|
@ -154,11 +151,13 @@ module.exports = function (grunt) {
|
||||||
angular: {
|
angular: {
|
||||||
src: ['bower_components/angular/angular.js',
|
src: ['bower_components/angular/angular.js',
|
||||||
'bower_components/angular-sanitize/angular-sanitize.js',
|
'bower_components/angular-sanitize/angular-sanitize.js',
|
||||||
|
'bower_components/angular-cookies/angular-cookies.js',
|
||||||
'bower_components/angular-route/angular-route.js',
|
'bower_components/angular-route/angular-route.js',
|
||||||
|
'bower_components/angular-ui-router/release/angular-ui-router.js',
|
||||||
'bower_components/angular-resource/angular-resource.js',
|
'bower_components/angular-resource/angular-resource.js',
|
||||||
'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
|
'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
|
||||||
'bower_components/angular-oboe/dist/angular-oboe.js',
|
'bower_components/angular-oboe/dist/angular-oboe.js',
|
||||||
'bower_components/angular-visjs/angular-vis.js'],
|
'bower_components/angular-ui-select/dist/select.js'],
|
||||||
dest: '<%= distdir %>/angular.js'
|
dest: '<%= distdir %>/angular.js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -222,6 +221,10 @@ module.exports = function (grunt) {
|
||||||
* Tried using a host volume with -v, copying files with `docker cp`, restating container, none worked
|
* Tried using a host volume with -v, copying files with `docker cp`, restating container, none worked
|
||||||
* Rebuilding image on each change was only method that worked, takes ~4s per change to update
|
* Rebuilding image on each change was only method that worked, takes ~4s per change to update
|
||||||
*/
|
*/
|
||||||
|
},
|
||||||
|
buildSwarm: {
|
||||||
|
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'],
|
||||||
|
tasks: ['build', 'shell:buildImage', 'shell:runSwarm', 'shell:cleanImages']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
jshint: {
|
jshint: {
|
||||||
|
@ -265,7 +268,7 @@ 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 --net=host -d --name ui-for-docker ui-for-docker -e http://127.0.0.1:2374'
|
'docker run --net=host -d --name ui-for-docker ui-for-docker -e http://10.0.7.11:4000'
|
||||||
].join(';')
|
].join(';')
|
||||||
},
|
},
|
||||||
cleanImages: {
|
cleanImages: {
|
||||||
|
|
69
index.html
69
index.html
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en" ng-app="<%= pkg.name %>">
|
<html lang="en" ng-app="<%= pkg.name %>">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>UI For Docker</title>
|
<title>CloudInovasi UI for Docker</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="<%= pkg.author %>">
|
<meta name="author" content="<%= pkg.author %>">
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
<link href="vendor.css" rel="stylesheet">
|
<link href="vendor.css" rel="stylesheet">
|
||||||
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
|
@ -25,17 +24,69 @@
|
||||||
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-precomposed.png">
|
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-precomposed.png">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body ng-controller="MasterCtrl">
|
||||||
|
<div id="page-wrapper" ng-class="{'open': toggle}" ng-cloak>
|
||||||
|
|
||||||
<div class="container">
|
<!-- Sidebar -->
|
||||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
<div id="sidebar-wrapper">
|
||||||
|
<ul class="sidebar">
|
||||||
|
<li class="sidebar-main">
|
||||||
|
<a ng-click="toggleSidebar()">
|
||||||
|
<img src="images/logo.png" class="img-responsive logo" alt="logo">
|
||||||
|
<span class="menu-icon glyphicon glyphicon-transfer"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-title"><span>NAVIGATION</span></li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#">Dashboard <span class="menu-icon fa fa-tachometer"></span></a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#/containers/">Containers <span class="menu-icon fa fa-server"></span></a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#/images/">Images <span class="menu-icon fa fa-clone"></span></a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#/networks/">Networks <span class="menu-icon fa fa-sitemap"></span></a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#/volumes/">Volumes <span class="menu-icon fa fa-cubes"></span></a>
|
||||||
|
</li>
|
||||||
|
<li class="sidebar-list">
|
||||||
|
<a href="#/swarm/">Swarm <span class="menu-icon fa fa-object-group"></span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="sidebar-footer">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<a href="https://github.com/cloud-inovasi/cloudinovasi-ui" target="_blank">CloudInovasi UI {{ uiVersion }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Sidebar -->
|
||||||
|
|
||||||
<div id="view" ng-view></div>
|
<div id="content-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
|
||||||
<div class="container-bottom"></div>
|
<!-- Header Bar -->
|
||||||
<div ng-include="template" ng-controller="FooterController"></div>
|
<div class="row header">
|
||||||
</div>
|
<div class="col-xs-12">
|
||||||
|
<div class="meta">
|
||||||
|
<div class="page">
|
||||||
|
Dashboard
|
||||||
|
</div>
|
||||||
|
<div class="breadcrumb-links">
|
||||||
|
Home / Dashboard
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Header Bar -->
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div id="view" ui-view></div>
|
||||||
|
|
||||||
|
</div><!-- End Page Content -->
|
||||||
|
</div><!-- End Content Wrapper -->
|
||||||
|
</div><!-- End Page Wrapper -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue