diff --git a/cmd/server_test.go b/cmd/server_test.go index aa86368bb..100663702 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -107,6 +107,7 @@ func runAllTests(suite *TestSuiteCommon, c *check) { suite.TestListObjectsHandler(c) suite.TestListObjectVersionsOutputOrderHandler(c) suite.TestListObjectsHandlerErrors(c) + suite.TestListObjectsV2HadoopUAHandler(c) suite.TestPutBucketErrors(c) suite.TestGetObjectLarge10MiB(c) suite.TestGetObjectLarge11MiB(c) @@ -1604,23 +1605,23 @@ func (s *TestSuiteCommon) TestListObjectsHandler(c *check) { {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"foo bar 1", "foo bar 2"}}, {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"foo+bar+1", "foo+bar+2"}}, { - getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""), + getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""), []string{ "foo bar 1", "foo bar 2", }, }, { - getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""), + getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", "", ""), []string{ "foo bar 1", "foo bar 2", fmt.Sprintf("%sminio", globalMinioDefaultOwnerID), }, }, - {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"foo+bar+1", "foo+bar+2"}}, + {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url", ""), []string{"foo+bar+1", "foo+bar+2"}}, { - getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""), + getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""), []string{ "obj2", "obj2/", @@ -1646,6 +1647,131 @@ func (s *TestSuiteCommon) TestListObjectsHandler(c *check) { } } +// TestListObjectsV2HadoopUAHandler - Test ListObjectsV2 call with max-keys=2 and Hadoop User-Agent +func (s *TestSuiteCommon) TestListObjectsV2HadoopUAHandler(c *check) { + // generate a random bucket name. + bucketName := getRandomBucketName() + // HTTP request to create the bucket. + request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), + 0, nil, s.accessKey, s.secretKey, s.signer) + c.Assert(err, nil) + + // execute the HTTP request to create bucket. + response, err := s.client.Do(request) + c.Assert(err, nil) + c.Assert(response.StatusCode, http.StatusOK) + + // enable versioning on the bucket. + enableVersioningBody := []byte("Enabled") + enableVersioningBucketRequest, err := newTestSignedRequest(http.MethodPut, getBucketVersioningConfigURL(s.endPoint, bucketName), + int64(len(enableVersioningBody)), bytes.NewReader(enableVersioningBody), s.accessKey, s.secretKey, s.signer) + c.Assert(err, nil) + // execute the HTTP request to create bucket. + response, err = s.client.Do(enableVersioningBucketRequest) + c.Assert(err, nil) + c.Assert(response.StatusCode, http.StatusOK) + + for _, objectName := range []string{"pfx/a/1.txt", "pfx/b/2.txt", "pfx/", "pfx2/c/3.txt", "pfx2/d/3.txt", "pfx1/1.txt", "pfx2/"} { + buffer := bytes.NewReader([]byte("")) + request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), + int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) + c.Assert(err, nil) + response, err = s.client.Do(request) + c.Assert(err, nil) + c.Assert(response.StatusCode, http.StatusOK) + } + for _, objectName := range []string{"pfx2/c/3.txt", "pfx2/d/3.txt", "pfx2/"} { + delRequest, err := newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, objectName), + 0, nil, s.accessKey, s.secretKey, s.signer) + c.Assert(err, nil) + response, err = s.client.Do(delRequest) + c.Assert(err, nil) + c.Assert(response.StatusCode, http.StatusNoContent) + } + testCases := []struct { + getURL string + expectedStrings []string + userAgent string + }{ + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx/a/", "2", "", "", "/"), + []string{ + "pfx/a/1.txt", + "pfx/a", + }, + "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx/a/", "2", "", "", "/"), + []string{ + "pfx/a/", + "pfx/a/1.txt", + }, + "", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"), + []string{ + "pfx2/c12/falsepfx2/c/", + }, + "", + }, + + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"), + []string{ + "pfx2/c02/false", + }, + "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"), + []string{ + "pfx2/c02/false", + }, + "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"), + []string{ + "pfx2/02/false", + }, + "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"), + []string{ + "pfx2/c/", + }, + "", + }, + { + getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"), + []string{ + "pfx2/02/false", + }, + "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy", + }, + } + for _, testCase := range testCases { + // create listObjectsV1 request with valid parameters + request, err = newTestSignedRequest(http.MethodGet, testCase.getURL, 0, nil, s.accessKey, s.secretKey, s.signer) + request.Header.Set("User-Agent", testCase.userAgent) + c.Assert(err, nil) + // execute the HTTP request. + response, err = s.client.Do(request) + c.Assert(err, nil) + c.Assert(response.StatusCode, http.StatusOK) + + getContent, err := io.ReadAll(response.Body) + c.Assert(err, nil) + + for _, expectedStr := range testCase.expectedStrings { + c.Assert(strings.Contains(string(getContent), expectedStr), true) + } + } +} + // TestListObjectVersionsHandler - checks the order of // and XML tags in a version listing func (s *TestSuiteCommon) TestListObjectVersionsOutputOrderHandler(c *check) { @@ -1747,7 +1873,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) { {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"foo bar 1", "foo bar 2", "foo  bar"}}, {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}}, { - getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""), + getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""), []string{ "foo bar 1", "foo bar 2", @@ -1756,7 +1882,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) { }, }, { - getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""), + getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", "", ""), []string{ "foo bar 1", "foo bar 2", @@ -1764,7 +1890,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) { fmt.Sprintf("%sminio", globalMinioDefaultOwnerID), }, }, - {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}}, + {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url", ""), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}}, } for _, testCase := range testCases { @@ -1811,7 +1937,7 @@ func (s *TestSuiteCommon) TestListObjectsHandlerErrors(c *check) { verifyError(c, response, "InvalidArgument", "Argument maxKeys must be an integer between 0 and 2147483647", http.StatusBadRequest) // create listObjectsV2 request with invalid value of max-keys parameter. max-keys is set to -2. - request, err = newTestSignedRequest(http.MethodGet, getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", ""), + request, err = newTestSignedRequest(http.MethodGet, getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", "", ""), 0, nil, s.accessKey, s.secretKey, s.signer) c.Assert(err, nil) // execute the HTTP request. diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index c57e3ce1e..cf4643a3a 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -1399,7 +1399,7 @@ func getListObjectVersionsURL(endPoint, bucketName, prefix, maxKeys, encodingTyp } // return URL for listing objects in the bucket with V2 API. -func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, encodingType string) string { +func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, encodingType, delimiter string) string { queryValue := url.Values{} queryValue.Set("list-type", "2") // Enables list objects V2 URL. if maxKeys != "" { @@ -1411,7 +1411,13 @@ func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, enco if encodingType != "" { queryValue.Set("encoding-type", encodingType) } - return makeTestTargetURL(endPoint, bucketName, prefix, queryValue) + if prefix != "" { + queryValue.Set("prefix", prefix) + } + if delimiter != "" { + queryValue.Set("delimiter", delimiter) + } + return makeTestTargetURL(endPoint, bucketName, "", queryValue) } // return URL for a new multipart upload.