puppet 4.10.1-universal-darwin → 4.10.4-universal-darwin

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 (120) hide show
  1. data/ext/project_data.yaml +1 -1
  2. data/lib/puppet.rb +40 -28
  3. data/lib/puppet/application/agent.rb +1 -1
  4. data/lib/puppet/application/apply.rb +1 -1
  5. data/lib/puppet/application/cert.rb +1 -1
  6. data/lib/puppet/application/describe.rb +1 -1
  7. data/lib/puppet/application/device.rb +1 -1
  8. data/lib/puppet/application/doc.rb +3 -3
  9. data/lib/puppet/application/filebucket.rb +1 -1
  10. data/lib/puppet/application/inspect.rb +2 -2
  11. data/lib/puppet/application/lookup.rb +1 -1
  12. data/lib/puppet/application/master.rb +1 -1
  13. data/lib/puppet/application/resource.rb +7 -7
  14. data/lib/puppet/defaults.rb +1 -1
  15. data/lib/puppet/etc.rb +75 -39
  16. data/lib/puppet/face/ca.rb +1 -1
  17. data/lib/puppet/face/catalog.rb +1 -1
  18. data/lib/puppet/face/certificate.rb +1 -1
  19. data/lib/puppet/face/certificate_request.rb +1 -1
  20. data/lib/puppet/face/certificate_revocation_list.rb +1 -1
  21. data/lib/puppet/face/config.rb +1 -1
  22. data/lib/puppet/face/epp.rb +1 -1
  23. data/lib/puppet/face/facts.rb +1 -1
  24. data/lib/puppet/face/file.rb +1 -1
  25. data/lib/puppet/face/help.rb +1 -1
  26. data/lib/puppet/face/key.rb +1 -1
  27. data/lib/puppet/face/man.rb +2 -2
  28. data/lib/puppet/face/module.rb +1 -1
  29. data/lib/puppet/face/node.rb +1 -1
  30. data/lib/puppet/face/parser.rb +1 -1
  31. data/lib/puppet/face/plugin.rb +1 -1
  32. data/lib/puppet/face/report.rb +1 -1
  33. data/lib/puppet/face/resource.rb +1 -1
  34. data/lib/puppet/face/resource_type.rb +1 -1
  35. data/lib/puppet/face/status.rb +1 -1
  36. data/lib/puppet/feature/base.rb +1 -1
  37. data/lib/puppet/functions/eyaml_lookup_key.rb +16 -12
  38. data/lib/puppet/functions/hiera.rb +9 -2
  39. data/lib/puppet/functions/hiera_array.rb +9 -2
  40. data/lib/puppet/functions/hiera_hash.rb +10 -2
  41. data/lib/puppet/functions/hiera_include.rb +17 -3
  42. data/lib/puppet/functions/hocon_data.rb +6 -0
  43. data/lib/puppet/functions/json_data.rb +4 -0
  44. data/lib/puppet/functions/yaml_data.rb +4 -0
  45. data/lib/puppet/generate/models/type/type.rb +6 -5
  46. data/lib/puppet/generate/templates/type/pcore.erb +1 -1
  47. data/lib/puppet/module_tool/skeleton/templates/generator/examples/init.pp.erb +1 -1
  48. data/lib/puppet/parser/functions/create_resources.rb +8 -0
  49. data/lib/puppet/parser/scope.rb +2 -2
  50. data/lib/puppet/pops/adapters.rb +10 -4
  51. data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +0 -2
  52. data/lib/puppet/pops/evaluator/runtime3_support.rb +31 -0
  53. data/lib/puppet/pops/issues.rb +8 -0
  54. data/lib/puppet/pops/loader/loader.rb +4 -0
  55. data/lib/puppet/pops/loader/module_loaders.rb +0 -2
  56. data/lib/puppet/pops/loader/static_loader.rb +1 -1
  57. data/lib/puppet/pops/loader/type_definition_instantiator.rb +1 -1
  58. data/lib/puppet/pops/loader/typed_name.rb +1 -0
  59. data/lib/puppet/pops/loaders.rb +7 -15
  60. data/lib/puppet/pops/lookup/environment_data_provider.rb +1 -1
  61. data/lib/puppet/pops/lookup/hiera_config.rb +3 -1
  62. data/lib/puppet/pops/lookup/interpolation.rb +2 -1
  63. data/lib/puppet/pops/lookup/lookup_key.rb +1 -1
  64. data/lib/puppet/pops/lookup/module_data_provider.rb +10 -2
  65. data/lib/puppet/pops/lookup/sub_lookup.rb +10 -9
  66. data/lib/puppet/pops/parser/lexer2.rb +20 -3
  67. data/lib/puppet/pops/pcore.rb +2 -2
  68. data/lib/puppet/pops/resource/resource_type_impl.rb +2 -2
  69. data/lib/puppet/pops/semantic_error.rb +12 -0
  70. data/lib/puppet/pops/serialization/deserializer.rb +7 -4
  71. data/lib/puppet/pops/types/p_type_set_type.rb +2 -2
  72. data/lib/puppet/pops/types/string_converter.rb +5 -17
  73. data/lib/puppet/pops/types/type_set_reference.rb +1 -1
  74. data/lib/puppet/pops/validation/checker4_0.rb +4 -0
  75. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  76. data/lib/puppet/provider/nameservice.rb +12 -4
  77. data/lib/puppet/provider/package/yum.rb +8 -8
  78. data/lib/puppet/provider/user/useradd.rb +1 -1
  79. data/lib/puppet/reference/configuration.rb +1 -1
  80. data/lib/puppet/resource.rb +9 -11
  81. data/lib/puppet/resource/type_collection.rb +1 -0
  82. data/lib/puppet/type/exec.rb +32 -26
  83. data/lib/puppet/type/file/mode.rb +4 -0
  84. data/lib/puppet/util/character_encoding.rb +77 -74
  85. data/lib/puppet/util/monkey_patches.rb +3 -1
  86. data/lib/puppet/util/windows/api_types.rb +3 -0
  87. data/lib/puppet/util/windows/file.rb +1 -1
  88. data/lib/puppet/version.rb +1 -1
  89. data/locales/puppet.pot +31 -7
  90. data/spec/integration/faces/documentation_spec.rb +2 -2
  91. data/spec/integration/parser/pcore_resource_spec.rb +15 -0
  92. data/spec/integration/resource/type_collection_spec.rb +6 -0
  93. data/spec/lib/puppet/face/1.0.0/huzzah.rb +1 -1
  94. data/spec/lib/puppet/face/basetest.rb +1 -1
  95. data/spec/lib/puppet/face/huzzah.rb +1 -1
  96. data/spec/lib/puppet/face/version_matching.rb +1 -1
  97. data/spec/lib/puppet_spec/character_encoding.rb +12 -0
  98. data/spec/lib/puppet_spec/compiler.rb +7 -0
  99. data/spec/shared_examples/rhel_package_provider.rb +10 -11
  100. data/spec/unit/application/resource_spec.rb +22 -1
  101. data/spec/unit/configurer/fact_handler_spec.rb +2 -1
  102. data/spec/unit/etc_spec.rb +361 -153
  103. data/spec/unit/functions/lookup_spec.rb +118 -2
  104. data/spec/unit/parser/functions/create_resources_spec.rb +47 -6
  105. data/spec/unit/parser/scope_spec.rb +8 -0
  106. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +40 -0
  107. data/spec/unit/pops/loaders/loaders_spec.rb +141 -79
  108. data/spec/unit/pops/lookup/interpolation_spec.rb +49 -9
  109. data/spec/unit/pops/lookup/lookup_spec.rb +32 -0
  110. data/spec/unit/pops/parser/lexer2_spec.rb +28 -0
  111. data/spec/unit/pops/types/p_object_type_spec.rb +1 -1
  112. data/spec/unit/pops/types/p_type_set_type_spec.rb +1 -1
  113. data/spec/unit/pops/types/string_converter_spec.rb +21 -0
  114. data/spec/unit/pops/validator/validator_spec.rb +43 -0
  115. data/spec/unit/provider/nameservice/directoryservice_spec.rb +2 -0
  116. data/spec/unit/provider/nameservice_spec.rb +113 -3
  117. data/spec/unit/provider/user/useradd_spec.rb +13 -0
  118. data/spec/unit/resource/catalog_spec.rb +21 -0
  119. data/spec/unit/util/character_encoding_spec.rb +193 -52
  120. metadata +4 -2
