Skip to content

Commit

Permalink
Enhance parameter type handling
Browse files Browse the repository at this point in the history
Better support non-string parameter types including comma-delimited-list,
number, boolean

https://bugzilla.redhat.com/show_bug.cgi?id=1489908
  • Loading branch information
bzwei committed Sep 29, 2017
1 parent 0c1cdc4 commit bce829b
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class ManageIQ::Providers::Openstack::CloudManager::OrchestrationStack < ManageI

def self.raw_create_stack(orchestration_manager, stack_name, template, options = {})
create_options = {:stack_name => stack_name, :template => template.content}.merge(options).except(:tenant_name)
transform_parameters(template, create_options[:parameters]) if create_options[:parameters]
connection_options = {:service => "Orchestration"}.merge(options.slice(:tenant_name))
orchestration_manager.with_provider_connection(connection_options) do |service|
service.stacks.new.save(create_options)["id"]
Expand All @@ -14,6 +15,7 @@ def self.raw_create_stack(orchestration_manager, stack_name, template, options =

def raw_update_stack(template, options)
update_options = {:template => template.content}.merge(options.except(:disable_rollback, :timeout_mins))
self.class.transform_parameters(template, update_options[:parameters]) if update_options[:parameters]
connection_options = {:service => "Orchestration"}
connection_options[:tenant_name] = cloud_tenant.name if cloud_tenant
ext_management_system.with_provider_connection(connection_options) do |service|
Expand Down Expand Up @@ -51,4 +53,17 @@ def raw_status
_log.error "stack=[#{name}], error: #{err}"
raise MiqException::MiqOrchestrationStatusError, err.to_s, err.backtrace
end

def self.transform_parameters(template, deploy_parameters)
# convert multiline text to comma delimited string
template.parameter_groups.each do |group|
group.parameters.each do |para_def|
next unless para_def.data_type == 'comma_delimited_list'
parameter = deploy_parameters[para_def.name]
next if parameter.nil? || !parameter.kind_of?(String)
parameter.chomp!('')
parameter.tr!("\n", ",")
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ def parameters(content_hash = nil)
:name => key,
:label => val.key?('label') ? val['label'] : key.titleize,
:data_type => val['type'],
:default_value => val['default'],
:default_value => parse_default_value(val),
:description => val['description'],
:hidden => val['hidden'] == true,
:constraints => val.key?('constraints') ? parse_constraints(val['constraints']) : nil,
:constraints => ([constraint_from_type(val['type'])] + parse_constraints(val['constraints'])).compact,
:required => true
)
end
Expand Down Expand Up @@ -97,8 +97,41 @@ def validate_format_json
err.message
end

def parse_default_value(parameter)
case parameter['type']
when 'json'
JSON.pretty_generate(parameter['default'] || {'sample(please delete)' => 'JSON format'})
when 'comma_delimited_list'
(parameter['default'] || ['sample(please delete)'] * 2).join("\n")
when 'boolean'
([true, 1] + %w(t T y Y yes Yes YES true True TRUE 1)).include?(parameter['default'])
else
parameter['default']
end
end

def constraint_from_type(parameter_type)
case parameter_type
when 'json'
OrchestrationTemplate::OrchestrationParameterMultiline.new(
:description => 'Parameter in JSON format'
)
when 'comma_delimited_list'
OrchestrationTemplate::OrchestrationParameterMultiline.new(
:description => 'Parameter in list format'
)
when 'boolean'
OrchestrationTemplate::OrchestrationParameterBoolean.new
when 'number'
OrchestrationTemplate::OrchestrationParameterPattern.new(
:pattern => '^[+-]?([1-9]\d*|0)(\.\d+)?$',
:description => 'Numeric parameter'
)
end
end

