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