Skip to content

Commit 4a09c26

Browse files
author
urcan
committed
issue 385 solved
1 parent a58945d commit 4a09c26

File tree

5 files changed

+120
-29
lines changed

5 files changed

+120
-29
lines changed

src/app/backend/apihandler.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient)
9393
deployFromFileWs.POST("").
9494
To(apiHandler.handleDeployFromFile).
9595
Reads(AppDeploymentFromFileSpec{}).
96-
Writes(AppDeploymentFromFileSpec{}))
96+
Writes(AppDeploymentFromFileResponse{}))
9797
wsContainer.Add(deployFromFileWs)
9898

9999
replicationControllerWs := new(restful.WebService)
@@ -205,12 +205,23 @@ func (apiHandler *ApiHandler) handleDeployFromFile(request *restful.Request, res
205205
handleInternalError(response, err)
206206
return
207207
}
208-
if err := DeployAppFromFile(deploymentSpec, CreateObjectFromInfoFn); err != nil {
208+
209+
isDeployed, err := DeployAppFromFile(deploymentSpec, CreateObjectFromInfoFn)
210+
if !isDeployed {
209211
handleInternalError(response, err)
210212
return
211213
}
212214

213-
response.WriteHeaderAndEntity(http.StatusCreated, deploymentSpec)
215+
errorMessage := ""
216+
if err != nil {
217+
errorMessage = err.Error()
218+
}
219+
220+
response.WriteHeaderAndEntity(http.StatusCreated, AppDeploymentFromFileResponse{
221+
Name: deploymentSpec.Name,
222+
Content: deploymentSpec.Content,
223+
Error: errorMessage,
224+
})
214225
}
215226

216227
// Handles app name validation API call.

src/app/backend/deploy.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ type AppDeploymentFromFileSpec struct {
9090
Content string `json:"content"`
9191
}
9292