@@ -6,28 +6,57 @@ module Puppet::Pops
6
6
  describe 'Puppet::Pops::Lookup::Interpolation' do
7
7
  include Lookup::SubLookup
8
8
 
9
+ class InterpolationTestAdapter < Lookup::LookupAdapter
10
+ include Lookup::SubLookup
11
+
12
+ def initialize(data, interpolator)
13
+ @data = data
14
+ @interpolator = interpolator
15
+ end
16
+
17
+ def track(name)
18
+ end
19
+
20
+ def lookup(name, lookup_invocation, merge)
21
+ track(name)
22
+ segments = split_key(name)
23
+ root_key = segments.shift
24
+ found = @data[root_key]
25
+ found = sub_lookup(name, lookup_invocation, segments, found) unless segments.empty?
26
+ @interpolator.interpolate(found, lookup_invocation, true)
27
+ end
28
+ end
29
+
9
30
  let(:interpolator) { Class.new { include Lookup::Interpolation }.new }
10
31
  let(:scope) { {} }
32
+ let(:data) { {} }
33
+ let(:adapter) { InterpolationTestAdapter.new(data, interpolator) }
11
34
  let(:lookup_invocation) { Lookup::Invocation.new(scope, {}, {}, nil) }
12
35
 
36
+ before(:each) do
37
+ Lookup::Invocation.any_instance.stubs(:lookup_adapter).returns(adapter)
38
+ end
39
+
13
40
  def expect_lookup(*keys)
