提交 a5b3196b 编写于 作者: P Piotr Bryk

Merge pull request #280 from floreks/edit-replica-card-menu

Added edit replica set option to replica set card menu
......@@ -14,13 +14,14 @@
import {StateParams} from './replicasetdetail_state';
import {getReplicaSetDetailsResource} from './replicasetdetail_stateconfig';
import showUpdateReplicasDialog from './updatereplicas_dialog';
/**
* Opens replica set delete dialog.
*
* @final
*/
export class DeleteReplicaSetService {
export class ReplicaSetService {
/**
* @param {!md.$dialog} $mdDialog
* @param {!angular.$resource} $resource
......@@ -39,7 +40,7 @@ export class DeleteReplicaSetService {
}
/**
* Opens a replica set delete dialog. Returns a promise that is resolved/rejeced when user wants
* Opens a replica set delete dialog. Returns a promise that is resolved/rejected when user wants
* to delete the replica. Nothing happens when user clicks cancel on the dialog.
*
* @param {string} namespace
......@@ -64,4 +65,19 @@ export class DeleteReplicaSetService {
return deferred.promise;
}
/**
* Opens an update replica set dialog. Returns a promise that is resolved/rejected when user wants
* to update the replicas. Nothing happens when user clicks cancel on the dialog.
*
* @param {string} namespace
* @param {string} replicaSet
* @param {number} currentPods
* @param {number} desiredPods
* @returns {!angular.$q.Promise}
*/
showUpdateReplicasDialog(namespace, replicaSet, currentPods, desiredPods) {
return showUpdateReplicasDialog(
this.mdDialog_, namespace, replicaSet, currentPods, desiredPods);
}
}
......@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import showUpdateReplicasDialog from 'replicasetdetail/updatereplicas_dialog';
import {UPWARDS, DOWNWARDS} from 'replicasetdetail/sortedheader_controller';
import {stateName as replicasets} from 'replicasetlist/replicasetlist_state';
import {stateName as logsStateName} from 'logs/logs_state';
......@@ -38,26 +37,18 @@ export default class ReplicaSetDetailController {
* @param {!angular.$log} $log
* @param {!backendApi.ReplicaSetDetail} replicaSetDetail
* @param {!backendApi.Events} replicaSetEvents
* @param {!angular.Resource<!backendApi.ReplicaSetDetail>} replicaSetDetailResource
* @param {!angular.Resource<!backendApi.ReplicaSetSpec>} replicaSetSpecPodsResource
* @param {!./deletereplicaset_service.DeleteReplicaSetService} kdDeleteReplicaSetService
* @param {!./replicaset_service.ReplicaSetService} kdReplicaSetService
* @ngInject
*/
constructor(
$mdDialog, $stateParams, $state, $resource, $log, replicaSetDetail, replicaSetEvents,
replicaSetDetailResource, replicaSetSpecPodsResource, kdDeleteReplicaSetService) {
kdReplicaSetService) {
/** @export {!backendApi.ReplicaSetDetail} */
this.replicaSetDetail = replicaSetDetail;
/** @export {!backendApi.Events} */
this.replicaSetEvents = replicaSetEvents;
/** @private {!angular.Resource<!backendApi.ReplicaSetDetail>} */
this.replicaSetDetailResource_ = replicaSetDetailResource;
/** @private {!angular.Resource<!backendApi.ReplicaSetSpec>} */
this.replicaSetSpecPodsResource_ = replicaSetSpecPodsResource;
/** @export !Array<!backendApi.Event> */
this.events = replicaSetEvents.events;
......@@ -88,8 +79,8 @@ export default class ReplicaSetDetailController {
/** @private {!angular.$log} */
this.log_ = $log;
/** @private {!./deletereplicaset_service.DeleteReplicaSetService} */
this.kdDeleteReplicaSetService_ = kdDeleteReplicaSetService;
/** @private {!./replicaset_service.ReplicaSetService} */
this.kdReplicaSetService_ = kdReplicaSetService;
/**
* Name of column, that will be used for pods sorting.
......@@ -183,8 +174,9 @@ export default class ReplicaSetDetailController {
* @export
*/
handleUpdateReplicasDialog() {
showUpdateReplicasDialog(
this.mdDialog_, this.replicaSetDetail, this.updateReplicas_.bind(this));
this.kdReplicaSetService_.showUpdateReplicasDialog(
this.replicaSetDetail.namespace, this.replicaSetDetail.name,
this.replicaSetDetail.podInfo.current, this.replicaSetDetail.podInfo.desired);
}
/**
......@@ -192,8 +184,8 @@ export default class ReplicaSetDetailController {
* @export
*/
handleDeleteReplicaSetDialog() {
this.kdDeleteReplicaSetService_.showDeleteDialog(
this.stateParams_.namespace, this.stateParams_.replicaSet)
this.kdReplicaSetService_.showDeleteDialog(
this.stateParams_.namespace, this.stateParams_.replicaSet)
.then(this.onReplicaSetDeleteSuccess_.bind(this), this.onReplicaSetDeleteError_.bind(this));
}
......@@ -202,23 +194,6 @@ export default class ReplicaSetDetailController {
* or log unsuccessful operation error.
*/
/**
* Updates replicas count in replica set
* @param {number} replicasCount
* @param {function(!backendApi.ReplicaSetSpec)=} opt_callback
* @param {function(!angular.$http.Response)=} opt_errback
* @private
*/
updateReplicas_(replicasCount, opt_callback, opt_errback) {
/** @type {!backendApi.ReplicaSetSpec} */
let replicaSetSpec = {
replicas: replicasCount,
};
this.replicaSetSpecPodsResource_.save(replicaSetSpec, opt_callback, opt_errback);
// TODO(floreks): Think about refreshing data on this page after update.
}
/**
* Changes state back to replica set list after successful deletion of replica set.
* @private
......@@ -229,7 +204,6 @@ export default class ReplicaSetDetailController {
}
/**
* TODO(floreks): display message to the user
* Logs error after replica set deletion failure.
* @param {!angular.$http.Response} err
* @private
......
......@@ -18,7 +18,7 @@ import logsModule from 'logs/logs_module';
import serviceEndpointDirective from './serviceendpoint_directive';
import stateConfig from './replicasetdetail_stateconfig';
import sortedHeaderDirective from './sortedheader_directive';
import {DeleteReplicaSetService} from './deletereplicaset_service';
import {ReplicaSetService} from './replicaset_service';
/**
* Angular module for the Replica Set details view.
......@@ -38,4 +38,4 @@ export default angular.module(
.config(stateConfig)
.directive('kdServiceEndpoint', serviceEndpointDirective)
.directive('kdSortedHeader', sortedHeaderDirective)
.service('kdDeleteReplicaSetService', DeleteReplicaSetService);
.service('kdReplicaSetService', ReplicaSetService);
......@@ -52,7 +52,7 @@ export function getReplicaSetDetailsResource($stateParams, $resource) {
* @return {!angular.Resource<!backendApi.ReplicaSetSpec>}
* @ngInject
*/
function getReplicaSetSpecPodsResource($stateParams, $resource) {
export function getReplicaSetSpecPodsResource($stateParams, $resource) {
return $resource(`api/replicasets/${$stateParams.namespace}/${$stateParams.replicaSet}/update/pods`);
}
......
......@@ -17,14 +17,14 @@ limitations under the License.
<md-dialog aria-label="Create a new namespace" layout="column" layout-padding>
<md-content layout-padding>
<h4 class="md-title" >Set desired number of pods</h4>
<p>Replica set {{ctrl.replicaSetDetail.name}} will be updated to reflect the desired count.
<p>Replica set {{ctrl.replicaSet}} will be updated to reflect the desired count.
<br/>
<span class="kd-updatereplicas-pod-status">
Current status: {{ctrl.replicaSetDetail.podsCreated}}
created, {{ctrl.replicaSetDetail.podsDesired}} desired
Current status: {{ctrl.currentPods}}
created, {{ctrl.desiredPods}} desired
</span>
</p>
<form ng-submit="ctrl.updateReplicasCount()">
<form ng-submit="ctrl.updateReplicas()">
<md-input-container class="md-block">
<label>Number of pods</label>
<input type="number" min="1" ng-model="ctrl.replicas" required>
......
......@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {StateParams} from './replicasetdetail_state';
import {getReplicaSetSpecPodsResource} from './replicasetdetail_stateconfig';
/**
* Controller for the update replica set dialog.
*
......@@ -22,39 +25,63 @@ export default class UpdateReplicasDialogController {
* @param {!md.$dialog} $mdDialog
* @param {!angular.$log} $log
* @param {!ui.router.$state} $state
* @param {!backendApi.ReplicaSetDetail} replicaSetDetail
* @param {!function(number, function(!backendApi.ReplicaSetSpec),
* function(!angular.$http.Response)=)} updateReplicasFn
* @param {!angular.$resource} $resource
* @param {!angular.$q} $q
* @param {string} namespace
* @param {string} replicaSet
* @param {number} currentPods
* @param {number} desiredPods
* @ngInject
*/
constructor($mdDialog, $log, $state, replicaSetDetail, updateReplicasFn) {
constructor(
$mdDialog, $log, $state, $resource, $q, namespace, replicaSet, currentPods, desiredPods) {
/** @export {number} */
this.replicas;
/** @export {!backendApi.ReplicaSetDetail} */
this.replicaSetDetail = replicaSetDetail;
/** @export {number} */
this.currentPods = currentPods;
/** @export {number} */
this.desiredPods = desiredPods;
/** @export {string} */
this.replicaSet = replicaSet;
/** @private {string} */
this.namespace_ = namespace;
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
/** @private {!function(number, function(!backendApi.ReplicaSetSpec),
* function(!angular.$http.Response)=)} */
this.updateReplicasFn_ = updateReplicasFn;
/** @private {!angular.$log} */
this.log_ = $log;
/** @private {!ui.router.$state} */
this.state_ = $state;
/** @private {!angular.$resource} */
this.resource_ = $resource;
/** @private {!angular.$q} */
this.q_ = $q;
}
/**
* Executes callback function to update replicas count in replica set.
* Updates number of replicas in replica set.
*
* @export
*/
updateReplicasCount() {
this.updateReplicasFn_(
this.replicas, this.onUpdateReplicasSuccess_.bind(this),
updateReplicas() {
let resource = getReplicaSetSpecPodsResource(
new StateParams(this.namespace_, this.replicaSet), this.resource_);
/** @type {!backendApi.ReplicaSetSpec} */
let replicaSetSpec = {
replicas: this.replicas,
};
resource.save(
replicaSetSpec, this.onUpdateReplicasSuccess_.bind(this),
this.onUpdateReplicasError_.bind(this));
}
......@@ -69,8 +96,7 @@ export default class UpdateReplicasDialogController {
* @private
*/
onUpdateReplicasSuccess_(updatedSpec) {
this.log_.info('Successfully updated replica set.');
this.log_.info(updatedSpec);
this.log_.info(`Successfully updated replicas number to ${updatedSpec.replicas}`);
this.mdDialog_.hide();
this.state_.reload();
}
......
......@@ -17,20 +17,24 @@ import UpdateReplicasDialogController from 'replicasetdetail/updatereplicas_cont
/**
* Opens update replicas dialog.
* @param {!md.$dialog} mdDialog
* @param {!backendApi.ReplicaSetDetail} replicaSetDetail
* @param {!function(number, function(!backendApi.ReplicaSetSpec)=,
* function(!angular.$http.Response)=)} updateReplicasFn
* @param {string} namespace
* @param {string} replicaSet
* @param {number} currentPods
* @param {number} desiredPods
* @return {!angular.$q.Promise}
*/
export default function showUpdateReplicasDialog(mdDialog, replicaSetDetail, updateReplicasFn) {
export default function showUpdateReplicasDialog(
mdDialog, namespace, replicaSet, currentPods, desiredPods) {
return mdDialog.show({
controller: UpdateReplicasDialogController,
controllerAs: 'ctrl',
clickOutsideToClose: true,
templateUrl: 'replicasetdetail/updatereplicas.html',
locals: {
'updateReplicasFn': updateReplicasFn,
'replicaSetDetail': replicaSetDetail,
'namespace': namespace,
'replicaSet': replicaSet,
'currentPods': currentPods,
'desiredPods': desiredPods,
},
});
}
......@@ -25,6 +25,11 @@ limitations under the License.
View details
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="ctrl.showUpdateReplicasDialog()">
Edit pod count
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="ctrl.showDeleteDialog()">
Delete
......
......@@ -24,11 +24,10 @@ export default class ReplicaSetCardMenuController {
/**
* @param {!ui.router.$state} $state
* @param {!angular.$log} $log
* @param {!./../replicasetdetail/deletereplicaset_service.DeleteReplicaSetService}
* kdDeleteReplicaSetService
* @param {!./../replicasetdetail/replicaset_service.ReplicaSetService} kdReplicaSetService
* @ngInject
*/
constructor($state, $log, kdDeleteReplicaSetService) {
constructor($state, $log, kdReplicaSetService) {
/**
* Initialized from the scope.
* @export {!backendApi.ReplicaSet}
......@@ -41,8 +40,8 @@ export default class ReplicaSetCardMenuController {
/** @private {!angular.$log} */
this.log_ = $log;
/** @private {!./../replicasetdetail/deletereplicaset_service.DeleteReplicaSetService} */
this.kdDeleteReplicaSetService_ = kdDeleteReplicaSetService;
/** @private {!./../replicasetdetail/replicaset_service.ReplicaSetService} */
this.kdReplicaSetService_ = kdReplicaSetService;
}
/**
......@@ -63,8 +62,16 @@ export default class ReplicaSetCardMenuController {
* @export
*/
showDeleteDialog() {
this.kdDeleteReplicaSetService_.showDeleteDialog(
this.replicaSet.namespace, this.replicaSet.name)
this.kdReplicaSetService_.showDeleteDialog(this.replicaSet.namespace, this.replicaSet.name)
.then(() => this.state_.reload(), () => this.log_.error('Error deleting replica set'));
}
/**
* @export
*/
showUpdateReplicasDialog() {
this.kdReplicaSetService_.showUpdateReplicasDialog(
this.replicaSet.namespace, this.replicaSet.name, this.replicaSet.pods.current,
this.replicaSet.pods.desired);
}
}
......@@ -14,8 +14,8 @@
import replicaSetDetailModule from 'replicasetdetail/replicasetdetail_module';
describe('Delete replica set service', () => {
/** @type {!DeleteReplicaSetService} */
describe('Replica set service', () => {
/** @type {!ReplicaSetService} */
let service;
/** @type {!md.$dialog} */
let mdDialog;
......@@ -27,8 +27,8 @@ describe('Delete replica set service', () => {
beforeEach(() => {
angular.mock.module(replicaSetDetailModule.name);
angular.mock.inject((kdDeleteReplicaSetService, $mdDialog, $q, $httpBackend) => {
service = kdDeleteReplicaSetService;
angular.mock.inject((kdReplicaSetService, $mdDialog, $q, $httpBackend) => {
service = kdReplicaSetService;
mdDialog = $mdDialog;
q = $q;
httpBackend = $httpBackend;
......@@ -64,4 +64,18 @@ describe('Delete replica set service', () => {
deferred.resolve();
httpBackend.flush();
});
it('should show edit replicas dialog', () => {
// given
let namespace = 'foo-namespace';
let replicaSet = 'foo-name';
let currentPods = 3;
spyOn(mdDialog, 'show');
// when
service.showUpdateReplicasDialog(namespace, replicaSet, currentPods, currentPods);
// then
expect(mdDialog.show).toHaveBeenCalled();
});
});
......@@ -21,15 +21,17 @@ describe('Replica Set Detail controller', () => {
* @type {!ReplicaSetDetailController}
*/
let ctrl;
/** @type {!md.$dialog} */
let mdDialog;
/** @type {!replicasetdetail/replicaset_service.ReplicaSetService} */
let kdReplicaSetService;
/** @type {!angular.$q} */
let q;
beforeEach(() => {
angular.mock.module(replicaSetDetailModule.name);
angular.mock.inject(($controller, $mdDialog, $resource) => {
mdDialog = $mdDialog;
angular.mock.inject(($controller, $resource, $q, _kdReplicaSetService_) => {
q = $q;
kdReplicaSetService = _kdReplicaSetService_;
ctrl = $controller(ReplicaSetDetailController, {
replicaSetDetail: {},
replicaSetEvents: {},
......@@ -89,19 +91,36 @@ describe('Replica Set Detail controller', () => {
it('should show edit replicas dialog', () => {
// given
ctrl.replicaSetDetail = {
pods: [],
namespace: 'foo-namespace',
name: 'foo-name',
podInfo: {
current: 3,
desired: 3,
},
};
spyOn(mdDialog, 'show');
spyOn(kdReplicaSetService, 'showUpdateReplicasDialog');
// when
ctrl.handleUpdateReplicasDialog();
// then
expect(mdDialog.show).toHaveBeenCalled();
expect(kdReplicaSetService.showUpdateReplicasDialog).toHaveBeenCalled();
});
it('should create logs href', () => {
expect(ctrl.getPodLogsHref({name: 'foo-pod'}))
.toBe('#/logs/foo-namespace/foo-replicaset/foo-pod/');
});
it('should show delete replicas dialog', () => {
// given
let deferred = q.defer();
spyOn(kdReplicaSetService, 'showDeleteDialog').and.returnValue(deferred.promise);
// when
ctrl.handleDeleteReplicaSetDialog();
// then
expect(kdReplicaSetService.showDeleteDialog).toHaveBeenCalled();
});
});
......@@ -15,77 +15,79 @@
import UpdateReplicasDialogController from 'replicasetdetail/updatereplicas_controller';
import replicaSetDetailModule from 'replicasetdetail/replicasetdetail_module';
describe('Replica Set Detail controller', () => {
describe('Update Replicas controller', () => {
/**
* Replica Set Detail controller.
* @type {!ReplicaSetDetailController}
* @type {!UpdateReplicasDialogController}
*/
let ctrl;
/** @type {!md.$dialog} */
let mdDialog;
/** @type {!angular.$log} */
let log;
/** @type {!ui.router.$state} */
let state;
/** @type {!angular.$resource} */
let resource;
/** @type {!angular.$httpBackend} */
let httpBackend;
/** @type {!angular.$log} */
let log;
/**
* First parameter behavior is changed to indicate successful/failed update.
* @param {!boolean} isError
* @param {!function(!backendApi.ReplicaSetSpec)} onSuccess
* @param {!function(!angular.$http.Response)} onError
*/
let mockUpdateReplicasFn = function(isError, onSuccess, onError) {
if (isError) {
onError();
} else {
onSuccess();
}
};
/** @type {string} */
let namespaceMock = 'foo-namespace';
/** @type {string} */
let replicaSetMock = 'foo-name';
beforeEach(() => {
angular.mock.module(replicaSetDetailModule.name);
angular.mock.inject(($resource, $log, $state, $mdDialog, $controller) => {
angular.mock.inject(($log, $state, $mdDialog, $controller, $httpBackend, $resource) => {
mdDialog = $mdDialog;
log = $log;
state = $state;
resource = $resource;
httpBackend = $httpBackend;
log = $log;
ctrl = $controller(
UpdateReplicasDialogController,
{replicaSetDetail: {}, updateReplicasFn: mockUpdateReplicasFn});
ctrl = $controller(UpdateReplicasDialogController, {
$resource: resource,
namespace: namespaceMock,
replicaSet: replicaSetMock,
currentPods: 1,
desiredPods: 1,
});
});
});
it('should log success after edit replicas and hide the dialog', () => {
it('should update controller replicas to given number and log success', () => {
// given
let replicaSpec = {
replicas: 5,
};
spyOn(log, 'info');
spyOn(mdDialog, 'hide');
spyOn(state, 'reload');
// Indicates if there was error during update
ctrl.replicas = false;
httpBackend.whenPOST('api/replicasets/foo-namespace/foo-name/update/pods')
.respond(200, replicaSpec);
// when
ctrl.updateReplicasCount();
ctrl.updateReplicas();
httpBackend.flush();
// then
expect(log.info).toHaveBeenCalledWith('Successfully updated replica set.');
expect(mdDialog.hide).toHaveBeenCalled();
expect(log.info)
.toHaveBeenCalledWith(`Successfully updated replicas number to ${replicaSpec.replicas}`);
expect(state.reload).toHaveBeenCalled();
});
it('should log error after edit replicas and hide the dialog', () => {
it('should log error on failed update', () => {
// given
spyOn(log, 'error');
spyOn(mdDialog, 'hide');
// Indicates if there was error during update
ctrl.replicas = true;
httpBackend.whenPOST('api/replicasets/foo-namespace/foo-name/update/pods').respond(404);
// when
ctrl.updateReplicasCount();
ctrl.updateReplicas();
httpBackend.flush();
// then
expect(log.error).toHaveBeenCalled();
expect(mdDialog.hide).toHaveBeenCalled();
});
it('should close the dialog on cancel', () => {
......
......@@ -23,15 +23,15 @@ describe('Replica set card menu controller', () => {
let state;
/** @type {!angular.Scope} */
let scope;
/** @type {!replicasetdetail/deletereplicaset_service.DeleteReplicaSetService} */
let kdDeleteReplicaSetService;
/** @type {!replicasetdetail/replicaset_service.ReplicaSetService} */
let kdReplicaSetService;
beforeEach(() => {
angular.mock.module(replicaSetListModule.name);
angular.mock.inject(($controller, $state, _kdDeleteReplicaSetService_, $rootScope) => {
angular.mock.inject(($controller, $state, _kdReplicaSetService_, $rootScope) => {
state = $state;
kdDeleteReplicaSetService = _kdDeleteReplicaSetService_;
kdReplicaSetService = _kdReplicaSetService_;
scope = $rootScope;
ctrl = $controller(
ReplicaSetCardMenuController, null,
......@@ -67,7 +67,7 @@ describe('Replica set card menu controller', () => {
// given
let deferred = $q.defer();
spyOn(state, 'reload');
spyOn(kdDeleteReplicaSetService, 'showDeleteDialog').and.returnValue(deferred.promise);
spyOn(kdReplicaSetService, 'showDeleteDialog').and.returnValue(deferred.promise);
// when
ctrl.showDeleteDialog();
......@@ -84,7 +84,7 @@ describe('Replica set card menu controller', () => {
let deferred = $q.defer();
spyOn($log, 'error');
spyOn(state, 'reload');
spyOn(kdDeleteReplicaSetService, 'showDeleteDialog').and.returnValue(deferred.promise);
spyOn(kdReplicaSetService, 'showDeleteDialog').and.returnValue(deferred.promise);
// when
ctrl.showDeleteDialog();
......@@ -97,4 +97,23 @@ describe('Replica set card menu controller', () => {
expect(state.reload).not.toHaveBeenCalled();
expect($log.error).toHaveBeenCalled();
}));
it('should show update replicas dialog', () => {
// given
ctrl.replicaSet = {
namespace: '',
name: '',
pods: {
current: 1,
desired: 1,
},
};
spyOn(kdReplicaSetService, 'showUpdateReplicasDialog');
// when
ctrl.showUpdateReplicasDialog();
// then
expect(kdReplicaSetService.showUpdateReplicasDialog).toHaveBeenCalled();
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册