From d32c708290dcd0934441687606497f5d133627ab Mon Sep 17 00:00:00 2001
From: Doug Szumski <doug@stackhpc.com>
Date: Thu, 15 Mar 2018 16:58:54 +0000
Subject: [PATCH] Support parsing ini files with no sections

Services such as Zookeeper and Kafka use ini files which do not
explicitly specify sections. This change supports merging ini
files with no sections, so that the configuration for these
services follows the example configuration provided for them
as closely as possible.

Closes-Bug: #1756101
Partially-Implements: blueprint monasca-roles
Change-Id: I1061729875e5545c7af7d80779f9c2124b6c7134
---
 ansible/action_plugins/merge_configs.py |  8 ++-
 tests/test_merge_config.py              | 77 ++++++++++++++++++++++++-
 2 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/ansible/action_plugins/merge_configs.py b/ansible/action_plugins/merge_configs.py
index 11462f4b3..556bfa83b 100644
--- a/ansible/action_plugins/merge_configs.py
+++ b/ansible/action_plugins/merge_configs.py
@@ -25,6 +25,8 @@ from six import StringIO
 
 from oslo_config import iniparser
 
+_ORPHAN_SECTION = 'TEMPORARY_ORPHAN_VARIABLE_SECTION'
+
 
 class OverrideConfigParser(iniparser.BaseParser):
 
@@ -34,6 +36,8 @@ class OverrideConfigParser(iniparser.BaseParser):
         self._cur_section = None
 
     def assignment(self, key, value):
+        if self._cur_section is None:
+            self.new_section(_ORPHAN_SECTION)
         cur_value = self._cur_section.get(key)
         if len(value) == 1 and value[0] == '':
             value = []
@@ -44,6 +48,7 @@ class OverrideConfigParser(iniparser.BaseParser):
 
     def parse(self, lineiter):
         self._cur_sections = collections.OrderedDict()
+        self._cur_section = None
         super(OverrideConfigParser, self).parse(lineiter)
 
         # merge _cur_sections into _sections
@@ -77,7 +82,8 @@ class OverrideConfigParser(iniparser.BaseParser):
                 write_key_value(key, values)
 
         for section in self._sections:
-            fp.write('[{}]\n'.format(section))
+            if section != _ORPHAN_SECTION:
+                fp.write('[{}]\n'.format(section))
             write_section(self._sections[section])
             fp.write('\n')
 
diff --git a/tests/test_merge_config.py b/tests/test_merge_config.py
index b972cc9bf..21a4a0f57 100644
--- a/tests/test_merge_config.py
+++ b/tests/test_merge_config.py
@@ -86,11 +86,68 @@ c_key2 = 1 2 3
 
 '''
 
+TESTA_NO_SECTIONS = '''key1 = a
+key2 = b
+
+'''
+
+TESTB_NO_SECTIONS = '''key3 = c
+
+'''
+
+# TESTA_NO_SECTIONS and TESTB_NO_SECTIONS combined
+TESTC_NO_SECTIONS = '''key1 = a
+key2 = b
+key3 = c
+
+'''
+
+TESTA_NO_DEFAULT_SECTION = '''key1 = a
+key2 = b
+
+[a]
+key1 = not_a
+
+[b]
+key3 = not_c
+
+'''
+
+TESTB_NO_DEFAULT_SECTION = '''key3 = c
+
+[b]
+key2 = not_b
+key3 = override
+
+'''
+
+# TESTA_NO_DEFAULT_SECTION and TESTB_NO_DEFAULT_SECTION combined
+TESTC_NO_DEFAULT_SECTION = '''key1 = a
+key2 = b
+key3 = c
+
+[a]
+key1 = not_a
+
+[b]
+key3 = override
+key2 = not_b
+
+'''
+
 
 class OverrideConfigParserTest(base.BaseTestCase):
 
     def test_read_write(self):
-        for ini in [TESTA, TESTB, TESTC]:
+        for ini in [TESTA,
+                    TESTB,
+                    TESTC,
+                    TESTA_NO_SECTIONS,
+                    TESTB_NO_SECTIONS,
+                    TESTC_NO_SECTIONS,
+                    TESTA_NO_DEFAULT_SECTION,
+                    TESTB_NO_DEFAULT_SECTION,
+                    TESTC_NO_DEFAULT_SECTION]:
             parser = merge_configs.OverrideConfigParser()
             parser.parse(StringIO(ini))
             output = StringIO()
@@ -106,3 +163,21 @@ class OverrideConfigParserTest(base.BaseTestCase):
         parser.write(output)
         self.assertEqual(TESTC, output.getvalue())
         output.close()
+
+    def test_merge_no_sections(self):
+        parser = merge_configs.OverrideConfigParser()
+        parser.parse(StringIO(TESTA_NO_SECTIONS))
+        parser.parse(StringIO(TESTB_NO_SECTIONS))
+        output = StringIO()
+        parser.write(output)
+        self.assertEqual(TESTC_NO_SECTIONS, output.getvalue())
+        output.close()
+
+    def test_merge_no_default_section(self):
+        parser = merge_configs.OverrideConfigParser()
+        parser.parse(StringIO(TESTA_NO_DEFAULT_SECTION))
+        parser.parse(StringIO(TESTB_NO_DEFAULT_SECTION))
+        output = StringIO()
+        parser.write(output)
+        self.assertEqual(TESTC_NO_DEFAULT_SECTION, output.getvalue())
+        output.close()
-- 
GitLab