14
- keys.each do |key|
15
- segments = split_key(key)
16
- root_key = segments.shift
17
- found = data[root_key]
18
- found = sub_lookup(key, lookup_invocation, segments, found) unless segments.empty?
19
- Lookup.expects(:lookup).with(key, nil, '', true, nil, is_a(Lookup::Invocation)).returns(found)
20
- end
41
+ keys.each { |key| adapter.expects(:track).with(key) }
21
42
  end
22
43
 
23
44
  context 'when interpolating nested data' do
24
45
  let(:nested_hash) { {'a' => {'aa' => "%{alias('aaa')}"}} }
25
46
 
47
+ let(:scope) {
48
+ {
49
+ 'ds1' => 'a',
50
+ 'ds2' => 'b'
51
+ }
52
+ }
53
+
26
54
  let(:data) {
27
55
  {
28
56
  'aaa' => {'b' => {'bb' => "%{alias('bbb')}"}},
29
57
  'bbb' => ["%{alias('ccc')}"],
30
- 'ccc' => 'text'
58
+ 'ccc' => 'text',
59
+ 'ddd' => "%{literal('%')}{ds1}_%{literal('%')}{ds2}",
31
60
  }
32
61
  }
33
62
 
@@ -35,6 +64,16 @@ describe 'Puppet::Pops::Lookup::Interpolation' do
35
64
  expect_lookup('aaa', 'bbb', 'ccc')
36
65
  expect(interpolator.interpolate(nested_hash, lookup_invocation, true)).to eq('a' => {'aa' => {'b' => {'bb' => ['text']}}})
37
66
  end
