| Index: gslib/commands/cors.py
|
| ===================================================================
|
| --- gslib/commands/cors.py (revision 33376)
|
| +++ gslib/commands/cors.py (working copy)
|
| @@ -1,3 +1,4 @@
|
| +# -*- coding: utf-8 -*-
|
| # Copyright 2012 Google Inc. All Rights Reserved.
|
| #
|
| # Licensed under the Apache License, Version 2.0 (the "License");
|
| @@ -11,38 +12,30 @@
|
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| # See the License for the specific language governing permissions and
|
| # limitations under the License.
|
| +"""Implementation of cors configuration command for GCS buckets."""
|
|
|
| +from __future__ import absolute_import
|
| +
|
| import sys
|
| -import xml
|
|
|
| -from boto import handler
|
| -from boto.gs.cors import Cors
|
| from gslib.command import Command
|
| -from gslib.command import COMMAND_NAME
|
| -from gslib.command import COMMAND_NAME_ALIASES
|
| -from gslib.command import FILE_URIS_OK
|
| -from gslib.command import MAX_ARGS
|
| -from gslib.command import MIN_ARGS
|
| -from gslib.command import PROVIDER_URIS_OK
|
| -from gslib.command import SUPPORTED_SUB_ARGS
|
| -from gslib.command import URIS_START_ARG
|
| +from gslib.cs_api_map import ApiSelector
|
| from gslib.exception import CommandException
|
| from gslib.help_provider import CreateHelpText
|
| -from gslib.help_provider import HELP_NAME
|
| -from gslib.help_provider import HELP_NAME_ALIASES
|
| -from gslib.help_provider import HELP_ONE_LINE_SUMMARY
|
| -from gslib.help_provider import HELP_TEXT
|
| -from gslib.help_provider import HelpType
|
| -from gslib.help_provider import HELP_TYPE
|
| -from gslib.help_provider import SUBCOMMAND_HELP_TEXT
|
| +from gslib.storage_url import StorageUrlFromString
|
| +from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages
|
| +from gslib.translation_helper import CorsTranslation
|
| +from gslib.translation_helper import REMOVE_CORS_CONFIG
|
| from gslib.util import NO_MAX
|
| +from gslib.util import UrlsAreForSingleProvider
|
|
|
| +
|
| _GET_SYNOPSIS = """
|
| -gsutil cors get uri
|
| +gsutil cors get url
|
| """
|
|
|
| _SET_SYNOPSIS = """
|
| -gsutil cors set cors-xml-file uri...
|
| +gsutil cors set cors-json-file url...
|
| """
|
|
|
| _GET_DESCRIPTION = """
|
| @@ -55,157 +48,146 @@
|
| _SET_DESCRIPTION = """
|
| <B>SET</B>
|
| Sets the CORS configuration for one or more buckets. The
|
| - cors-xml-file specified on the command line should be a path to a local
|
| - file containing an XML document as described above.
|
| + cors-json-file specified on the command line should be a path to a local
|
| + file containing a JSON document as described above.
|
| """
|
|
|
| -_SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') + '\n\n'
|
| +_SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') + '\n\n'
|
|
|
| _DESCRIPTION = ("""
|
| Gets or sets the Cross-Origin Resource Sharing (CORS) configuration on one or
|
| - more buckets. This command is supported for buckets only, not objects. A CORS
|
| - XML document should have the following structure:
|
| + more buckets. This command is supported for buckets only, not objects. An
|
| + example CORS JSON document looks like the folllowing:
|
|
|
| - <?xml version="1.0" ?>
|
| - <CorsConfig>
|
| - <Cors>
|
| - <Origins>
|
| - <Origin>http://origin1.example.com</Origin>
|
| - </Origins>
|
| - <Methods>
|
| - <Method>GET</Method>
|
| - </Methods>
|
| - <ResponseHeaders>
|
| - <ResponseHeader>Content-Type</ResponseHeader>
|
| - </ResponseHeaders>
|
| - </Cors>
|
| - </CorsConfig>
|
| + [
|
| + {
|
| + "origin": ["http://origin1.example.com"],
|
| + "responseHeader": ["Content-Type"],
|
| + "method": ["GET"],
|
| + "maxAgeSeconds": 3600
|
| + }
|
| + ]
|
|
|
| - The above XML document explicitly allows cross-origin GET requests from
|
| - `http://origin1.example.com` and may include the Content-Type response header.
|
| + The above JSON document explicitly allows cross-origin GET requests from
|
| + http://origin1.example.com and may include the Content-Type response header.
|
| + The preflight request may be cached for 1 hour.
|
|
|
| + The following (empty) CORS JSON document removes all CORS configuration for
|
| + a bucket:
|
| +
|
| + []
|
| +
|
| The cors command has two sub-commands:
|
| """ + '\n'.join([_GET_DESCRIPTION, _SET_DESCRIPTION]) + """
|
| For more info about CORS, see http://www.w3.org/TR/cors/.
|
| """)
|
|
|
| -_detailed_help_text = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
|
| +_DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
|
|
|
| _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION)
|
| _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION)
|
|
|
| +
|
| class CorsCommand(Command):
|
| """Implementation of gsutil cors command."""
|
|
|
| - # Command specification (processed by parent class).
|
| - command_spec = {
|
| - # Name of command.
|
| - COMMAND_NAME : 'cors',
|
| - # List of command name aliases.
|
| - COMMAND_NAME_ALIASES : ['getcors', 'setcors'],
|
| - # Min number of args required by this command.
|
| - MIN_ARGS : 2,
|
| - # Max number of args required by this command, or NO_MAX.
|
| - MAX_ARGS : NO_MAX,
|
| - # Getopt-style string specifying acceptable sub args.
|
| - SUPPORTED_SUB_ARGS : '',
|
| - # True if file URIs acceptable for this command.
|
| - FILE_URIS_OK : False,
|
| - # True if provider-only URIs acceptable for this command.
|
| - PROVIDER_URIS_OK : False,
|
| - # Index in args of first URI arg.
|
| - URIS_START_ARG : 1,
|
| - }
|
| - help_spec = {
|
| - # Name of command or auxiliary help info for which this help applies.
|
| - HELP_NAME : 'cors',
|
| - # List of help name aliases.
|
| - HELP_NAME_ALIASES : ['getcors', 'setcors', 'cross-origin'],
|
| - # Type of help)
|
| - HELP_TYPE : HelpType.COMMAND_HELP,
|
| - # One line summary of this help.
|
| - HELP_ONE_LINE_SUMMARY : 'Set a CORS XML document for one or more buckets',
|
| - # The full help text.
|
| - HELP_TEXT : _detailed_help_text,
|
| - # Help text for sub-commands.
|
| - SUBCOMMAND_HELP_TEXT : {'get' : _get_help_text,
|
| - 'set' : _set_help_text},
|
| - }
|
| + # Command specification. See base class for documentation.
|
| + command_spec = Command.CreateCommandSpec(
|
| + 'cors',
|
| + command_name_aliases=['getcors', 'setcors'],
|
| + min_args=2,
|
| + max_args=NO_MAX,
|
| + supported_sub_args='',
|
| + file_url_ok=False,
|
| + provider_url_ok=False,
|
| + urls_start_arg=1,
|
| + gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
|
| + gs_default_api=ApiSelector.JSON,
|
| + )
|
| + # Help specification. See help_provider.py for documentation.
|
| + help_spec = Command.HelpSpec(
|
| + help_name='cors',
|
| + help_name_aliases=['getcors', 'setcors', 'cross-origin'],
|
| + help_type='command_help',
|
| + help_one_line_summary=(
|
| + 'Set a CORS JSON document for one or more buckets'),
|
| + help_text=_DETAILED_HELP_TEXT,
|
| + subcommand_help_text={'get': _get_help_text, 'set': _set_help_text},
|
| + )
|
|
|
| - def _CalculateUrisStartArg(self):
|
| + def _CalculateUrlsStartArg(self):
|
| if not self.args:
|
| self._RaiseWrongNumberOfArgumentsException()
|
| - if (self.args[0].lower() == 'set'):
|
| + if self.args[0].lower() == 'set':
|
| return 2
|
| else:
|
| return 1
|
|
|
| def _SetCors(self):
|
| + """Sets CORS configuration on a Google Cloud Storage bucket."""
|
| cors_arg = self.args[0]
|
| - uri_args = self.args[1:]
|
| + url_args = self.args[1:]
|
| # Disallow multi-provider 'cors set' requests.
|
| - storage_uri = self.UrisAreForSingleProvider(uri_args)
|
| - if not storage_uri:
|
| + if not UrlsAreForSingleProvider(url_args):
|
| raise CommandException('"%s" command spanning providers not allowed.' %
|
| self.command_name)
|
|
|
| - # Open, read and parse file containing XML document.
|
| + # Open, read and parse file containing JSON document.
|
| cors_file = open(cors_arg, 'r')
|
| cors_txt = cors_file.read()
|
| cors_file.close()
|
| - cors_obj = Cors()
|
|
|
| - # Parse XML document and convert into Cors object.
|
| - h = handler.XmlHandler(cors_obj, None)
|
| - try:
|
| - xml.sax.parseString(cors_txt, h)
|
| - except xml.sax._exceptions.SAXParseException, e:
|
| - raise CommandException('Requested CORS is invalid: %s at line %s, '
|
| - 'column %s' % (e.getMessage(), e.getLineNumber(),
|
| - e.getColumnNumber()))
|
| + self.api = self.gsutil_api.GetApiSelector(
|
| + StorageUrlFromString(url_args[0]).scheme)
|
|
|
| - # Iterate over URIs, expanding wildcards, and setting the CORS on each.
|
| + cors = CorsTranslation.JsonCorsToMessageEntries(cors_txt)
|
| + if not cors:
|
| + cors = REMOVE_CORS_CONFIG
|
| +
|
| + # Iterate over URLs, expanding wildcards and setting the CORS on each.
|
| some_matched = False
|
| - for uri_str in uri_args:
|
| - for blr in self.WildcardIterator(uri_str):
|
| - uri = blr.GetUri()
|
| - if not uri.names_bucket():
|
| - raise CommandException('URI %s must name a bucket for the %s command'
|
| - % (str(uri), self.command_name))
|
| + for url_str in url_args:
|
| + bucket_iter = self.GetBucketUrlIterFromArg(url_str, bucket_fields=['id'])
|
| + for blr in bucket_iter:
|
| + url = blr.storage_url
|
| some_matched = True
|
| - self.logger.info('Setting CORS on %s...', uri)
|
| - uri.set_cors(cors_obj, False, self.headers)
|
| + self.logger.info('Setting CORS on %s...', blr)
|
| + if url.scheme == 's3':
|
| + self.gsutil_api.XmlPassThroughSetCors(
|
| + cors_txt, url, provider=url.scheme)
|
| + else:
|
| + bucket_metadata = apitools_messages.Bucket(cors=cors)
|
| + self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata,
|
| + provider=url.scheme, fields=['id'])
|
| if not some_matched:
|
| - raise CommandException('No URIs matched')
|
| + raise CommandException('No URLs matched')
|
| + return 0
|
|
|
| - def _GetCors(self):
|
| - # Wildcarding is allowed but must resolve to just one bucket.
|
| - uris = list(self.WildcardIterator(self.args[0]).IterUris())
|
| - if len(uris) == 0:
|
| - raise CommandException('No URIs matched')
|
| - if len(uris) != 1:
|
| - raise CommandException('%s matched more than one URI, which is not\n'
|
| - 'allowed by the %s command' % (self.args[0], self.command_name))
|
| - uri = uris[0]
|
| - if not uri.names_bucket():
|
| - raise CommandException('"%s" command must specify a bucket' %
|
| - self.command_name)
|
| - cors = uri.get_cors(False, self.headers)
|
| - # Pretty-print the XML to make it more easily human editable.
|
| - parsed_xml = xml.dom.minidom.parseString(cors.to_xml().encode('utf-8'))
|
| - sys.stdout.write(parsed_xml.toprettyxml(indent=' '))
|
| + def _GetCors(self):
|
| + """Gets CORS configuration for a Google Cloud Storage bucket."""
|
| + bucket_url, bucket_metadata = self.GetSingleBucketUrlFromArg(
|
| + self.args[0], bucket_fields=['cors'])
|
|
|
| - # Command entry point.
|
| + if bucket_url.scheme == 's3':
|
| + sys.stdout.write(self.gsutil_api.XmlPassThroughGetCors(
|
| + bucket_url, provider=bucket_url.scheme))
|
| + else:
|
| + if bucket_metadata.cors:
|
| + sys.stdout.write(
|
| + CorsTranslation.MessageEntriesToJson(bucket_metadata.cors))
|
| + else:
|
| + sys.stdout.write('%s has no CORS configuration.\n' % bucket_url)
|
| + return 0
|
| +
|
| def RunCommand(self):
|
| + """Command entry point for the cors command."""
|
| action_subcommand = self.args.pop(0)
|
| - self.CheckArguments()
|
| if action_subcommand == 'get':
|
| func = self._GetCors
|
| elif action_subcommand == 'set':
|
| func = self._SetCors
|
| else:
|
| raise CommandException(('Invalid subcommand "%s" for the %s command.\n'
|
| - 'See "gsutil help cors".') %
|
| + 'See "gsutil help cors".') %
|
| (action_subcommand, self.command_name))
|
| - func()
|
| - return 0
|
| + return func()
|
|
|