1# -*- coding: utf-8 -*-
2# Copyright 2013 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 lifecycle configuration command for GCS buckets."""
16
17from __future__ import absolute_import
18
19import sys
20
21from gslib.command import Command
22from gslib.command_argument import CommandArgument
23from gslib.cs_api_map import ApiSelector
24from gslib.exception import CommandException
25from gslib.help_provider import CreateHelpText
26from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages
27from gslib.translation_helper import LifecycleTranslation
28from gslib.util import NO_MAX
29from gslib.util import UrlsAreForSingleProvider
30
31
32_GET_SYNOPSIS = """
33  gsutil lifecycle get url
34"""
35
36_SET_SYNOPSIS = """
37  gsutil lifecycle set config-json-file url...
38"""
39
40_SYNOPSIS = _GET_SYNOPSIS + _SET_SYNOPSIS.lstrip('\n') + '\n'
41
42_GET_DESCRIPTION = """
43<B>GET</B>
44  Gets the lifecycle configuration for a given bucket. You can get the
45  lifecycle configuration for only one bucket at a time. The output can be
46  redirected into a file, edited and then updated via the set sub-command.
47
48"""
49
50_SET_DESCRIPTION = """
51<B>SET</B>
52  Sets the lifecycle configuration on one or more buckets. The config-json-file
53  specified on the command line should be a path to a local file containing
54  the lifecycle configuration JSON document.
55
56"""
57
58_DESCRIPTION = """
59  The lifecycle command can be used to get or set lifecycle management policies
60  for the given bucket(s). This command is supported for buckets only, not
61  objects. For more information on object lifecycle management, please see the
62  `developer guide <https://developers.google.com/storage/docs/lifecycle>`_.
63
64  The lifecycle command has two sub-commands:
65""" + _GET_DESCRIPTION + _SET_DESCRIPTION + """
66<B>EXAMPLES</B>
67  The following lifecycle configuration JSON document specifies that all objects
68  in this bucket that are more than 365 days old will be deleted automatically:
69
70    {
71      "rule":
72      [
73        {
74          "action": {"type": "Delete"},
75          "condition": {"age": 365}
76        }
77      ]
78    }
79
80  The following (empty) lifecycle configuration JSON document removes all
81  lifecycle configuration for a bucket:
82
83    {}
84
85"""
86
87_DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION)
88
89_get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION)
90_set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION)
91
92
93class LifecycleCommand(Command):
94  """Implementation of gsutil lifecycle command."""
95
96  # Command specification. See base class for documentation.
97  command_spec = Command.CreateCommandSpec(
98      'lifecycle',
99      command_name_aliases=['lifecycleconfig'],
100      usage_synopsis=_SYNOPSIS,
101      min_args=2,
102      max_args=NO_MAX,
103      supported_sub_args='',
104      file_url_ok=True,
105      provider_url_ok=False,
106      urls_start_arg=1,
107      gs_api_support=[ApiSelector.XML, ApiSelector.JSON],
108      gs_default_api=ApiSelector.JSON,
109      argparse_arguments={
110          'set': [
111              CommandArgument.MakeNFileURLsArgument(1),
112              CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument()
113          ],
114          'get': [
115              CommandArgument.MakeNCloudBucketURLsArgument(1)
116          ]
117      }
118  )
119  # Help specification. See help_provider.py for documentation.
120  help_spec = Command.HelpSpec(
121      help_name='lifecycle',
122      help_name_aliases=['getlifecycle', 'setlifecycle'],
123      help_type='command_help',
124      help_one_line_summary=(
125          'Get or set lifecycle configuration for a bucket'),
126      help_text=_DETAILED_HELP_TEXT,
127      subcommand_help_text={'get': _get_help_text, 'set': _set_help_text},
128  )
129
130  def _SetLifecycleConfig(self):
131    """Sets lifecycle configuration for a Google Cloud Storage bucket."""
132    lifecycle_arg = self.args[0]
133    url_args = self.args[1:]
134    # Disallow multi-provider 'lifecycle set' requests.
135    if not UrlsAreForSingleProvider(url_args):
136      raise CommandException('"%s" command spanning providers not allowed.' %
137                             self.command_name)
138
139    # Open, read and parse file containing JSON document.
140    lifecycle_file = open(lifecycle_arg, 'r')
141    lifecycle_txt = lifecycle_file.read()
142    lifecycle_file.close()
143
144    # Iterate over URLs, expanding wildcards and setting the lifecycle on each.
145    some_matched = False
146    for url_str in url_args:
147      bucket_iter = self.GetBucketUrlIterFromArg(url_str,
148                                                 bucket_fields=['lifecycle'])
149      for blr in bucket_iter:
150        url = blr.storage_url
151        some_matched = True
152        self.logger.info('Setting lifecycle configuration on %s...', blr)
153        if url.scheme == 's3':
154          self.gsutil_api.XmlPassThroughSetLifecycle(
155              lifecycle_txt, url, provider=url.scheme)
156        else:
157          lifecycle = LifecycleTranslation.JsonLifecycleToMessage(lifecycle_txt)
158          bucket_metadata = apitools_messages.Bucket(lifecycle=lifecycle)
159          self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata,
160                                      provider=url.scheme, fields=['id'])
161    if not some_matched:
162      raise CommandException('No URLs matched')
163    return 0
164
165  def _GetLifecycleConfig(self):
166    """Gets lifecycle configuration for a Google Cloud Storage bucket."""
167    bucket_url, bucket_metadata = self.GetSingleBucketUrlFromArg(
168        self.args[0], bucket_fields=['lifecycle'])
169
170    if bucket_url.scheme == 's3':
171      sys.stdout.write(self.gsutil_api.XmlPassThroughGetLifecycle(
172          bucket_url, provider=bucket_url.scheme))
173    else:
174      if bucket_metadata.lifecycle and bucket_metadata.lifecycle.rule:
175        sys.stdout.write(LifecycleTranslation.JsonLifecycleFromMessage(
176            bucket_metadata.lifecycle))
177      else:
178        sys.stdout.write('%s has no lifecycle configuration.\n' % bucket_url)
179
180    return 0
181
182  def RunCommand(self):
183    """Command entry point for the lifecycle command."""
184    subcommand = self.args.pop(0)
185    if subcommand == 'get':
186      return self._GetLifecycleConfig()
187    elif subcommand == 'set':
188      return self._SetLifecycleConfig()
189    else:
190      raise CommandException('Invalid subcommand "%s" for the %s command.' %
191                             (subcommand, self.command_name))
192