67
+
68
+ it "'%{lookup('key')} will interpolate the returned value'" do
69
+ expect_lookup('ddd')
70
+ expect(interpolator.interpolate("%{lookup('ddd')}", lookup_invocation, true)).to eq('a_b')
71
+ end
72
+
73
+ it "'%{alias('key')} will not interpolate the returned value'" do
74
+ expect_lookup('ddd')
75
+ expect(interpolator.interpolate("%{alias('ddd')}", lookup_invocation, true)).to eq('%{ds1}_%{ds2}')
76
+ end
38
77
  end
39
78
 
40
79
  context 'when interpolating boolean scope values' do
@@ -242,7 +281,8 @@ describe 'Puppet::Pops::Lookup::Interpolation' do
242
281
  end
243
282
 
244
283
  it 'should not find a subkey that is matched within a string' do
245
- expect{ expect_lookup('key.subkey') }.to raise_error(/Got String when a hash-like object was expected to access value using 'subkey' from key 'key.subkey'/)
284
+ expect{ interpolator.interpolate("%{lookup('key.subkey')}", lookup_invocation, true) }.to raise_error(
285
+ /Got String when a hash-like object was expected to access value using 'subkey' from key 'key.subkey'/)
246
286
  end
247
287
  end
248
288
 
@@ -57,6 +57,13 @@ describe 'The lookup API' do
57
57
  mod::c: mod::c (from module)
58
58
  mod::e: mod::e (from module)
59
59
  mod::f: mod::f (from module)
60
+ mod::g:
61
+ :symbol: symbol key value
62
+ key: string key value
63
+ 6: integer key value
64
+ -4: negative integer key value
65
+ 2.7: float key value
66
+ '6': string integer key value
60
67
  YAML
61
68
  }
62
69
  }
@@ -137,6 +144,31 @@ describe 'The lookup API' do
137
144
  expect(Lookup.lookup('mod::f', nil, 'not found', true, nil, invocation)).to eql('mod::f (from environment)')
138
145
  end
139
146
 
147
+ it 'returns the correct types for hash keys' do
148
+ expect(Lookup.lookup('mod::g', nil, 'not found', true, nil, invocation)).to eql(
149
+ {
150
+ 'symbol' => 'symbol key value',
151
+ 'key' => 'string key value',
152
+ 6 => 'integer key value',
153
+ -4 => 'negative integer key value',
154
+ 2.7 => 'float key value',
155
+ '6' => 'string integer key value'
156
+ }
157
+ )
158
+ end
159
+
160
+ it 'can navigate a hash with an integer key using a dotted key' do
161
+ expect(Lookup.lookup('mod::g.6', nil, 'not found', true, nil, invocation)).to eql('integer key value')
162
+ end
163
+
164
+ it 'can navigate a hash with a negative integer key using a dotted key' do
165
+ expect(Lookup.lookup('mod::g.-4', nil, 'not found', true, nil, invocation)).to eql('negative integer key value')
166
+ end
167
+
168
+ it 'can navigate a hash with an string integer key using a dotted key with quoted integer' do
169
+ expect(Lookup.lookup("mod::g.'6'", nil, 'not found', true, nil, invocation)).to eql('string integer key value')
170
+ end
171
+
140
172
  context "with 'global_only' set to true in the invocation" do
141
173
  let(:invocation) { Invocation.new(scope).set_global_only }
142
174
 
@@ -412,6 +412,34 @@ describe 'Lexer2' do
412
412
  expect(tokens_scanned_from("1 / /./")).to match_tokens2(:NUMBER, :DIV, :REGEX)
413
413
  end
414
414
 
