From c7ef6894c8a5354256eb0e2a476fac3b51c3827b Mon Sep 17 00:00:00 2001 From: Mick Gregg Date: Wed, 6 May 2015 15:03:04 +0100 Subject: [PATCH 1/3] [macgreagoir] Make heat.conf instance_user item configurable. --- Makefile | 4 ++ config.yaml | 9 ++++- hooks/heat_context.py | 12 ++++++ hooks/heat_utils.py | 2 + templates/heat.conf | 4 +- tests/00-setup | 5 +++ tests/50-basic-deploy | 66 +++++++++++++++++++++++++++++++++ unit_tests/test_heat_context.py | 9 ++++- 8 files changed, 108 insertions(+), 3 deletions(-) create mode 100755 tests/00-setup create mode 100755 tests/50-basic-deploy diff --git a/Makefile b/Makefile index 7965721..be5004c 100644 --- a/Makefile +++ b/Makefile @@ -21,4 +21,8 @@ sync: bin/charm_helpers_sync.py unit_test: @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests +test: + @echo "Running amulet tests: " + @for f in tests/*; do $$f; done + all: unit_test lint diff --git a/config.yaml b/config.yaml index 1e39182..5347505 100644 --- a/config.yaml +++ b/config.yaml @@ -22,6 +22,13 @@ options: default: heat type: string description: Database name + instance-user: + default: + type: string + description: | + The default user for new instances. This option is deprecated as of Juno. + If left empty, Heat will use the default user set up with your cloud + image (for OS::Nova::Server) or 'ec2-user' (for AWS::EC2::Instance). region: default: RegionOne type: string @@ -62,4 +69,4 @@ options: default: description: | SSL CA to use with the certificate and key provided - this is only - required if you are providing a privately signed ssl_cert and ssl_key. \ No newline at end of file + required if you are providing a privately signed ssl_cert and ssl_key. diff --git a/hooks/heat_context.py b/hooks/heat_context.py index 922c4bc..20ab49b 100644 --- a/hooks/heat_context.py +++ b/hooks/heat_context.py @@ -96,3 +96,15 @@ class HeatApacheSSLContext(context.ApacheSSLContext): external_ports = API_PORTS.values() service_namespace = 'heat' + + +class InstanceUserContext(context.OSContextGenerator): + + def __call__(self): + ctxt = {} + + instance_user = '' + if config('instance-user'): + instance_user = config('instance-user') + ctxt['instance_user'] = instance_user + return ctxt diff --git a/hooks/heat_utils.py b/hooks/heat_utils.py index 651d936..6dce806 100644 --- a/hooks/heat_utils.py +++ b/hooks/heat_utils.py @@ -29,6 +29,7 @@ from heat_context import ( API_PORTS, HeatIdentityServiceContext, EncryptionContext, + InstanceUserContext, HeatApacheSSLContext, HeatHAProxyContext, ) @@ -68,6 +69,7 @@ CONFIG_FILES = OrderedDict([ HeatIdentityServiceContext(service=SVC, service_user=SVC), HeatHAProxyContext(), EncryptionContext(), + InstanceUserContext(), context.SyslogContext()] }), (HEAT_API_PASTE, { diff --git a/templates/heat.conf b/templates/heat.conf index 9e35ffb..2fa58c0 100644 --- a/templates/heat.conf +++ b/templates/heat.conf @@ -3,7 +3,9 @@ use_syslog = {{ use_syslog }} debug = False verbose = False log_dir = /var/log/heat -instance_user=ec2-user +# Icehouse expects 'instance_user=' to allow the image's default user +# Not including instance_user at all results in 'ec2-user' being used +instance_user={{ instance_user }} instance_driver=heat.engine.nova plugin_dirs=/usr/lib64/heat,/usr/lib/heat environment_dir=/etc/heat/environment.d diff --git a/tests/00-setup b/tests/00-setup new file mode 100755 index 0000000..cd5b3a2 --- /dev/null +++ b/tests/00-setup @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo add-apt-repository ppa:juju/stable -y +sudo apt-get update +sudo apt-get install amulet python3-requests -y diff --git a/tests/50-basic-deploy b/tests/50-basic-deploy new file mode 100755 index 0000000..0a5ed2a --- /dev/null +++ b/tests/50-basic-deploy @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import amulet +import unittest + + +class TestDeployment(unittest.TestCase): + + @classmethod + def setUpClass(cls): + try: + cls.d = amulet.Deployment(series='trusty') + cls.d.add('heat') + cls.d.configure('heat', {'instance-user': 'ubuntu'}) + cls.d.setup(timeout=1800) + cls.d.sentry.wait() + + cls.u = cls.d.sentry.unit['heat/0'] + except amulet.helpers.TimeoutError: + msg = "Environment wasn't stood up in time" + amulet.raise_status(amulet.SKIP, msg=msg) + except: + raise + + # amulet.raise_status(): + # - amulet.PASS + # - amulet.FAIL + # - amulet.SKIP + # Each unit has the following methods: + # - .info - An array of the information of that unit from Juju + # - .file(PATH) - Get the details of a file on that unit + # - .file_contents(PATH) - Get plain text output of PATH file from that unit + # - .directory(PATH) - Get details of directory + # - .directory_contents(PATH) - List files and folders in PATH on that unit + # - .relation(relation, service:rel) - Get relation data from return service + # add tests here to confirm service is up and working properly + # - .run(something) + # For example, to confirm that it has a functioning HTTP server: + # page = requests.get('http://{}'.format(self.unit.info['public-address'])) + # page.raise_for_status() + # More information on writing Amulet tests can be found at: + # https://juju.ubuntu.com/docs/tools-amulet.html + + def check_file_content(self, name, find): + """Check that the named file exists and contains the find text.""" + stat = TestDeployment.u.file(name) + if stat is None: + msg = "Could not retrieve status of %s" % name + amulet.raise_status(amulet.FAIL, msg=msg) + content = TestDeployment.u.file_contents(name) + if content.find(find) < 0: + msg = "%s does not contain the required text (%s)" % (name, find) + amulet.raise_status(amulet.FAIL, msg=msg) + return False + else: + print("%s contains the required text (%s)" % (name, find)) + return True + + def test_instance_user(self): + """Check that /etc/heat/heat.conf has instance_user item""" + # finding only 'instance_user=' will catch comments too + self.check_file_content(name='/etc/heat/heat.conf', find='instance_user=ubuntu') + + +if __name__ == '__main__': + unittest.main() diff --git a/unit_tests/test_heat_context.py b/unit_tests/test_heat_context.py index 4241668..9692d4d 100644 --- a/unit_tests/test_heat_context.py +++ b/unit_tests/test_heat_context.py @@ -4,7 +4,8 @@ from test_utils import CharmTestCase TO_PATCH = [ 'get_encryption_key', - 'generate_ec2_tokens' + 'generate_ec2_tokens', + 'config' ] @@ -19,6 +20,12 @@ class TestHeatContext(CharmTestCase): heat_context.EncryptionContext()(), {'encryption_key': 'key'}) + def test_instance_user_empty_configuration(self): + self.config.return_value = None + self.assertEquals( + heat_context.InstanceUserContext()(), + {'instance_user': ''}) + @patch('charmhelpers.contrib.openstack.' 'context.IdentityServiceContext.__call__') def test_identity_configuration(self, __call__): From 8823e1d7b780e7a771e2f5eb369d2031ba3a963b Mon Sep 17 00:00:00 2001 From: Mick Gregg Date: Wed, 6 May 2015 15:56:16 +0100 Subject: [PATCH 2/3] Updated Makefile for Amulet tests. --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index be5004c..7e28138 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,10 @@ unit_test: @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests test: - @echo "Running amulet tests: " - @for f in tests/*; do $$f; done + @echo Starting Amulet tests... + # coreycb note: The -v should only be temporary until Amulet sends + # raise_status() messages to stderr: + # https://bugs.launchpad.net/amulet/+bug/1320357 + @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700 all: unit_test lint From efc9e74410a3e3faa86407e1d80795e0fe045930 Mon Sep 17 00:00:00 2001 From: Mick Gregg Date: Wed, 2 Dec 2015 16:06:55 +0000 Subject: [PATCH 3/3] [macgreagoir] Moved instance_user heat.conf change to icehouse/ template; updated kilo/ template --- templates/heat.conf | 4 +- templates/icehouse/heat.conf | 79 ++++++++++++++++++++++++++++++++++++ templates/kilo/heat.conf | 2 +- 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 templates/icehouse/heat.conf diff --git a/templates/heat.conf b/templates/heat.conf index 19b4fad..43c3e30 100644 --- a/templates/heat.conf +++ b/templates/heat.conf @@ -3,9 +3,7 @@ use_syslog = {{ use_syslog }} verbose = {{ verbose }} debug = {{ debug }} log_dir = /var/log/heat -# Icehouse expects 'instance_user=' to allow the image's default user -# Not including instance_user at all results in 'ec2-user' being used -instance_user={{ instance_user }} +instance_user=ec2-user instance_driver=heat.engine.nova plugin_dirs=/usr/lib64/heat,/usr/lib/heat environment_dir=/etc/heat/environment.d diff --git a/templates/icehouse/heat.conf b/templates/icehouse/heat.conf new file mode 100644 index 0000000..19b4fad --- /dev/null +++ b/templates/icehouse/heat.conf @@ -0,0 +1,79 @@ +[DEFAULT] +use_syslog = {{ use_syslog }} +verbose = {{ verbose }} +debug = {{ debug }} +log_dir = /var/log/heat +# Icehouse expects 'instance_user=' to allow the image's default user +# Not including instance_user at all results in 'ec2-user' being used +instance_user={{ instance_user }} +instance_driver=heat.engine.nova +plugin_dirs=/usr/lib64/heat,/usr/lib/heat +environment_dir=/etc/heat/environment.d +deferred_auth_method=password +host=heat +auth_encryption_key={{ encryption_key }} + +{% if database_host -%} +# < Icehouse db config +sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} +{% endif %} + +{% if rabbitmq_host or rabbitmq_hosts -%} +rabbit_userid = {{ rabbitmq_user }} +rabbit_virtual_host = {{ rabbitmq_virtual_host }} +rabbit_password = {{ rabbitmq_password }} +{% if rabbitmq_hosts -%} +rabbit_hosts = {{ rabbitmq_hosts }} +{% if rabbitmq_ha_queues -%} +rabbit_ha_queues = True +rabbit_durable_queues = False +{% endif -%} +{% else -%} +rabbit_host = {{ rabbitmq_host }} +{% endif -%} +{% if rabbit_ssl_port -%} +rabbit_use_ssl = True +rabbit_port = {{ rabbit_ssl_port }} +{% if rabbit_ssl_ca -%} +kombu_ssl_ca_certs = {{ rabbit_ssl_ca }} +{% endif -%} +{% endif -%} +{% endif %} + +{% if auth_host -%} +[keystone_authtoken] +auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0 +auth_host = {{ auth_host }} +auth_port = {{ auth_port }} +auth_protocol = {{ auth_protocol }} +admin_tenant_name = {{ admin_tenant_name }} +admin_user = {{ admin_user }} +admin_password = {{ admin_password }} +signing_dir = {{ signing_dir }} +{% endif %} + +[ec2_authtoken] +auth_uri = {{service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0 +keystone_ec2_uri = {{service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0/ec2tokens + +{% if database_host -%} +[database] +connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} +{% endif -%} + +[paste_deploy] +api_paste_config=/etc/heat/api-paste.ini + +[heat_api] +{% if api_listen_port -%} +bind_port={{ api_listen_port }} +{% else -%} +bind_port=8004 +{% endif %} + +[heat_api_cfn] +{% if api_cfn_listen_port -%} +bind_port={{ api_cfn_listen_port }} +{% else -%} +bind_port=8000 +{% endif %} diff --git a/templates/kilo/heat.conf b/templates/kilo/heat.conf index 81da4aa..8fc909b 100644 --- a/templates/kilo/heat.conf +++ b/templates/kilo/heat.conf @@ -3,7 +3,7 @@ use_syslog = {{ use_syslog }} debug = False verbose = False log_dir = /var/log/heat -instance_user=ec2-user +instance_user={{ instance_user }} instance_driver=heat.engine.nova plugin_dirs=/usr/lib64/heat,/usr/lib/heat environment_dir=/etc/heat/environment.d