-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathdeploy_command_test.go
More file actions
498 lines (450 loc) · 21.6 KB
/
deploy_command_test.go
File metadata and controls
498 lines (450 loc) · 21.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
package commands_test
import (
"encoding/base64"
"fmt"
"io"
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
"time"
cli_fakes "github.com/cloudfoundry-incubator/multiapps-cli-plugin/cli/fakes"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/models"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/mtaclient"
mtafake "github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/mtaclient/fakes"
plugin_fakes "code.cloudfoundry.org/cli/plugin/pluginfakes"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/commands"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/testutil"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/ui"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/util"
util_fakes "github.com/cloudfoundry-incubator/multiapps-cli-plugin/util/fakes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type mockFile struct {
reader io.Reader
}
type mockFileInfo struct{}
func (mockFileInfo) Name() string {
return ""
}
func (mockFileInfo) Size() int64 {
return 0
}
func (mockFileInfo) Mode() fs.FileMode {
return ^fs.ModeCharDevice
}
func (mockFileInfo) ModTime() time.Time {
return time.Time{}
}
func (mockFileInfo) IsDir() bool {
return false
}
func (mockFileInfo) Sys() any {
return nil
}
func (f *mockFile) Stat() (fs.FileInfo, error) {
return mockFileInfo{}, nil
}
func (f *mockFile) Read(p []byte) (int, error) {
return f.reader.Read(p)
}
func (f *mockFile) Close() error {
return nil
}
var _ = Describe("DeployCommand", func() {
Describe("Execute", func() {
const org = "test-org"
const space = "test-space"
const namespace = "namespace"
const user = "test-user"
const testFilesLocation = "../test_resources/commands/"
const testArchive = "mtaArchive.mtar"
const mtaArchivePath = testFilesLocation + testArchive
const extDescriptorPath = testFilesLocation + "extDescriptor.mtaext"
var name string
var cliConnection *plugin_fakes.FakeCliConnection
// var fakeSession csrffake.FakeSessionProvider
var mtaClient *mtafake.FakeMtaClientOperations
// var restClient *restfake.FakeRestClientOperations
var testClientFactory *commands.TestClientFactory
var command *commands.DeployCommand
var oc = testutil.NewUIOutputCapturer()
var ex = testutil.NewUIExpector()
var fullMtaArchivePath, _ = filepath.Abs(mtaArchivePath)
var fullExtDescriptorPath, _ = filepath.Abs(extDescriptorPath)
var correctMtaUrl = "https://localhost/path/anatz.mtar?query=true"
var incorrectMtaUrl = "https://localhost"
var newMockFileReader = func(url string) fs.File {
return &mockFile{strings.NewReader(url)}
}
var getLinesForAbortingProcess = func() []string {
return []string{
"Executing action \"abort\" on operation test-process-id...",
"OK",
}
}
var getOutputLines = func(extDescriptor, processAborted, fromUrl bool) []string {
var lines []string
mtaNameToPrint := mtaArchivePath
if fromUrl {
mtaNameToPrint = "from url"
}
lines = append(lines,
"Deploying multi-target app archive "+mtaNameToPrint+" in org "+org+" / space "+space+" as "+user+"...")
lines = append(lines, "")
if processAborted {
lines = append(lines,
"Executing action \"abort\" on operation test-process-id...",
"OK",
)
}
if fromUrl {
lines = append(lines, "OK")
} else {
lines = append(lines,
"Uploading 1 files...",
" "+fullMtaArchivePath,
"OK")
}
if extDescriptor {
lines = append(lines,
"Uploading 1 files...",
" "+fullExtDescriptorPath,
"OK")
}
lines = append(lines,
"Test message",
"Process finished.",
"Use \"cf dmol -i 1000\" to download the logs of the process.")
return lines
}
// var getProcessParameters = func(additional bool) map[string]string {
// params := map[string]string{
// "appArchiveId": "mtaArchive.mtar",
// "failOnCrashed": "false",
// }
// if additional {
// params["deleteServices"] = "true"
// params["keepFiles"] = "true"
// params["noStart"] = "true"
// }
// return params
// }
var getFile = func(path string) (*os.File, *models.FileMetadata) {
file, _ := os.Open(path)
digest, _ := util.ComputeFileChecksum(path, "MD5")
f := testutil.GetFile(file, strings.ToUpper(digest), namespace)
return file, f
}
// var expectProcessParameters = func(expectedParameters map[string]string, processParameters map[string]interface{}) {
// for processParam, processParamValue := range processParameters {
// if expectedParameters[processParam] != "" {
// Expect(processParamValue).To(Equal(expectedParameters[processParamValue.(string)]))
// }
// }
// }
BeforeEach(func() {
ui.DisableTerminalOutput(true)
name = command.GetPluginCommand().Name
cliConnection = cli_fakes.NewFakeCliConnectionBuilder().
CurrentOrg("test-org-guid", org, nil).
CurrentSpace("test-space-guid", space, nil).
Username(user, nil).
AccessToken("bearer test-token", nil).Build()
mtaArchiveFile, mtaArchive := getFile(mtaArchivePath)
defer mtaArchiveFile.Close()
extDescriptorFile, extDescriptor := getFile(extDescriptorPath)
defer extDescriptorFile.Close()
fileUploadJobId := make(http.Header)
jobId := "one"
fileUploadJobId.Add("Location", jobId)
jobResult := mtaclient.AsyncUploadJobResult{
File: mtaArchive,
MtaId: "anatz",
}
mtaClient = mtafake.NewFakeMtaClientBuilder().
GetMtaFiles([]*models.FileMetadata{&testutil.SimpleFile}, nil).
UploadMtaFile(mtaArchiveFile, mtaArchive, nil).
UploadMtaFile(extDescriptorFile, extDescriptor, nil).
StartUploadMtaArchiveFromUrl(base64.URLEncoding.EncodeToString([]byte(correctMtaUrl)), nil, fileUploadJobId, nil).
StartUploadMtaArchiveFromUrl(base64.URLEncoding.EncodeToString([]byte(incorrectMtaUrl)), nil, nil, fmt.Errorf("connection refused")).
GetAsyncUploadJob(jobId, nil, jobResult, nil).
StartMtaOperation(testutil.OperationResult, mtaclient.ResponseHeader{Location: "operations/1000?embed=messages"}, nil).
GetMtaOperation("1000", "messages", &testutil.OperationResult, nil).
GetMtaOperationLogContent("1000", testutil.LogID, testutil.LogContent, nil).
GetMtaOperations(nil, nil, nil, []*models.Operation{&testutil.OperationResult}, nil).Build()
testClientFactory = commands.NewTestClientFactory(mtaClient, nil, nil)
command = commands.NewDeployCommand()
testTokenFactory := commands.NewTestTokenFactory(cliConnection)
deployServiceURLCalculator := util_fakes.NewDeployServiceURLFakeCalculator("deploy-service.test.ondemand.com")
command.InitializeAll(name, cliConnection, testutil.NewCustomTransport(200), testClientFactory, testTokenFactory, deployServiceURLCalculator)
command.FileUrlReadTimeout = time.Second
})
// with argument that is not a directory or MTA and a valid and unknown flag (unknown flag - error)
Context("with argument that is not a directory or MTA and a valid and unknown flag", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"x", "-l"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: -l")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// with argument that is a directory or MTA and with unknown flag (unknown flag - error)
Context("with argument that is a directory or MTA and with unknown flag", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-l"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: -l")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// with argument that is a directory or MTA and with unknown flags - some even duplicated (unknown flag - error)
Context("with argument that is a directory or MTA and with unknown flags - some even duplicated", func() {
It("should print incorrect usage (and print the duplicating flags only once), call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-nonValidFlag", "-u", "-nonValidFlag", "-nonValidFlag", "--flagInvalid"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: -nonValidFlag, --flagInvalid")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
Context("with a correct URL argument", func() {
It("should upload the MTAR from the correct URL and initiate a deploy", func() {
command.FileUrlReader = newMockFileReader(correctMtaUrl)
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, true))
})
})
Context("with an incorrect URL argument", func() {
It("should fail with the error returned from the server", func() {
command.FileUrlReader = newMockFileReader(incorrectMtaUrl)
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{}).ToInt()
})
ex.ExpectFailureOnLine(status, output, "Could not upload from url: connection refused", 3)
})
})
// wrong arguments - error
Context("with wrong arguments", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"x", "y", "z"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Wrong arguments")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// non-existing MTA archive - error
Context("with a non-existing mta archive", func() {
It("should print a file not found error and exit with a non-zero status", func() {
const fileName = "non-existing-mtar.mtar"
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{fileName}).ToInt()
})
ex.ExpectFailure(status, output, "Could not find MTA "+fileName)
})
})
// strategy flag set to "" - error
Context("with strategy flag set to blank string", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--strategy"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Parsing of arguments has failed")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// more than one unknown flag and one valid flag, which is a boolean, but its value is missing
Context("with argument that is a directory or MTA and with more than one unknown flag and one valid flag, which is a boolean, but its value is missing", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--nonValidFlag", "--apply-namespace-app-names", "-notAValidOne", "-not-a-valid-one"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: --nonValidFlag, -notAValidOne, -not-a-valid-one")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// more than one unknown flag and one valid flag, which is a boolean and its value is set
Context("with argument that is a directory or MTA and with more than one unknown flag and one valid flag, which is a boolean and its value is set", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--nonValidFlag", "--apply-namespace-app-names", "true", "-notAValidOne", "-not-a-valid-one"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: --nonValidFlag, -notAValidOne, -not-a-valid-one")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// more than one unknown flag and one valid flag that is not boolean and has a value set
Context("with argument that is a directory or MTA and with more than one unknown flag and one valid flag that is not boolean and has a value set", func() {
It("should print incorrect usage, call cf help, and exit with a non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--nonValidFlag", "--apps-stage-timeout", "8000", "-notAValidOne", "-inavalid-flag"}).ToInt()
})
ex.ExpectFailure(status, output, "Incorrect usage. Unknown or wrong flags: --nonValidFlag, -notAValidOne, -inavalid-flag")
Expect(cliConnection.CliCommandArgsForCall(0)).To(Equal([]string{"help", name}))
})
})
// strategy flag set to invalid deployment strategy - error
Context("with strategy flag set to an invalid deployment strategy", func() {
It("should print the available strategies and exit with a non-zero status", func() {
invalidStrategy := "asd"
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--strategy", invalidStrategy}).ToInt()
})
message := fmt.Sprintf("%s is not a valid deployment strategy, available strategies: %v", invalidStrategy, commands.AvailableStrategies())
ex.ExpectFailure(status, output, message)
})
})
// TODO: can't connect to backend - error
// TODO: backend returns an an error response - error
// existing MTA archive - success
Context("with an existing mta archive", func() {
It("should upload 1 file and start the deployment process", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
})
// existing MTA archive and an extension descriptor - success
Context("with an existing mta archive and an extension descriptor", func() {
It("should upload 2 files and start the deployment process", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-e", extDescriptorPath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(true, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
})
// existing MTA archive and additional options - success
Context("with an existing mta archive and some options", func() {
It("should upload 1 file and start the deployment process", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-f", "-delete-services", "-no-start", "-keep-files", "-do-not-fail-on-missing-permissions"}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(true), operation.Parameters)
})
})
// non-existing ongoing operations - success
// Context("with correct mta id from archive and no ongoing operations", func() {
// It("should not try to abort confliction operations", func() {
// testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
// GetMtaOperations(nil, nil, []*models.Operation{}, nil).
// StartMtaOperation(models.Operation{}, mtaclient.ResponseHeader{Location: "operations/1000?embed=messages"}, nil).Build()
// output, status := oc.CaptureOutputAndStatus(func() int {
// return command.Execute([]string{mtaArchivePath}).ToInt()
// })
// fmt.Println(output)
// ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false))
// // operation := mtaClient.StartMtaOperationArgsForCall(1)
// // expectProcessParameters(getProcessParameters(false), operation.Parameters)
// })
// })
Context("with namespace starting with whitespace and ongoing operations", func() {
It("should correctly detect a conflict with ongoing operation and result in error", func() {
conflictingOperation := testutil.GetOperation("test", "test-space-guid", "test", "test", "deploy", "ERROR", true)
testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
GetMtaOperations(nil, nil, nil, []*models.Operation{conflictingOperation}, nil).Build()
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "--namespace", " test "}).ToInt()
})
ex.ExpectFailureOnLine(status, output, "Deploy cancelled", 2)
})
})
// existing ongoing operations and force option not supplied - success
Context("with correct mta id from archive, with ongoing operations provided and no force option", func() {
It("should not try to abort conflicting operations", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
})
// existing ongoing operations and force option supplied - success
// Context("with correct mta id from archive, with ongoing operations provided and with force option", func() {
// It("should try to abort confliction operations", func() {
// testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
// GetMtaOperations(nil, nil, []*models.Operation{testutil.GetOperation("process-id", "test-space-guid", "test", "deploy", "ERROR", true)}, nil).Build()
// output, status := oc.CaptureOutputAndStatus(func() int {
// return command.Execute([]string{mtaArchivePath, "-f"}).ToInt()
// })
// ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, true))
// // operation := mtaClient.StartMtaOperationArgsForCall(1)
// // expectProcessParameters(getProcessParameters(false), operation.Parameters)
// })
// })
Context("with an error returned from getting ongoing operations", func() {
It("should display error and exit with non-zero status", func() {
testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
GetMtaOperations(nil, nil, nil, []*models.Operation{}, fmt.Errorf("test-error-from backend")).Build()
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath}).ToInt()
})
ex.ExpectFailureOnLine(status, output, "Could not get ongoing operation", 3)
})
})
Context("with non-valid operation id and action id provided", func() {
It("should return error and exit with non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"-i", "test", "-a", "abort"}).ToInt()
})
ex.ExpectFailure(status, output, "Multi-target app operation with ID test not found")
})
})
Context("with valid operation id and non-valid action id provided", func() {
It("should return error and exit with non-zero status", func() {
testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
GetMtaOperations(nil, nil, nil, []*models.Operation{
testutil.GetOperation("test-process-id", space, "test-mta-id", namespace, "deploy", "ERROR", true),
}, nil).Build()
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"-i", "test-process-id", "-a", "test"}).ToInt()
})
ex.ExpectFailure(status, output, "Invalid action test")
})
})
Context("with valid operation id and no action id provided", func() {
It("should return error and exit with non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"-i", "test-process-id"}).ToInt()
})
ex.ExpectFailure(status, output, "All the a i options should be specified together")
})
})
Context("with valid action id and no operation id provided", func() {
It("should return error and exit with non-zero status", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"-a", "abort"}).ToInt()
})
ex.ExpectFailure(status, output, "All the a i options should be specified together")
})
})
Context("with valid operation id and valid action id provided", func() {
It("should execute action on the process specified with process id and exit with zero status", func() {
testClientFactory.MtaClient = mtafake.NewFakeMtaClientBuilder().
GetMtaOperations(nil, nil, nil, []*models.Operation{
testutil.GetOperation("test-process-id", space, "test-mta-id", namespace, "deploy", "ERROR", true),
}, nil).
GetOperationActions("test", []string{"abort", "retry"}, nil).
ExecuteAction("test-process-id", "test", mtaclient.ResponseHeader{Location: ""}, nil).Build()
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"-i", "test-process-id", "-a", "abort"}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getLinesForAbortingProcess())
})
})
})
})