From 94bd31e45e82e199239d1300b4a425c1cd14a2fb Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 2 Dec 2014 15:41:59 -0500 Subject: [PATCH] heat-config-puppet: support hiera Updates the puppet hook so that we support using Hiera in addition to the existing Facter based input mechanism. Hiera provides a more flexible way of dealing with puppet parameters and seems to be prefered in the Puppet community as well. Use of the Hiera data will also require an additional 'hiera' element which will probably live in the triple-puppet-elements tree. Change-Id: I497f2d2286025e48e42691bfe178fc9c491928ea --- .../elements/heat-config-puppet/README.rst | 10 +++- .../install.d/hook-puppet.py | 24 ++++++++- tests/software_config/test_hook_puppet.py | 54 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/hot/software-config/elements/heat-config-puppet/README.rst b/hot/software-config/elements/heat-config-puppet/README.rst index 26225625..5cae8620 100644 --- a/hot/software-config/elements/heat-config-puppet/README.rst +++ b/hot/software-config/elements/heat-config-puppet/README.rst @@ -1,3 +1,9 @@ A hook which invokes ``puppet apply`` on the provided configuration. -Config inputs are passed in as facts, and output values are read from written-out -files. \ No newline at end of file + +Config inputs are passed in as facts and/or using hiera, and output values +are read from written-out files. + +Hook Options: +------------- + use_facter: default True. Set to True to pass puppet inputs via Facter + use_hiera: default False. Set to True to pass puppet inputs via Hiera diff --git a/hot/software-config/elements/heat-config-puppet/install.d/hook-puppet.py b/hot/software-config/elements/heat-config-puppet/install.d/hook-puppet.py index ec4a7866..0171e1f3 100755 --- a/hot/software-config/elements/heat-config-puppet/install.d/hook-puppet.py +++ b/hot/software-config/elements/heat-config-puppet/install.d/hook-puppet.py @@ -23,6 +23,8 @@ WORKING_DIR = os.environ.get('HEAT_PUPPET_WORKING', OUTPUTS_DIR = os.environ.get('HEAT_PUPPET_OUTPUTS', '/var/run/heat-config/heat-config-puppet') PUPPET_CMD = os.environ.get('HEAT_PUPPET_CMD', 'puppet') +HIERA_DATADIR = os.environ.get('HEAT_PUPPET_HIERA_DATADIR', + '/etc/puppet/hieradata') def prepare_dir(path): @@ -45,11 +47,29 @@ def main(argv=sys.argv): c = json.load(sys.stdin) + use_hiera = c['options'].get('enable_hiera', False) + use_facter = c['options'].get('enable_facter', True) + facts = {} + hiera = {} + for input in c['inputs']: input_name = input['name'] - fact_name = 'FACTER_%s' % input_name - facts[fact_name] = input.get('value', '') + input_value = input.get('value', '') + if use_facter: + fact_name = 'FACTER_%s' % input_name + facts[fact_name] = input_value + if use_hiera: + hiera[input_name] = input_value + + if use_hiera: + prepare_dir(HIERA_DATADIR) + hiera_data = os.path.join(HIERA_DATADIR, + 'heat_config_%s.json' % c['name']) + with os.fdopen(os.open(hiera_data, os.O_CREAT | os.O_WRONLY, 0o600), + 'w') as hiera_file: + hiera_file.write(json.dumps(hiera).encode('utf8')) + facts['FACTER_deploy_config_name'] = c['name'] fn = os.path.join(WORKING_DIR, '%s.pp' % c['id']) heat_outputs_path = os.path.join(OUTPUTS_DIR, c['id']) diff --git a/tests/software_config/test_hook_puppet.py b/tests/software_config/test_hook_puppet.py index a7f25344..16ec2f3c 100644 --- a/tests/software_config/test_hook_puppet.py +++ b/tests/software_config/test_hook_puppet.py @@ -23,7 +23,12 @@ class HookPuppetTest(common.RunScriptTest): data = { 'id': '1234', + 'name': 'fake_resource_name', 'group': 'puppet', + 'options': { + 'enable_hiera': True, + 'enable_facter': True, + }, 'inputs': [ {'name': 'foo', 'value': 'bar'}, {'name': 'another', 'value': 'input'} @@ -49,12 +54,14 @@ class HookPuppetTest(common.RunScriptTest): self.working_dir = self.useFixture(fixtures.TempDir()) self.outputs_dir = self.useFixture(fixtures.TempDir()) + self.hiera_datadir = self.useFixture(fixtures.TempDir()) self.test_state_path = self.outputs_dir.join('test_state.json') self.env = os.environ.copy() self.env.update({ 'HEAT_PUPPET_WORKING': self.working_dir.join(), 'HEAT_PUPPET_OUTPUTS': self.outputs_dir.join(), + 'HEAT_PUPPET_HIERA_DATADIR': self.hiera_datadir.join(), 'HEAT_PUPPET_CMD': self.fake_tool_path, 'TEST_STATE_PATH': self.test_state_path, }) @@ -137,3 +144,50 @@ class HookPuppetTest(common.RunScriptTest): state['env']['FACTER_heat_outputs_path']) with open(puppet_script) as f: self.assertEqual('the puppet script', f.read()) + + def test_hook_hiera(self): + + self.env.update({ + 'TEST_RESPONSE': json.dumps({ + 'stdout': 'puppet success', + 'stderr': 'thing happened', + 'files': { + self.outputs_dir.join('1234.first_output'): 'output 1', + self.outputs_dir.join('1234.second_output'): 'output 2', + } + }), + }) + returncode, stdout, stderr = self.run_cmd( + [self.hook_path], self.env, json.dumps(self.data)) + + self.assertEqual(0, returncode, stderr) + self.assertEqual({ + 'deploy_stdout': 'puppet success', + 'deploy_stderr': 'thing happened', + 'deploy_status_code': 0, + 'first_output': 'output 1', + 'second_output': 'output 2', + }, json.loads(stdout)) + + state = self.json_from_file(self.test_state_path) + puppet_script = self.working_dir.join('1234.pp') + hiera_datafile = self.hiera_datadir.join('heat_config_%s.json' + % self.data['name']) + self.assertEqual( + [ + self.fake_tool_path, + 'apply', + '--detailed-exitcodes', + puppet_script + ], + state['args']) + + self.assertEqual(self.outputs_dir.join('1234'), + state['env']['FACTER_heat_outputs_path']) + with open(puppet_script) as f: + self.assertEqual('the puppet script', f.read()) + with open(hiera_datafile) as f: + self.assertEqual({ + 'foo': 'bar', + 'another': 'input', + }, json.loads(f.read()))