summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST.in1
-rw-r--r--doc/source/conf.py5
-rw-r--r--doc/source/man/heat-db-setup.rst63
-rw-r--r--heat/cloudinit/boothook.sh7
-rw-r--r--heat/cloudinit/config7
-rw-r--r--heat/cloudinit/loguserdata.py16
-rw-r--r--heat/cloudinit/part-handler.py3
-rw-r--r--heat/common/config.py3
-rw-r--r--heat/engine/resource.py14
-rw-r--r--heat/engine/resources/instance.py7
-rw-r--r--heat/tests/test_loguserdata.py57
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()