提交 83668343 编写于 作者: S Sebastian Florek 提交者: GitHub

Added option to force deploy on file validation error (#1257)

上级 1eaf7653
......@@ -796,4 +796,9 @@
<translation id="545013982201122635" key="MSG_LOGS_TITLE_IN" source="/home/maciaszczykm/workspace/dashboard/.tmp/serve/app-dev.js" desc="Title part for logs card.">in</translation>
<translation id="9015703491006359663" key="MSG_LOGS_TITLE_TO" source="/home/maciaszczykm/workspace/dashboard/.tmp/serve/app-dev.js" desc="Footer part for logs card.">to</translation>
<translation id="7569032475255121462" key="MSG_NAV_MENU_INGRESS" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Ingress item in the nav.">Ingresses</translation>
<translation id="8002584284110338066" key="MSG_DEPLOY_ANYWAY_DIALOG_TITLE" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title for the dialog shown on deploy validation error.">Validation error occurred</translation>
<translation id="7984990840791623021" key="MSG_DEPLOY_ANYWAY_DIALOG_CONTENT" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Content for the dialog shown on deploy validation error.">Would you like to deploy anyway?</translation>
<translation id="4208506908994292909" key="MSG_DEPLOY_ANYWAY_DIALOG_OK" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Confirmation text for the dialog shown on deploy validation error.">Yes</translation>
<translation id="8097230988371724477" key="MSG_DEPLOY_ANYWAY_DIALOG_CANCEL" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Cancellation text for the dialog shown on deploy validation error.">No</translation>
<translation id="6885083433056530489" key="MSG_DEPLOY_DIALOG_ERROR" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Text shown on failed deploy in error dialog.">Deploying file has failed</translation>
</translationbundle>
\ No newline at end of file
......@@ -998,4 +998,9 @@
<translation id="545013982201122635" key="MSG_LOGS_TITLE_IN" source="/home/maciaszczykm/workspace/dashboard/.tmp/serve/app-dev.js" desc="Title part for logs card.">in</translation>
<translation id="9015703491006359663" key="MSG_LOGS_TITLE_TO" source="/home/maciaszczykm/workspace/dashboard/.tmp/serve/app-dev.js" desc="Footer part for logs card.">to</translation>
<translation id="7569032475255121462" key="MSG_NAV_MENU_INGRESS" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Ingress item in the nav.">Ingresses</translation>
<translation id="8002584284110338066" key="MSG_DEPLOY_ANYWAY_DIALOG_TITLE" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Title for the dialog shown on deploy validation error.">Validation error occurred</translation>
<translation id="7984990840791623021" key="MSG_DEPLOY_ANYWAY_DIALOG_CONTENT" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Content for the dialog shown on deploy validation error.">Would you like to deploy anyway?</translation>
<translation id="4208506908994292909" key="MSG_DEPLOY_ANYWAY_DIALOG_OK" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Confirmation text for the dialog shown on deploy validation error.">Yes</translation>
<translation id="8097230988371724477" key="MSG_DEPLOY_ANYWAY_DIALOG_CANCEL" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Cancellation text for the dialog shown on deploy validation error.">No</translation>
<translation id="6885083433056530489" key="MSG_DEPLOY_DIALOG_ERROR" source="/home/floreks/Projects/dashboard/.tmp/serve/app-dev.js" desc="Text shown on failed deploy in error dialog.">Deploying file has failed</translation>
</translationbundle>
\ No newline at end of file
......@@ -90,6 +90,9 @@ type AppDeploymentFromFileSpec struct {
// File content
Content string `json:"content"`
// Whether validate content before creation or not
Validate bool `json:"validate"`
}
// AppDeploymentFromFileResponse is a specification for deployment from file
......@@ -289,10 +292,8 @@ func CreateObjectFromInfoFn(info *kubectlResource.Info) (bool, error) {
// DeployAppFromFile deploys an app based on the given yaml or json file.
func DeployAppFromFile(spec *AppDeploymentFromFileSpec,
createObjectFromInfoFn createObjectFromInfo, clientConfig clientcmd.ClientConfig) (bool, error) {
const (
validate = true
emptyCacheDir = ""
)
const emptyCacheDir = ""
validate := spec.Validate
factory := cmdutil.NewFactory(clientConfig)
schema, err := factory.Validator(validate, emptyCacheDir)
......
......@@ -88,7 +88,8 @@ backendApi.AppDeploymentSpec;
/**
* @typedef {{
* name: string,
* content: string
* content: string,
* validate: boolean,
* }}
*/
backendApi.AppDeploymentFromFileSpec;
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* Displays deploy anyway confirm dialog.
*
* @param {!md.$dialog} mdDialog
* @param {string} title
* @param {string} content
* @param {string} err
* @param {string} okMsg
* @param {string} cancelMsg
* @return {!angular.$q.Promise}
*/
export default function showDeployAnywayDialog(mdDialog, title, content, err, okMsg, cancelMsg) {
let dialog = mdDialog.confirm()
.title(title)
.htmlContent(`${err}<br><br>${content}`)
.ok(okMsg)
.cancel(cancelMsg);
return mdDialog.show(dialog);
}
......@@ -14,6 +14,8 @@
import {stateName as workloads} from 'workloads/workloads_state';
import showDeployAnywayDialog from './deployanyway_dialog';
/**
* Controller for the deploy from file directive.
*
......@@ -27,9 +29,10 @@ export default class DeployFromFileController {
* TODO (cheld) Set correct type after fixing issue #159
* @param {!Object} errorDialog
* @param {!./../common/history/history_service.HistoryService} kdHistoryService
* @param {!md.$dialog} $mdDialog
* @ngInject
*/
constructor($log, $resource, $q, errorDialog, kdHistoryService) {
constructor($log, $resource, $q, errorDialog, kdHistoryService, $mdDialog) {
/**
* Initialized the template.
* @export {!angular.FormController}
......@@ -64,6 +67,9 @@ export default class DeployFromFileController {
/** @private {!./../common/history/history_service.HistoryService} */
this.kdHistoryService_ = kdHistoryService;
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
/** @export */
this.i18n = i18n;
}
......@@ -73,12 +79,13 @@ export default class DeployFromFileController {
*
* @export
*/
deploy() {
deploy(validate = true) {
if (this.form.$valid) {
/** @type {!backendApi.AppDeploymentFromFileSpec} */
let deploymentSpec = {
name: this.file.name,
content: this.file.content,
validate: validate,
};
let defer = this.q_.defer();
......@@ -98,13 +105,40 @@ export default class DeployFromFileController {
},
(err) => {
defer.reject(err); // Progress ends
this.log_.error('Error deploying application:', err);
this.errorDialog_.open('Deploying file has failed', err.data);
if (this.hasValidationError_(err.data)) {
this.handleDeployAnywayDialog_(err.data);
} else {
this.log_.error('Error deploying application:', err);
this.errorDialog_.open(this.i18n.MSG_DEPLOY_DIALOG_ERROR, err.data);
}
});
defer.promise.finally(() => { this.isDeployInProgress_ = false; });
}
}
/**
* Returns true if given error contains information about validate=false argument, false otherwise.
*
* @param {string} err
* @return {boolean}
* @private
*/
hasValidationError_(err) { return err.indexOf('validate=false') > -1; }
/**
* Handles deploy anyway dialog.
*
* @param {string} err
* @private
*/
handleDeployAnywayDialog_(err) {
showDeployAnywayDialog(
this.mdDialog_, this.i18n.MSG_DEPLOY_ANYWAY_DIALOG_TITLE,
this.i18n.MSG_DEPLOY_ANYWAY_DIALOG_CONTENT, err, this.i18n.MSG_DEPLOY_ANYWAY_DIALOG_OK,
this.i18n.MSG_DEPLOY_ANYWAY_DIALOG_CANCEL)
.then(() => { this.deploy(false); });
}
/**
* Returns true when the deploy action should be enabled.
* @return {boolean}
......@@ -127,4 +161,19 @@ const i18n = {
/** @export {string} @desc The text is put on the 'Cancel' button at the end of the YAML upload
* page. */
MSG_UPLOAD_FILE_ACTION_CANCEL: goog.getMsg('Cancel'),
/** @export {string} @desc Title for the dialog shown on deploy validation error. */
MSG_DEPLOY_ANYWAY_DIALOG_TITLE: goog.getMsg('Validation error occurred'),
/** @export {string} @desc Content for the dialog shown on deploy validation error. */
MSG_DEPLOY_ANYWAY_DIALOG_CONTENT: goog.getMsg('Would you like to deploy anyway?'),
/** @export {string} @desc Confirmation text for the dialog shown on deploy validation error. */
MSG_DEPLOY_ANYWAY_DIALOG_OK: goog.getMsg('Yes'),
/** @export {string} @desc Cancellation text for the dialog shown on deploy validation error. */
MSG_DEPLOY_ANYWAY_DIALOG_CANCEL: goog.getMsg('No'),
/** @export {string} @desc Text shown on failed deploy in error dialog. */
MSG_DEPLOY_DIALOG_ERROR: goog.getMsg('Deploying file has failed'),
};
......@@ -26,16 +26,27 @@ describe('DeployFromFile controller', () => {
let form;
/** @type {!angular.$httpBackend} */
let httpBackend;
/** @type {!md.$dialog} */
let mdDialog;
/** @type {!angular.$q} **/
let q;
/** @type {!angular.$scope} **/
let scope;
beforeEach(() => {
angular.mock.module(deployModule.name);
angular.mock.inject(($controller, $httpBackend, $resource) => {
angular.mock.inject(($controller, $httpBackend, $resource, $mdDialog, $q, $rootScope) => {
mockResource = jasmine.createSpy('$resource');
resource = $resource;
mdDialog = $mdDialog;
q = $q;
scope = $rootScope.$new();
form = {
$valid: true,
};
ctrl = $controller(DeployFromFileController, {$resource: mockResource}, {form: form});
ctrl = $controller(
DeployFromFileController, {$resource: mockResource, $mdDialog: mdDialog}, {form: form});
httpBackend = $httpBackend;
});
});
......@@ -119,4 +130,65 @@ describe('DeployFromFile controller', () => {
ctrl.cancel();
expect(ctrl.kdHistoryService_.back).toHaveBeenCalled();
});
it('should open deploy anyway dialog when validation error occurs', () => {
spyOn(ctrl, 'handleDeployAnywayDialog_');
mockResource.and.callFake(resource);
httpBackend.expectPOST('api/v1/appdeploymentfromfile')
.respond(500, `error: use --validate=false`);
// when
ctrl.deploy();
httpBackend.flush();
// then
expect(ctrl.handleDeployAnywayDialog_).toHaveBeenCalled();
});
it('should redeploy on deploy anyway', () => {
let deferred = q.defer();
spyOn(mdDialog, 'show').and.returnValue(deferred.promise);
spyOn(mdDialog, 'confirm').and.callThrough();
spyOn(ctrl, 'deploy').and.callThrough();
mockResource.and.callFake(resource);
httpBackend.expectPOST('api/v1/appdeploymentfromfile')
.respond(500, `error: use --validate=false`);
// first deploy
ctrl.deploy();
httpBackend.flush();
// dialog shown and redeploy accepted
expect(mdDialog.show).toHaveBeenCalled();
expect(mdDialog.confirm).toHaveBeenCalled();
// redeploying
deferred.resolve();
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(200, 'ok');
scope.$digest();
expect(ctrl.deploy).toHaveBeenCalledTimes(2);
});
it('should do nothing on cancel deploy anyway', () => {
let deferred = q.defer();
spyOn(mdDialog, 'show').and.returnValue(deferred.promise);
spyOn(mdDialog, 'confirm').and.callThrough();
spyOn(ctrl, 'deploy').and.callThrough();
mockResource.and.callFake(resource);
httpBackend.expectPOST('api/v1/appdeploymentfromfile')
.respond(500, `error: use --validate=false`);
// first deploy
ctrl.deploy();
httpBackend.flush();
// dialog shown and redeploy cancelled
expect(mdDialog.show).toHaveBeenCalled();
expect(mdDialog.confirm).toHaveBeenCalled();
deferred.reject();
scope.$digest();
expect(ctrl.deploy).toHaveBeenCalledTimes(1);
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册