diff --git a/lib/astute/pre_deployment_actions/upload_facts.rb b/lib/astute/pre_deployment_actions/upload_facts.rb index dd9c3cc1..622a500e 100644 --- a/lib/astute/pre_deployment_actions/upload_facts.rb +++ b/lib/astute/pre_deployment_actions/upload_facts.rb @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +require 'psych' + module Astute class UploadFacts < PreDeploymentAction @@ -22,16 +24,32 @@ module Astute private + # This is simple version of 'YAML::dump' with force quoting of strings started with prefixed numeral values + def safe_yaml_dump(obj) + visitor = Psych::Visitors::YAMLTree.new({}) + visitor << obj + visitor.tree.grep(Psych::Nodes::Scalar).each do |node| + node.style = Psych::Nodes::Scalar::DOUBLE_QUOTED if + node.value =~ /^0[xbod0]+/i && node.plain && node.quoted + end + visitor.tree.yaml + end + def upload_facts(context, node) + + # TODO: Should be changed to the default 'to_yaml' method only after upgrading + # to Ruby 2.1 everywhere on client nodes which used this YAML. + yaml_data = safe_yaml_dump(node) + Astute.logger.info "#{context.task_id}: storing metadata for node uid=#{node['uid']} "\ "role=#{node['role']}" - Astute.logger.debug "#{context.task_id}: stores metadata: #{node.to_yaml}" + Astute.logger.debug "#{context.task_id}: stores metadata: #{yaml_data}" # This is synchronious RPC call, so we are sure that data were sent and processed remotely upload_mclient = Astute::MClient.new(context, "uploadfile", [node['uid']]) upload_mclient.upload( :path => "/etc/#{node['role']}.yaml", - :content => node.to_yaml, + :content => yaml_data, :overwrite => true, :parents => true, :permissions => '0600' diff --git a/spec/unit/pre_deployment_actions/upload_facts_hook_spec.rb b/spec/unit/pre_deployment_actions/upload_facts_hook_spec.rb index 20b482ab..164e360f 100644 --- a/spec/unit/pre_deployment_actions/upload_facts_hook_spec.rb +++ b/spec/unit/pre_deployment_actions/upload_facts_hook_spec.rb @@ -31,7 +31,16 @@ describe Astute::UploadFacts do 'openstack_version_prev' => 'old_version', 'cobbler' => { 'profile' => 'centos-x86_64' - } + }, + 'password_1' => '0xABC123', + 'password_2' => '0XABC123', + 'password_3' => '0b101010', + 'password_4' => '0B101010', + 'password_5' => '0o123456', + 'password_6' => '0O123456', + 'password_7' => '0d123456', + 'password_8' => '0D123456', + 'mac_address' => '00:12:34:ab:cd:ef' } ] } @@ -49,7 +58,24 @@ describe Astute::UploadFacts do it 'should upload facts using YAML format to nodes in .yaml file' do mclient.expects(:upload).with( :path =>'/etc/controller.yaml', - :content => deploy_data.first.to_yaml, + :content => upload_facts.send(:safe_yaml_dump, deploy_data.first), + :overwrite => true, + :parents => true, + :permissions => '0600' + ) + + upload_facts.process(deploy_data, ctx) + end + + it 'should upload valid YAML format to nodes in .yaml file' do + valid_yaml_data = "---\nuid: '1'\nrole: controller\nopenstack_version_prev: old_version\ncobbler:\n profile: centos-x86_64\n"\ + "password_1: \"0xABC123\"\npassword_2: \"0XABC123\"\npassword_3: \"0b101010\"\npassword_4: \"0B101010\"\n"\ + "password_5: \"0o123456\"\npassword_6: \"0O123456\"\npassword_7: \"0d123456\"\npassword_8: \"0D123456\"\n"\ + "mac_address: \"00:12:34:ab:cd:ef\"\n" + + mclient.expects(:upload).with( + :path =>'/etc/controller.yaml', + :content => valid_yaml_data, :overwrite => true, :parents => true, :permissions => '0600'