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:
parent
0d19394458
commit
b8eefd1de9
|
@ -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)
|
||||||
|
|
|
@ -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/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue