Repository: ansible/ansible

Issue:
AttributeError: 'S3' object has no attribute 'delete_lifecycle_configuration'
##### SUMMARY
Since Ansible 2.6.0, managing an S3 bucket's lifecycle with `s3_lifecycle` is failing when `state` is `absent`.  This seems to be related to changes in https://github.com/ansible/ansible/pull/40161, which ported s3_lifecycle to boto3.  I believe I am currently using the latest versions of boto, boto3 and botocore, as shown below.  A colleague of mine (also running Ubuntu) is also experiencing this issue, and doesn't necessarily share any config specific to my environment.

```
Name: boto
Version: 2.48.0
Summary: Amazon Web Services Library
Home-page: https://github.com/boto/boto/
Author: Mitch Garnaat
Author-email: mitch@garnaat.com
License: MIT
Location: /usr/local/lib/python2.7/dist-packages
Requires: 
---
Name: boto3
Version: 1.7.52
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: UNKNOWN
License: Apache License 2.0
Location: /usr/local/lib/python2.7/dist-packages
Requires: jmespath, botocore, s3transfer
---
Name: botocore
Version: 1.10.52
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: UNKNOWN
License: Apache License 2.0
Location: /usr/local/lib/python2.7/dist-packages
Requires: jmespath, docutils, python-dateutil
```

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
s3_lifecycle.py

##### ANSIBLE VERSION
```
ansible 2.6.1
  config file = /home/benp/.ansible.cfg
  configured module search path = [u'/home/benp/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0]
```

##### CONFIGURATION
ANSIBLE_PIPELINING(/home/benp/.ansible.cfg) = True

##### OS / ENVIRONMENT
Ubuntu 17.10

##### STEPS TO REPRODUCE
Replace `some-test-bucket` with an arbitrary, non-existing bucket name in the following playbook, and `my-profile` with an AWS profile that allows you to create/manage S3 buckets in some account, and then run it.
```yaml
- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
  - name: Manage bucket 'some-test-bucket'
    s3_bucket:
      name: "some-test-bucket"
      state: present
      profile:        "my-profile"
      region:         "us-east-1"

  - name: Manage bucket lifecycle for 'some-test-bucket'
    s3_lifecycle:
      name: "some-test-bucket"
      prefix: ""
      status: enabled
      state: absent
      profile:        "my-profile"
      region:         "us-east-1"
```

##### EXPECTED RESULTS
The bucket is created with no lifecycle configuration.

##### ACTUAL RESULTS
```
TASK [Manage bucket lifecycle for 'some-test-bucket'] **********************************************************************************************************************************************************************************************************************
task path: /home/benp/workspace/devops/testcase.yml:14
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/s3_lifecycle.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: benp
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 498, in <module>
    main()
  File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 494, in main
    destroy_lifecycle_rule(client, module)
  File "/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py", line 419, in destroy_lifecycle_rule
    client.delete_lifecycle_configuration(Bucket=name)
  File "/usr/local/lib/python2.7/dist-packages/botocore/client.py", line 555, in __getattr__
    self.__class__.__name__, item)
AttributeError: 'S3' object has no attribute 'delete_lifecycle_configuration'

fatal: [localhost]: FAILED! => {
    "changed": false, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 498, in <module>\n    main()\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 494, in main\n    destroy_lifecycle_rule(client, module)\n  File \"/tmp/ansible_hTzAdL/ansible_module_s3_lifecycle.py\", line 419, in destroy_lifecycle_rule\n    client.delete_lifecycle_configuration(Bucket=name)\n  File \"/usr/local/lib/python2.7/dist-packages/botocore/client.py\", line 555, in __getattr__\n    self.__class__.__name__, item)\nAttributeError: 'S3' object has no attribute 'delete_lifecycle_configuration'\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 1
}
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/s3_lifecycle.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/s3_lifecycle.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/s3_lifecycle.py b/lib/ansible/modules/cloud/amazon/s3_lifecycle.py
--- a/lib/ansible/modules/cloud/amazon/s3_lifecycle.py
+++ b/lib/ansible/modules/cloud/amazon/s3_lifecycle.py
@@ -415,8 +415,9 @@ def destroy_lifecycle_rule(client, module):
     try:
         if lifecycle_obj['Rules']:
             client.put_bucket_lifecycle_configuration(Bucket=name, LifecycleConfiguration=lifecycle_obj)
-        else:
-            client.delete_lifecycle_configuration(Bucket=name)
+        elif current_lifecycle_rules:
+            changed = True
+            client.delete_bucket_lifecycle(Bucket=name)
     except (ClientError, BotoCoreError) as e:
         module.fail_json_aws(e)
     module.exit_json(changed=changed)


Test patch: