Ansible: сравнить два массива по конкретному полю - PullRequest
0 голосов
/ 31 мая 2019

У меня есть два массива с описаниями приложений:

source_array:
  - status: Active
    AppName": "Application 1"
    version: "0.1.1"
    metadata: ""
  - status": "Active"
    AppName: "Application 2"
    version: "0.2.2"
    metadata: "ID123"
  - status: "Active"
    AppName: "Application 3"
    version: "0.3.3"
    metadata: ""

И:

target_array:
  - status: "Active"
    AppName: "Application 1"
    version: "0.1.1"
    metadata: ""
  - status: "Active"
    AppName: "Application 2"
    version: "0.2.2"
    metadata: "ID321"
  - status: "Active",
    AppName: "Application 3"
    version: "0.3.0"
    metadata: ""

Мне нужно сравнить эти два массива на основе поля версии.Так, например, желаемый результат должен быть:

[{
    "status": "Active",
    "AppName": "Application 3",
    "version": "0.3.0",
    "metadata": ""
}]

Я пытался использовать разностный фильтр, но он также возвращает второй элемент - так как он имеет разные метаданные

- name: Comparing arrays
  set_fact:
    delta: "{{ source_array | difference(target_array) }}"

И у меня неверный результат:

[{
    "status": "Active",
    "AppName": "Application 2",
    "version": "0.2.2",
    "metadata": "ID123"
},
{
    "status": "Active",
    "AppName": "Application 3",
    "version": "0.3.3",
    "metadata": ""
},
{
    "status": "Active",
    "AppName": "Application 2",
    "version": "0.2.2",
    "metadata": "ID321"
},
{
    "status": "Active",
    "AppName": "Application 3",
    "version": "0.3.0",
    "metadata": ""
}]

Любая помощь будет высоко оценена!

1 Ответ

0 голосов
/ 31 мая 2019

Это на самом деле не тривиально. Вы не предоставляете много контекста, но я подозреваю, что вы хотите сделать что-то вроде проверки, было ли приложение обновлено или должно быть обновлено. Правильно?

Вот один из способов:

- hosts: localhost
  vars:
    array1:
      - status: "Active"
        AppName: "Application 1"
        version: "0.1.1"
        metadata: ""
      - status: "Active"
        AppName: "Application 2"
        version: "0.2.2"
        metadata: "ID321"
      - status: "Active"
        AppName: "Application 3"
        version: "0.3.3"
        metadata: ""
    array2:
      - status: "Active"
        AppName: "Application 1"
        version: "0.1.1"
        metadata: ""
      - status: "Active"
        AppName: "Application 2"
        version: "0.2.2"
        metadata: "ID321"
      - status: "Active"
        AppName: "Application 3"
        version: "0.3.0"
        metadata: ""

  tasks:
    - name: "Show matching pattern"
      debug:
        msg: "{{'^' + (array1|map(attribute='version'))|difference(array2|map(attribute='version'))|join('|') + '$'}}"

    - name: "Compare arrays"
      debug:
        msg: "{{ array1 | selectattr('version', 'match', '^' + (array1|map(attribute='version'))|difference(array2|map(attribute='version'))|join('|') + '$') | list }}"

Он работает, сначала находя «более новые версии», а затем просматривая первоначальный список на основе этих. Но это немного хрупкая причина:

  • теперь это предполагает, a priori , в каком массиве содержатся «более новые» данные (здесь все более новые версии находятся в array1).
  • если у вас есть два или более элементов в вашем массиве с одинаковыми версиями, он сохранит оба, не зная, какой выбрать.

Может быть, вам следует рассмотреть другую структуру данных, например отображение (dict). См. current_state переменную ниже:

- hosts: localhost
  vars:
    current_state:
      "Application 1":
        status: "Active"
        version: "0.1.1"
        metadata: ""
      "Application 2":
        status: "Active"
        version: "0.2.2"
        metadata: "ID321"
      "Application 3":
        status: "Active"
        version: "0.3.0"
        metadata: ""
    new_applications:
      - status: "Active"
        AppName: "Application 1"
        version: "0.1.1"
        metadata: ""
      - status: "Active"
        AppName: "Application 2"
        version: "0.2.2"
        metadata: "ID321"
      - status: "Active"
        AppName: "Application 3"
        version: "0.3.3"
        metadata: ""
      - status: "Active"
        AppName: "Application 4"
        version: "0.1.0"
        metadata: ""
  tasks:
    - name: "Different appraoch"
      debug:
         msg: "{{ item.0 }} -- {{ item.1 }} -- Should update: {{ item.1.version is version((current_state[item.0]|default({'version': '0.0.0'}))['version'], '>') }}"
      loop: "{{ new_applications|map(attribute='AppName')|zip(new_applications)|list }}"

    - name: "Build 'current_state' from a list (if not available as is)"
      # There might be a smarter way using items2dict...
      set_fact:
        dict_from_list: "{{ dict_from_list|default({})|combine({item[0]: item[1]})}}"
      loop: "{{ new_applications|map(attribute='AppName')|zip(new_applications)|list }}"

    - debug:
        var: dict_from_list

Эта версия исправляет последнюю из двух проблем, упомянутых выше. Это также более надежно в случае, если порядок двух массивов не одинаков или массив не имеет одинаковую длину.

Первую проблему, которую я решил игнорировать, потому что, хотя ваш вопрос приводит к убеждению, что array1 и array2 взаимозаменяемы, я предполагаю, что на самом деле это не так, в данном контексте.

...