From 74d2df9d95eeb2999995fe10d0fb6ac507a79d92 Mon Sep 17 00:00:00 2001 From: Ian Main Date: Mon, 18 Feb 2013 11:20:20 -0800 Subject: [PATCH] Allow heat to be used without a KeyName set. This patch lets you start an instance without setting the KeyName, which I gather is allowed in ec2 as well. This revision adds a template and test. Fixes: bug #1129394 Change-Id: I667f373b73479caa506ee49d8d1615ff4aad47ed Signed-off-by: Ian Main --- heat/engine/resources/instance.py | 7 +- heat/tests/test_nokey.py | 79 ++++++++++++++++ templates/WordPress_NoKey.template | 143 +++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 heat/tests/test_nokey.py create mode 100644 templates/WordPress_NoKey.template diff --git a/heat/engine/resources/instance.py b/heat/engine/resources/instance.py index 1b626bcb..6f555f01 100644 --- a/heat/engine/resources/instance.py +++ b/heat/engine/resources/instance.py @@ -70,8 +70,7 @@ class Instance(resource.Resource): 'Required': True}, 'InstanceType': {'Type': 'String', 'Required': True}, - 'KeyName': {'Type': 'String', - 'Required': True}, + 'KeyName': {'Type': 'String'}, 'AvailabilityZone': {'Type': 'String'}, 'DisableApiTermination': {'Type': 'String', 'Implemented': False}, @@ -227,7 +226,7 @@ class Instance(resource.Resource): availability_zone = self.properties['AvailabilityZone'] keypairs = [k.name for k in self.nova().keypairs.list()] - if key_name not in keypairs: + if key_name not in keypairs and key_name is not None: raise exception.UserKeyPairMissing(key_name=key_name) image_name = self.properties['ImageId'] @@ -344,6 +343,8 @@ class Instance(resource.Resource): # check validity of key try: key_name = self.properties['KeyName'] + if key_name is None: + return except ValueError: return else: diff --git a/heat/tests/test_nokey.py b/heat/tests/test_nokey.py new file mode 100644 index 00000000..848b073a --- /dev/null +++ b/heat/tests/test_nokey.py @@ -0,0 +1,79 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import os +import copy + +import unittest +import mox + +from nose.plugins.attrib import attr + +from heat.tests.v1_1 import fakes +from heat.engine.resources import instance as instances +from heat.common import template_format +from heat.engine import parser +from heat.openstack.common import uuidutils + + +@attr(tag=['unit', 'resource', 'instance']) +@attr(speed='fast') +class nokeyTest(unittest.TestCase): + def setUp(self): + self.m = mox.Mox() + self.fc = fakes.FakeClient() + self.path = os.path.dirname(os.path.realpath(__file__)).\ + replace('heat/tests', 'templates') + + def tearDown(self): + self.m.UnsetStubs() + print "nokeyTest teardown complete" + + def test_nokey_create(self): + f = open("%s/WordPress_NoKey.template" % self.path) + t = template_format.parse(f.read()) + f.close() + + stack_name = 'instance_create_test_nokey_stack' + template = parser.Template(t) + params = parser.Parameters(stack_name, template, {}) + stack = parser.Stack(None, stack_name, template, params, + stack_id=uuidutils.generate_uuid()) + + t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2' + t['Resources']['WebServer']['Properties']['InstanceType'] = \ + '256 MB Server' + instance = instances.Instance('create_instance_name', + t['Resources']['WebServer'], stack) + + self.m.StubOutWithMock(instance, 'nova') + instance.nova().MultipleTimes().AndReturn(self.fc) + + instance.t = instance.stack.resolve_runtime_data(instance.t) + + # need to resolve the template functions + server_userdata = instance._build_userdata( + instance.t['Properties']['UserData']) + self.m.StubOutWithMock(self.fc.servers, 'create') + self.fc.servers.create( + image=1, flavor=1, key_name=None, + name='%s.%s' % (stack_name, instance.name), + security_groups=None, + userdata=server_userdata, scheduler_hints=None, + meta=None, nics=None, availability_zone=None).AndReturn( + self.fc.servers.list()[1]) + self.m.ReplayAll() + + self.assertEqual(instance.create(), None) diff --git a/templates/WordPress_NoKey.template b/templates/WordPress_NoKey.template new file mode 100644 index 00000000..f739eeb6 --- /dev/null +++ b/templates/WordPress_NoKey.template @@ -0,0 +1,143 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + + "Description" : "AWS CloudFormation Sample Template WordPress_Single_Instance: WordPress is web software you can use to create a beautiful website or blog. This template installs a single-instance WordPress deployment using a local MySQL database to store the data.", + + "Parameters" : { + + "InstanceType" : { + "Description" : "WebServer EC2 instance type", + "Type" : "String", + "Default" : "m1.large", + "AllowedValues" : [ "t1.micro", "m1.small", "m1.large", "m1.xlarge", "m2.xlarge", "m2.2xlarge", "m2.4xlarge", "c1.medium", "c1.xlarge", "cc1.4xlarge" ], + "ConstraintDescription" : "must be a valid EC2 instance type." + }, + + "DBName": { + "Default": "wordpress", + "Description" : "The WordPress database name", + "Type": "String", + "MinLength": "1", + "MaxLength": "64", + "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", + "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." + }, + + "DBUsername": { + "Default": "admin", + "NoEcho": "true", + "Description" : "The WordPress database admin account username", + "Type": "String", + "MinLength": "1", + "MaxLength": "16", + "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", + "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." + }, + + "DBPassword": { + "Default": "admin", + "NoEcho": "true", + "Description" : "The WordPress database admin account password", + "Type": "String", + "MinLength": "1", + "MaxLength": "41", + "AllowedPattern" : "[a-zA-Z0-9]*", + "ConstraintDescription" : "must contain only alphanumeric characters." + }, + + "DBRootPassword": { + "Default": "admin", + "NoEcho": "true", + "Description" : "Root password for MySQL", + "Type": "String", + "MinLength": "1", + "MaxLength": "41", + "AllowedPattern" : "[a-zA-Z0-9]*", + "ConstraintDescription" : "must contain only alphanumeric characters." + }, + "LinuxDistribution": { + "Default": "F17", + "Description" : "Distribution of choice", + "Type": "String", + "AllowedValues" : [ "F16", "F17", "U10", "RHEL-6.1", "RHEL-6.2", "RHEL-6.3" ] + } + }, + + "Mappings" : { + "AWSInstanceType2Arch" : { + "t1.micro" : { "Arch" : "32" }, + "m1.small" : { "Arch" : "32" }, + "m1.large" : { "Arch" : "64" }, + "m1.xlarge" : { "Arch" : "64" }, + "m2.xlarge" : { "Arch" : "64" }, + "m2.2xlarge" : { "Arch" : "64" }, + "m2.4xlarge" : { "Arch" : "64" }, + "c1.medium" : { "Arch" : "32" }, + "c1.xlarge" : { "Arch" : "64" }, + "cc1.4xlarge" : { "Arch" : "64" } + }, + "DistroArch2AMI": { + "F16" : { "32" : "F16-i386-gold", "64" : "F16-x86_64-gold" }, + "F17" : { "32" : "F17-i386-gold", "64" : "F17-x86_64-gold" }, + "U10" : { "32" : "U10-i386-gold", "64" : "U10-x86_64-gold" }, + "RHEL-6.1" : { "32" : "rhel61-i386-gold", "64" : "rhel61-x86_64-gold" }, + "RHEL-6.2" : { "32" : "rhel62-i386-gold", "64" : "rhel62-x86_64-gold" }, + "RHEL-6.3" : { "32" : "rhel63-i386-gold", "64" : "rhel63-x86_64-gold" } + } + }, + + "Resources" : { + + "WebServer": { + "Type": "AWS::EC2::Instance", + "Metadata" : { + "AWS::CloudFormation::Init" : { + "config" : { + "packages" : { + "yum" : { + "httpd" : [], + "mysql" : [], + "mysql-server" : [], + "wordpress" : [] + } + }, + "services" : { + "systemd" : { + "httpd" : { "enabled" : "true", "ensureRunning" : "true" }, + "mysqld" : { "enabled" : "true", "ensureRunning" : "true" } + } + } + } + } + }, + "Properties": { + "ImageId" : { "Fn::FindInMap" : [ "DistroArch2AMI", { "Ref" : "LinuxDistribution" }, + { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }, + "InstanceType" : { "Ref" : "InstanceType" }, + "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [ + "#!/bin/bash -v\n", + "# Setup MySQL root password and create a user\n", + "mysqladmin -u root password '", { "Ref" : "DBRootPassword" }, "'\n", + "cat >> /tmp/mysql-wordpress-config << EOF\n", + "CREATE DATABASE ", { "Ref" : "DBName" }, ";\n", + "GRANT ALL PRIVILEGES ON ", { "Ref" : "DBName" }, ".* TO \"", { "Ref" : "DBUsername" }, "\"@\"localhost\"\n", + "IDENTIFIED BY \"", { "Ref" : "DBPassword" }, "\";\n", + "FLUSH PRIVILEGES;\n", + "EXIT\n", + "EOF\n", + "cat /tmp/mysql-wordpress-config | mysql -u root --password='", { "Ref" : "DBRootPassword" }, "' < /tmp/mysql-wordpress-config\n", + "sed -i \"/Deny from All/d\" /etc/httpd/conf.d/wordpress.conf\n", + "sed --in-place --e s/database_name_here/", { "Ref" : "DBName" }, "/ --e s/username_here/", { "Ref" : "DBUsername" }, "/ --e s/password_here/", { "Ref" : "DBPassword" }, "/ /usr/share/wordpress/wp-config.php\n", + "cp /usr/share/wordpress/wp-config.php /usr/share/wordpress/wp-config.orig\n" + ]]}} + } + } + }, + + "Outputs" : { + "WebsiteURL" : { + "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WebServer", "PublicIp" ]}, "/wordpress"]] }, + "Description" : "URL for Wordpress wiki" + } + } +} -- 2.45.2