heat-templates/tests/software_config/test_heat_config.py
Steve Baker c002854fa4 Move deployed dir from /var/run to /var/lib
Deployments should not be run again when a server is rebooted because
heat will likely not be listening for the deployment signals. This
change moves the deployed directory from /var/run/heat-config/deployed
to /var/lib/heat-config/deployed so that deployed state is persisted
across reboots.

There is migration logic to move the existing files from the old to the
new location, this will only be run on the first run of 55-heat-config
after its package is updated.

Change-Id: I3d305a4ac5b68c29037760682d37e5b9a530828e
Closes-Bug: #1513220
2015-11-11 12:18:41 +13:00

242 lines
8.0 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import json
import os
import shutil
import tempfile
import fixtures
from testtools import matchers
from tests.software_config import common
class HeatConfigTest(common.RunScriptTest):
fake_hooks = ['cfn-init', 'chef', 'puppet', 'salt', 'script']
data = [
{
'id': '1111',
'group': 'chef',
'inputs': [{
'name': 'deploy_signal_id',
'value': 'mock://192.0.2.2/foo'
}],
'config': 'one'
}, {
'id': '2222',
'group': 'cfn-init',
'inputs': [],
'config': 'two'
}, {
'id': '3333',
'group': 'salt',
'inputs': [{'name': 'foo', 'value': 'bar'}],
'outputs': [{'name': 'foo'}],
'config': 'three'
}, {
'id': '4444',
'group': 'puppet',
'inputs': [],
'config': 'four'
}, {
'id': '5555',
'group': 'script',
'inputs': [{
'name': 'deploy_status_code', 'value': '-1'
}, {
'name': 'deploy_stderr', 'value': 'A bad thing happened'
}, {
'name': 'deploy_signal_id',
'value': 'mock://192.0.2.3/foo'
}],
'config': 'five'
}, {
'id': '6666',
'group': 'no-such-hook',
'inputs': [],
'config': 'six'
}]
outputs = {
'chef': {
'deploy_status_code': '0',
'deploy_stderr': 'stderr',
'deploy_stdout': 'stdout'
},
'cfn-init': {
'deploy_status_code': '0',
'deploy_stderr': 'stderr',
'deploy_stdout': 'stdout'
},
'salt': {
'deploy_status_code': '0',
'deploy_stderr': 'stderr',
'deploy_stdout': 'stdout',
'foo': 'bar'
},
'puppet': {
'deploy_status_code': '0',
'deploy_stderr': 'stderr',
'deploy_stdout': 'stdout'
},
'script': {
'deploy_status_code': '-1',
'deploy_stderr': 'A bad thing happened',
'deploy_stdout': 'stdout'
}
}
def setUp(self):
super(HeatConfigTest, self).setUp()
self.fake_hook_path = self.relative_path(__file__, 'hook-fake.py')
self.heat_config_path = self.relative_path(
__file__,
'../..',
'hot/software-config/elements',
'heat-config/os-refresh-config/configure.d/55-heat-config')
self.hooks_dir = self.useFixture(fixtures.TempDir())
self.deployed_dir = self.useFixture(fixtures.TempDir())
with open(self.fake_hook_path) as f:
fake_hook = f.read()
for hook in self.fake_hooks:
hook_name = self.hooks_dir.join(hook)
with open(hook_name, 'w') as f:
os.utime(hook_name, None)
f.write(fake_hook)
f.flush()
os.chmod(hook_name, 0o755)
self.env = os.environ.copy()
def write_config_file(self, data):
config_file = tempfile.NamedTemporaryFile()
config_file.write(json.dumps(data))
config_file.flush()
return config_file
def run_heat_config(self, data):
with self.write_config_file(data) as config_file:
self.env.update({
'HEAT_CONFIG_HOOKS': self.hooks_dir.join(),
'HEAT_CONFIG_DEPLOYED': self.deployed_dir.join(),
'HEAT_SHELL_CONFIG': config_file.name
})
returncode, stdout, stderr = self.run_cmd(
[self.heat_config_path], self.env)
self.assertEqual(0, returncode, stderr)
def test_hooks_exist(self):
self.assertThat(
self.hooks_dir.join('no-such-hook'),
matchers.Not(matchers.FileExists()))
for hook in self.fake_hooks:
hook_path = self.hooks_dir.join(hook)
self.assertThat(hook_path, matchers.FileExists())
def test_run_heat_config(self):
self.run_heat_config(self.data)
for config in self.data:
hook = config['group']
stdin_path = self.hooks_dir.join('%s.stdin' % hook)
stdout_path = self.hooks_dir.join('%s.stdout' % hook)
deployed_file = self.deployed_dir.join('%s.json' % config['id'])
if hook == 'no-such-hook':
self.assertThat(
stdin_path, matchers.Not(matchers.FileExists()))
self.assertThat(
stdout_path, matchers.Not(matchers.FileExists()))
continue
self.assertThat(stdin_path, matchers.FileExists())
self.assertThat(stdout_path, matchers.FileExists())
# parsed stdin should match the config item
self.assertEqual(config,
self.json_from_file(stdin_path))
# parsed stdin should match the written deployed file
self.assertEqual(config,
self.json_from_file(deployed_file))
self.assertEqual(self.outputs[hook],
self.json_from_file(stdout_path))
# clean up files in preperation for second run
os.remove(stdin_path)
os.remove(stdout_path)
# run again with no changes, assert no new files
self.run_heat_config(self.data)
for config in self.data:
hook = config['group']
stdin_path = self.hooks_dir.join('%s.stdin' % hook)
stdout_path = self.hooks_dir.join('%s.stdout' % hook)
self.assertThat(
stdin_path, matchers.Not(matchers.FileExists()))
self.assertThat(
stdout_path, matchers.Not(matchers.FileExists()))
# run again changing the puppet config
data = copy.deepcopy(self.data)
for config in data:
if config['id'] == '4444':
config['id'] = '44444444'
self.run_heat_config(data)
for config in self.data:
hook = config['group']
stdin_path = self.hooks_dir.join('%s.stdin' % hook)
stdout_path = self.hooks_dir.join('%s.stdout' % hook)
if hook == 'puppet':
self.assertThat(stdin_path, matchers.FileExists())
self.assertThat(stdout_path, matchers.FileExists())
else:
self.assertThat(
stdin_path, matchers.Not(matchers.FileExists()))
self.assertThat(
stdout_path, matchers.Not(matchers.FileExists()))
# run again with a different deployed_dir
old_deployed_dir = self.deployed_dir
self.env['HEAT_CONFIG_DEPLOYED_OLD'] = old_deployed_dir.join()
self.deployed_dir = self.useFixture(fixtures.TempDir())
# make sure the new deployed_dir doesn't exist to trigger the migration
shutil.rmtree(self.deployed_dir.join())
self.run_heat_config(data)
for config in self.data:
hook = config['group']
if hook == 'no-such-hook':
continue
deployed_file = self.deployed_dir.join('%s.json' % config['id'])
old_deployed_file = old_deployed_dir.join('%s.json' % config['id'])
self.assertEqual(config,
self.json_from_file(deployed_file))
self.assertThat(
old_deployed_file, matchers.Not(matchers.FileExists()))