def parse_constraints(raw_constraints)
raw_constraints.collect do |raw_constraint|
(raw_constraints || []).collect do |raw_constraint|
if raw_constraint.key?('allowed_values')
parse_allowed_values(raw_constraint)
elsif raw_constraint.key?('allowed_pattern')
Expand Down
114 changes: 113 additions & 1 deletion spec/fixtures/orchestration_templates/heat_parameters.json
Original file line number Diff line number Diff line change
@@ -1 +1,113 @@
{"heat_template_version":"2013-05-23","description":"Incomplete sample template for testing parsing of various parameters","parameter_groups":[{"label":"General parameters","description":"General parameters","parameters":["flavor","image_id","cartridges"]},{"parameters":["admin_pass","db_port","metadata"]}],"parameters":{"admin_pass":{"type":"string","description":"Admin password","hidden":true,"constraints":[{"length":{"min":6,"max":8},"description":"Admin password must be between 6 and 8 characters long.\n"},{"allowed_pattern":"[a-zA-Z0-9]+","description":"Password must consist of characters and numbers only"},{"allowed_pattern":"[A-Z]+[a-zA-Z0-9]*","description":"Password must start with an uppercase character"}]},"flavor":{"type":"string","description":"Flavor for the instances to be created","default":"m1.small","constraints":[{"custom_constraint":"nova.flavor","description":"Must be a flavor known to Nova"}]},"cartridges":{"description":"Cartridges to install. \"all\" for all cartridges; \"standard\" for all cartridges except for JBossEWS or JBossEAP\n","type":"string","default":"cron,diy,haproxy,mysql,nodejs,perl,php,postgresql,python,ruby"},"db_port":{"type":"number","label":"Port Number","description":"Database port number","default":50000,"constraints":[{"range":{"min":40000,"max":60000},"description":"Port number must be between 40000 and 60000"}]},"image_id":{"type":"string","description":"ID of the image to use for the instance to be created.","default":"F18-x86_64-cfntools","constraints":[{"allowed_values":["F18-i386-cfntools","F18-x86_64-cfntools"],"description":"Image ID must be either F18-i386-cfntools or F18-x86_64-cfntools."}]},"metadata":{"type":"json"}}}
{
"heat_template_version": "2013-05-23",
"description": "Incomplete sample template for testing parsing of various parameters",
"parameter_groups": [
{
"label": "General parameters",
"description": "General parameters",
"parameters": [
"flavor",
"image_id",
"cartridges"
]
},
{
"parameters": [
"admin_pass",
"db_port",
"metadata",
"skip_failed"
]
}
],
"parameters": {
"admin_pass": {
"type": "string",
"description": "Admin password",
"hidden": true,
"constraints": [
{
"length": {
"min": 6,
"max": 8
},
"description": "Admin password must be between 6 and 8 characters long.\n"
},
{
"allowed_pattern": "[a-zA-Z0-9]+",
"description": "Password must consist of characters and numbers only"
},
{
"allowed_pattern": "[A-Z]+[a-zA-Z0-9]*",
"description": "Password must start with an uppercase character"
}
]
},
"flavor": {
"type": "string",
"description": "Flavor for the instances to be created",
"default": "m1.small",
"constraints": [
{
"custom_constraint": "nova.flavor",
"description": "Must be a flavor known to Nova"
}
]
},
"cartridges": {
"description": "Cartridges to install. \"all\" for all cartridges; \"standard\" for all cartridges except for JBossEWS or JBossEAP\n",
"type": "comma_delimited_list",
"default": [
"cron",
"diy",
"haproxy",
"mysql",
"nodejs",
"perl",
"php",
"postgresql",
"python",
"ruby"
]
},
"db_port": {
"type": "number",
"label": "Port Number",
"description": "Database port number",
"default": 50000,
"constraints": [
{
"range": {
"min": 40000,
"max": 60000
},
"description": "Port number must be between 40000 and 60000"
}
]
},
"image_id": {
"type": "string",
"description": "ID of the image to use for the instance to be created.",
"default": "F18-x86_64-cfntools",
"constraints": [
{
"allowed_values": [
"F18-i386-cfntools",
"F18-x86_64-cfntools"
],
"description": "Image ID must be either F18-i386-cfntools or F18-x86_64-cfntools."
}
]
},
"metadata": {
"type": "json",
"default": {
"ver": "test"
}
},
"skip_failed": {
"type": "boolean",
"default": "t"
}
}
}
49 changes: 33 additions & 16 deletions spec/fixtures/orchestration_templates/heat_parameters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,69 @@ parameter_groups:
- admin_pass
- db_port
- metadata
- skip_failed

parameters:
admin_pass:
type: string
description: Admin password
hidden: true
constraints:
- length: { min: 6, max: 8 }
description: >
Admin password must be between 6 and 8 characters long.
- allowed_pattern: "[a-zA-Z0-9]+"
description: Password must consist of characters and numbers only
- allowed_pattern: "[A-Z]+[a-zA-Z0-9]*"
description: Password must start with an uppercase character
- length: { min: 6, max: 8 }
description: >
Admin password must be between 6 and 8 characters long.
- allowed_pattern: "[a-zA-Z0-9]+"
description: Password must consist of characters and numbers only
- allowed_pattern: "[A-Z]+[a-zA-Z0-9]*"
description: Password must start with an uppercase character

flavor:
type: string
description: Flavor for the instances to be created
default: m1.small
constraints:
- custom_constraint: nova.flavor
description: Must be a flavor known to Nova
- custom_constraint: nova.flavor
description: Must be a flavor known to Nova

cartridges:
description: >
Cartridges to install. "all" for all cartridges; "standard" for all cartridges except for JBossEWS or JBossEAP
type: string
default: "cron,diy,haproxy,mysql,nodejs,perl,php,postgresql,python,ruby"
type: comma_delimited_list
default:
- cron
- diy
- haproxy
- mysql
- nodejs
- perl
- php
- postgresql
- python
- ruby