415
+ it 'should lex regexp with escaped slash' do
416
+ scanned = tokens_scanned_from('/\//')
417
+ expect(scanned).to match_tokens2(:REGEX)
418
+ expect(scanned[0][1][:value]).to eql(Regexp.new('/'))
419
+ end
420
+
421
+ it 'should lex regexp with escaped backslash' do
422
+ scanned = tokens_scanned_from('/\\\\/')
423
+ expect(scanned).to match_tokens2(:REGEX)
424
+ expect(scanned[0][1][:value]).to eql(Regexp.new('\\\\'))
425
+ end
426
+
427
+ it 'should lex regexp with escaped backslash followed escaped slash ' do
428
+ scanned = tokens_scanned_from('/\\\\\\//')
429
+ expect(scanned).to match_tokens2(:REGEX)
430
+ expect(scanned[0][1][:value]).to eql(Regexp.new('\\\\/'))
431
+ end
432
+
433
+ it 'should lex regexp with escaped slash followed escaped backslash ' do
434
+ scanned = tokens_scanned_from('/\\/\\\\/')
435
+ expect(scanned).to match_tokens2(:REGEX)
436
+ expect(scanned[0][1][:value]).to eql(Regexp.new('/\\\\'))
437
+ end
438
+
439
+ it 'should not lex regexp with escaped ending slash' do
440
+ expect(tokens_scanned_from('/\\/')).to match_tokens2(:DIV, :OTHER, :DIV)
441
+ end
442
+
415
443
  it "should accept newline in a regular expression" do
416
444
  scanned = tokens_scanned_from("/\n.\n/")
417
445
  # Note that strange formatting here is important
@@ -20,7 +20,7 @@ describe 'The Object Type' do
20
20
 
21
21
  def type_object_t(name, body_string)
22
22
  object = PObjectType.new(name, pp_parser.parse_string("{#{body_string}}").current.body)
23
- loader.set_entry(Loader::TypedName.new(:type, name.downcase), object)
23
+ loader.set_entry(Loader::TypedName.new(:type, name), object)
24
24
  object
25
25
  end
26
26
 
@@ -16,7 +16,7 @@ module Puppet::Pops
16
16
  def type_set_t(name, body_string, name_authority)
17
17
  i12n_literal_hash = pp_parser.parse_string("{#{body_string}}").current.body
18
18
  typeset = PTypeSetType.new(name, i12n_literal_hash, name_authority)
19
- loader.set_entry(Loader::TypedName.new(:type, name.downcase, name_authority), typeset)
19
+ loader.set_entry(Loader::TypedName.new(:type, name, name_authority), typeset)
20
20
  typeset
21
21
  end
22
22
 
@@ -874,6 +874,22 @@ describe 'The string converter' do
874
874
  end
875
875
  end
876
876
 
877
+ context 'when converting a runtime type' do
878
+ [ :sym, (1..3), Time.now ].each do |value|
879
+ it "the default string representation for #{value} is #to_s" do
880
+ expect(converter.convert(value, :default)).to eq(value.to_s)
881
+ end
882
+
883
+ it "the '%q' string representation for #{value} is #inspect" do
884
+ expect(converter.convert(value, '%q')).to eq(value.inspect)
885
+ end
886
+ end
887
+
888
+ it 'an unknown format raises an error' do
889
+ expect { converter.convert(:sym, '%b') }.to raise_error("Illegal format 'b' specified for value of Runtime type - expected one of the characters 'sq'")
890
+ end
891
+ end
892
+
877
893
  context 'when converting regexp' do
878
894
  it 'the default string representation is "regexp"' do
879
895
  expect(converter.convert(/.*/, :default)).to eq('.*')
@@ -906,6 +922,11 @@ describe 'The string converter' do
906
922
  string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}
907
923
  expect(converter.convert(/[a-z]\s*/m, string_formats)).to eq('/(?m-ix:[a-z]\s*)/')
908
924
  end
925
+
926
+ it 'the format %p produces \'/foo\/bar/\' for expression /foo\/bar/' do
927
+ string_formats = { Puppet::Pops::Types::PRegexpType::DEFAULT => '%p'}
928
+ expect(converter.convert(/foo\/bar/, string_formats)).to eq('/foo\/bar/')
929
+ end
909
930
  end
910
931
 
911
932
  it 'errors when format is not recognized' do
@@ -94,6 +94,21 @@ describe "validating 4x" do
94
94
  expect(acceptor.error_count).to eql(0)
95
95
  expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_DEFAULT)
96
96
  end
