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