From cb2801cc7bb6be272057a4aad51d075c601285eb Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Fri, 11 Oct 2013 16:54:57 +1100 Subject: [PATCH] Allow overriding the instance_user per server I have called the property 'admin_user' as the bug suggests. Closes-bug: #1229825 Change-Id: I578ba9c0419383b34ac137e714f9cc00fe696ea3 --- heat/engine/resources/nova_utils.py | 6 ++++-- heat/engine/resources/server.py | 13 ++++++++++++- heat/tests/test_nova_utils.py | 28 +++++++++++++++++++++++++--- test-requirements.txt | 1 + 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/heat/engine/resources/nova_utils.py b/heat/engine/resources/nova_utils.py index 740db39c1a..e541e137fa 100644 --- a/heat/engine/resources/nova_utils.py +++ b/heat/engine/resources/nova_utils.py @@ -122,7 +122,7 @@ def get_keypair(nova_client, key_name): raise exception.UserKeyPairMissing(key_name=key_name) -def build_userdata(resource, userdata=None): +def build_userdata(resource, userdata=None, instance_user=None): ''' Build multipart data blob for CloudInit which includes user-supplied Metadata, user data, and the required Heat in-instance configuration. @@ -131,6 +131,8 @@ def build_userdata(resource, userdata=None): :type resource: heat.engine.Resource :param userdata: user data string :type userdata: str or None + :param instance_user: the user to create on the server + :type instance_user: string :returns: multipart mime as a string ''' @@ -145,7 +147,7 @@ def build_userdata(resource, userdata=None): def read_cloudinit_file(fn): data = pkgutil.get_data('heat', 'cloudinit/%s' % fn) data = data.replace('@INSTANCE_USER@', - cfg.CONF.instance_user) + instance_user or cfg.CONF.instance_user) return data attachments = [(read_cloudinit_file('config'), 'cloud-config'), diff --git a/heat/engine/resources/server.py b/heat/engine/resources/server.py index e2cc46e271..a79e60b716 100644 --- a/heat/engine/resources/server.py +++ b/heat/engine/resources/server.py @@ -13,6 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.config import cfg + +cfg.CONF.import_opt('instance_user', 'heat.common.config') + from heat.common import exception from heat.engine import clients from heat.engine import scheduler @@ -95,6 +99,12 @@ class Server(resource.Resource): 'key_name': { 'Type': 'String', 'Description': _('Name of keypair to inject into the server')}, + 'admin_user': { + 'Type': 'String', + 'Default': cfg.CONF.instance_user, + 'Description': _('Name of the administrative user to use ' + ' on the server')}, + 'availability_zone': { 'Type': 'String', 'Description': _('Name of the availability zone for server ' @@ -171,7 +181,8 @@ class Server(resource.Resource): def get_mime_string(self, userdata): if not self.mime_string: - self.mime_string = nova_utils.build_userdata(self, userdata) + self.mime_string = nova_utils.build_userdata( + self, userdata, instance_user=self.properties['admin_user']) return self.mime_string def physical_resource_name(self): diff --git a/heat/tests/test_nova_utils.py b/heat/tests/test_nova_utils.py index ca3f5559e8..0aee55ebf0 100644 --- a/heat/tests/test_nova_utils.py +++ b/heat/tests/test_nova_utils.py @@ -14,12 +14,15 @@ # under the License. """Tests for :module:'heat.engine.resources.nova_utls'.""" +import testscenarios import uuid from heat.common import exception from heat.engine.resources import nova_utils from heat.tests.common import HeatTestCase +load_tests = testscenarios.load_tests_apply_scenarios + class NovaUtilsTests(HeatTestCase): """ @@ -84,19 +87,38 @@ class NovaUtilsTests(HeatTestCase): self.nova_client, 'notakey') self.m.VerifyAll() + +class NovaUtilsUserdataTests(HeatTestCase): + + scenarios = [ + ('no_conf_no_prop', dict( + conf_user='ec2-user', instance_user=None, expect='ec2-user')), + ('no_conf_prop', dict( + conf_user='ec2-user', instance_user='fruity', expect='fruity')), + ('conf_no_prop', dict( + conf_user='nutty', instance_user=None, expect='nutty')), + ('conf_prop', dict( + conf_user='nutty', instance_user='fruity', expect='fruity')), + ] + + def setUp(self): + super(NovaUtilsUserdataTests, self).setUp() + self.nova_client = self.m.CreateMockAnything() + def test_build_userdata(self): """Tests the build_userdata function.""" resource = self.m.CreateMockAnything() resource.t = {} self.m.StubOutWithMock(nova_utils.cfg, 'CONF') cnf = nova_utils.cfg.CONF - cnf.instance_user = 'testuser' + cnf.instance_user = self.conf_user cnf.heat_metadata_server_url = 'http://server.test:123' cnf.heat_watch_server_url = 'http://server.test:345' cnf.instance_connection_is_secure = False cnf.instance_connection_https_validate_certificates = False self.m.ReplayAll() - data = nova_utils.build_userdata(resource) + data = nova_utils.build_userdata(resource, + instance_user=self.instance_user) self.assertTrue("Content-Type: text/cloud-config;" in data) self.assertTrue("Content-Type: text/cloud-boothook;" in data) self.assertTrue("Content-Type: text/part-handler;" in data) @@ -105,5 +127,5 @@ class NovaUtilsTests(HeatTestCase): self.assertTrue("http://server.test:345" in data) self.assertTrue("http://server.test:123" in data) self.assertTrue("[Boto]" in data) - self.assertTrue('testuser' in data) + self.assertTrue(self.expect in data) self.m.VerifyAll() diff --git a/test-requirements.txt b/test-requirements.txt index a9957306a4..915532c995 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -10,6 +10,7 @@ mock>=1.0 mox>=0.5.3 testtools>=0.9.32 testrepository>=0.0.17 +testscenarios>=0.4 python-glanceclient>=0.9.0 sphinx>=1.1.2 oslo.sphinx