OLD | NEW |
(Empty) | |
| 1 # Copyright 2013 Google Inc. All Rights Reserved. |
| 2 # |
| 3 # 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 obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 """Contains gsutil base integration test case class.""" |
| 16 |
| 17 import logging |
| 18 import os.path |
| 19 import subprocess |
| 20 import sys |
| 21 |
| 22 import boto |
| 23 from boto.exception import GSResponseError |
| 24 |
| 25 from gslib.project_id import ProjectIdHandler |
| 26 import gslib.tests.util as util |
| 27 from gslib.tests.util import unittest |
| 28 from gslib.util import IS_WINDOWS |
| 29 from gslib.util import Retry |
| 30 import base |
| 31 |
| 32 |
| 33 CURDIR = os.path.abspath(os.path.dirname(__file__)) |
| 34 TESTS_DIR = os.path.split(CURDIR)[0] |
| 35 GSLIB_DIR = os.path.split(TESTS_DIR)[0] |
| 36 GSUTIL_DIR = os.path.split(GSLIB_DIR)[0] |
| 37 GSUTIL_PATH = os.path.join(GSUTIL_DIR, 'gsutil') |
| 38 LOGGER = logging.getLogger('integration-test') |
| 39 |
| 40 |
| 41 @unittest.skipUnless(util.RUN_INTEGRATION_TESTS, |
| 42 'Not running integration tests.') |
| 43 class GsUtilIntegrationTestCase(base.GsUtilTestCase): |
| 44 """Base class for gsutil integration tests.""" |
| 45 GROUP_TEST_ADDRESS = 'gs-discussion@googlegroups.com' |
| 46 GROUP_TEST_ID = '00b4903a97d097895ab58ef505d535916a712215b79c3e54932c2eb502ad9
7f5' |
| 47 USER_TEST_ADDRESS = 'gs-team@google.com' |
| 48 USER_TEST_ID = '00b4903a9703325c6bfc98992d72e75600387a64b3b6bee9ef74613ef88420
80' |
| 49 DOMAIN_TEST = 'google.com' |
| 50 |
| 51 def setUp(self): |
| 52 super(GsUtilIntegrationTestCase, self).setUp() |
| 53 self.bucket_uris = [] |
| 54 |
| 55 # Set up API version and project ID handler. |
| 56 self.api_version = boto.config.get_value( |
| 57 'GSUtil', 'default_api_version', '1') |
| 58 self.proj_id_handler = ProjectIdHandler() |
| 59 |
| 60 # Retry with an exponential backoff if a server error is received. This |
| 61 # ensures that we try *really* hard to clean up after ourselves. |
| 62 @Retry(GSResponseError, logger=LOGGER) |
| 63 def tearDown(self): |
| 64 super(GsUtilIntegrationTestCase, self).tearDown() |
| 65 |
| 66 while self.bucket_uris: |
| 67 bucket_uri = self.bucket_uris[-1] |
| 68 bucket_list = list(bucket_uri.list_bucket(all_versions=True)) |
| 69 while bucket_list: |
| 70 for k in bucket_list: |
| 71 k.delete() |
| 72 bucket_list = list(bucket_uri.list_bucket(all_versions=True)) |
| 73 bucket_uri.delete_bucket() |
| 74 self.bucket_uris.pop() |
| 75 |
| 76 def CreateBucket(self, bucket_name=None, test_objects=0, storage_class=None): |
| 77 """Creates a test bucket. |
| 78 |
| 79 The bucket and all of its contents will be deleted after the test. |
| 80 |
| 81 Args: |
| 82 bucket_name: Create the bucket with this name. If not provided, a |
| 83 temporary test bucket name is constructed. |
| 84 test_objects: The number of objects that should be placed in the bucket. |
| 85 Defaults to 0. |
| 86 storage_class: storage class to use. If not provided we us standard. |
| 87 |
| 88 Returns: |
| 89 StorageUri for the created bucket. |
| 90 """ |
| 91 bucket_name = bucket_name or self.MakeTempName('bucket') |
| 92 |
| 93 bucket_uri = boto.storage_uri('gs://%s' % bucket_name.lower(), |
| 94 suppress_consec_slashes=False) |
| 95 |
| 96 # Apply API version and project ID headers if necessary. |
| 97 headers = {'x-goog-api-version': self.api_version} |
| 98 self.proj_id_handler.FillInProjectHeaderIfNeeded( |
| 99 'test', bucket_uri, headers) |
| 100 |
| 101 bucket_uri.create_bucket(storage_class=storage_class, headers=headers) |
| 102 self.bucket_uris.append(bucket_uri) |
| 103 for i in range(test_objects): |
| 104 self.CreateObject(bucket_uri=bucket_uri, |
| 105 object_name=self.MakeTempName('obj'), |
| 106 contents='test %d' % i) |
| 107 return bucket_uri |
| 108 |
| 109 def CreateVersionedBucket(self, bucket_name=None, test_objects=0): |
| 110 """Creates a versioned test bucket. |
| 111 |
| 112 The bucket and all of its contents will be deleted after the test. |
| 113 |
| 114 Args: |
| 115 bucket_name: Create the bucket with this name. If not provided, a |
| 116 temporary test bucket name is constructed. |
| 117 test_objects: The number of objects that should be placed in the bucket. |
| 118 Defaults to 0. |
| 119 |
| 120 Returns: |
| 121 StorageUri for the created bucket with versioning enabled. |
| 122 """ |
| 123 bucket_uri = self.CreateBucket(bucket_name=bucket_name, |
| 124 test_objects=test_objects) |
| 125 bucket_uri.configure_versioning(True) |
| 126 return bucket_uri |
| 127 |
| 128 def CreateObject(self, bucket_uri=None, object_name=None, contents=None): |
| 129 """Creates a test object. |
| 130 |
| 131 Args: |
| 132 bucket: The URI of the bucket to place the object in. If not specified, a |
| 133 new temporary bucket is created. |
| 134 object_name: The name to use for the object. If not specified, a temporary |
| 135 test object name is constructed. |
| 136 contents: The contents to write to the object. If not specified, the key |
| 137 is not written to, which means that it isn't actually created |
| 138 yet on the server. |
| 139 |
| 140 Returns: |
| 141 A StorageUri for the created object. |
| 142 """ |
| 143 bucket_uri = bucket_uri or self.CreateBucket() |
| 144 object_name = object_name or self.MakeTempName('obj') |
| 145 key_uri = bucket_uri.clone_replace_name(object_name) |
| 146 if contents is not None: |
| 147 key_uri.set_contents_from_string(contents) |
| 148 return key_uri |
| 149 |
| 150 def RunGsUtil(self, cmd, return_status=False, return_stdout=False, |
| 151 return_stderr=False, expected_status=0, stdin=None): |
| 152 """Runs the gsutil command. |
| 153 |
| 154 Args: |
| 155 cmd: The command to run, as a list, e.g. ['cp', 'foo', 'bar'] |
| 156 return_status: If True, the exit status code is returned. |
| 157 return_stdout: If True, the standard output of the command is returned. |
| 158 return_stderr: If True, the standard error of the command is returned. |
| 159 expected_status: The expected return code. If not specified, defaults to |
| 160 0. If the return code is a different value, an exception |
| 161 is raised. |
| 162 stdin: A string of data to pipe to the process as standard input. |
| 163 |
| 164 Returns: |
| 165 A tuple containing the desired return values specified by the return_* |
| 166 arguments. |
| 167 """ |
| 168 cmd = [GSUTIL_PATH] + cmd |
| 169 if IS_WINDOWS: |
| 170 cmd = [sys.executable] + cmd |
| 171 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 172 stdin=subprocess.PIPE) |
| 173 (stdout, stderr) = p.communicate(stdin) |
| 174 status = p.returncode |
| 175 |
| 176 if expected_status is not None: |
| 177 self.assertEqual( |
| 178 status, expected_status, |
| 179 msg='Expected status %d, got %d.\nCommand:\n%s\n\nstderr:\n%s' % ( |
| 180 expected_status, status, ' '.join(cmd), stderr)) |
| 181 |
| 182 toreturn = [] |
| 183 if return_status: |
| 184 toreturn.append(status) |
| 185 if return_stdout: |
| 186 if IS_WINDOWS: |
| 187 stdout = stdout.replace('\r\n', '\n') |
| 188 toreturn.append(stdout) |
| 189 if return_stderr: |
| 190 if IS_WINDOWS: |
| 191 stderr = stderr.replace('\r\n', '\n') |
| 192 toreturn.append(stderr) |
| 193 |
| 194 if len(toreturn) == 1: |
| 195 return toreturn[0] |
| 196 elif toreturn: |
| 197 return tuple(toreturn) |
OLD | NEW |