Skip to content

Commit

Permalink
Add ignore_none_type to config template
Browse files Browse the repository at this point in the history
It is sometimes useful to tell ConfigTemplateParser to write
out options that are valueless and not suffixed with '=' or ':',
such as when overriding a my.cnf.

This commit adds the 'ignore_none_type' attribute to the config_template
module. If this attribute is set to false, then valueless options will be
written out to the resultant INI file as-is, without the '=' or ':' suffix.

Change-Id: I5c88b2019c01b44193a5d0df9299ecce6de52f01
Partial-Bug: #1693234
  • Loading branch information
Miguel Alex Cantu committed Jun 26, 2017
1 parent 30af082 commit 76d5f02
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 10 deletions.
53 changes: 43 additions & 10 deletions action/_v2_config_template.py
Expand Up @@ -155,9 +155,17 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
key = var3
key = var2
"""
def _write(self, fp, section, item, entry):
def __init__(self, *args, **kwargs):
self.ignore_none_type = bool(kwargs.pop('ignore_none_type', True))
ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)

def _write(self, fp, section, key, item, entry):
if section:
if (item is not None) or (self._optcre == self.OPTCRE):
# If we are not ignoring a none type value, then print out
# the option name only if the value type is None.
if not self.ignore_none_type and item is None:
fp.write(key + '\n')
elif (item is not None) or (self._optcre == self.OPTCRE):
fp.write(entry)
else:
fp.write(entry)
Expand All @@ -167,14 +175,14 @@ def _write_check(self, fp, key, value, section=False):
for item in value:
item = str(item).replace('\n', '\n\t')
entry = "%s = %s\n" % (key, item)
self._write(fp, section, item, entry)
self._write(fp, section, key, item, entry)
else:
if isinstance(value, list):
_value = [str(i.replace('\n', '\n\t')) for i in value]
entry = '%s = %s\n' % (key, ','.join(_value))
else:
entry = '%s = %s\n' % (key, str(value).replace('\n', '\n\t'))
self._write(fp, section, value, entry)
self._write(fp, section, key, value, entry)

def write(self, fp):
if self._defaults:
Expand Down Expand Up @@ -268,7 +276,11 @@ def _read(self, fp, fpname):
class ActionModule(ActionBase):
TRANSFERS_FILES = True

def return_config_overrides_ini(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_ini(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Returns string value from a modified config file.
:param config_overrides: ``dict``
Expand All @@ -281,7 +293,8 @@ def return_config_overrides_ini(self, config_overrides, resultant, list_extend=T
try:
config = ConfigTemplateParser(
allow_no_value=True,
dict_type=MultiKeyDict
dict_type=MultiKeyDict,
ignore_none_type=ignore_none_type
)
config.optionxform = str
except Exception:
Expand Down Expand Up @@ -344,7 +357,11 @@ def _option_write(config, section, key, value):
else:
config.set(str(section), str(key), str(value))

def return_config_overrides_json(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_json(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Returns config json
Its important to note that file ordering will not be preserved as the
Expand All @@ -366,7 +383,11 @@ def return_config_overrides_json(self, config_overrides, resultant, list_extend=
sort_keys=True
)

def return_config_overrides_yaml(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_yaml(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Return config yaml.
:param config_overrides: ``dict``
Expand Down Expand Up @@ -484,13 +505,23 @@ def _load_options_and_status(self, task_vars):
if user_dest.endswith(os.sep):
user_dest = os.path.join(user_dest, os.path.basename(source))

# Get ignore_none_type
# In some situations(i.e. my.cnf files), INI files can have valueless
# options that don't have a '=' or ':' suffix. In these cases,
# ConfigParser gives these options a "None" value. If ignore_none_type
# is set to true, these key/value options will be ignored, if it's set
# to false, then ConfigTemplateParser will write out only the option
# name with out the '=' or ':' suffix. The default is true.
ignore_none_type = self._task.args.get('ignore_none_type', True)

return True, dict(
source=source,
dest=user_dest,
config_overrides=self._task.args.get('config_overrides', dict()),
config_type=config_type,
searchpath=searchpath,
list_extend=list_extend
list_extend=list_extend,
ignore_none_type=ignore_none_type
)

def run(self, tmp=None, task_vars=None):
Expand Down Expand Up @@ -563,7 +594,8 @@ def run(self, tmp=None, task_vars=None):
resultant = type_merger(
config_overrides=_vars['config_overrides'],
resultant=resultant,
list_extend=_vars.get('list_extend', True)
list_extend=_vars.get('list_extend', True),
ignore_none_type=_vars.get('ignore_none_type', True)
)

# Re-template the resultant object as it may have new data within it
Expand Down Expand Up @@ -595,6 +627,7 @@ def run(self, tmp=None, task_vars=None):
new_module_args.pop('config_overrides', None)
new_module_args.pop('config_type', None)
new_module_args.pop('list_extend', None)
new_module_args.pop('ignore_none_type', None)
# Content from config_template is converted to src
new_module_args.pop('content', None)

Expand Down
10 changes: 10 additions & 0 deletions library/config_template
Expand Up @@ -49,6 +49,16 @@ options:
choices:
- True
- False
ignore_none_type:
description:
- Can be true or false. If ignore_none_type is set to true, then
valueless INI options will not be written out to the resultant file.
If it's set to false, then config_template will write out only
the option name without the '=' or ':' suffix. The default is true.
choices:
- True
- False

author: Kevin Carter
"""

Expand Down
@@ -0,0 +1,9 @@
---
features:
- |
The config_template template module now supports writing out valueless
INI options without suffixing them with '=' or ':'. This is done via the
'ignore_none_type' attribute. If ignore_none_type is set to true, these
key/value entries will be ignored, if it's set to false, then
ConfigTemplateParser will write out only the option name without the
'=' or ':' suffix. The default is true.
8 changes: 8 additions & 0 deletions tests/templates/test_ignore_none_type.ini
@@ -0,0 +1,8 @@
[alfa]
bravo = charlie
delta = echo
[foxtrot]
golf = hotel
india
juliett kilo
lima = mike
20 changes: 20 additions & 0 deletions tests/test-config_template.yml
Expand Up @@ -136,6 +136,26 @@
that:
- "(content_no_overrides_file.content | b64decode | from_json) == (content_no_overrides_file_expected.content | b64decode | from_json)"

# Test the ignore_none_type attribute when set to False
- name: Template test with ignore_none_type set to false
config_template:
src: "{{ playbook_dir }}/templates/test_ignore_none_type.ini"
dest: "/tmp/test_ignore_none_type.ini"
config_overrides: "{{ test_config_ini_overrides }}"
config_type: "ini"
ignore_none_type: False
- name: Read test_ignore_none_type.ini
slurp:
src: /tmp/test_ignore_none_type.ini
register: test_ignore_none_type
- debug:
msg: "test_ignore_none_type.ini - {{ test_ignore_none_type.content | b64decode }}"
- name: Validate output has valueless options printed out
assert:
that:
- "{{ test_ignore_none_type.content | b64decode | search('(?m)^india$') }}"
- "{{ test_ignore_none_type.content | b64decode | search('(?m)^juliett kilo$') }}"

vars:
test_config_ini_overrides:
DEFAULT:
Expand Down

0 comments on commit 76d5f02

Please sign in to comment.