1# -*- coding: utf-8 -*- 2# Copyright 2012 Google Inc. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Implementation of website configuration command for buckets.""" 16 17from __future__ import absolute_import 18 19import sys 20 21from apitools.base.py import encoding 22 23from gslib.command import Command 24from gslib.command_argument import CommandArgument 25from gslib.cs_api_map import ApiSelector 26from gslib.exception import CommandException 27from gslib.help_provider import CreateHelpText 28from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages 29from gslib.util import NO_MAX 30 31 32_SET_SYNOPSIS = """ 33 gsutil web set [-m main_page_suffix] [-e error_page] bucket_url... 34""" 35 36_GET_SYNOPSIS = """ 37 gsutil web get bucket_url 38""" 39 40_SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') 41 42_SET_DESCRIPTION = """ 43<B>SET</B> 44 The "gsutil web set" command will allow you to configure or disable 45 Website Configuration on your bucket(s). The "set" sub-command has the 46 following options (leave both options blank to disable): 47 48<B>SET OPTIONS</B> 49 -m <index.html> Specifies the object name to serve when a bucket 50 listing is requested via the CNAME alias to 51 c.storage.googleapis.com. 52 53 -e <404.html> Specifies the error page to serve when a request is made 54 for a non-existent object via the CNAME alias to 55 c.storage.googleapis.com. 56 57""" 58 59_GET_DESCRIPTION = """ 60<B>GET</B> 61 The "gsutil web get" command will gets the web semantics configuration for 62 a bucket and displays a JSON representation of the configuration. 63 64 In Google Cloud Storage, this would look like: 65 66 { 67 "notFoundPage": "404.html", 68 "mainPageSuffix": "index.html" 69 } 70 71""" 72 73_DESCRIPTION = """ 74 The Website Configuration feature enables you to configure a Google Cloud 75 Storage bucket to behave like a static website. This means requests made via a 76 domain-named bucket aliased using a Domain Name System "CNAME" to 77 c.storage.googleapis.com will work like any other website, i.e., a GET to the 78 bucket will serve the configured "main" page instead of the usual bucket 79 listing and a GET for a non-existent object will serve the configured error 80 page. 81 82 For example, suppose your company's Domain name is example.com. You could set 83 up a website bucket as follows: 84 85 1. Create a bucket called example.com (see the "DOMAIN NAMED BUCKETS" 86 section of "gsutil help naming" for details about creating such buckets). 87 88 2. Create index.html and 404.html files and upload them to the bucket. 89 90 3. Configure the bucket to have website behavior using the command: 91 92 gsutil web set -m index.html -e 404.html gs://www.example.com 93 94 4. Add a DNS CNAME record for example.com pointing to c.storage.googleapis.com 95 (ask your DNS administrator for help with this). 96 97 Now if you open a browser and navigate to http://www.example.com, it will 98 display the main page instead of the default bucket listing. Note: It can 99 take time for DNS updates to propagate because of caching used by the DNS, 100 so it may take up to a day for the domain-named bucket website to work after 101 you create the CNAME DNS record. 102 103 Additional notes: 104 105 1. Because the main page is only served when a bucket listing request is made 106 via the CNAME alias, you can continue to use "gsutil ls" to list the bucket 107 and get the normal bucket listing (rather than the main page). 108 109 2. The main_page_suffix applies to each subdirectory of the bucket. For 110 example, with the main_page_suffix configured to be index.html, a GET 111 request for http://www.example.com would retrieve 112 http://www.example.com/index.html, and a GET request for 113 http://www.example.com/photos would retrieve 114 http://www.example.com/photos/index.html. 115 116 3. There is just one 404.html page: For example, a GET request for 117 http://www.example.com/photos/missing would retrieve 118 http://www.example.com/404.html, not 119 http://www.example.com/photos/404.html. 120 121 4. For additional details see 122 https://developers.google.com/storage/docs/website-configuration. 123 124 The web command has two sub-commands: 125""" + _SET_DESCRIPTION + _GET_DESCRIPTION 126 127_DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION) 128 129_get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION) 130_set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION) 131 132 133class WebCommand(Command): 134 """Implementation of gsutil web command.""" 135 136 # Command specification. See base class for documentation. 137 command_spec = Command.CreateCommandSpec( 138 'web', 139 command_name_aliases=['setwebcfg', 'getwebcfg'], 140 usage_synopsis=_SYNOPSIS, 141 min_args=2, 142 max_args=NO_MAX, 143 supported_sub_args='m:e:', 144 file_url_ok=False, 145 provider_url_ok=False, 146 urls_start_arg=1, 147 gs_api_support=[ApiSelector.XML, ApiSelector.JSON], 148 gs_default_api=ApiSelector.JSON, 149 argparse_arguments={ 150 'set': [ 151 CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument() 152 ], 153 'get': [ 154 CommandArgument.MakeNCloudBucketURLsArgument(1) 155 ] 156 } 157 ) 158 # Help specification. See help_provider.py for documentation. 159 help_spec = Command.HelpSpec( 160 help_name='web', 161 help_name_aliases=['getwebcfg', 'setwebcfg'], 162 help_type='command_help', 163 help_one_line_summary=( 164 'Set a main page and/or error page for one or more buckets'), 165 help_text=_DETAILED_HELP_TEXT, 166 subcommand_help_text={'get': _get_help_text, 'set': _set_help_text}, 167 ) 168 169 def _GetWeb(self): 170 """Gets website configuration for a bucket.""" 171 bucket_url, bucket_metadata = self.GetSingleBucketUrlFromArg( 172 self.args[0], bucket_fields=['website']) 173 174 if bucket_url.scheme == 's3': 175 sys.stdout.write(self.gsutil_api.XmlPassThroughGetWebsite( 176 bucket_url, provider=bucket_url.scheme)) 177 else: 178 if bucket_metadata.website and (bucket_metadata.website.mainPageSuffix or 179 bucket_metadata.website.notFoundPage): 180 sys.stdout.write(str(encoding.MessageToJson( 181 bucket_metadata.website)) + '\n') 182 else: 183 sys.stdout.write('%s has no website configuration.\n' % bucket_url) 184 185 return 0 186 187 def _SetWeb(self): 188 """Sets website configuration for a bucket.""" 189 main_page_suffix = None 190 error_page = None 191 if self.sub_opts: 192 for o, a in self.sub_opts: 193 if o == '-m': 194 main_page_suffix = a 195 elif o == '-e': 196 error_page = a 197 198 url_args = self.args 199 200 website = apitools_messages.Bucket.WebsiteValue( 201 mainPageSuffix=main_page_suffix, notFoundPage=error_page) 202 203 # Iterate over URLs, expanding wildcards and setting the website 204 # configuration on each. 205 some_matched = False 206 for url_str in url_args: 207 bucket_iter = self.GetBucketUrlIterFromArg(url_str, bucket_fields=['id']) 208 for blr in bucket_iter: 209 url = blr.storage_url 210 some_matched = True 211 self.logger.info('Setting website configuration on %s...', blr) 212 bucket_metadata = apitools_messages.Bucket(website=website) 213 self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata, 214 provider=url.scheme, fields=['id']) 215 if not some_matched: 216 raise CommandException('No URLs matched') 217 return 0 218 219 def RunCommand(self): 220 """Command entry point for the web command.""" 221 action_subcommand = self.args.pop(0) 222 self.ParseSubOpts(check_args=True) 223 if action_subcommand == 'get': 224 func = self._GetWeb 225 elif action_subcommand == 'set': 226 func = self._SetWeb 227 else: 228 raise CommandException(('Invalid subcommand "%s" for the %s command.\n' 229 'See "gsutil help web".') % 230 (action_subcommand, self.command_name)) 231 return func() 232