We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
GCP  object_storage_service   MissingObjectCredentialException: Boto file cannot be found in [None, '~/.boto', '/etc/boto.cfg'].
* How to reproduce
  set flag `  gce_preemptible_vms: true`

*  errors
>  File "/Users/martin/work/PerfKitBenchmarker/perfkitbenchmarker/object_storage_service.py", line 383, in FindBotoFile
    'Boto file cannot be found in %s.' % paths_to_check)
> perfkitbenchmarker.errors.Benchmarks.MissingObjectCredentialException: Boto file cannot be found in [None, '~/.boto', '/etc/boto.cfg'].


* code 

https://github.com/GoogleCloudPlatform/PerfKitBenchmarker/blob/master/perfkitbenchmarker/object_storage_service.py#L368-L383




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `perfkitbenchmarker/object_storage_service.py`
Content:
```
1 # Copyright 2016 PerfKitBenchmarker Authors. 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 """An interface to object storage services."""
16 
17 
18 import abc
19 import logging
20 import os
21 import pathlib
22 from typing import Optional
23 
24 from absl import flags
25 from perfkitbenchmarker import errors
26 import six
27 
28 flags.DEFINE_string('object_storage_credential_file', None,
29                     'Directory of credential file.')
30 flags.DEFINE_string('boto_file_location', None,
31                     'The location of the boto file.')
32 
33 FLAGS = flags.FLAGS
34 
35 DEFAULT_BOTO_LOCATION_USER = '~/.boto'
36 DEFAULT_BOTO_LOCATION_MACHINE = '/etc/boto.cfg'
37 BOTO_LIB_VERSION = 'boto_lib_version'
38 
39 _OBJECT_STORAGE_REGISTRY = {}
40 
41 
42 class AutoRegisterObjectStorageMeta(abc.ABCMeta):
43   """Metaclass for auto registration."""
44   STORAGE_NAME = None
45 
46   def __init__(cls, name, bases, dct):
47     super(AutoRegisterObjectStorageMeta, cls).__init__(name, bases, dct)
48     if cls.STORAGE_NAME in _OBJECT_STORAGE_REGISTRY:
49       logging.info(
50           "Duplicate storage implementations for name '%s'. "
51           'Replacing %s with %s', cls.STORAGE_NAME,
52           _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME].__name__, cls.__name__)
53     _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME] = cls
54 
55 
56 class ObjectStorageService(
57     six.with_metaclass(AutoRegisterObjectStorageMeta, object)):
58   """Base class for ObjectStorageServices."""
59 
60   # Keeping the location in the service object is not very clean, but
61   # a nicer solution would be more complex, and we only use different
62   # locations in a very limited way. Also, true multi-location
63   # providers would require another abstraction for Azure service
64   # accounts, which would add more complexity.
65 
66   # Service object lifecycle
67 
68   def PrepareService(self, location):
69     """Get ready to use object storage.
70 
71     This method should be called before any other method of this
72     class. Once it is called, all of this class' methods should work
73     with data in the given location.
74 
75     Args:
76       location: where to place our data.
77     """
78     pass
79 
80   def CleanupService(self):
81     """Clean up what we did.
82 
83     No other method of this class should be called after
84     CleanupProvider.
85     """
86     pass
87 
88   # Bucket management
89 
90   @abc.abstractmethod
91   def MakeBucket(self, bucket, raise_on_failure=True):
92     """Make an object storage bucket.
93 
94     Args:
95       bucket: the name of the bucket to create.
96       raise_on_failure: Whether to raise errors.Benchmarks.BucketCreationError
97           if the bucket fails to be created.
98     """
99     pass
100 
101   @abc.abstractmethod
102   def Copy(self, src_url, dst_url, recursive=False):
103     """Copy files, objects and directories.
104 
105     Note: Recursive copy behavior mimics gsutil cp -r where:
106     Copy(/foo/bar, /baz, True) copies the directory bar into /baz/bar whereas
107     aws s3 cp --recursive would copy the contents of bar into /baz.
108 
109     Args:
110       src_url: string, the source url path.
111       dst_url: string, the destination url path.
112       recursive: whether to copy directories.
113     """
114     pass
115 
116   @abc.abstractmethod
117   def CopyToBucket(self, src_path, bucket, object_path):
118     """Copy a local file to a bucket.
119 
120     Args:
121       src_path: string, the local source path.
122       bucket: string, the destination bucket.
123       object_path: string, the object's path in the bucket.
124     """
125     pass
126 
127   @abc.abstractmethod
128   def MakeRemoteCliDownloadUrl(self, bucket, object_path):
129     """Creates a download url for an object in a bucket.
130 
131     This is used by GenerateCliDownloadFileCommand().
132 
133     Args:
134       bucket: string, the name of the bucket.
135       object_path: string, the path of the object in the bucket.
136     """
137     pass
138 
139   @abc.abstractmethod
140   def GenerateCliDownloadFileCommand(self, src_url, local_path):
141     """Generates a CLI command to copy src_url to local_path.
142 
143     This is suitable for use in scripts e.g. startup scripts.
144 
145     Args:
146       src_url: string, the source url path.
147       local_path: string, the local path.
148     """
149     pass
150 
151   @abc.abstractmethod
152   def List(self, bucket):
153     """List providers, buckets, or objects.
154 
155     Args:
156       bucket: the name of the bucket to list the contents of.
157     """
158     pass
159 
160   def ListTopLevelSubfolders(self, bucket):
161     """Lists the top level folders (not files) in a bucket.
162 
163     Args:
164       bucket: Name of the bucket to list the top level subfolders of.
165 
166     Returns:
167       A list of top level subfolder names. Can be empty if there are no folders.
168     """
169     return []
170 
171   @abc.abstractmethod
172   def DeleteBucket(self, bucket):
173     """Delete an object storage bucket.
174 
175     This method should succeed even if bucket contains objects.
176 
177     Args:
178       bucket: the name of the bucket to delete.
179     """
180     pass
181 
182   @abc.abstractmethod
183   def EmptyBucket(self, bucket):
184     """Empty an object storage bucket.
185 
186     Args:
187       bucket: the name of the bucket to empty.
188     """
189     pass
190 
191   # Working with a VM
192 
193   def PrepareVM(self, vm):
194     """Prepare a VM to use object storage.
195 
196     Args:
197       vm: the VM to prepare.
198     """
199     pass
200 
201   def CleanupVM(self, vm):
202     """Clean up a VM that was used in this benchmark.
203 
204     Args:
205       vm: the VM to clean up.
206     """
207     pass
208 
209   # CLI commands
210 
211   @abc.abstractmethod
212   def CLIUploadDirectory(self, vm, directory, file_names, bucket):
213     """Upload directory contents to a bucket through the CLI.
214 
215     The VM must have had PrepareVM called on it first. The command
216     will be wrapped in 'time ...'.
217 
218     The caller must ensure that file_names is a full list of files in
219     the directory, so the provider implementation can either use a
220     generic "upload directory" command or use the file names. This
221     method *must* pass all file names to the CLI at once if possible,
222     not in a loop, to give it the chance to share connections and
223     overlap uploads.
224 
225     Args:
226       vm: the VM to run commands on directory: the directory to
227       directory: the directory to upload files from
228       file_names: a list of paths (relative to directory) to upload
229       bucket: the bucket to upload the file to
230 
231     Returns:
232       A tuple of the (stdout, stderr) of the command.
233     """
234     pass
235 
236   @abc.abstractmethod
237   def CLIDownloadBucket(self, vm, bucket, objects, dest):
238     """Download bucket contents to a folder.
239 
240     The VM must have had PrepareVM called on it first. The command
241     will be wrapped in 'time ...'.
242 
243     The caller must ensure that objects is a full list of objects in
244     the bucket, so the provider implementation can either use a
245     generic "download bucket" command or use the object names. This
246     method *must* pass all object names to the CLI at once if
247     possible, not in a loop, to give it the chance to share
248     connections and overlap downloads.
249 
250     Args:
251       vm: the VM to run commands on
252       bucket: the name of the bucket to download from
253       objects: a list of names of objects to download
254       dest: the name of the folder to download to
255 
256     Returns:
257       A tuple of the (stdout, stderr) of the command.
258     """
259     pass
260 
261   # General methods
262 
263   def Metadata(self, vm):
264     """Provider-specific metadata for collected samples.
265 
266     Args:
267       vm: the VM we're running on.
268 
269     Returns:
270       A dict of key, value pairs to add to our sample metadata.
271     """
272 
273     return {}
274 
275   def UpdateSampleMetadata(self, samples):
276     """Updates metadata of samples with provider specific information.
277 
278     Args:
279       samples: the samples that need the metadata to be updated with provider
280         specific information.
281     """
282     pass
283 
284   def GetDownloadUrl(self,
285                      bucket: str,
286                      object_name: str,
287                      use_https=True) -> str:
288     """Get the URL to download objects over HTTP(S).
289 
290     Args:
291       bucket: name of bucket
292       object_name: name of object
293       use_https: whether to use HTTPS or else HTTP
294 
295     Returns:
296       The URL to download objects over.
297     """
298     raise NotImplementedError
299 
300   def GetUploadUrl(self, bucket: str, object_name: str, use_https=True) -> str:
301     """Get the URL to upload objects over HTTP(S).
302 
303     Args:
304       bucket: name of bucket
305       object_name: name of object
306       use_https: whether to use HTTPS or else HTTP
307 
308     Returns:
309       The URL to upload objects over.
310     """
311     return self.GetDownloadUrl(bucket, object_name, use_https)
312 
313   # Different services require uploads to be POST or PUT.
314   UPLOAD_HTTP_METHOD: Optional[str] = None
315 
316   def MakeBucketPubliclyReadable(self, bucket: str, also_make_writable=False):
317     """Make a bucket readable and optionally writable by everyone."""
318     raise NotImplementedError
319 
320   def APIScriptArgs(self):
321     """Extra arguments for the API test script.
322 
323     The service implementation has two parts - one that runs in the
324     PKB controller, and one that runs on worker VMs. This method is
325     how the controller communicates service-specific information to
326     the workers.
327 
328     Returns:
329       A list of strings, which will be passed as arguments to the API
330       test script.
331     """
332 
333     return []
334 
335   @classmethod
336   def APIScriptFiles(cls):
337     """Files to upload for the API test script.
338 
339     Returns:
340       A list of file names. These files will be uploaded to the remote
341       VM if this service's API is being benchmarked.
342     """
343 
344     return []
345 
346 
347 def GetObjectStorageClass(storage_name) -> type(ObjectStorageService):
348   """Return the ObjectStorageService subclass corresponding to storage_name."""
349 
350   return _OBJECT_STORAGE_REGISTRY[storage_name]
351 
352 
353 # TODO(user): Move somewhere more generic
354 def FindCredentialFile(default_location):
355   """Return the path to the credential file."""
356 
357   credential_file = (
358       FLAGS.object_storage_credential_file or default_location)
359   credential_file = os.path.expanduser(credential_file)
360   if not (os.path.isfile(credential_file) or
361           os.path.isdir(credential_file)):
362     raise errors.Benchmarks.MissingObjectCredentialException(
363         'Credential cannot be found in %s' % credential_file)
364 
365   return credential_file
366 
367 
368 def FindBotoFile():
369   """Return the path to the boto file."""
370   paths_to_check = [
371       FLAGS.boto_file_location,
372       DEFAULT_BOTO_LOCATION_USER,
373       DEFAULT_BOTO_LOCATION_MACHINE,
374   ]
375 
376   for path in paths_to_check:
377     if not path:
378       continue
379     if pathlib.Path(path).exists():
380       return path
381 
382   raise errors.Benchmarks.MissingObjectCredentialException(
383       'Boto file cannot be found in %s.' % paths_to_check)
384
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/perfkitbenchmarker/object_storage_service.py b/perfkitbenchmarker/object_storage_service.py
--- a/perfkitbenchmarker/object_storage_service.py
+++ b/perfkitbenchmarker/object_storage_service.py
@@ -376,8 +376,9 @@
   for path in paths_to_check:
     if not path:
       continue
-    if pathlib.Path(path).exists():
-      return path
+    absolute_path = pathlib.Path(path).expanduser().absolute()
+    if absolute_path.exists():
+      return absolute_path.as_posix()
 
   raise errors.Benchmarks.MissingObjectCredentialException(
       'Boto file cannot be found in %s.' % paths_to_check)

{"golden_diff": "diff --git a/perfkitbenchmarker/object_storage_service.py b/perfkitbenchmarker/object_storage_service.py\n--- a/perfkitbenchmarker/object_storage_service.py\n+++ b/perfkitbenchmarker/object_storage_service.py\n@@ -376,8 +376,9 @@\n   for path in paths_to_check:\n     if not path:\n       continue\n-    if pathlib.Path(path).exists():\n-      return path\n+    absolute_path = pathlib.Path(path).expanduser().absolute()\n+    if absolute_path.exists():\n+      return absolute_path.as_posix()\n \n   raise errors.Benchmarks.MissingObjectCredentialException(\n       'Boto file cannot be found in %s.' % paths_to_check)\n", "issue": "GCP  object_storage_service   MissingObjectCredentialException: Boto file cannot be found in [None, '~/.boto', '/etc/boto.cfg'].\n* How to reproduce\r\n  set flag `  gce_preemptible_vms: true`\r\n\r\n*  errors\r\n>  File \"/Users/martin/work/PerfKitBenchmarker/perfkitbenchmarker/object_storage_service.py\", line 383, in FindBotoFile\r\n    'Boto file cannot be found in %s.' % paths_to_check)\r\n> perfkitbenchmarker.errors.Benchmarks.MissingObjectCredentialException: Boto file cannot be found in [None, '~/.boto', '/etc/boto.cfg'].\r\n\r\n\r\n* code \r\n\r\nhttps://github.com/GoogleCloudPlatform/PerfKitBenchmarker/blob/master/perfkitbenchmarker/object_storage_service.py#L368-L383\r\n\r\n\r\n\n", "before_files": [{"content": "# Copyright 2016 PerfKitBenchmarker Authors. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"An interface to object storage services.\"\"\"\n\n\nimport abc\nimport logging\nimport os\nimport pathlib\nfrom typing import Optional\n\nfrom absl import flags\nfrom perfkitbenchmarker import errors\nimport six\n\nflags.DEFINE_string('object_storage_credential_file', None,\n                    'Directory of credential file.')\nflags.DEFINE_string('boto_file_location', None,\n                    'The location of the boto file.')\n\nFLAGS = flags.FLAGS\n\nDEFAULT_BOTO_LOCATION_USER = '~/.boto'\nDEFAULT_BOTO_LOCATION_MACHINE = '/etc/boto.cfg'\nBOTO_LIB_VERSION = 'boto_lib_version'\n\n_OBJECT_STORAGE_REGISTRY = {}\n\n\nclass AutoRegisterObjectStorageMeta(abc.ABCMeta):\n  \"\"\"Metaclass for auto registration.\"\"\"\n  STORAGE_NAME = None\n\n  def __init__(cls, name, bases, dct):\n    super(AutoRegisterObjectStorageMeta, cls).__init__(name, bases, dct)\n    if cls.STORAGE_NAME in _OBJECT_STORAGE_REGISTRY:\n      logging.info(\n          \"Duplicate storage implementations for name '%s'. \"\n          'Replacing %s with %s', cls.STORAGE_NAME,\n          _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME].__name__, cls.__name__)\n    _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME] = cls\n\n\nclass ObjectStorageService(\n    six.with_metaclass(AutoRegisterObjectStorageMeta, object)):\n  \"\"\"Base class for ObjectStorageServices.\"\"\"\n\n  # Keeping the location in the service object is not very clean, but\n  # a nicer solution would be more complex, and we only use different\n  # locations in a very limited way. Also, true multi-location\n  # providers would require another abstraction for Azure service\n  # accounts, which would add more complexity.\n\n  # Service object lifecycle\n\n  def PrepareService(self, location):\n    \"\"\"Get ready to use object storage.\n\n    This method should be called before any other method of this\n    class. Once it is called, all of this class' methods should work\n    with data in the given location.\n\n    Args:\n      location: where to place our data.\n    \"\"\"\n    pass\n\n  def CleanupService(self):\n    \"\"\"Clean up what we did.\n\n    No other method of this class should be called after\n    CleanupProvider.\n    \"\"\"\n    pass\n\n  # Bucket management\n\n  @abc.abstractmethod\n  def MakeBucket(self, bucket, raise_on_failure=True):\n    \"\"\"Make an object storage bucket.\n\n    Args:\n      bucket: the name of the bucket to create.\n      raise_on_failure: Whether to raise errors.Benchmarks.BucketCreationError\n          if the bucket fails to be created.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def Copy(self, src_url, dst_url, recursive=False):\n    \"\"\"Copy files, objects and directories.\n\n    Note: Recursive copy behavior mimics gsutil cp -r where:\n    Copy(/foo/bar, /baz, True) copies the directory bar into /baz/bar whereas\n    aws s3 cp --recursive would copy the contents of bar into /baz.\n\n    Args:\n      src_url: string, the source url path.\n      dst_url: string, the destination url path.\n      recursive: whether to copy directories.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def CopyToBucket(self, src_path, bucket, object_path):\n    \"\"\"Copy a local file to a bucket.\n\n    Args:\n      src_path: string, the local source path.\n      bucket: string, the destination bucket.\n      object_path: string, the object's path in the bucket.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def MakeRemoteCliDownloadUrl(self, bucket, object_path):\n    \"\"\"Creates a download url for an object in a bucket.\n\n    This is used by GenerateCliDownloadFileCommand().\n\n    Args:\n      bucket: string, the name of the bucket.\n      object_path: string, the path of the object in the bucket.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def GenerateCliDownloadFileCommand(self, src_url, local_path):\n    \"\"\"Generates a CLI command to copy src_url to local_path.\n\n    This is suitable for use in scripts e.g. startup scripts.\n\n    Args:\n      src_url: string, the source url path.\n      local_path: string, the local path.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def List(self, bucket):\n    \"\"\"List providers, buckets, or objects.\n\n    Args:\n      bucket: the name of the bucket to list the contents of.\n    \"\"\"\n    pass\n\n  def ListTopLevelSubfolders(self, bucket):\n    \"\"\"Lists the top level folders (not files) in a bucket.\n\n    Args:\n      bucket: Name of the bucket to list the top level subfolders of.\n\n    Returns:\n      A list of top level subfolder names. Can be empty if there are no folders.\n    \"\"\"\n    return []\n\n  @abc.abstractmethod\n  def DeleteBucket(self, bucket):\n    \"\"\"Delete an object storage bucket.\n\n    This method should succeed even if bucket contains objects.\n\n    Args:\n      bucket: the name of the bucket to delete.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def EmptyBucket(self, bucket):\n    \"\"\"Empty an object storage bucket.\n\n    Args:\n      bucket: the name of the bucket to empty.\n    \"\"\"\n    pass\n\n  # Working with a VM\n\n  def PrepareVM(self, vm):\n    \"\"\"Prepare a VM to use object storage.\n\n    Args:\n      vm: the VM to prepare.\n    \"\"\"\n    pass\n\n  def CleanupVM(self, vm):\n    \"\"\"Clean up a VM that was used in this benchmark.\n\n    Args:\n      vm: the VM to clean up.\n    \"\"\"\n    pass\n\n  # CLI commands\n\n  @abc.abstractmethod\n  def CLIUploadDirectory(self, vm, directory, file_names, bucket):\n    \"\"\"Upload directory contents to a bucket through the CLI.\n\n    The VM must have had PrepareVM called on it first. The command\n    will be wrapped in 'time ...'.\n\n    The caller must ensure that file_names is a full list of files in\n    the directory, so the provider implementation can either use a\n    generic \"upload directory\" command or use the file names. This\n    method *must* pass all file names to the CLI at once if possible,\n    not in a loop, to give it the chance to share connections and\n    overlap uploads.\n\n    Args:\n      vm: the VM to run commands on directory: the directory to\n      directory: the directory to upload files from\n      file_names: a list of paths (relative to directory) to upload\n      bucket: the bucket to upload the file to\n\n    Returns:\n      A tuple of the (stdout, stderr) of the command.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def CLIDownloadBucket(self, vm, bucket, objects, dest):\n    \"\"\"Download bucket contents to a folder.\n\n    The VM must have had PrepareVM called on it first. The command\n    will be wrapped in 'time ...'.\n\n    The caller must ensure that objects is a full list of objects in\n    the bucket, so the provider implementation can either use a\n    generic \"download bucket\" command or use the object names. This\n    method *must* pass all object names to the CLI at once if\n    possible, not in a loop, to give it the chance to share\n    connections and overlap downloads.\n\n    Args:\n      vm: the VM to run commands on\n      bucket: the name of the bucket to download from\n      objects: a list of names of objects to download\n      dest: the name of the folder to download to\n\n    Returns:\n      A tuple of the (stdout, stderr) of the command.\n    \"\"\"\n    pass\n\n  # General methods\n\n  def Metadata(self, vm):\n    \"\"\"Provider-specific metadata for collected samples.\n\n    Args:\n      vm: the VM we're running on.\n\n    Returns:\n      A dict of key, value pairs to add to our sample metadata.\n    \"\"\"\n\n    return {}\n\n  def UpdateSampleMetadata(self, samples):\n    \"\"\"Updates metadata of samples with provider specific information.\n\n    Args:\n      samples: the samples that need the metadata to be updated with provider\n        specific information.\n    \"\"\"\n    pass\n\n  def GetDownloadUrl(self,\n                     bucket: str,\n                     object_name: str,\n                     use_https=True) -> str:\n    \"\"\"Get the URL to download objects over HTTP(S).\n\n    Args:\n      bucket: name of bucket\n      object_name: name of object\n      use_https: whether to use HTTPS or else HTTP\n\n    Returns:\n      The URL to download objects over.\n    \"\"\"\n    raise NotImplementedError\n\n  def GetUploadUrl(self, bucket: str, object_name: str, use_https=True) -> str:\n    \"\"\"Get the URL to upload objects over HTTP(S).\n\n    Args:\n      bucket: name of bucket\n      object_name: name of object\n      use_https: whether to use HTTPS or else HTTP\n\n    Returns:\n      The URL to upload objects over.\n    \"\"\"\n    return self.GetDownloadUrl(bucket, object_name, use_https)\n\n  # Different services require uploads to be POST or PUT.\n  UPLOAD_HTTP_METHOD: Optional[str] = None\n\n  def MakeBucketPubliclyReadable(self, bucket: str, also_make_writable=False):\n    \"\"\"Make a bucket readable and optionally writable by everyone.\"\"\"\n    raise NotImplementedError\n\n  def APIScriptArgs(self):\n    \"\"\"Extra arguments for the API test script.\n\n    The service implementation has two parts - one that runs in the\n    PKB controller, and one that runs on worker VMs. This method is\n    how the controller communicates service-specific information to\n    the workers.\n\n    Returns:\n      A list of strings, which will be passed as arguments to the API\n      test script.\n    \"\"\"\n\n    return []\n\n  @classmethod\n  def APIScriptFiles(cls):\n    \"\"\"Files to upload for the API test script.\n\n    Returns:\n      A list of file names. These files will be uploaded to the remote\n      VM if this service's API is being benchmarked.\n    \"\"\"\n\n    return []\n\n\ndef GetObjectStorageClass(storage_name) -> type(ObjectStorageService):\n  \"\"\"Return the ObjectStorageService subclass corresponding to storage_name.\"\"\"\n\n  return _OBJECT_STORAGE_REGISTRY[storage_name]\n\n\n# TODO(user): Move somewhere more generic\ndef FindCredentialFile(default_location):\n  \"\"\"Return the path to the credential file.\"\"\"\n\n  credential_file = (\n      FLAGS.object_storage_credential_file or default_location)\n  credential_file = os.path.expanduser(credential_file)\n  if not (os.path.isfile(credential_file) or\n          os.path.isdir(credential_file)):\n    raise errors.Benchmarks.MissingObjectCredentialException(\n        'Credential cannot be found in %s' % credential_file)\n\n  return credential_file\n\n\ndef FindBotoFile():\n  \"\"\"Return the path to the boto file.\"\"\"\n  paths_to_check = [\n      FLAGS.boto_file_location,\n      DEFAULT_BOTO_LOCATION_USER,\n      DEFAULT_BOTO_LOCATION_MACHINE,\n  ]\n\n  for path in paths_to_check:\n    if not path:\n      continue\n    if pathlib.Path(path).exists():\n      return path\n\n  raise errors.Benchmarks.MissingObjectCredentialException(\n      'Boto file cannot be found in %s.' % paths_to_check)\n", "path": "perfkitbenchmarker/object_storage_service.py"}], "after_files": [{"content": "# Copyright 2016 PerfKitBenchmarker Authors. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"An interface to object storage services.\"\"\"\n\n\nimport abc\nimport logging\nimport os\nimport pathlib\nfrom typing import Optional\n\nfrom absl import flags\nfrom perfkitbenchmarker import errors\nimport six\n\nflags.DEFINE_string('object_storage_credential_file', None,\n                    'Directory of credential file.')\nflags.DEFINE_string('boto_file_location', None,\n                    'The location of the boto file.')\n\nFLAGS = flags.FLAGS\n\nDEFAULT_BOTO_LOCATION_USER = '~/.boto'\nDEFAULT_BOTO_LOCATION_MACHINE = '/etc/boto.cfg'\nBOTO_LIB_VERSION = 'boto_lib_version'\n\n_OBJECT_STORAGE_REGISTRY = {}\n\n\nclass AutoRegisterObjectStorageMeta(abc.ABCMeta):\n  \"\"\"Metaclass for auto registration.\"\"\"\n  STORAGE_NAME = None\n\n  def __init__(cls, name, bases, dct):\n    super(AutoRegisterObjectStorageMeta, cls).__init__(name, bases, dct)\n    if cls.STORAGE_NAME in _OBJECT_STORAGE_REGISTRY:\n      logging.info(\n          \"Duplicate storage implementations for name '%s'. \"\n          'Replacing %s with %s', cls.STORAGE_NAME,\n          _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME].__name__, cls.__name__)\n    _OBJECT_STORAGE_REGISTRY[cls.STORAGE_NAME] = cls\n\n\nclass ObjectStorageService(\n    six.with_metaclass(AutoRegisterObjectStorageMeta, object)):\n  \"\"\"Base class for ObjectStorageServices.\"\"\"\n\n  # Keeping the location in the service object is not very clean, but\n  # a nicer solution would be more complex, and we only use different\n  # locations in a very limited way. Also, true multi-location\n  # providers would require another abstraction for Azure service\n  # accounts, which would add more complexity.\n\n  # Service object lifecycle\n\n  def PrepareService(self, location):\n    \"\"\"Get ready to use object storage.\n\n    This method should be called before any other method of this\n    class. Once it is called, all of this class' methods should work\n    with data in the given location.\n\n    Args:\n      location: where to place our data.\n    \"\"\"\n    pass\n\n  def CleanupService(self):\n    \"\"\"Clean up what we did.\n\n    No other method of this class should be called after\n    CleanupProvider.\n    \"\"\"\n    pass\n\n  # Bucket management\n\n  @abc.abstractmethod\n  def MakeBucket(self, bucket, raise_on_failure=True):\n    \"\"\"Make an object storage bucket.\n\n    Args:\n      bucket: the name of the bucket to create.\n      raise_on_failure: Whether to raise errors.Benchmarks.BucketCreationError\n          if the bucket fails to be created.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def Copy(self, src_url, dst_url, recursive=False):\n    \"\"\"Copy files, objects and directories.\n\n    Note: Recursive copy behavior mimics gsutil cp -r where:\n    Copy(/foo/bar, /baz, True) copies the directory bar into /baz/bar whereas\n    aws s3 cp --recursive would copy the contents of bar into /baz.\n\n    Args:\n      src_url: string, the source url path.\n      dst_url: string, the destination url path.\n      recursive: whether to copy directories.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def CopyToBucket(self, src_path, bucket, object_path):\n    \"\"\"Copy a local file to a bucket.\n\n    Args:\n      src_path: string, the local source path.\n      bucket: string, the destination bucket.\n      object_path: string, the object's path in the bucket.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def MakeRemoteCliDownloadUrl(self, bucket, object_path):\n    \"\"\"Creates a download url for an object in a bucket.\n\n    This is used by GenerateCliDownloadFileCommand().\n\n    Args:\n      bucket: string, the name of the bucket.\n      object_path: string, the path of the object in the bucket.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def GenerateCliDownloadFileCommand(self, src_url, local_path):\n    \"\"\"Generates a CLI command to copy src_url to local_path.\n\n    This is suitable for use in scripts e.g. startup scripts.\n\n    Args:\n      src_url: string, the source url path.\n      local_path: string, the local path.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def List(self, bucket):\n    \"\"\"List providers, buckets, or objects.\n\n    Args:\n      bucket: the name of the bucket to list the contents of.\n    \"\"\"\n    pass\n\n  def ListTopLevelSubfolders(self, bucket):\n    \"\"\"Lists the top level folders (not files) in a bucket.\n\n    Args:\n      bucket: Name of the bucket to list the top level subfolders of.\n\n    Returns:\n      A list of top level subfolder names. Can be empty if there are no folders.\n    \"\"\"\n    return []\n\n  @abc.abstractmethod\n  def DeleteBucket(self, bucket):\n    \"\"\"Delete an object storage bucket.\n\n    This method should succeed even if bucket contains objects.\n\n    Args:\n      bucket: the name of the bucket to delete.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def EmptyBucket(self, bucket):\n    \"\"\"Empty an object storage bucket.\n\n    Args:\n      bucket: the name of the bucket to empty.\n    \"\"\"\n    pass\n\n  # Working with a VM\n\n  def PrepareVM(self, vm):\n    \"\"\"Prepare a VM to use object storage.\n\n    Args:\n      vm: the VM to prepare.\n    \"\"\"\n    pass\n\n  def CleanupVM(self, vm):\n    \"\"\"Clean up a VM that was used in this benchmark.\n\n    Args:\n      vm: the VM to clean up.\n    \"\"\"\n    pass\n\n  # CLI commands\n\n  @abc.abstractmethod\n  def CLIUploadDirectory(self, vm, directory, file_names, bucket):\n    \"\"\"Upload directory contents to a bucket through the CLI.\n\n    The VM must have had PrepareVM called on it first. The command\n    will be wrapped in 'time ...'.\n\n    The caller must ensure that file_names is a full list of files in\n    the directory, so the provider implementation can either use a\n    generic \"upload directory\" command or use the file names. This\n    method *must* pass all file names to the CLI at once if possible,\n    not in a loop, to give it the chance to share connections and\n    overlap uploads.\n\n    Args:\n      vm: the VM to run commands on directory: the directory to\n      directory: the directory to upload files from\n      file_names: a list of paths (relative to directory) to upload\n      bucket: the bucket to upload the file to\n\n    Returns:\n      A tuple of the (stdout, stderr) of the command.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def CLIDownloadBucket(self, vm, bucket, objects, dest):\n    \"\"\"Download bucket contents to a folder.\n\n    The VM must have had PrepareVM called on it first. The command\n    will be wrapped in 'time ...'.\n\n    The caller must ensure that objects is a full list of objects in\n    the bucket, so the provider implementation can either use a\n    generic \"download bucket\" command or use the object names. This\n    method *must* pass all object names to the CLI at once if\n    possible, not in a loop, to give it the chance to share\n    connections and overlap downloads.\n\n    Args:\n      vm: the VM to run commands on\n      bucket: the name of the bucket to download from\n      objects: a list of names of objects to download\n      dest: the name of the folder to download to\n\n    Returns:\n      A tuple of the (stdout, stderr) of the command.\n    \"\"\"\n    pass\n\n  # General methods\n\n  def Metadata(self, vm):\n    \"\"\"Provider-specific metadata for collected samples.\n\n    Args:\n      vm: the VM we're running on.\n\n    Returns:\n      A dict of key, value pairs to add to our sample metadata.\n    \"\"\"\n\n    return {}\n\n  def UpdateSampleMetadata(self, samples):\n    \"\"\"Updates metadata of samples with provider specific information.\n\n    Args:\n      samples: the samples that need the metadata to be updated with provider\n        specific information.\n    \"\"\"\n    pass\n\n  def GetDownloadUrl(self,\n                     bucket: str,\n                     object_name: str,\n                     use_https=True) -> str:\n    \"\"\"Get the URL to download objects over HTTP(S).\n\n    Args:\n      bucket: name of bucket\n      object_name: name of object\n      use_https: whether to use HTTPS or else HTTP\n\n    Returns:\n      The URL to download objects over.\n    \"\"\"\n    raise NotImplementedError\n\n  def GetUploadUrl(self, bucket: str, object_name: str, use_https=True) -> str:\n    \"\"\"Get the URL to upload objects over HTTP(S).\n\n    Args:\n      bucket: name of bucket\n      object_name: name of object\n      use_https: whether to use HTTPS or else HTTP\n\n    Returns:\n      The URL to upload objects over.\n    \"\"\"\n    return self.GetDownloadUrl(bucket, object_name, use_https)\n\n  # Different services require uploads to be POST or PUT.\n  UPLOAD_HTTP_METHOD: Optional[str] = None\n\n  def MakeBucketPubliclyReadable(self, bucket: str, also_make_writable=False):\n    \"\"\"Make a bucket readable and optionally writable by everyone.\"\"\"\n    raise NotImplementedError\n\n  def APIScriptArgs(self):\n    \"\"\"Extra arguments for the API test script.\n\n    The service implementation has two parts - one that runs in the\n    PKB controller, and one that runs on worker VMs. This method is\n    how the controller communicates service-specific information to\n    the workers.\n\n    Returns:\n      A list of strings, which will be passed as arguments to the API\n      test script.\n    \"\"\"\n\n    return []\n\n  @classmethod\n  def APIScriptFiles(cls):\n    \"\"\"Files to upload for the API test script.\n\n    Returns:\n      A list of file names. These files will be uploaded to the remote\n      VM if this service's API is being benchmarked.\n    \"\"\"\n\n    return []\n\n\ndef GetObjectStorageClass(storage_name) -> type(ObjectStorageService):\n  \"\"\"Return the ObjectStorageService subclass corresponding to storage_name.\"\"\"\n\n  return _OBJECT_STORAGE_REGISTRY[storage_name]\n\n\n# TODO(user): Move somewhere more generic\ndef FindCredentialFile(default_location):\n  \"\"\"Return the path to the credential file.\"\"\"\n\n  credential_file = (\n      FLAGS.object_storage_credential_file or default_location)\n  credential_file = os.path.expanduser(credential_file)\n  if not (os.path.isfile(credential_file) or\n          os.path.isdir(credential_file)):\n    raise errors.Benchmarks.MissingObjectCredentialException(\n        'Credential cannot be found in %s' % credential_file)\n\n  return credential_file\n\n\ndef FindBotoFile():\n  \"\"\"Return the path to the boto file.\"\"\"\n  paths_to_check = [\n      FLAGS.boto_file_location,\n      DEFAULT_BOTO_LOCATION_USER,\n      DEFAULT_BOTO_LOCATION_MACHINE,\n  ]\n\n  for path in paths_to_check:\n    if not path:\n      continue\n    absolute_path = pathlib.Path(path).expanduser().absolute()\n    if absolute_path.exists():\n      return absolute_path.as_posix()\n\n  raise errors.Benchmarks.MissingObjectCredentialException(\n      'Boto file cannot be found in %s.' % paths_to_check)\n", "path": "perfkitbenchmarker/object_storage_service.py"}]}