Don't create cloud-init user unless specified

When the the instance_user value from heat.conf is set to empty string/None and
the user doesn't specify Server's admin_user property, Heat will not create a
custom cloud-init user.

The instance_user config option and admin_user property are deprecated and will
be removed in Juno where this behaviour becomes the default.

AWS::EC2::Instance will still create a cloud-init user for CloudFormation
compatibility. In the absence of the instance_user config option, 'ec2-user'
will be used.

Closes-Bug: #1257410
Change-Id: I42dda36045f79be079b2030669284e9db71463d7
This commit is contained in:
Tomas Sedovic 2014-03-03 07:25:31 -05:00
parent 0d19394458
commit b8eefd1de9
14 changed files with 274 additions and 61 deletions

View File

@ -14,7 +14,11 @@
# Options defined in heat.common.config # Options defined in heat.common.config
# #
# The default user for new instances. (string value) # The default user for new instances. This option is
# deprecated and will be removed in the Juno release. If it's
# empty, Heat will use the default user set up with your cloud
# image (for OS::Nova::Server) or 'ec2-user' (for
# AWS::EC2::Instance). (string value)
#instance_user=ec2-user #instance_user=ec2-user
# Driver to use for controlling instances. (string value) # Driver to use for controlling instances. (string value)

View File

@ -1,11 +1,14 @@
#!/bin/bash #!/bin/bash
# FIXME(shadower) The `useradd` and `sudoers` lines are a workaround for # FIXME(shadower) this is a workaround for cloud-init 0.6.3 present in Ubuntu
# cloud-init 0.6.3 present in Ubuntu 12.04 LTS: # 12.04 LTS:
# https://bugs.launchpad.net/heat/+bug/1257410 # https://bugs.launchpad.net/heat/+bug/1257410
# Once we drop support for it, we can safely remove them. #
useradd -m @INSTANCE_USER@ # The old cloud-init doesn't create the users directly so the commands to do
echo -e '@INSTANCE_USER@\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers # this are injected though nova_utils.py.
#
# Once we drop support for 0.6.3, we can safely remove this.
${add_custom_user}
# in case heat-cfntools has been installed from package but no symlinks # in case heat-cfntools has been installed from package but no symlinks
# are yet in /opt/aws/bin/ # are yet in /opt/aws/bin/

View File

@ -1,8 +1,4 @@
# Set the SSH key provided by Nova to this user. ${add_custom_user}
# On cloud-init 0.7.x (anything except Ubuntu 12.04 LTS which ships 0.6.3) this
# also creates the user and sets up passwordless sudo if the user isn't present
# already.
user: @INSTANCE_USER@
# Capture all subprocess output into a logfile # Capture all subprocess output into a logfile
# Useful for troubleshooting cloud-init issues # Useful for troubleshooting cloud-init issues

View File

