示例#1
0
class TestContentRebuildFilter(BaseTestCase):
    def setUp(self):
        super(TestContentRebuildFilter, self).setUp()
        self.namespace = self.conf['namespace']
        self.gridconf = {"namespace": self.namespace}
        self.container = "TestContentRebuildFilter%f" % time.time()
        self.ref = self.container
        self.container_client = ContainerClient(self.conf)
        self.container_client.container_create(self.account, self.container)
        syst = self.container_client.container_get_properties(
            self.account, self.container)['system']
        self.container_id = syst['sys.name'].split('.', 1)[0]
        self.object_storage_api = ObjectStorageApi(namespace=self.namespace)
        self.stgpol = "SINGLE"
        self.conf['tube'] = 'rebuild'
        self.conf['queue_url'] = 'beanstalk://127.0.0.1:11300'
        self.notify_filter = NotifyFilter(app=_App, conf=self.conf)
        queue_url = self.conf.get('queue_url', 'tcp://127.0.0.1:11300')
        self.tube = self.conf.get('tube', 'rebuild')
        self.beanstalk = Beanstalk.from_url(queue_url)
        self.beanstalk.use(self.tube)

    def _create_event(self, content_name, present_chunks, missing_chunks,
                      content_id):
        event = {}
        event["when"] = time.time()
        event["event"] = "storage.content.broken"
        event["data"] = {
            "present_chunks": present_chunks,
            "missing_chunks": missing_chunks
        }
        event["url"] = {
            "ns": self.namespace,
            "account": self.account,
            "user": self.container,
            "path": content_name,
            "id": self.container_id,
            "content": content_id
        }
        return event

    def _is_chunks_created(self, previous, after, pos_created):
        remain = list(after)
        for p in previous:
            for r in remain:
                if p["url"] == r["url"]:
                    remain.remove(r)
                    break
        if len(remain) != len(pos_created):
            return False
        for r in remain:
            if r["pos"] in pos_created:
                remain.remove(r)
            else:
                return False
        return True

    def _rebuild(self, event, job_id=0):
        self.blob_rebuilder = subprocess.Popen([
            'oio-blob-rebuilder', self.namespace,
            '--beanstalkd=127.0.0.1:11300'
        ])
        time.sleep(3)
        self.blob_rebuilder.kill()

    def _remove_chunks(self, chunks, content_id):
        if not chunks:
            return
        for chunk in chunks:
            chunk['id'] = chunk['url']
            chunk['content'] = content_id
            chunk['type'] = 'chunk'
        self.container_client.container_raw_delete(self.account,
                                                   self.container,
                                                   data=chunks)

    def _check_rebuild(self,
                       content_name,
                       chunks,
                       missing_pos,
                       meta,
                       chunks_to_remove,
                       chunk_created=True):
        self._remove_chunks(chunks_to_remove, meta['id'])
        event = self._create_event(content_name, chunks, missing_pos,
                                   meta['id'])
        self.notify_filter.process(env=event, cb=None)
        self._rebuild(event)
        _, after = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        self.assertIs(chunk_created,
                      self._is_chunks_created(chunks, after, missing_pos))

    def test_nothing_missing(self):
        content_name = "test_nothing_missing"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="THREECOPIES",
                                              obj_name=content_name)

        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = []
        self._check_rebuild(content_name,
                            chunks,
                            missing_pos,
                            meta,
                            chunks_to_remove,
                            chunk_created=True)

    def test_missing_1_chunk(self):
        content_name = "test_missing_1_chunk"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="THREECOPIES",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_last_chunk(self):
        content_name = "test_missing_last_chunk"
        data = random_str(1024 * 1024 * 4)
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data=data,
                                              policy="THREECOPIES",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["3"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_2_chunks(self):
        content_name = "test_missing_2_chunks"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="THREECOPIES",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        for i in range(0, 2):
            chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0", "0"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_all_chunks(self):
        content_name = "test_missing_all_chunks"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="SINGLE",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0"]
        self._check_rebuild(content_name,
                            chunks,
                            missing_pos,
                            meta,
                            chunks_to_remove,
                            chunk_created=False)

    def test_missing_all_chunks_of_a_pos(self):
        content_name = "test_missing_2_chunks"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="THREECOPIES",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        for i in range(0, 3):
            chunks_to_remove.append(chunks.pop(0))

        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0"]
        self._check_rebuild(content_name,
                            chunks,
                            missing_pos,
                            meta,
                            chunks_to_remove,
                            chunk_created=False)

    def test_missing_multiple_chunks(self):
        content_name = "test_missing_multiple_chunks"
        data = random_str(1024 * 1024 * 4)
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data=data,
                                              policy="THREECOPIES",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(9))
        chunks_to_remove.append(chunks.pop(6))
        chunks_to_remove.append(chunks.pop(4))
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0", "1", "2", "3"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_1_chunk_ec(self):
        if len(self.conf['services']['rawx']) < 9:
            self.skipTest("Not enough rawx. "
                          "EC tests needs at least 9 rawx to run")
        content_name = "test_missing_1_chunk_ec"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="EC",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0.1"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_m_chunk_ec(self):
        if len(self.conf['services']['rawx']) < 9:
            self.skipTest("Not enough rawx. "
                          "EC tests needs at least 9 rawx to run")
        content_name = "test_missing_m_chunk_ec"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="EC",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        for i in range(0, 3):
            chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0.1", "0.2", "0.3"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_m_chunk_ec_2(self):
        if len(self.conf['services']['rawx']) < 9:
            self.skipTest("Not enough rawx. "
                          "EC tests needs at least 9 rawx to run")
        content_name = "test_missing_m_chunk_ec"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="EC",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        chunks_to_remove.append(chunks.pop(3))
        chunks_to_remove.append(chunks.pop(5))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0.1", "0.5", "0.8"]
        self._check_rebuild(content_name, chunks, missing_pos, meta,
                            chunks_to_remove)

    def test_missing_m1_chunk_ec(self):
        if len(self.conf['services']['rawx']) < 9:
            self.skipTest("Not enough rawx. "
                          "EC tests needs at least 9 rawx to run")
        content_name = "test_missing_m1_chunk_ec"
        self.object_storage_api.object_create(account=self.account,
                                              container=self.container,
                                              data="test",
                                              policy="EC",
                                              obj_name=content_name)
        meta, chunks = self.object_storage_api.object_locate(
            container=self.container, obj=content_name, account=self.account)
        chunks_to_remove = []
        chunks_to_remove.append(chunks.pop(0))
        chunks_to_remove.append(chunks.pop(0))
        chunks_to_remove.append(chunks.pop(0))
        chunks_to_remove.append(chunks.pop(0))
        for chunk in chunks:
            chunk.pop('score', None)

        missing_pos = ["0.1", "0.2", "0.3", "0.4"]
        self._check_rebuild(content_name,
                            chunks,
                            missing_pos,
                            meta,
                            chunks_to_remove,
                            chunk_created=False)
