Repository: ansible/ansible-modules-core

Issue:
ec2_asg errors when using replace_all_instances but not specifying batch max_size
##### Issue Type: Bug Report
##### Ansible Version:

ansible 1.8.2 (detached HEAD 6f568bd649) last updated 2014/12/18 17:37:34 (GMT +000)
  lib/ansible/modules/core: (detached HEAD 54ee8aa8e8) last updated 2014/12/18 17:37:43 (GMT +000)
  lib/ansible/modules/extras: (detached HEAD c60441fddd) last updated 2014/12/18 17:37:44 (GMT +000)
  v2/ansible/modules/core: (detached HEAD cb69744bce) last updated 2014/12/18 16:21:43 (GMT +000)
  v2/ansible/modules/extras: (detached HEAD 8a4f07eecd) last updated 2014/12/18 16:21:49 (GMT +000)
  configured module search path = None
##### Environment:

AWS Linux AMI
##### Summary:

I'm trying to replace all instances in an auto scaling group but don't want to specify min/max/desired capacity since these shouldn't change, when doing so ansible throws an error.

The min/max/desired capacity options are set as required in the docs and it'd be ideal if they didn't have to be otherwise I'd have to go and check the values before hand and edit the playbook.
##### Steps To Reproduce:

Example playbook

```
- hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Replace autoscaling group instances
      ec2_asg: >
          replace_all_instances=yes
          name=autoscalinggroup
          region=eu-west-1
          launch_config_name=launchconfig
```
##### Expected Results:

The instances in the group to be replaced one by one
##### Actual Results:

```
failed: [localhost] => {"failed": true, "parsed": false}
Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1418925017.49-215566664631245/ec2_asg", line 2355, in <module>
    main()
  File "/root/.ansible/tmp/ansible-tmp-1418925017.49-215566664631245/ec2_asg", line 2350, in main
    replace_changed, asg_properties=replace(connection, module)
  File "/root/.ansible/tmp/ansible-tmp-1418925017.49-215566664631245/ec2_asg", line 2204, in replace
    as_group.max_size = max_size + batch_size
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'


FATAL: all hosts have already failed -- aborting
```



Discussion:
Let me explain how this works, and maybe that will help figure out a way to solve the problem.  When we are doing a rolling replace, we temporarily increase the the instance count of the ASG by the batch size, so we always have a fully functional ASG, even when instances are being replaced.  Once the instances are finished being replaced, the ASG is reduced to its normal size.  The reason why you have to specify the sizes is in case Ansible crashes at some point or someone prematurely exits the play,  there is no way for Ansible to know what size the ASG should return to.

Correct me if I'm wrong, but if ansible crashes or someone prematurely exits the play then the ASG will be between the size it should be and the size it should be plus the batch size depending on when the process ended. So worst case scenario there's extra instances laying around that are probably in an unknown state in which case the user would intervene and tidy up the instances manually.

Example, if we tried to infer what it should be based on the size currently returned by the ec2 api, here's how it could break:
1. ASG originally had a desired capacity of 5
2. during a rolling replace it was modified to 6 (5 + batch size of 1)
3.  for some reason the playbook crashed or was otherwise interrupted
4.  Playbook runs again
5. during a rolling replace it was modified to 7 (6 + batch size of 1)

and so on.


Resolving patch:
diff --git a/cloud/amazon/ec2_asg.py b/cloud/amazon/ec2_asg.py
--- a/cloud/amazon/ec2_asg.py
+++ b/cloud/amazon/ec2_asg.py
@@ -47,15 +47,15 @@
     required: false
   min_size:
     description:
-      - Minimum number of instances in group
+      - Minimum number of instances in group, if unspecified then the current group value will be used.
     required: false
   max_size:
     description:
-      - Maximum number of instances in group
+      - Maximum number of instances in group, if unspecified then the current group value will be used.
     required: false
   desired_capacity:
     description:
-      - Desired number of instances in group
+      - Desired number of instances in group, if unspecified then the current group value will be used.
     required: false
   replace_all_instances:
     description:
@@ -449,6 +449,13 @@ def replace(connection, module):
         changed = False
         return(changed, props)
         
+    #check if min_size/max_size/desired capacity have been specified and if not use ASG values
+    if min_size is None:
+        min_size = as_group.min_size
+    if max_size is None:
+        max_size = as_group.max_size
+    if desired_capacity is None:
+        desired_capacity = as_group.desired_capacity
     # set temporary settings and wait for them to be reached
     as_group.max_size = max_size + batch_size
     as_group.min_size = min_size + batch_size


Test patch: