Repository: ansible/ansible

Issue:
'with_sequence' should not fail with 'count=0'
**Issue Type:** Bug Report

**Ansible Version:** 1.8.4

**Environment:** N/A

**Summary:**

The `with_sequence` statement should not fail when used with `count=0`. Instead, it should just skip the task.

**Steps To Reproduce:**

Here is a minimal playbook `bug.yml` to illustrate the issue:

``` yaml

---
- hosts: all
  gather_facts: False
  tasks:
    - name: "This task should be skipped because count=0 in the 'with_sequence' statement"
      command: echo "Hello world"
      with_sequence: count=0
```

It can be run with:

`ansible-playbook -c local -i localhost, bug.yml`

**Expected Results:**

The task should be skipped because `count=0`.

**Actual Results:**

The task fails with the error message "can't count backwards" (which is counterintuitive because `count` is zero, not negative).

```
TASK: [command echo "Hello world"] ******************************************** 
fatal: [localhost] => can't count backwards

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



Discussion:
+1 this is a hell of a bug, since apprently my "when" to skip including the task doesn't prevent this from being evaluate and shitting all over the place.


Resolving patch:
diff --git a/lib/ansible/runner/lookup_plugins/sequence.py b/lib/ansible/runner/lookup_plugins/sequence.py
--- a/lib/ansible/runner/lookup_plugins/sequence.py
+++ b/lib/ansible/runner/lookup_plugins/sequence.py
@@ -151,15 +151,26 @@ def sanity_check(self):
             )
         elif self.count is not None:
             # convert count to end
-            self.end = self.start + self.count * self.stride - 1
+            if self.count != 0:
+                self.end = self.start + self.count * self.stride - 1
+            else:
+                self.start = 0
+                self.end = 0
+                self.stride = 0
             del self.count
-        if self.end < self.start:
-            raise AnsibleError("can't count backwards")
+        if self.stride > 0 and self.end < self.start:
+            raise AnsibleError("to count backwards make stride negative")
+        if self.stride < 0 and self.end > self.start:
+            raise AnsibleError("to count forward don't make stride negative")
         if self.format.count('%') != 1:
             raise AnsibleError("bad formatting string: %s" % self.format)
 
     def generate_sequence(self):
-        numbers = xrange(self.start, self.end + 1, self.stride)
+        if self.stride > 0:
+            adjust = 1
+        else:
+            adjust = -1
+        numbers = xrange(self.start, self.end + adjust, self.stride)
 
         for i in numbers:
             try:
@@ -193,12 +204,13 @@ def run(self, terms, inject=None, **kwargs):
 
                 self.sanity_check()
 
-                results.extend(self.generate_sequence())
+                if self.start != self.end:
+                    results.extend(self.generate_sequence())
             except AnsibleError:
                 raise
-            except Exception:
+            except Exception, e:
                 raise AnsibleError(
-                    "unknown error generating sequence"
+                    "unknown error generating sequence: %s" % str(e)
                 )
 
         return results


Test patch:
diff --git a/test/integration/roles/test_iterators/tasks/main.yml b/test/integration/roles/test_iterators/tasks/main.yml
--- a/test/integration/roles/test_iterators/tasks/main.yml
+++ b/test/integration/roles/test_iterators/tasks/main.yml
@@ -60,6 +60,10 @@
   set_fact: "{{ 'x' + item }}={{ item }}"
   with_sequence: start=0 end=3
 
+- name: test with_sequence backwards
+  set_fact: "{{ 'y' + item }}={{ item }}"
+  with_sequence: start=3 end=0 stride=-1
+
 - name: verify with_sequence
   assert:
     that:
@@ -67,6 +71,20 @@
         - "x1 == '1'"
         - "x2 == '2'"
         - "x3 == '3'"
+        - "y3 == '3'"
+        - "y2 == '2'"
+        - "y1 == '1'"
+        - "y0 == '0'"
+
+- name: test with_sequence not failing on count == 0
+  debug: msg='previously failed with backward counting error'
+  with_sequence: count=0
+  register: count_of_zero
+
+- assert:
+    that:
+        - count_of_zero | skipped
+        - not count_of_zero | failed
 
 # WITH_RANDOM_CHOICE