| OLD | NEW |
| 1 # -*- coding: utf-8 -*- |
| 1 # Copyright 2012 Google Inc. All Rights Reserved. | 2 # Copyright 2012 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. |
| 15 """Implementation of website configuration command for buckets.""" |
| 16 |
| 17 from __future__ import absolute_import |
| 14 | 18 |
| 15 import getopt | 19 import getopt |
| 16 import sys | 20 import sys |
| 17 | 21 |
| 18 from gslib.command import Command | 22 from gslib.command import Command |
| 19 from gslib.command import COMMAND_NAME | 23 from gslib.cs_api_map import ApiSelector |
| 20 from gslib.command import COMMAND_NAME_ALIASES | |
| 21 from gslib.command import FILE_URIS_OK | |
| 22 from gslib.command import MAX_ARGS | |
| 23 from gslib.command import MIN_ARGS | |
| 24 from gslib.command import PROVIDER_URIS_OK | |
| 25 from gslib.command import SUPPORTED_SUB_ARGS | |
| 26 from gslib.command import URIS_START_ARG | |
| 27 from gslib.exception import CommandException | 24 from gslib.exception import CommandException |
| 28 from gslib.help_provider import CreateHelpText | 25 from gslib.help_provider import CreateHelpText |
| 29 from gslib.help_provider import HELP_NAME | 26 from gslib.third_party.storage_apitools import encoding as encoding |
| 30 from gslib.help_provider import HELP_NAME_ALIASES | 27 from gslib.third_party.storage_apitools import storage_v1_messages as apitools_m
essages |
| 31 from gslib.help_provider import HELP_ONE_LINE_SUMMARY | |
| 32 from gslib.help_provider import HELP_TEXT | |
| 33 from gslib.help_provider import HelpType | |
| 34 from gslib.help_provider import HELP_TYPE | |
| 35 from gslib.help_provider import SUBCOMMAND_HELP_TEXT | |
| 36 from gslib.util import NO_MAX | 28 from gslib.util import NO_MAX |
| 37 from gslib.util import UnaryDictToXml | |
| 38 from xml.dom.minidom import parseString as XmlParseString | |
| 39 | 29 |
| 40 | 30 |
| 41 _SET_SYNOPSIS = """ | 31 _SET_SYNOPSIS = """ |
| 42 gsutil web set [-m main_page_suffix] [-e error_page] bucket_uri... | 32 gsutil web set [-m main_page_suffix] [-e error_page] bucket_url... |
| 43 """ | 33 """ |
| 44 | 34 |
| 45 _GET_SYNOPSIS = """ | 35 _GET_SYNOPSIS = """ |
| 46 gsutil web get bucket_uri | 36 gsutil web get bucket_url |
| 47 """ | 37 """ |
| 48 | 38 |
| 49 _SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') | 39 _SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') |
| 50 | 40 |
| 51 _SET_DESCRIPTION = """ | 41 _SET_DESCRIPTION = """ |
| 52 <B>SET</B> | 42 <B>SET</B> |
| 53 The "gsutil web set" command will allow you to set Website Configuration | 43 The "gsutil web set" command will allow you to configure or disable |
| 54 on your bucket(s). The "set" sub-command has the following options: | 44 Website Configuration on your bucket(s). The "set" sub-command has the |
| 45 following options (leave both options blank to disable): |
| 55 | 46 |
| 56 <B>SET OPTIONS</B> | 47 <B>SET OPTIONS</B> |
| 57 -m <index.html> Specifies the object name to serve when a bucket | 48 -m <index.html> Specifies the object name to serve when a bucket |
| 58 listing is requested via the CNAME alias to | 49 listing is requested via the CNAME alias to |
| 59 c.storage.googleapis.com. | 50 c.storage.googleapis.com. |
| 60 | 51 |
| 61 -e <404.html> Specifies the error page to serve when a request is made | 52 -e <404.html> Specifies the error page to serve when a request is made |
| 62 for a non-existent object via the CNAME alias to | 53 for a non-existent object via the CNAME alias to |
| 63 c.storage.googleapis.com. | 54 c.storage.googleapis.com. |
| 64 | 55 |
| 65 """ | 56 """ |
| 66 | 57 |
| 67 _GET_DESCRIPTION = """ | 58 _GET_DESCRIPTION = """ |
| 68 <B>GET</B> | 59 <B>GET</B> |
| 69 The "gsutil web get" command will gets the web semantics configuration for | 60 The "gsutil web get" command will gets the web semantics configuration for |
| 70 a bucket and displays an XML representation of the configuration. | 61 a bucket and displays a JSON representation of the configuration. |
| 71 | 62 |
| 72 In Google Cloud Storage, this would look like: | 63 In Google Cloud Storage, this would look like: |
| 73 | 64 |
| 74 <?xml version="1.0" ?> | 65 { |
| 75 <WebsiteConfiguration> | 66 "notFoundPage": "404.html", |
| 76 <MainPageSuffix> | 67 "mainPageSuffix": "index.html" |
| 77 index.html | 68 } |
| 78 </MainPageSuffix> | |
| 79 <NotFoundPage> | |
| 80 404.html | |
| 81 </NotFoundPage> | |
| 82 </WebsiteConfiguration> | |
| 83 | 69 |
| 84 """ | 70 """ |
| 85 | 71 |
| 86 _DESCRIPTION = """ | 72 _DESCRIPTION = """ |
| 87 The Website Configuration feature enables you to configure a Google Cloud | 73 The Website Configuration feature enables you to configure a Google Cloud |
| 88 Storage bucket to behave like a static website. This means requests made via a | 74 Storage bucket to behave like a static website. This means requests made via a |
| 89 domain-named bucket aliased using a Domain Name System "CNAME" to | 75 domain-named bucket aliased using a Domain Name System "CNAME" to |
| 90 c.storage.googleapis.com will work like any other website, i.e., a GET to the | 76 c.storage.googleapis.com will work like any other website, i.e., a GET to the |
| 91 bucket will serve the configured "main" page instead of the usual bucket | 77 bucket will serve the configured "main" page instead of the usual bucket |
| 92 listing and a GET for a non-existent object will serve the configured error | 78 listing and a GET for a non-existent object will serve the configured error |
| (...skipping 26 matching lines...) Expand all Loading... |
| 119 via the CNAME alias, you can continue to use "gsutil ls" to list the bucket | 105 via the CNAME alias, you can continue to use "gsutil ls" to list the bucket |
| 120 and get the normal bucket listing (rather than the main page). | 106 and get the normal bucket listing (rather than the main page). |
| 121 | 107 |
| 122 2. The main_page_suffix applies to each subdirectory of the bucket. For | 108 2. The main_page_suffix applies to each subdirectory of the bucket. For |
| 123 example, with the main_page_suffix configured to be index.html, a GET | 109 example, with the main_page_suffix configured to be index.html, a GET |
| 124 request for http://example.com would retrieve | 110 request for http://example.com would retrieve |
| 125 http://example.com/index.html, and a GET request for | 111 http://example.com/index.html, and a GET request for |
| 126 http://example.com/photos would retrieve | 112 http://example.com/photos would retrieve |
| 127 http://example.com/photos/index.html. | 113 http://example.com/photos/index.html. |
| 128 | 114 |
| 129 2. There is just one 404.html page: For example, a GET request for | 115 3. There is just one 404.html page: For example, a GET request for |
| 130 http://example.com/photos/missing would retrieve | 116 http://example.com/photos/missing would retrieve |
| 131 http://example.com/404.html, not http://example.com/photos/404.html. | 117 http://example.com/404.html, not http://example.com/photos/404.html. |
| 132 | 118 |
| 133 3. For additional details see | 119 4. For additional details see |
| 134 https://developers.google.com/storage/docs/website-configuration. | 120 https://developers.google.com/storage/docs/website-configuration. |
| 135 | 121 |
| 136 The web command has two sub-commands: | 122 The web command has two sub-commands: |
| 137 """ + _SET_DESCRIPTION + _GET_DESCRIPTION | 123 """ + _SET_DESCRIPTION + _GET_DESCRIPTION |
| 138 | 124 |
| 139 _detailed_help_text = CreateHelpText(_SYNOPSIS, _DESCRIPTION) | 125 _DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION) |
| 140 | 126 |
| 141 _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION) | 127 _get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION) |
| 142 _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION) | 128 _set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION) |
| 143 | 129 |
| 144 | 130 |
| 145 def BuildGSWebConfig(main_page_suffix=None, not_found_page=None): | |
| 146 config_body_l = ['<WebsiteConfiguration>'] | |
| 147 if main_page_suffix: | |
| 148 config_body_l.append('<MainPageSuffix>%s</MainPageSuffix>' % | |
| 149 main_page_suffix) | |
| 150 if not_found_page: | |
| 151 config_body_l.append('<NotFoundPage>%s</NotFoundPage>' % | |
| 152 not_found_page) | |
| 153 config_body_l.append('</WebsiteConfiguration>') | |
| 154 return "".join(config_body_l) | |
| 155 | |
| 156 def BuildS3WebConfig(main_page_suffix=None, error_page=None): | |
| 157 config_body_l = ['<WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/200
6-03-01/">'] | |
| 158 if not main_page_suffix: | |
| 159 raise CommandException('S3 requires main page / index document') | |
| 160 config_body_l.append('<IndexDocument><Suffix>%s</Suffix></IndexDocument>' % | |
| 161 main_page_suffix) | |
| 162 if error_page: | |
| 163 config_body_l.append('<ErrorDocument><Key>%s</Key></ErrorDocument>' % | |
| 164 error_page) | |
| 165 config_body_l.append('</WebsiteConfiguration>') | |
| 166 return "".join(config_body_l) | |
| 167 | |
| 168 class WebCommand(Command): | 131 class WebCommand(Command): |
| 169 """Implementation of gsutil web command.""" | 132 """Implementation of gsutil web command.""" |
| 170 | 133 |
| 171 # Command specification (processed by parent class). | 134 # Command specification. See base class for documentation. |
| 172 command_spec = { | 135 command_spec = Command.CreateCommandSpec( |
| 173 # Name of command. | 136 'web', |
| 174 COMMAND_NAME : 'web', | 137 command_name_aliases=['setwebcfg', 'getwebcfg'], |
| 175 # List of command name aliases. | 138 min_args=2, |
| 176 COMMAND_NAME_ALIASES : ['setwebcfg', 'getwebcfg'], | 139 max_args=NO_MAX, |
| 177 # Min number of args required by this command. | 140 supported_sub_args='m:e:', |
| 178 MIN_ARGS : 2, | 141 file_url_ok=False, |
| 179 # Max number of args required by this command, or NO_MAX. | 142 provider_url_ok=False, |
| 180 MAX_ARGS : NO_MAX, | 143 urls_start_arg=1, |
| 181 # Getopt-style string specifying acceptable sub args. | 144 gs_api_support=[ApiSelector.XML, ApiSelector.JSON], |
| 182 SUPPORTED_SUB_ARGS : 'm:e:', | 145 gs_default_api=ApiSelector.JSON, |
| 183 # True if file URIs acceptable for this command. | 146 ) |
| 184 FILE_URIS_OK : False, | 147 # Help specification. See help_provider.py for documentation. |
| 185 # True if provider-only URIs acceptable for this command. | 148 help_spec = Command.HelpSpec( |
| 186 PROVIDER_URIS_OK : False, | 149 help_name='web', |
| 187 # Index in args of first URI arg. | 150 help_name_aliases=['getwebcfg', 'setwebcfg'], |
| 188 URIS_START_ARG : 1, | 151 help_type='command_help', |
| 189 } | 152 help_one_line_summary=( |
| 190 help_spec = { | 153 'Set a main page and/or error page for one or more buckets'), |
| 191 # Name of command or auxiliary help info for which this help applies. | 154 help_text=_DETAILED_HELP_TEXT, |
| 192 HELP_NAME : 'web', | 155 subcommand_help_text={'get': _get_help_text, 'set': _set_help_text}, |
| 193 # List of help name aliases. | 156 ) |
| 194 HELP_NAME_ALIASES : ['getwebcfg', 'setwebcfg'], | 157 |
| 195 # Type of help) | |
| 196 HELP_TYPE : HelpType.COMMAND_HELP, | |
| 197 # One line summary of this help. | |
| 198 HELP_ONE_LINE_SUMMARY : 'Set a main page and/or error page for one or more b
uckets', | |
| 199 # The full help text. | |
| 200 HELP_TEXT : _detailed_help_text, | |
| 201 # Help text for sub-commands. | |
| 202 SUBCOMMAND_HELP_TEXT : {'get' : _get_help_text, | |
| 203 'set' : _set_help_text}, | |
| 204 } | |
| 205 | |
| 206 def _GetWeb(self): | 158 def _GetWeb(self): |
| 207 uri_args = self.args | 159 """Gets website configuration for a bucket.""" |
| 160 bucket_url, bucket_metadata = self.GetSingleBucketUrlFromArg( |
| 161 self.args[0], bucket_fields=['website']) |
| 208 | 162 |
| 209 # Iterate over URIs, expanding wildcards, and getting the website | 163 if bucket_url.scheme == 's3': |
| 210 # configuration on each. | 164 sys.stdout.write(self.gsutil_api.XmlPassThroughGetWebsite( |
| 211 some_matched = False | 165 bucket_url, provider=bucket_url.scheme)) |
| 212 for uri_str in uri_args: | 166 else: |
| 213 for blr in self.WildcardIterator(uri_str): | 167 if bucket_metadata.website and (bucket_metadata.website.mainPageSuffix or |
| 214 uri = blr.GetUri() | 168 bucket_metadata.website.notFoundPage): |
| 215 if not uri.names_bucket(): | 169 sys.stdout.write(str(encoding.MessageToJson( |
| 216 raise CommandException('URI %s must name a bucket for the %s command' | 170 bucket_metadata.website)) + '\n') |
| 217 % (uri, self.command_name)) | 171 else: |
| 218 some_matched = True | 172 sys.stdout.write('%s has no website configuration.\n' % bucket_url) |
| 219 sys.stderr.write('Getting website config on %s...\n' % uri) | 173 |
| 220 web_config_xml = UnaryDictToXml(uri.get_website_config()) | 174 return 0 |
| 221 sys.stdout.write(XmlParseString(web_config_xml).toprettyxml()) | |
| 222 if not some_matched: | |
| 223 raise CommandException('No URIs matched') | |
| 224 | 175 |
| 225 def _SetWeb(self): | 176 def _SetWeb(self): |
| 177 """Sets website configuration for a bucket.""" |
| 226 main_page_suffix = None | 178 main_page_suffix = None |
| 227 error_page = None | 179 error_page = None |
| 228 if self.sub_opts: | 180 if self.sub_opts: |
| 229 for o, a in self.sub_opts: | 181 for o, a in self.sub_opts: |
| 230 if o == '-m': | 182 if o == '-m': |
| 231 main_page_suffix = a | 183 main_page_suffix = a |
| 232 elif o == '-e': | 184 elif o == '-e': |
| 233 error_page = a | 185 error_page = a |
| 234 | 186 |
| 235 uri_args = self.args | 187 url_args = self.args |
| 236 | 188 |
| 237 # Iterate over URIs, expanding wildcards, and setting the website | 189 website = apitools_messages.Bucket.WebsiteValue( |
| 190 mainPageSuffix=main_page_suffix, notFoundPage=error_page) |
| 191 |
| 192 # Iterate over URLs, expanding wildcards and setting the website |
| 238 # configuration on each. | 193 # configuration on each. |
| 239 some_matched = False | 194 some_matched = False |
| 240 for uri_str in uri_args: | 195 for url_str in url_args: |
| 241 for blr in self.WildcardIterator(uri_str): | 196 bucket_iter = self.GetBucketUrlIterFromArg(url_str, bucket_fields=['id']) |
| 242 uri = blr.GetUri() | 197 for blr in bucket_iter: |
| 243 if not uri.names_bucket(): | 198 url = blr.storage_url |
| 244 raise CommandException('URI %s must name a bucket for the %s command' | |
| 245 % (str(uri), self.command_name)) | |
| 246 some_matched = True | 199 some_matched = True |
| 247 self.logger.info('Setting website config on %s...', uri) | 200 self.logger.info('Setting website configuration on %s...', blr) |
| 248 uri.set_website_config(main_page_suffix, error_page) | 201 bucket_metadata = apitools_messages.Bucket(website=website) |
| 202 self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata, |
| 203 provider=url.scheme, fields=['id']) |
| 249 if not some_matched: | 204 if not some_matched: |
| 250 raise CommandException('No URIs matched') | 205 raise CommandException('No URLs matched') |
| 206 return 0 |
| 251 | 207 |
| 252 # Command entry point. | |
| 253 def RunCommand(self): | 208 def RunCommand(self): |
| 209 """Command entry point for the web command.""" |
| 254 action_subcommand = self.args.pop(0) | 210 action_subcommand = self.args.pop(0) |
| 255 (self.sub_opts, self.args) = getopt.getopt(self.args, | 211 self.sub_opts, self.args = getopt.getopt( |
| 256 self.command_spec[SUPPORTED_SUB_ARGS]) | 212 self.args, self.command_spec.supported_sub_args) |
| 257 self.CheckArguments() | 213 self.CheckArguments() |
| 258 if action_subcommand == 'get': | 214 if action_subcommand == 'get': |
| 259 func = self._GetWeb | 215 func = self._GetWeb |
| 260 elif action_subcommand == 'set': | 216 elif action_subcommand == 'set': |
| 261 func = self._SetWeb | 217 func = self._SetWeb |
| 262 else: | 218 else: |
| 263 raise CommandException(('Invalid subcommand "%s" for the %s command.\n' | 219 raise CommandException(('Invalid subcommand "%s" for the %s command.\n' |
| 264 'See "gsutil help web".') % | 220 'See "gsutil help web".') % |
| 265 (action_subcommand, self.command_name)) | 221 (action_subcommand, self.command_name)) |
| 266 func() | 222 return func() |
| 267 return 0 | |
| OLD | NEW |