93+
// Specification for deployment from file
94+
type AppDeploymentFromFileResponse struct {
95+
// Name of the file
96+
Name string `json:"name"`
97+
98+
// File content
99+
Content string `json:"content"`
100+
101+
// Error after create resource
102+
Error string `json:"error"`
103+
}
104+
93105
// Port mapping for an application deployment.
94106
type PortMapping struct {
95107
// Port that will be exposed on the service.
@@ -264,16 +276,16 @@ func getLabelsMap(labels []Label) map[string]string {
264276
return result
265277
}
266278

267-
type createObjectFromInfo func(info *kubectlResource.Info) error
279+
type createObjectFromInfo func(info *kubectlResource.Info) (bool, error)
268280

269281
// Implementation of createObjectFromInfo
270-
func CreateObjectFromInfoFn(info *kubectlResource.Info) error {
271-
_, err := kubectlResource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
272-
return err
282+
func CreateObjectFromInfoFn(info *kubectlResource.Info) (bool, error) {
283+
createdResource, err := kubectlResource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
284+
return createdResource != nil , err
273285
}
274286

275287
// Deploys an app based on the given yaml or json file.
276-
func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn createObjectFromInfo) error {
288+
func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn createObjectFromInfo) (bool, error) {
277289
const (
278290
validate = true
279291
emptyCacheDir = ""
@@ -282,7 +294,7 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c
282294
factory := cmdutil.NewFactory(nil)
283295
schema, err := factory.Validator(validate, emptyCacheDir)
284296
if err != nil {
285-
return err
297+
return false, err
286298
}
287299

288300
mapper, typer := factory.Object()
@@ -295,12 +307,16 @@ func DeployAppFromFile(spec *AppDeploymentFromFileSpec, createObjectFromInfoFn c
295307
Flatten().
296308
Do()
297309

298-
return r.Visit(func(info *kubectlResource.Info, err error) error {
299-
err = createObjectFromInfoFn(info)
300-
if err != nil {
301-
return err
310+
deployedResourcesCount:= 0
311+
312+
err = r.Visit(func(info *kubectlResource.Info, err error) error {
313+
isDeployed, err := createObjectFromInfoFn(info)
314+
if isDeployed {
315+
deployedResourcesCount ++
316+
log.Printf("%s is deployed", info.Name)
302317
}
303-
log.Printf("%s is deployed", info.Name)
304-
return nil
318+
return err
305319
})
320+
321+
return deployedResourcesCount > 0, err
306322
}

src/app/frontend/deploy/deployfromfile_controller.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,12 @@ export default class DeployFromFileController {
8484
let resource = this.resource_('api/v1/appdeploymentfromfile');
8585
resource.save(
8686
deploymentSpec,
87-
(savedConfig) => {
88-
defer.resolve(savedConfig); // Progress ends
89-
this.log_.info('Successfully deployed application: ', savedConfig);
87+
(response) => {
88+
defer.resolve(response); // Progress ends
89+
this.log_.info('Deployment is completed: ', response);
90+
if (response.error.length > 0) {
91+
this.errorDialog_.open('Deployment has been partly completed', response.error);
92+
}
9093
this.state_.go(replicationcontrollerliststate);
9194
},
9295
(err) => {

src/test/backend/deploy_test.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,23 @@ func TestGetAvailableProtocols(t *testing.T) {
182182
}
183183

184184
func TestDeployAppFromFileWithValidContent(t *testing.T) {
185-
const (
186-
testNamespace = "test-deployfile-namespace"
187-
)
188185
validContent := "{\"kind\": \"Namespace\"," +
189186
"\"apiVersion\": \"v1\"," +
190187
"\"metadata\": {" +
191-
"\"name\": \"" + testNamespace + "\"," +
188+
"\"name\": \"test-deployfile-namespace\"," +
192189
"\"labels\": {\"name\": \"development\"}}}"
193190
spec := &AppDeploymentFromFileSpec{
194191
Name: "foo-name",
195192
Content: validContent,
196193
}
197-
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) error { return nil }
194+
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) (bool, error) { return true, nil }
198195

199-
err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
196+
isDeployed, err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
200197
if err != nil {
201-
t.Errorf("Expected return value to be %#v but got %#v", nil, err)
198+
t.Errorf("Expected return value to have %#v but got %#v", nil, err)
199+
}
200+
if !isDeployed {
201+
t.Errorf("Expected return value to have %#v but got %#v", true, isDeployed)
202202
}
203203
}
204204

@@ -207,10 +207,14 @@ func TestDeployAppFromFileWithInvalidContent(t *testing.T) {
207207
Name: "foo-name",
208208
Content: "foo-content-invalid",
209209
}
210-
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) error { return nil }
210+
// return is set to true to check if the validation prior to this function really works
211+
fakeCreateObjectFromInfo := func(info *kubectlResource.Info) (bool, error) { return true, nil }
211212

212-
err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
213+
isDeployed, err := DeployAppFromFile(spec, fakeCreateObjectFromInfo)
213214
if err == nil {
214-
t.Errorf("Expected return value to be an error but got %#v", nil)
215+
t.Errorf("Expected return value to have an error but got %#v", nil)
216+
}
217+
if isDeployed {
218+
t.Errorf("Expected return value to have %#v but got %#v", false, isDeployed)
215219
}
216220
}

src/test/frontend/deploy/deployfromfile_controller_test.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ describe('DeployFromFile controller', () => {
2222
let mockResource;
2323
/** @type {!angular.FormController} */
2424
let form;
25-
2625
beforeEach(() => {
2726
angular.mock.module(deployModule.name);
2827

@@ -52,4 +51,62 @@ describe('DeployFromFile controller', () => {
5251
expect(resourceObject.save).toHaveBeenCalled();
5352
});
5453

54+
describe('After deploy', () => {
55+
let httpBackend;
56+
beforeEach(() => {
57+
angular.mock.inject(($controller, $resource, $httpBackend) => {
58+
ctrl = $controller(DeployFromFileController, {$resource: $resource}, {form: form});
59+
httpBackend = $httpBackend;
60+
});
61+
});
62+
63+
it('should open error dialog and redirect the page', () => {
64+
spyOn(ctrl.errorDialog_, 'open');
65+
spyOn(ctrl.state_, 'go');
66+
let response = {
67+
name: 'foo-name',
68+
content: 'foo-content',
69+
error: 'service already exists',
70+
};
71+
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(201, response);
72+
// when
73+
ctrl.deploy();
74+
httpBackend.flush();
75+
76+
// then
77+
expect(ctrl.errorDialog_.open).toHaveBeenCalled();
78+
expect(ctrl.state_.go).toHaveBeenCalled();
79+
});
80+
81+
it('should redirect the page and not open error dialog', () => {
82+
spyOn(ctrl.errorDialog_, 'open');
83+
spyOn(ctrl.state_, 'go');
84+
let response = {
85+
name: 'foo-name',
86+
content: 'foo-content',
87+
error: '',
88+
};
89+
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(201, response);
90+
// when
91+
ctrl.deploy();
92+
httpBackend.flush();
93+
94+
// then
95+
expect(ctrl.errorDialog_.open).not.toHaveBeenCalled();
96+
expect(ctrl.state_.go).toHaveBeenCalled();
97+
});
98+
99+
it('should not redirect the page and but open error dialog', () => {
100+
spyOn(ctrl.errorDialog_, 'open');
101+
spyOn(ctrl.state_, 'go');
102+
httpBackend.expectPOST('api/v1/appdeploymentfromfile').respond(500, "Deployment failed");
103+
// when
104+
ctrl.deploy();
105+
httpBackend.flush();
106+
107+
// then
108+
expect(ctrl.errorDialog_.open).toHaveBeenCalled();
109+
expect(ctrl.state_.go).not.toHaveBeenCalled();
110+
});
111+
});
55112
});

0 commit comments

Comments
 (0)