Show a warning in the superuser panel if a container restart is required, and provide a button to do so. This change also moves the restart and monitoring code into a service
This commit is contained in:
parent
c88d97cf8b
commit
6a0158d361
5 changed files with 164 additions and 47 deletions
|
@ -4955,3 +4955,24 @@ i.slack-icon {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-left: 22px;
|
padding-left: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.restart-required {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.restart-required button {
|
||||||
|
float: right;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.restart-required button i.fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.restart-required i.fa-warning {
|
||||||
|
position: absolute;
|
||||||
|
top: 24px;
|
||||||
|
left: 16px;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
|
@ -1047,12 +1047,35 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var freshLoginInProgress = [];
|
||||||
|
var reject = function(msg) {
|
||||||
|
for (var i = 0; i < freshLoginInProgress.length; ++i) {
|
||||||
|
freshLoginInProgress[i].deferred.reject({'data': {'message': msg}});
|
||||||
|
}
|
||||||
|
freshLoginInProgress = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
var retry = function() {
|
||||||
|
for (var i = 0; i < freshLoginInProgress.length; ++i) {
|
||||||
|
freshLoginInProgress[i].retry();
|
||||||
|
}
|
||||||
|
freshLoginInProgress = [];
|
||||||
|
};
|
||||||
|
|
||||||
var freshLoginFailCheck = function(opName, opArgs) {
|
var freshLoginFailCheck = function(opName, opArgs) {
|
||||||
return function(resp) {
|
return function(resp) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
// If the error is a fresh login required, show the dialog.
|
// If the error is a fresh login required, show the dialog.
|
||||||
if (resp.status == 401 && resp.data['error_type'] == 'fresh_login_required') {
|
if (resp.status == 401 && resp.data['error_type'] == 'fresh_login_required') {
|
||||||
|
var retryOperation = function() {
|
||||||
|
apiService[opName].apply(apiService, opArgs).then(function(resp) {
|
||||||
|
deferred.resolve(resp);
|
||||||
|
}, function(resp) {
|
||||||
|
deferred.reject(resp);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var verifyNow = function() {
|
var verifyNow = function() {
|
||||||
var info = {
|
var info = {
|
||||||
'password': $('#freshPassword').val()
|
'password': $('#freshPassword').val()
|
||||||
|
@ -1062,19 +1085,27 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
|
|
||||||
// Conduct the sign in of the user.
|
// Conduct the sign in of the user.
|
||||||
apiService.verifyUser(info).then(function() {
|
apiService.verifyUser(info).then(function() {
|
||||||
// On success, retry the operation. if it succeeds, then resolve the
|
// On success, retry the operations. if it succeeds, then resolve the
|
||||||
// deferred promise with the result. Otherwise, reject the same.
|
// deferred promise with the result. Otherwise, reject the same.
|
||||||
apiService[opName].apply(apiService, opArgs).then(function(resp) {
|
retry();
|
||||||
deferred.resolve(resp);
|
|
||||||
}, function(resp) {
|
|
||||||
deferred.reject(resp);
|
|
||||||
});
|
|
||||||
}, function(resp) {
|
}, function(resp) {
|
||||||
// Reject with the sign in error.
|
// Reject with the sign in error.
|
||||||
deferred.reject({'data': {'message': 'Invalid verification credentials'}});
|
reject('Invalid verification credentials');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add the retry call to the in progress list. If there is more than a single
|
||||||
|
// in progress call, we skip showing the dialog (since it has already been
|
||||||
|
// shown).
|
||||||
|
freshLoginInProgress.push({
|
||||||
|
'deferred': deferred,
|
||||||
|
'retry': retryOperation
|
||||||
|
})
|
||||||
|
|
||||||
|
if (freshLoginInProgress.length > 1) {
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
var box = bootbox.dialog({
|
var box = bootbox.dialog({
|
||||||
"message": 'It has been more than a few minutes since you last logged in, ' +
|
"message": 'It has been more than a few minutes since you last logged in, ' +
|
||||||
'so please verify your password to perform this sensitive operation:' +
|
'so please verify your password to perform this sensitive operation:' +
|
||||||
|
@ -1092,7 +1123,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
"label": "Cancel",
|
"label": "Cancel",
|
||||||
"className": "btn-default",
|
"className": "btn-default",
|
||||||
"callback": function() {
|
"callback": function() {
|
||||||
deferred.reject({'data': {'message': 'Verification canceled'}});
|
reject('Verification canceled')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1244,6 +1275,40 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
return cookieService;
|
return cookieService;
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
$provide.factory('ContainerService', ['ApiService', '$timeout',
|
||||||
|
function(ApiService, $timeout) {
|
||||||
|
var containerService = {};
|
||||||
|
containerService.restartContainer = function() {
|
||||||
|
ApiService.scShutdownContainer(null, null).then(function(resp) {
|
||||||
|
$timeout(function() {
|
||||||
|
containerService.checkStatus();
|
||||||
|
}, 2000);
|
||||||
|
}, ApiService.errorDisplay('Cannot restart container. Please report this to support.'))
|
||||||
|
};
|
||||||
|
|
||||||
|
containerService.scheduleStatusCheck = function(callback) {
|
||||||
|
$timeout(function() {
|
||||||
|
containerService.checkStatus(callback);
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
containerService.checkStatus = function(callback) {
|
||||||
|
var errorHandler = function(resp) {
|
||||||
|
if (resp.status == 404 || resp.status == 502) {
|
||||||
|
// Container has not yet come back up, so we schedule another check.
|
||||||
|
containerService.scheduleStatusCheck(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiService.errorDisplay('Cannot load status. Please report this to support')(resp);
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.scRegistryStatus(null, null).then(callback, errorHandler, /* background */true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return containerService;
|
||||||
|
}]);
|
||||||
|
|
||||||
$provide.factory('UserService', ['ApiService', 'CookieService', '$rootScope', 'Config',
|
$provide.factory('UserService', ['ApiService', 'CookieService', '$rootScope', 'Config',
|
||||||
function(ApiService, CookieService, $rootScope, Config) {
|
function(ApiService, CookieService, $rootScope, Config) {
|
||||||
var userResponse = {
|
var userResponse = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularPollChannel, CoreDialog) {
|
function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) {
|
||||||
if (!Features.SUPER_USERS) {
|
if (!Features.SUPER_USERS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,11 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.restartContainer = function(state) {
|
||||||
|
$scope.currentStep = state;
|
||||||
|
ContainerService.restartContainer();
|
||||||
|
};
|
||||||
|
|
||||||
$scope.showSuperuserPanel = function() {
|
$scope.showSuperuserPanel = function() {
|
||||||
$('#setupModal').modal('hide');
|
$('#setupModal').modal('hide');
|
||||||
window.location = '/superuser';
|
window.location = '/superuser';
|
||||||
|
@ -160,37 +165,6 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP
|
||||||
CoreDialog.fatal(title, message);
|
CoreDialog.fatal(title, message);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.restartContainer = function(restartState) {
|
|
||||||
$scope.currentStep = restartState;
|
|
||||||
ApiService.scShutdownContainer(null, null).then(function(resp) {
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.checkStatus();
|
|
||||||
}, 2000);
|
|
||||||
}, ApiService.errorDisplay('Cannot restart container. Please report this to support.'))
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.scheduleStatusCheck = function() {
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.checkStatus();
|
|
||||||
}, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.checkStatus = function() {
|
|
||||||
var errorHandler = function(resp) {
|
|
||||||
if (resp.status == 404 || resp.status == 502) {
|
|
||||||
// Container has not yet come back up, so we schedule another check.
|
|
||||||
$scope.scheduleStatusCheck();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiService.errorDisplay('Cannot load status. Please report this to support')(resp);
|
|
||||||
};
|
|
||||||
|
|
||||||
ApiService.scRegistryStatus(null, null).then(function(resp) {
|
|
||||||
$scope.currentStep = resp['status'];
|
|
||||||
}, errorHandler, /* background */true);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.parseDbUri = function(value) {
|
$scope.parseDbUri = function(value) {
|
||||||
if (!value) { return null; }
|
if (!value) { return null; }
|
||||||
|
|
||||||
|
@ -276,6 +250,12 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP
|
||||||
}, ApiService.errorDisplay('Cannot validate database. Please report this to support'));
|
}, ApiService.errorDisplay('Cannot validate database. Please report this to support'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.checkStatus = function() {
|
||||||
|
ContainerService.checkStatus(function(resp) {
|
||||||
|
$scope.currentStep = resp['status'];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Load the initial status.
|
// Load the initial status.
|
||||||
$scope.checkStatus();
|
$scope.checkStatus();
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, AngularPollChannel, CoreDialog) {
|
function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) {
|
||||||
if (!Features.SUPER_USERS) {
|
if (!Features.SUPER_USERS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService,
|
||||||
UserService.updateUserIn($scope);
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
$scope.configStatus = null;
|
$scope.configStatus = null;
|
||||||
|
$scope.requiresRestart = null;
|
||||||
$scope.logsCounter = 0;
|
$scope.logsCounter = 0;
|
||||||
$scope.newUser = {};
|
$scope.newUser = {};
|
||||||
$scope.createdUser = null;
|
$scope.createdUser = null;
|
||||||
|
@ -17,6 +18,10 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService,
|
||||||
$scope.logsScrolled = false;
|
$scope.logsScrolled = false;
|
||||||
$scope.csrf_token = window.__token;
|
$scope.csrf_token = window.__token;
|
||||||
|
|
||||||
|
$scope.configurationSaved = function() {
|
||||||
|
$scope.requiresRestart = true;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.showCreateUser = function() {
|
$scope.showCreateUser = function() {
|
||||||
$scope.createdUser = null;
|
$scope.createdUser = null;
|
||||||
$('#createUserModal').modal('show');
|
$('#createUserModal').modal('show');
|
||||||
|
@ -183,9 +188,24 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService,
|
||||||
}, ApiService.errorDisplay('Cannot send recovery email'))
|
}, ApiService.errorDisplay('Cannot send recovery email'))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.restartContainer = function() {
|
||||||
|
$('#restartingContainerModal').modal({
|
||||||
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
|
});
|
||||||
|
|
||||||
|
ContainerService.restartContainer();
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.checkStatus();
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.checkStatus = function() {
|
$scope.checkStatus = function() {
|
||||||
ApiService.scRegistryStatus(null, null).then(function(resp) {
|
ContainerService.checkStatus(function(resp) {
|
||||||
|
$('#restartingContainerModal').modal('hide');
|
||||||
$scope.configStatus = resp['status'];
|
$scope.configStatus = resp['status'];
|
||||||
|
$scope.requiresRestart = resp['requires_restart'];
|
||||||
|
|
||||||
if ($scope.configStatus == 'ready') {
|
if ($scope.configStatus == 'ready') {
|
||||||
$scope.loadUsers();
|
$scope.loadUsers();
|
||||||
} else {
|
} else {
|
||||||
|
@ -197,7 +217,7 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService,
|
||||||
var title = "Installation Incomplete";
|
var title = "Installation Incomplete";
|
||||||
CoreDialog.fatal(title, message);
|
CoreDialog.fatal(title, message);
|
||||||
}
|
}
|
||||||
}, ApiService.errorDisplay('Cannot load status. Please report this to support'), /* background */true);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load the initial status.
|
// Load the initial status.
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="cor-loader" ng-show="!configStatus"></div>
|
<div class="cor-loader" ng-show="!configStatus"></div>
|
||||||
<div class="page-content" quay-show="Features.SUPER_USERS && configStatus == 'ready'">
|
<div class="page-content" quay-show="Features.SUPER_USERS && configStatus == 'ready'">
|
||||||
|
<div ng-if="requiresRestart" class="alert alert-warning restart-required">
|
||||||
|
<button class="btn btn-warning" ng-click="restartContainer()">
|
||||||
|
<i class="fa fa-refresh"></i>Restart Now
|
||||||
|
</button>
|
||||||
|
<i class="fa fa-lg fa-warning"></i>
|
||||||
|
<div><strong>Container restart required!</strong></div>
|
||||||
|
Configuration changes have been made but the container hasn't been restarted yet.
|
||||||
|
</div>
|
||||||
<div class="cor-title">
|
<div class="cor-title">
|
||||||
<span class="cor-title-link"></span>
|
<span class="cor-title-link"></span>
|
||||||
<span class="cor-title-content">Enterprise Registry Management</span>
|
<span class="cor-title-content">Enterprise Registry Management</span>
|
||||||
|
@ -30,7 +38,8 @@
|
||||||
<div class="cor-tab-content">
|
<div class="cor-tab-content">
|
||||||
<!-- Setup tab -->
|
<!-- Setup tab -->
|
||||||
<div id="setup" class="tab-pane">
|
<div id="setup" class="tab-pane">
|
||||||
<div class="config-setup-tool" is-active="configStatus == 'ready'"></div>
|
<div class="config-setup-tool" is-active="configStatus == 'ready'"
|
||||||
|
configuration-saved="configurationSaved()"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Debugging tab -->
|
<!-- Debugging tab -->
|
||||||
|
@ -161,7 +170,7 @@
|
||||||
</div> <!-- /cor-tab-panel -->
|
</div> <!-- /cor-tab-panel -->
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="confirmDeleteUserModal">
|
<div class="co-dialog modal fade" id="confirmDeleteUserModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
@ -184,7 +193,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="createUserModal">
|
<div class="co-dialog modal fade" id="createUserModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
@ -237,7 +246,29 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="changePasswordModal">
|
<div class="co-dialog modal fade" id="restartingContainerModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">Container Currently Restarting</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" style="padding: 20px;">
|
||||||
|
<i class="fa fa-lg fa-refresh" style="margin-right: 10px;"></i>
|
||||||
|
<span class="registry-name"></span> is currently being restarted.
|
||||||
|
<br><br>
|
||||||
|
This can take several minutes. If the container does not restart on its own,
|
||||||
|
please reexecute the <code>docker run</code> command.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer working">
|
||||||
|
<span class="cor-loader-inline"></span> Waiting for container to restart...
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Modal message dialog -->
|
||||||
|
<div class="co-dialog modal fade" id="changePasswordModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
|
Reference in a new issue