puppet 4.9.4 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +6 -0
  3. data/ext/project_data.yaml +2 -2
  4. data/lib/hiera/puppet_function.rb +1 -1
  5. data/lib/puppet.rb +1 -0
  6. data/lib/puppet/application.rb +14 -0
  7. data/lib/puppet/application/inspect.rb +3 -0
  8. data/lib/puppet/defaults.rb +12 -2
  9. data/lib/puppet/etc.rb +125 -0
  10. data/lib/puppet/face/help.rb +1 -1
  11. data/lib/puppet/functions.rb +49 -4
  12. data/lib/puppet/functions/eyaml_lookup_key.rb +12 -3
  13. data/lib/puppet/functions/hocon_data.rb +9 -0
  14. data/lib/puppet/functions/json_data.rb +9 -0
  15. data/lib/puppet/functions/yaml_data.rb +9 -0
  16. data/lib/puppet/indirector/file_bucket_file/file.rb +69 -22
  17. data/lib/puppet/indirector/key/file.rb +2 -1
  18. data/lib/puppet/indirector/ssl_file.rb +24 -3
  19. data/lib/puppet/module.rb +28 -22
  20. data/lib/puppet/network/http/compression.rb +2 -1
  21. data/lib/puppet/parser/compiler.rb +15 -38
  22. data/lib/puppet/parser/functions/hiera.rb +1 -1
  23. data/lib/puppet/parser/functions/hiera_array.rb +1 -1
  24. data/lib/puppet/parser/functions/hiera_hash.rb +1 -1
  25. data/lib/puppet/parser/functions/hiera_include.rb +1 -1
  26. data/lib/puppet/parser/scope.rb +59 -17
  27. data/lib/puppet/pops/evaluator/callable_signature.rb +7 -0
  28. data/lib/puppet/pops/functions/dispatch.rb +18 -5
  29. data/lib/puppet/pops/functions/dispatcher.rb +7 -13
  30. data/lib/puppet/pops/issue_reporter.rb +1 -1
  31. data/lib/puppet/pops/issues.rb +84 -0
  32. data/lib/puppet/pops/loader/base_loader.rb +13 -5
  33. data/lib/puppet/pops/lookup/configured_data_provider.rb +8 -2
  34. data/lib/puppet/pops/lookup/data_dig_function_provider.rb +109 -19
  35. data/lib/puppet/pops/lookup/data_hash_function_provider.rb +19 -4
  36. data/lib/puppet/pops/lookup/data_provider.rb +43 -29
  37. data/lib/puppet/pops/lookup/environment_data_provider.rb +1 -1
  38. data/lib/puppet/pops/lookup/explainer.rb +1 -0
  39. data/lib/puppet/pops/lookup/function_provider.rb +36 -11
  40. data/lib/puppet/pops/lookup/global_data_provider.rb +18 -5
  41. data/lib/puppet/pops/lookup/hiera_config.rb +203 -84
  42. data/lib/puppet/pops/lookup/interpolation.rb +21 -6
  43. data/lib/puppet/pops/lookup/invocation.rb +14 -9
  44. data/lib/puppet/pops/lookup/location_resolver.rb +27 -0
  45. data/lib/puppet/pops/lookup/lookup_adapter.rb +59 -6
  46. data/lib/puppet/pops/lookup/lookup_key_function_provider.rb +9 -77
  47. data/lib/puppet/pops/lookup/module_data_provider.rb +27 -4
  48. data/lib/puppet/pops/parser/lexer2.rb +1 -1
  49. data/lib/puppet/pops/pcore.rb +3 -3
  50. data/lib/puppet/pops/types/p_object_type.rb +4 -6
  51. data/lib/puppet/pops/types/ruby_generator.rb +2 -2
  52. data/lib/puppet/pops/types/type_asserter.rb +3 -3
  53. data/lib/puppet/pops/types/type_mismatch_describer.rb +25 -7
  54. data/lib/puppet/pops/types/types.rb +20 -29
  55. data/lib/puppet/provider/exec.rb +4 -2
  56. data/lib/puppet/provider/nameservice.rb +8 -8
  57. data/lib/puppet/provider/selmodule/semodule.rb +20 -16
  58. data/lib/puppet/provider/service/src.rb +39 -39
  59. data/lib/puppet/provider/service/systemd.rb +1 -1
  60. data/lib/puppet/provider/user/aix.rb +7 -2
  61. data/lib/puppet/settings.rb +30 -17
  62. data/lib/puppet/ssl/base.rb +14 -1
  63. data/lib/puppet/ssl/certificate_authority.rb +4 -2
  64. data/lib/puppet/ssl/configuration.rb +4 -1
  65. data/lib/puppet/ssl/inventory.rb +10 -3
  66. data/lib/puppet/ssl/key.rb +7 -3
  67. data/lib/puppet/test/test_helper.rb +3 -0
  68. data/lib/puppet/type.rb +13 -1
  69. data/lib/puppet/type/exec.rb +16 -1
  70. data/lib/puppet/type/group.rb +17 -11
  71. data/lib/puppet/type/user.rb +3 -1
  72. data/lib/puppet/util.rb +1 -0
  73. data/lib/puppet/util/character_encoding.rb +95 -0
  74. data/lib/puppet/util/execution.rb +9 -6
  75. data/lib/puppet/util/reference.rb +4 -2
  76. data/lib/puppet/util/windows/file.rb +5 -1
  77. data/lib/puppet/version.rb +6 -2
  78. data/locales/config.yaml +1 -1
  79. data/locales/puppet.pot +18 -4
  80. data/spec/integration/ssl/autosign_spec.rb +18 -3
  81. data/spec/integration/ssl/key_spec.rb +104 -0
  82. data/spec/integration/type/user_spec.rb +13 -6
  83. data/spec/spec_helper.rb +7 -0
  84. data/spec/unit/application/inspect_spec.rb +9 -2
  85. data/spec/unit/data_providers/function_data_provider_spec.rb +2 -2
  86. data/spec/unit/etc_spec.rb +234 -0
  87. data/spec/unit/face/certificate_spec.rb +10 -2
  88. data/spec/unit/functions/dig_spec.rb +1 -1
  89. data/spec/unit/functions/hiera_spec.rb +40 -1
  90. data/spec/unit/functions/lookup_fixture_spec.rb +10 -10
  91. data/spec/unit/functions/lookup_spec.rb +1217 -357
  92. data/spec/unit/functions4_spec.rb +37 -1
  93. data/spec/unit/indirector/file_bucket_file/file_spec.rb +33 -2
  94. data/spec/unit/indirector/key/file_spec.rb +1 -1
  95. data/spec/unit/indirector/ssl_file_spec.rb +3 -3
  96. data/spec/unit/module_spec.rb +52 -59
  97. data/spec/unit/network/http/compression_spec.rb +39 -8
  98. data/spec/unit/parser/compiler_spec.rb +14 -0
  99. data/spec/unit/pops/loaders/loaders_spec.rb +21 -3
  100. data/spec/unit/pops/loaders/module_loaders_spec.rb +61 -0
  101. data/spec/unit/pops/lookup/context_spec.rb +56 -8
  102. data/spec/unit/pops/lookup/lookup_spec.rb +32 -1
  103. data/spec/unit/pops/parser/lexer2_spec.rb +8 -0
  104. data/spec/unit/pops/types/ruby_generator_spec.rb +48 -0
  105. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +12 -3
  106. data/spec/unit/pops/types/types_spec.rb +6 -7
  107. data/spec/unit/provider/nameservice_spec.rb +12 -12
  108. data/spec/unit/provider/package/pkg_spec.rb +2 -0
  109. data/spec/unit/provider/service/src_spec.rb +5 -0
  110. data/spec/unit/ssl/base_spec.rb +9 -0
  111. data/spec/unit/ssl/certificate_authority_spec.rb +2 -2
  112. data/spec/unit/ssl/certificate_request_attributes_spec.rb +6 -0
  113. data/spec/unit/ssl/certificate_request_spec.rb +1 -1
  114. data/spec/unit/ssl/certificate_spec.rb +1 -1
  115. data/spec/unit/ssl/configuration_spec.rb +11 -2
  116. data/spec/unit/ssl/inventory_spec.rb +27 -3
  117. data/spec/unit/ssl/key_spec.rb +7 -7
  118. data/spec/unit/type/exec_spec.rb +41 -4
  119. data/spec/unit/type/file_spec.rb +4 -1
  120. data/spec/unit/util/character_encoding_spec.rb +88 -0
  121. data/spec/unit/util/execution_spec.rb +12 -0
  122. data/spec/unit/version_spec.rb +4 -0
  123. metadata +3803 -3808
  124. data/tasks/i18n.rake +0 -20
