Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: gslib/tests/testcase/integration_testcase.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/tests/testcase/base.py ('k') | gslib/tests/testcase/unit_testcase.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*-
1 # Copyright 2013 Google Inc. All Rights Reserved. 2 # Copyright 2013 Google Inc. All Rights Reserved.
2 # 3 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 5 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 6 # You may obtain a copy of the License at
6 # 7 #
7 # http://www.apache.org/licenses/LICENSE-2.0 8 # http://www.apache.org/licenses/LICENSE-2.0
8 # 9 #
9 # Unless required by applicable law or agreed to in writing, software 10 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and 13 # See the License for the specific language governing permissions and
13 # limitations under the License. 14 # limitations under the License.
14
15 """Contains gsutil base integration test case class.""" 15 """Contains gsutil base integration test case class."""
16 16
17 import base 17 from __future__ import absolute_import
18 import boto 18
19 import gslib 19 from contextlib import contextmanager
20 import gslib.tests.util as util
21 import logging 20 import logging
22 import subprocess 21 import subprocess
23 import sys 22 import sys
24 23
25 from boto.exception import GSResponseError 24 import boto
26 from contextlib import contextmanager 25 from boto.exception import StorageResponseError
27 from gslib.project_id import ProjectIdHandler 26 from boto.s3.deletemarker import DeleteMarker
28 from gslib.tests.util import SetBotoConfigForTest 27 import gslib
28 from gslib.project_id import GOOG_PROJ_ID_HDR
29 from gslib.project_id import PopulateProjectId
30 from gslib.tests.testcase import base
31 import gslib.tests.util as util
32 from gslib.tests.util import ObjectToURI as suri
33 from gslib.tests.util import RUN_S3_TESTS
34 from gslib.tests.util import SetBotoConfigFileForTest
29 from gslib.tests.util import unittest 35 from gslib.tests.util import unittest
30 from gslib.util import IS_WINDOWS 36 from gslib.util import IS_WINDOWS
31 from gslib.util import Retry 37 from gslib.util import Retry
32 38
33 39
34 LOGGER = logging.getLogger('integration-test') 40 LOGGER = logging.getLogger('integration-test')
35 41
36 # Contents of boto config file that will tell gsutil not to override the real 42 # Contents of boto config file that will tell gsutil not to override the real
37 # error message with a warning about anonymous access if no credentials are 43 # error message with a warning about anonymous access if no credentials are
38 # provided in the config file. 44 # provided in the config file.
39 BOTO_CONFIG_CONTENTS_IGNORE_ANON_WARNING = """ 45 BOTO_CONFIG_CONTENTS_IGNORE_ANON_WARNING = """
40 [Tests] 46 [Tests]
41 bypass_anonymous_access_warning = True 47 bypass_anonymous_access_warning = True
42 """ 48 """
43 49
50
51 def SkipForGS(reason):
52 if not RUN_S3_TESTS:
53 return unittest.skip(reason)
54 else:
55 return lambda func: func
56
57
58 def SkipForS3(reason):
59 if RUN_S3_TESTS:
60 return unittest.skip(reason)
61 else:
62 return lambda func: func
63
64
44 @unittest.skipUnless(util.RUN_INTEGRATION_TESTS, 65 @unittest.skipUnless(util.RUN_INTEGRATION_TESTS,
45 'Not running integration tests.') 66 'Not running integration tests.')
46 class GsUtilIntegrationTestCase(base.GsUtilTestCase): 67 class GsUtilIntegrationTestCase(base.GsUtilTestCase):
47 """Base class for gsutil integration tests.""" 68 """Base class for gsutil integration tests."""
48 GROUP_TEST_ADDRESS = 'gs-discussion@googlegroups.com' 69 GROUP_TEST_ADDRESS = 'gs-discussion@googlegroups.com'
49 GROUP_TEST_ID = '00b4903a97d097895ab58ef505d535916a712215b79c3e54932c2eb502ad9 7f5' 70 GROUP_TEST_ID = (
71 '00b4903a97d097895ab58ef505d535916a712215b79c3e54932c2eb502ad97f5')
50 USER_TEST_ADDRESS = 'gs-team@google.com' 72 USER_TEST_ADDRESS = 'gs-team@google.com'
51 USER_TEST_ID = '00b4903a9703325c6bfc98992d72e75600387a64b3b6bee9ef74613ef88420 80' 73 USER_TEST_ID = (
74 '00b4903a9703325c6bfc98992d72e75600387a64b3b6bee9ef74613ef8842080')
52 DOMAIN_TEST = 'google.com' 75 DOMAIN_TEST = 'google.com'
53 # No one can create this bucket without owning the google.com domain, and we 76 # No one can create this bucket without owning the gmail.com domain, and we
54 # won't create this bucket, so it shouldn't exist. 77 # won't create this bucket, so it shouldn't exist.
55 NONEXISTENT_BUCKET_NAME = 'nonexistent-bucket-foobar.google.com' 78 # It would be nice to use google.com here but JSON API disallows
79 # 'google' in resource IDs.
80 nonexistent_bucket_name = 'nonexistent-bucket-foobar.gmail.com'
56 81
57 def setUp(self): 82 def setUp(self):
83 """Creates base configuration for integration tests."""
58 super(GsUtilIntegrationTestCase, self).setUp() 84 super(GsUtilIntegrationTestCase, self).setUp()
59 self.bucket_uris = [] 85 self.bucket_uris = []
60 86
61 # Set up API version and project ID handler. 87 # Set up API version and project ID handler.
62 self.api_version = boto.config.get_value( 88 self.api_version = boto.config.get_value(
63 'GSUtil', 'default_api_version', '1') 89 'GSUtil', 'default_api_version', '1')
64 self.proj_id_handler = ProjectIdHandler() 90
91 if util.RUN_S3_TESTS:
92 self.nonexistent_bucket_name = (
93 'nonexistentbucket-asf801rj3r9as90mfnnkjxpo02')
65 94
66 # Retry with an exponential backoff if a server error is received. This 95 # Retry with an exponential backoff if a server error is received. This
67 # ensures that we try *really* hard to clean up after ourselves. 96 # ensures that we try *really* hard to clean up after ourselves.
68 @Retry(GSResponseError, tries=6, timeout_secs=1) 97 # TODO: As long as we're still using boto to do the teardown,
98 # we decorate with boto exceptions. Eventually this should be migrated
99 # to CloudApi exceptions.
100 @Retry(StorageResponseError, tries=7, timeout_secs=1)
69 def tearDown(self): 101 def tearDown(self):
70 super(GsUtilIntegrationTestCase, self).tearDown() 102 super(GsUtilIntegrationTestCase, self).tearDown()
71 103
72 while self.bucket_uris: 104 while self.bucket_uris:
73 bucket_uri = self.bucket_uris[-1] 105 bucket_uri = self.bucket_uris[-1]
74 try: 106 try:
75 bucket_list = list(bucket_uri.list_bucket(all_versions=True)) 107 bucket_list = self._ListBucket(bucket_uri)
76 except GSResponseError, e: 108 except StorageResponseError, e:
77 # This can happen for tests of rm -r command, which for bucket-only 109 # This can happen for tests of rm -r command, which for bucket-only
78 # URIs delete the bucket at the end. 110 # URIs delete the bucket at the end.
79 if e.status == 404: 111 if e.status == 404:
80 self.bucket_uris.pop() 112 self.bucket_uris.pop()
81 continue 113 continue
82 else: 114 else:
83 raise 115 raise
84 while bucket_list: 116 while bucket_list:
85 error = None 117 error = None
86 for k in bucket_list: 118 for k in bucket_list:
87 try: 119 try:
88 k.delete() 120 if isinstance(k, DeleteMarker):
89 except GSResponseError, e: 121 bucket_uri.get_bucket().delete_key(k.name,
122 version_id=k.version_id)
123 else:
124 k.delete()
125 except StorageResponseError, e:
90 # This could happen if objects that have already been deleted are 126 # This could happen if objects that have already been deleted are
91 # still showing up in the listing due to eventual consistency. In 127 # still showing up in the listing due to eventual consistency. In
92 # that case, we continue on until we've tried to deleted every 128 # that case, we continue on until we've tried to deleted every
93 # object in the listing before raising the error on which to retry. 129 # object in the listing before raising the error on which to retry.
94 if e.status == 404: 130 if e.status == 404:
95 error = e 131 error = e
96 else: 132 else:
97 raise 133 raise
98 if error: 134 if error:
99 raise error 135 raise error # pylint: disable=raising-bad-type
100 bucket_list = list(bucket_uri.list_bucket(all_versions=True)) 136 bucket_list = self._ListBucket(bucket_uri)
101 bucket_uri.delete_bucket() 137 bucket_uri.delete_bucket()
102 self.bucket_uris.pop() 138 self.bucket_uris.pop()
103 139
140 def _ListBucket(self, bucket_uri):
141 if bucket_uri.scheme == 's3':
142 # storage_uri will omit delete markers from bucket listings, but
143 # these must be deleted before we can remove an S3 bucket.
144 return list(v for v in bucket_uri.get_bucket().list_versions())
145 return list(bucket_uri.list_bucket(all_versions=True))
146
147 def AssertNObjectsInBucket(self, bucket_uri, num_objects, versioned=False):
148 """Checks (with retries) that 'ls bucket_uri/**' returns num_objects.
149
150 This is a common test pattern to deal with eventual listing consistency for
151 tests that rely on a set of objects to be listed.
152
153 Args:
154 bucket_uri: storage_uri for the bucket.
155 num_objects: number of objects expected in the bucket.
156 versioned: If True, perform a versioned listing.
157
158 Raises:
159 AssertionError if number of objects does not match expected value.
160
161 Returns:
162 Listing split across lines.
163 """
164 # Use @Retry as hedge against bucket listing eventual consistency.
165 @Retry(AssertionError, tries=3, timeout_secs=1)
166 def _Check1():
167 command = ['ls', '-a'] if versioned else ['ls']
168 b_uri = [suri(bucket_uri) + '/**'] if num_objects else [suri(bucket_uri)]
169 listing = self.RunGsUtil(command + b_uri, return_stdout=True).split('\n')
170 # num_objects + one trailing newline.
171 self.assertEquals(len(listing), num_objects + 1)
172 return listing
173 return _Check1()
174
104 def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None, 175 def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None,
105 provider=None): 176 provider=None):
106 """Creates a test bucket. 177 """Creates a test bucket.
107 178
108 The bucket and all of its contents will be deleted after the test. 179 The bucket and all of its contents will be deleted after the test.
109 180
110 Args: 181 Args:
111 bucket_name: Create the bucket with this name. If not provided, a 182 bucket_name: Create the bucket with this name. If not provided, a
112 temporary test bucket name is constructed. 183 temporary test bucket name is constructed.
113 test_objects: The number of objects that should be placed in the bucket. 184 test_objects: The number of objects that should be placed in the bucket.
114 Defaults to 0. 185 Defaults to 0.
115 storage_class: storage class to use. If not provided we us standard. 186 storage_class: storage class to use. If not provided we us standard.
116 provider: Provider to use - either "gs" (the default) or "s3". 187 provider: Provider to use - either "gs" (the default) or "s3".
117 188
118 Returns: 189 Returns:
119 StorageUri for the created bucket. 190 StorageUri for the created bucket.
120 """ 191 """
121 if not provider: 192 if not provider:
122 provider = 'gs' 193 provider = self.default_provider
123 bucket_name = bucket_name or self.MakeTempName('bucket') 194 bucket_name = bucket_name or self.MakeTempName('bucket')
124 195
125 bucket_uri = boto.storage_uri('%s://%s' % (provider, bucket_name.lower()), 196 bucket_uri = boto.storage_uri('%s://%s' % (provider, bucket_name.lower()),
126 suppress_consec_slashes=False) 197 suppress_consec_slashes=False)
127 198
128 if provider == 'gs': 199 if provider == 'gs':
129 # Apply API version and project ID headers if necessary. 200 # Apply API version and project ID headers if necessary.
130 headers = {'x-goog-api-version': self.api_version} 201 headers = {'x-goog-api-version': self.api_version}
131 self.proj_id_handler.FillInProjectHeaderIfNeeded( 202 headers[GOOG_PROJ_ID_HDR] = PopulateProjectId()
132 'test', bucket_uri, headers)
133 else: 203 else:
134 headers = {} 204 headers = {}
135 205
136 bucket_uri.create_bucket(storage_class=storage_class, headers=headers) 206 # Parallel tests can easily run into bucket creation quotas.
207 # Retry with exponential backoff so that we create them as fast as we
208 # reasonably can.
209 @Retry(StorageResponseError, tries=7, timeout_secs=1)
210 def _CreateBucketWithExponentialBackoff():
211 bucket_uri.create_bucket(storage_class=storage_class, headers=headers)
212
213 _CreateBucketWithExponentialBackoff()
137 self.bucket_uris.append(bucket_uri) 214 self.bucket_uris.append(bucket_uri)
138 for i in range(test_objects): 215 for i in range(test_objects):
139 self.CreateObject(bucket_uri=bucket_uri, 216 self.CreateObject(bucket_uri=bucket_uri,
140 object_name=self.MakeTempName('obj'), 217 object_name=self.MakeTempName('obj'),
141 contents='test %d' % i) 218 contents='test %d' % i)
142 return bucket_uri 219 return bucket_uri
143 220
144 def CreateVersionedBucket(self, bucket_name=None, test_objects=0): 221 def CreateVersionedBucket(self, bucket_name=None, test_objects=0):
145 """Creates a versioned test bucket. 222 """Creates a versioned test bucket.
146 223
(...skipping 10 matching lines...) Expand all
157 """ 234 """
158 bucket_uri = self.CreateBucket(bucket_name=bucket_name, 235 bucket_uri = self.CreateBucket(bucket_name=bucket_name,
159 test_objects=test_objects) 236 test_objects=test_objects)
160 bucket_uri.configure_versioning(True) 237 bucket_uri.configure_versioning(True)
161 return bucket_uri 238 return bucket_uri
162 239
163 def CreateObject(self, bucket_uri=None, object_name=None, contents=None): 240 def CreateObject(self, bucket_uri=None, object_name=None, contents=None):
164 """Creates a test object. 241 """Creates a test object.
165 242
166 Args: 243 Args:
167 bucket: The URI of the bucket to place the object in. If not specified, a 244 bucket_uri: The URI of the bucket to place the object in. If not
168 new temporary bucket is created. 245 specified, a new temporary bucket is created.
169 object_name: The name to use for the object. If not specified, a temporary 246 object_name: The name to use for the object. If not specified, a temporary
170 test object name is constructed. 247 test object name is constructed.
171 contents: The contents to write to the object. If not specified, the key 248 contents: The contents to write to the object. If not specified, the key
172 is not written to, which means that it isn't actually created 249 is not written to, which means that it isn't actually created
173 yet on the server. 250 yet on the server.
174 251
175 Returns: 252 Returns:
176 A StorageUri for the created object. 253 A StorageUri for the created object.
177 """ 254 """
178 bucket_uri = bucket_uri or self.CreateBucket() 255 bucket_uri = bucket_uri or self.CreateBucket()
(...skipping 14 matching lines...) Expand all
193 return_stderr: If True, the standard error of the command is returned. 270 return_stderr: If True, the standard error of the command is returned.
194 expected_status: The expected return code. If not specified, defaults to 271 expected_status: The expected return code. If not specified, defaults to
195 0. If the return code is a different value, an exception 272 0. If the return code is a different value, an exception
196 is raised. 273 is raised.
197 stdin: A string of data to pipe to the process as standard input. 274 stdin: A string of data to pipe to the process as standard input.
198 275
199 Returns: 276 Returns:
200 A tuple containing the desired return values specified by the return_* 277 A tuple containing the desired return values specified by the return_*
201 arguments. 278 arguments.
202 """ 279 """
203 cmd = [gslib.GSUTIL_PATH] + cmd 280 cmd = [gslib.GSUTIL_PATH] + ['--testexceptiontraces'] + cmd
204 if IS_WINDOWS: 281 if IS_WINDOWS:
205 cmd = [sys.executable] + cmd 282 cmd = [sys.executable] + cmd
206 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 283 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
207 stdin=subprocess.PIPE) 284 stdin=subprocess.PIPE)
208 (stdout, stderr) = p.communicate(stdin) 285 (stdout, stderr) = p.communicate(stdin)
209 status = p.returncode 286 status = p.returncode
210 287
211 if expected_status is not None: 288 if expected_status is not None:
212 self.assertEqual( 289 self.assertEqual(
213 status, expected_status, 290 status, expected_status,
214 msg='Expected status %d, got %d.\nCommand:\n%s\n\nstderr:\n%s' % ( 291 msg='Expected status %d, got %d.\nCommand:\n%s\n\nstderr:\n%s' % (
215 expected_status, status, ' '.join(cmd), stderr)) 292 expected_status, status, ' '.join(cmd), stderr))
216 293
217 toreturn = [] 294 toreturn = []
218 if return_status: 295 if return_status:
219 toreturn.append(status) 296 toreturn.append(status)
220 if return_stdout: 297 if return_stdout:
221 if IS_WINDOWS: 298 if IS_WINDOWS:
222 stdout = stdout.replace('\r\n', '\n') 299 stdout = stdout.replace('\r\n', '\n')
223 toreturn.append(stdout) 300 toreturn.append(stdout)
224 if return_stderr: 301 if return_stderr:
225 if IS_WINDOWS: 302 if IS_WINDOWS:
226 stderr = stderr.replace('\r\n', '\n') 303 stderr = stderr.replace('\r\n', '\n')
227 toreturn.append(stderr) 304 toreturn.append(stderr)
228 305
229 if len(toreturn) == 1: 306 if len(toreturn) == 1:
230 return toreturn[0] 307 return toreturn[0]
231 elif toreturn: 308 elif toreturn:
232 return tuple(toreturn) 309 return tuple(toreturn)
233 310
234 @contextmanager 311 @contextmanager
235 def SetAnonymousBotoCreds(self): 312 def SetAnonymousBotoCreds(self):
236 boto_config_path = self.CreateTempFile( 313 boto_config_path = self.CreateTempFile(
237 contents=BOTO_CONFIG_CONTENTS_IGNORE_ANON_WARNING) 314 contents=BOTO_CONFIG_CONTENTS_IGNORE_ANON_WARNING)
238 with SetBotoConfigForTest(boto_config_path): 315 with SetBotoConfigFileForTest(boto_config_path):
239 yield 316 yield
OLDNEW
« no previous file with comments | « gslib/tests/testcase/base.py ('k') | gslib/tests/testcase/unit_testcase.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698