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