提交 4a09c269 编写于 作者: U urcan

issue 385 solved

上级 a58945db
...@@ -93,7 +93,7 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient) ...@@ -93,7 +93,7 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient)
deployFromFileWs.POST(""). deployFromFileWs.POST("").
To(apiHandler.handleDeployFromFile). To(apiHandler.handleDeployFromFile).
Reads(AppDeploymentFromFileSpec{}). Reads(AppDeploymentFromFileSpec{}).
Writes(AppDeploymentFromFileSpec{})) Writes(AppDeploymentFromFileResponse{}))
wsContainer.Add(deployFromFileWs) wsContainer.Add(deployFromFileWs)
replicationControllerWs := new(restful.WebService) replicationControllerWs := new(restful.WebService)
...@@ -205,12 +205,23 @@ func (apiHandler *ApiHandler) handleDeployFromFile(request *restful.Request, res ...@@ -205,12 +205,23 @@ func (apiHandler *ApiHandler) handleDeployFromFile(request *restful.Request, res
handleInternalError(response, err) handleInternalError(response, err)
return return
} }
if err := DeployAppFromFile(deploymentSpec, CreateObjectFromInfoFn); err != nil {
isDeployed, err := DeployAppFromFile(deploymentSpec, CreateObjectFromInfoFn)
if !isDeployed {
handleInternalError(response, err) handleInternalError(response, err)
return return
} }
response.WriteHeaderAndEntity(http.StatusCreated, deploymentSpec) errorMessage := ""
if err != nil {
errorMessage = err.Error()
}
response.WriteHeaderAndEntity(http.StatusCreated, AppDeploymentFromFileResponse{
Name: deploymentSpec.Name,
Content: deploymentSpec.Content,
Error: errorMessage,
})
} }
// Handles app name validation API call. // Handles app name validation API call.
......
...@@ -90,6 +90,18 @@ type AppDeploymentFromFileSpec struct { ...@@ -90,6 +90,18 @@ type AppDeploymentFromFileSpec struct {
Content string `json:"content"` Content string `json:"content"`
} }
// Specification for deployment from file
type AppDeploymentFromFileResponse struct {
// Name of the file
Name string `json:"name"`
// File content
Content string `json:"content"`
// Error after create resource
Error string `json:"error"`
}
// Port mapping for an application deployment. // Port mapping for an application deployment.
type PortMapping struct { type PortMapping struct {
// Port that will be exposed on the service. // Port that will be exposed on the service.
...@@ -264,16 +276,16 @@ func getLabelsMap(labels []Label) map[string]string { ...@@ -264,16 +276,16 @@ func getLabelsMap(labels []Label) map[string]string {
return result return result
} }
type createObjectFromInfo func(info *kubectlResource.Info) error type createObjectFromInfo func(info *kubectlResource.Info) (bool, error)
// Implementation of createObjectFromInfo // Implementation of createObjectFromInfo
func CreateObjectFromInfoFn(info *kubectlResource.Info) error { func CreateObjectFromInfoFn(info *kubectlResource.Info) (bool, error) {
_, err := kubectlResource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) createdResource, err := kubectlResource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
return err return createdResource != nil , err
} }
// Deploys an app based on the given yaml or json file. // Deploys an app based on the given yaml or json file.
func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn createObjectFromInfo) error { func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn createObjectFromInfo) (bool, error) {
const ( const (
validate = true validate = true
emptyCacheDir = "" emptyCacheDir = ""
...@@ -282,7 +294,7 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c ...@@ -282,7 +294,7 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c
factory := cmdutil.NewFactory(nil) factory := cmdutil.NewFactory(nil)
schema, err := factory.Validator(validate, emptyCacheDir) schema, err := factory.Validator(validate, emptyCacheDir)
if err != nil { if err != nil {
return err return false, err
} }
mapper, typer := factory.Object() mapper, typer := factory.Object()
...@@ -295,12 +307,16 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c ...@@ -295,12 +307,16 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c
Flatten(). Flatten().
Do() Do()
return r.Visit(func(info *kubectlResource.Info, err error) error { deployedResourcesCount:= 0
err = createObjectFromInfoFn(info)
if err != nil { err = r.Visit(func(info *kubectlResource.Info, err error) error {
return err isDeployed, err := createObjectFromInfoFn(info)
if isDeployed {
deployedResourcesCount ++
log.Printf("%s is deployed", info.Name)
} }
log.Printf("%s is deployed", info.Name) return err
return nil
}) })
return deployedResourcesCount > 0, err
} }
...@@ -84,9 +84,12 @@ export default class DeployFromFileController { ...@@ -84,9 +84,12 @@ export default class DeployFromFileController {
let resource = this.resource_('api/v1/appdeploymentfromfile'); let resource = this.resource_('api/v1/appdeploymentfromfile');
resource.save( resource.save(
deploymentSpec, deploymentSpec,
(savedConfig) => { (response) => {
defer.resolve(savedConfig); // Progress ends defer.resolve(response); // Progress ends
this.log_.info('Successfully deployed application: ', savedConfig); this.log_.info('Deployment is completed: ', response);
if (response.error.length > 0) {
this.errorDialog_.open('Deployment has been partly completed', response.error);
}
this.state_.go(replicationcontrollerliststate); this.state_.go(replicationcontrollerliststate);
}, },
(err) => { (err) => {
......
...@@ -182,23 +182,23 @@ func TestGetAvailableProtocols(t *testing.T) { ...@@ -182,23 +182,23 @@ func TestGetAvailableProtocols(t *testing.T) {
} }
func TestDeployAppFromFileWithValidContent(t *testing.T) { func TestDeployAppFromFileWithValidContent(t *testing.T) {
const (
testNamespace = "test-deployfile-namespace"
)
validContent := "{\"kind\": \"Namespace\"," + validContent := "{\"kind\": \"Namespace\"," +
"\"apiVersion\": \"v1\"," + "\"apiVersion\": \"v1\"," +
"\"metadata\": {" + "\"metadata\": {" +
"\"name\": \"" + testNamespace + "\"," + "\"name\": \"test-deployfile-namespace\"," +
"\"labels\": {\"name\": \"development\"}}}" "\"labels\": {\"name\": \"development\"}}}"
spec := &AppDeploymentFromFileSpec{ spec := &AppDeploymentFromFileSpec{
Name: "foo-name", Name: "foo-name",
Content: validContent, Content: validContent,
} }
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) error { return nil } fakeCreateObjectFromInfo := func(info *kubectlResource.Info) (bool, error) { return true, nil }
err := DeployAppFromFile(spec, fakeCreateObjectFromInfo) isDeployed, err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
if err != nil { if err != nil {
t.Errorf("Expected return value to be %#v but got %#v", nil, err) t.Errorf("Expected return value to have %#v but got %#v", nil, err)
}
if !isDeployed {
t.Errorf("Expected return value to have %#v but got %#v", true, isDeployed)
} }
} }
...@@ -207,10 +207,14 @@ func TestDeployAppFromFileWithInvalidContent(t *testing.T) { ...@@ -207,10 +207,14 @@ func TestDeployAppFromFileWithInvalidContent(t *testing.T) {
Name: "foo-name", Name: "foo-name",
Content: "foo-content-invalid", Content: "foo-content-invalid",
} }
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) error { return nil } // return is set to true to check if the validation prior to this function really works
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) (bool, error) { return true, nil }
err := DeployAppFromFile(spec, fakeCreateObjectFromInfo) isDeployed, err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
if err == nil { if err == nil {
t.Errorf("Expected return value to be an error but got %#v", nil) t.Errorf("Expected return value to have an error but got %#v", nil)
}
if isDeployed {
t.Errorf("Expected return value to have %#v but got %#v", false, isDeployed)
} }
} }
...@@ -22,7 +22,6 @@ describe('DeployFromFile controller', () => { ...@@ -22,7 +22,6 @@ describe('DeployFromFile controller', () => {
let mockResource; let mockResource;
/** @type {!angular.FormController} */ /** @type {!angular.FormController} */
let form; let form;
beforeEach(() => { beforeEach(() => {
angular.mock.module(deployModule.name); angular.mock.module(deployModule.name);
...@@ -52,4 +51,62 @@ describe('DeployFromFile controller', () => { ...@@ -52,4 +51,62 @@ describe('DeployFromFile controller', () => {
expect(resourceObject.save).toHaveBeenCalled(); expect(resourceObject.save).toHaveBeenCalled();
}); });
describe('After deploy', () => {
let httpBackend;
beforeEach(() => {
angular.mock.inject(($controller, $resource, $httpBackend) => {
ctrl = $controller(DeployFromFileController, {$resource: $resource}, {form: form});
httpBackend = $httpBackend;
});
});
it('should open error dialog and redirect the page', () => {
spyOn(ctrl.errorDialog_, 'open');
spyOn(ctrl.state_, 'go');
let response = {
name: 'foo-name',
content: 'foo-content',
error: 'service already exists',
};
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(201, response);
// when
ctrl.deploy();
httpBackend.flush();
// then
expect(ctrl.errorDialog_.open).toHaveBeenCalled();
expect(ctrl.state_.go).toHaveBeenCalled();
});
it('should redirect the page and not open error dialog', () => {
spyOn(ctrl.errorDialog_, 'open');
spyOn(ctrl.state_, 'go');
let response = {
name: 'foo-name',
content: 'foo-content',
error: '',
};
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(201, response);
// when
ctrl.deploy();
httpBackend.flush();
// then
expect(ctrl.errorDialog_.open).not.toHaveBeenCalled();
expect(ctrl.state_.go).toHaveBeenCalled();
});
it('should not redirect the page and but open error dialog', () => {
spyOn(ctrl.errorDialog_, 'open');
spyOn(ctrl.state_, 'go');
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(500, "Deployment failed");
// when
ctrl.deploy();
httpBackend.flush();
// then
expect(ctrl.errorDialog_.open).toHaveBeenCalled();
expect(ctrl.state_.go).not.toHaveBeenCalled();
});
});
}); });
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册