mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2024-09-14 20:13:21 +02:00 
			
		
		
		
	Adding a new filter: to_ini, which allows conversion of a dictionary to an INI formatted string (#7744)
* Adding a new filter: to_ini, which allows conversion of a dictionary to an INI formatted string * Adding to_ini maintainers into BOTMETA * Correcting filter suffix * Moving filter to correct path * Adding error handling; Removing quotes from examples; Fixing RETURN documentation * Removing the last newline char; Adding error handling for an empty dict * Adding integration tests for to_ini * Fixing F-String usage * Fixing formatting * Fixing whitespace * Moving import statements below documentation; Adding a more generic Exception handling; Removing unused imports * Removing not needed set_fact and replacing it with using vars: * Replacing MutableMapping with Mapping
This commit is contained in:
		
							parent
							
								
									f79940c415
								
							
						
					
					
						commit
						ec12422fae
					
				
					 4 changed files with 173 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								.github/BOTMETA.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/BOTMETA.yml
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -158,6 +158,8 @@ files:
 | 
			
		|||
    maintainers: resmo
 | 
			
		||||
  $filters/to_hours.yml:
 | 
			
		||||
    maintainers: resmo
 | 
			
		||||
  $filters/to_ini.py:
 | 
			
		||||
    maintainers: sscheib
 | 
			
		||||
  $filters/to_milliseconds.yml:
 | 
			
		||||
    maintainers: resmo
 | 
			
		||||
  $filters/to_minutes.yml:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										105
									
								
								plugins/filter/to_ini.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								plugins/filter/to_ini.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,105 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
 | 
			
		||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import, division, print_function
 | 
			
		||||
 | 
			
		||||
DOCUMENTATION = r'''
 | 
			
		||||
  name: to_ini
 | 
			
		||||
  short_description: Converts a dictionary to the INI file format
 | 
			
		||||
  version_added: 8.2.0
 | 
			
		||||
  author: Steffen Scheib (@sscheib)
 | 
			
		||||
  description:
 | 
			
		||||
    - Converts a dictionary to the INI file format.
 | 
			
		||||
  options:
 | 
			
		||||
    _input:
 | 
			
		||||
      description: The dictionary that should be converted to the INI format.
 | 
			
		||||
      type: dictionary
 | 
			
		||||
      required: true
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
EXAMPLES = r'''
 | 
			
		||||
  - name: Define a dictionary
 | 
			
		||||
    ansible.builtin.set_fact:
 | 
			
		||||
      my_dict:
 | 
			
		||||
        section_name:
 | 
			
		||||
          key_name: 'key value'
 | 
			
		||||
 | 
			
		||||
        another_section:
 | 
			
		||||
          connection: 'ssh'
 | 
			
		||||
 | 
			
		||||
  - name: Write dictionary to INI file
 | 
			
		||||
    ansible.builtin.copy:
 | 
			
		||||
      dest: /tmp/test.ini
 | 
			
		||||
      content: '{{ my_dict | community.general.to_ini }}'
 | 
			
		||||
 | 
			
		||||
  # /tmp/test.ini will look like this:
 | 
			
		||||
  # [section_name]
 | 
			
		||||
  # key_name = key value
 | 
			
		||||
  #
 | 
			
		||||
  # [another_section]
 | 
			
		||||
  # connection = ssh
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
RETURN = r'''
 | 
			
		||||
  _value:
 | 
			
		||||
    description: A string formatted as INI file.
 | 
			
		||||
    type: string
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__metaclass__ = type
 | 
			
		||||
 | 
			
		||||
from ansible.errors import AnsibleFilterError
 | 
			
		||||
from ansible.module_utils.common._collections_compat import Mapping
 | 
			
		||||
from ansible.module_utils.six.moves import StringIO
 | 
			
		||||
from ansible.module_utils.six.moves.configparser import ConfigParser
 | 
			
		||||
from ansible.module_utils.common.text.converters import to_native
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IniParser(ConfigParser):
 | 
			
		||||
    ''' Implements a configparser which sets the correct optionxform '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super().__init__()
 | 
			
		||||
        self.optionxform = str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_ini(obj):
 | 
			
		||||
    ''' Read the given dict and return an INI formatted string '''
 | 
			
		||||
 | 
			
		||||
    if not isinstance(obj, Mapping):
 | 
			
		||||
        raise AnsibleFilterError(f'to_ini requires a dict, got {type(obj)}')
 | 
			
		||||
 | 
			
		||||
    ini_parser = IniParser()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        ini_parser.read_dict(obj)
 | 
			
		||||
    except Exception as ex:
 | 
			
		||||
        raise AnsibleFilterError('to_ini failed to parse given dict:'
 | 
			
		||||
                                 f'{to_native(ex)}', orig_exc=ex)
 | 
			
		||||
 | 
			
		||||
    # catching empty dicts
 | 
			
		||||
    if obj == dict():
 | 
			
		||||
        raise AnsibleFilterError('to_ini received an empty dict. '
 | 
			
		||||
                                 'An empty dict cannot be converted.')
 | 
			
		||||
 | 
			
		||||
    config = StringIO()
 | 
			
		||||
    ini_parser.write(config)
 | 
			
		||||
 | 
			
		||||
    # config.getvalue() returns two \n at the end
 | 
			
		||||
    # with the below insanity, we remove the very last character of
 | 
			
		||||
    # the resulting string
 | 
			
		||||
    return ''.join(config.getvalue().rsplit(config.getvalue()[-1], 1))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FilterModule(object):
 | 
			
		||||
    ''' Query filter '''
 | 
			
		||||
 | 
			
		||||
    def filters(self):
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            'to_ini': to_ini
 | 
			
		||||
        }
 | 
			
		||||
							
								
								
									
										58
									
								
								tests/integration/targets/filter_to_ini/tasks/main.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/integration/targets/filter_to_ini/tasks/main.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