@ -28,6 +28,8 @@ from heat.common import wsgi
from heat.openstack.common import log as logging from heat.openstack.common import log as logging
from heat.openstack.common import rpc from heat.openstack.common import rpc
logger = logging.getLogger(__name__)
paste_deploy_group = cfg.OptGroup('paste_deploy') paste_deploy_group = cfg.OptGroup('paste_deploy')
paste_deploy_opts = [ paste_deploy_opts = [
cfg.StrOpt('flavor', cfg.StrOpt('flavor',
@ -80,7 +82,11 @@ service_opts = [
engine_opts = [ engine_opts = [
cfg.StrOpt('instance_user', cfg.StrOpt('instance_user',
default='ec2-user', default='ec2-user',
help='The default user for new instances.'), help="The default user for new instances. This option "
"is deprecated and will be removed in the Juno release. "
"If it's empty, Heat will use the default user set up "
"with your cloud image (for OS::Nova::Server) or "
"'ec2-user' (for AWS::EC2::Instance)."),
cfg.StrOpt('instance_driver', cfg.StrOpt('instance_driver',
default='heat.engine.nova', default='heat.engine.nova',
help='Driver to use for controlling instances.'), help='Driver to use for controlling instances.'),
@ -213,6 +219,10 @@ allowed_rpc_exception_modules.append('heat.common.exception')
cfg.CONF.set_default(name='allowed_rpc_exception_modules', cfg.CONF.set_default(name='allowed_rpc_exception_modules',
default=allowed_rpc_exception_modules) default=allowed_rpc_exception_modules)
if cfg.CONF.instance_user:
logger.warn(_('The "instance_user" option in heat.conf is deprecated and '
'will be removed in the Juno release.'))
def _get_deployment_flavor(): def _get_deployment_flavor():
""" """

View File

@ -12,6 +12,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo.config import cfg
cfg.CONF.import_opt('instance_user', 'heat.common.config')
from heat.common import exception from heat.common import exception
from heat.engine import clients from heat.engine import clients
from heat.engine import constraints from heat.engine import constraints
@ -430,6 +434,14 @@ class Instance(resource.Resource):
subnet_id=self.properties[self.SUBNET_ID]) subnet_id=self.properties[self.SUBNET_ID])
server = None server = None
# FIXME(shadower): the instance_user config option is deprecated. Once
# it's gone, we should always use ec2-user for compatibility with
# CloudFormation.
if cfg.CONF.instance_user:
instance_user = cfg.CONF.instance_user
else:
instance_user = 'ec2-user'
try: try:
server = self.nova().servers.create( server = self.nova().servers.create(
name=self.physical_resource_name(), name=self.physical_resource_name(),
@ -437,7 +449,8 @@ class Instance(resource.Resource):
flavor=flavor_id, flavor=flavor_id,
key_name=self.properties[self.KEY_NAME], key_name=self.properties[self.KEY_NAME],
security_groups=security_groups, security_groups=security_groups,
userdata=nova_utils.build_userdata(self, userdata), userdata=nova_utils.build_userdata(self, userdata,
instance_user),
meta=self._get_nova_metadata(self.properties), meta=self._get_nova_metadata(self.properties),
scheduler_hints=scheduler_hints, scheduler_hints=scheduler_hints,
nics=nics, nics=nics,

View File

@ -19,6 +19,7 @@ from email.mime.text import MIMEText
import json import json
import os import os
import pkgutil import pkgutil
import string
from oslo.config import cfg from oslo.config import cfg
import six import six
@ -187,16 +188,33 @@ def build_userdata(resource, userdata=None, instance_user=None,
return msg return msg
def read_cloudinit_file(fn): def read_cloudinit_file(fn):
data = pkgutil.get_data('heat', 'cloudinit/%s' % fn) return pkgutil.get_data('heat', 'cloudinit/%s' % fn)
data = data.replace('@INSTANCE_USER@',
instance_user or cfg.CONF.instance_user)
return data
attachments = [(read_cloudinit_file('config'), 'cloud-config'), if instance_user:
(read_cloudinit_file('boothook.sh'), 'boothook.sh', config_custom_user = 'user: %s' % instance_user
'cloud-boothook')] # FIXME(shadower): compatibility workaround for cloud-init 0.6.3. We
attachments.append((read_cloudinit_file('part_handler.py'), # can drop this once we stop supporting 0.6.3 (which ships with Ubuntu
'part-handler.py')) # 12.04 LTS).
#
# See bug https://bugs.launchpad.net/heat/+bug/1257410
boothook_custom_user = r"""useradd -m %s
echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
""" % (instance_user, instance_user)
else:
config_custom_user = ''
boothook_custom_user = ''
cloudinit_config = string.Template(
read_cloudinit_file('config')).safe_substitute(
add_custom_user=config_custom_user)
cloudinit_boothook = string.Template(
read_cloudinit_file('boothook.sh')).safe_substitute(
add_custom_user=boothook_custom_user)
attachments = [(cloudinit_config, 'cloud-config'),
(cloudinit_boothook, 'boothook.sh', 'cloud-boothook'),
(read_cloudinit_file('part_handler.py'),
'part-handler.py')]
if is_cfntools: if is_cfntools:
attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata')) attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata'))

View File

@ -171,8 +171,12 @@ class Server(stack_user.StackUser):
), ),
ADMIN_USER: properties.Schema( ADMIN_USER: properties.Schema(
properties.Schema.STRING, properties.Schema.STRING,
_('Name of the administrative user to use on the server.'), _('Name of the administrative user to use on the server. '
default=cfg.CONF.instance_user 'This property will be removed from Juno in favor of the '
'default cloud-init user set up for each image (e.g. "ubuntu" '
'for Ubuntu 12.04+, "fedora" for Fedora 19+ and "cloud-user" '
'for CentOS/RHEL 6.5).'),
support_status=support.SupportStatus(status=support.DEPRECATED)
), ),
AVAILABILITY_ZONE: properties.Schema( AVAILABILITY_ZONE: properties.Schema(
properties.Schema.STRING, properties.Schema.STRING,
@ -470,10 +474,17 @@ class Server(stack_user.StackUser):
if self.user_data_software_config(): if self.user_data_software_config():
self._create_transport_credentials() self._create_transport_credentials()
if self.properties[self.ADMIN_USER]:
instance_user = self.properties[self.ADMIN_USER]
elif cfg.CONF.instance_user:
instance_user = cfg.CONF.instance_user
else:
instance_user = None
userdata = nova_utils.build_userdata( userdata = nova_utils.build_userdata(
self, self,
ud_content, ud_content,
instance_user=self.properties[self.ADMIN_USER], instance_user=instance_user,
user_data_format=user_data_format) user_data_format=user_data_format)
flavor = self.properties[self.FLAVOR] flavor = self.properties[self.FLAVOR]

View File

@ -195,11 +195,13 @@ def setup_mocks(mocks, stack):
instance = stack['WebServer'] instance = stack['WebServer']
user_data = instance.properties['UserData'] user_data = instance.properties['UserData']
server_userdata = nova_utils.build_userdata(instance, user_data) server_userdata = nova_utils.build_userdata(instance, user_data,
'ec2-user')
mocks.StubOutWithMock(nova_utils, 'build_userdata') mocks.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata( nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']).AndReturn(server_userdata) instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
mocks.StubOutWithMock(fc.servers, 'create') mocks.StubOutWithMock(fc.servers, 'create')
fc.servers.create(image=744, flavor=3, key_name='test', fc.servers.create(image=744, flavor=3, key_name='test',

View File

@ -93,16 +93,6 @@ class InstancesTest(HeatTestCase):
instance.t = instance.stack.resolve_runtime_data(instance.t) instance.t = instance.stack.resolve_runtime_data(instance.t)
if stub_create: if stub_create:
# need to resolve the template functions
server_userdata = nova_utils.build_userdata(
instance,
instance.t['Properties']['UserData'])
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
instance.t['Properties']['UserData']).AndReturn(
server_userdata)
self.m.StubOutWithMock(self.fc.servers, 'create') self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create( self.fc.servers.create(
image=1, flavor=1, key_name='test', image=1, flavor=1, key_name='test',
@ -111,7 +101,7 @@ class InstancesTest(HeatTestCase):
instance.name, instance.name,
limit=instance.physical_resource_name_limit), limit=instance.physical_resource_name_limit),
security_groups=None, security_groups=None,
userdata=server_userdata, scheduler_hints=None, userdata=mox.IgnoreArg(), scheduler_hints=None,
meta=None, nics=None, availability_zone=None).AndReturn( meta=None, nics=None, availability_zone=None).AndReturn(
return_server) return_server)
@ -855,3 +845,50 @@ class InstancesTest(HeatTestCase):
'wo_ipaddr') 'wo_ipaddr')
self.assertEqual('0.0.0.0', instance.FnGetAtt('PrivateIp')) self.assertEqual('0.0.0.0', instance.FnGetAtt('PrivateIp'))
def test_default_instance_user(self):
"""The default value for instance_user in heat.conf is ec2-user."""
return_server = self.fc.servers.list()[1]
instance = self._setup_test_instance(return_server, 'default_user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'ec2-user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()
def test_custom_instance_user(self):
"""Test instance_user in heat.conf being set to a custom value.
Launching the instance should call build_userdata with the custom user
name.
This option is deprecated and will be removed in Juno.
"""
return_server = self.fc.servers.list()[1]
instance = self._setup_test_instance(return_server, 'custom_user')
self.m.StubOutWithMock(instances.cfg.CONF, 'instance_user')
instances.cfg.CONF.instance_user = 'custom_user'
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'custom_user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()
def test_empty_instance_user(self):
"""Test instance_user in heat.conf being empty.
Launching the instance should call build_userdata with
"ec2-user".
This behaviour is compatible with CloudFormation and will be
the default in Juno once the instance_user option gets removed.
"""
return_server = self.fc.servers.list()[1]
instance = self._setup_test_instance(return_server, 'empty_user')
self.m.StubOutWithMock(instances.cfg.CONF, 'instance_user')
instances.cfg.CONF.instance_user = ''
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'ec2-user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()

View File

@ -178,11 +178,13 @@ class instancesTest(HeatTestCase):
# need to resolve the template functions # need to resolve the template functions
server_userdata = nova_utils.build_userdata( server_userdata = nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']) instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata') self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata( nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']).AndReturn(server_userdata) instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
self.m.StubOutWithMock(self.fc.servers, 'create') self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create( self.fc.servers.create(
@ -232,11 +234,13 @@ class instancesTest(HeatTestCase):
# need to resolve the template functions # need to resolve the template functions
server_userdata = nova_utils.build_userdata( server_userdata = nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']) instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata') self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata( nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']).AndReturn(server_userdata) instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
self.m.StubOutWithMock(self.fc.servers, 'create') self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create( self.fc.servers.create(

View File

@ -68,11 +68,13 @@ class nokeyTest(HeatTestCase):
# need to resolve the template functions # need to resolve the template functions
server_userdata = nova_utils.build_userdata( server_userdata = nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']) instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata') self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata( nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']).AndReturn(server_userdata) instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
self.m.StubOutWithMock(self.fc.servers, 'create') self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create( self.fc.servers.create(

View File

@ -161,17 +161,6 @@ class NovaUtilsRefreshServerTests(HeatTestCase):
class NovaUtilsUserdataTests(HeatTestCase): 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): def setUp(self):
super(NovaUtilsUserdataTests, self).setUp() super(NovaUtilsUserdataTests, self).setUp()
self.nova_client = self.m.CreateMockAnything() self.nova_client = self.m.CreateMockAnything()
@ -182,14 +171,12 @@ class NovaUtilsUserdataTests(HeatTestCase):
resource.metadata = {} resource.metadata = {}
self.m.StubOutWithMock(nova_utils.cfg, 'CONF') self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
cnf = nova_utils.cfg.CONF cnf = nova_utils.cfg.CONF
cnf.instance_user = self.conf_user
cnf.heat_metadata_server_url = 'http://server.test:123' cnf.heat_metadata_server_url = 'http://server.test:123'
cnf.heat_watch_server_url = 'http://server.test:345' cnf.heat_watch_server_url = 'http://server.test:345'
cnf.instance_connection_is_secure = False cnf.instance_connection_is_secure = False
cnf.instance_connection_https_validate_certificates = False cnf.instance_connection_https_validate_certificates = False
self.m.ReplayAll() self.m.ReplayAll()
data = nova_utils.build_userdata(resource, data = nova_utils.build_userdata(resource)
instance_user=self.instance_user)
self.assertIn("Content-Type: text/cloud-config;", data) self.assertIn("Content-Type: text/cloud-config;", data)
self.assertIn("Content-Type: text/cloud-boothook;", data) self.assertIn("Content-Type: text/cloud-boothook;", data)
self.assertIn("Content-Type: text/part-handler;", data) self.assertIn("Content-Type: text/part-handler;", data)
@ -198,7 +185,38 @@ class NovaUtilsUserdataTests(HeatTestCase):
self.assertIn("http://server.test:345", data) self.assertIn("http://server.test:345", data)
self.assertIn("http://server.test:123", data) self.assertIn("http://server.test:123", data)
self.assertIn("[Boto]", data) self.assertIn("[Boto]", data)
self.assertIn(self.expect, data) self.m.VerifyAll()
def test_build_userdata_without_instance_user(self):
"""Don't add a custom instance user when not requested."""
resource = self.m.CreateMockAnything()
resource.metadata = {}
self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
cnf = nova_utils.cfg.CONF
cnf.instance_user = 'config_instance_user'
cnf.heat_metadata_server_url = 'http://server.test:123'
cnf.heat_watch_server_url = 'http://server.test:345'
self.m.ReplayAll()
data = nova_utils.build_userdata(resource, instance_user=None)
self.assertNotIn('user: ', data)
self.assertNotIn('useradd', data)
self.assertNotIn('config_instance_user', data)
self.m.VerifyAll()
def test_build_userdata_with_instance_user(self):
"""Add the custom instance user when requested."""
resource = self.m.CreateMockAnything()
resource.metadata = {}
self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
cnf = nova_utils.cfg.CONF
cnf.instance_user = 'config_instance_user'
cnf.heat_metadata_server_url = 'http://server.test:123'
cnf.heat_watch_server_url = 'http://server.test:345'
self.m.ReplayAll()
data = nova_utils.build_userdata(resource,
instance_user="custominstanceuser")
self.assertNotIn('config_instance_user', data)
self.assertIn("custominstanceuser", data)
self.m.VerifyAll() self.m.VerifyAll()

View File

@ -1858,7 +1858,100 @@ class ServersTest(HeatTestCase):
self.m.ReplayAll() self.m.ReplayAll()
self.assertEqual(server._resolve_attribute("accessIPv4"), '') self.assertEqual(server._resolve_attribute("accessIPv4"), '')
self.m.VerifyAll()
def test_default_instance_user(self):
"""The default value for instance_user in heat.conf is ec2-user."""
return_server = self.fc.servers.list()[1]
server = self._setup_test_server(return_server, 'default_user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
'wordpress',
instance_user='ec2-user',
user_data_format='HEAT_CFNTOOLS')
self.m.ReplayAll()
scheduler.TaskRunner(server.create)()
self.m.VerifyAll()
def test_admin_user_property(self):
"""Test the admin_user property on the server overrides instance_user.
Launching the instance should call build_userdata with the
custom user name. This property is deprecated and will be
removed in Juno.
"""
return_server = self.fc.servers.list()[1]
stack_name = 'stack_with_custom_admin_user_server'
(t, stack) = self._setup_test_stack(stack_name)
t['Resources']['WebServer']['Properties']['admin_user'] = 'custom_user'
server = servers.Server('create_metadata_test_server',
t['Resources']['WebServer'], stack)
server.t = server.stack.resolve_runtime_data(server.t)
self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create(
image=mox.IgnoreArg(), flavor=mox.IgnoreArg(), key_name='test',
name=mox.IgnoreArg(), security_groups=[],
userdata=mox.IgnoreArg(), scheduler_hints=None,
meta=mox.IgnoreArg(), nics=None, availability_zone=None,
block_device_mapping=None, config_drive=None,
disk_config=None, reservation_id=None, files={},
admin_pass=None).AndReturn(return_server)
self.m.StubOutWithMock(server, 'nova')
server.nova().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
'wordpress',
instance_user='custom_user',
user_data_format='HEAT_CFNTOOLS')
self.m.ReplayAll()
scheduler.TaskRunner(server.create)()
self.m.VerifyAll()
def test_custom_instance_user(self):
"""Test instance_user in heat.conf being set to a custom value.
Launching the instance should call build_userdata with the
custom user name.
This option is deprecated and will be removed in Juno.
"""
return_server = self.fc.servers.list()[1]
server = self._setup_test_server(return_server, 'custom_user')
self.m.StubOutWithMock(servers.cfg.CONF, 'instance_user')
servers.cfg.CONF.instance_user = 'custom_user'
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
'wordpress',
instance_user='custom_user',
user_data_format='HEAT_CFNTOOLS')
self.m.ReplayAll()
scheduler.TaskRunner(server.create)()
self.m.VerifyAll()
def test_empty_instance_user(self):
"""Test instance_user in heat.conf being empty.
Launching the instance should not pass any user to
build_userdata. The default cloud-init user set up for the image
will be used instead.
This will the default behaviour in Juno once we remove the
instance_user option.
"""
return_server = self.fc.servers.list()[1]
server = self._setup_test_server(return_server, 'custom_user')
self.m.StubOutWithMock(servers.cfg.CONF, 'instance_user')
servers.cfg.CONF.instance_user = ''
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
'wordpress',
instance_user=None,
user_data_format='HEAT_CFNTOOLS')
self.m.ReplayAll()
scheduler.TaskRunner(server.create)()
self.m.VerifyAll() self.m.VerifyAll()

View File

@ -151,11 +151,13 @@ class ServerTagsTest(HeatTestCase):
# need to resolve the template functions # need to resolve the template functions
server_userdata = nova_utils.build_userdata( server_userdata = nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']) instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata') self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata( nova_utils.build_userdata(
instance, instance,
instance.t['Properties']['UserData']).AndReturn(server_userdata) instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
self.m.StubOutWithMock(self.fc.servers, 'create') self.m.StubOutWithMock(self.fc.servers, 'create')
self.fc.servers.create( self.fc.servers.create(