97
+
98
+ it 'produces a warning for virtual class resource' do
99
+ acceptor = validate(parse('@class { test: }'))
100
+ expect(acceptor.warning_count).to eql(1)
101
+ expect(acceptor.error_count).to eql(0)
102
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
103
+ end
104
+
105
+ it 'produces a warning for exported class resource' do
106
+ acceptor = validate(parse('@@class { test: }'))
107
+ expect(acceptor.warning_count).to eql(1)
108
+ expect(acceptor.error_count).to eql(0)
109
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
110
+ end
111
+
97
112
  end
98
113
 
99
114
  context 'with --strict set to error' do
@@ -118,6 +133,20 @@ describe "validating 4x" do
118
133
  expect(acceptor.error_count).to eql(1)
119
134
  expect(acceptor).to have_issue(Puppet::Pops::Issues::DUPLICATE_DEFAULT)
120
135
  end
136
+
137
+ it 'produces an error for virtual class resource' do
138
+ acceptor = validate(parse('@class { test: }'))
139
+ expect(acceptor.warning_count).to eql(0)
140
+ expect(acceptor.error_count).to eql(1)
141
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
142
+ end
143
+
144
+ it 'produces an error for exported class resource' do
145
+ acceptor = validate(parse('@@class { test: }'))
146
+ expect(acceptor.warning_count).to eql(0)
147
+ expect(acceptor.error_count).to eql(1)
148
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
149
+ end
121
150
  end
122
151
 
123
152
  context 'with --strict set to off' do
@@ -142,6 +171,20 @@ describe "validating 4x" do
142
171
  expect(acceptor.error_count).to eql(0)
143
172
  expect(acceptor).to_not have_issue(Puppet::Pops::Issues::DUPLICATE_DEFAULT)
144
173
  end
174
+
175
+ it 'produces a warning for virtual class resource' do
176
+ acceptor = validate(parse('@class { test: }'))
177
+ expect(acceptor.warning_count).to eql(1)
178
+ expect(acceptor.error_count).to eql(0)
179
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
180
+ end
181
+
182
+ it 'produces a warning for exported class resource' do
183
+ acceptor = validate(parse('@@class { test: }'))
184
+ expect(acceptor.warning_count).to eql(1)
185
+ expect(acceptor.error_count).to eql(0)
186
+ expect(acceptor).to have_issue(Puppet::Pops::Issues::CLASS_NOT_VIRTUALIZABLE)
187
+ end
145
188
  end
146
189
 
147
190
  context 'for non productive expressions' do
@@ -11,6 +11,7 @@ end
11
11
  describe provider_class do
12
12
  before do
13
13
  @resource = stub("resource")
14
+ @resource.stubs(:[]).with(:name)
14
15
  @provider = provider_class.new(@resource)
15
16
  end
16
17
 
@@ -143,6 +144,7 @@ describe '(#4855) directoryservice group resource failure' do
143
144
 
144
145
  before :each do
145
146
  @resource = stub("resource")
147
+ @resource.stubs(:[]).with(:name)
146
148
  @provider = provider_class.new(@resource)
147
149
  end
148
150
 
@@ -3,6 +3,7 @@
3
3
  require 'spec_helper'
4
4
  require 'puppet/provider/nameservice'
5
5
  require 'puppet/etc'
6
+ require 'puppet_spec/character_encoding'
6
7
 
7
8
  describe Puppet::Provider::NameService do
8
9
 
@@ -60,6 +61,36 @@ describe Puppet::Provider::NameService do
60
61
  resource
61
62
  end
62
63
 