---
 | 
			
		||||
# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
 | 
			
		||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
- name: >-
 | 
			
		||||
    Write INI file that reflects using to_ini to {{ ini_test_file_filter }}
 | 
			
		||||
  ansible.builtin.copy:
 | 
			
		||||
    dest: '{{ ini_test_file_filter }}'
 | 
			
		||||
    content: '{{ ini_test_dict | community.general.to_ini }}'
 | 
			
		||||
  vars:
 | 
			
		||||
    ini_test_dict:
 | 
			
		||||
      section_name:
 | 
			
		||||
        key_name: 'key value'
 | 
			
		||||
 | 
			
		||||
      another_section:
 | 
			
		||||
        connection: 'ssh'
 | 
			
		||||
 | 
			
		||||
- name: 'Write INI file manually to {{ ini_test_file }}'
 | 
			
		||||
  ansible.builtin.copy:
 | 
			
		||||
    dest: '{{ ini_test_file }}'
 | 
			
		||||
    content: |
 | 
			
		||||
      [section_name]
 | 
			
		||||
      key_name = key value
 | 
			
		||||
 | 
			
		||||
      [another_section]
 | 
			
		||||
      connection = ssh
 | 
			
		||||
 | 
			
		||||
- name: 'Slurp the manually created test file: {{ ini_test_file }}'
 | 
			
		||||
  ansible.builtin.slurp:
 | 
			
		||||
    src: '{{ ini_test_file }}'
 | 
			
		||||
  register: 'ini_file_content'
 | 
			
		||||
 | 
			
		||||
- name: 'Slurp the test file created with to_ini: {{ ini_test_file_filter }}'
 | 
			
		||||
  ansible.builtin.slurp:
 | 
			
		||||
    src: '{{ ini_test_file_filter }}'
 | 
			
		||||
  register: 'ini_file_filter_content'
 | 
			
		||||
 | 
			
		||||
- name: >-
 | 
			
		||||
    Ensure the manually created test file and the test file created with
 | 
			
		||||
    to_ini are identical
 | 
			
		||||
  ansible.builtin.assert:
 | 
			
		||||
    that:
 | 
			
		||||
      - 'ini_file_content.content | b64decode ==
 | 
			
		||||
         ini_file_filter_content.content | b64decode'
 | 
			
		||||
 | 
			
		||||
- name: 'Try to convert an empty dictionary with to_ini'
 | 
			
		||||
  ansible.builtin.debug:
 | 
			
		||||
    msg: '{{ {} | community.general.to_ini }}'
 | 
			
		||||
  register: 'ini_empty_dict'
 | 
			
		||||
  ignore_errors: true
 | 
			
		||||
 | 
			
		||||
- name: 'Ensure the correct exception was raised'
 | 
			
		||||
  ansible.builtin.assert:
 | 
			
		||||
    that:
 | 
			
		||||
      - "'to_ini received an empty dict. An empty dict cannot be converted.' in
 | 
			
		||||
        ini_empty_dict.msg"
 | 
			
		||||
...
 | 
			
		||||
							
								
								
									
										8
									
								
								tests/integration/targets/filter_to_ini/vars/main.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/integration/targets/filter_to_ini/vars/main.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
---
 | 
			
		||||
# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
 | 
			
		||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
ini_test_file: '/tmp/test.ini'
 | 
			
		||||
ini_test_file_filter: '/tmp/test_filter.ini'
 | 
			
		||||
...
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue