From 0a87770c9cd426abc136de791dad06efc7dbaf89 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Tue, 26 Jan 2016 10:04:53 -0800 Subject: [PATCH 1/6] (PUP-5879) Always load settings as UTF-8 - Previously, the encoding wasn't specified when loading settings files, even though they should be in UTF-8. This is particularly of concern to Windows, where Ruby will normally attempt to load the file using the current local codepage, which may represent UTF-8 characters incorrectly. --- lib/puppet/settings.rb | 2 +- spec/integration/util/settings_spec.rb | 26 ++++++++++++++++++++++++++ spec/unit/settings_spec.rb | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb index 8be4a667dad..f2a35aaf5c4 100644 --- a/lib/puppet/settings.rb +++ b/lib/puppet/settings.rb @@ -1189,7 +1189,7 @@ def value_sets_for(environment, mode) # Read the file in. # @api private def read_file(file) - return Puppet::FileSystem.read(file) + return Puppet::FileSystem.read(file, :encoding => 'utf-8') end # Private method for internal test use only; allows to do a comprehensive clear of all settings between tests. diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index e97fd974566..310b84529df 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -44,6 +44,32 @@ def define_settings(section, settings_hash) expect(Puppet::FileSystem.stat(settings[:maindir]).mode & 007777).to eq(0750) end + it "will properly parse a UTF-8 configuration file" do + rune_utf8 = "\u16A0\u16C7\u16BB" # ᚠᛇᚻ + config = tmpfile("config") + define_settings(:main, + :config => { + :type => :file, + :default => config, + :desc => "a" + }, + :environment => { + :default => 'dingos', + :desc => 'test', + } + ) + + File.open(config, 'w') do |file| + file.puts <<-EOF +[main] +environment=#{rune_utf8} + EOF + end + + settings.initialize_global_settings + expect(settings[:environment]).to eq(rune_utf8) + end + it "reparses configuration if configuration file is touched", :if => !Puppet.features.microsoft_windows? do config = tmpfile("config") define_settings(:main, diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index 2a1ef1777e8..37560030eed 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -737,7 +737,7 @@ def metadata(setting) Puppet::FileSystem.expects(:exist?).with(myfile).returns(true) - Puppet::FileSystem.expects(:read).with(myfile).returns "[main]" + Puppet::FileSystem.expects(:read).with(myfile, :encoding => 'utf-8').returns "[main]" @settings.send(:parse_config_files) end From dc4bf9626aa792c959639deec7a9d82507f34e30 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Tue, 19 Apr 2016 18:45:04 -0700 Subject: [PATCH 2/6] (PUP-5879) PMT should handle UTF-8 ignores - When reading .pmtignore and .gitignore, allow the contents to specify Unicode paths - Note that calls to Puppet::FileSystem.open were changed to File.open given Puppet::FileSystem does not have a way of forcing the encoding when writing files. At a later date, FileSystem.open / exclusive_open will be fixed to address this problem. --- .../module_tool/applications/builder.rb | 4 +-- .../module_tool/applications/builder_spec.rb | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/puppet/module_tool/applications/builder.rb b/lib/puppet/module_tool/applications/builder.rb index edfd2c9efdb..d2e3f3b8d3f 100644 --- a/lib/puppet/module_tool/applications/builder.rb +++ b/lib/puppet/module_tool/applications/builder.rb @@ -65,9 +65,9 @@ def ignored_files gitignore = File.join(@path, '.gitignore') if File.file? pmtignore - @ignored_files = PathSpec.new File.read(pmtignore) + @ignored_files = PathSpec.new Puppet::FileSystem.read(pmtignore, :encoding => 'utf-8') elsif File.file? gitignore - @ignored_files = PathSpec.new File.read(gitignore) + @ignored_files = PathSpec.new Puppet::FileSystem.read(gitignore, :encoding => 'utf-8') else @ignored_files = PathSpec.new end diff --git a/spec/unit/module_tool/applications/builder_spec.rb b/spec/unit/module_tool/applications/builder_spec.rb index 8878ab32c0b..67d808a5e5a 100644 --- a/spec/unit/module_tool/applications/builder_spec.rb +++ b/spec/unit/module_tool/applications/builder_spec.rb @@ -56,37 +56,41 @@ def create_ignored_files Puppet::FileSystem.touch(File.join(path, 'gitdirectory/gitartifact')) Puppet::FileSystem.touch(File.join(path, 'gitdirectory/gitimportantfile')) Puppet::FileSystem.touch(File.join(path, 'gitdirectory/sub/artifact')) + Puppet::FileSystem.touch(File.join(path, "git\u16A0\u16C7\u16BB")) Puppet::FileSystem.touch(File.join(path, 'pmtignored.foo')) Puppet::FileSystem.mkpath(File.join(path, 'pmtdirectory/sub')) Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/pmtimportantfile')) Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/pmtartifact')) Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/sub/artifact')) + Puppet::FileSystem.touch(File.join(path, "pmt\u16A0\u16C7\u16BB")) end def create_pmtignore_file - Puppet::FileSystem.open(File.join(path, '.pmtignore'), 0600, 'w') do |f| + File.open(File.join(path, '.pmtignore'), 'w', 0600, :encoding => 'utf-8') do |f| f << <<-PMTIGNORE pmtignored.* pmtdirectory/sub/** pmtdirectory/pmt* !pmtimportantfile +pmt\u16A0\u16C7\u16BB PMTIGNORE end end def create_gitignore_file - Puppet::FileSystem.open(File.join(path, '.gitignore'), 0600, 'w') do |f| + File.open(File.join(path, '.gitignore'), 'w', 0600, :encoding => 'utf-8') do |f| f << <<-GITIGNORE gitignored.* gitdirectory/sub/** gitdirectory/git* !gitimportantfile +git\u16A0\u16C7\u16BB GITIGNORE end end def create_symlink_gitignore_file - Puppet::FileSystem.open(File.join(path, '.gitignore'), 0600, 'w') do |f| + File.open(File.join(path, '.gitignore'), 'w', 0600, :encoding => 'utf-8') do |f| f << <<-GITIGNORE symlinkfile GITIGNORE @@ -152,6 +156,10 @@ def create_symlink_gitignore_file expect(target_exists?('gitignored.foo')).to eq true end + it "leaves UTF-8 files" do + expect(target_exists?("git\u16A0\u16C7\u16BB")).to eq true + end + it "leaves directories" do expect(target_exists?('gitdirectory')).to eq true end @@ -178,6 +186,10 @@ def create_symlink_gitignore_file expect(target_exists?('gitignored.foo')).to eq false end + it "ignores UTF-8 files" do + expect(target_exists?("git\u16A0\u16C7\u16BB")).to eq false + end + it "ignores directories" do expect(target_exists?('gitdirectory')).to eq true end @@ -204,6 +216,10 @@ def create_symlink_gitignore_file expect(target_exists?('pmtignored.foo')).to eq true end + it "leaves UTF-8 files" do + expect(target_exists?("pmt\u16A0\u16C7\u16BB")).to eq true + end + it "leaves directories" do expect(target_exists?('pmtdirectory')).to eq true end @@ -230,6 +246,10 @@ def create_symlink_gitignore_file expect(target_exists?('pmtignored.foo')).to eq false end + it "ignores UTF-8 files" do + expect(target_exists?("pmt\u16A0\u16C7\u16BB")).to eq false + end + it "ignores directories" do expect(target_exists?('pmtdirectory')).to eq true end From 5f5343b2effef14dfffb8484f35454ec1a0ee2ab Mon Sep 17 00:00:00 2001 From: Iristyle Date: Tue, 19 Apr 2016 19:31:31 -0700 Subject: [PATCH 3/6] (PUP-5879) AuthConfigParser reads files as UTF-8 - Ensure that authorization config files may contain UTF-8. This is unlikely to change behavior within Puppet as Windows cannot host routes, but added for UTF-8 unification. --- lib/puppet/network/auth_config_parser.rb | 2 +- spec/unit/network/auth_config_parser_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/puppet/network/auth_config_parser.rb b/lib/puppet/network/auth_config_parser.rb index 01a17ae1107..eb3781cf572 100644 --- a/lib/puppet/network/auth_config_parser.rb +++ b/lib/puppet/network/auth_config_parser.rb @@ -4,7 +4,7 @@ module Puppet::Network class AuthConfigParser def self.new_from_file(file) - self.new(File.read(file)) + self.new(Puppet::FileSystem.read(file, :encoding => 'utf-8')) end def initialize(string) diff --git a/spec/unit/network/auth_config_parser_spec.rb b/spec/unit/network/auth_config_parser_spec.rb index 2830ac98681..ceb07b20bd8 100644 --- a/spec/unit/network/auth_config_parser_spec.rb +++ b/spec/unit/network/auth_config_parser_spec.rb @@ -4,6 +4,7 @@ require 'puppet/network/authconfig' describe Puppet::Network::AuthConfigParser do + include PuppetSpec::Files let(:fake_authconfig) do "path ~ ^/catalog/([^/])\nmethod find\nallow *\n" @@ -98,4 +99,19 @@ described_class.new("path /certificates\nauthenticated yes").parse_rights end end + + describe "when parsing rights from files" do + it "can read UTF-8" do + rune_path = "/\u16A0\u16C7\u16BB" # ᚠᛇᚻ + config = tmpfile('config') + + File.open(config, 'w', :encoding => 'utf-8') do |file| + file.puts <<-EOF +path #{rune_path} + EOF + end + + expect(described_class.new_from_file(config).parse_rights[rune_path]).to be + end + end end From f49db6ab3047c4df23382b3407e892c611d23001 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Tue, 19 Apr 2016 18:38:53 -0700 Subject: [PATCH 4/6] (PUP-5879) Always read metadata.json as UTF-8 - Always specify that module manifest.json files should be loaded with a UTF-8 encoding. - The desire in this commit was to use the Puppet::FileSystem abstraction, rather than the underlying Ruby classes, but this proved difficult to manage due to how mocking is being used in loaders_spec.rb (of particular importance there is how ModuleLoaders::FileBased#get_contents is called). --- lib/puppet/module.rb | 6 ++--- spec/unit/module_spec.rb | 36 +++++++++++++++++++++----- spec/unit/pops/loaders/loaders_spec.rb | 16 ++++++------ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 268b435cf02..2c656b7adc0 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -48,7 +48,7 @@ def self.is_module_directory_name?(name) def self.is_module_namespaced_name?(name) # it must match the full module name according to forge validator - return true if name =~ /^[a-zA-Z0-9]+[-][a-z][a-z0-9_]*$/ + return true if name =~ /^[a-zA-Z0-9]+[-][a-z][a-z0-9_]*$/ return false end @@ -90,7 +90,7 @@ def has_metadata? return false unless Puppet::FileSystem.exist?(metadata_file) begin - metadata = JSON.parse(File.read(metadata_file)) + metadata = JSON.parse(File.read(metadata_file, :encoding => 'utf-8')) rescue JSON::JSONError => e Puppet.debug("#{name} has an invalid and unparsable metadata.json file. The parse error: #{e.message}") return false @@ -145,7 +145,7 @@ def license_file end def load_metadata - @metadata = data = JSON.parse(File.read(metadata_file)) + @metadata = data = JSON.parse(File.read(metadata_file, :encoding => 'utf-8')) @forge_name = data['name'].gsub('-', '/') if data['name'] [:source, :author, :version, :license, :dependencies].each do |attr| diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 6b1871f41a4..c992e24825c 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -609,21 +609,21 @@ it "should have metadata if it has a metadata file and its data is not empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true - File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" + File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns "{\"foo\" : \"bar\"}" expect(@module).to be_has_metadata end it "should have metadata if it has a metadata file and its data is not empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true - File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" + File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns "{\"foo\" : \"bar\"}" expect(@module).to be_has_metadata end it "should not have metadata if has a metadata file and its data is empty" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true - File.stubs(:read).with(@module.metadata_file).returns "This is some invalid json.\n" + File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns "This is some invalid json.\n" expect(@module).not_to be_has_metadata end @@ -646,7 +646,7 @@ it "should tolerate failure to parse" do Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true - File.stubs(:read).with(@module.metadata_file).returns(my_fixture('trailing-comma.json')) + File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json')) expect(@module.has_metadata?).to be_falsey end @@ -656,7 +656,7 @@ def a_module_with_metadata(data) mod = Puppet::Module.new("foo", "/path", mock("env")) mod.stubs(:metadata_file).returns "/my/file" - File.stubs(:read).with("/my/file").returns text + File.stubs(:read).with("/my/file", {:encoding => 'utf-8'}).returns text mod end @@ -681,7 +681,7 @@ def a_module_with_metadata(data) it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_pson - File.stubs(:read).with("/my/file").returns @text + File.stubs(:read).with("/my/file", {:encoding => 'utf-8'}).returns @text expect { @module.load_metadata }.to raise_error( Puppet::Module::MissingMetadata, "No #{attr} module metadata provided for foo" @@ -690,6 +690,30 @@ def a_module_with_metadata(data) end end + describe "when loading the metadata file from disk" do + it "should properly parse utf-8 contents" do + rune_utf8 = "\u16A0\u16C7\u16BB" # ᚠᛇᚻ + metadata_json = tmpfile('metadata.json') + File.open(metadata_json, 'w') do |file| + file.puts <<-EOF + { + "license" : "GPL2", + "author" : "#{rune_utf8}", + "version" : "1.0", + "source" : "http://foo/", + "dependencies" : [] + } + EOF + end + + mod = Puppet::Module.new('foo', '/path', mock('env')) + mod.stubs(:metadata_file).returns metadata_json + + mod.load_metadata + expect(mod.author).to eq(rune_utf8) + end + end + it "should be able to tell if there are local changes" do modpath = tmpdir('modpath') foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8' diff --git a/spec/unit/pops/loaders/loaders_spec.rb b/spec/unit/pops/loaders/loaders_spec.rb index 5440e189512..47136c52ef4 100644 --- a/spec/unit/pops/loaders/loaders_spec.rb +++ b/spec/unit/pops/loaders/loaders_spec.rb @@ -107,7 +107,7 @@ end it 'loader allows loading a function more than once' do - File.stubs(:read).with(user_metadata_path).returns '' + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns '' env = environment_for(File.join(dependent_modules_with_metadata, 'modules')) loaders = Puppet::Pops::Loaders.new(env) @@ -166,7 +166,7 @@ def compile_and_get_notifications(code) end it 'all dependent modules are visible' do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'}, { 'name' => 'test-usee2'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'}, { 'name' => 'test-usee2'} ]).to_pson loaders = Puppet::Pops::Loaders.new(env) moduleb_loader = loaders.private_loader_for_module('user') @@ -183,7 +183,7 @@ def compile_and_get_notifications(code) {:from => from, :called => 'a ruby function', :expects => "I'm the function usee::usee_ruby()"} ].each_with_index do |desc, called_idx| case_number = from_idx * 3 + called_idx + 1 it "can call #{desc[:called]} from #{desc[:from]} when dependency is present in metadata.json" do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson Puppet[:code] = "$case_number = #{case_number}\ninclude ::user" catalog = compiler.compile resource = catalog.resource('Notify', "case_#{case_number}") @@ -201,7 +201,7 @@ def compile_and_get_notifications(code) end it 'can not call ruby function in a dependent module from outside a function if dependency is missing in existing metadata.json' do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => []).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => []).to_pson Puppet[:code] = "$case_number = #{case_number}\ninclude ::user" expect { catalog = compiler.compile }.to raise_error(Puppet::Error, /Unknown function/) end @@ -209,7 +209,7 @@ def compile_and_get_notifications(code) end it "a type can reference an autoloaded type alias from another module when dependency is present in metadata.json" do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok']) assert_type(Usee::Zero, 0) notice(ok) @@ -225,7 +225,7 @@ def compile_and_get_notifications(code) end it "a type can reference a type alias from another module when other module has it declared in init.pp" do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok']) include 'usee' assert_type(Usee::One, 1) @@ -234,7 +234,7 @@ def compile_and_get_notifications(code) end it "an autoloaded type can reference an autoloaded type alias from another module when dependency is present in metadata.json" do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok']) assert_type(User::WithUseeZero, [0]) notice(ok) @@ -242,7 +242,7 @@ def compile_and_get_notifications(code) end it "an autoloaded type can reference an autoloaded type alias from another module when other module has it declared in init.pp" do - File.stubs(:read).with(user_metadata_path).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson + File.stubs(:read).with(user_metadata_path, {:encoding => 'utf-8'}).returns user_metadata.merge('dependencies' => [ { 'name' => 'test-usee'} ]).to_pson expect(eval_and_collect_notices(<<-CODE, node)).to eq(['ok']) include 'usee' assert_type(User::WithUseeOne, [1]) From faf542823ec4912475886673b6bc5094b7cdd339 Mon Sep 17 00:00:00 2001 From: "Ethan J. Brown" Date: Fri, 29 Jan 2016 14:45:32 -0800 Subject: [PATCH 5/6] (PUP-5879) Use UTF-8 in JSON / Hiera Data Provider - Hiera can use a JSON data provider to load environment data. Previously, this provider did not explicitly specify UTF8 when loading files, which could result in corrupted data on platforms like Windows, where the default encoding is not UTF8. - Add some UTF8 fixture data to use in the hiera_data_provider_spec that includes both JSON and YAML files. --- lib/puppet/data_providers/json_data_provider_factory.rb | 2 +- .../environments/hiera_env_config/data1/third_utf8.json | 3 +++ .../environments/hiera_env_config/data1/utf8.yaml | 3 +++ .../data_providers/environments/hiera_env_config/hiera.yaml | 6 +++++- .../environments/hiera_env_config/manifests/site.pp | 4 ++-- spec/unit/data_providers/hiera_data_provider_spec.rb | 2 +- 6 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/third_utf8.json create mode 100644 spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/utf8.yaml diff --git a/lib/puppet/data_providers/json_data_provider_factory.rb b/lib/puppet/data_providers/json_data_provider_factory.rb index 1f2af71b4d4..f61c063149d 100644 --- a/lib/puppet/data_providers/json_data_provider_factory.rb +++ b/lib/puppet/data_providers/json_data_provider_factory.rb @@ -18,7 +18,7 @@ class JsonDataProvider < Puppet::Plugins::DataProviders::PathBasedDataProvider include HieraInterpolate def initialize_data(path, lookup_invocation) - JSON.parse(File.read(path)) + JSON.parse(Puppet::FileSystem.read(path, :encoding => 'utf-8')) rescue JSON::ParserError => ex # Filename not included in message, so we add it here. raise Puppet::DataBinding::LookupError, "Unable to parse (#{path}): #{ex.message}" diff --git a/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/third_utf8.json b/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/third_utf8.json new file mode 100644 index 00000000000..0c650a718e3 --- /dev/null +++ b/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/third_utf8.json @@ -0,0 +1,3 @@ +{ + "test::param_json_utf8": "env data param_json_utf8 is ᚠᛇᚻ" +} diff --git a/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/utf8.yaml b/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/utf8.yaml new file mode 100644 index 00000000000..06771320f9b --- /dev/null +++ b/spec/fixtures/unit/data_providers/environments/hiera_env_config/data1/utf8.yaml @@ -0,0 +1,3 @@ +--- +test::param_yaml_utf8: env data param_yaml_utf8 is ᛫ᛒᛦ + diff --git a/spec/fixtures/unit/data_providers/environments/hiera_env_config/hiera.yaml b/spec/fixtures/unit/data_providers/environments/hiera_env_config/hiera.yaml index 6d4b7edde86..902fbec8e92 100644 --- a/spec/fixtures/unit/data_providers/environments/hiera_env_config/hiera.yaml +++ b/spec/fixtures/unit/data_providers/environments/hiera_env_config/hiera.yaml @@ -6,11 +6,15 @@ - :name: "one path" :backend: yaml :path: "single" - - :name: "two paths" + - :name: "utf8" + :backend: yaml + :path: "utf8" + - :name: "three paths" :backend: json :paths: - "first" - "second" + - "third_utf8" - :name: 'other datadir' :backend: yaml :datadir: "data2" diff --git a/spec/fixtures/unit/data_providers/environments/hiera_env_config/manifests/site.pp b/spec/fixtures/unit/data_providers/environments/hiera_env_config/manifests/site.pp index feece8c3fdf..1706f3a0260 100644 --- a/spec/fixtures/unit/data_providers/environments/hiera_env_config/manifests/site.pp +++ b/spec/fixtures/unit/data_providers/environments/hiera_env_config/manifests/site.pp @@ -1,5 +1,5 @@ -class test($param_a = 1, $param_b = 2, $param_c = 3, $param_d = 4, $param_e = 5) { - notify { "$param_a, $param_b, $param_c, $param_d, $param_e": } +class test($param_a = 1, $param_b = 2, $param_c = 3, $param_d = 4, $param_e = 5, $param_yaml_utf8 = 'hi', $param_json_utf8 = 'hi') { + notify { "$param_a, $param_b, $param_c, $param_d, $param_e, $param_yaml_utf8, $param_json_utf8": } } include test diff --git a/spec/unit/data_providers/hiera_data_provider_spec.rb b/spec/unit/data_providers/hiera_data_provider_spec.rb index 6f579befd51..d59e981db5e 100644 --- a/spec/unit/data_providers/hiera_data_provider_spec.rb +++ b/spec/unit/data_providers/hiera_data_provider_spec.rb @@ -45,7 +45,7 @@ def compile(environment, code = nil) it 'reads hiera.yaml in environment root and configures multiple json and yaml providers' do resources = compile_and_get_notifications('hiera_env_config') - expect(resources).to include('env data param_a is 10, env data param_b is 20, env data param_c is 30, env data param_d is 40, env data param_e is 50') + expect(resources).to include("env data param_a is 10, env data param_b is 20, env data param_c is 30, env data param_d is 40, env data param_e is 50, env data param_yaml_utf8 is \u16EB\u16D2\u16E6, env data param_json_utf8 is \u16A0\u16C7\u16BB") end it 'reads hiera.yaml in module root and configures multiple json and yaml providers' do From 44ac448769378d334911f1bcb5b11a6ddf3d3dad Mon Sep 17 00:00:00 2001 From: Iristyle Date: Tue, 19 Apr 2016 19:15:52 -0700 Subject: [PATCH 6/6] (PUP-5879) MsgPack reads files as UTF-8 - With a previously unspecified encoding, this could read msgpack files incorrectly on platforms like Windows where the default encoding is not UTF-8. - Add a test that serializes / deserializes a UTF-8 string through msgpack --- lib/puppet/indirector/msgpack.rb | 2 +- spec/unit/indirector/msgpack_spec.rb | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/puppet/indirector/msgpack.rb b/lib/puppet/indirector/msgpack.rb index d90d830e116..19751669607 100644 --- a/lib/puppet/indirector/msgpack.rb +++ b/lib/puppet/indirector/msgpack.rb @@ -58,7 +58,7 @@ def load_msgpack_from_file(file, key) msgpack = nil begin - msgpack = File.read(file) + msgpack = Puppet::FileSystem.read(file, :encoding => 'utf-8') rescue Errno::ENOENT return nil rescue => detail diff --git a/spec/unit/indirector/msgpack_spec.rb b/spec/unit/indirector/msgpack_spec.rb index cb289e0846e..a694f8e40cf 100755 --- a/spec/unit/indirector/msgpack_spec.rb +++ b/spec/unit/indirector/msgpack_spec.rb @@ -76,12 +76,22 @@ def with_content(text) expect(subject.find(request)).to be_nil end + it "can load a UTF-8 file from disk" do + rune_utf8 = "\u16A0\u16C7\u16BB" # ᚠᛇᚻ + + with_content(model.new(rune_utf8).to_msgpack) do + instance = subject.find(request) + expect(instance).to be_an_instance_of model + expect(instance.value).to eq(rune_utf8) + end + end + it "raises a descriptive error when the file can't be read" do with_content(model.new('foo').to_msgpack) do # I don't like this, but there isn't a credible alternative that # also works on Windows, so a stub it is. At least the expectation # will fail if the implementation changes. Sorry to the next dev. - File.expects(:read).with(file).raises(Errno::EPERM) + Puppet::FileSystem.expects(:read).with(file, {:encoding => 'utf-8'}).raises(Errno::EPERM) expect { subject.find(request) }. to raise_error Puppet::Error, /Could not read MessagePack/ end