@@ -237,6 +237,22 @@ describe 'the 4x function api' do
237
237
  rejected: parameter 's1' expects a String value, got Integer")
238
238
  end
239
239
 
240
+ context 'an argument_mismatch handler' do
241
+ let(:func) { create_function_with_mismatch_handler.new(:closure_scope, :loader) }
242
+
243
+ it 'is called on matching arguments' do
244
+ expect { func.call({}, '1') }.to raise_error(ArgumentError, "'test' It's not OK to pass a string")
245
+ end
246
+
247
+ it 'is not called unless arguments are matching' do
248
+ expect { func.call({}, '1', 3) }.to raise_error(ArgumentError, "'test' expects 1 argument, got 2")
249
+ end
250
+
251
+ it 'is not included in a signature mismatch description' do
252
+ expect { func.call({}, 2.3) }.to raise_error { |e| expect(e.message).not_to match(/String/) }
253
+ end
254
+ end
255
+
240
256
  context 'can use injection' do
241
257
  before :all do
242
258
  injector = Puppet::Pops::Binder::Injector.create('test') do
@@ -474,7 +490,7 @@ describe 'the 4x function api' do
474
490
  # evaluate a puppet call
475
491
  source = "testing::test(10) |$x| { $x+1 }"
476
492
  program = parser.parse_string(source, __FILE__)
