Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / telemetry / third_party / gsutilz / gslib / commands / notification.py
blob89ecbd291cb43635c1731a32e2f44baea8983d9c
1 # -*- coding: utf-8 -*-
2 # Copyright 2013 Google Inc. All Rights Reserved.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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 """This module provides the notification command to gsutil."""
17 from __future__ import absolute_import
19 import getopt
20 import uuid
22 from gslib.cloud_api import AccessDeniedException
23 from gslib.command import Command
24 from gslib.command import NO_MAX
25 from gslib.command_argument import CommandArgument
26 from gslib.cs_api_map import ApiSelector
27 from gslib.exception import CommandException
28 from gslib.help_provider import CreateHelpText
29 from gslib.storage_url import StorageUrlFromString
32 _WATCHBUCKET_SYNOPSIS = """
33 gsutil notification watchbucket [-i id] [-t token] app_url bucket_url...
34 """
36 _STOPCHANNEL_SYNOPSIS = """
37 gsutil notification stopchannel channel_id resource_id
38 """
40 _SYNOPSIS = _WATCHBUCKET_SYNOPSIS + _STOPCHANNEL_SYNOPSIS.lstrip('\n')
42 _WATCHBUCKET_DESCRIPTION = """
43 <B>WATCHBUCKET</B>
44 The watchbucket sub-command can be used to watch a bucket for object changes.
45 A service account must be used when running this command.
47 The app_url parameter must be an HTTPS URL to an application that will be
48 notified of changes to any object in the bucket. The URL endpoint must be
49 a verified domain on your project. See
50 `Notification Authorization <https://developers.google.com/storage/docs/object-change-notification#_Authorization>`_
51 for details.
53 The optional id parameter can be used to assign a unique identifier to the
54 created notification channel. If not provided, a random UUID string will be
55 generated.
57 The optional token parameter can be used to validate notifications events.
58 To do this, set this custom token and store it to later verify that
59 notification events contain the client token you expect.
61 """
63 _STOPCHANNEL_DESCRIPTION = """
64 <B>STOPCHANNEL</B>
65 The stopchannel sub-command can be used to stop sending change events to a
66 notification channel.
68 The channel_id and resource_id parameters should match the values from the
69 response of a bucket watch request.
71 """
73 _DESCRIPTION = """
74 The notification command can be used to configure notifications.
75 For more information on the Object Change Notification feature, please see:
76 https://developers.google.com/storage/docs/object-change-notification
78 The notification command has two sub-commands:
79 """ + _WATCHBUCKET_DESCRIPTION + _STOPCHANNEL_DESCRIPTION + """
81 <B>EXAMPLES</B>
83 Watch the bucket example-bucket for changes and send notifications to an
84 application server running at example.com:
86 gsutil notification watchbucket https://example.com/notify \\
87 gs://example-bucket
89 Assign identifier my-channel-id to the created notification channel:
91 gsutil notification watchbucket -i my-channel-id \\
92 https://example.com/notify gs://example-bucket
94 Set a custom client token that will be included with each notification event:
96 gsutil notification watchbucket -t my-client-token \\
97 https://example.com/notify gs://example-bucket
99 Stop the notification event channel with channel identifier channel1 and
100 resource identifier SoGqan08XDIFWr1Fv_nGpRJBHh8:
102 gsutil notification stopchannel channel1 SoGqan08XDIFWr1Fv_nGpRJBHh8
104 <B>NOTIFICATIONS AND PARALLEL COMPOSITE UPLOADS</B>
106 By default, gsutil enables parallel composite uploads for large files (see
107 "gsutil help cp"), which means that an upload of a large object can result
108 in multiple temporary component objects being uploaded before the actual
109 intended object is created. Any subscriber to notifications for this bucket
110 will then see a notification for each of these components being created and
111 deleted. If this is a concern for you, note that parallel composite uploads
112 can be disabled by setting "parallel_composite_upload_threshold = 0" in your
113 boto config file.
117 NOTIFICATION_AUTHORIZATION_FAILED_MESSAGE = """
118 Watch bucket attempt failed:
119 {watch_error}
121 You attempted to watch a bucket with an application URL of:
123 {watch_url}
125 which is not authorized for your project. Please ensure that you are using
126 Service Account authentication and that the Service Account's project is
127 authorized for the application URL. Notification endpoint URLs must also be
128 whitelisted in your Cloud Console project. To do that, the domain must also be
129 verified using Google Webmaster Tools. For instructions, please see:
131 https://developers.google.com/storage/docs/object-change-notification#_Authorization
134 _DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
136 _watchbucket_help_text = (
137 CreateHelpText(_WATCHBUCKET_SYNOPSIS, _WATCHBUCKET_DESCRIPTION))
138 _stopchannel_help_text = (
139 CreateHelpText(_STOPCHANNEL_SYNOPSIS, _STOPCHANNEL_DESCRIPTION))
142 class NotificationCommand(Command):
143 """Implementation of gsutil notification command."""
145 # Command specification. See base class for documentation.
146 command_spec = Command.CreateCommandSpec(
147 'notification',
148 command_name_aliases=[
149 'notify', 'notifyconfig', 'notifications', 'notif'],
150 usage_synopsis=_SYNOPSIS,
151 min_args=3,
152 max_args=NO_MAX,
153 supported_sub_args='i:t:',
154 file_url_ok=False,
155 provider_url_ok=False,
156 urls_start_arg=1,
157 gs_api_support=[ApiSelector.JSON],
158 gs_default_api=ApiSelector.JSON,
159 argparse_arguments={
160 'watchbucket': [
161 CommandArgument.MakeFreeTextArgument(),
162 CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument()
164 'stopchannel': []
167 # Help specification. See help_provider.py for documentation.
168 help_spec = Command.HelpSpec(
169 help_name='notification',
170 help_name_aliases=['watchbucket', 'stopchannel', 'notifyconfig'],
171 help_type='command_help',
172 help_one_line_summary='Configure object change notification',
173 help_text=_DETAILED_HELP_TEXT,
174 subcommand_help_text={'watchbucket': _watchbucket_help_text,
175 'stopchannel': _stopchannel_help_text},
178 def _WatchBucket(self):
179 """Creates a watch on a bucket given in self.args."""
180 self.CheckArguments()
181 identifier = None
182 client_token = None
183 if self.sub_opts:
184 for o, a in self.sub_opts:
185 if o == '-i':
186 identifier = a
187 if o == '-t':
188 client_token = a
190 identifier = identifier or str(uuid.uuid4())
191 watch_url = self.args[0]
192 bucket_arg = self.args[-1]
194 if not watch_url.lower().startswith('https://'):
195 raise CommandException('The application URL must be an https:// URL.')
197 bucket_url = StorageUrlFromString(bucket_arg)
198 if not (bucket_url.IsBucket() and bucket_url.scheme == 'gs'):
199 raise CommandException(
200 'The %s command can only be used with gs:// bucket URLs.' %
201 self.command_name)
202 if not bucket_url.IsBucket():
203 raise CommandException('URL must name a bucket for the %s command.' %
204 self.command_name)
206 self.logger.info('Watching bucket %s with application URL %s ...',
207 bucket_url, watch_url)
209 try:
210 channel = self.gsutil_api.WatchBucket(
211 bucket_url.bucket_name, watch_url, identifier, token=client_token,
212 provider=bucket_url.scheme)
213 except AccessDeniedException, e:
214 self.logger.warn(NOTIFICATION_AUTHORIZATION_FAILED_MESSAGE.format(
215 watch_error=str(e), watch_url=watch_url))
216 raise
218 channel_id = channel.id
219 resource_id = channel.resourceId
220 client_token = channel.token
221 self.logger.info('Successfully created watch notification channel.')
222 self.logger.info('Watch channel identifier: %s', channel_id)
223 self.logger.info('Canonicalized resource identifier: %s', resource_id)
224 self.logger.info('Client state token: %s', client_token)
226 return 0
228 def _StopChannel(self):
229 channel_id = self.args[0]
230 resource_id = self.args[1]
232 self.logger.info('Removing channel %s with resource identifier %s ...',
233 channel_id, resource_id)
234 self.gsutil_api.StopChannel(channel_id, resource_id, provider='gs')
235 self.logger.info('Succesfully removed channel.')
237 return 0
239 def _RunSubCommand(self, func):
240 try:
241 (self.sub_opts, self.args) = getopt.getopt(
242 self.args, self.command_spec.supported_sub_args)
243 return func()
244 except getopt.GetoptError, e:
245 self.RaiseInvalidArgumentException()
247 def RunCommand(self):
248 """Command entry point for the notification command."""
249 subcommand = self.args.pop(0)
251 if subcommand == 'watchbucket':
252 return self._RunSubCommand(self._WatchBucket)
253 elif subcommand == 'stopchannel':
254 return self._RunSubCommand(self._StopChannel)
255 else:
256 raise CommandException('Invalid subcommand "%s" for the %s command.' %
257 (subcommand, self.command_name))