Compare commits

...

20 Commits

Author SHA1 Message Date
Anthony Lapenna 65cde27334 Merge branch 'release/1.15.3' 2017-11-26 10:08:04 +01:00
Anthony Lapenna 2275467bdc chore(version): bump version number 2017-11-26 10:07:59 +01:00
1138-4EB 688b15fb4b feat(about): add a new about view as well as a support header 2017-11-26 10:05:03 +01:00
Anthony Lapenna 3362ba0c8c fix(services): do not display exposed ports when published port is missing (#1440) 2017-11-25 10:30:40 +01:00
Anthony Lapenna 39cf4d75ff fix(container-creation): reset NetworkConfig when changing the network during container edition (#1431) 2017-11-23 16:02:40 +01:00
Duvel 13d8d38bf9 fix(service-details): fix an issue with invalid service restart policy (#1415) 2017-11-23 10:47:39 +01:00
1138-4EB e51246ee78 style(sidebar): prevent icon of active item moving on hover (#1422) 2017-11-23 09:59:29 +01:00
Anthony Lapenna 4ab580923f fix(templates): fix an issue preventing linuxserver.io templates to be displayed (#1426) 2017-11-22 22:16:53 +01:00
1138-4EB 547511c8aa feat(UX): change background color for selected items (#1414) 2017-11-20 17:46:01 +01:00
1138-4EB 8a101f67f6 style(container-details): change the grouping of buttons
* style(containers) make add container button responsive

* style(container) make action buttons responsive, group as in containers
2017-11-20 14:48:42 +01:00
Thomas Krzero 3ee2e20f8e feat(services): add the ability to specify a target for secrets (#1365) 2017-11-20 14:44:23 +01:00
Yassir Hannoun 6b9f3dad7a feat(UX): add an image autocomplete feature for services and containers (#1389) 2017-11-20 14:34:14 +01:00
1138-4EB a2d41e5316 feat(build-system): check that files listed in vendor.yml exist (#1398)
* chore(build-system) check that files listed in vendor.yml exist (#1410)

* fix(build-system) Chart.min.js duplicated in vendor.yml (#1410)
2017-11-20 10:09:11 +01:00
Thomas Kooi 3548f0db6f refactor(webapp): simplify isAdmin statement (#1388) 2017-11-14 08:54:35 +01:00
Anthony Lapenna 521cc3d6ab Merge tag '1.15.2' into develop
Release 1.15.2
2017-11-13 10:11:27 +01:00
Anthony Lapenna b044aa9a84 Merge branch 'release/1.15.2' 2017-11-13 10:11:14 +01:00
Anthony Lapenna d9262d4b7f chore(version): bump version number 2017-11-13 10:11:11 +01:00
Anthony Lapenna efc3154617 refactor(ux): rename deploymentInProgress variable (#1385) 2017-11-12 22:39:12 +01:00
Anthony Lapenna d68708add7 feat(ux): replace spinners (#1383) 2017-11-12 20:27:28 +01:00
Anthony Lapenna 9bef7cd69f Merge tag '1.15.1' into develop
Release 1.15.1
2017-11-08 08:29:09 +01:00
156 changed files with 1091 additions and 1220 deletions
+1
View File
@@ -127,6 +127,7 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL
if err == portainer.ErrSettingsNotFound {
settings := &portainer.Settings{
LogoURL: *flags.Logo,
DisplayDonationHeader: true,
DisplayExternalContributors: false,
AuthenticationMethod: portainer.AuthenticationInternal,
LDAPSettings: portainer.LDAPSettings{
+4
View File
@@ -46,6 +46,7 @@ func NewSettingsHandler(bouncer *security.RequestBouncer) *SettingsHandler {
type (
publicSettingsResponse struct {
LogoURL string `json:"LogoURL"`
DisplayDonationHeader bool `json:"DisplayDonationHeader"`
DisplayExternalContributors bool `json:"DisplayExternalContributors"`
AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod"`
AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"`
@@ -56,6 +57,7 @@ type (
TemplatesURL string `valid:"required"`
LogoURL string `valid:""`
BlackListedLabels []portainer.Pair `valid:""`
DisplayDonationHeader bool `valid:""`
DisplayExternalContributors bool `valid:""`
AuthenticationMethod int `valid:"required"`
LDAPSettings portainer.LDAPSettings `valid:""`
@@ -90,6 +92,7 @@ func (handler *SettingsHandler) handleGetPublicSettings(w http.ResponseWriter, r
publicSettings := &publicSettingsResponse{
LogoURL: settings.LogoURL,
DisplayDonationHeader: settings.DisplayDonationHeader,
DisplayExternalContributors: settings.DisplayExternalContributors,
AuthenticationMethod: settings.AuthenticationMethod,
AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers,
@@ -118,6 +121,7 @@ func (handler *SettingsHandler) handlePutSettings(w http.ResponseWriter, r *http
TemplatesURL: req.TemplatesURL,
LogoURL: req.LogoURL,
BlackListedLabels: req.BlackListedLabels,
DisplayDonationHeader: req.DisplayDonationHeader,
DisplayExternalContributors: req.DisplayExternalContributors,
LDAPSettings: req.LDAPSettings,
AllowBindMountsForRegularUsers: req.AllowBindMountsForRegularUsers,
+2 -1
View File
@@ -73,6 +73,7 @@ type (
TemplatesURL string `json:"TemplatesURL"`
LogoURL string `json:"LogoURL"`
BlackListedLabels []Pair `json:"BlackListedLabels"`
DisplayDonationHeader bool `json:"DisplayDonationHeader"`
DisplayExternalContributors bool `json:"DisplayExternalContributors"`
AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod"`
LDAPSettings LDAPSettings `json:"LDAPSettings"`
@@ -389,7 +390,7 @@ type (
const (
// APIVersion is the version number of the Portainer API.
APIVersion = "1.15.1"
APIVersion = "1.15.3"
// DBVersion is the version number of the Portainer database.
DBVersion = 6
// DefaultTemplatesURL represents the default URL for the templates definitions.
+14 -2
View File
@@ -56,7 +56,7 @@ info:
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).
version: "1.15.1"
version: "1.15.3"
title: "Portainer API"
contact:
email: "info@portainer.io"
@@ -1869,7 +1869,7 @@ definitions:
description: "Is analytics enabled"
Version:
type: "string"
example: "1.15.1"
example: "1.15.3"
description: "Portainer API version"
PublicSettingsInspectResponse:
type: "object"
@@ -1880,6 +1880,10 @@ definitions:
description: "URL to a logo that will be displayed on the login page as well\
\ as on top of the sidebar. Will use default Portainer logo when value is\
\ empty string"
DisplayDonationHeader:
type: "boolean"
example: true
description: "Whether to display or not the donation message in the header."\
DisplayExternalContributors:
type: "boolean"
example: false
@@ -1983,6 +1987,10 @@ definitions:
\ when querying containers"
items:
$ref: "#/definitions/Settings_BlackListedLabels"
DisplayDonationHeader:
type: "boolean"
example: true
description: "Whether to display or not the donation message in the header."
DisplayExternalContributors:
type: "boolean"
example: false
@@ -2398,6 +2406,10 @@ definitions:
\ when querying containers"
items:
$ref: "#/definitions/Settings_BlackListedLabels"
DisplayDonationHeader:
type: "boolean"
example: true
description: "Whether to display or not the donation message in the header."
DisplayExternalContributors:
type: "boolean"
example: false
+1
View File
@@ -9,6 +9,7 @@ angular.module('portainer', [
'LocalStorageModule',
'angular-jwt',
'angular-google-analytics',
'angular-loading-bar',
'portainer.templates',
'portainer.filters',
'portainer.rest',
+10 -1
View File
@@ -1,5 +1,5 @@
angular.module('portainer')
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', 'EndpointProvider', 'Notifications', 'Analytics', function ($rootScope, $state, Authentication, authManager, StateManager, EndpointProvider, Notifications, Analytics) {
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', 'EndpointProvider', 'Notifications', 'Analytics', 'cfpLoadingBar', function ($rootScope, $state, Authentication, authManager, StateManager, EndpointProvider, Notifications, Analytics, cfpLoadingBar) {
'use strict';
EndpointProvider.initialize();
@@ -18,6 +18,15 @@ angular.module('portainer')
});
$rootScope.$state = $state;
// Workaround to prevent the loading bar from going backward
// https://github.com/chieffancypants/angular-loading-bar/issues/273
var originalSet = cfpLoadingBar.set;
cfpLoadingBar.set = function overrideSet(n) {
if (n > cfpLoadingBar.status()) {
originalSet.apply(cfpLoadingBar, arguments);
}
};
}]);
+100
View File
@@ -0,0 +1,100 @@
<rd-header>
<rd-header-title title="About">
</rd-header-title>
<rd-header-content>
About Portainer
</rd-header-content>
</rd-header>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<b>Portainer</b> is a <a href="https://github.com/portainer/portainer/blob/develop/LICENSE" target="_blank" >free and open-source software</a> brought to you with <span class="menu-icon fa fa-heart red-icon "></span> by <a href="https://portainer.io/" target="_blank">portainer.io</a> and <a href="https://github.com/portainer/portainer/graphs/contributors" target="_blank">contributors.</a>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header title="Help support portainer" icon="fa-heartbeat"></rd-widget-header>
<rd-widget-body>
<div class="small" style="line-height:1.65;">
<p>
It is a community effort to make <b>Portainer</b> as feature-rich as simple it is to deploy and use. We need all the help we can get!
</p>
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Fund our work</u>
<ul>
<li>Become a <a href="https://www.patreon.com/Portainerio" target="_blank"><i class="fa fa-money" aria-hidden="true"></i> patron</a></li>
<li>Consider a <a href="https://portainer.io/support.html" target="_blank">paid support plan</a></li>
<li>Make a <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6" target="_blank"><i class="fa fa-paypal" aria-hidden="true"></i> donation</a></li>
</ul>
</p>
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Contribute</u>
<ul>
<li>Found a bug or got a feature idea? Let's talk about it on <a href="https://github.com/portainer/portainer/issues" target="_blank"><i class="fa fa-github" aria-hidden="true"></i> Github</a></li>
<li>Follow our <a href="https://portainer.readthedocs.io/en/latest/contribute.html" target="_blank">contribution guidelines</a> to build it locally and make a <a target="_blank" href="https://github.com/portainer/portainer/compare"><i class="fa fa-github" aria-hidden="true"></i> pull request</a></li>
</ul>
</p>
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Spread the word</u>
<ul>
<li>Talk to your friends and colleagues about how awesome Portainer is!</li>
<li>Follow us on <a href="https://twitter.com/portainerio" target="_blank"><i class="fa fa-twitter" aria-hidden="true"></i> Twitter</a></li>
</ul>
</p>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header title="Support and services" icon="fa-building-o"></rd-widget-header>
<rd-widget-body>
<div class="small" style="line-height:1.65;">
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Documentation</u>
<ul>
<li>Checkout our <a target="_blank" href="http://portainer.readthedocs.io"><i class="fa fa-book" aria-hidden="true"></i> online documentation</a></li>
<li>Be sure to have a look at our <a href="https://portainer.readthedocs.io/en/latest/faq.html" target="_blank">FAQ</a> and our list of <a href="https://github.com/portainer/portainer/issues" target="_blank">open issues</a></li>
</ul>
</p>
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Community support</u>
<ul>
<li>Join us on <a href="https://portainer.io/slack/" target="_blank"><i class="fa fa-slack" aria-hidden="true"></i> Slack</a></li>
<li>We're also on <a href="https://gitter.im/portainer/Lobby" target="_blank"><i class="fa fa-github-alt" aria-hidden="true"></i> Gitter</a></li>
</ul>
</p>
<p>
<i class="fa fa-chevron-circle-right" aria-hidden="true"></i> <u>Services</u>
<ul>
<li>Find out more about our <a href="https://portainer.io/support.html" target="_blank">consulting and commercial support plans</a></li>
<li>We also propose a fund-a-feature plan, reach out to us at <a target="_blank" href="mailto:info@portainer.io"><i class="fa fa-envelope-o" aria-hidden="true"></i> portainer.io</a></li>
</ul>
</p>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header title="Limitations" icon="fa-plug"></rd-widget-header>
<rd-widget-body>
<div class="small">
Portainer has full support for Docker >=1.10 and Docker Swarm >= 1.2.3, and partial support for Docker 1.9 (some features may not be available).
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="config({id: config.Id})" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="configs">Configs</a> &gt; <a ui-sref="config({id: config.Id})">{{ config.Name }}</a>
@@ -3,7 +3,6 @@ angular.module('config', [])
function ($scope, $transition$, $state, $document, ConfigService, Notifications, CodeMirrorService) {
$scope.removeConfig = function removeConfig(configId) {
$('#loadingViewSpinner').show();
ConfigService.remove(configId)
.then(function success(data) {
Notifications.success('Config successfully removed');
@@ -11,9 +10,6 @@ function ($scope, $transition$, $state, $document, ConfigService, Notifications,
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove config');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
@@ -27,7 +23,6 @@ function ($scope, $transition$, $state, $document, ConfigService, Notifications,
}
function initView() {
$('#loadingViewSpinner').show();
ConfigService.config($transition$.params().id)
.then(function success(data) {
$scope.config = data;
@@ -35,9 +30,6 @@ function ($scope, $transition$, $state, $document, ConfigService, Notifications,
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve config details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -2
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="configs" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Configs</rd-header-content>
</rd-header>
@@ -52,7 +51,7 @@
</th>
</thead>
<tbody>
<tr dir-paginate="config in (state.filteredConfigs = ( configs | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: pagination_count))">
<tr dir-paginate="config in (state.filteredConfigs = ( configs | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: pagination_count))" ng-class="{active: config.Checked}">
<td><input type="checkbox" ng-model="config.Checked" ng-change="selectItem(config)"/></td>
<td><a ui-sref="config({id: config.Id})">{{ config.Name }}</a></td>
<td>{{ config.CreatedAt | getisodate }}</td>
@@ -30,17 +30,8 @@ function ($scope, $stateParams, $state, ConfigService, Notifications, Pagination
};
$scope.removeAction = function () {
$('#loadingViewSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadingViewSpinner').hide();
}
};
angular.forEach($scope.configs, function (config) {
if (config.Checked) {
counter = counter + 1;
ConfigService.remove(config.Id)
.then(function success() {
Notifications.success('Config deleted', config.Id);
@@ -49,16 +40,12 @@ function ($scope, $stateParams, $state, ConfigService, Notifications, Pagination
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove config');
})
.finally(function final() {
complete();
});
}
});
};
function initView() {
$('#loadingViewSpinner').show();
ConfigService.configs()
.then(function success(data) {
$scope.configs = data;
@@ -66,9 +53,6 @@ function ($scope, $stateParams, $state, ConfigService, Notifications, Pagination
.catch(function error(err) {
$scope.configs = [];
Notifications.error('Failure', err, 'Unable to retrieve configs');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+316 -310
View File
@@ -1,330 +1,336 @@
<rd-header>
<rd-header-title title="Container details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="containers">Containers</a> &gt; <a ui-sref="container({id: container.Id})">{{ container.Name|trimcontainername }}</a>
</rd-header-content>
</rd-header>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-cogs" title="Actions"></rd-widget-header>
<rd-widget-body classes="padding">
<div class="btn-group" role="group" aria-label="...">
<button class="btn btn-success" ng-click="start()" ng-disabled="container.State.Running"><i class="fa fa-play space-right" aria-hidden="true"></i>Start</button>
<button class="btn btn-danger" ng-click="stop()" ng-disabled="!container.State.Running"><i class="fa fa-stop space-right" aria-hidden="true"></i>Stop</button>
<button class="btn btn-danger" ng-click="kill()" ng-disabled="!container.State.Running"><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button>
<button class="btn btn-primary" ng-click="restart()" ng-disabled="!container.State.Running"><i class="fa fa-refresh space-right" aria-hidden="true"></i>Restart</button>
<button class="btn btn-primary" ng-click="pause()" ng-disabled="!container.State.Running || container.State.Paused"><i class="fa fa-pause space-right" aria-hidden="true"></i>Pause</button>
<button class="btn btn-primary" ng-click="unpause()" ng-disabled="!container.State.Paused"><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button>
<button class="btn btn-danger" ng-click="confirmRemove()"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button>
<button class="btn btn-danger" ng-click="recreate()" ng-if="!container.Config.Labels['com.docker.swarm.service.id']"><i class="fa fa-refresh space-right" aria-hidden="true"></i>Recreate</button>
<button class="btn btn-primary" ng-click="duplicate()" ng-if="!container.Config.Labels['com.docker.swarm.service.id']"><i class="fa fa-files-o space-right" aria-hidden="true"></i>Duplicate/Edit</button>
</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-header icon="fa-server" title="Container status"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>ID</td>
<td>{{ container.Id }}</td>
</tr>
<tr>
<td>Name</td>
<td ng-if="!container.edit">
{{ container.Name|trimcontainername }}
<a href="" data-toggle="tooltip" title="Edit container name" ng-click="container.edit = true;"><i class="fa fa-edit"></i></a>
</td>
<td ng-if="container.edit">
<form ng-submit="renameContainer()">
<input type="text" class="containerNameInput" ng-model="container.newContainerName">
<a href="" ng-click="container.edit = false;"><i class="fa fa-times"></i></a>
<a href="" ng-click="renameContainer()"><i class="fa fa-check-square-o"></i></a>
</form>
</td>
</tr>
<tr ng-if="container.NetworkSettings.IPAddress">
<td>IP address</td>
<td>{{ container.NetworkSettings.IPAddress }}</td>
</tr>
<tr>
<td>Status</td>
<td>
<i class="fa fa-heartbeat space-right green-icon" ng-if="container.State.Running"></i>
<i class="fa fa-heartbeat space-right red-icon" ng-if="!container.State.Running && container.State.Status !== 'created'"></i>
{{ container.State|getstatetext }} since {{ activityTime }}<span ng-if="!container.State.Running && container.State.Status !== 'created'"> with exit code {{ container.State.ExitCode }}</span>
</td>
</tr>
<tr>
<td>Created</td>
<td>{{ container.Created|getisodate }}</td>
</tr>
<tr ng-if="container.State.Running">
<td>Start time</td>
<td>{{ container.State.StartedAt|getisodate }}</td>
</tr>
<tr ng-if="!container.State.Running && container.State.Status !== 'created'">
<td>Finished</td>
<td>{{ container.State.FinishedAt|getisodate }}</td>
</tr>
<tr>
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a class="btn btn-outline-secondary" type="button" ui-sref="stats({id: container.Id})"><i class="fa fa-area-chart space-right" aria-hidden="true"></i>Stats</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="containerlogs({id: container.Id})"><i class="fa fa-exclamation-circle space-right" aria-hidden="true"></i>Logs</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="console({id: container.Id})"><i class="fa fa-terminal space-right" aria-hidden="true"></i>Console</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="inspect({id: container.Id})"><i class="fa fa-info-circle space-right" aria-hidden="true"></i>Inspect</a>
</div>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="container && applicationState.application.authentication"
resource-id="container.Id"
resource-control="container.ResourceControl"
resource-type="'container'">
</por-access-control-panel>
<!-- !access-control-panel -->
<div ng-if="container.State.Health" class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-server" title="Container health"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Status</td>
<td>
<i ng-class="{'healthy': 'fa fa-heartbeat space-right green-icon', 'unhealthy': 'fa fa-heartbeat space-right red-icon', 'starting': 'fa fa-heartbeat space-right orange-icon'}[container.State.Health.Status]"></i>
{{ container.State.Health.Status }}
</td>
</tr>
<tr>
<td>Failure count</td>
<td>{{ container.State.Health.FailingStreak }}</td>
</tr>
<tr>
<td>Last output</td>
<td>{{ container.State.Health.Log[container.State.Health.Log.length - 1].Output }}</td>
</tr>
</tbody>
</table>
</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-header icon="fa-clone" title="Create image"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<!-- tag-description -->
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">
You can create an image from this container, this allows you to backup important data or save
helpful configurations. You'll be able to spin up another container based on this image afterward.
</span>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-cogs" title="Actions"></rd-widget-header>
<rd-widget-body classes="padding">
<div class="btn-group" role="group" aria-label="...">
<button class="btn btn-success btn-responsive" ng-click="start()" ng-disabled="container.State.Running"><i class="fa fa-play space-right" aria-hidden="true"></i>Start</button>
<button class="btn btn-danger btn-responsive" ng-click="stop()" ng-disabled="!container.State.Running"><i class="fa fa-stop space-right" aria-hidden="true"></i>Stop</button>
<button class="btn btn-danger btn-responsive" ng-click="kill()" ng-disabled="!container.State.Running"><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button>
<button class="btn btn-primary btn-responsive" ng-click="restart()" ng-disabled="!container.State.Running"><i class="fa fa-refresh space-right" aria-hidden="true"></i>Restart</button>
<button class="btn btn-primary btn-responsive" ng-click="pause()" ng-disabled="!container.State.Running || container.State.Paused"><i class="fa fa-pause space-right" aria-hidden="true"></i>Pause</button>
<button class="btn btn-primary btn-responsive" ng-click="unpause()" ng-disabled="!container.State.Paused"><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button>
<button class="btn btn-danger btn-responsive" ng-click="confirmRemove()"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button>
</div>
<!-- !tag-description -->
<!-- image-and-registry -->
<div class="form-group">
<por-image-registry image="config.Image" registry="config.Registry"></por-image-registry>
<div class="btn-group" role="group" aria-label="...">
<button class="btn btn-danger btn-responsive" ng-click="recreate()" ng-if="!container.Config.Labels['com.docker.swarm.service.id']"><i class="fa fa-refresh space-right" aria-hidden="true"></i>Recreate</button>
<button class="btn btn-primary btn-responsive" ng-click="duplicate()" ng-if="!container.Config.Labels['com.docker.swarm.service.id']"><i class="fa fa-files-o space-right" aria-hidden="true"></i>Duplicate/Edit</button>
</div>
<!-- !image-and-registry -->
<!-- tag-note -->
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">Note: if you don't specify the tag in the image name, <span class="label label-default">latest</span> will be used.</span>
</div>
</div>
<!-- !tag-note -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Image" ng-click="commit()">Create</button>
<i id="createImageSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-server" title="Container details"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Image</td>
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
</tr>
<tr ng-if="portBindings.length > 0">
<td>Port configuration</td>
<td>
<div ng-repeat="portMapping in portBindings">
{{ portMapping.container }} <i class="fa fa-long-arrow-right"></i> {{ portMapping.host }}
</div>
</td>
</tr>
<tr>
<td>CMD</td>
<td><code>{{ container.Config.Cmd|command }}</code></td>
</tr>
<tr>
<td>ENV</td>
<td>
<table class="table table-bordered table-condensed">
<tr ng-repeat="var in container.Config.Env track by $index">
<td>{{ var|key: '=' }}</td>
<td>{{ var|value: '=' }}</td>
</tr>
</table>
</td>
</tr>
<tr ng-if="!(container.Config.Labels | emptyobject)">
<td>Labels</td>
<td>
<table class="table table-bordered table-condensed">
<tr ng-repeat="(k, v) in container.Config.Labels">
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
</table>
</td>
</tr>
<tr ng-if="container.HostConfig.RestartPolicy.Name !== 'no'">
<td>Restart policies</td>
<td>
<table class="table table-bordered table-condensed">
<tr>
<td class="col-md-3">Name</td>
<td>{{ container.HostConfig.RestartPolicy.Name }}</td>
</tr>
<tr>
<td class="col-md-3">MaximumRetryCount</td>
<td>
{{ container.HostConfig.RestartPolicy.MaximumRetryCount }}
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-server" title="Container status"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>ID</td>
<td>{{ container.Id }}</td>
</tr>
<tr>
<td>Name</td>
<td ng-if="!container.edit">
{{ container.Name|trimcontainername }}
<a href="" data-toggle="tooltip" title="Edit container name" ng-click="container.edit = true;"><i class="fa fa-edit"></i></a>
</td>
<td ng-if="container.edit">
<form ng-submit="renameContainer()">
<input type="text" class="containerNameInput" ng-model="container.newContainerName">
<a href="" ng-click="container.edit = false;"><i class="fa fa-times"></i></a>
<a href="" ng-click="renameContainer()"><i class="fa fa-check-square-o"></i></a>
</form>
</td>
</tr>
<tr ng-if="container.NetworkSettings.IPAddress">
<td>IP address</td>
<td>{{ container.NetworkSettings.IPAddress }}</td>
</tr>
<tr>
<td>Status</td>
<td>
<i class="fa fa-heartbeat space-right green-icon" ng-if="container.State.Running"></i>
<i class="fa fa-heartbeat space-right red-icon" ng-if="!container.State.Running && container.State.Status !== 'created'"></i>
{{ container.State|getstatetext }} since {{ activityTime }}<span ng-if="!container.State.Running && container.State.Status !== 'created'"> with exit code {{ container.State.ExitCode }}</span>
</td>
</tr>
<tr>
<td>Created</td>
<td>{{ container.Created|getisodate }}</td>
</tr>
<tr ng-if="container.State.Running">
<td>Start time</td>
<td>{{ container.State.StartedAt|getisodate }}</td>
</tr>
<tr ng-if="!container.State.Running && container.State.Status !== 'created'">
<td>Finished</td>
<td>{{ container.State.FinishedAt|getisodate }}</td>
</tr>
<tr>
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a class="btn btn-outline-secondary" type="button" ui-sref="stats({id: container.Id})"><i class="fa fa-area-chart space-right" aria-hidden="true"></i>Stats</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="containerlogs({id: container.Id})"><i class="fa fa-exclamation-circle space-right" aria-hidden="true"></i>Logs</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="console({id: container.Id})"><i class="fa fa-terminal space-right" aria-hidden="true"></i>Console</a>
<a class="btn btn-outline-secondary" type="button" ui-sref="inspect({id: container.Id})"><i class="fa fa-info-circle space-right" aria-hidden="true"></i>Inspect</a>
</div>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>
<div class="row" ng-if="container.Mounts.length > 0">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-cubes" title="Volumes"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<thead>
<tr>
<th>Host/volume</th>
<th>Path in container</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="vol in container.Mounts">
<td ng-if="vol.Type === 'bind'">{{ vol.Source }}</td>
<td ng-if="vol.Type === 'volume'"><a ui-sref="volume({id: vol.Name})">{{ vol.Name }}</a></td>
<td>{{ vol.Destination }}</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="container && applicationState.application.authentication"
resource-id="container.Id"
resource-control="container.ResourceControl"
resource-type="'container'">
</por-access-control-panel>
<!-- !access-control-panel -->
<div ng-if="container.State.Health" class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-server" title="Container health"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Status</td>
<td>
<i ng-class="{'healthy': 'fa fa-heartbeat space-right green-icon', 'unhealthy': 'fa fa-heartbeat space-right red-icon', 'starting': 'fa fa-heartbeat space-right orange-icon'}[container.State.Health.Status]"></i>
{{ container.State.Health.Status }}
</td>
</tr>
<tr>
<td>Failure count</td>
<td>{{ container.State.Health.FailingStreak }}</td>
</tr>
<tr>
<td>Last output</td>
<td>{{ container.State.Health.Log[container.State.Health.Log.length - 1].Output }}</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-sitemap" title="Connected networks">
<div class="pull-right">
Items per page:
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
</rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<thead>
<th>Network Name</th>
<th>IP Address</th>
<th>Gateway</th>
<th>MacAddress</th>
<th>Actions</th>
</thead>
<tbody>
<tr dir-paginate="(key, value) in container.NetworkSettings.Networks | itemsPerPage: state.pagination_count">
<td><a ui-sref="network({id: value.NetworkID})">{{ key }}</a></td>
<td>{{ value.IPAddress || '-' }}</td>
<td>{{ value.Gateway || '-' }}</td>
<td>{{ value.MacAddress || '-' }}</td>
<td>
<button type="button" class="btn btn-xs btn-danger" ng-click="containerLeaveNetwork(container, value.NetworkID)"><i class="fa fa-trash space-right" aria-hidden="true"></i>Leave Network</button>
</td>
</tr>
<tr ng-if="(container.NetworkSettings.Networks | emptyobject)">
<td colspan="5" class="text-center text-muted">No networks connected.</td>
</tr>
</tbody>
</table>
<div class="pagination-controls">
<dir-pagination-controls></dir-pagination-controls>
</div>
<hr />
<form class="form-horizontal">
<!-- network-input -->
<div class="row">
<label for="container_network" class="col-sm-3 col-lg-2 control-label text-left">Join a Network</label>
<div class="col-sm-5 col-lg-4">
<select class="form-control" ng-model="selectedNetwork" id="container_network">
<option selected disabled hidden value="">Select a network</option>
<option ng-repeat="net in availableNetworks" ng-value="net.Id">{{ net.Name }}</option>
</select>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-clone" title="Create image"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<!-- tag-description -->
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">
You can create an image from this container, this allows you to backup important data or save
helpful configurations. You'll be able to spin up another container based on this image afterward.
</span>
</div>
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!selectedNetwork" ng-click="containerJoinNetwork(container, selectedNetwork)">Join Network</button>
<!-- !tag-description -->
<!-- image-and-registry -->
<div class="form-group">
<por-image-registry image="config.Image" registry="config.Registry"></por-image-registry>
</div>
<!-- !image-and-registry -->
<!-- tag-note -->
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">Note: if you don't specify the tag in the image name, <span class="label label-default">latest</span> will be used.</span>
</div>
</div>
<!-- !tag-note -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Image" ng-click="commit()">Create</button>
</div>
</div>
</form>
</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-header icon="fa-server" title="Container details"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Image</td>
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
</tr>
<tr ng-if="portBindings.length > 0">
<td>Port configuration</td>
<td>
<div ng-repeat="portMapping in portBindings">
{{ portMapping.container }} <i class="fa fa-long-arrow-right"></i> {{ portMapping.host }}
</div>
</td>
</tr>
<tr>
<td>CMD</td>
<td><code>{{ container.Config.Cmd|command }}</code></td>
</tr>
<tr>
<td>ENV</td>
<td>
<table class="table table-bordered table-condensed">
<tr ng-repeat="var in container.Config.Env track by $index">
<td>{{ var|key: '=' }}</td>
<td>{{ var|value: '=' }}</td>
</tr>
</table>
</td>
</tr>
<tr ng-if="!(container.Config.Labels | emptyobject)">
<td>Labels</td>
<td>
<table class="table table-bordered table-condensed">
<tr ng-repeat="(k, v) in container.Config.Labels">
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
</table>
</td>
</tr>
<tr ng-if="container.HostConfig.RestartPolicy.Name !== 'no'">
<td>Restart policies</td>
<td>
<table class="table table-bordered table-condensed">
<tr>
<td class="col-md-3">Name</td>
<td>{{ container.HostConfig.RestartPolicy.Name }}</td>
</tr>
<tr>
<td class="col-md-3">MaximumRetryCount</td>
<td>
{{ container.HostConfig.RestartPolicy.MaximumRetryCount }}
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row" ng-if="container.Mounts.length > 0">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-cubes" title="Volumes"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<thead>
<tr>
<th>Host/volume</th>
<th>Path in container</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="vol in container.Mounts">
<td ng-if="vol.Type === 'bind'">{{ vol.Source }}</td>
<td ng-if="vol.Type === 'volume'"><a ui-sref="volume({id: vol.Name})">{{ vol.Name }}</a></td>
<td>{{ vol.Destination }}</td>
</tr>
</tbody>
</table>
</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-header icon="fa-sitemap" title="Connected networks">
<div class="pull-right">
Items per page:
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
</form>
</rd-widget-body>
</rd-widget>
</rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<thead>
<th>Network Name</th>
<th>IP Address</th>
<th>Gateway</th>
<th>MacAddress</th>
<th>Actions</th>
</thead>
<tbody>
<tr dir-paginate="(key, value) in container.NetworkSettings.Networks | itemsPerPage: state.pagination_count">
<td><a ui-sref="network({id: value.NetworkID})">{{ key }}</a></td>
<td>{{ value.IPAddress || '-' }}</td>
<td>{{ value.Gateway || '-' }}</td>
<td>{{ value.MacAddress || '-' }}</td>
<td>
<button type="button" class="btn btn-xs btn-danger" ng-disabled="state.leaveNetworkInProgress" button-spinner="state.leaveNetworkInProgress" ng-click="containerLeaveNetwork(container, value.NetworkID)">
<span ng-hide="state.leaveNetworkInProgress"><i class="fa fa-trash space-right" aria-hidden="true"></i> Leave network</span>
<span ng-show="state.leaveNetworkInProgress">Leaving network...</span>
</button>
</td>
</tr>
<tr ng-if="(container.NetworkSettings.Networks | emptyobject)">
<td colspan="5" class="text-center text-muted">No networks connected.</td>
</tr>
</tbody>
</table>
<div class="pagination-controls">
<dir-pagination-controls></dir-pagination-controls>
</div>
<hr />
<form class="form-horizontal">
<!-- network-input -->
<div class="row">
<label for="container_network" class="col-sm-3 col-lg-2 control-label text-left">Join a Network</label>
<div class="col-sm-5 col-lg-4">
<select class="form-control" ng-model="selectedNetwork" id="container_network">
<option selected disabled hidden value="">Select a network</option>
<option ng-repeat="net in availableNetworks" ng-value="net.Id">{{ net.Name }}</option>
</select>
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.joinNetworkInProgress || !selectedNetwork" ng-click="containerJoinNetwork(container, selectedNetwork)" button-spinner="state.joinNetworkInProgress">
<span ng-hide="state.joinNetworkInProgress">Join network</span>
<span ng-show="state.joinNetworkInProgress">Joining network...</span>
</button>
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>
+11 -30
View File
@@ -7,15 +7,17 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
Image: '',
Registry: ''
};
$scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('container_networks');
$scope.state = {
joinNetworkInProgress: false,
leaveNetworkInProgress: false,
pagination_count: Pagination.getPaginationCount('container_networks')
};
$scope.changePaginationCount = function() {
Pagination.setPaginationCount('container_networks', $scope.state.pagination_count);
};
var update = function () {
$('#loadingViewSpinner').show();
Container.get({id: $transition$.params().id}, function (d) {
var container = new ContainerDetailsViewModel(d);
$scope.container = container;
@@ -41,15 +43,12 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
}
});
}
$('#loadingViewSpinner').hide();
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to retrieve container info');
});
};
$scope.start = function () {
$('#loadingViewSpinner').show();
Container.start({id: $scope.container.Id}, {}, function (d) {
update();
Notifications.success('Container started', $transition$.params().id);
@@ -60,7 +59,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.stop = function () {
$('#loadingViewSpinner').show();
Container.stop({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container stopped', $transition$.params().id);
@@ -71,7 +69,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.kill = function () {
$('#loadingViewSpinner').show();
Container.kill({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container killed', $transition$.params().id);
@@ -82,23 +79,19 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.commit = function () {
$('#createImageSpinner').show();
var image = $scope.config.Image;
var registry = $scope.config.Registry;
var imageConfig = ImageHelper.createImageConfigForCommit(image, registry.URL);
ContainerCommit.commit({id: $transition$.params().id, tag: imageConfig.tag, repo: imageConfig.repo}, function (d) {
$('#createImageSpinner').hide();
update();
Notifications.success('Container commited', $transition$.params().id);
}, function (e) {
$('#createImageSpinner').hide();
update();
Notifications.error('Failure', e, 'Unable to commit container');
});
};
$scope.pause = function () {
$('#loadingViewSpinner').show();
Container.pause({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container paused', $transition$.params().id);
@@ -109,7 +102,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.unpause = function () {
$('#loadingViewSpinner').show();
Container.unpause({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container unpaused', $transition$.params().id);
@@ -138,7 +130,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.remove = function(cleanAssociatedVolumes) {
$('#loadingViewSpinner').show();
ContainerService.remove($scope.container, cleanAssociatedVolumes)
.then(function success() {
Notifications.success('Container successfully removed');
@@ -146,14 +137,10 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove container');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.restart = function () {
$('#loadingViewSpinner').show();
Container.restart({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container restarted', $transition$.params().id);
@@ -180,19 +167,18 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.containerLeaveNetwork = function containerLeaveNetwork(container, networkId) {
$('#loadingViewSpinner').show();
$scope.state.leaveNetworkInProgress = true;
Network.disconnect({id: networkId}, { Container: $transition$.params().id, Force: false }, function (d) {
if (container.message) {
$('#loadingViewSpinner').hide();
Notifications.error('Error', d, 'Unable to disconnect container from network');
} else {
$('#loadingViewSpinner').hide();
Notifications.success('Container left network', $transition$.params().id);
$state.go('container', {id: $transition$.params().id}, {reload: true});
}
$scope.state.leaveNetworkInProgress = false;
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to disconnect container from network');
$scope.state.leaveNetworkInProgress = false;
});
};
@@ -219,7 +205,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
function recreateContainer(pullImage) {
$('#loadingViewSpinner').show();
var container = $scope.container;
var config = ContainerHelper.configFromContainer(container.Model);
ContainerService.remove(container, true)
@@ -254,9 +239,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to re-create container');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -272,19 +254,18 @@ function ($q, $scope, $state, $transition$, $filter, Container, ContainerCommit,
};
$scope.containerJoinNetwork = function containerJoinNetwork(container, networkId) {
$('#joinNetworkSpinner').show();
$scope.state.joinNetworkInProgress = true;
Network.connect({id: networkId}, { Container: $transition$.params().id }, function (d) {
if (container.message) {
$('#joinNetworkSpinner').hide();
Notifications.error('Error', d, 'Unable to connect container to network');
} else {
$('#joinNetworkSpinner').hide();
Notifications.success('Container joined network', $transition$.params().id);
$state.go('container', {id: $transition$.params().id}, {reload: true});
}
$scope.state.joinNetworkInProgress = false;
}, function (e) {
$('#joinNetworkSpinner').hide();
Notifications.error('Failure', e, 'Unable to connect container to network');
$scope.state.joinNetworkInProgress = false;
});
};
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Container console">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Container console"></rd-header-title>
<rd-header-content ng-if="state.loaded">
<a ui-sref="containers">Containers</a> &gt; <a ui-sref="container({id: container.Id})">{{ container.Name|trimcontainername }}</a> &gt; Console
</rd-header-content>
@@ -10,11 +8,7 @@
<div class="row" ng-if="state.loaded">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-terminal" title="Console">
<div class="pull-right">
<i id="loadConsoleSpinner" class="fa fa-cog fa-2x fa-spin" style="margin-top: 5px; display: none;"></i>
</div>
</rd-widget-header>
<rd-widget-header icon="fa-terminal" title="Console"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<div ng-if="!state.connected">
@@ -54,8 +48,11 @@
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-1 col-sm-offset-2 col-lg-11 col-sm-10">
<button type="button" class="btn btn-primary" ng-click="connect()">Connect</button>
<div class="col-sm-12">
<button type="button" class="btn btn-primary" ng-disabled="state.connected" button-spinner="state.connected" ng-click="connect()">
<span ng-hide="state.leaveNetworkInProgress">Connect</span>
<span ng-show="state.leaveNetworkInProgress">Connecting...</span>
</button>
</div>
</div>
</div>
@@ -1,9 +1,11 @@
angular.module('containerConsole', [])
.controller('ContainerConsoleController', ['$scope', '$transition$', 'Container', 'Image', 'EndpointProvider', 'Notifications', 'ContainerHelper', 'ContainerService', 'ExecService',
function ($scope, $transition$, Container, Image, EndpointProvider, Notifications, ContainerHelper, ContainerService, ExecService) {
$scope.state = {};
$scope.state.loaded = false;
$scope.state.connected = false;
$scope.state = {
loaded: false,
connected: false
};
$scope.formValues = {};
var socket, term;
@@ -19,25 +21,20 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
$scope.container = d;
if (d.message) {
Notifications.error('Error', d, 'Unable to retrieve container details');
$('#loadingViewSpinner').hide();
} else {
Image.get({id: d.Image}, function(imgData) {
$scope.imageOS = imgData.Os;
$scope.formValues.command = imgData.Os === 'windows' ? 'powershell' : 'bash';
$scope.state.loaded = true;
$('#loadingViewSpinner').hide();
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve image details');
$('#loadingViewSpinner').hide();
});
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve container details');
$('#loadingViewSpinner').hide();
});
$scope.connect = function() {
$('#loadConsoleSpinner').show();
var termWidth = Math.floor(($('#terminal-container').width() - 20) / 8.39);
var termHeight = 30;
var command = $scope.formValues.isCustomCommand ?
@@ -67,9 +64,6 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to exec into container');
})
.finally(function final() {
$('#loadConsoleSpinner').hide();
});
};
@@ -88,7 +82,6 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
$scope.state.connected = true;
socket.onopen = function(evt) {
$('#loadConsoleSpinner').hide();
term = new Terminal();
term.on('data', function (data) {
@@ -2,21 +2,18 @@ angular.module('containerInspect', ['angular-json-tree'])
.controller('ContainerInspectController', ['$scope', '$transition$', 'Notifications', 'ContainerService',
function ($scope, $transition$, Notifications, ContainerService) {
$scope.state = { DisplayTextView: false };
$scope.state = {
DisplayTextView: false
};
$scope.containerInfo = {};
function initView() {
$('#loadingViewSpinner').show();
ContainerService.inspect($transition$.params().id)
.then(function success(d) {
$scope.containerInfo = d;
})
.catch(function error(e) {
Notifications.error('Failure', e, 'Unable to inspect container');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -8,20 +8,15 @@ function ($scope, $transition$, $anchorScroll, ContainerLogs, Container) {
$scope.stderr = '';
$scope.tailLines = 2000;
$('#loadingViewSpinner').show();
Container.get({id: $transition$.params().id}, function (d) {
$scope.container = d;
$('#loadingViewSpinner').hide();
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to retrieve container info');
});
function getLogs() {
$('#loadingViewSpinner').show();
getLogsStdout();
getLogsStderr();
$('#loadingViewSpinner').hide();
}
function getLogsStderr() {
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Container logs">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Container logs"></rd-header-title>
<rd-header-content>
<a ui-sref="containers">Containers</a> &gt; <a ui-sref="container({id: container.Id})">{{ container.Name|trimcontainername }}</a> &gt; Logs
</rd-header-content>
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Container statistics">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Container statistics"></rd-header-title>
<rd-header-content>
<a ui-sref="containers">Containers</a> &gt; <a ui-sref="container({id: container.Id})">{{ container.Name|trimcontainername }}</a> &gt; Stats
</rd-header-content>
@@ -80,7 +80,6 @@ function ($q, $scope, $transition$, $document, $interval, ContainerService, Char
};
function startChartUpdate(networkChart, cpuChart, memoryChart) {
$('#loadingViewSpinner').show();
$q.all({
stats: ContainerService.containerStats($transition$.params().id),
top: ContainerService.containerTop($transition$.params().id)
@@ -99,9 +98,6 @@ function ($q, $scope, $transition$, $document, $interval, ContainerService, Char
.catch(function error(err) {
stopRepeater();
Notifications.error('Failure', err, 'Unable to retrieve container statistics');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -143,17 +139,12 @@ function ($q, $scope, $transition$, $document, $interval, ContainerService, Char
}
function initView() {
$('#loadingViewSpinner').show();
ContainerService.container($transition$.params().id)
.then(function success(data) {
$scope.container = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve container information');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
$document.ready(function() {
+2 -3
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="containers" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadContainersSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Containers</rd-header-content>
</rd-header>
@@ -33,7 +32,7 @@
<button type="button" class="btn btn-primary btn-responsive" ng-click="unpauseAction()" ng-disabled="!state.selectedItemCount || state.noPausedItemsSelected"><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button>
<button type="button" class="btn btn-danger btn-responsive" ng-click="confirmRemoveAction()" ng-disabled="!state.selectedItemCount"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button>
</div>
<a class="btn btn-primary" type="button" ui-sref="actions.create.container"><i class="fa fa-plus space-right" aria-hidden="true"></i>Add container</a>
<a class="btn btn-primary btn-responsive" type="button" ui-sref="actions.create.container"><i class="fa fa-plus space-right" aria-hidden="true"></i>Add container</a>
</div>
<div class="pull-right">
<input type="checkbox" ng-model="state.displayAll" id="displayAll" ng-change="toggleGetAll()" style="margin-top: -2px; margin-right: 5px;"/><label for="displayAll">Show all containers</label>
@@ -110,7 +109,7 @@
</tr>
</thead>
<tbody>
<tr dir-paginate="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: container.Checked}">
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
<td>
<span ng-if="['starting','healthy','unhealthy'].indexOf(container.Status) !== -1" class="label label-{{ container.Status|containerstatusbadge }} interactive" uib-tooltip="This container has a health check">{{ container.Status }}</span>
@@ -1,6 +1,6 @@
angular.module('containers', [])
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'SystemService', 'Notifications', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider', 'LocalStorage',
function ($q, $scope, $filter, Container, ContainerService, ContainerHelper, SystemService, Notifications, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider, LocalStorage) {
.controller('ContainersController', ['$q', '$scope', '$state', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'SystemService', 'Notifications', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider', 'LocalStorage',
function ($q, $scope, $state, $filter, Container, ContainerService, ContainerHelper, SystemService, Notifications, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider, LocalStorage) {
$scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('containers');
$scope.state.displayAll = LocalStorage.getFilterContainerShowAll();
@@ -24,7 +24,6 @@ angular.module('containers', [])
$scope.cleanAssociatedVolumes = false;
var update = function (data) {
$('#loadContainersSpinner').show();
$scope.state.selectedItemCount = 0;
Container.query(data, function (d) {
var containers = d;
@@ -45,21 +44,17 @@ angular.module('containers', [])
return model;
});
updateSelectionFlags();
$('#loadContainersSpinner').hide();
}, function (e) {
$('#loadContainersSpinner').hide();
Notifications.error('Failure', e, 'Unable to retrieve containers');
$scope.containers = [];
});
};
var batch = function (items, action, msg) {
$('#loadContainersSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadContainersSpinner').hide();
update({all: $scope.state.displayAll ? 1 : 0});
}
};
@@ -78,12 +73,13 @@ angular.module('containers', [])
else if (action === Container.remove) {
ContainerService.remove(c, $scope.cleanAssociatedVolumes)
.then(function success() {
var index = items.indexOf(c);
items.splice(index, 1);
Notifications.success('Container successfully removed');
complete();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove container');
})
.finally(function final() {
complete();
});
}
@@ -108,13 +104,9 @@ angular.module('containers', [])
Notifications.error('Failure', e, 'An error occured');
complete();
});
}
}
});
if (counter === 0) {
$('#loadContainersSpinner').hide();
}
};
$scope.selectItems = function (allSelected) {
@@ -58,14 +58,11 @@ function ($scope, $state, $document, Notifications, ConfigService, Authenticatio
}
$scope.create = function () {
$('#createResourceSpinner').show();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createResourceSpinner').hide();
return;
}
@@ -83,9 +80,6 @@ function ($scope, $state, $document, Notifications, ConfigService, Authenticatio
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to create config');
})
.finally(function final() {
$('#createResourceSpinner').hide();
});
};
@@ -62,8 +62,6 @@
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name" ng-click="create()">Create config</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="configs">Cancel</a>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</div>
</div>
<!-- !actions -->
@@ -20,7 +20,8 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
};
$scope.state = {
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.refreshSlider = function () {
@@ -348,6 +349,12 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
}
}
$scope.resetNetworkConfig = function() {
$scope.config.NetworkingConfig = {
EndpointsConfig: {}
};
};
function loadFromContainerNetworkConfig(d) {
$scope.config.NetworkingConfig = {
EndpointsConfig: {}
@@ -549,7 +556,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
});
var userDetails = Authentication.getUserDetails();
$scope.isAdmin = userDetails.role === 1 ? true : false;
$scope.isAdmin = userDetails.role === 1;
}
function validateForm(accessControlData, isAdmin) {
@@ -570,16 +577,16 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
if (!confirm) {
return false;
}
$('#createContainerSpinner').show();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createContainerSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
var config = prepareConfiguration();
createContainer(config, accessControlData);
})
@@ -605,7 +612,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
Notifications.error('Failure', err, 'Unable to create container');
})
.finally(function final() {
$('#createContainerSpinner').hide();
$scope.state.actionInProgress = false;
});
});
}
@@ -9,7 +9,7 @@
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal">
<form class="form-horizontal" autocomplete="off">
<!-- name-input -->
<div class="form-group">
<label for="container_name" class="col-sm-1 control-label text-left">Name</label>
@@ -28,7 +28,7 @@
<div ng-if="formValues.Registry || !fromContainer">
<!-- image-and-registry -->
<div class="form-group">
<por-image-registry image="config.Image" registry="formValues.Registry" ng-if="formValues.Registry"></por-image-registry>
<por-image-registry image="config.Image" registry="formValues.Registry" ng-if="formValues.Registry" auto-complete="true"></por-image-registry>
</div>
<!-- !image-and-registry -->
<!-- always-pull -->
@@ -112,9 +112,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Image || (!formValues.Registry && fromContainer)" ng-click="create()">Start container</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="containers">Cancel</a>
<i id="createContainerSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !config.Image || (!formValues.Registry && fromContainer)" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Deploy the container</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
<span ng-if="fromContainerMultipleNetworks" style="margin-left: 10px">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
@@ -295,7 +296,7 @@
<div class="form-group">
<label for="container_network" class="col-sm-2 col-lg-1 control-label text-left">Network</label>
<div class="col-sm-9">
<select class="form-control" ng-model="config.HostConfig.NetworkMode" id="container_network">
<select class="form-control" ng-model="config.HostConfig.NetworkMode" id="container_network" ng-change="resetNetworkConfig()">
<option selected disabled hidden value="">Select a network</option>
<option ng-repeat="net in availableNetworks" ng-value="net.Name">{{ net.Name }}</option>
</select>
@@ -11,7 +11,8 @@ function ($q, $scope, $state, PluginService, Notifications, NetworkService, Labe
};
$scope.state = {
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.availableNetworkDrivers = [];
@@ -89,18 +90,16 @@ function ($q, $scope, $state, PluginService, Notifications, NetworkService, Labe
}
$scope.create = function () {
$('#createResourceSpinner').show();
var networkConfiguration = prepareConfiguration();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createResourceSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
NetworkService.create(networkConfiguration)
.then(function success(data) {
var networkIdentifier = data.Id;
@@ -115,12 +114,11 @@ function ($q, $scope, $state, PluginService, Notifications, NetworkService, Labe
Notifications.error('Failure', err, 'An error occured during network creation');
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
function initView() {
$('#loadingViewSpinner').show();
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
if(endpointProvider !== 'DOCKER_SWARM') {
@@ -130,9 +128,6 @@ function ($q, $scope, $state, PluginService, Notifications, NetworkService, Labe
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve network drivers');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Create network">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Create network"></rd-header-title>
<rd-header-content>
<a ui-sref="networks">Networks</a> &gt; Add network
</rd-header-content>
@@ -130,8 +128,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Name" ng-click="create()">Create network</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="networks">Cancel</a>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !config.Name" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Create the network</span>
<span ng-show="state.actionInProgress">Creating network...</span>
</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
@@ -3,7 +3,8 @@ angular.module('createRegistry', [])
function ($scope, $state, RegistryService, Notifications) {
$scope.state = {
RegistryType: 'quay'
RegistryType: 'quay',
actionInProgress: false
};
$scope.formValues = {
@@ -27,13 +28,13 @@ function ($scope, $state, RegistryService, Notifications) {
};
$scope.addRegistry = function() {
$('#createRegistrySpinner').show();
var registryName = $scope.formValues.Name;
var registryURL = $scope.formValues.URL.replace(/^https?\:\/\//i, '');
var authentication = $scope.formValues.Authentication;
var username = $scope.formValues.Username;
var password = $scope.formValues.Password;
$scope.state.actionInProgress = true;
RegistryService.createRegistry(registryName, registryURL, authentication, username, password)
.then(function success(data) {
Notifications.success('Registry successfully created');
@@ -43,7 +44,7 @@ function ($scope, $state, RegistryService, Notifications) {
Notifications.error('Failure', err, 'Unable to create registry');
})
.finally(function final() {
$('#createRegistrySpinner').hide();
$scope.state.actionInProgress = false;
});
};
}]);
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Create registry">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="display:none"></i>
</rd-header-title>
<rd-header-title title="Create registry"></rd-header-title>
<rd-header-content>
<a ui-sref="registries">Registries</a> &gt; Add registry
</rd-header-content>
@@ -104,10 +102,15 @@
<!-- !credentials-password -->
</div>
<!-- !authentication-credentials -->
<div class="col-sm-12 form-section-title">
Actions
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name || !formValues.URL || (formValues.Authentication && (!formValues.Username || !formValues.Password))" ng-click="addRegistry()">Add registry</button>
<i id="createRegistrySpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL || (formValues.Authentication && (!formValues.Username || !formValues.Password))" ng-click="addRegistry()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Add registry</span>
<span ng-show="state.actionInProgress">Adding registry...</span>
</button>
</div>
</div>
</form>
@@ -11,7 +11,8 @@ function ($scope, $state, Notifications, SecretService, LabelHelper, Authenticat
};
$scope.state = {
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.addLabel = function() {
@@ -55,17 +56,16 @@ function ($scope, $state, Notifications, SecretService, LabelHelper, Authenticat
}
$scope.create = function () {
$('#createResourceSpinner').show();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createResourceSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
var secretConfiguration = prepareConfiguration();
SecretService.create(secretConfiguration)
.then(function success(data) {
@@ -81,7 +81,7 @@ function ($scope, $state, Notifications, SecretService, LabelHelper, Authenticat
Notifications.error('Failure', err, 'Unable to create secret');
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
}]);
@@ -75,9 +75,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name || !formValues.Data" ng-click="create()">Create secret</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="secrets">Cancel</a>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.Data" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Create the secret</span>
<span ng-show="state.actionInProgress">Creating secret...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
@@ -39,7 +39,8 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
};
$scope.state = {
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.refreshSlider = function () {
@@ -81,7 +82,7 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
};
$scope.addSecret = function() {
$scope.formValues.Secrets.push({});
$scope.formValues.Secrets.push({ overrideTarget: false });
};
$scope.removeSecret = function(index) {
@@ -242,7 +243,7 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
function prepareUpdateConfig(config, input) {
config.UpdateConfig = {
Parallelism: input.Parallelism || 0,
Delay: input.UpdateDelay || 0,
Delay: input.UpdateDelay * 1000000000 || 0,
FailureAction: input.FailureAction,
Order: input.UpdateOrder
};
@@ -274,6 +275,9 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
if (secret.model) {
var s = SecretHelper.secretConfig(secret.model);
s.File.Name = s.SecretName;
if (secret.overrideTarget && secret.target && secret.target !== '') {
s.File.Name = secret.target;
}
secrets.push(s);
}
});
@@ -366,7 +370,7 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
Notifications.error('Failure', err, 'Unable to create service');
})
.finally(function final() {
$('#createServiceSpinner').hide();
$scope.state.actionInProgress = false;
});
}
@@ -383,17 +387,16 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
}
$scope.create = function createService() {
$('#createServiceSpinner').show();
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createServiceSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
var config = prepareConfiguration();
createNewService(config, accessControlData);
};
@@ -422,7 +425,6 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
}
function initView() {
$('#loadingViewSpinner').show();
var apiVersion = $scope.applicationState.endpoint.apiVersion;
var provider = $scope.applicationState.endpoint.mode.provider;
@@ -444,13 +446,10 @@ function ($q, $scope, $state, $timeout, Service, ServiceHelper, ConfigService, C
var settings = data.settings;
$scope.allowBindMounts = settings.AllowBindMountsForRegularUsers;
var userDetails = Authentication.getUserDetails();
$scope.isAdmin = userDetails.role === 1 ? true : false;
$scope.isAdmin = userDetails.role === 1;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to initialize view');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Create service">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-title title="Create service"></rd-header-title>
<rd-header-content>
<a ui-sref="services">Services</a> &gt; Add service
</rd-header-content>
@@ -11,7 +9,7 @@
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal">
<form class="form-horizontal" autocomplete="off">
<!-- name-input -->
<div class="form-group">
<label for="service_name" class="col-sm-1 control-label text-left">Name</label>
@@ -25,7 +23,7 @@
</div>
<!-- image-and-registry -->
<div class="form-group">
<por-image-registry image="formValues.Image" registry="formValues.Registry"></por-image-registry>
<por-image-registry image="formValues.Image" registry="formValues.Registry" auto-complete="true"></por-image-registry>
</div>
<!-- !image-and-registry -->
<div class="col-sm-12 form-section-title">
@@ -109,9 +107,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Image" ng-click="create()">Create service</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="services">Cancel</a>
<i id="createServiceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Image" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Create the service</span>
<span ng-show="state.actionInProgress">Creating service...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
@@ -396,7 +395,7 @@
</div>
<div class="col-sm-5">
<p class="small text-muted">
Amount of time between updates.
Amount of time between updates. Time in seconds.
</p>
</div>
</div>
@@ -1,7 +1,7 @@
<form class="form-horizontal" style="margin-top: 15px;">
<div class="form-group">
<div class="col-sm-12 small text-muted">
Secrets will be available under <code>/run/secrets/$SECRET_NAME</code> in containers.
By default, secrets will be available under <code>/run/secrets/$SECRET_NAME</code> in containers.
</div>
</div>
<div class="form-group">
@@ -12,16 +12,26 @@
</span>
</div>
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="secret in formValues.Secrets" style="margin-top: 2px;">
<div ng-repeat="secret in formValues.Secrets track by $index" style="margin-top: 4px;">
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">secret</span>
<select class="form-control" ng-model="secret.model" ng-options="secret.Name for secret in availableSecrets">
<option value="" selected="selected">Select a secret</option>
</select>
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeSecret($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
<div class="input-group col-sm-4 input-group-sm" ng-if="applicationState.endpoint.apiVersion >= 1.30 && secret.overrideTarget">
<span class="input-group-addon">target</span>
<input class="form-control" ng-model="secret.target" placeholder="/path/in/container">
</div>
<div class="input-group col-sm-3 input-group-sm">
<div class="btn-group btn-group-sm" ng-if="applicationState.endpoint.apiVersion >= 1.30">
<label class="btn btn-primary" ng-model="secret.overrideTarget" uib-btn-radio="false">Default location</label>
<label class="btn btn-primary" ng-model="secret.overrideTarget" uib-btn-radio="true">Override</label>
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removeSecret($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
@@ -18,7 +18,8 @@ function ($scope, $state, $document, StackService, CodeMirrorService, Authentica
$scope.state = {
Method: 'editor',
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.addEnvironmentVariable = function() {
@@ -62,20 +63,18 @@ function ($scope, $state, $document, StackService, CodeMirrorService, Authentica
}
$scope.deployStack = function () {
$('#createResourceSpinner').show();
var name = $scope.formValues.Name;
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
var userId = userDetails.ID;
if (!validateForm(accessControlData, isAdmin)) {
$('#createResourceSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
createStack(name)
.then(function success(data) {
Notifications.success('Stack successfully deployed');
@@ -93,7 +92,7 @@ function ($scope, $state, $document, StackService, CodeMirrorService, Authentica
Notifications.error('Failure', err, 'Unable to apply resource control on the stack');
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
+6 -7
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Create stack">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="display: none;"></i>
</rd-header-title>
<rd-header-title title="Create stack"></rd-header-title>
<rd-header-content>
<a ui-sref="stacks">Stacks</a> > Add stack
</rd-header-content>
@@ -169,12 +167,13 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="(state.Method === 'editor' && !formValues.StackFileContent)
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || (state.Method === 'editor' && !formValues.StackFileContent)
|| (state.Method === 'upload' && !formValues.StackFile)
|| (state.Method === 'repository' && (!formValues.RepositoryURL || !formValues.RepositoryPath))
|| !formValues.Name" ng-click="deployStack()">Deploy the stack</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="stacks">Cancel</a>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|| !formValues.Name" ng-click="deployStack()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Deploy the stack</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
@@ -9,7 +9,8 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
};
$scope.state = {
formValidationError: ''
formValidationError: '',
actionInProgress: false
};
$scope.availableVolumeDrivers = [];
@@ -35,7 +36,6 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
}
$scope.create = function () {
$('#createVolumeSpinner').show();
var name = $scope.formValues.Name;
var driver = $scope.formValues.Driver;
@@ -43,13 +43,13 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
var volumeConfiguration = VolumeService.createVolumeConfiguration(name, driver, driverOptions);
var accessControlData = $scope.formValues.AccessControlData;
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true : false;
var isAdmin = userDetails.role === 1;
if (!validateForm(accessControlData, isAdmin)) {
$('#createVolumeSpinner').hide();
return;
}
$scope.state.actionInProgress = true;
VolumeService.createVolume(volumeConfiguration)
.then(function success(data) {
var volumeIdentifier = data.Id;
@@ -64,12 +64,11 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
Notifications.error('Failure', err, 'An error occured during volume creation');
})
.finally(function final() {
$('#createVolumeSpinner').hide();
$scope.state.actionInProgress = false;
});
};
function initView() {
$('#loadingViewSpinner').show();
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
if (endpointProvider !== 'DOCKER_SWARM') {
@@ -79,9 +78,6 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve volume drivers');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Create volume">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Create volume"></rd-header-title>
<rd-header-content>
<a ui-sref="volumes">Volumes</a> &gt; Add volume
</rd-header-content>
@@ -73,9 +71,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-click="create()">Create volume</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="volumes">Cancel</a>
<i id="createVolumeSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-click="create()" ng-disabled="state.actionInProgress" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Create the volume</span>
<span ng-show="state.actionInProgress">Creating volume...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
+1 -6
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Home">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Home"></rd-header-title>
<rd-header-content>Dashboard</rd-header-content>
</rd-header>
@@ -171,6 +169,3 @@
</a>
</div>
</div>
<div class="row">
</div>
@@ -65,8 +65,6 @@ function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, System
}
function initView() {
$('#loadingViewSpinner').show();
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
var endpointRole = $scope.applicationState.endpoint.mode.role;
@@ -86,9 +84,7 @@ function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, System
prepareInfoData(d[4]);
$scope.serviceCount = d[5].length;
$scope.stackCount = d[6].length;
$('#loadingViewSpinner').hide();
}, function(e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to load dashboard data');
});
}
+5 -5
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Endpoint details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Endpoint details"></rd-header-title>
<rd-header-content>
<a ui-sref="endpoints">Endpoints</a> &gt; <a ui-sref="endpoint({id: endpoint.Id})">{{ endpoint.Name }}</a>
</rd-header-content>
@@ -55,9 +53,11 @@
<!-- !endpoint-security -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!endpoint.Name || !endpoint.URL || (endpoint.TLS && ((endpoint.TLSVerify && !formValues.TLSCACert) || (endpoint.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="updateEndpoint()">Update endpoint</button>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !endpoint.Name || !endpoint.URL || (endpoint.TLS && ((endpoint.TLSVerify && !formValues.TLSCACert) || (endpoint.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="updateEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Update endpoint</span>
<span ng-show="state.actionInProgress">Updating endpoint...</span>
</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="endpoints">Cancel</a>
<i id="updateResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</div>
</div>
</form>
@@ -7,7 +7,8 @@ function ($scope, $state, $transition$, $filter, EndpointService, Notifications)
}
$scope.state = {
uploadInProgress: false
uploadInProgress: false,
actionInProgress: false
};
$scope.formValues = {
@@ -35,13 +36,14 @@ function ($scope, $state, $transition$, $filter, EndpointService, Notifications)
type: $scope.endpointType
};
$('updateResourceSpinner').show();
$scope.state.actionInProgress = true;
EndpointService.updateEndpoint(endpoint.Id, endpointParams)
.then(function success(data) {
Notifications.success('Endpoint updated', $scope.endpoint.Name);
$state.go('endpoints');
}, function error(err) {
Notifications.error('Failure', err, 'Unable to update endpoint');
$scope.state.actionInProgress = false;
}, function update(evt) {
if (evt.upload) {
$scope.state.uploadInProgress = evt.upload;
@@ -50,7 +52,6 @@ function ($scope, $state, $transition$, $filter, EndpointService, Notifications)
};
function initView() {
$('#loadingViewSpinner').show();
EndpointService.endpoint($transition$.params().id)
.then(function success(data) {
var endpoint = data;
@@ -64,9 +65,6 @@ function ($scope, $state, $transition$, $filter, EndpointService, Notifications)
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Endpoint access">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Endpoint access"></rd-header-title>
<rd-header-content>
<a ui-sref="endpoints">Endpoints</a> &gt; <a ui-sref="endpoint({id: endpoint.Id})">{{ endpoint.Name }}</a> &gt; Access management
</rd-header-content>
@@ -7,16 +7,12 @@ function ($scope, $transition$, EndpointService, Notifications) {
};
function initView() {
$('#loadingViewSpinner').show();
EndpointService.endpoint($transition$.params().id)
.then(function success(data) {
$scope.endpoint = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
})
.finally(function final(){
$('#loadingViewSpinner').hide();
});
}
+6 -4
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="endpoints" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadEndpointsSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Endpoint management</rd-header-content>
</rd-header>
@@ -66,8 +65,11 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (formValues.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="addEndpoint()"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (formValues.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="addEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</span>
</button>
</div>
</div>
<!-- !actions -->
@@ -126,7 +128,7 @@
</tr>
</thead>
<tbody>
<tr dir-paginate="endpoint in (state.filteredEndpoints = (endpoints | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="endpoint in (state.filteredEndpoints = (endpoints | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: endpoint.Checked}">
<td ng-if="applicationState.application.endpointManagement"><input type="checkbox" ng-model="endpoint.Checked" ng-change="selectItem(endpoint)" /></td>
<td><i class="fa fa-star" aria-hidden="true" ng-if="endpoint.Id === activeEndpoint.Id"></i> {{ endpoint.Name }}</td>
<td>{{ endpoint.URL | stripprotocol }}</td>
@@ -4,7 +4,8 @@ function ($scope, $state, $filter, EndpointService, EndpointProvider, Notificati
$scope.state = {
uploadInProgress: false,
selectedItemCount: 0,
pagination_count: Pagination.getPaginationCount('endpoints')
pagination_count: Pagination.getPaginationCount('endpoints'),
actionInProgress: false
};
$scope.sortType = 'Name';
$scope.sortReverse = true;
@@ -59,11 +60,13 @@ function ($scope, $state, $filter, EndpointService, EndpointProvider, Notificati
var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert;
var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey;
$scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, URL, PublicURL, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile).then(function success(data) {
Notifications.success('Endpoint created', name);
$state.reload();
}, function error(err) {
$scope.state.uploadInProgress = false;
$scope.state.actionInProgress = false;
Notifications.error('Failure', err, 'Unable to create endpoint');
}, function update(evt) {
if (evt.upload) {
@@ -73,32 +76,20 @@ function ($scope, $state, $filter, EndpointService, EndpointProvider, Notificati
};
$scope.removeAction = function () {
$('#loadEndpointsSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadEndpointsSpinner').hide();
}
};
angular.forEach($scope.endpoints, function (endpoint) {
if (endpoint.Checked) {
counter = counter + 1;
EndpointService.deleteEndpoint(endpoint.Id).then(function success(data) {
Notifications.success('Endpoint deleted', endpoint.Name);
var index = $scope.endpoints.indexOf(endpoint);
$scope.endpoints.splice(index, 1);
complete();
}, function error(err) {
Notifications.error('Failure', err, 'Unable to remove endpoint');
complete();
});
}
});
};
function fetchEndpoints() {
$('#loadEndpointsSpinner').show();
EndpointService.endpoints()
.then(function success(data) {
$scope.endpoints = data;
@@ -106,9 +97,6 @@ function ($scope, $state, $filter, EndpointService, EndpointProvider, Notificati
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve endpoints');
$scope.endpoints = [];
})
.finally(function final() {
$('#loadEndpointsSpinner').hide();
});
}
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="engine" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>Docker</rd-header-content>
</rd-header>
+1 -5
View File
@@ -1,9 +1,8 @@
angular.module('engine', [])
.controller('EngineController', ['$q', '$scope', 'SystemService', 'Notifications',
function ($q, $scope, SystemService, Notifications) {
function initView() {
$('#loadingViewSpinner').show();
$q.all({
version: SystemService.version(),
info: SystemService.info()
@@ -16,9 +15,6 @@ function ($q, $scope, SystemService, Notifications) {
$scope.info = {};
$scope.version = {};
Notifications.error('Failure', err, 'Unable to retrieve engine details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="events" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadEventsSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Events</rd-header-content>
</rd-header>
@@ -19,16 +19,12 @@ function ($scope, Notifications, SystemService, Pagination) {
var from = moment().subtract(24, 'hour').unix();
var to = moment().unix();
$('#loadEventsSpinner').show();
SystemService.events(from, to)
.then(function success(data) {
$scope.events = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load events');
})
.finally(function final() {
$('#loadEventsSpinner').hide();
});
}
+11 -4
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Image details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Image details"></rd-header-title>
<rd-header-content>
<a ui-sref="images">Images</a> &gt; <a ui-sref="image({id: image.Id})">{{ image.Id }}</a>
</rd-header-content>
@@ -41,6 +39,16 @@
or on the trash icon <span class="fa fa-trash-o" aria-hidden="true"></span> to delete a tag.
</span>
</div>
<div class="col-sm-12">
<span id="downloadResourceHint" class="createResource" style="display: none; margin-left: 0;">
Download in progress...
<i class="fa fa-circle-o-notch fa-spin" aria-hidden="true" style="margin-left: 2px;"></i>
</span>
<span id="uploadResourceHint" class="createResource" style="display: none; margin-left: 0;">
Upload in progress...
<i class="fa fa-circle-o-notch fa-spin" aria-hidden="true" style="margin-left: 2px;"></i>
</span>
</div>
</div>
</form>
</rd-widget-body>
@@ -69,7 +77,6 @@
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Image" ng-click="tagImage()">Tag</button>
<i id="pullImageSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</div>
</div>
</form>
+4 -20
View File
@@ -21,7 +21,6 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
};
$scope.tagImage = function() {
$('#loadingViewSpinner').show();
var image = $scope.formValues.Image;
var registry = $scope.formValues.Registry;
@@ -32,14 +31,11 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to tag image');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.pushTag = function(repository) {
$('#loadingViewSpinner').show();
$('#uploadResourceHint').show();
RegistryService.retrieveRegistryFromRepository(repository)
.then(function success(data) {
var registry = data;
@@ -52,12 +48,12 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
Notifications.error('Failure', err, 'Unable to push image to repository');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
$('#uploadResourceHint').hide();
});
};
$scope.pullTag = function(repository) {
$('#loadingViewSpinner').show();
$('#downloadResourceHint').show();
RegistryService.retrieveRegistryFromRepository(repository)
.then(function success(data) {
var registry = data;
@@ -70,12 +66,11 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
Notifications.error('Failure', err, 'Unable to pull image');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
$('#downloadResourceHint').hide();
});
};
$scope.removeTag = function(repository) {
$('#loadingViewSpinner').show();
ImageService.deleteImage(repository, false)
.then(function success() {
if ($scope.image.RepoTags.length === 1) {
@@ -88,14 +83,10 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove image');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.removeImage = function (id) {
$('#loadingViewSpinner').show();
ImageService.deleteImage(id, false)
.then(function success() {
Notifications.success('Image successfully deleted', id);
@@ -103,14 +94,10 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove image');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
function initView() {
$('#loadingViewSpinner').show();
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
$q.all({
image: ImageService.image($transition$.params().id),
@@ -123,9 +110,6 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve image details');
$state.go('images');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+5 -4
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="images" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadImagesSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Images</rd-header-content>
</rd-header>
@@ -29,8 +28,10 @@
<!-- !tag-note -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Image" ng-click="pullImage()">Pull</button>
<i id="pullImageSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Image" ng-click="pullImage()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Pull the image</span>
<span ng-show="state.actionInProgress">Download in progress...</span>
</button>
</div>
</div>
</form>
@@ -121,7 +122,7 @@
</tr>
</thead>
<tbody>
<tr dir-paginate="image in (state.filteredImages = (images | filter:{ ContainerCount: state.containersCountFilter } | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="image in (state.filteredImages = (images | filter:{ ContainerCount: state.containersCountFilter } | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: image.Checked}">
<td><input type="checkbox" ng-model="image.Checked" ng-change="selectItem(image)" /></td>
<td>
<a class="monospaced" ui-sref="image({id: image.Id})">{{ image.Id|truncate:20}}</a>
+9 -21
View File
@@ -1,11 +1,14 @@
angular.module('images', [])
.controller('ImagesController', ['$scope', '$state', 'ImageService', 'Notifications', 'Pagination', 'ModalService',
function ($scope, $state, ImageService, Notifications, Pagination, ModalService) {
$scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('images');
$scope.state = {
pagination_count: Pagination.getPaginationCount('images'),
actionInProgress: false,
selectedItemCount: 0
};
$scope.sortType = 'RepoTags';
$scope.sortReverse = true;
$scope.state.selectedItemCount = 0;
$scope.formValues = {
Image: '',
@@ -39,9 +42,10 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
};
$scope.pullImage = function() {
$('#pullImageSpinner').show();
var image = $scope.formValues.Image;
var registry = $scope.formValues.Registry;
$scope.state.actionInProgress = true;
ImageService.pullImage(image, registry, false)
.then(function success(data) {
Notifications.success('Image successfully pulled', image);
@@ -51,7 +55,7 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
Notifications.error('Failure', err, 'Unable to pull image');
})
.finally(function final() {
$('#pullImageSpinner').hide();
$scope.state.actionInProgress = false;
});
};
@@ -64,17 +68,8 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
$scope.removeAction = function (force) {
force = !!force;
$('#loadImagesSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadImagesSpinner').hide();
}
};
angular.forEach($scope.images, function (i) {
if (i.Checked) {
counter = counter + 1;
ImageService.deleteImage(i.Id, force)
.then(function success(data) {
Notifications.success('Image deleted', i.Id);
@@ -83,16 +78,12 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove image');
})
.finally(function final() {
complete();
});
}
});
};
function fetchImages() {
$('#loadImagesSpinner').show();
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
ImageService.images(true)
@@ -102,9 +93,6 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve images');
$scope.images = [];
})
.finally(function final() {
$('#loadImagesSpinner').hide();
});
}
+4 -2
View File
@@ -64,8 +64,10 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="formValues.Password.length < 8 || formValues.Password !== formValues.ConfirmPassword" ng-click="createAdminUser()"><i class="fa fa-user-plus" aria-hidden="true"></i> Create user</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || formValues.Password.length < 8 || formValues.Password !== formValues.ConfirmPassword" ng-click="createAdminUser()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-user-plus" aria-hidden="true"></i> Create user</span>
<span ng-show="state.actionInProgress">Creating user...</span>
</button>
</div>
</div>
<!-- !actions -->
@@ -10,11 +10,15 @@ function ($scope, $state, $sanitize, Notifications, Authentication, StateManager
ConfirmPassword: ''
};
$scope.state = {
actionInProgress: false
};
$scope.createAdminUser = function() {
$('#createResourceSpinner').show();
var username = $sanitize($scope.formValues.Username);
var password = $sanitize($scope.formValues.Password);
$scope.state.actionInProgress = true;
UserService.initAdministrator(username, password)
.then(function success() {
return Authentication.login(username, password);
@@ -41,7 +45,7 @@ function ($scope, $state, $sanitize, Notifications, Authentication, StateManager
Notifications.error('Failure', err, 'Unable to create administrator user');
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
@@ -66,8 +66,10 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-click="createLocalEndpoint()"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress" ng-click="createLocalEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
<span ng-show="state.actionInProgress">Connecting...</span>
</button>
</div>
</div>
<!-- !actions -->
@@ -184,8 +186,10 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (!formValues.TLSSKipClientVerify && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="createRemoteEndpoint()"><i class="fa fa-plug" aria-hidden="true"></i> Connect</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (!formValues.TLSSKipClientVerify && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="createRemoteEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
<span ng-show="state.actionInProgress">Connecting...</span>
</button>
</div>
</div>
<!-- !actions -->
@@ -9,7 +9,8 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
$scope.logo = StateManager.getState().application.logo;
$scope.state = {
uploadInProgress: false
uploadInProgress: false,
actionInProgress: false
};
$scope.formValues = {
@@ -25,11 +26,11 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
};
$scope.createLocalEndpoint = function() {
$('#createResourceSpinner').show();
var name = 'local';
var URL = 'unix:///var/run/docker.sock';
var endpointID = 1;
$scope.state.actionInProgress = true;
EndpointService.createLocalEndpoint(name, URL, false, true)
.then(function success(data) {
endpointID = data.Id;
@@ -44,12 +45,11 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
EndpointService.deleteEndpoint(endpointID);
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
$scope.createRemoteEndpoint = function() {
$('#createResourceSpinner').show();
var name = $scope.formValues.Name;
var URL = $scope.formValues.URL;
var PublicURL = URL.split(':')[0];
@@ -59,8 +59,9 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
var TLSCAFile = TLSSkipVerify ? null : $scope.formValues.TLSCACert;
var TLSCertFile = TLSSKipClientVerify ? null : $scope.formValues.TLSCert;
var TLSKeyFile = TLSSKipClientVerify ? null : $scope.formValues.TLSKey;
var endpointID = 1;
$scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
.then(function success(data) {
endpointID = data.Id;
@@ -75,7 +76,7 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
EndpointService.deleteEndpoint(endpointID);
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
}]);
+1 -3
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Network details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Network details"></rd-header-title>
<rd-header-content>
<a ui-sref="networks">Networks</a> &gt; <a ui-sref="network({id: network.Id})">{{ network.Name }}</a>
</rd-header-content>
@@ -3,35 +3,27 @@ angular.module('network', [])
function ($scope, $state, $transition$, $filter, Network, NetworkService, Container, ContainerHelper, Notifications) {
$scope.removeNetwork = function removeNetwork(networkId) {
$('#loadingViewSpinner').show();
Network.remove({id: $transition$.params().id}, function (d) {
if (d.message) {
$('#loadingViewSpinner').hide();
Notifications.error('Error', d, 'Unable to remove network');
} else {
$('#loadingViewSpinner').hide();
Notifications.success('Network removed', $transition$.params().id);
$state.go('networks', {});
}
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to remove network');
});
};
$scope.containerLeaveNetwork = function containerLeaveNetwork(network, containerId) {
$('#loadingViewSpinner').show();
Network.disconnect({id: $transition$.params().id}, { Container: containerId, Force: false }, function (d) {
if (d.message) {
$('#loadingViewSpinner').hide();
Notifications.error('Error', d, 'Unable to disconnect container from network');
} else {
$('#loadingViewSpinner').hide();
Notifications.success('Container left network', $transition$.params().id);
$state.go('network', {id: network.Id}, {reload: true});
}
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to disconnect container from network');
});
};
@@ -63,9 +55,7 @@ function ($scope, $state, $transition$, $filter, Network, NetworkService, Contai
}
});
filterContainersInNetwork(network, containersInNetwork);
$('#loadingViewSpinner').hide();
}, function error(err) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', err, 'Unable to retrieve containers in network');
});
} else {
@@ -73,9 +63,7 @@ function ($scope, $state, $transition$, $filter, Network, NetworkService, Contai
filters: { network: [$transition$.params().id] }
}, function success(data) {
filterContainersInNetwork(network, data);
$('#loadingViewSpinner').hide();
}, function error(err) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', err, 'Unable to retrieve containers in network');
});
}
@@ -83,7 +71,6 @@ function ($scope, $state, $transition$, $filter, Network, NetworkService, Contai
}
function initView() {
$('#loadingViewSpinner').show();
NetworkService.network($transition$.params().id)
.then(function success(data) {
$scope.network = data;
@@ -93,11 +80,7 @@ function ($scope, $state, $transition$, $filter, Network, NetworkService, Contai
}
})
.catch(function error(err) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', err, 'Unable to retrieve network info');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -2
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="networks" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadNetworksSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Networks</rd-header-content>
</rd-header>
@@ -99,7 +98,7 @@
</tr>
</thead>
<tbody>
<tr dir-paginate="network in ( state.filteredNetworks = (networks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="network in ( state.filteredNetworks = (networks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: network.Checked}">
<td><input type="checkbox" ng-model="network.Checked" ng-change="selectItem(network)"/></td>
<td><a ui-sref="network({id: network.Id})">{{ network.Name | truncate:40 }}</a></td>
<td>{{ network.StackName ? network.StackName : '-' }}</td>
@@ -35,17 +35,8 @@ function ($scope, $state, Network, NetworkService, Notifications, Pagination) {
};
$scope.removeAction = function () {
$('#loadNetworksSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadNetworksSpinner').hide();
}
};
angular.forEach($scope.networks, function (network) {
if (network.Checked) {
counter = counter + 1;
Network.remove({id: network.Id}, function (d) {
if (d.message) {
Notifications.error('Error', d, 'Unable to remove network');
@@ -54,18 +45,14 @@ function ($scope, $state, Network, NetworkService, Notifications, Pagination) {
var index = $scope.networks.indexOf(network);
$scope.networks.splice(index, 1);
}
complete();
}, function (e) {
Notifications.error('Failure', e, 'Unable to remove network');
complete();
});
}
});
};
function initView() {
$('#loadNetworksSpinner').show();
NetworkService.networks(true, true, true, true)
.then(function success(data) {
$scope.networks = data;
@@ -73,9 +60,6 @@ function ($scope, $state, Network, NetworkService, Notifications, Pagination) {
.catch(function error(err) {
$scope.networks = [];
Notifications.error('Failure', err, 'Unable to retrieve networks');
})
.finally(function final() {
$('#loadNetworksSpinner').hide();
});
}
-2
View File
@@ -68,11 +68,9 @@ function ($scope, $state, $transition$, LabelHelper, Node, NodeHelper, Task, Pag
config.Labels = LabelHelper.fromKeyValueToLabelHash(node.Labels);
Node.update({ id: node.Id, version: node.Version }, config, function (data) {
$('#loadServicesSpinner').hide();
Notifications.success('Node successfully updated', 'Node updated');
$state.go('node', {id: node.Id}, {reload: true});
}, function (e) {
$('#loadServicesSpinner').hide();
Notifications.error('Failure', e, 'Failed to update node');
});
};
+5 -4
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="registries" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Registry management</rd-header-content>
</rd-header>
@@ -57,8 +56,10 @@
<!-- !authentication-credentials -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="dockerhub.Authentication && (!dockerhub.Username || !dockerhub.Password)" ng-click="updateDockerHub()">Update</button>
<i id="updateDockerhubSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || dockerhub.Authentication && (!dockerhub.Username || !dockerhub.Password)" ng-click="updateDockerHub()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Update</span>
<span ng-show="state.actionInProgress">Updating DockerHub settings...</span>
</button>
</div>
</div>
</form>
@@ -117,7 +118,7 @@
</tr>
</thead>
<tbody>
<tr dir-paginate="registry in (state.filteredRegistries = (registries | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="registry in (state.filteredRegistries = (registries | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: registry.Checked}">
<td><input type="checkbox" ng-model="registry.Checked" ng-change="selectItem(registry)" /></td>
<td>
<a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a>
@@ -4,14 +4,15 @@ function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, N
$scope.state = {
selectedItemCount: 0,
pagination_count: Pagination.getPaginationCount('registries')
pagination_count: Pagination.getPaginationCount('registries'),
actionInProgress: false
};
$scope.sortType = 'Name';
$scope.sortReverse = true;
$scope.updateDockerHub = function() {
$('#updateDockerhubSpinner').show();
var dockerhub = $scope.dockerhub;
$scope.state.actionInProgress = true;
DockerHubService.update(dockerhub)
.then(function success(data) {
Notifications.success('DockerHub registry updated');
@@ -20,7 +21,7 @@ function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, N
Notifications.error('Failure', err, 'Unable to update DockerHub details');
})
.finally(function final() {
$('#updateDockerhubSpinner').hide();
$scope.state.actionInProgress = false;
});
};
@@ -61,19 +62,9 @@ function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, N
};
function removeRegistries() {
$('#loadingViewSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadingViewSpinner').hide();
}
};
var registries = $scope.registries;
angular.forEach(registries, function (registry) {
if (registry.Checked) {
counter = counter + 1;
RegistryService.deleteRegistry(registry.Id)
.then(function success(data) {
var index = registries.indexOf(registry);
@@ -82,16 +73,12 @@ function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, N
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove registry');
})
.finally(function final() {
complete();
});
}
});
}
function initView() {
$('#loadingViewSpinner').show();
$q.all({
registries: RegistryService.registries(),
dockerhub: DockerHubService.dockerhub()
@@ -103,9 +90,6 @@ function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, N
.catch(function error(err) {
$scope.registries = [];
Notifications.error('Failure', err, 'Unable to retrieve registries');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+5 -5
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Registry details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Registry details"></rd-header-title>
<rd-header-content>
<a ui-sref="registries">Registries</a> &gt; <a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a>
</rd-header-content>
@@ -66,9 +64,11 @@
<!-- !authentication-credentials -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!registry.Name || !registry.URL || (registry.Authentication && (!registry.Username || !registry.Password))" ng-click="updateRegistry()">Update registry</button>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !registry.Name || !registry.URL || (registry.Authentication && (!registry.Username || !registry.Password))" ng-click="updateRegistry()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Update registry</span>
<span ng-show="state.actionInProgress">Updating registry...</span>
</button>
<a type="button" class="btn btn-default btn-sm" ui-sref="registries">Cancel</a>
<i id="updateRegistrySpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</div>
</div>
</form>
@@ -2,9 +2,13 @@ angular.module('registry', [])
.controller('RegistryController', ['$scope', '$state', '$transition$', '$filter', 'RegistryService', 'Notifications',
function ($scope, $state, $transition$, $filter, RegistryService, Notifications) {
$scope.state = {
actionInProgress: false
};
$scope.updateRegistry = function() {
$('#updateRegistrySpinner').show();
var registry = $scope.registry;
$scope.state.actionInProgress = true;
RegistryService.updateRegistry(registry)
.then(function success(data) {
Notifications.success('Registry successfully updated');
@@ -14,12 +18,11 @@ function ($scope, $state, $transition$, $filter, RegistryService, Notifications)
Notifications.error('Failure', err, 'Unable to update registry');
})
.finally(function final() {
$('#updateRegistrySpinner').hide();
$scope.state.actionInProgress = false;
});
};
function initView() {
$('#loadingViewSpinner').show();
var registryID = $transition$.params().id;
RegistryService.registry(registryID)
.then(function success(data) {
@@ -27,9 +30,6 @@ function ($scope, $state, $transition$, $filter, RegistryService, Notifications)
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve registry details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Registry access">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Registry access"></rd-header-title>
<rd-header-content>
<a ui-sref="registries">Registries</a> &gt; <a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a> &gt; Access management
</rd-header-content>
@@ -7,16 +7,12 @@ function ($scope, $transition$, RegistryService, Notifications) {
};
function initView() {
$('#loadingViewSpinner').show();
RegistryService.registry($transition$.params().id)
.then(function success(data) {
$scope.registry = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve registry details');
})
.finally(function final(){
$('#loadingViewSpinner').hide();
});
}
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="secret({id: secret.Id})" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="secrets">Secrets</a> &gt; <a ui-sref="secret({id: secret.Id})">{{ secret.Name }}</a>
@@ -3,7 +3,6 @@ angular.module('secret', [])
function ($scope, $transition$, $state, SecretService, Notifications) {
$scope.removeSecret = function removeSecret(secretId) {
$('#loadingViewSpinner').show();
SecretService.remove(secretId)
.then(function success(data) {
Notifications.success('Secret successfully removed');
@@ -11,23 +10,16 @@ function ($scope, $transition$, $state, SecretService, Notifications) {
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove secret');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
function initView() {
$('#loadingViewSpinner').show();
SecretService.secret($transition$.params().id)
.then(function success(data) {
$scope.secret = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve secret details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="secrets" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Secrets</rd-header-content>
</rd-header>
@@ -30,17 +30,8 @@ function ($scope, $state, SecretService, Notifications, Pagination) {
};
$scope.removeAction = function () {
$('#loadingViewSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadingViewSpinner').hide();
}
};
angular.forEach($scope.secrets, function (secret) {
if (secret.Checked) {
counter = counter + 1;
SecretService.remove(secret.Id)
.then(function success() {
Notifications.success('Secret deleted', secret.Id);
@@ -49,16 +40,12 @@ function ($scope, $state, SecretService, Notifications, Pagination) {
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove secret');
})
.finally(function final() {
complete();
});
}
});
};
function initView() {
$('#loadingViewSpinner').show();
SecretService.secrets()
.then(function success(data) {
$scope.secrets = data;
@@ -66,9 +53,6 @@ function ($scope, $state, SecretService, Notifications, Pagination) {
.catch(function error(err) {
$scope.secrets = [];
Notifications.error('Failure', err, 'Unable to retrieve secrets');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -1
View File
@@ -51,7 +51,7 @@
</td>
<td>
<p class="small text-muted" style="margin-top: 10px;">
The time window used to evaluate the restart policy (default value is 0, which is unbounded).
The time window used to evaluate the restart policy (default value is 0, which is unbounded). Time in seconds.
</p>
</td>
</tr>
+10 -2
View File
@@ -5,10 +5,18 @@
<rd-widget-body classes="no-padding">
<div class="form-inline" style="padding: 10px;">
Add a secret:
<select class="form-control" ng-options="secret.Name for secret in secrets" ng-model="newSecret">
<select class="form-control" ng-options="secret.Name for secret in secrets" ng-model="state.addSecret.secret">
<option selected disabled hidden value="">Select a secret</option>
</select>
<a class="btn btn-default btn-sm" ng-click="addSecret(service, newSecret)">
<div class="form-group" ng-if="applicationState.endpoint.apiVersion >= 1.30 && state.addSecret.override">
Target:
<input class="form-control" ng-model="state.addSecret.target" placeholder="/path/in/container">
</div>
<div class="btn-group btn-group-sm" ng-if="applicationState.endpoint.apiVersion >= 1.30">
<label class="btn btn-primary" ng-model="state.addSecret.override" uib-btn-radio="false">Default location</label>
<label class="btn btn-primary" ng-model="state.addSecret.override" uib-btn-radio="true">Override</label>
</div>
<a class="btn btn-default btn-sm" ng-click="addSecret(service, state.addSecret)">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add secret
</a>
</div>
@@ -23,7 +23,7 @@
</td>
<td>
<p class="small text-muted" style="margin-top: 10px;">
Amount of time between updates.
Amount of time between updates. Time in seconds.
</p>
</td>
</tr>
+2 -2
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="service({id: service.Id})" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="services">Services</a> &gt; <a ui-sref="service({id: service.Id})">{{ service.Name }}</a>
@@ -69,7 +68,8 @@
<tr>
<td>Image</td>
<td>
<input type="text" class="form-control" ng-model="service.Image" ng-change="updateServiceAttribute(service, 'Image')" ng-disabled="isUpdating" />
<input type="text" class="form-control" uib-typeahead="image for image in availableImages | filter:$viewValue | limitTo:5"
ng-model="service.Image" ng-change="updateServiceAttribute(service, 'Image')" id="image_name" ng-disabled="isUpdating">
</td>
</tr>
<tr ng-if="applicationState.endpoint.apiVersion >= 1.30">
+29 -21
View File
@@ -1,12 +1,13 @@
angular.module('service', [])
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService',
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, Notifications, Pagination, ModalService) {
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'Notifications', 'Pagination', 'ModalService',
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, Notifications, Pagination, ModalService) {
$scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('service_tasks');
$scope.tasks = [];
$scope.sortType = 'Updated';
$scope.sortReverse = true;
$scope.availableImages = [];
$scope.lastVersion = 0;
@@ -74,10 +75,16 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.updateConfig = function updateConfig(service) {
updateServiceArray(service, 'ServiceConfigs', service.ServiceConfigs);
};
$scope.addSecret = function addSecret(service, secret) {
if (secret && service.ServiceSecrets.filter(function(serviceSecret) { return serviceSecret.Id === secret.Id;}).length === 0) {
service.ServiceSecrets.push({ Id: secret.Id, Name: secret.Name, FileName: secret.Name, Uid: '0', Gid: '0', Mode: 444 });
updateServiceArray(service, 'ServiceSecrets', service.ServiceSecrets);
$scope.addSecret = function addSecret(service, newSecret) {
if (newSecret.secret) {
var filename = newSecret.secret.Name;
if (newSecret.override) {
filename = newSecret.target;
}
if (service.ServiceSecrets.filter(function(serviceSecret) { return serviceSecret.Id === newSecret.secret.Id && serviceSecret.FileName === filename;}).length === 0) {
service.ServiceSecrets.push({ Id: newSecret.secret.Id, Name: newSecret.secret.Name, FileName: filename, Uid: '0', Gid: '0', Mode: 444 });
updateServiceArray(service, 'ServiceSecrets', service.ServiceSecrets);
}
}
};
$scope.removeSecret = function removeSecret(service, index) {
@@ -200,7 +207,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
};
$scope.updateService = function updateService(service) {
$('#loadingViewSpinner').show();
var config = ServiceHelper.serviceToConfig(service.Model);
config.Name = service.Name;
config.Labels = LabelHelper.fromKeyValueToLabelHash(service.ServiceLabels);
@@ -238,16 +244,16 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
config.UpdateConfig = {
Parallelism: service.UpdateParallelism,
Delay: service.UpdateDelay,
Delay: service.UpdateDelay * 1000000000,
FailureAction: service.UpdateFailureAction,
Order: service.UpdateOrder
};
config.TaskTemplate.RestartPolicy = {
Condition: service.RestartCondition,
Delay: service.RestartDelay,
Delay: service.RestartDelay * 1000000000,
MaxAttempts: service.RestartMaxAttempts,
Window: service.RestartWindow
Window: service.RestartWindow * 1000000000
};
if (service.Ports) {
@@ -264,7 +270,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
};
Service.update({ id: service.Id, version: service.Version }, config, function (data) {
$('#loadingViewSpinner').hide();
if (data.message && data.message.match(/^rpc error:/)) {
Notifications.error(data.message, 'Error');
} else {
@@ -273,7 +278,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.cancelChanges({});
initView();
}, function (e) {
$('#loadingViewSpinner').hide();
Notifications.error('Failure', e, 'Unable to update service');
});
};
@@ -289,7 +293,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
};
function removeService() {
$('#loadingViewSpinner').show();
ServiceService.remove($scope.service)
.then(function success(data) {
Notifications.success('Service successfully deleted');
@@ -297,9 +300,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove service');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -320,9 +320,14 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
service.LimitMemoryBytes = service.LimitMemoryBytes / 1024 / 1024 || 0;
service.ReservationMemoryBytes = service.ReservationMemoryBytes / 1024 / 1024 || 0;
}
function transformDurations(service) {
service.RestartDelay = service.RestartDelay / 1000000000 || 5;
service.RestartWindow = service.RestartWindow / 1000000000 || 0;
service.UpdateDelay = service.UpdateDelay / 1000000000 || 0;
}
function initView() {
$('#loadingViewSpinner').show();
var apiVersion = $scope.applicationState.endpoint.apiVersion;
ServiceService.service($transition$.params().id)
.then(function success(data) {
@@ -334,6 +339,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
transformResources(service);
translateServiceArrays(service);
transformDurations(service);
$scope.service = service;
originalService = angular.copy(service);
@@ -341,7 +347,8 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
tasks: TaskService.tasks({ service: [service.Name] }),
nodes: NodeService.nodes(),
secrets: apiVersion >= 1.25 ? SecretService.secrets() : [],
configs: apiVersion >= 1.30 ? ConfigService.configs() : []
configs: apiVersion >= 1.30 ? ConfigService.configs() : [],
availableImages: ImageService.images()
});
})
.then(function success(data) {
@@ -349,6 +356,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.nodes = data.nodes;
$scope.configs = data.configs;
$scope.secrets = data.secrets;
$scope.availableImages = ImageService.getUniqueTagListFromImages(data.availableImages);
// Set max cpu value
var maxCpus = 0;
@@ -363,6 +371,9 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.state.sliderMaxCpu = 32;
}
// Default values
$scope.state.addSecret = {override: false};
$timeout(function() {
$anchorScroll();
});
@@ -371,9 +382,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
$scope.secrets = [];
$scope.configs = [];
Notifications.error('Failure', err, 'Unable to retrieve service details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -9,10 +9,8 @@ function ($scope, $transition$, $anchorScroll, ServiceLogs, Service) {
$scope.tailLines = 2000;
function getLogs() {
$('#loadingViewSpinner').show();
getLogsStdout();
getLogsStderr();
$('#loadingViewSpinner').hide();
}
function getLogsStderr() {
@@ -48,13 +46,10 @@ function ($scope, $transition$, $anchorScroll, ServiceLogs, Service) {
}
function getService() {
$('#loadingViewSpinner').show();
Service.get({id: $transition$.params().id}, function (d) {
$scope.service = d;
$('#loadingViewSpinner').hide();
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve service info');
$('#loadingViewSpinner').hide();
});
}
+1 -3
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Service logs">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Service logs"></rd-header-title>
<rd-header-content>
<a ui-sref="services">Services</a> > <a ui-sref="service({id: service.ID})">{{ service.Spec.Name }}</a> > Logs
</rd-header-content>
+2 -3
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="services" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadServicesSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Services</rd-header-content>
</rd-header>
@@ -88,7 +87,7 @@
</th>
</thead>
<tbody>
<tr dir-paginate="service in (state.filteredServices = ( services | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
<tr dir-paginate="service in (state.filteredServices = ( services | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-class="{active: service.Checked}">
<td><input type="checkbox" ng-model="service.Checked" ng-change="selectItem(service)"/></td>
<td><a ui-sref="service({id: service.Id})">{{ service.Name }}</a></td>
<td>{{ service.StackName ? service.StackName : '-' }}</td>
@@ -108,7 +107,7 @@
</span>
</td>
<td>
<a ng-if="service.Ports && service.Ports.length > 0 && swarmManagerIP" ng-repeat="p in service.Ports" class="image-tag" ng-href="http://{{swarmManagerIP}}:{{p.PublishedPort}}" target="_blank">
<a ng-if="service.Ports && service.Ports.length > 0 && swarmManagerIP && p.PublishedPort" ng-repeat="p in service.Ports" class="image-tag" ng-href="http://{{swarmManagerIP}}:{{p.PublishedPort}}" target="_blank">
<i class="fa fa-external-link" aria-hidden="true"></i> {{ p.PublishedPort }}:{{ p.TargetPort }}
</a>
<span ng-if="!service.Ports || service.Ports.length === 0 || !swarmManagerIP" >-</span>
@@ -25,15 +25,12 @@ function ($q, $scope, $transition$, $state, Service, ServiceService, ServiceHelp
};
$scope.scaleService = function scaleService(service) {
$('#loadServicesSpinner').show();
var config = ServiceHelper.serviceToConfig(service.Model);
config.Mode.Replicated.Replicas = service.Replicas;
Service.update({ id: service.Id, version: service.Version }, config, function (data) {
$('#loadServicesSpinner').hide();
Notifications.success('Service successfully scaled', 'New replica count: ' + service.Replicas);
$state.reload();
}, function (e) {
$('#loadServicesSpinner').hide();
service.Scale = false;
service.Replicas = service.ReplicaCount;
Notifications.error('Failure', e, 'Unable to scale service');
@@ -51,17 +48,8 @@ function ($q, $scope, $transition$, $state, Service, ServiceService, ServiceHelp
};
function removeServices() {
$('#loadServicesSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadServicesSpinner').hide();
}
};
angular.forEach($scope.services, function (service) {
if (service.Checked) {
counter = counter + 1;
ServiceService.remove(service)
.then(function success(data) {
Notifications.success('Service successfully deleted');
@@ -70,9 +58,6 @@ function ($q, $scope, $transition$, $state, Service, ServiceService, ServiceHelp
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove service');
})
.finally(function final() {
complete();
});
}
});
@@ -94,7 +79,6 @@ function ($q, $scope, $transition$, $state, Service, ServiceService, ServiceHelp
}
function initView() {
$('#loadServicesSpinner').show();
$q.all({
services: Service.query({}).$promise,
tasks: Task.query({filters: {'desired-state': ['running','accepted']}}).$promise,
@@ -118,9 +102,6 @@ function ($q, $scope, $transition$, $state, Service, ServiceService, ServiceHelp
.catch(function error(err) {
$scope.services = [];
Notifications.error('Failure', err, 'Unable to retrieve services');
})
.finally(function final() {
$('#loadServicesSpinner').hide();
});
}
+42 -36
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Settings">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-title title="Settings"></rd-header-title>
<rd-header-content>Settings</rd-header-content>
</rd-header>
@@ -11,6 +9,43 @@
<rd-widget-header icon="fa-cogs" title="Application settings"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<label for="toggle_donation" class="control-label text-left">
Disable donation header
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" name="toggle_donation" ng-model="formValues.donationHeader"><i></i>
</label>
</div>
</div>
<!-- logo -->
<div class="form-group">
<div class="col-sm-12">
<label for="toggle_logo" class="control-label text-left">
Use custom logo
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" name="toggle_logo" ng-model="formValues.customLogo"><i></i>
</label>
</div>
</div>
<div ng-if="formValues.customLogo">
<div class="form-group">
<span class="col-sm-12 text-muted small">
You can specify the URL to your logo here. For an optimal display, logo dimensions should be 155px by 55px.
</span>
</div>
<div class="form-group">
<label for="logo_url" class="col-sm-1 control-label text-left">
URL
</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="settings.LogoURL" id="logo_url" placeholder="https://mycompany.com/logo.png">
</div>
</div>
</div>
<!-- !logo -->
<!-- security -->
<div class="col-sm-12 form-section-title">
Security
@@ -38,36 +73,6 @@
</div>
</div>
<!-- security -->
<!-- logo -->
<div class="col-sm-12 form-section-title">
Logo
</div>
<div class="form-group">
<div class="col-sm-12">
<label for="toggle_logo" class="control-label text-left">
Use custom logo
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" name="toggle_logo" ng-model="formValues.customLogo"><i></i>
</label>
</div>
</div>
<div ng-if="formValues.customLogo">
<div class="form-group">
<span class="col-sm-12 text-muted small">
You can specify the URL to your logo here. For an optimal display, logo dimensions should be 155px by 55px.
</span>
</div>
<div class="form-group">
<label for="logo_url" class="col-sm-1 control-label text-left">
URL
</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="settings.LogoURL" id="logo_url" placeholder="https://mycompany.com/logo.png">
</div>
</div>
</div>
<!-- !logo -->
<!-- app-templates -->
<div class="col-sm-12 form-section-title">
App Templates
@@ -112,9 +117,10 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-click="saveApplicationSettings()">Save</button>
<i id="updateSettingsSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<!-- <span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span> -->
<button type="button" class="btn btn-primary btn-sm" ng-click="saveApplicationSettings()" ng-disabled="state.actionInProgress" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Save settings</span>
<span ng-show="state.actionInProgress">Saving...</span>
</button>
</div>
</div>
<!-- !actions -->
+10 -7
View File
@@ -2,9 +2,14 @@ angular.module('settings', [])
.controller('SettingsController', ['$scope', '$state', 'Notifications', 'SettingsService', 'StateManager', 'DEFAULT_TEMPLATES_URL',
function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_TEMPLATES_URL) {
$scope.state = {
actionInProgress: false
};
$scope.formValues = {
customLogo: false,
customTemplates: false,
donationHeader: true,
externalContributions: false,
restrictBindMounts: false,
restrictPrivilegedMode: false,
@@ -41,10 +46,12 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_
settings.TemplatesURL = DEFAULT_TEMPLATES_URL;
}
settings.DisplayDonationHeader = !$scope.formValues.donationHeader;
settings.DisplayExternalContributors = !$scope.formValues.externalContributions;
settings.AllowBindMountsForRegularUsers = !$scope.formValues.restrictBindMounts;
settings.AllowPrivilegedModeForRegularUsers = !$scope.formValues.restrictPrivilegedMode;
$scope.state.actionInProgress = true;
updateSettings(settings, false);
};
@@ -54,12 +61,11 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_
}
function updateSettings(settings, resetForm) {
$('#loadingViewSpinner').show();
SettingsService.update(settings)
.then(function success(data) {
Notifications.success('Settings updated');
StateManager.updateLogo(settings.LogoURL);
StateManager.updateDonationHeader(settings.DisplayDonationHeader);
StateManager.updateExternalContributions(settings.DisplayExternalContributors);
if (resetForm) {
resetFormValues();
@@ -69,12 +75,11 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_
Notifications.error('Failure', err, 'Unable to update settings');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
$scope.state.actionInProgress = false;
});
}
function initView() {
$('#loadingViewSpinner').show();
SettingsService.settings()
.then(function success(data) {
var settings = data;
@@ -85,15 +90,13 @@ function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_
if (settings.TemplatesURL !== DEFAULT_TEMPLATES_URL) {
$scope.formValues.customTemplates = true;
}
$scope.formValues.donationHeader = !settings.DisplayDonationHeader;
$scope.formValues.externalContributions = !settings.DisplayExternalContributors;
$scope.formValues.restrictBindMounts = !settings.AllowBindMountsForRegularUsers;
$scope.formValues.restrictPrivilegedMode = !settings.AllowPrivilegedModeForRegularUsers;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve application settings');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Authentication settings">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-title title="Authentication settings"></rd-header-title>
<rd-header-content>
<a ui-sref="settings">Settings</a> &gt; Authentication
</rd-header-content>
@@ -100,8 +98,10 @@
<i class="fa fa-times red-icon" style="margin-left: 5px;" ng-if="state.failedConnectivityCheck"></i>
</label>
<div class="col-sm-9 col-lg-10">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!LDAPSettings.URL || !LDAPSettings.ReaderDN || !LDAPSettings.Password" ng-click="LDAPConnectivityCheck()">Test connectivity</button>
<i id="connectivityCheckSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.connectivityCheckInProgress || !LDAPSettings.URL || !LDAPSettings.ReaderDN || !LDAPSettings.Password" ng-click="LDAPConnectivityCheck()" button-spinner="state.connectivityCheckInProgress">
<span ng-hide="state.connectivityCheckInProgress">Test connectivity</span>
<span ng-show="state.connectivityCheckInProgress">Testing connectivity...</span>
</button>
</div>
</div>
@@ -240,9 +240,10 @@
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-click="saveSettings()">Save</button>
<i id="updateSettingsSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<!-- <span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span> -->
<button type="button" class="btn btn-primary btn-sm" ng-click="saveSettings()" ng-disabled="state.actionInProgress" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Save settings</span>
<span ng-show="state.actionInProgress">Saving...</span>
</button>
</div>
</div>
<!-- !actions -->
@@ -5,7 +5,9 @@ function ($q, $scope, Notifications, SettingsService, FileUploadService) {
$scope.state = {
successfulConnectivityCheck: false,
failedConnectivityCheck: false,
uploadInProgress: false
uploadInProgress: false,
connectivityCheckInProgress: false,
actionInProgress: false
};
$scope.formValues = {
@@ -21,13 +23,13 @@ function ($q, $scope, Notifications, SettingsService, FileUploadService) {
};
$scope.LDAPConnectivityCheck = function() {
$('#connectivityCheckSpinner').show();
var settings = $scope.settings;
var TLSCAFile = $scope.formValues.TLSCACert !== settings.LDAPSettings.TLSConfig.TLSCACert ? $scope.formValues.TLSCACert : null;
var uploadRequired = ($scope.LDAPSettings.TLSConfig.TLS || $scope.LDAPSettings.StartTLS) && !$scope.LDAPSettings.TLSConfig.TLSSkipVerify;
$scope.state.uploadInProgress = uploadRequired;
$scope.state.connectivityCheckInProgress = true;
$q.when(!uploadRequired || FileUploadService.uploadLDAPTLSFiles(TLSCAFile, null, null))
.then(function success(data) {
return SettingsService.checkLDAPConnectivity(settings);
@@ -44,18 +46,18 @@ function ($q, $scope, Notifications, SettingsService, FileUploadService) {
})
.finally(function final() {
$scope.state.uploadInProgress = false;
$('#connectivityCheckSpinner').hide();
$scope.state.connectivityCheckInProgress = false;
});
};
$scope.saveSettings = function() {
$('#updateSettingsSpinner').show();
var settings = $scope.settings;
var TLSCAFile = $scope.formValues.TLSCACert !== settings.LDAPSettings.TLSConfig.TLSCACert ? $scope.formValues.TLSCACert : null;
var uploadRequired = ($scope.LDAPSettings.TLSConfig.TLS || $scope.LDAPSettings.StartTLS) && !$scope.LDAPSettings.TLSConfig.TLSSkipVerify;
$scope.state.uploadInProgress = uploadRequired;
$scope.state.actionInProgress = true;
$q.when(!uploadRequired || FileUploadService.uploadLDAPTLSFiles(TLSCAFile, null, null))
.then(function success(data) {
return SettingsService.update(settings);
@@ -68,12 +70,11 @@ function ($q, $scope, Notifications, SettingsService, FileUploadService) {
})
.finally(function final() {
$scope.state.uploadInProgress = false;
$('#updateSettingsSpinner').hide();
$scope.state.actionInProgress = false;
});
};
function initView() {
$('#loadingViewSpinner').show();
SettingsService.settings()
.then(function success(data) {
var settings = data;
@@ -83,9 +84,6 @@ function ($q, $scope, Notifications, SettingsService, FileUploadService) {
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve application settings');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+22 -22
View File
@@ -9,75 +9,75 @@
</div>
<div class="sidebar-content">
<ul class="sidebar">
<li class="sidebar-title">
<span>Active endpoint</span>
</li>
<li class="sidebar-title"><span>Active endpoint</span></li>
<li class="sidebar-title">
<select class="select-endpoint form-control" ng-options="endpoint.Name for endpoint in endpoints" ng-model="activeEndpoint" ng-change="switchEndpoint(activeEndpoint)">
</select>
</li>
<li class="sidebar-title"><span>Endpoint actions</span></li>
<li class="sidebar-list">
<a ui-sref="dashboard" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer"></span></a>
<a ui-sref="dashboard" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="templates" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket"></span></a>
<a ui-sref="templates" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket fa-fw"></span></a>
<div class="sidebar-sublist" ng-if="toggle && displayExternalContributors && ($state.current.name === 'templates' || $state.current.name === 'templates_linuxserver')">
<a ui-sref="templates_linuxserver" ui-sref-active="active">LinuxServer.io</a>
</div>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.apiVersion >= 1.25 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<a ui-sref="stacks" ui-sref-active="active">Stacks <span class="menu-icon fa fa-th-list"></span></a>
<a ui-sref="stacks" ui-sref-active="active">Stacks <span class="menu-icon fa fa-th-list fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<a ui-sref="services" ui-sref-active="active">Services <span class="menu-icon fa fa-list-alt"></span></a>
<a ui-sref="services" ui-sref-active="active">Services <span class="menu-icon fa fa-list-alt fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="containers" ui-sref-active="active">Containers <span class="menu-icon fa fa-server"></span></a>
<a ui-sref="containers" ui-sref-active="active">Containers <span class="menu-icon fa fa-server fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="images" ui-sref-active="active">Images <span class="menu-icon fa fa-clone"></span></a>
<a ui-sref="images" ui-sref-active="active">Images <span class="menu-icon fa fa-clone fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="networks" ui-sref-active="active">Networks <span class="menu-icon fa fa-sitemap"></span></a>
<a ui-sref="networks" ui-sref-active="active">Networks <span class="menu-icon fa fa-sitemap fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="volumes" ui-sref-active="active">Volumes <span class="menu-icon fa fa-cubes"></span></a>
<a ui-sref="volumes" ui-sref-active="active">Volumes <span class="menu-icon fa fa-cubes fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.apiVersion >= 1.30 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<a ui-sref="configs" ui-sref-active="active">Configs <span class="menu-icon fa fa-file-code-o"></span></a>
<a ui-sref="configs" ui-sref-active="active">Configs <span class="menu-icon fa fa-file-code-o fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.apiVersion >= 1.25 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'">
<a ui-sref="secrets" ui-sref-active="active">Secrets <span class="menu-icon fa fa-user-secret"></span></a>
<a ui-sref="secrets" ui-sref-active="active">Secrets <span class="menu-icon fa fa-user-secret fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="(applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC') && isAdmin">
<a ui-sref="events" ui-sref-active="active">Events <span class="menu-icon fa fa-history"></span></a>
<a ui-sref="events" ui-sref-active="active">Events <span class="menu-icon fa fa-history fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || (applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER')">
<a ui-sref="swarm" ui-sref-active="active">Swarm <span class="menu-icon fa fa-object-group"></span></a>
<a ui-sref="swarm" ui-sref-active="active">Swarm <span class="menu-icon fa fa-object-group fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'">
<a ui-sref="engine" ui-sref-active="active">Engine <span class="menu-icon fa fa-th"></span></a>
<a ui-sref="engine" ui-sref-active="active">Engine <span class="menu-icon fa fa-th fa-fw"></span></a>
</li>
<li class="sidebar-title" ng-if="!applicationState.application.authentication || isAdmin || isTeamLeader">
<span>Portainer settings</span>
</li>
<li class="sidebar-list" ng-if="applicationState.application.authentication && (isAdmin || isTeamLeader)">
<a ui-sref="users" ui-sref-active="active">User management <span class="menu-icon fa fa-users"></span></a>
<a ui-sref="users" ui-sref-active="active">User management <span class="menu-icon fa fa-users fa-fw"></span></a>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'users' || $state.current.name === 'user' || $state.current.name === 'teams' || $state.current.name === 'team')">
<a ui-sref="teams" ui-sref-active="active">Teams</a>
</div>
</li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
<a ui-sref="endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug"></span></a>
<a ui-sref="endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
<a ui-sref="registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database"></span></a>
<a ui-sref="registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database fa-fw"></span></a>
</li>
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
<a ui-sref="settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs"></span></a>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'settings' || $state.current.name === 'settings_authentication') && applicationState.application.authentication && isAdmin">
<a ui-sref="settings_authentication" ui-sref-active="active">Authentication</a>
<a ui-sref="settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs fa-fw"></span></a>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'settings' || $state.current.name === 'settings_authentication' || $state.current.name === 'settings_about') && applicationState.application.authentication && isAdmin">
<a ui-sref="settings_authentication" ui-sref-active="active">Authentication</a></div>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'settings' || $state.current.name === 'settings_authentication' || $state.current.name === 'settings_about')">
<a ui-sref="settings_about" ui-sref-active="active">About</a>
</div>
</li>
</ul>
+4 -3
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="stack({id: stack.Id})" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="stacks">Stacks</a> > <a ui-sref="stack({id: stack.Id})">{{ stack.Name }}</a>
@@ -73,8 +72,10 @@
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-sm btn-primary" ng-click="deployStack()">Update stack</button>
<i id="createResourceSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
<button type="button" class="btn btn-sm btn-primary" ng-disabled="state.actionInProgress" ng-click="deployStack()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Update the stack</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
</button>
</div>
</div>
</form>
+6 -7
View File
@@ -2,14 +2,17 @@ angular.module('stack', [])
.controller('StackController', ['$q', '$scope', '$state', '$stateParams', '$document', 'StackService', 'NodeService', 'ServiceService', 'TaskService', 'ServiceHelper', 'CodeMirrorService', 'Notifications', 'FormHelper',
function ($q, $scope, $state, $stateParams, $document, StackService, NodeService, ServiceService, TaskService, ServiceHelper, CodeMirrorService, Notifications, FormHelper) {
$scope.deployStack = function () {
$('#createResourceSpinner').show();
$scope.state = {
actionInProgress: false
};
$scope.deployStack = function () {
// The codemirror editor does not work with ng-model so we need to retrieve
// the value directly from the editor.
var stackFile = $scope.editor.getValue();
var env = FormHelper.removeInvalidEnvVars($scope.stack.Env);
$scope.state.actionInProgress = true;
StackService.updateStack($scope.stack.Id, stackFile, env)
.then(function success(data) {
Notifications.success('Stack successfully deployed');
@@ -19,7 +22,7 @@ function ($q, $scope, $state, $stateParams, $document, StackService, NodeService
Notifications.error('Failure', err, 'Unable to create stack');
})
.finally(function final() {
$('#createResourceSpinner').hide();
$scope.state.actionInProgress = false;
});
};
@@ -32,7 +35,6 @@ function ($q, $scope, $state, $stateParams, $document, StackService, NodeService
};
function initView() {
$('#loadingViewSpinner').show();
var stackId = $stateParams.id;
StackService.stack(stackId)
@@ -77,9 +79,6 @@ function ($q, $scope, $state, $stateParams, $document, StackService, NodeService
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve tasks details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -2
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="stacks" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Stacks</rd-header-content>
</rd-header>
@@ -85,7 +84,7 @@
</th>
</thead>
<tbody>
<tr dir-paginate="stack in (state.filteredStacks = ( stacks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-if="state.DisplayExternalStacks || (!state.DisplayExternalStacks && !stack.External)">
<tr dir-paginate="stack in (state.filteredStacks = ( stacks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))" ng-if="state.DisplayExternalStacks || (!state.DisplayExternalStacks && !stack.External)" ng-class="{active: stacks.Checked}">
<td><input type="checkbox" ng-model="stack.Checked" ng-change="selectItem(stack)" ng-disabled="!stack.Id"/></td>
<td>
<span ng-if="stack.Id">
-19
View File
@@ -46,19 +46,8 @@ function ($scope, Notifications, Pagination, StackService, ModalService) {
};
function deleteSelectedStacks() {
$('#loadingViewSpinner').show();
var counter = 0;
var complete = function () {
counter = counter - 1;
if (counter === 0) {
$('#loadingViewSpinner').hide();
}
};
angular.forEach($scope.stacks, function (stack) {
if (stack.Checked) {
counter = counter + 1;
StackService.remove(stack)
.then(function success() {
Notifications.success('Stack deleted', stack.Name);
@@ -67,17 +56,12 @@ function ($scope, Notifications, Pagination, StackService, ModalService) {
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove stack ' + stack.Name);
})
.finally(function final() {
complete();
});
}
});
}
function initView() {
$('#loadingViewSpinner').show();
StackService.stacks(true)
.then(function success(data) {
var stacks = data;
@@ -93,9 +77,6 @@ function ($scope, Notifications, Pagination, StackService, ModalService) {
.catch(function error(err) {
$scope.stacks = [];
Notifications.error('Failure', err, 'Unable to retrieve stacks');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
-1
View File
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="swarm" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
</rd-header-title>
<rd-header-content>Swarm</rd-header-content>
</rd-header>
-5
View File
@@ -72,8 +72,6 @@ function ($q, $scope, SystemService, NodeService, Pagination, Notifications, Sta
}
function initView() {
$('#loadingViewSpinner').show();
if (StateManager.getState().application.authentication) {
var userDetails = Authentication.getUserDetails();
var isAdmin = userDetails.role === 1 ? true: false;
@@ -99,9 +97,6 @@ function ($q, $scope, SystemService, NodeService, Pagination, Notifications, Sta
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve cluster details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -3,7 +3,6 @@
<a data-toggle="tooltip" title="Refresh" ui-sref="swarm.visualizer" ui-sref-opts="{reload: true}">
<i class="fa fa-refresh" aria-hidden="true"></i>
</a>
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
</rd-header-title>
<rd-header-content>
<a ui-sref="swarm">Swarm</a> &gt; <a ui-sref="swarm.visualizer">Cluster visualizer</a>
@@ -47,7 +47,6 @@ function ($q, $scope, $document, NodeService, ServiceService, TaskService, Notif
}
function initView() {
$('#loadingViewSpinner').show();
$q.all({
nodes: NodeService.nodes(),
services: ServiceService.services(),
@@ -64,9 +63,6 @@ function ($q, $scope, $document, NodeService, ServiceService, TaskService, Notif
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to initialize cluster visualizer');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -3
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Task details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Task details"></rd-header-title>
<rd-header-content ng-if="task && service">
<a ui-sref="services">Services</a> &gt; <a ui-sref="service({id: service.Id })">{{ service.Name }}</a> &gt; {{ task.Id }}
</rd-header-content>
-4
View File
@@ -3,7 +3,6 @@ angular.module('task', [])
function ($scope, $transition$, TaskService, Service, Notifications) {
function initView() {
$('#loadingViewSpinner').show();
TaskService.task($transition$.params().id)
.then(function success(data) {
var task = data;
@@ -16,9 +15,6 @@ function ($scope, $transition$, TaskService, Service, Notifications) {
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve task details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
+1 -3
View File
@@ -1,7 +1,5 @@
<rd-header>
<rd-header-title title="Team details">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-title title="Team details"></rd-header-title>
<rd-header-content>
<a ui-sref="teams">Teams</a> &gt; <a ui-sref="team({id: team.Id})">{{ team.Name }}</a>
</rd-header-content>
-32
View File
@@ -44,7 +44,6 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
};
$scope.promoteToLeader = function(user) {
$('#loadingViewSpinner').show();
TeamMembershipService.updateMembership(user.MembershipId, user.Id, $scope.team.Id, 1)
.then(function success(data) {
$scope.leaderCount++;
@@ -53,14 +52,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update user role');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.demoteToMember = function(user) {
$('#loadingViewSpinner').show();
TeamMembershipService.updateMembership(user.MembershipId, user.Id, $scope.team.Id, 2)
.then(function success(data) {
user.TeamRole = 'Member';
@@ -69,14 +64,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update user role');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.addAllUsers = function() {
$('#loadingViewSpinner').show();
var teamMembershipQueries = [];
angular.forEach($scope.users, function (user) {
teamMembershipQueries.push(TeamMembershipService.createMembership(user.Id, $scope.team.Id, 2));
@@ -95,14 +86,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update team members');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.addUser = function(user) {
$('#loadingViewSpinner').show();
TeamMembershipService.createMembership(user.Id, $scope.team.Id, 2)
.then(function success(data) {
removeUserFromArray(user.Id, $scope.users);
@@ -113,14 +100,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update team members');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.removeAllUsers = function() {
$('#loadingViewSpinner').show();
var teamMembershipQueries = [];
angular.forEach($scope.teamMembers, function (user) {
teamMembershipQueries.push(TeamMembershipService.deleteMembership(user.MembershipId));
@@ -133,14 +116,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update team members');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
$scope.removeUser = function(user) {
$('#loadingViewSpinner').show();
TeamMembershipService.deleteMembership(user.MembershipId)
.then(function success() {
removeUserFromArray(user.Id, $scope.teamMembers);
@@ -149,14 +128,10 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update team members');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
};
function deleteTeam() {
$('#loadingViewSpinner').show();
TeamService.deleteTeam($scope.team.Id)
.then(function success(data) {
Notifications.success('Team successfully deleted', $scope.team.Name);
@@ -164,9 +139,6 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove team');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}
@@ -205,7 +177,6 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
}
function initView() {
$('#loadingViewSpinner').show();
$scope.isAdmin = Authentication.getUserDetails().role === 1 ? true: false;
$q.all({
team: TeamService.team($transition$.params().id),
@@ -219,9 +190,6 @@ function ($q, $scope, $state, $transition$, TeamService, UserService, TeamMember
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve team details');
})
.finally(function final() {
$('#loadingViewSpinner').hide();
});
}

Some files were not shown because too many files have changed in this diff Show More