Как отфильтровать последнюю версию каждого файла в дереве, используя Ansible? - PullRequest
2 голосов
/ 11 января 2020

У меня есть дерево каталогов среднего размера с множеством файлов:

  • / some / place / distfiles / foo-1.2.jar
  • / some / place / distfiles /subdir/foo-1.3.jar
  • / some / place / distfiles / bar-1.1.jar
  • / some / place / distfiles / bar-1.1.2.jar

Я использую модуль поиска , чтобы получить полный список, но мне нужны только последние версии для каждой foo и бара . Например, приведенный выше набор необходимо уменьшить до:

  • / some / place / distfiles / subdir / foo-1.3.jar
  • / some / place / distfiles / bar -1.1.2.jar

Нет, я не могу полагаться на временные метки файлов - только на числовые части имен файлов ...

Может кто-нибудь предложить элегантный способ делать это?

Ответы [ 2 ]

2 голосов
/ 11 января 2020

Это будет немного нетривиальное количество jinja2, так что вы можете быть счастливее писать собственный модуль, чтобы вы могли работать на реальном языке программирования (вы все равно можете использовать find: и register: для захвата списка имен файлов) и затем отдайте его в свой пользовательский модуль, чтобы свернуть «последние» для каждого файла на основе ваших правил)

Тем не менее, я думаю, можно использовать version test чтобы найти самое новое после того, как вы разбили элементы на tuple[str, str] от базового имени до номера версии:

- find:
    paths:
    - /some/place/distfiles
    # etc
  register: my_jars
- set_fact:
    versions_by_filename: >-
       {%- set results = {} -%}
       {%- for f in my_jars.files -%}
       {%-   set bn = f.path | basename 
             | regex_replace(ver_regex, '\\1') -%}
       {%-   set v = f.path | regex_replace(ver_regex, '\\2') -%}
       {%-   set _ = results.setdefault(bn, []).append(v) -%}
       {%- endfor -%}
       {{ results }}
  vars:
    ver_regex: '(.*)-([0-9.-]+)\.jar'

- set_fact:
    most_recent: >-
       {%- set results = {} -%}
       {%- for fn, ver_list in versions_by_filename.items() -%}
       {%-   set tmp = namespace(latest=ver_list[0]) -%}
       {%-   for v in ver_list -%}
       {%-      if tmp.latest is version(v, '<') -%}
       {%-        set tmp.latest = v -%}
       {%-      endif -%}
       {%-   endfor -%}
       {%-   set _ = results.update({fn: tmp.latest}) -%}
       {%- endfor -%}
       {{ results }}
1 голос
/ 11 января 2020

Давайте сначала преобразуем данные для этой цели. Например,

   - set_fact:
        my_files: "{{ result.files|json_query('[].path') }}"
    - debug:
        var: my_files

дает

  my_files:
  - /some/place/distfiles/foo-1.2.jar
  - /some/place/distfiles/bar-1.1.jar
  - /some/place/distfiles/bar-1.1.2.jar
  - /some/place/distfiles/subdir/foo-1.3.jar

Создать список словарей

    - set_fact:
        my_dict: "{{ my_dict
                    |default([]) + [
                     dict(['path', 'archive', 'version']
                          |zip([item,
                                (item|basename).split('-')[0],
                                (item|basename).split('-')[1]|splitext|list|first])) ] }}"
      loop: "{{ my_files }}"
    - debug:
        var: my_dict

дает

  my_dict:
  - archive: foo
    path: /some/place/distfiles/foo-1.2.jar
    version: '1.2'
  - archive: bar
    path: /some/place/distfiles/bar-1.1.jar
    version: '1.1'
  - archive: bar
    path: /some/place/distfiles/bar-1.1.2.jar
    version: 1.1.2
  - archive: foo
    path: /some/place/distfiles/subdir/foo-1.3.jar
    version: '1.3'

Группировать элементы по имя архива

    - set_fact:
        my_groups: "{{ my_dict|groupby('archive') }}"
    - debug:
        var: my_groups

дает

  my_groups:
  - - bar
    - - archive: bar
        path: /some/place/distfiles/bar-1.1.jar
        version: '1.1'
      - archive: bar
        path: /some/place/distfiles/bar-1.1.2.jar
        version: 1.1.2
  - - foo
    - - archive: foo
        path: /some/place/distfiles/foo-1.2.jar
        version: '1.2'
      - archive: foo
        path: /some/place/distfiles/subdir/foo-1.3.jar
        version: '1.3'

Когда данные готовы, найдите последние версии и распечатайте результаты

    - debug:
        msg: "Latest version of {{ item.0 }} is {{ item.1|json_query(query)|first }}"
      vars:
        query: "[?version == '{{ latest_version }}'].path"
        latest_version: "{{ item.1|json_query('[].version')|max }}"
      loop: "{{ my_groups }}"

дает

  msg: Latest version of bar is /some/place/distfiles/bar-1.1.2.jar
  msg: Latest version of foo is /some/place/distfiles/subdir/foo-1.3.jar


Примечания
  • Проблема с фильтром max

Кредит @mdaniel: "Макс страдает из давней проблемы попытки использовать лексикографическую сортировку для номеров версий, поскольку он утверждает, что из вариантов 1.2 и 1.10, что 1.2 является «самым последним»

Это возможно создать пользовательский плагин фильтра с фильтром и использовать его для выбора последней версии. Например,

$ cat filter_plugins/version_filters.py

from distutils.version import LooseVersion

def version_max(l):
    return sorted(l, key=LooseVersion)[-1]

class FilterModule(object):

    def filters(self):
        return {
            'version_max' : version_max
            }
latest_version: "{{ item.1|json_query('[].version')|version_max }}"
  • Игра ожидает, что имена файлов имеют формат
<archive>-<version>.<extension>
...