示例#2
0
class TestContainerLifecycle(BaseTestCase):

    CONTAINERS = set()

    def setUp(self, recursive=False):
        super(TestContainerLifecycle, self).setUp()
        self.api = ObjectStorageApi(self.ns)
        self.account = "test_lifecycle"
        self.container = "lifecycle-" + random_str(4)
        self.lifecycle = ContainerLifecycle(
            self.api, self.account, self.container, recursive=recursive)

    @staticmethod
    def _time_to_date(timestamp=None):
        if timestamp is None:
            timestamp = time.time()
        return time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(timestamp))

    def _upload_something(self, prefix="", path=None, **kwargs):
        path = path or (prefix + random_str(8))
        self.api.object_create(self.account, self.container,
                               obj_name=path, data=path, **kwargs)
        self.__class__.CONTAINERS.add(self.container)
        obj_meta = self.api.object_show(self.account, self.container, path)
        # add fields like LifeCycle do in execute to support CH
        obj_meta['orig_name'] = obj_meta['name']
        obj_meta['container'] = self.container
        return obj_meta

    def _enable_versioning(self):
        if not self.api.container_create(
                self.account, self.container,
                system={'sys.m2.policy.version': '-1'}):
            self.api.container_set_properties(
                self.account, self.container,
                system={'sys.policy.version': '-1'})

    def test_load_from_container_property(self):
        source = """
        <LifecycleConfiguration>
            <Rule>
                <ID>rule1</ID>
                <Filter>
                    <And>
                        <Prefix>documents/</Prefix>
                        <Tag>
                            <Key>key1</Key>
                            <Value>value1</Value>
                        </Tag>
                        <Tag>
                            <Key>key2</Key>
                            <Value>value2</Value>
                        </Tag>
                    </And>
                </Filter>
                <Status>Enabled</Status>
                <Transition>
                    <Days>1</Days>
                    <StorageClass>THREECOPIES</StorageClass>
                </Transition>
                <Expiration>
                    <Days>60</Days>
                </Expiration>
                <NoncurrentVersionTransition>
                    <NoncurrentDays>1</NoncurrentDays>
                    <StorageClass>THREECOPIES</StorageClass>
                </NoncurrentVersionTransition>
                <NoncurrentVersionExpiration>
                    <NoncurrentDays>60</NoncurrentDays>
                </NoncurrentVersionExpiration>
            </Rule>
        </LifecycleConfiguration>
        """
        props = {LIFECYCLE_PROPERTY_KEY: source}
        self.api.container_create(self.account, self.container,
                                  properties=props)
        self.lifecycle.load()
        self.assertEqual(1, len(self.lifecycle.rules))
        rule = self.lifecycle.rules[0]
        self.assertIsNotNone(rule)
        self.assertEqual('rule1', rule.id)
        self.assertIsNotNone(rule.filter)
        self.assertEqual('documents/', rule.filter.prefix)
        self.assertDictEqual({'key1': 'value1', 'key2': 'value2'},
                             rule.filter.tags)
        self.assertTrue(rule.enabled)
        self.assertEqual(4, len(rule.actions))
        expiration = rule.actions[0]
        self.assertEqual(Expiration, type(expiration))
        self.assertEqual(60, expiration.filter.days)
        transition = rule.actions[1]
        self.assertEqual(Transition, type(transition))
        self.assertEqual(1, transition.filter.days)
        self.assertEqual('THREECOPIES', transition.policy)
        expiration = rule.actions[2]
        self.assertEqual(NoncurrentVersionExpiration, type(expiration))
        self.assertEqual(60, expiration.filter.days)
        transition = rule.actions[3]
        self.assertEqual(NoncurrentVersionTransition, type(transition))
        self.assertEqual(1, transition.filter.days)
        self.assertEqual('THREECOPIES', transition.policy)

    def test_save_to_container_property(self):
        source = """
        <LifecycleConfiguration
                xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
            <Rule>
                <ID>rule1</ID>
                <Filter>
                    <And>
                        <Prefix>documents/</Prefix>
                        <Tag>
                            <Key>key</Key>
                            <Value>value</Value>
                        </Tag>
                    </And>
                </Filter>
                <Status>Enabled</Status>
                <Expiration>
                    <Days>60</Days>
                </Expiration>
                <Transition>
                    <StorageClass>SINGLE</StorageClass>
                    <Days>30</Days>
                </Transition>
                <Transition>
                    <StorageClass>THREECOPIES</StorageClass>
                    <Days>1</Days>
                </Transition>
                <NoncurrentVersionExpiration>
                    <NoncurrentDays>60</NoncurrentDays>
                </NoncurrentVersionExpiration>
                <NoncurrentVersionTransition>
                    <StorageClass>THREECOPIES</StorageClass>
                    <NoncurrentDays>1</NoncurrentDays>
                </NoncurrentVersionTransition>
            </Rule>
        </LifecycleConfiguration>
        """

        self.api.container_create(self.account, self.container)
        self.lifecycle.load_xml(source)
        self.lifecycle.save()
        xml = self.lifecycle.get_configuration()
        self.assertEqual(
            source.replace(' ', '').replace('\n', ''),
            xml.replace(' ', '').replace('\n', ''))

    def test_immediate_expiration_by_date(self):
        obj_meta = self._upload_something()
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                </Filter>
                <Expiration>
                    <Date>%s</Date>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """ % self._time_to_date(time.time() + 86400))
        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+172800)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Deleted', status)
        self.assertRaises(NoSuchObject, self.api.object_show,
                          self.account, obj_meta['container'],
                          obj_meta['name'])

    def test_immediate_expiration_by_date_after_new_object(self):
        obj_meta = self._upload_something()
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                </Filter>
                <Expiration>
                    <Date>%s</Date>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """ % self._time_to_date(time.time() + 172800))
        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(
            self.account, obj_meta['container'], obj_meta['name'])

    def test_future_expiration_by_date(self):
        obj_meta = self._upload_something()
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                </Filter>
                <Expiration>
                    <Date>%s</Date>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """ % self._time_to_date(time.time() + 86400))
        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'])

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['name'])

    def test_expiration_by_delay(self):
        obj_meta = self._upload_something()
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                </Filter>
                <Expiration>
                    <Days>1</Days>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'])

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Deleted', status)
        self.assertRaises(NoSuchObject, self.api.object_show,
                          self.account, obj_meta['container'],
                          obj_meta['name'])

    def test_expiration_filtered_by_prefix(self):
        obj_meta = self._upload_something(prefix="photos/")
        obj_meta2 = self._upload_something(prefix="documents/")
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                    <Prefix>documents/</Prefix>
                </Filter>
                <Expiration>
                    <Days>1</Days>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta2)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['orig_name'])
        self.api.object_show(self.account, obj_meta2['container'],
                             obj_meta2['orig_name'])

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Deleted', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['orig_name'])
        self.assertRaises(NoSuchObject, self.api.object_show,
                          self.account, obj_meta2['container'],
                          obj_meta2['orig_name'])

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['orig_name'])

    def test_expiration_filtered_by_tag(self):
        obj_meta = self._upload_something()
        obj_meta2 = self._upload_something(
            properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        obj_meta3 = self._upload_something(
            properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>approved</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </Filter>
                <Expiration>
                    <Days>1</Days>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta2)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta3)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'])
        self.api.object_show(self.account, obj_meta2['container'],
                             obj_meta2['name'])
        self.api.object_show(self.account, obj_meta3['container'],
                             obj_meta3['name'])

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Deleted', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta3, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'])
        self.assertRaises(NoSuchObject, self.api.object_show,
                          self.account, obj_meta2['container'],
                          obj_meta2['name'])
        self.api.object_show(self.account, obj_meta3['container'],
                             obj_meta3['name'])

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['name'])
        self.api.object_delete(self.account, obj_meta3['container'],
                               obj_meta3['name'])

    def test_transition_filtered_by_tag(self):
        obj_meta = self._upload_something(policy='SINGLE')
        obj_meta2 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        obj_meta3 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>approved</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </Filter>
                <Transition>
                    <Days>1</Days>
                    <StorageClass>THREECOPIES</StorageClass>
                </Transition>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta2)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta3)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['name'])
        self.assertEqual('SINGLE', obj_meta2_after['policy'])
        self.assertEqual(1, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Policy changed to THREECOPIES', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta3, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['name'])
        self.assertEqual('THREECOPIES', obj_meta2_after['policy'])
        self.assertEqual(3, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['name'])
        self.api.object_delete(self.account, obj_meta2['container'],
                               obj_meta2['name'])
        self.api.object_delete(self.account, obj_meta3['container'],
                               obj_meta3['name'])

    def test_transition_filtered_by_tags(self):
        obj_meta = self._upload_something(policy='SINGLE')
        obj_meta2 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>test1</Key>
                        <Value>test2</Value>
                    </Tag>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        obj_meta3 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                    <And>
                        <Tag>
                            <Key>status</Key>
                            <Value>deprecated</Value>
                        </Tag>
                        <Tag>
                            <Key>test1</Key>
                            <Value>test2</Value>
                        </Tag>
                    </And>
                </Filter>
                <Transition>
                    <Days>1</Days>
                    <StorageClass>THREECOPIES</StorageClass>
                </Transition>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta2)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta3)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['name'])
        self.assertEqual('SINGLE', obj_meta2_after['policy'])
        self.assertEqual(1, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Policy changed to THREECOPIES', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta3, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['name'])
        self.assertEqual('THREECOPIES', obj_meta2_after['policy'])
        self.assertEqual(3, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['name'])
        self.api.object_delete(self.account, obj_meta2['container'],
                               obj_meta2['name'])
        self.api.object_delete(self.account, obj_meta3['container'],
                               obj_meta3['name'])

    def test_transition_filtered_by_prefix_and_tags(self):
        obj_meta = self._upload_something(policy='SINGLE', prefix="documents/")
        obj_meta2 = self._upload_something(
            policy='SINGLE', prefix="documents/", properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>test1</Key>
                        <Value>test2</Value>
                    </Tag>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        obj_meta3 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>test1</Key>
                        <Value>test2</Value>
                    </Tag>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """})
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter>
                    <And>
                        <Prefix>documents/</Prefix>
                        <Tag>
                            <Key>status</Key>
                            <Value>deprecated</Value>
                        </Tag>
                        <Tag>
                            <Key>test1</Key>
                            <Value>test2</Value>
                        </Tag>
                    </And>
                </Filter>
                <Transition>
                    <Days>1</Days>
                    <StorageClass>THREECOPIES</StorageClass>
                </Transition>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta2)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta3)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['orig_name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['orig_name'])
        self.assertEqual('SINGLE', obj_meta2_after['policy'])
        self.assertEqual(1, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['orig_name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta2, obj_meta2_copy)
        self.assertEqual('Policy changed to THREECOPIES', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta3, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta3_copy, _, _, status = results[0]
        self.assertEqual(obj_meta3, obj_meta3_copy)
        self.assertEqual('Kept', status)
        obj_meta_after, chunks = self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['orig_name'])
        self.assertEqual('SINGLE', obj_meta_after['policy'])
        self.assertEqual(1, len(chunks))
        obj_meta2_after, chunks2 = self.api.object_locate(
            self.account, obj_meta2['container'], obj_meta2['orig_name'])
        self.assertEqual('THREECOPIES', obj_meta2_after['policy'])
        self.assertEqual(3, len(chunks2))
        obj_meta3_after, chunks3 = self.api.object_locate(
            self.account, obj_meta3['container'], obj_meta3['orig_name'])
        self.assertEqual('SINGLE', obj_meta3_after['policy'])
        self.assertEqual(1, len(chunks3))

        self.api.object_delete(self.account, obj_meta['container'],
                               obj_meta['orig_name'])
        self.api.object_delete(self.account, obj_meta2['container'],
                               obj_meta2['orig_name'])
        self.api.object_delete(self.account, obj_meta3['container'],
                               obj_meta3['orig_name'])

    def test_expiration_with_versioning(self):
        self._enable_versioning()
        obj_meta = self._upload_something()
        obj_meta_v2 = self._upload_something(path=obj_meta['name'])
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter></Filter>
                <Expiration>
                    <Days>1</Days>
                </Expiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta_v2)]
        self.assertEqual(1, len(results))
        obj_meta_v2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta_v2, obj_meta_v2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, self.container, obj_meta_v2['name'],
                             version=obj_meta_v2['version'])
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'], version=obj_meta['version'])

        results = [x for x in self.lifecycle.apply(
            obj_meta_v2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_v2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta_v2, obj_meta_v2_copy)
        self.assertEqual('Deleted', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.assertRaises(NoSuchObject, self.api.object_locate,
                          self.account, self.container, obj_meta_v2['name'])
        self.api.object_show(self.account, self.container, obj_meta_v2['name'],
                             version=obj_meta_v2['version'])
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'], version=obj_meta['version'])

        self.api.object_delete(
            self.account, obj_meta['container'], obj_meta['name'],
            version=obj_meta['version'])

    def test_noncurrent_expiration(self):
        self._enable_versioning()
        obj_meta = self._upload_something()
        obj_meta_v2 = self._upload_something(path=obj_meta['name'])
        self.lifecycle.load_xml("""
        <LifecycleConfiguration>
            <Rule>
                <Filter></Filter>
                <NoncurrentVersionExpiration>
                    <NoncurrentDays>1</NoncurrentDays>
                </NoncurrentVersionExpiration>
                <Status>enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """)

        results = [x for x in self.lifecycle.apply(obj_meta_v2)]
        self.assertEqual(1, len(results))
        obj_meta_v2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta_v2, obj_meta_v2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(obj_meta)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Kept', status)
        self.api.object_show(self.account, self.container, obj_meta_v2['name'],
                             version=obj_meta_v2['version'])
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['name'], version=obj_meta['version'])

        results = [x for x in self.lifecycle.apply(
            obj_meta_v2, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_v2_copy, _, _, status = results[0]
        self.assertEqual(obj_meta_v2, obj_meta_v2_copy)
        self.assertEqual('Kept', status)
        results = [x for x in self.lifecycle.apply(
            obj_meta, now=time.time()+86400)]
        self.assertEqual(1, len(results))
        obj_meta_copy, _, _, status = results[0]
        self.assertEqual(obj_meta, obj_meta_copy)
        self.assertEqual('Deleted', status)
        self.api.object_show(self.account, self.container,
                             obj_meta_v2['name'],
                             version=obj_meta_v2['version'])
        self.assertRaises(NoSuchObject, self.api.object_show,
                          self.account, obj_meta['container'],
                          obj_meta['name'], version=obj_meta['version'])

        self.api.object_delete(
            self.account, self.container, obj_meta_v2['name'],
            version=obj_meta_v2['version'])

    def test_execute_expiration(self):
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <ID>rule1</ID>
                    <Filter>
                    </Filter>
                    <Status>Enabled</Status>
                    <Expiration>
                        <Days>1</Days>
                    </Expiration>
                </Rule>
            </LifecycleConfiguration>
            """})
        for _ in range(3):
            self._upload_something()
        self.lifecycle.load()

        results = [x for x in self.lifecycle.execute()]
        self.assertEqual(3, len(results))
        for res in results:
            self.assertEqual('Kept', res[3])
        listing = self.api.object_list(self.account, self.container)
        self.assertEqual(3, len(listing['objects']))

        results = [x for x in self.lifecycle.execute(now=time.time()+86400)]
        self.assertEqual(3, len(results))
        for res in results:
            self.assertEqual('Deleted', res[3])
        listing = self.api.object_list(self.account, self.container)
        self.assertEqual(0, len(listing['objects']))

    def test_execute_expiration_with_disabled_status(self):
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <ID>rule1</ID>
                    <Filter>
                    </Filter>
                    <Status>Disabled</Status>
                    <Expiration>
                        <Days>1</Days>
                    </Expiration>
                </Rule>
            </LifecycleConfiguration>
            """})
        for _ in range(3):
            self._upload_something()
        self.lifecycle.load()

        results = [x for x in self.lifecycle.execute()]
        self.assertEqual(3, len(results))
        for res in results:
            self.assertEqual('Kept', res[3])
        listing = self.api.object_list(self.account, self.container)
        self.assertEqual(3, len(listing['objects']))

        results = [x for x in self.lifecycle.execute(now=time.time()+86400)]
        self.assertEqual(3, len(results))
        for res in results:
            self.assertEqual('Kept', res[3])
        listing = self.api.object_list(self.account, self.container)
        self.assertEqual(3, len(listing['objects']))

    def test_execute_expiration_on_missing_objects(self):
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <ID>rule1</ID>
                    <Filter>
                    </Filter>
                    <Status>Enabled</Status>
                    <Expiration>
                        <Days>1</Days>
                    </Expiration>
                </Rule>
            </LifecycleConfiguration>
            """})
        fake_listing = {
            'objects': [
                {'name': 'a', 'version': 1540933092888883, 'mtime': '12',
                 'deleted': False},
                {'name': 'b', 'version': 1540933092888883, 'mtime': '12',
                 'deleted': False},
                {'name': 'c', 'version': 1540933092888883, 'mtime': '12',
                 'deleted': False},
                {'name': 'd', 'version': 1540933092888883, 'mtime': '12',
                 'deleted': False}],
            'truncated': False
        }
        with patch.object(self.api, 'object_list',
                          side_effect=[fake_listing]):
            self.lifecycle.load()

            results = [x for x in self.lifecycle.execute()]
            self.assertEqual(4, len(results))
            for res in results:
                self.assertIsInstance(res[3], Exception)

    def test_execute_exceeding_version_expiration_without_versioning(self):
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <ID>rule1</ID>
                    <Filter>
                    </Filter>
                    <Status>Enabled</Status>
                    <NoncurrentVersionExpiration>
                        <NoncurrentCount>2</NoncurrentCount>
                    </NoncurrentVersionExpiration>
                </Rule>
            </LifecycleConfiguration>
            """})
        for _ in range(5):
            self._upload_something()
        self.lifecycle.load()

        results = [x for x in self.lifecycle.execute()]
        self.assertEqual(5, len(results))
        for res in results:
            self.assertEqual('Kept', res[3])
        listing = self.api.object_list(self.account, self.container,
                                       versions=True)
        self.assertEqual(5, len(listing['objects']))

    def test_execute_exceeding_version_expiration_with_versioning(self):
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <ID>rule1</ID>
                    <Filter>
                    </Filter>
                    <Status>Enabled</Status>
                    <NoncurrentVersionExpiration>
                        <NoncurrentCount>2</NoncurrentCount>
                    </NoncurrentVersionExpiration>
                </Rule>
            </LifecycleConfiguration>
            """}, system={"sys.m2.policy.version": "4"})
        for _ in range(5):
            self._upload_something(path="versioned1")
        for _ in range(5):
            self._upload_something(path="versioned2")
        listing = self.api.object_list(self.account, self.container,
                                       versions=True)
        self.assertEqual(10, len(listing['objects']))
        self.lifecycle.load()

        results = [x for x in self.lifecycle.execute()]
        self.assertEqual(10, len(results))
        listing = self.api.object_list(self.account, self.container,
                                       versions=True)
        self.assertEqual(6, len(listing['objects']))

    def test_execute_multiple_rules(self):
        meta = {"sys.m2.policy.version": "4"}
        self.api.container_create(
            self.account, self.container, properties={LIFECYCLE_PROPERTY_KEY: """
            <LifecycleConfiguration>
                <Rule>
                    <Filter>
                        <Tag>
                            <Key>status</Key>
                            <Value>deprecated</Value>
                        </Tag>
                    </Filter>
                    <Expiration>
                        <Days>1</Days>
                    </Expiration>
                    <Status>enabled</Status>
                </Rule>
                <Rule>
                    <Filter>
                        <Prefix>documents/</Prefix>
                    </Filter>
                    <Expiration>
                        <Days>1</Days>
                    </Expiration>
                    <Status>Enabled</Status>
                </Rule>
            </LifecycleConfiguration>
            """}, system=meta)
        obj_meta = self._upload_something(policy='SINGLE', system=meta)
        obj_meta2 = self._upload_something(
            policy='SINGLE', properties={TAGGING_KEY: """
            <Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
                <TagSet>
                    <Tag>
                        <Key>status</Key>
                        <Value>deprecated</Value>
                    </Tag>
                </TagSet>
            </Tagging>
            """}, system=meta)
        obj_meta3 = self._upload_something(path="documents/object3",
                                           policy='SINGLE',
                                           system=meta)
        self.lifecycle.load()

        results = [x for x in self.lifecycle.execute()]
        self.assertEqual(6, len(results))
        for res in results:
            self.assertEqual('Kept', res[3])
        self.api.object_show(self.account, obj_meta['container'],
                             obj_meta['orig_name'])
        self.api.object_show(self.account, obj_meta2['container'],
                             obj_meta2['orig_name'])
        self.api.object_show(self.account, obj_meta3['container'],
                             obj_meta3['orig_name'])

        results = [x for x in self.lifecycle.execute(now=time.time()+86400)]
        self.assertEqual(5, len(results))
        self.api.object_locate(
            self.account, obj_meta['container'], obj_meta['orig_name'])
        self.api.object_show(
            self.account, obj_meta['container'], obj_meta['orig_name'],
            version=obj_meta['version'])
        self.assertRaises(NoSuchObject, self.api.object_locate,
                          self.account, obj_meta2['container'],
                          obj_meta2['orig_name'])
        self.api.object_show(
            self.account, obj_meta2['container'], obj_meta2['orig_name'],
            version=obj_meta2['version'])
        self.assertRaises(NoSuchObject, self.api.object_locate,
                          self.account, obj_meta3['container'],
                          obj_meta3['orig_name'])
        self.api.object_show(
            self.account, obj_meta3['container'], obj_meta3['orig_name'],
            version=obj_meta3['version'])

        self.api.object_delete(
            self.account, obj_meta['container'], obj_meta['orig_name'],
            version=obj_meta['version'])
        self.api.object_delete(
            self.account, obj_meta2['container'], obj_meta2['orig_name'],
            version=obj_meta2['version'])
        self.api.object_delete(
            self.account, obj_meta3['container'], obj_meta3['orig_name'],
            version=obj_meta3['version'])