diff options
-rw-r--r-- | MANIFEST.in | 1 | ||||
-rw-r--r-- | doc/source/conf.py | 5 | ||||
-rw-r--r-- | doc/source/man/heat-db-setup.rst | 63 | ||||
-rw-r--r-- | heat/cloudinit/boothook.sh | 7 | ||||
-rw-r--r-- | heat/cloudinit/config | 7 | ||||
-rw-r--r-- | heat/cloudinit/loguserdata.py | 16 | ||||
-rw-r--r-- | heat/cloudinit/part-handler.py | 3 | ||||
-rw-r--r-- | heat/common/config.py | 3 | ||||
-rw-r--r-- | heat/engine/resource.py | 14 | ||||
-rw-r--r-- | heat/engine/resources/instance.py | 7 | ||||
-rw-r--r-- | heat/tests/test_loguserdata.py | 57 |
11 files changed, 158 insertions, 25 deletions
diff --git a/MANIFEST.in b/MANIFEST.in index 77b8604e5..299e22fab 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include babel.cfg install.sh run_tests.sh tox.ini uninstall.sh graft templates include heat/versioninfo include heat/cloudinit/config +include heat/cloudinit/boothook.sh include heat/cloudinit/loguserdata.py include heat/cloudinit/part-handler.py include heat/db/sqlalchemy/migrate_repo/migrate.cfg diff --git a/doc/source/conf.py b/doc/source/conf.py index 7f976bdab..1987f68f4 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -45,7 +45,7 @@ master_doc = 'index' # General information about the project. project = u'Heat' -copyright = u'2012, Heat Developers' +copyright = u'2012,2013 Heat Developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -237,6 +237,9 @@ man_pages = [ ('man/heat-cfn', 'heat-cfn', u'Command line utility to run heat actions over the CloudFormation API', [u'Heat Developers'], 1), + ('man/heat-db-setup', 'heat-db-setup', + u'Command line utility to setup the Heat database', + [u'Heat Developers'], 1), ('man/heat-engine', 'heat-engine', u'Service which performs the actions from the API calls made by the user', [u'Heat Developers'], 1), diff --git a/doc/source/man/heat-db-setup.rst b/doc/source/man/heat-db-setup.rst new file mode 100644 index 000000000..c0aa58fc5 --- /dev/null +++ b/doc/source/man/heat-db-setup.rst @@ -0,0 +1,63 @@ +======== +heat-db-setup +======== + +.. program:: heat-db-setup + + +SYNOPSIS +======== + +``heat-db-setup [COMMANDS] [OPTIONS]`` + + +DESCRIPTION +=========== +heat-db-setup is a tool which configures the local MySQL database for +heat. Typically distro-specific tools would provide this functionality +so please read the distro-specific documentation for configuring Heat. + + +COMMANDS +======== + +``rpm`` + + Indicate the distribution is a RPM packaging based distribution. + +``deb`` + + Indicate the distribution is a DEB packaging based distribution. + + +OPTIONS +======= + +.. cmdoption:: -h, --help + + Print usage information. + +.. cmdoption:: -p, --password + + Specify the password for the 'heat' MySQL user that will use to connect to the 'heat' MySQL database. By default, the password 'heat' will be used. + +.. cmdoption:: -r, --rootpw + + Specify the root MySQL password. If the script installs the MySQL server, it will set the root password to this value instead of prompting for a password. If the MySQL server is already installed, this password will be used to connect to the database instead of having to prompt for it. + +.. cmdoption:: -y, --yes + + In cases where the script would normally ask for confirmation before doing something, such as installing mysql-server, just assume yes. This is useful if you want to run the script non-interactively. + +EXAMPLES +======== + + heat-db-setup rpm -p heat_password -r mysql_pwd -y + + heat-db-setup deb -p heat_password -r mysql_pwd -y + + heat-db-setup rpm + +BUGS +==== +Heat bugs are managed through Launchpad <https://launchpad.net/heat> diff --git a/heat/cloudinit/boothook.sh b/heat/cloudinit/boothook.sh new file mode 100644 index 000000000..f7d46a7f5 --- /dev/null +++ b/heat/cloudinit/boothook.sh @@ -0,0 +1,7 @@ +#!/bin/bash +setenforce 0 +useradd -m @INSTANCE_USER@ +echo -e '@INSTANCE_USER@\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers + +# Do not remove - the cloud boothook should always return success +exit 0 diff --git a/heat/cloudinit/config b/heat/cloudinit/config index 9ed9c8970..bd363f89b 100644 --- a/heat/cloudinit/config +++ b/heat/cloudinit/config @@ -1,16 +1,11 @@ -runcmd: - - setenforce 0 > /dev/null 2>&1 || true - -user: ec2-user +user: @INSTANCE_USER@ cloud_config_modules: - locale - set_hostname - - ssh - timezone - update_etc_hosts - update_hostname - - runcmd # Capture all subprocess output into a logfile # Useful for troubleshooting cloud-init issues diff --git a/heat/cloudinit/loguserdata.py b/heat/cloudinit/loguserdata.py index 7e66166f7..a33e8b3c5 100644 --- a/heat/cloudinit/loguserdata.py +++ b/heat/cloudinit/loguserdata.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import errno import datetime import pkg_resources import os @@ -25,8 +26,19 @@ def create_log(log_path): def call(args, logger): logger.write('%s\n' % ' '.join(args)) logger.flush() - p = subprocess.Popen(args, stdout=logger, stderr=logger) - p.wait() + try: + p = subprocess.Popen(args, stdout=logger, stderr=logger) + p.wait() + except OSError as ex: + if ex.errno == errno.ENOEXEC: + logger.write('Userdata empty or not executable: %s\n' % str(ex)) + return os.EX_OK + else: + logger.write('OS error running userdata: %s\n' % str(ex)) + return os.EX_OSERR + except Exception as ex: + logger.write('Unknown error running userdata: %s\n' % str(ex)) + return os.EX_SOFTWARE return p.returncode diff --git a/heat/cloudinit/part-handler.py b/heat/cloudinit/part-handler.py index 138d6d586..4b1c6b575 100644 --- a/heat/cloudinit/part-handler.py +++ b/heat/cloudinit/part-handler.py @@ -1,7 +1,8 @@ #part-handler -import os import datetime +import errno +import os def list_types(): diff --git a/heat/common/config.py b/heat/common/config.py index e84fc7496..cd23d8db1 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -95,6 +95,9 @@ db_opts = [ help='timeout before idle sql connections are reaped')] engine_opts = [ + cfg.StrOpt('instance_user', + default='ec2-user', + help='The default user for new instances'), cfg.StrOpt('instance_driver', default='heat.engine.nova', help='Driver to use for controlling instances'), diff --git a/heat/engine/resource.py b/heat/engine/resource.py index f64430549..450c575d7 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -320,16 +320,12 @@ class Resource(object): self.handle_create() while not self.check_active(): eventlet.sleep(1) + except greenlet.GreenletExit: + raise except Exception as ex: - # If we get a GreenletExit exception, the create thread has - # been killed so we should raise allowing this thread to exit - if type(ex) is greenlet.GreenletExit: - logger.warning('GreenletExit during create, exiting') - raise - else: - logger.exception('create %s', str(self)) - self.state_set(self.CREATE_FAILED, str(ex)) - return str(ex) or "Error : %s" % type(ex) + logger.exception('create %s', str(self)) + self.state_set(self.CREATE_FAILED, str(ex)) + return str(ex) or "Error : %s" % type(ex) else: self.state_set(self.CREATE_COMPLETE) diff --git a/heat/engine/resources/instance.py b/heat/engine/resources/instance.py index 5e068cbdf..d35ce8906 100644 --- a/heat/engine/resources/instance.py +++ b/heat/engine/resources/instance.py @@ -171,9 +171,14 @@ class Instance(resource.Resource): return msg def read_cloudinit_file(fn): - return pkgutil.get_data('heat', 'cloudinit/%s' % fn) + data = pkgutil.get_data('heat', 'cloudinit/%s' % fn) + data = data.replace('@INSTANCE_USER@', + cfg.CONF.instance_user) + return data attachments = [(read_cloudinit_file('config'), 'cloud-config'), + (read_cloudinit_file('boothook.sh'), 'boothook.sh', + 'cloud-boothook'), (read_cloudinit_file('part-handler.py'), 'part-handler.py'), (userdata, 'cfn-userdata', 'x-cfninitdata'), diff --git a/heat/tests/test_loguserdata.py b/heat/tests/test_loguserdata.py index 0fce0e151..94c0f2cb3 100644 --- a/heat/tests/test_loguserdata.py +++ b/heat/tests/test_loguserdata.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. - +import errno import mox import os import pkg_resources @@ -23,7 +23,7 @@ import StringIO from nose.plugins.attrib import attr -import heat.cloudinit.loguserdata as loguserdata +from heat.cloudinit import loguserdata class FakeCiVersion(): @@ -118,14 +118,62 @@ class LoguserdataTest(unittest.TestCase): loguserdata.main(log) self.m.VerifyAll() - def test_main_fails(self): + def test_main_script_empty(self): log = StringIO.StringIO() + pkg_resources.get_distribution('cloud-init').AndReturn( + FakeCiVersion('0.7.0')) + + os.chmod('/var/lib/heat-cfntools/cfn-userdata', 0700).AndReturn(None) + subprocess.Popen( + ['/var/lib/heat-cfntools/cfn-userdata'], + stderr=log, + stdout=log).AndRaise(OSError(errno.ENOEXEC, "empty script")) + + self.m.ReplayAll() + self.assertEqual(None, loguserdata.main(log)) + + self.m.VerifyAll() + + def test_main_os_error(self): + + log = StringIO.StringIO() + + pkg_resources.get_distribution('cloud-init').AndReturn( + FakeCiVersion('0.7.0')) + + os.chmod('/var/lib/heat-cfntools/cfn-userdata', 0700).AndReturn(None) + subprocess.Popen( + ['/var/lib/heat-cfntools/cfn-userdata'], + stderr=log, + stdout=log).AndRaise(OSError(errno.ENOENT, "no such file")) + + self.m.ReplayAll() + self.assertEqual(os.EX_OSERR, loguserdata.main(log)) + + self.m.VerifyAll() + + def test_main_error_other(self): + log = StringIO.StringIO() + pkg_resources.get_distribution('cloud-init').AndReturn( + FakeCiVersion('0.7.0')) + os.chmod('/var/lib/heat-cfntools/cfn-userdata', 0700).AndReturn(None) + subprocess.Popen( + ['/var/lib/heat-cfntools/cfn-userdata'], + stderr=log, + stdout=log).AndRaise(IOError("read failed")) + + self.m.ReplayAll() + self.assertEqual(os.EX_SOFTWARE, loguserdata.main(log)) + self.m.VerifyAll() + + def test_main_fails(self): + log = StringIO.StringIO() + #fail on ci version pkg_resources.get_distribution('cloud-init').AndReturn( FakeCiVersion('0.5.0')) - #fail on execute cfn-userdata pkg_resources.get_distribution('cloud-init').AndReturn( FakeCiVersion('0.7.0')) @@ -139,5 +187,4 @@ class LoguserdataTest(unittest.TestCase): self.m.ReplayAll() self.assertEqual(-1, loguserdata.main(log)) self.assertEqual(-2, loguserdata.main(log)) - self.m.VerifyAll() |