github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/plugin/builtin/s3/s3_plugin_test.go (about) 1 package s3 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/evergreen-ci/evergreen/agent/comm" 12 "github.com/evergreen-ci/evergreen/command" 13 "github.com/evergreen-ci/evergreen/db" 14 "github.com/evergreen-ci/evergreen/model" 15 "github.com/evergreen-ci/evergreen/model/artifact" 16 "github.com/evergreen-ci/evergreen/model/host" 17 "github.com/evergreen-ci/evergreen/model/task" 18 modelutil "github.com/evergreen-ci/evergreen/model/testutil" 19 "github.com/evergreen-ci/evergreen/plugin" 20 "github.com/evergreen-ci/evergreen/plugin/plugintest" 21 "github.com/evergreen-ci/evergreen/service" 22 "github.com/evergreen-ci/evergreen/testutil" 23 "github.com/evergreen-ci/evergreen/util" 24 . "github.com/smartystreets/goconvey/convey" 25 "github.com/smartystreets/goconvey/convey/reporting" 26 ) 27 28 func init() { 29 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testutil.TestConfig())) 30 reporting.QuietMode() 31 } 32 33 func reset(t *testing.T) { 34 testutil.HandleTestingErr(db.ClearCollections(task.Collection, artifact.Collection, host.Collection), 35 t, "error clearing test collections") 36 } 37 38 func TestValidateS3BucketName(t *testing.T) { 39 Convey("When validating s3 bucket names", t, func() { 40 41 Convey("a bucket name that is too short should be rejected", func() { 42 So(validateS3BucketName("a"), ShouldNotBeNil) 43 }) 44 45 Convey("a bucket name that is too long should be rejected", func() { 46 So(validateS3BucketName(strings.Repeat("a", 64)), ShouldNotBeNil) 47 }) 48 49 Convey("a bucket name that does not start with a label should be rejected", func() { 50 So(validateS3BucketName(".xx"), ShouldNotBeNil) 51 }) 52 53 Convey("a bucket name that does not end with a label should be rejected", func() { 54 So(validateS3BucketName("xx."), ShouldNotBeNil) 55 }) 56 57 Convey("a bucket name with two consecutive periods should be rejected", func() { 58 So(validateS3BucketName("xx..xx"), ShouldNotBeNil) 59 }) 60 61 Convey("a bucket name with invalid characters should be rejected", func() { 62 So(validateS3BucketName("a*a"), ShouldNotBeNil) 63 So(validateS3BucketName("a'a"), ShouldNotBeNil) 64 So(validateS3BucketName("a?a"), ShouldNotBeNil) 65 }) 66 67 Convey("valid bucket names should be accepted", func() { 68 So(validateS3BucketName("aaa"), ShouldBeNil) 69 So(validateS3BucketName("aa.aa.aa"), ShouldBeNil) 70 So(validateS3BucketName("a12.a-a"), ShouldBeNil) 71 }) 72 }) 73 74 } 75 76 func TestS3PutAndGetSingleFile(t *testing.T) { 77 reset(t) 78 79 conf := testutil.TestConfig() 80 testutil.ConfigureIntegrationTest(t, conf, "TestS3PutAndGet") 81 82 Convey("When putting to and retrieving from an s3 bucket", t, func() { 83 84 var putCmd *S3PutCommand 85 var getCmd *S3GetCommand 86 87 testDataDir := "testdata" 88 remoteFile := "remote_mci_put_test.tgz" 89 bucket := "mci-test-uploads" 90 permissions := "private" 91 contentType := "application/x-tar" 92 displayName := "testfile" 93 94 // create the local directory to be tarred 95 localDirToTar := filepath.Join(testDataDir, "put_test") 96 localFileToTar := filepath.Join(localDirToTar, "put_test_file.txt") 97 testutil.HandleTestingErr(os.RemoveAll(localDirToTar), t, "Error removing"+ 98 " directory") 99 testutil.HandleTestingErr(os.MkdirAll(localDirToTar, 0755), t, 100 "Error creating directory") 101 randStr := util.RandomString() 102 So(ioutil.WriteFile(localFileToTar, []byte(randStr), 0755), ShouldBeNil) 103 104 // tar it 105 tarCmd := &command.LocalCommand{ 106 CmdString: "tar czf put_test.tgz put_test", 107 WorkingDirectory: testDataDir, 108 Stdout: ioutil.Discard, 109 Stderr: ioutil.Discard, 110 } 111 testutil.HandleTestingErr(tarCmd.Run(), t, "Error tarring directories") 112 tarballSource := filepath.Join(testDataDir, "put_test.tgz") 113 114 // remove the untarred version 115 testutil.HandleTestingErr(os.RemoveAll(localDirToTar), t, "Error removing directories") 116 117 Convey("the file retrieved should be the exact same as the file put", func() { 118 119 // load params into the put command 120 putCmd = &S3PutCommand{} 121 putParams := map[string]interface{}{ 122 "aws_key": conf.Providers.AWS.Id, 123 "aws_secret": conf.Providers.AWS.Secret, 124 "local_file": tarballSource, 125 "remote_file": remoteFile, 126 "bucket": bucket, 127 "permissions": permissions, 128 "content_type": contentType, 129 "display_name": displayName, 130 } 131 So(putCmd.ParseParams(putParams), ShouldBeNil) 132 _, err := putCmd.Put() 133 So(err, ShouldBeNil) 134 135 // next, get the file, untarring it 136 getCmd = &S3GetCommand{} 137 getParams := map[string]interface{}{ 138 "aws_key": conf.Providers.AWS.Id, 139 "aws_secret": conf.Providers.AWS.Secret, 140 "remote_file": remoteFile, 141 "bucket": bucket, 142 "extract_to": testDataDir, 143 "display_name": displayName, 144 } 145 So(getCmd.ParseParams(getParams), ShouldBeNil) 146 So(getCmd.Get(), ShouldBeNil) 147 // read in the file that was pulled down 148 fileContents, err := ioutil.ReadFile(localFileToTar) 149 So(err, ShouldBeNil) 150 So(string(fileContents), ShouldEqual, randStr) 151 152 // now, get the tarball without untarring it 153 getCmd = &S3GetCommand{} 154 localDlTarget := filepath.Join(testDataDir, "put_test_dl.tgz") 155 getParams = map[string]interface{}{ 156 "aws_key": conf.Providers.AWS.Id, 157 "aws_secret": conf.Providers.AWS.Secret, 158 "remote_file": remoteFile, 159 "bucket": bucket, 160 "local_file": localDlTarget, 161 "display_name": displayName, 162 } 163 So(getCmd.ParseParams(getParams), ShouldBeNil) 164 So(getCmd.Get(), ShouldBeNil) 165 exists, err := util.FileExists(localDlTarget) 166 So(err, ShouldBeNil) 167 So(exists, ShouldBeTrue) 168 169 }) 170 171 Convey("the put command should always run if there is no variants filter", func() { 172 // load params into the put command 173 putCmd = &S3PutCommand{} 174 putParams := map[string]interface{}{ 175 "aws_key": conf.Providers.AWS.Id, 176 "aws_secret": conf.Providers.AWS.Secret, 177 "local_file": tarballSource, 178 "remote_file": remoteFile, 179 "bucket": bucket, 180 "permissions": permissions, 181 "content_type": contentType, 182 "display_name": displayName, 183 } 184 So(putCmd.ParseParams(putParams), ShouldBeNil) 185 So(putCmd.shouldRunForVariant("linux-64"), ShouldBeTrue) 186 }) 187 188 Convey("put cmd with variants filter should only run if variant is in list", func() { 189 // load params into the put command 190 putCmd = &S3PutCommand{} 191 putParams := map[string]interface{}{ 192 "aws_key": conf.Providers.AWS.Id, 193 "aws_secret": conf.Providers.AWS.Secret, 194 "local_file": tarballSource, 195 "remote_file": remoteFile, 196 "bucket": bucket, 197 "permissions": permissions, 198 "content_type": contentType, 199 "display_name": displayName, 200 "build_variants": []string{"linux-64", "windows-64"}, 201 } 202 So(putCmd.ParseParams(putParams), ShouldBeNil) 203 204 So(putCmd.shouldRunForVariant("linux-64"), ShouldBeTrue) 205 So(putCmd.shouldRunForVariant("osx-108"), ShouldBeFalse) 206 }) 207 208 Convey("put cmd with 'optional' and missing file should not throw an error", func() { 209 // load params into the put command 210 putCmd = &S3PutCommand{} 211 putParams := map[string]interface{}{ 212 "aws_key": conf.Providers.AWS.Id, 213 "aws_secret": conf.Providers.AWS.Secret, 214 "optional": true, 215 "local_file": "this_file_does_not_exist.txt", 216 "remote_file": "remote_file", 217 "bucket": "test_bucket", 218 "permissions": "private", 219 "content_type": "text/plain", 220 } 221 So(putCmd.ParseParams(putParams), ShouldBeNil) 222 server, err := service.CreateTestServer(conf, nil, plugin.APIPlugins) 223 testutil.HandleTestingErr(err, t, "problem setting up server") 224 defer server.Close() 225 226 httpCom := plugintest.TestAgentCommunicator(&modelutil.TestModelData{}, server.URL) 227 pluginCom := &comm.TaskJSONCommunicator{"s3", httpCom} 228 229 So(err, ShouldBeNil) 230 231 err = putCmd.Execute(&plugintest.MockLogger{}, pluginCom, 232 &model.TaskConfig{nil, nil, nil, nil, nil, &model.BuildVariant{Name: "linux"}, &command.Expansions{}, "."}, make(chan bool)) 233 So(err, ShouldBeNil) 234 }) 235 Convey("put cmd without 'optional' and missing file should throw an error", func() { 236 // load params into the put command 237 putCmd = &S3PutCommand{} 238 putParams := map[string]interface{}{ 239 "aws_key": conf.Providers.AWS.Id, 240 "aws_secret": conf.Providers.AWS.Secret, 241 "local_file": "this_file_does_not_exist.txt", 242 "remote_file": "remote_file", 243 "bucket": "test_bucket", 244 "permissions": "private", 245 "content_type": "text/plain", 246 } 247 So(putCmd.ParseParams(putParams), ShouldBeNil) 248 server, err := service.CreateTestServer(conf, nil, plugin.APIPlugins) 249 testutil.HandleTestingErr(err, t, "problem setting up server") 250 defer server.Close() 251 httpCom := plugintest.TestAgentCommunicator(&modelutil.TestModelData{}, server.URL) 252 pluginCom := &comm.TaskJSONCommunicator{"s3", httpCom} 253 254 So(err, ShouldBeNil) 255 256 err = putCmd.Execute(&plugintest.MockLogger{}, pluginCom, 257 &model.TaskConfig{nil, nil, nil, nil, nil, &model.BuildVariant{Name: "linux"}, &command.Expansions{}, "."}, make(chan bool)) 258 So(err, ShouldNotBeNil) 259 }) 260 }) 261 } 262 263 type multiPutFileInfo struct { 264 remoteName string 265 localPath string 266 displayName string 267 data string 268 } 269 270 func TestS3PutAndGetMultiFile(t *testing.T) { 271 reset(t) 272 273 conf := testutil.TestConfig() 274 testutil.ConfigureIntegrationTest(t, conf, "TestS3PutAndGet") 275 276 Convey("When putting to and retrieving from an s3 bucket", t, func() { 277 278 testDataDir := "testdata" 279 bucket := "mci-test-uploads" 280 permissions := "private" 281 contentType := "application/x-tar" 282 remoteFilePrefix := "remote_mci_put_test-" 283 displayNamePrefix := "testfile-" 284 285 // create the local directory 286 localDir := filepath.Join(testDataDir, "put_test") 287 localFilePrefix := filepath.Join(localDir, "put_test_file") 288 defer func() { 289 testutil.HandleTestingErr(os.RemoveAll(testDataDir), t, "error removing test dir") 290 }() 291 testutil.HandleTestingErr(os.RemoveAll(localDir), t, 292 "Error removing directory") 293 testutil.HandleTestingErr(os.MkdirAll(localDir, 0755), t, 294 "Error creating directory") 295 fileInfos := []multiPutFileInfo{} 296 for i := 0; i < 5; i++ { 297 randStr := util.RandomString() 298 localPath := fmt.Sprintf("%s%d.txt", localFilePrefix, i) 299 localName := filepath.Base(localPath) 300 remoteName := fmt.Sprintf("%s%s", remoteFilePrefix, localName) 301 displayName := fmt.Sprintf("%s%s", displayNamePrefix, localName) 302 So(ioutil.WriteFile(localPath, []byte(randStr), 0755), ShouldBeNil) 303 fileInfo := multiPutFileInfo{ 304 remoteName: remoteName, 305 localPath: localPath, 306 displayName: displayName, 307 data: randStr, 308 } 309 fileInfos = append(fileInfos, fileInfo) 310 } 311 312 Convey("the files should be put without error", func() { 313 includesFilter := []string{fmt.Sprintf("%s/*.txt", localDir)} 314 315 // load params into the put command 316 putCmd := &S3PutCommand{} 317 putParams := map[string]interface{}{ 318 "aws_key": conf.Providers.AWS.Id, 319 "aws_secret": conf.Providers.AWS.Secret, 320 "local_files_include_filter": includesFilter, 321 "remote_file": remoteFilePrefix, 322 "bucket": bucket, 323 "permissions": permissions, 324 "content_type": contentType, 325 "display_name": displayNamePrefix, 326 } 327 So(putCmd.ParseParams(putParams), ShouldBeNil) 328 _, err := putCmd.Put() 329 So(err, ShouldBeNil) 330 Convey("the files should each be gotten without error", func() { 331 for _, f := range fileInfos { 332 // next, get the file 333 getCmd := &S3GetCommand{} 334 getParams := map[string]interface{}{ 335 "aws_key": conf.Providers.AWS.Id, 336 "aws_secret": conf.Providers.AWS.Secret, 337 "remote_file": f.remoteName, 338 "bucket": bucket, 339 "local_file": f.localPath, 340 "display_name": f.displayName, 341 } 342 So(getCmd.ParseParams(getParams), ShouldBeNil) 343 So(getCmd.Get(), ShouldBeNil) 344 345 // read in the file that was pulled down 346 fileContents, err := ioutil.ReadFile(f.localPath) 347 So(err, ShouldBeNil) 348 So(string(fileContents), ShouldEqual, f.data) 349 350 } 351 }) 352 }) 353 354 }) 355 } 356 357 func TestAttachResults(t *testing.T) { 358 remoteFname := "remote_file" 359 localFpath := "path/to/local_file" 360 taskId := "testTask" 361 displayName := "display_name" 362 testBucket := "testBucket" 363 Convey("When putting to an s3 bucket", t, func() { 364 reset(t) 365 366 testTask := &task.Task{ 367 Id: taskId, 368 Secret: "secret", 369 } 370 So(testTask.Insert(), ShouldBeNil) 371 372 testHost := &host.Host{ 373 Id: "hostId", 374 RunningTask: testTask.Id, 375 Secret: "secret", 376 } 377 So(testHost.Insert(), ShouldBeNil) 378 379 conf := testutil.TestConfig() 380 testutil.ConfigureIntegrationTest(t, conf, "TestAttachResults") 381 server, err := service.CreateTestServer(conf, nil, plugin.APIPlugins) 382 testutil.HandleTestingErr(err, t, "problem setting up server") 383 defer server.Close() 384 385 httpCom := plugintest.TestAgentCommunicator(&modelutil.TestModelData{Task: testTask, Host: testHost}, server.URL) 386 pluginCom := &comm.TaskJSONCommunicator{"s3", httpCom} 387 388 s3pc := S3PutCommand{ 389 LocalFile: localFpath, 390 RemoteFile: remoteFname, 391 DisplayName: displayName, 392 Visibility: "visible", 393 Bucket: testBucket, 394 } 395 Convey("and attach is multi", func() { 396 s3pc.LocalFilesIncludeFilter = []string{"one", "two"} 397 So(s3pc.AttachTaskFiles(&plugintest.MockLogger{}, pluginCom, 398 s3pc.LocalFile, s3pc.RemoteFile), ShouldBeNil) 399 Convey("files should each be added properly", func() { 400 entry, err := artifact.FindOne(artifact.ByTaskId(taskId)) 401 So(err, ShouldBeNil) 402 So(entry, ShouldNotBeNil) 403 So(len(entry.Files), ShouldEqual, 1) 404 file := entry.Files[0] 405 406 So(file.Link, ShouldEqual, fmt.Sprintf("%s%s/%s%s", s3baseURL, testBucket, remoteFname, filepath.Base(localFpath))) 407 So(file.Name, ShouldEqual, fmt.Sprintf("%s %s", displayName, filepath.Base(localFpath))) 408 }) 409 }) 410 Convey("and attaching is singular", func() { 411 s3pc.LocalFilesIncludeFilter = []string{} 412 So(s3pc.AttachTaskFiles(&plugintest.MockLogger{}, pluginCom, 413 s3pc.LocalFile, s3pc.RemoteFile), ShouldBeNil) 414 Convey("file should be added properly", func() { 415 entry, err := artifact.FindOne(artifact.ByTaskId(taskId)) 416 So(err, ShouldBeNil) 417 So(entry, ShouldNotBeNil) 418 So(len(entry.Files), ShouldEqual, 1) 419 file := entry.Files[0] 420 421 So(file.Link, ShouldEqual, fmt.Sprintf("%s%s/%s", s3baseURL, testBucket, remoteFname)) 422 So(file.Name, ShouldEqual, fmt.Sprintf("%s", displayName)) 423 }) 424 }) 425 Convey("and attach is called many times", func() { 426 filesList := make([][]string, 10) 427 for i := 0; i < 10; i++ { 428 filesList[i] = []string{fmt.Sprintf("remote%d-", i), fmt.Sprintf("local%d", i)} 429 } 430 s3pc.LocalFilesIncludeFilter = []string{"one", "two"} 431 for _, fileData := range filesList { 432 So(s3pc.AttachTaskFiles(&plugintest.MockLogger{}, pluginCom, 433 fileData[1], fileData[0]), ShouldBeNil) 434 } 435 Convey("files should each be added properly", func() { 436 entry, err := artifact.FindOne(artifact.ByTaskId(taskId)) 437 So(err, ShouldBeNil) 438 So(len(entry.Files), ShouldEqual, 10) 439 for _, file := range entry.Files { 440 entryIndex := fetchFileIndex(file.Name, displayName, filesList) 441 So(entryIndex, ShouldNotEqual, -1) 442 So(file.Link, ShouldEqual, fmt.Sprintf("%s%s/%s%s", s3baseURL, 443 testBucket, filesList[entryIndex][0], filesList[entryIndex][1])) 444 So(file.Name, ShouldEqual, fmt.Sprintf("%s %s", displayName, 445 filesList[entryIndex][1])) 446 filesList = append(filesList[:entryIndex], filesList[entryIndex+1:]...) 447 } 448 So(len(filesList), ShouldEqual, 0) 449 }) 450 }) 451 }) 452 } 453 454 func fetchFileIndex(fName, displayName string, filesList [][]string) int { 455 for index, fileData := range filesList { 456 fullDisplayName := fmt.Sprintf("%s %s", displayName, fileData[1]) 457 if fName == fullDisplayName { 458 return index 459 } 460 } 461 return -1 462 }