From 04aa1215a1cbc5b38d9529f8984258088d2c0861 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Tue, 7 May 2013 15:59:49 +0100 Subject: [PATCH] tests : convert test_template_format to test-local templates test_template_format is different from all other tests, in that we don't want to use the most minimal template possible, we actually want to test conversion works with maximal/non-trivial templates, so copy the two templates used by this test into a local tests/templates directory, with a README caution that in general inline-minimal templates are preferred. Change-Id: Ib5421545103728385992625884f86f2667ee2465 --- heat/tests/templates/Quantum.template | 100 +++++++++++ heat/tests/templates/Quantum.yaml | 72 ++++++++ heat/tests/templates/README | 6 + .../WordPress_Single_Instance.template | 149 ++++++++++++++++ .../templates/WordPress_Single_Instance.yaml | 161 ++++++++++++++++++ heat/tests/test_template_format.py | 36 ++-- 6 files changed, 499 insertions(+), 25 deletions(-) create mode 100644 heat/tests/templates/Quantum.template create mode 100644 heat/tests/templates/Quantum.yaml create mode 100644 heat/tests/templates/README create mode 100644 heat/tests/templates/WordPress_Single_Instance.template create mode 100644 heat/tests/templates/WordPress_Single_Instance.yaml diff --git a/heat/tests/templates/Quantum.template b/heat/tests/templates/Quantum.template new file mode 100644 index 0000000000..1fd3298540 --- /dev/null +++ b/heat/tests/templates/Quantum.template @@ -0,0 +1,100 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + + "Description" : "Template to test Quantum resources", + + "Parameters" : { + + }, + + "Resources" : { + "network": { + "Type": "OS::Quantum::Net", + "Properties": { + "name": "the_network" + } + }, + "unnamed_network": { + "Type": "OS::Quantum::Net" + }, + "admin_down_network": { + "Type": "OS::Quantum::Net", + "Properties": { + "admin_state_up": false + } + }, + + "subnet": { + "Type": "OS::Quantum::Subnet", + "Properties": { + "network_id": { "Ref" : "network" }, + "ip_version": 4, + "cidr": "10.0.3.0/24", + "allocation_pools": [{"start": "10.0.3.20", "end": "10.0.3.150"}] + } + }, + + "port": { + "Type": "OS::Quantum::Port", + "Properties": { + "device_id": "d6b4d3a5-c700-476f-b609-1493dd9dadc0", + "name": "port1", + "network_id": { "Ref" : "network" }, + "fixed_ips": [{ + "subnet_id": { "Ref" : "subnet" }, + "ip_address": "10.0.3.21" + }] + } + }, + + "router": { + "Type": "OS::Quantum::Router" + }, + + "router_interface": { + "Type": "OS::Quantum::RouterInterface", + "Properties": { + "router_id": { "Ref" : "router" }, + "subnet_id": { "Ref" : "subnet" } + } + } + }, + "Outputs" : { + "the_network_status" : { + "Value" : { "Fn::GetAtt" : [ "network", "status" ]}, + "Description" : "Status of network" + }, + "port_device_owner" : { + "Value" : { "Fn::GetAtt" : [ "port", "device_owner" ]}, + "Description" : "Device owner of the port" + }, + "port_fixed_ips" : { + "Value" : { "Fn::GetAtt" : [ "port", "fixed_ips" ]}, + "Description" : "Fixed IPs of the port" + }, + "port_mac_address" : { + "Value" : { "Fn::GetAtt" : [ "port", "mac_address" ]}, + "Description" : "MAC address of the port" + }, + "port_status" : { + "Value" : { "Fn::GetAtt" : [ "port", "status" ]}, + "Description" : "Status of the port" + }, + "port_show" : { + "Value" : { "Fn::GetAtt" : [ "port", "show" ]}, + "Description" : "All attributes for port" + }, + "subnet_show" : { + "Value" : { "Fn::GetAtt" : [ "subnet", "show" ]}, + "Description" : "All attributes for subnet" + }, + "network_show" : { + "Value" : { "Fn::GetAtt" : [ "network", "show" ]}, + "Description" : "All attributes for network" + }, + "router_show" : { + "Value" : { "Fn::GetAtt" : [ "router", "show" ]}, + "Description" : "All attributes for router" + } + } +} \ No newline at end of file diff --git a/heat/tests/templates/Quantum.yaml b/heat/tests/templates/Quantum.yaml new file mode 100644 index 0000000000..3015a4f53d --- /dev/null +++ b/heat/tests/templates/Quantum.yaml @@ -0,0 +1,72 @@ +HeatTemplateFormatVersion: '2012-12-12' +Description: Template to test Quantum resources +Resources: + network: + Type: OS::Quantum::Net + Properties: {name: the_network} + unnamed_network: + Type: 'OS::Quantum::Net' + admin_down_network: + Type: OS::Quantum::Net + Properties: {admin_state_up: false} + subnet: + Type: OS::Quantum::Subnet + Properties: + network_id: {Ref: network} + ip_version: 4 + cidr: 10.0.3.0/24 + allocation_pools: + - {end: 10.0.3.150, start: 10.0.3.20} + port: + Type: OS::Quantum::Port + Properties: + device_id: d6b4d3a5-c700-476f-b609-1493dd9dadc0 + name: port1 + network_id: {Ref: network} + fixed_ips: + - subnet_id: {Ref: subnet} + ip_address: 10.0.3.21 + router: + Type: 'OS::Quantum::Router' + router_interface: + Type: OS::Quantum::RouterInterface + Properties: + router_id: {Ref: router} + subnet_id: {Ref: subnet} +Outputs: + the_network_status: + Value: + Fn::GetAtt: [network, status] + Description: Status of network + port_device_owner: + Value: + Fn::GetAtt: [port, device_owner] + Description: Device owner of the port + port_fixed_ips: + Value: + Fn::GetAtt: [port, fixed_ips] + Description: Fixed IPs of the port + port_mac_address: + Value: + Fn::GetAtt: [port, mac_address] + Description: MAC address of the port + port_status: + Value: + Fn::GetAtt: [port, status] + Description: Status of the port + port_show: + Value: + Fn::GetAtt: [port, show] + Description: All attributes for port + subnet_show: + Value: + Fn::GetAtt: [subnet, show] + Description: All attributes for subnet + network_show: + Value: + Fn::GetAtt: [network, show] + Description: All attributes for network + router_show: + Value: + Fn::GetAtt: [router, show] + Description: All attributes for router diff --git a/heat/tests/templates/README b/heat/tests/templates/README new file mode 100644 index 0000000000..2c2716a2ca --- /dev/null +++ b/heat/tests/templates/README @@ -0,0 +1,6 @@ +These templates are required by test_template_format, where we don't want to +use a minimal template snippet (we want ideally to test the maximum possible +syntax to prove the format conversion works) + +In general, tests should not depend on these templates, inline minimal +template snippets are preferred. diff --git a/heat/tests/templates/WordPress_Single_Instance.template b/heat/tests/templates/WordPress_Single_Instance.template new file mode 100644 index 0000000000..3d79430261 --- /dev/null +++ b/heat/tests/templates/WordPress_Single_Instance.template @@ -0,0 +1,149 @@ +{ + "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" : { + + "KeyName" : { + "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances", + "Type" : "String" + }, + + "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" : [ "F18", "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": { + "F18" : { "32" : "F18-i386-cfntools", "64" : "F18-x86_64-cfntools" }, + "F17" : { "32" : "F17-i386-cfntools", "64" : "F17-x86_64-cfntools" }, + "U10" : { "32" : "U10-i386-cfntools", "64" : "U10-x86_64-cfntools" }, + "RHEL-6.1" : { "32" : "rhel61-i386-cfntools", "64" : "rhel61-x86_64-cfntools" }, + "RHEL-6.2" : { "32" : "rhel62-i386-cfntools", "64" : "rhel62-x86_64-cfntools" }, + "RHEL-6.3" : { "32" : "rhel63-i386-cfntools", "64" : "rhel63-x86_64-cfntools" } + } + }, + + "Resources" : { + "WikiDatabase": { + "Type": "AWS::EC2::Instance", + "Metadata" : { + "AWS::CloudFormation::Init" : { + "config" : { + "packages" : { + "yum" : { + "mysql" : [], + "mysql-server" : [], + "httpd" : [], + "wordpress" : [] + } + }, + "services" : { + "systemd" : { + "mysqld" : { "enabled" : "true", "ensureRunning" : "true" }, + "httpd" : { "enabled" : "true", "ensureRunning" : "true" } + } + } + } + } + }, + "Properties": { + "ImageId" : { "Fn::FindInMap" : [ "DistroArch2AMI", { "Ref" : "LinuxDistribution" }, + { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }, + "InstanceType" : { "Ref" : "InstanceType" }, + "KeyName" : { "Ref" : "KeyName" }, + "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [ + "#!/bin/bash -v\n", + "/opt/aws/bin/cfn-init\n", + "# Setup MySQL root password and create a user\n", + "mysqladmin -u root password '", { "Ref" : "DBRootPassword" }, "'\n", + "cat << EOF | mysql -u root --password='", { "Ref" : "DBRootPassword" }, "'\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", + "sed -i \"/Deny from All/d\" /etc/httpd/conf.d/wordpress.conf\n", + "sed -i \"s/Require local/Require all granted/\" /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", + "systemctl restart httpd.service\n" + ]]}} + } + } + }, + + "Outputs" : { + "WebsiteURL" : { + "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WikiDatabase", "PublicIp" ]}, "/wordpress"]] }, + "Description" : "URL for Wordpress wiki" + } + } +} diff --git a/heat/tests/templates/WordPress_Single_Instance.yaml b/heat/tests/templates/WordPress_Single_Instance.yaml new file mode 100644 index 0000000000..eed4fcf048 --- /dev/null +++ b/heat/tests/templates/WordPress_Single_Instance.yaml @@ -0,0 +1,161 @@ +HeatTemplateFormatVersion: '2012-12-12' +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: + KeyName: {Description: Name of an existing EC2 KeyPair + to enable SSH access to the instances, Type: String} + 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: [F18, 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: + F18: {'32': F18-i386-cfntools, '64': F18-x86_64-cfntools} + F17: {'32': F17-i386-cfntools, '64': F17-x86_64-cfntools} + U10: {'32': U10-i386-cfntools, '64': U10-x86_64-cfntools} + RHEL-6.1: {'32': rhel61-i386-cfntools, '64': rhel61-x86_64-cfntools} + RHEL-6.2: {'32': rhel62-i386-cfntools, '64': rhel62-x86_64-cfntools} + RHEL-6.3: {'32': rhel63-i386-cfntools, '64': rhel63-x86_64-cfntools} +Resources: + WikiDatabase: + Type: AWS::EC2::Instance + Metadata: + AWS::CloudFormation::Init: + config: + packages: + yum: + mysql: [] + mysql-server: [] + httpd: [] + wordpress: [] + services: + systemd: + mysqld: {enabled: 'true', ensureRunning: 'true'} + httpd: {enabled: 'true', ensureRunning: 'true'} + Properties: + ImageId: + Fn::FindInMap: + - DistroArch2AMI + - {Ref: LinuxDistribution} + - Fn::FindInMap: + - AWSInstanceType2Arch + - {Ref: InstanceType} + - Arch + InstanceType: {Ref: InstanceType} + KeyName: {Ref: KeyName} + UserData: + Fn::Base64: + Fn::Join: + - '' + - - '#!/bin/bash -v + + ' + - '/opt/aws/bin/cfn-init + + ' + - '# Setup MySQL root password and create a user + + ' + - mysqladmin -u root password ' + - {Ref: DBRootPassword} + - ''' + + ' + - cat << EOF | mysql -u root --password=' + - {Ref: DBRootPassword} + - ''' + + ' + - 'CREATE DATABASE ' + - {Ref: DBName} + - '; + + ' + - 'GRANT ALL PRIVILEGES ON ' + - {Ref: DBName} + - .* TO " + - {Ref: DBUsername} + - '"@"localhost" + + ' + - IDENTIFIED BY " + - {Ref: DBPassword} + - '"; + + ' + - 'FLUSH PRIVILEGES; + + ' + - 'EXIT + + ' + - 'EOF + + ' + - 'sed -i "/Deny from All/d" /etc/httpd/conf.d/wordpress.conf + + ' + - 'sed -i "s/Require local/Require all granted/" /etc/httpd/conf.d/wordpress.conf + + ' + - 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 + + ' + - 'systemctl restart httpd.service + + ' +Outputs: + WebsiteURL: + Value: + Fn::Join: + - '' + - - http:// + - Fn::GetAtt: [WikiDatabase, PublicIp] + - /wordpress + Description: URL for Wordpress wiki + diff --git a/heat/tests/test_template_format.py b/heat/tests/test_template_format.py index 03ae62e9e3..eddbb1be02 100644 --- a/heat/tests/test_template_format.py +++ b/heat/tests/test_template_format.py @@ -16,24 +16,23 @@ from testtools import skipIf import os from heat.engine import clients -from heat.common import context from heat.common import template_format -from heat.engine import parser from heat.tests.common import HeatTestCase from heat.tests.utils import setup_dummy_db +from heat.tests.utils import parse_stack class JsonToYamlTest(HeatTestCase): def setUp(self): super(JsonToYamlTest, self).setUp() - self.expected_test_count = 10 + self.expected_test_count = 2 self.longMessage = True self.maxDiff = None def test_convert_all_templates(self): - path = os.path.dirname(os.path.realpath(__file__)).\ - replace('heat/tests', 'templates') + path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'templates') template_test_count = 0 for (json_str, @@ -46,8 +45,8 @@ class JsonToYamlTest(HeatTestCase): break self.assertTrue(template_test_count >= self.expected_test_count, - 'Expected at least %d templates to be tested' % - self.expected_test_count) + 'Expected at least %d templates to be tested, not %d' % + (self.expected_test_count, template_test_count)) def compare_json_vs_yaml(self, json_str, yml_str, file_name): yml = template_format.parse(yml_str) @@ -101,26 +100,13 @@ class JsonYamlResolvedCompareTest(HeatTestCase): setup_dummy_db() def load_template(self, file_name): - self.path = os.path.dirname(os.path.realpath(__file__)).\ - replace('heat/tests', 'templates') - f = open("%s/%s" % (self.path, file_name)) + filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'templates', file_name) + f = open(filepath) t = template_format.parse(f.read()) f.close() return t - def parse_stack(self, t, parameters): - ctx = context.RequestContext.from_dict({ - 'tenant': 'test_tenant', - 'username': 'test_username', - 'password': 'password', - 'auth_url': 'http://localhost:5000/v2.0'}) - stack_name = 'test_stack' - template = parser.Template(t) - params = parser.Parameters(stack_name, template, parameters) - stack = parser.Stack(ctx, stack_name, parser.Template(t), params) - - return stack - def compare_stacks(self, json_file, yaml_file, parameters): t1 = self.load_template(json_file) template_format.default_for_missing(t1, 'AWSTemplateFormatVersion', @@ -130,8 +116,8 @@ class JsonYamlResolvedCompareTest(HeatTestCase): t2 = self.load_template(yaml_file) del(t2[u'HeatTemplateFormatVersion']) - stack1 = self.parse_stack(t1, parameters) - stack2 = self.parse_stack(t2, parameters) + stack1 = parse_stack(t1, parameters) + stack2 = parse_stack(t2, parameters) # compare resources separately so that resolved static data # is compared