64
+ # These values simulate what Ruby Etc would return from a host with the "same"
65
+ # user represented in different encodings on disk.
66
+ let(:utf_8_jose) { "Jos\u00E9"}
67
+ let(:utf_8_labeled_as_latin_1_jose) { utf_8_jose.dup.force_encoding(Encoding::ISO_8859_1) }
68
+ let(:valid_latin1_jose) { utf_8_jose.encode(Encoding::ISO_8859_1)}
69
+ let(:invalid_utf_8_jose) { valid_latin1_jose.dup.force_encoding(Encoding::UTF_8) }
70
+ let(:escaped_utf_8_jose) { "Jos\uFFFD".force_encoding(Encoding::UTF_8) }
71
+
72
+ let(:utf_8_mixed_users) {
73
+ [
74
+ Struct::Passwd.new('root', 'x', 0, 0),
75
+ Struct::Passwd.new('foo', 'x', 1000, 2000),
76
+ Struct::Passwd.new(utf_8_jose, utf_8_jose, 1001, 2000), # UTF-8 character
77
+ # In a UTF-8 environment, ruby will return strings labeled as UTF-8 even if they're not valid in UTF-8
78
+ Struct::Passwd.new(invalid_utf_8_jose, invalid_utf_8_jose, 1002, 2000),
79
+ nil
80
+ ]
81
+ }
82
+
83
+ let(:latin_1_mixed_users) {
84
+ [
85
+ # In a LATIN-1 environment, ruby will return *all* strings labeled as LATIN-1
86
+ Struct::Passwd.new('root'.force_encoding(Encoding::ISO_8859_1), 'x', 0, 0),
87
+ Struct::Passwd.new('foo'.force_encoding(Encoding::ISO_8859_1), 'x', 1000, 2000),
88
+ Struct::Passwd.new(utf_8_labeled_as_latin_1_jose, utf_8_labeled_as_latin_1_jose, 1002, 2000),
89
+ Struct::Passwd.new(valid_latin1_jose, valid_latin1_jose, 1001, 2000), # UTF-8 character
90
+ nil
91
+ ]
92
+ }
93
+
63
94
  describe "#options" do
64
95
  it "should add options for a valid property" do
65
96
  described_class.options :foo, :key1 => 'val1', :key2 => 'val2'
@@ -121,6 +152,11 @@ describe Puppet::Provider::NameService do
121
152
  end
122
153
 
123
154
  describe "#listbyname" do
155
+ it "should be deprecated" do
156
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/listbyname is deprecated/))
157
+ described_class.listbyname
158
+ end
159
+
124
160
  it "should return a list of users if resource_type is user" do
125
161
  described_class.resource_type = Puppet::Type.type(:user)
126
162
  Puppet::Etc.expects(:setpwent)
@@ -129,6 +165,30 @@ describe Puppet::Provider::NameService do
129
165
  expect(described_class.listbyname).to eq(%w{root foo})
130
166
  end
131
167
 
168
+ context "encoding handling" do
169
+ described_class.resource_type = Puppet::Type.type(:user)
170
+
171
+ # These two tests simulate an environment where there are two users with
172
+ # the same name on disk, but each name is stored on disk in a different
173
+ # encoding
174
+ it "should return names with invalid byte sequences replaced with '?'" do
175
+ Etc.stubs(:getpwent).returns *utf_8_mixed_users
176
+ expect(invalid_utf_8_jose).to_not be_valid_encoding
177
+ result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do
178
+ described_class.listbyname
179
+ end
180
+ expect(result).to eq(['root', 'foo', utf_8_jose, escaped_utf_8_jose])
181
+ end
182
+
183
+ it "should return names in their original encoding/bytes if they would not be valid UTF-8" do
184
+ Etc.stubs(:getpwent).returns *latin_1_mixed_users
185
+ result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do
186
+ described_class.listbyname
187
+ end
188
+ expect(result).to eq(['root'.force_encoding(Encoding::UTF_8), 'foo'.force_encoding(Encoding::UTF_8), utf_8_jose, valid_latin1_jose])
189
+ end
190
+ end
191
+
132
192
  it "should return a list of groups if resource_type is group", :unless => Puppet.features.microsoft_windows? do
133
193
  described_class.resource_type = Puppet::Type.type(:group)
134
194
  Puppet::Etc.expects(:setgrent)
@@ -149,9 +209,44 @@ describe Puppet::Provider::NameService do
149
209
  end