477
- Puppet::Pops::Adapters::LoaderAdapter.expects(:loader_for_model_object).returns(the_loader)
493
+ Puppet::Pops::Adapters::LoaderAdapter.expects(:loader_for_model_object).at_least_once.returns(the_loader)
478
494
  expect(parser.evaluate(scope, program)).to eql(11)
479
495
  end
480
496
  end
@@ -962,6 +978,26 @@ describe 'the 4x function api' do
962
978
  end
963
979
  end
964
980
 
981
+ def create_function_with_mismatch_handler
982
+ f = Puppet::Functions.create_function('test') do
983
+ dispatch :test do
984
+ param 'Integer', :x
985
+ end
986
+
987
+ argument_mismatch :on_error do
988
+ param 'String', :x
989
+ end
990
+
991
+ def test(x)
992
+ yield(5,x) if block_given?
993
+ end
994
+
995
+ def on_error(x)
996
+ "It's not OK to pass a string"
997
+ end
998
+ end
999
+ end
1000
+
965
1001
  def type_alias_t(name, type_string)
966
1002
  type_expr = Puppet::Pops::Parser::EvaluatingParser.new.parse_string(type_string).current
967
1003
  Puppet::Pops::Types::TypeFactory.type_alias(name, type_expr)
@@ -52,11 +52,42 @@ describe Puppet::FileBucketFile::File, :uses_checksums => true do
52
52
  0x2f94cc40,0x15a12deb,0xdc15f4a3,0x490786bb,
53
53
  0x6d658673,0xa4341f7d,0x8fd75920,0xefd18d5a].pack("I" * 16)
54
54
 
55
- save_bucket_file(first_contents, "/foo/bar")
55
+ checksum_value = save_bucket_file(first_contents, "/foo/bar")
56
56
 
57
+ # We expect Puppet to log an error with the path to the file
58
+ Puppet.expects(:err).with(regexp_matches(/Unable to verify existing FileBucket backup at '#{Puppet[:bucketdir]}.*#{checksum_value}\/contents'/))
59
+
60
+ # But the exception should not contain it
57
61
  expect do
58
62
  save_bucket_file(collision_contents, "/foo/bar")
