| OLD | NEW |
| 1 # -*- coding: utf-8 -*- |
| 1 # Copyright 2013 Google Inc. All Rights Reserved. | 2 # Copyright 2013 Google Inc. All Rights Reserved. |
| 2 # | 3 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 4 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 5 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 6 # You may obtain a copy of the License at |
| 6 # | 7 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 8 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 9 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 10 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 11 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 13 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 14 # limitations under the License. |
| 15 """Integration tests for lifecycle command.""" |
| 14 | 16 |
| 17 from __future__ import absolute_import |
| 18 |
| 19 import json |
| 15 import posixpath | 20 import posixpath |
| 16 from xml.dom.minidom import parseString | 21 from xml.dom.minidom import parseString |
| 17 | 22 |
| 23 import gslib.tests.testcase as testcase |
| 24 from gslib.tests.testcase.base import NotParallelizable |
| 25 from gslib.tests.testcase.integration_testcase import SkipForS3 |
| 26 from gslib.tests.util import ObjectToURI as suri |
| 27 from gslib.translation_helper import LifecycleTranslation |
| 18 from gslib.util import Retry | 28 from gslib.util import Retry |
| 19 import gslib.tests.testcase as testcase | |
| 20 from gslib.tests.util import ObjectToURI as suri | |
| 21 | 29 |
| 22 | 30 |
| 31 @SkipForS3('Lifecycle command is only supported for gs:// URLs') |
| 23 class TestSetLifecycle(testcase.GsUtilIntegrationTestCase): | 32 class TestSetLifecycle(testcase.GsUtilIntegrationTestCase): |
| 24 """Integration tests for lifecycle command.""" | 33 """Integration tests for lifecycle command.""" |
| 25 | 34 |
| 26 empty_doc1 = parseString( | 35 empty_doc1 = '{}' |
| 27 '<LifecycleConfiguration/>').toprettyxml(indent=' ') | |
| 28 | 36 |
| 29 empty_doc2 = parseString( | 37 xml_doc = parseString( |
| 30 '<LifecycleConfiguration>' | |
| 31 '</LifecycleConfiguration>').toprettyxml(indent=' ') | |
| 32 | |
| 33 bad_doc1 = ('<?xml version="1.0" ?><LifecycleConfiguration><Rule>' | |
| 34 '<Action><Delete/></Action></Rule></LifecycleConfiguration>') | |
| 35 | |
| 36 bad_doc2 = ('<?xml version="1.0" ?><LifecycleConfiguration><Rule>' | |
| 37 '<Condition><Age>365</Age></Condition></Rule>' | |
| 38 '</LifecycleConfiguration>') | |
| 39 | |
| 40 bad_doc3 = ('<?xml version="1.0" ?><LifecycleConfiguration><Rule>' | |
| 41 '<Delete/><Condition><Age>365</Age></Condition></Rule>' | |
| 42 '</LifecycleConfiguration>') | |
| 43 | |
| 44 bad_doc4 = ('<?xml version="1.0" ?><LifecycleConfiguration><Rule>' | |
| 45 '<Action><Delete/></Action><Age>365</Age></Rule>' | |
| 46 '</LifecycleConfiguration>') | |
| 47 | |
| 48 valid_doc = parseString( | |
| 49 '<LifecycleConfiguration><Rule>' | 38 '<LifecycleConfiguration><Rule>' |
| 50 '<Action><Delete/></Action>' | 39 '<Action><Delete/></Action>' |
| 51 '<Condition><Age>365</Age></Condition>' | 40 '<Condition><Age>365</Age></Condition>' |
| 52 '</Rule></LifecycleConfiguration>').toprettyxml(indent=' ') | 41 '</Rule></LifecycleConfiguration>').toprettyxml(indent=' ') |
| 53 | 42 |
| 43 bad_doc = ( |
| 44 '{"rule": [{"action": {"type": "Add"}, "condition": {"age": 365}}]}\n') |
| 45 |
| 46 lifecycle_doc = ( |
| 47 '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 365}}]}\n') |
| 48 lifecycle_json_obj = json.loads(lifecycle_doc) |
| 49 |
| 50 no_lifecycle_config = 'has no lifecycle configuration.' |
| 51 |
| 52 def test_lifecycle_translation(self): |
| 53 """Tests lifecycle translation for various formats.""" |
| 54 json_text = self.lifecycle_doc |
| 55 entries_list = LifecycleTranslation.JsonLifecycleToMessage(json_text) |
| 56 boto_lifecycle = LifecycleTranslation.BotoLifecycleFromMessage(entries_list) |
| 57 converted_entries_list = LifecycleTranslation.BotoLifecycleToMessage( |
| 58 boto_lifecycle) |
| 59 converted_json_text = LifecycleTranslation.JsonLifecycleFromMessage( |
| 60 converted_entries_list) |
| 61 self.assertEqual(json.loads(json_text), json.loads(converted_json_text)) |
| 62 |
| 54 def test_default_lifecycle(self): | 63 def test_default_lifecycle(self): |
| 55 bucket_uri = self.CreateBucket() | 64 bucket_uri = self.CreateBucket() |
| 56 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | 65 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], |
| 57 return_stdout=True) | 66 return_stdout=True) |
| 58 self.assertEqual(stdout, self.empty_doc1) | 67 self.assertIn(self.no_lifecycle_config, stdout) |
| 59 | 68 |
| 60 def test_set_empty_lifecycle1(self): | 69 def test_set_empty_lifecycle1(self): |
| 61 bucket_uri = self.CreateBucket() | 70 bucket_uri = self.CreateBucket() |
| 62 fpath = self.CreateTempFile(contents=self.empty_doc1) | 71 fpath = self.CreateTempFile(contents=self.empty_doc1) |
| 63 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) | 72 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) |
| 64 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | 73 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], |
| 65 return_stdout=True) | 74 return_stdout=True) |
| 66 self.assertEqual(stdout, self.empty_doc1) | 75 self.assertIn(self.no_lifecycle_config, stdout) |
| 67 | 76 |
| 68 def test_set_empty_lifecycle2(self): | 77 def test_valid_lifecycle(self): |
| 69 bucket_uri = self.CreateBucket() | 78 bucket_uri = self.CreateBucket() |
| 70 fpath = self.CreateTempFile(contents=self.empty_doc2) | 79 fpath = self.CreateTempFile(contents=self.lifecycle_doc) |
| 71 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) | 80 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) |
| 72 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | 81 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], |
| 73 return_stdout=True) | 82 return_stdout=True) |
| 74 self.assertEqual(stdout, self.empty_doc1) | 83 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| 75 | 84 |
| 76 def test_valid_lifecycle(self): | 85 def test_bad_lifecycle(self): |
| 77 bucket_uri = self.CreateBucket() | 86 bucket_uri = self.CreateBucket() |
| 78 fpath = self.CreateTempFile(contents=self.valid_doc) | 87 fpath = self.CreateTempFile(contents=self.bad_doc) |
| 88 stderr = self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], |
| 89 expected_status=1, return_stderr=True) |
| 90 self.assertNotIn('XML lifecycle data provided', stderr) |
| 91 |
| 92 def test_bad_xml_lifecycle(self): |
| 93 bucket_uri = self.CreateBucket() |
| 94 fpath = self.CreateTempFile(contents=self.xml_doc) |
| 95 stderr = self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], |
| 96 expected_status=1, return_stderr=True) |
| 97 self.assertIn('XML lifecycle data provided', stderr) |
| 98 |
| 99 def test_set_lifecycle_and_reset(self): |
| 100 """Tests setting and turning off lifecycle configuration.""" |
| 101 bucket_uri = self.CreateBucket() |
| 102 tmpdir = self.CreateTempDir() |
| 103 fpath = self.CreateTempFile(tmpdir=tmpdir, contents=self.lifecycle_doc) |
| 79 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) | 104 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) |
| 80 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | 105 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], |
| 81 return_stdout=True) | 106 return_stdout=True) |
| 82 self.assertEqual(stdout, self.valid_doc) | 107 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| 83 | |
| 84 def test_bad_lifecycle1(self): | |
| 85 bucket_uri = self.CreateBucket() | |
| 86 fpath = self.CreateTempFile(contents=self.bad_doc1) | |
| 87 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], | |
| 88 expected_status=1) | |
| 89 | |
| 90 def test_bad_lifecycle2(self): | |
| 91 bucket_uri = self.CreateBucket() | |
| 92 fpath = self.CreateTempFile(contents=self.bad_doc2) | |
| 93 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], | |
| 94 expected_status=1) | |
| 95 | |
| 96 def test_bad_lifecycle3(self): | |
| 97 bucket_uri = self.CreateBucket() | |
| 98 fpath = self.CreateTempFile(contents=self.bad_doc3) | |
| 99 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], | |
| 100 expected_status=1) | |
| 101 | |
| 102 def test_bad_lifecycle4(self): | |
| 103 bucket_uri = self.CreateBucket() | |
| 104 fpath = self.CreateTempFile(contents=self.bad_doc4) | |
| 105 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)], | |
| 106 expected_status=1) | |
| 107 | |
| 108 def test_set_lifecycle_and_reset(self): | |
| 109 bucket_uri = self.CreateBucket() | |
| 110 tmpdir = self.CreateTempDir() | |
| 111 fpath = self.CreateTempFile(tmpdir=tmpdir, contents=self.valid_doc) | |
| 112 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) | |
| 113 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | |
| 114 return_stdout=True) | |
| 115 self.assertEqual(stdout, self.valid_doc) | |
| 116 | 108 |
| 117 fpath = self.CreateTempFile(tmpdir=tmpdir, contents=self.empty_doc1) | 109 fpath = self.CreateTempFile(tmpdir=tmpdir, contents=self.empty_doc1) |
| 118 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) | 110 self.RunGsUtil(['lifecycle', 'set', fpath, suri(bucket_uri)]) |
| 119 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], | 111 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket_uri)], |
| 120 return_stdout=True) | 112 return_stdout=True) |
| 121 self.assertEqual(stdout, self.empty_doc1) | 113 self.assertIn(self.no_lifecycle_config, stdout) |
| 122 | 114 |
| 123 def test_set_lifecycle_multi_buckets(self): | 115 def test_set_lifecycle_multi_buckets(self): |
| 116 """Tests setting lifecycle configuration on multiple buckets.""" |
| 124 bucket1_uri = self.CreateBucket() | 117 bucket1_uri = self.CreateBucket() |
| 125 bucket2_uri = self.CreateBucket() | 118 bucket2_uri = self.CreateBucket() |
| 126 fpath = self.CreateTempFile(contents=self.valid_doc) | 119 fpath = self.CreateTempFile(contents=self.lifecycle_doc) |
| 127 self.RunGsUtil( | 120 self.RunGsUtil( |
| 128 ['lifecycle', 'set', fpath, suri(bucket1_uri), suri(bucket2_uri)]) | 121 ['lifecycle', 'set', fpath, suri(bucket1_uri), suri(bucket2_uri)]) |
| 129 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket1_uri)], | 122 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket1_uri)], |
| 130 return_stdout=True) | 123 return_stdout=True) |
| 131 self.assertEqual(stdout, self.valid_doc) | 124 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| 132 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket2_uri)], | 125 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket2_uri)], |
| 133 return_stdout=True) | 126 return_stdout=True) |
| 134 self.assertEqual(stdout, self.valid_doc) | 127 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| 135 | 128 |
| 129 # Script lists buckets with wildcards while they are being deleted by other |
| 130 # tests, which can cause an XML metadata get for the buckets' lifecycle |
| 131 # configurations to fail on a just-deleted bucket. |
| 132 @NotParallelizable |
| 136 def test_set_lifecycle_wildcard(self): | 133 def test_set_lifecycle_wildcard(self): |
| 134 """Tests setting lifecycle with a wildcarded bucket URI.""" |
| 137 random_prefix = self.MakeRandomTestString() | 135 random_prefix = self.MakeRandomTestString() |
| 138 bucket1_name = self.MakeTempName('bucket', prefix=random_prefix) | 136 bucket1_name = self.MakeTempName('bucket', prefix=random_prefix) |
| 139 bucket2_name = self.MakeTempName('bucket', prefix=random_prefix) | 137 bucket2_name = self.MakeTempName('bucket', prefix=random_prefix) |
| 140 bucket1_uri = self.CreateBucket(bucket_name=bucket1_name) | 138 bucket1_uri = self.CreateBucket(bucket_name=bucket1_name) |
| 141 bucket2_uri = self.CreateBucket(bucket_name=bucket2_name) | 139 bucket2_uri = self.CreateBucket(bucket_name=bucket2_name) |
| 142 # This just double checks that the common prefix of the two buckets is what | 140 # This just double checks that the common prefix of the two buckets is what |
| 143 # we think it should be (based on implementation detail of CreateBucket). | 141 # we think it should be (based on implementation detail of CreateBucket). |
| 144 # We want to be careful when setting a wildcard on buckets to make sure we | 142 # We want to be careful when setting a wildcard on buckets to make sure we |
| 145 # don't step outside the test buckets to affect other buckets. | 143 # don't step outside the test buckets to affect other buckets. |
| 146 common_prefix = posixpath.commonprefix([suri(bucket1_uri), | 144 common_prefix = posixpath.commonprefix([suri(bucket1_uri), |
| 147 suri(bucket2_uri)]) | 145 suri(bucket2_uri)]) |
| 148 self.assertTrue(common_prefix.startswith( | 146 self.assertTrue(common_prefix.startswith( |
| 149 'gs://%sgsutil-test-test_set_lifecycle_wildcard-' % random_prefix)) | 147 'gs://%sgsutil-test-test_set_lifecycle_wildcard-' % random_prefix)) |
| 150 wildcard = '%s*' % common_prefix | 148 wildcard = '%s*' % common_prefix |
| 151 | 149 |
| 152 fpath = self.CreateTempFile(contents=self.valid_doc) | 150 fpath = self.CreateTempFile(contents=self.lifecycle_doc) |
| 153 | 151 |
| 154 # Use @Retry as hedge against bucket listing eventual consistency. | 152 # Use @Retry as hedge against bucket listing eventual consistency. |
| 155 expected = set([ | 153 expected = set([ |
| 156 'Setting lifecycle configuration on %s/...' % suri(bucket1_uri), | 154 'Setting lifecycle configuration on %s/...' % suri(bucket1_uri), |
| 157 'Setting lifecycle configuration on %s/...' % suri(bucket2_uri)]) | 155 'Setting lifecycle configuration on %s/...' % suri(bucket2_uri)]) |
| 158 actual = set() | 156 actual = set() |
| 159 @Retry(AssertionError, tries=3, timeout_secs=1) | 157 @Retry(AssertionError, tries=3, timeout_secs=1) |
| 160 def _Check1(): | 158 def _Check1(): |
| 161 stderr = self.RunGsUtil(['lifecycle', 'set', fpath, wildcard], | 159 stderr = self.RunGsUtil(['lifecycle', 'set', fpath, wildcard], |
| 162 return_stderr=True) | 160 return_stderr=True) |
| 163 actual.update(stderr.splitlines()) | 161 actual.update(stderr.splitlines()) |
| 164 self.assertEqual(expected, actual) | 162 self.assertEqual(expected, actual) |
| 165 self.assertEqual(stderr.count('Setting lifecycle configuration'), 2) | 163 self.assertEqual(stderr.count('Setting lifecycle configuration'), 2) |
| 166 _Check1() | 164 _Check1() |
| 167 | 165 |
| 168 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket1_uri)], | 166 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket1_uri)], |
| 169 return_stdout=True) | 167 return_stdout=True) |
| 170 self.assertEqual(stdout, self.valid_doc) | 168 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| 171 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket2_uri)], | 169 stdout = self.RunGsUtil(['lifecycle', 'get', suri(bucket2_uri)], |
| 172 return_stdout=True) | 170 return_stdout=True) |
| 173 self.assertEqual(stdout, self.valid_doc) | 171 self.assertEqual(json.loads(stdout), self.lifecycle_json_obj) |
| OLD | NEW |