cc_seed_random: fix bug and support pollinate command
there was a bug that prevented seeding of /dev/urandom from metadata provided by the datasource unless the user provided random_seed config. This should, instead, be the default behavior.
This commit is contained in:
parent
40a2f859c1
commit
0f9df1ebba
@ -1,8 +1,11 @@
|
||||
# vi: ts=4 expandtab
|
||||
#
|
||||
# Copyright (C) 2013 Yahoo! Inc.
|
||||
# Copyright (C) 2014 Canonical, Ltd
|
||||
#
|
||||
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
|
||||
# Author: Dustin Kirkland <kirkland@ubuntu.com>
|
||||
# Author: Scott Moser <scott.moser@canonical.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 3, as
|
||||
@ -17,12 +20,15 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import os
|
||||
from StringIO import StringIO
|
||||
|
||||
from cloudinit.settings import PER_INSTANCE
|
||||
from cloudinit import log as logging
|
||||
from cloudinit import util
|
||||
|
||||
frequency = PER_INSTANCE
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _decode(data, encoding=None):
|
||||
@ -38,24 +44,50 @@ def _decode(data, encoding=None):
|
||||
raise IOError("Unknown random_seed encoding: %s" % (encoding))
|
||||
|
||||
|
||||
def handle(name, cfg, cloud, log, _args):
|
||||
if not cfg or "random_seed" not in cfg:
|
||||
log.debug(("Skipping module named %s, "
|
||||
"no 'random_seed' configuration found"), name)
|
||||
def handle_random_seed_command(command, required, env=None):
|
||||
if not command and required:
|
||||
raise ValueError("no command found but required=true")
|
||||
elif not command:
|
||||
LOG.debug("no command provided")
|
||||
return
|
||||
|
||||
my_cfg = cfg['random_seed']
|
||||
seed_path = my_cfg.get('file', '/dev/urandom')
|
||||
seed_buf = StringIO()
|
||||
seed_buf.write(_decode(my_cfg.get('data', ''),
|
||||
encoding=my_cfg.get('encoding')))
|
||||
cmd = command[0]
|
||||
if not util.which(cmd):
|
||||
if required:
|
||||
raise ValueError("command '%s' not found but required=true", cmd)
|
||||
else:
|
||||
LOG.debug("command '%s' not found for seed_command", cmd)
|
||||
return
|
||||
util.subp(command, env=env)
|
||||
|
||||
|
||||
def handle(name, cfg, cloud, log, _args):
|
||||
mycfg = cfg.get('random_seed', {})
|
||||
seed_path = mycfg.get('file', '/dev/urandom')
|
||||
seed_data = mycfg.get('data', '')
|
||||
|
||||
seed_buf = StringIO()
|
||||
if seed_data:
|
||||
seed_buf.write(_decode(seed_data, encoding=mycfg.get('encoding')))
|
||||
|
||||
# 'random_seed' is set up by Azure datasource, and comes already in
|
||||
# openstack meta_data.json
|
||||
metadata = cloud.datasource.metadata
|
||||
if metadata and 'random_seed' in metadata:
|
||||
seed_buf.write(metadata['random_seed'])
|
||||
|
||||
seed_data = seed_buf.getvalue()
|
||||
if len(seed_data):
|
||||
log.debug("%s: adding %s bytes of random seed entrophy to %s", name,
|
||||
log.debug("%s: adding %s bytes of random seed entropy to %s", name,
|
||||
len(seed_data), seed_path)
|
||||
util.append_file(seed_path, seed_data)
|
||||
|
||||
command = mycfg.get('command', ['pollinate', '-q'])
|
||||
req = mycfg.get('command_required', False)
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env['RANDOM_SEED_FILE'] = seed_path
|
||||
handle_random_seed_command(command=command, required=req, env=env)
|
||||
except ValueError as e:
|
||||
log.warn("handling random command [%s] failed: %s", command, e)
|
||||
raise e
|
||||
|
@ -42,10 +42,32 @@ class TestRandomSeed(t_help.TestCase):
|
||||
def setUp(self):
|
||||
super(TestRandomSeed, self).setUp()
|
||||
self._seed_file = tempfile.mktemp()
|
||||
self.unapply = []
|
||||
|
||||
# by default 'which' has nothing in its path
|
||||
self.apply_patches([(util, 'which', self._which)])
|
||||
self.apply_patches([(util, 'subp', self._subp)])
|
||||
self.subp_called = []
|
||||
self.whichdata = {}
|
||||
|
||||
def tearDown(self):
|
||||
apply_patches([i for i in reversed(self.unapply)])
|
||||
util.del_file(self._seed_file)
|
||||
|
||||
def apply_patches(self, patches):
|
||||
ret = apply_patches(patches)
|
||||
self.unapply += ret
|
||||
|
||||
def _which(self, program):
|
||||
return self.whichdata.get(program)
|
||||
|
||||
def _subp(self, *args, **kwargs):
|
||||
# supports subp calling with cmd as args or kwargs
|
||||
if 'args' not in kwargs:
|
||||
kwargs['args'] = args[0]
|
||||
self.subp_called.append(kwargs)
|
||||
return
|
||||
|
||||
def _compress(self, text):
|
||||
contents = StringIO()
|
||||
gz_fh = gzip.GzipFile(mode='wb', fileobj=contents)
|
||||
@ -148,3 +170,56 @@ class TestRandomSeed(t_help.TestCase):
|
||||
cc_seed_random.handle('test', cfg, c, LOG, [])
|
||||
contents = util.load_file(self._seed_file)
|
||||
self.assertEquals('tiny-tim-was-here-so-was-josh', contents)
|
||||
|
||||
def test_seed_command_not_provided_pollinate_available(self):
|
||||
c = self._get_cloud('ubuntu', {})
|
||||
self.whichdata = {'pollinate': '/usr/bin/pollinate'}
|
||||
cc_seed_random.handle('test', {}, c, LOG, [])
|
||||
|
||||
subp_args = [f['args'] for f in self.subp_called]
|
||||
self.assertIn(['pollinate', '-q'], subp_args)
|
||||
|
||||
def test_seed_command_not_provided_pollinate_not_available(self):
|
||||
c = self._get_cloud('ubuntu', {})
|
||||
self.whichdata = {}
|
||||
cc_seed_random.handle('test', {}, c, LOG, [])
|
||||
|
||||
# subp should not have been called as which would say not available
|
||||
self.assertEquals(self.subp_called, list())
|
||||
|
||||
def test_unavailable_seed_command_and_required_raises_error(self):
|
||||
c = self._get_cloud('ubuntu', {})
|
||||
self.whichdata = {}
|
||||
self.assertRaises(ValueError, cc_seed_random.handle,
|
||||
'test', {'random_seed': {'command_required': True}}, c, LOG, [])
|
||||
|
||||
def test_seed_command_and_required(self):
|
||||
c = self._get_cloud('ubuntu', {})
|
||||
self.whichdata = {'foo': 'foo'}
|
||||
cfg = {'random_seed': {'command_required': True, 'command': ['foo']}}
|
||||
cc_seed_random.handle('test', cfg, c, LOG, [])
|
||||
|
||||
self.assertIn(['foo'], [f['args'] for f in self.subp_called])
|
||||
|
||||
def test_file_in_environment_for_command(self):
|
||||
c = self._get_cloud('ubuntu', {})
|
||||
self.whichdata = {'foo': 'foo'}
|
||||
cfg = {'random_seed': {'command_required': True, 'command': ['foo'],
|
||||
'file': self._seed_file}}
|
||||
cc_seed_random.handle('test', cfg, c, LOG, [])
|
||||
|
||||
# this just instists that the first time subp was called,
|
||||
# RANDOM_SEED_FILE was in the environment set up correctly
|
||||
subp_env = [f['env'] for f in self.subp_called]
|
||||
self.assertEqual(subp_env[0].get('RANDOM_SEED_FILE'), self._seed_file)
|
||||
|
||||
|
||||
def apply_patches(patches):
|
||||
ret = []
|
||||
for (ref, name, replace) in patches:
|
||||
if replace is None:
|
||||
continue
|
||||
orig = getattr(ref, name)
|
||||
setattr(ref, name, replace)
|
||||
ret.append((ref, name, orig))
|
||||
return ret
|
||||
|
Loading…
x
Reference in New Issue
Block a user