150
210
 
151
211
  describe "instances" do
152
- it "should return a list of objects based on listbyname" do
153
- described_class.expects(:listbyname).multiple_yields 'root', 'foo', 'nobody'
154
- expect(described_class.instances.map(&:name)).to eq(%w{root foo nobody})
212
+ it "should return a list of objects in UTF-8 with any invalid characters replaced with '?'" do
213
+ # These two tests simulate an environment where there are two users with
214
+ # the same name on disk, but each name is stored on disk in a different
215
+ # encoding
216
+ Etc.stubs(:getpwent).returns(*utf_8_mixed_users)
217
+ result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::UTF_8) do
218
+ described_class.instances
219
+ end
220
+ expect(result.map(&:name)).to eq(
221
+ [
222
+ 'root'.force_encoding(Encoding::UTF_8), # started as UTF-8 on disk, returned unaltered as UTF-8
223
+ 'foo'.force_encoding(Encoding::UTF_8), # started as UTF-8 on disk, returned unaltered as UTF-8
224
+ utf_8_jose, # started as UTF-8 on disk, returned unaltered as UTF-8
225
+ escaped_utf_8_jose # started as LATIN-1 on disk, but Etc returned as UTF-8 and we escaped invalid chars
226
+ ]
227
+ )
228
+ end
229
+
230
+ it "should have object names in their original encoding/bytes if they would not be valid UTF-8" do
231
+ Etc.stubs(:getpwent).returns(*latin_1_mixed_users)
232
+ result = PuppetSpec::CharacterEncoding.with_external_encoding(Encoding::ISO_8859_1) do
233
+ described_class.instances
234
+ end
235
+ expect(result.map(&:name)).to eq(
236
+ [
237
+ 'root'.force_encoding(Encoding::UTF_8), # started as LATIN-1 on disk, we overrode to UTF-8
238
+ 'foo'.force_encoding(Encoding::UTF_8), # started as LATIN-1 on disk, we overrode to UTF-8
239
+ utf_8_jose, # started as UTF-8 on disk, returned by Etc as LATIN-1, and we overrode to UTF-8
240
+ valid_latin1_jose # started as LATIN-1 on disk, returned by Etc as valid LATIN-1, and we leave as LATIN-1
241
+ ]
242
+ )
243
+ end
244
+
245
+ it "should pass the Puppet::Etc :canonical_name Struct member to the constructor" do
246
+ users = [ Struct::Passwd.new(invalid_utf_8_jose, invalid_utf_8_jose, 1002, 2000), nil ]
247
+ Etc.stubs(:getpwent).returns(*users)
248
+ described_class.expects(:new).with(:name => escaped_utf_8_jose, :canonical_name => invalid_utf_8_jose, :ensure => :present)
249
+ described_class.instances
155
250
  end
156
251
  end
157
252
 
@@ -198,6 +293,21 @@ describe Puppet::Provider::NameService do
198
293
  provider.expects(:info2hash).never
199
294
  expect(provider.getinfo(true)).to be_nil
200
295
  end
296
+
297
+ # Nameservice instances track the original resource name on disk, before
298
+ # overriding to UTF-8, in @canonical_name for querying that state on disk
299
+ # again if needed
300
+ it "should use the instance's @canonical_name to query the system" do
301
+ provider_instance = described_class.new(:name => 'foo', :canonical_name => 'original_foo', :ensure => :present)
302
+ Puppet::Etc.expects(:send).with(:getfoonam, 'original_foo')
303
+ provider_instance.getinfo(true)
304
+ end
305
+
306
+ it "should use the instance's name instead of canonical_name if not supplied during instantiation" do
307
+ provider_instance = described_class.new(:name => 'foo', :ensure => :present)
308
+ Puppet::Etc.expects(:send).with(:getfoonam, 'foo')
309
+ provider_instance.getinfo(true)
310
+ end
201
311
  end
202
312
 
203
313
  describe "info2hash" do