59
- end.to raise_error(Puppet::FileBucket::BucketError, /Got passed new contents/)
63
+ end.to raise_error(Puppet::FileBucket::BucketError, /\AExisting backup and new file have different content but same checksum, {md5}#{checksum_value}\. Verify existing backup and remove if incorrect\.\Z/)
64
+ end
65
+
66
+ # See PUP-1334
67
+ context "when the contents file exists but is corrupted and does not match the expected checksum" do
68
+ let(:original_contents) { "a file that will get corrupted" }
69
+ let(:bucket_file) { Puppet::FileBucket::File.new(original_contents) }
70
+ let(:contents_file) { "#{Puppet[:bucketdir]}/8/e/6/4/f/8/5/d/8e64f85dd54a412f65edabcafe44d491/contents" }
71
+
72
+ before(:each) do
73
+ # Ensure we're starting with a clean slate - no pre-existing backup
74
+ Puppet::FileSystem.unlink(contents_file) if Puppet::FileSystem.exist?(contents_file)
75
+ # Create initial "correct" backup
76
+ Puppet::FileBucket::File.indirection.save(bucket_file)
77
+ # Modify the contents file so that it no longer matches the SHA, simulating a corrupt backup
78
+ Puppet::FileSystem.unlink(contents_file) # bucket_files are read-only
79
+ Puppet::Util.replace_file(contents_file, 0600) { |fh| fh.puts "now with corrupted content" }
80
+ end
81
+
82
+ it "issues a warning that the backup will be overwritten" do
83
+ Puppet.expects(:warning).with(regexp_matches(/Existing backup does not match its expected sum, #{bucket_file.checksum}/))
84
+ Puppet::FileBucket::File.indirection.save(bucket_file)
85
+ end
86
+
87
+ it "overwrites the existing contents file (backup)" do
88
+ Puppet::FileBucket::File.indirection.save(bucket_file)
89
+ expect(Puppet::FileSystem.read(contents_file)).to eq(original_contents)
90
+ end
60
91
  end
61
92
 
62
93
  describe "when supplying a path" do
@@ -66,7 +66,7 @@ describe Puppet::SSL::Key::File do
66
66
  it "should save the public key when saving the private key" do
67
67
  fh = StringIO.new
68
68
 
69
- Puppet.settings.setting(:publickeydir).expects(:open_file).with(@public_key_path, 'w').yields fh
69
+ Puppet.settings.setting(:publickeydir).expects(:open_file).with(@public_key_path, 'w:ASCII').yields fh
70
70
  Puppet.settings.setting(:privatekeydir).stubs(:open_file)
71
71
  @public_key.expects(:to_pem).returns "my pem"
72
72
 
@@ -221,7 +221,7 @@ describe Puppet::Indirector::SslFile do
221
221
  @searcher.class.store_in @setting
222
222
  fh = mock 'filehandle'
223
223
  fh.stubs :print
224
- Puppet.settings.setting(@setting).expects(:open_file).with(@certpath, 'w').yields fh
224
+ Puppet.settings.setting(@setting).expects(:open_file).with(@certpath, 'w:ASCII').yields fh
225
225
 
226
226
  @searcher.save(@request)
227
227
  end
@@ -233,7 +233,7 @@ describe Puppet::Indirector::SslFile do
233
233
 
234
234
  fh = mock 'filehandle'
235
235
  fh.stubs :print
236
- Puppet.settings.setting(@setting).expects(:open).with('w').yields fh
236
+ Puppet.settings.setting(@setting).expects(:open).with('w:ASCII').yields fh
237
237
  @searcher.save(@request)
238
238
  end
239
239
  end
@@ -246,7 +246,7 @@ describe Puppet::Indirector::SslFile do
246
246
 
247
247
  fh = mock 'filehandle'
248
248
  fh.stubs :print
249
- Puppet.settings.setting(:cakey).expects(:open).with('w').yields fh
249
+ Puppet.settings.setting(:cakey).expects(:open).with('w:ASCII').yields fh
250
250
  @searcher.stubs(:ca?).returns true
251
251
  @searcher.save(@request)
252
252
  end
@@ -170,7 +170,6 @@ describe Puppet::Module do
170
170
 
171
171
  it "should list modules that are missing" do
172
172
  metadata_file = "#{@modpath}/needy/metadata.json"
173
- Puppet::FileSystem.expects(:exist?).with(metadata_file).returns true
174
173
  mod = PuppetSpec::Modules.create(
175
174
  'needy',
176
175
  @modpath,
@@ -196,7 +195,6 @@ describe Puppet::Module do
196
195
 
197
196
  it "should list modules that are missing and have invalid names" do
198
197
  metadata_file = "#{@modpath}/needy/metadata.json"
199
- Puppet::FileSystem.expects(:exist?).with(metadata_file).returns true
200
198
  mod = PuppetSpec::Modules.create(
201
199
  'needy',
202
200
  @modpath,
@@ -305,7 +303,6 @@ describe Puppet::Module do
305
303
  env = Puppet::Node::Environment.create(:testing, [@modpath])
306
304
 
307
305
  metadata_file = "#{@modpath}/foobar/metadata.json"
308
- Puppet::FileSystem.expects(:exist?).with(metadata_file).times(3).returns true
309
306
  mod = PuppetSpec::Modules.create(
310
307
  'foobar',
311
308
  @modpath,
@@ -364,10 +361,6 @@ describe Puppet::Module do
364
361
  it "should only list unmet dependencies" do
365
362
  env = Puppet::Node::Environment.create(:testing, [@modpath])
366
363
 
367
- [name, 'satisfied'].each do |mod_name|
368
- metadata_file = "#{@modpath}/#{mod_name}/metadata.json"
369
- Puppet::FileSystem.expects(:exist?).with(metadata_file).twice.returns true
370
- end
371
364
  mod = PuppetSpec::Modules.create(
372
365
  name,
373
366
  @modpath,
@@ -588,60 +581,70 @@ end
588
581
 
589
582
  describe Puppet::Module do
590
583
  include PuppetSpec::Files
591
- before do
592
- @modpath = tmpdir('modpath')
593
- @module = PuppetSpec::Modules.create('mymod', @modpath)
584
+
585
+ let!(:modpath) do
586
+ path = tmpdir('modpath')
587
+ PuppetSpec::Modules.create('mymod', path)
588
+ path
594
589
  end
595
590
 
591
+ let!(:mymodpath) { File.join(modpath, 'mymod') }
592
+
593
+ let!(:mymod_metadata) { File.join(mymodpath, 'metadata.json') }
594
+
595
+ let(:mymod) { Puppet::Module.new('mymod', mymodpath, nil) }
596
+
596
597
  it "should use 'License' in its current path as its metadata file" do
597
- expect(@module.license_file).to eq("#{@modpath}/mymod/License")
598
+ expect(mymod.license_file).to eq("#{modpath}/mymod/License")
598
599
  end
599
600
 
600
601
  it "should cache the license file" do
601
- @module.expects(:path).once.returns nil
602
- @module.license_file
603
- @module.license_file
602
+ mymod.expects(:path).once.returns nil
603
+ mymod.license_file
604
+ mymod.license_file
604
605
  end
605
606
 
606
607
  it "should use 'metadata.json' in its current path as its metadata file" do
607
- expect(@module.metadata_file).to eq("#{@modpath}/mymod/metadata.json")
608
+ expect(mymod_metadata).to eq("#{modpath}/mymod/metadata.json")
609
+ end
610
+
611
+ it "should not have metadata if it has a metadata file and its data is valid but empty json hash" do
612
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns "{}"
613
+
614
+ expect(mymod).not_to be_has_metadata
608
615
  end
609
616
 
610
- it "should have metadata if it has a metadata file and its data is not empty" do
611
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
612
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns "{\"foo\" : \"bar\"}"
617
+ it "should not have metadata if it has a metadata file and its data is empty" do
618
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns ""
613
619
 
614
- expect(@module).to be_has_metadata
620
+ expect(mymod).not_to be_has_metadata
615
621
  end
616
622
 
617
- it "should not have metadata if has a metadata file and its data is empty" do
618
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
619
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns "This is some invalid json.\n"
620
- expect(@module).not_to be_has_metadata
623
+ it "should not have metadata if has a metadata file and its data is invalid" do
624
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns "This is some invalid json.\n"
625
+ expect(mymod).not_to be_has_metadata
621
626
  end
622
627
 
623
628
  it "should know if it is missing a metadata file" do
624
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns false
629
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).raises(Errno::ENOENT)
625
630
 
626
- expect(@module).not_to be_has_metadata
631
+ expect(mymod).not_to be_has_metadata
627
632
  end
628
633
 
629
634
  it "should be able to parse its metadata file" do
630
- expect(@module).to respond_to(:load_metadata)
635
+ expect(mymod).to respond_to(:load_metadata)
631
636
  end
632
637
 
633
638
  it "should parse its metadata file on initialization if it is present" do
634
- Puppet::Module.any_instance.expects(:has_metadata?).returns true
635
639
  Puppet::Module.any_instance.expects(:load_metadata)
636
640
 
637
641
  Puppet::Module.new("yay", "/path", mock("env"))
638
642
  end
639
643
 
640
644
  it "should tolerate failure to parse" do
641
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
642
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
645
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
643
646
 
644
- expect(@module.has_metadata?).to be_falsey
647
+ expect(mymod.has_metadata?).to be_falsey
645
648
  end
646
649
 
647
650
  describe 'when --strict is warning' do
@@ -650,10 +653,9 @@ describe Puppet::Module do
650
653
  end
651
654
 
652
655
  it "should warn about a failure to parse" do
653
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
654
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
656
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
655
657
 
656
- expect(@module.has_metadata?).to be_falsey
658
+ expect(mymod.has_metadata?).to be_falsey
657
659
  expect(@logs).to have_matching_log(/mymod has an invalid and unparsable metadata\.json file/)
658
660
  end
659
661
  end
@@ -664,10 +666,9 @@ describe Puppet::Module do
664
666
  end
665
667
 
666
668
  it "should warn about a failure to parse" do
667
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
668
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
669
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
669
670
 
670
- expect(@module.has_metadata?).to be_falsey
671
+ expect(mymod.has_metadata?).to be_falsey
671
672
  expect(@logs).to have_matching_log(/mymod has an invalid and unparsable metadata\.json file.*/)
672
673
  end
673
674
  end
@@ -678,47 +679,39 @@ describe Puppet::Module do
678
679
  end
679
680
 
680
681
  it "should fail on a failure to parse" do
681
- Puppet::FileSystem.expects(:exist?).with(@module.metadata_file).returns true
682
- File.stubs(:read).with(@module.metadata_file, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
682
+ File.stubs(:read).with(mymod_metadata, {:encoding => 'utf-8'}).returns(my_fixture('trailing-comma.json'))
683
683
 
684
684
  expect do
685
- expect(@module.has_metadata?).to be_falsey
685
+ expect(mymod.has_metadata?).to be_falsey
686
686
  end.to raise_error(/mymod has an invalid and unparsable metadata\.json file/)
687
687
  end
688
688
  end
689
689
 
690
690
  def a_module_with_metadata(data)
691
- text = data.to_pson
692
-
693
- mod = Puppet::Module.new("foo", "/path", mock("env"))
694
- mod.stubs(:metadata_file).returns "/my/file"
695
- File.stubs(:read).with("/my/file", {:encoding => 'utf-8'}).returns text
696
- mod
691
+ File.stubs(:read).with("/path/metadata.json", {:encoding => 'utf-8'}).returns data.to_pson
692
+ Puppet::Module.new("foo", "/path", mock("env"))
697
693
  end
698
694
 
699
695
  describe "when loading the metadata file" do
700
- before do
701
- @data = {
696
+ let(:data) do
697
+ {
702
698
  :license => "GPL2",
703
699
  :author => "luke",
704
700
  :version => "1.0",
705
701
  :source => "http://foo/",
706
702
  :dependencies => []
707
703
  }
708
- @module = a_module_with_metadata(@data)
709
704
  end
710
705
 
711
706
  %w{source author version license}.each do |attr|
712
707
  it "should set #{attr} if present in the metadata file" do
713
- @module.load_metadata
714
- expect(@module.send(attr)).to eq(@data[attr.to_sym])
708
+ mod = a_module_with_metadata(data)
709
+ expect(mod.send(attr)).to eq(data[attr.to_sym])
715
710
  end
716
711
 
717
712
  it "should fail if #{attr} is not present in the metadata file" do
718
- @data.delete(attr.to_sym)
719
- @text = @data.to_pson
720
- File.stubs(:read).with("/my/file", {:encoding => 'utf-8'}).returns @text
721
- expect { @module.load_metadata }.to raise_error(
713
+ data.delete(attr.to_sym)
714
+ expect { a_module_with_metadata(data) }.to raise_error(
722
715
  Puppet::Module::MissingMetadata,
723
716
  "No #{attr} module metadata provided for foo"
724
717
  )
@@ -742,8 +735,8 @@ describe Puppet::Module do
742
735
  EOF
743
736
  end
744
737
 
738
+ Puppet::Module.any_instance.stubs(:metadata_file).returns metadata_json
745
739
  mod = Puppet::Module.new('foo', '/path', mock('env'))
746
- mod.stubs(:metadata_file).returns metadata_json
747
740
 
748
741
  mod.load_metadata
749
742
  expect(mod.author).to eq(rune_utf8)
@@ -773,17 +766,17 @@ describe Puppet::Module do
773
766
  end
774
767
 
775
768
  it "should know what other modules require it" do
776
- env = Puppet::Node::Environment.create(:testing, [@modpath])
769
+ env = Puppet::Node::Environment.create(:testing, [modpath])
777
770
 
778
771
  dependable = PuppetSpec::Modules.create(
779
772
  'dependable',
780
- @modpath,
773
+ modpath,
781
774
  :metadata => {:author => 'puppetlabs'},
782
775
  :environment => env
783
776
  )
784
777
  PuppetSpec::Modules.create(
785
778
  'needy',
786
- @modpath,
779
+ modpath,
787
780
  :metadata => {
788
781
  :author => 'beggar',
789
782
  :dependencies => [{
@@ -795,7 +788,7 @@ describe Puppet::Module do
795
788
  )
796
789
  PuppetSpec::Modules.create(
797
790
  'wantit',
798
- @modpath,
791
+ modpath,
799
792
  :metadata => {
800
793
  :author => 'spoiled',
801
794
  :dependencies => [{
@@ -86,17 +86,48 @@ describe "http compression" do
86
86
  expect(@uncompressor.uncompress_body(@response)).to eq("uncompresseddata")
87
87
  end
88
88
 
89
- it "should use a GzipReader with 'gzip' content-encoding" do
90
- @response.stubs(:[]).with('content-encoding').returns('gzip')
89
+ context "with 'gzip' content-encoding" do
90
+ it "should use a GzipReader" do
91
+ @response.stubs(:[]).with('content-encoding').returns('gzip')
91
92
 
92
- io = stub 'io'
93
- StringIO.expects(:new).with("mydata").returns io
93
+ io = stub 'io'
94
+ StringIO.expects(:new).with("mydata").returns io
94
95
 
95
- reader = stub 'gzip reader'
96
- Zlib::GzipReader.expects(:new).with(io).returns(reader)
97
- reader.expects(:read).returns "uncompresseddata"
96
+ reader = stub 'gzip reader'
97
+ Zlib::GzipReader.expects(:new).with(io, :encoding => Encoding::BINARY).returns(reader)
98
+ reader.expects(:read).returns "uncompresseddata"
98
99
 
99
- expect(@uncompressor.uncompress_body(@response)).to eq("uncompresseddata")
100
+ expect(@uncompressor.uncompress_body(@response)).to eq("uncompresseddata")
101
+ end
102
+
103
+ it "should correctly decompress PSON containing UTF-8 in Binary Encoding" do
104
+ # Simulate a compressed response body containing PSON containing UTF-8
105
+ # using different UTF-8 widths:
106
+
107
+ # \u06ff - ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191
108
+ # \u16A0 - ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160
109
+ # \u{2070E} - 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142
110
+
111
+ pson = "foo\u06ff\u16A0\u{2070E}".to_pson # unicode expression eqivalent of "foo\xDB\xBF\xE1\x9A\xA0\xF0\xA0\x9C\x8E\" per above
112
+ writer = Zlib::GzipWriter.new(StringIO.new)
113
+ writer.write(pson)
114
+ compressed_body = writer.close.string
115
+
116
+ begin
117
+ default_external = Encoding.default_external
118
+ Encoding.default_external = Encoding::ISO_8859_1
119
+
120
+ @response.stubs(:[]).with('content-encoding').returns('gzip')
121
+ @response.stubs(:body).returns(compressed_body)
122
+
123
+ uncompressed = @uncompressor.uncompress_body(@response)
124
+ # By default Zlib::GzipReader decompresses into Encoding.default_external, and we want to ensure our result is BINARY too
125
+ expect(uncompressed.encoding).to eq(Encoding::BINARY)
126
+ expect(uncompressed).to eq("\"foo\xDB\xBF\xE1\x9A\xA0\xF0\xA0\x9C\x8E\"".force_encoding(Encoding::BINARY))
127
+ ensure
128
+ Encoding.default_external = default_external
129
+ end
130
+ end
100
131
  end
101
132
  end
102
133
 
@@ -109,6 +109,20 @@ describe Puppet::Parser::Compiler do
109
109
 
110
110
  describe "when initializing" do
111
111
 
112
+ it 'should not create the settings class more than once' do
113
+ logs = []
114
+ Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
115
+ Puppet[:code] = 'undef'
116
+ @compiler.compile
117
+
118
+ @compiler = Puppet::Parser::Compiler.new(@node)
119
+ Puppet[:code] = 'undef'
120
+ @compiler.compile
121
+ end
122
+ warnings = logs.select { |log| log.level == :warning }.map { |log| log.message }
123
+ expect(warnings).not_to include(/Class 'settings' is already defined/)
124
+ end
125
+
112
126
  it "should set its node attribute" do
113
127
  expect(@compiler.node).to equal(@node)
114
128
  end