db_port:
type: number
label: Port Number
description: Database port number
default: 50000
constraints:
- range: { min: 40000, max: 60000 }
description: Port number must be between 40000 and 60000
- range: { min: 40000, max: 60000 }
description: Port number must be between 40000 and 60000

image_id:
type: string
description: ID of the image to use for the instance to be created.
default: F18-x86_64-cfntools
constraints:
- allowed_values: [ F18-i386-cfntools, F18-x86_64-cfntools ]
description:
Image ID must be either F18-i386-cfntools or F18-x86_64-cfntools.
- allowed_values: [ F18-i386-cfntools, F18-x86_64-cfntools ]
description:
Image ID must be either F18-i386-cfntools or F18-x86_64-cfntools.

metadata:
type: json
default:
ver: test

skip_failed:
type: boolean
default: t
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,14 @@
end
end
end

describe '.transform_parameters' do
let(:template) { FactoryGirl.create(:orchestration_template_openstack_in_yaml) }
it 'converts multiline text into one comma delimitered string' do
parameters = {'cartridges' => "test1\ntest2\n\n"}

described_class.transform_parameters(template, parameters)
expect(parameters).to eq('cartridges' => "test1,test2")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def assert_db_group(group)
assert_hidden_length_patterns(group.parameters[0])
assert_min_max_value(group.parameters[1])
assert_json_type(group.parameters[2])
assert_boolean_type(group.parameters[3])
end

def assert_custom_constraint(parameter)
Expand Down Expand Up @@ -75,12 +76,15 @@ def assert_list_string_type(parameter)
:name => "cartridges",
:label => "Cartridges",
:description => "Cartridges to install. \"all\" for all cartridges; \"standard\" for all cartridges except for JBossEWS or JBossEAP\n",
:data_type => "string", # HOT has type comma_delimited_list, but all examples use type string. Why?
:default_value => "cron,diy,haproxy,mysql,nodejs,perl,php,postgresql,python,ruby",
:data_type => "comma_delimited_list",
:default_value => %w(cron diy haproxy mysql nodejs perl php postgresql python ruby).join("\n"),
:hidden => false,
:required => true,
:constraints => [],
)
constraints = parameter.constraints
expect(constraints.size).to eq(1)
expect(constraints[0]).to be_a ::OrchestrationTemplate::OrchestrationParameterMultiline
expect(constraints[0]).to be_kind_of ::OrchestrationTemplate::OrchestrationParameterConstraint
end

def assert_allowed_values(parameter)
Expand Down Expand Up @@ -114,10 +118,11 @@ def assert_min_max_value(parameter)
:required => true
)
constraints = parameter.constraints
expect(constraints.size).to eq(1)
expect(constraints[0]).to be_a ::OrchestrationTemplate::OrchestrationParameterRange
expect(constraints[0]).to be_kind_of ::OrchestrationTemplate::OrchestrationParameterConstraint
expect(constraints[0]).to have_attributes(
expect(constraints.size).to eq(2)
expect(constraints[0]).to be_a ::OrchestrationTemplate::OrchestrationParameterPattern
expect(constraints[1]).to be_a ::OrchestrationTemplate::OrchestrationParameterRange
expect(constraints[1]).to be_kind_of ::OrchestrationTemplate::OrchestrationParameterConstraint
expect(constraints[1]).to have_attributes(
:description => "Port number must be between 40000 and 60000",
:min_value => 40_000,
:max_value => 60_000
Expand Down Expand Up @@ -161,15 +166,34 @@ def assert_hidden_length_patterns(parameter)

def assert_json_type(parameter)
expect(parameter).to have_attributes(
:name => "metadata",
:label => "Metadata",
:name => "metadata",
:label => "Metadata",
:description => nil,
:data_type => "json",
:hidden => false,
:required => true,
)
expect(JSON.parse(parameter.default_value)).to eq('ver' => 'test')
constraints = parameter.constraints
expect(constraints.size).to eq(1)
expect(constraints[0]).to be_a ::OrchestrationTemplate::OrchestrationParameterMultiline
expect(constraints[0]).to be_kind_of ::OrchestrationTemplate::OrchestrationParameterConstraint
end

def assert_boolean_type(parameter)
expect(parameter).to have_attributes(
:name => "skip_failed",
:label => "Skip Failed",
:description => nil,
:data_type => "json",
:default_value => nil,
:data_type => "boolean",
:default_value => true,
:hidden => false,
:required => true,
:constraints => [],
)
constraints = parameter.constraints
expect(constraints.size).to eq(1)
expect(constraints[0]).to be_a ::OrchestrationTemplate::OrchestrationParameterBoolean
expect(constraints[0]).to be_kind_of ::OrchestrationTemplate::OrchestrationParameterConstraint
end

describe '#validate_format' do
Expand Down

0 comments on commit bce829b

Please sign in to comment.