ansible / proposals

Repository for sharing and tracking progress on enhancement proposals for Ansible.
Creative Commons Zero v1.0 Universal
93 stars 19 forks source link

Ansible loop interface redesign #140

Open bcoca opened 5 years ago

bcoca commented 5 years ago

Proposal: future loops

Author: Brian Coca <@bcoca> IRC: bcoca

Date: 2016-02-32

Motivation

Current with_ loops are misleading and have much magic that confuses users and obfuscates what really goes on. The until/retry keywords are 'also loops' and don't interact well with with_ loops.

Problems

Solution proposal

Redesign loop syntax (names not final, feel free to bikeshed)

    # loops, default values!
- action: ...
  loop:
    over: []
    until:
      when: result is failed
      retry: 0
      scope: item
    pause: 0
    display: item
    variable: item
    index: _index
    last: _last
    squash_option: name
    forks: 1

The terms

OPTIONAL

This would give a cleaner and clearer interface for all looping needs as well as unify the until/retry and 'list looping' into one structure and hopefully do the same in execution so they can work together intelligently.

A lot of this is base on feedback for https://github.com/ansible/ansible/issues/12086, some we are implementing already in other wa ys.

caphrim007 commented 5 years ago

for the squash_option field, was wondering how might modules decide to support this? And/or Ansible report if a module does not support this (like interrogating the module argspec before exec'ing it)

for example, if a module foo_member today recommends that a user use the loop construct to create X members;

foo_member:
   first: "{{ item.first }}"
   middle: "{{ item.middle }"
   last: "{{ item.last }}"
loop:
  - first: Alice
    middle: M
    last: User
  - first: Bob
    middle: C
    last: User

Is this module a candidate for squash_option? and how would the definitions of the arguments in this module's argument spec need to change? For instance, if today they were type='str'?

What would be the recommendation from Ansible?

bcoca commented 5 years ago

actually, that is one i'm now partial to dropping (i really just pasted pretty old proposal) .. squashing should not be needed, people should just pass the list directly to fields for modules that support it.

bcoca commented 5 years ago

one thing the current loop has, it avoids 'flattening' magic so now it would be possible to use 'naked' jinja2 expresions again. Since now it is simple to disambiguate between a string as part of a list vs a string that represents a variable or expression.

Even the patch is simple:

diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py
index 84c2dad295..6de788ca87 100644
--- a/lib/ansible/executor/task_executor.py
+++ b/lib/ansible/executor/task_executor.py
@@ -236,7 +236,12 @@ class TaskExecutor:
                 raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop_with)

         elif self._task.loop:
-            items = templar.template(self._task.loop)
+
+            if isinstance(self._task.loop, string_types) and not templar.is_template(self._task.loop):
+                items = templar.template('{{%s}}' % self._task.loop)
+            else:
+                items = templar.template(self._task.loop)
+
             if not isinstance(items, list):
                 raise AnsibleError(
                     "Invalid data passed to 'loop', it requires a list, got this instead: %s."

so now you could do:


- debug: var=item
  loop: test
  vars:
    test: [1,2,3]
webknjaz commented 5 years ago

Ref: https://github.com/ansible/ansible/issues/44741

webknjaz commented 5 years ago

Integration tests for currently supported interaction between loop and until: https://github.com/ansible/ansible/pull/44927

dagwieers commented 5 years ago

@caphrim007 For squashing and pure functionality, we still have this one open: https://github.com/ansible/proposals/issues/71

vbotka commented 4 years ago

(ansible 2.9.6) FWIW, the task below

    - command: '[ "{{ item }}" -gt "3" ]'
      loop: "{{ range(1, 5 + 1)|list }}"
      register: result
      ignore_errors: true
      when: not condition
      vars:
        condition: '{{ (result|default({"rc": 1})).rc == 0 }}'

gives (abridged)

    changed: [localhost] => (item=4)
    skipping: [localhost] => (item=5) 
    ...ignoring