puppet 6.4.4-x86-mingw32 → 6.4.5-x86-mingw32

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +1 -1
  3. data/Gemfile +4 -4
  4. data/Gemfile.lock +38 -32
  5. data/ext/build_defaults.yaml +1 -0
  6. data/ext/cert_inspector +3 -3
  7. data/ext/puppet-test +2 -2
  8. data/ext/regexp_nodes/regexp_nodes.rb +4 -4
  9. data/ext/windows/service/daemon.rb +38 -8
  10. data/install.rb +6 -6
  11. data/lib/puppet/application.rb +1 -1
  12. data/lib/puppet/application/apply.rb +2 -2
  13. data/lib/puppet/application/describe.rb +3 -9
  14. data/lib/puppet/application/doc.rb +1 -1
  15. data/lib/puppet/application/lookup.rb +1 -1
  16. data/lib/puppet/application/script.rb +2 -2
  17. data/lib/puppet/application/ssl.rb +4 -1
  18. data/lib/puppet/configurer.rb +86 -30
  19. data/lib/puppet/configurer/downloader.rb +2 -6
  20. data/lib/puppet/defaults.rb +32 -6
  21. data/lib/puppet/error.rb +9 -1
  22. data/lib/puppet/face/module/list.rb +5 -5
  23. data/lib/puppet/face/module/search.rb +1 -1
  24. data/lib/puppet/face/module/uninstall.rb +1 -1
  25. data/lib/puppet/face/module/upgrade.rb +1 -1
  26. data/lib/puppet/file_serving/http_metadata.rb +1 -1
  27. data/lib/puppet/file_system.rb +0 -8
  28. data/lib/puppet/file_system/memory_file.rb +1 -1
  29. data/lib/puppet/file_system/posix.rb +3 -2
  30. data/lib/puppet/forge.rb +3 -3
  31. data/lib/puppet/functions.rb +1 -2
  32. data/lib/puppet/functions/camelcase.rb +2 -2
  33. data/lib/puppet/functions/epp.rb +4 -4
  34. data/lib/puppet/functions/find_file.rb +9 -9
  35. data/lib/puppet/functions/inline_epp.rb +5 -5
  36. data/lib/puppet/gettext/module_translations.rb +1 -1
  37. data/lib/puppet/graph/rb_tree_map.rb +2 -2
  38. data/lib/puppet/graph/simple_graph.rb +4 -3
  39. data/lib/puppet/indirector/file_bucket_file/file.rb +1 -1
  40. data/lib/puppet/indirector/hiera.rb +2 -0
  41. data/lib/puppet/indirector/resource/ral.rb +1 -3
  42. data/lib/puppet/indirector/resource/validator.rb +1 -1
  43. data/lib/puppet/interface.rb +2 -1
  44. data/lib/puppet/loaders.rb +0 -1
  45. data/lib/puppet/metatype/manager.rb +1 -1
  46. data/lib/puppet/module.rb +1 -1
  47. data/lib/puppet/module/task.rb +20 -4
  48. data/lib/puppet/module_tool/applications/installer.rb +1 -1
  49. data/lib/puppet/module_tool/applications/uninstaller.rb +3 -3
  50. data/lib/puppet/module_tool/metadata.rb +1 -1
  51. data/lib/puppet/module_tool/shared_behaviors.rb +4 -4
  52. data/lib/puppet/module_tool/tar/mini.rb +1 -1
  53. data/lib/puppet/network/http/api/indirected_routes.rb +12 -11
  54. data/lib/puppet/network/http/connection.rb +10 -12
  55. data/lib/puppet/network/http/pool.rb +2 -0
  56. data/lib/puppet/network/http/site.rb +1 -1
  57. data/lib/puppet/network/resolver.rb +2 -2
  58. data/lib/puppet/node/environment.rb +4 -2
  59. data/lib/puppet/pal/pal_impl.rb +2 -2
  60. data/lib/puppet/parser/ast.rb +1 -1
  61. data/lib/puppet/parser/ast/resourceparam.rb +1 -1
  62. data/lib/puppet/parser/functions.rb +1 -1
  63. data/lib/puppet/parser/functions/epp.rb +3 -3
  64. data/lib/puppet/parser/functions/inline_epp.rb +5 -5
  65. data/lib/puppet/parser/scope.rb +8 -7
  66. data/lib/puppet/pops/evaluator/collectors/catalog_collector.rb +1 -1
  67. data/lib/puppet/pops/evaluator/collectors/exported_collector.rb +1 -1
  68. data/lib/puppet/pops/evaluator/external_syntax_support.rb +3 -2
  69. data/lib/puppet/pops/evaluator/runtime3_support.rb +4 -4
  70. data/lib/puppet/pops/loader/task_instantiator.rb +4 -0
  71. data/lib/puppet/pops/loaders.rb +1 -1
  72. data/lib/puppet/pops/lookup/hiera_config.rb +1 -0
  73. data/lib/puppet/pops/lookup/sub_lookup.rb +1 -1
  74. data/lib/puppet/pops/merge_strategy.rb +22 -18
  75. data/lib/puppet/pops/parser/heredoc_support.rb +1 -1
  76. data/lib/puppet/pops/parser/interpolation_support.rb +4 -4
  77. data/lib/puppet/pops/parser/locator.rb +1 -1
  78. data/lib/puppet/pops/parser/pn_parser.rb +17 -16
  79. data/lib/puppet/pops/puppet_stack.rb +51 -49
  80. data/lib/puppet/pops/types/p_sensitive_type.rb +1 -1
  81. data/lib/puppet/pops/types/string_converter.rb +10 -10
  82. data/lib/puppet/pops/types/types.rb +3 -3
  83. data/lib/puppet/property.rb +1 -1
  84. data/lib/puppet/property/ensure.rb +1 -1
  85. data/lib/puppet/provider/exec.rb +6 -2
  86. data/lib/puppet/provider/nameservice/directoryservice.rb +1 -1
  87. data/lib/puppet/provider/nameservice/pw.rb +2 -2
  88. data/lib/puppet/provider/package/apt.rb +5 -1
  89. data/lib/puppet/provider/package/dnfmodule.rb +87 -0
  90. data/lib/puppet/provider/package/dpkg.rb +34 -18
  91. data/lib/puppet/provider/package/openbsd.rb +1 -1
  92. data/lib/puppet/provider/package/pip.rb +34 -9
  93. data/lib/puppet/provider/package/portage.rb +4 -4
  94. data/lib/puppet/provider/package/rpm.rb +5 -5
  95. data/lib/puppet/provider/package/windows/package.rb +1 -1
  96. data/lib/puppet/provider/package/yum.rb +1 -1
  97. data/lib/puppet/provider/package_targetable.rb +5 -4
  98. data/lib/puppet/provider/parsedfile.rb +1 -1
  99. data/lib/puppet/provider/service/daemontools.rb +9 -9
  100. data/lib/puppet/provider/service/openbsd.rb +1 -1
  101. data/lib/puppet/provider/service/rcng.rb +2 -2
  102. data/lib/puppet/provider/service/runit.rb +2 -8
  103. data/lib/puppet/provider/service/systemd.rb +9 -9
  104. data/lib/puppet/provider/user/directoryservice.rb +1 -1
  105. data/lib/puppet/provider/user/hpux.rb +1 -1
  106. data/lib/puppet/provider/user/user_role_add.rb +1 -1
  107. data/lib/puppet/provider/user/useradd.rb +22 -13
  108. data/lib/puppet/provider/user/windows_adsi.rb +4 -5
  109. data/lib/puppet/reference/indirection.rb +2 -2
  110. data/lib/puppet/reference/metaparameter.rb +1 -3
  111. data/lib/puppet/reference/providers.rb +1 -3
  112. data/lib/puppet/reference/type.rb +3 -9
  113. data/lib/puppet/reports.rb +1 -1
  114. data/lib/puppet/resource.rb +1 -1
  115. data/lib/puppet/resource/catalog.rb +1 -1
  116. data/lib/puppet/settings.rb +3 -3
  117. data/lib/puppet/settings/environment_conf.rb +1 -0
  118. data/lib/puppet/ssl/host.rb +1 -1
  119. data/lib/puppet/ssl/oids.rb +1 -1
  120. data/lib/puppet/transaction.rb +33 -11
  121. data/lib/puppet/transaction/report.rb +1 -1
  122. data/lib/puppet/type.rb +2 -4
  123. data/lib/puppet/type/exec.rb +7 -3
  124. data/lib/puppet/type/file.rb +1 -2
  125. data/lib/puppet/type/file/data_sync.rb +5 -1
  126. data/lib/puppet/type/group.rb +4 -2
  127. data/lib/puppet/type/notify.rb +3 -2
  128. data/lib/puppet/type/package.rb +10 -3
  129. data/lib/puppet/type/schedule.rb +1 -1
  130. data/lib/puppet/type/service.rb +1 -1
  131. data/lib/puppet/type/user.rb +4 -2
  132. data/lib/puppet/util.rb +35 -12
  133. data/lib/puppet/util/command_line/trollop.rb +1 -1
  134. data/lib/puppet/util/http_proxy.rb +8 -14
  135. data/lib/puppet/util/log.rb +2 -2
  136. data/lib/puppet/util/log/destinations.rb +2 -2
  137. data/lib/puppet/util/logging.rb +32 -20
  138. data/lib/puppet/util/metric.rb +2 -2
  139. data/lib/puppet/util/provider_features.rb +2 -4
  140. data/lib/puppet/util/rdoc.rb +1 -1
  141. data/lib/puppet/util/reference.rb +1 -1
  142. data/lib/puppet/util/resource_template.rb +1 -1
  143. data/lib/puppet/util/selinux.rb +3 -1
  144. data/lib/puppet/util/windows/adsi.rb +48 -18
  145. data/lib/puppet/util/windows/registry.rb +7 -5
  146. data/lib/puppet/vendor.rb +1 -1
  147. data/lib/puppet/version.rb +1 -1
  148. data/lib/puppet/x509/cert_provider.rb +13 -6
  149. data/locales/puppet.pot +199 -159
  150. data/man/man5/puppet.conf.5 +35 -5
  151. data/man/man8/puppet-agent.8 +1 -1
  152. data/man/man8/puppet-apply.8 +1 -1
  153. data/man/man8/puppet-catalog.8 +1 -1
  154. data/man/man8/puppet-config.8 +1 -1
  155. data/man/man8/puppet-describe.8 +1 -1
  156. data/man/man8/puppet-device.8 +1 -1
  157. data/man/man8/puppet-doc.8 +1 -1
  158. data/man/man8/puppet-epp.8 +1 -1
  159. data/man/man8/puppet-facts.8 +1 -1
  160. data/man/man8/puppet-filebucket.8 +1 -1
  161. data/man/man8/puppet-generate.8 +1 -1
  162. data/man/man8/puppet-help.8 +1 -1
  163. data/man/man8/puppet-key.8 +1 -1
  164. data/man/man8/puppet-lookup.8 +1 -1
  165. data/man/man8/puppet-man.8 +1 -1
  166. data/man/man8/puppet-module.8 +1 -1
  167. data/man/man8/puppet-node.8 +1 -1
  168. data/man/man8/puppet-parser.8 +1 -1
  169. data/man/man8/puppet-plugin.8 +1 -1
  170. data/man/man8/puppet-report.8 +1 -1
  171. data/man/man8/puppet-resource.8 +1 -1
  172. data/man/man8/puppet-script.8 +1 -1
  173. data/man/man8/puppet-ssl.8 +1 -1
  174. data/man/man8/puppet-status.8 +1 -1
  175. data/man/man8/puppet.8 +2 -2
  176. data/spec/fixtures/unit/provider/package/dnfmodule/dnf-module-list-installed.txt +11 -0
  177. data/spec/integration/configurer_spec.rb +52 -0
  178. data/spec/integration/type/notify_spec.rb +46 -0
  179. data/spec/lib/puppet/certificate_factory.rb +2 -2
  180. data/spec/spec_helper.rb +28 -0
  181. data/spec/unit/application/ssl_spec.rb +4 -7
  182. data/spec/unit/configurer_spec.rb +394 -398
  183. data/spec/unit/defaults_spec.rb +4 -4
  184. data/spec/unit/forge/forge_spec.rb +1 -3
  185. data/spec/unit/forge/repository_spec.rb +1 -3
  186. data/spec/unit/indirector/resource/ral_spec.rb +4 -4
  187. data/spec/unit/network/http/connection_spec.rb +119 -145
  188. data/spec/unit/parser/scope_spec.rb +10 -0
  189. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +8 -3
  190. data/spec/unit/pops/loaders/module_loaders_spec.rb +37 -0
  191. data/spec/unit/provider/exec_spec.rb +209 -0
  192. data/spec/unit/provider/package/dnfmodule_spec.rb +186 -0
  193. data/spec/unit/provider/package/dpkg_spec.rb +238 -78
  194. data/spec/unit/provider/package/pip_spec.rb +51 -6
  195. data/spec/unit/provider/package/portage_spec.rb +4 -4
  196. data/spec/unit/provider/package_targetable_spec.rb +60 -0
  197. data/spec/unit/provider/service/daemontools_spec.rb +24 -0
  198. data/spec/unit/provider/service/runit_spec.rb +24 -0
  199. data/spec/unit/provider/service/systemd_spec.rb +25 -25
  200. data/spec/unit/provider/user/hpux_spec.rb +2 -2
  201. data/spec/unit/provider/user/useradd_spec.rb +46 -0
  202. data/spec/unit/ssl/host_spec.rb +0 -5
  203. data/spec/unit/ssl/state_machine_spec.rb +0 -6
  204. data/spec/unit/transaction_spec.rb +46 -0
  205. data/spec/unit/type/exec_spec.rb +6 -12
  206. data/spec/unit/type/file/content_spec.rb +9 -3
  207. data/spec/unit/type/file_spec.rb +9 -4
  208. data/spec/unit/type/package_spec.rb +5 -0
  209. data/spec/unit/util/execution_spec.rb +16 -0
  210. data/spec/unit/util/http_proxy_spec.rb +118 -27
  211. data/spec/unit/util/log/destinations_spec.rb +7 -3
  212. data/spec/unit/util/log_spec.rb +0 -138
  213. data/spec/unit/util/logging_spec.rb +200 -0
  214. data/spec/unit/util/windows/adsi_spec.rb +51 -0
  215. data/spec/unit/x509/cert_provider_spec.rb +24 -4
  216. data/tasks/manpages.rake +1 -0
  217. metadata +12 -10
  218. data/lib/puppet/pops/loader/null_loader.rb +0 -60
  219. data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_get/should_yield_to_the_block.yml +0 -24
  220. data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_head/should_yield_to_the_block.yml +0 -24
  221. data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_post/should_yield_to_the_block.yml +0 -24
@@ -680,5 +680,15 @@ describe Puppet::Parser::Scope do
680
680
  @scope.setvar("orange", :undef)
681
681
  expect(@scope.to_hash).to eq({'pear' => :green, 'apple' => :red })
682
682
  end
683
+
684
+ it "should not delete values that are :undef in inner scope when include_undef is true" do
685
+ @scope.new_ephemeral true
686
+ @scope.setvar("orange", :tangerine)
687
+ @scope.setvar("pear", :green)
688
+ @scope.new_ephemeral true
689
+ @scope.setvar("apple", :red)
690
+ @scope.setvar("orange", :undef)
691
+ expect(@scope.to_hash(true, true)).to eq({'pear' => :green, 'apple' => :red, 'orange' => :undef })
692
+ end
683
693
  end
684
694
  end
@@ -478,21 +478,26 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
478
478
  }.each do |source, coerced_val|
479
479
  it "should warn about numeric coercion in '#{source}' when strict = warning" do
480
480
  Puppet[:strict] = :warning
481
+ expect(Puppet::Pops::Evaluator::Runtime3Support::EvaluationError).not_to receive(:new)
481
482
  collect_notices(source)
482
483
  expect(warnings).to include(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)
483
484
  end
484
485
 
485
486
  it "should not warn about numeric coercion in '#{source}' if strict = off" do
486
487
  Puppet[:strict] = :off
488
+ expect(Puppet::Pops::Evaluator::Runtime3Support::EvaluationError).not_to receive(:new)
487
489
  collect_notices(source)
488
490
  expect(warnings).to_not include(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)
489
491
  end
490
492
 
491
493
  it "should error when finding numeric coercion in '#{source}' if strict = error" do
492
494
  Puppet[:strict] = :error
493
- expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(
494
- /The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/
495
- )
495
+ expect {
496
+ parser.evaluate_string(scope, source, __FILE__)
497
+ }.to raise_error {|error|
498
+ expect(error.message).to match(/The string '#{coerced_val}' was automatically coerced to the numerical value #{coerced_val}/)
499
+ expect(error.backtrace.first).to match(/runtime3_support\.rb.+optionally_fail/)
500
+ }
496
501
  end
497
502
  end
498
503
 
@@ -163,6 +163,43 @@ describe 'FileBased module loader' do
163
163
  expect(module_loader.load_typed(typed_name(:task, 'testmodule::baz'))).not_to be_nil
164
164
  expect { module_loader.load_typed(typed_name(:task, 'testmodule::qux')) }.to raise_error(/No source besides task metadata was found/)
165
165
  end
166
+
167
+ it 'raises and error when `parameters` is not a hash' do
168
+ metadata = { 'parameters' => 'foo' }
169
+ module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})
170
+
171
+ module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)
172
+ expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}
173
+ .to raise_error(Puppet::ParseError, /Failed to load metadata for task testmodule::foo: 'parameters' must be a hash/)
174
+ end
175
+
176
+ it 'raises and error when `implementations` `requirements` key is not an array' do
177
+ metadata = { 'implementations' => { 'name' => 'foo.py', 'requirements' => 'foo'} }
178
+ module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})
179
+
180
+ module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)
181
+ expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}
182
+ .to raise_error(Puppet::Module::Task::InvalidMetadata,
183
+ /Task metadata for task testmodule::foo does not specify implementations as an array/)
184
+ end
185
+
186
+ it 'raises and error when top-level `files` is not an array' do
187
+ metadata = { 'files' => 'foo' }
188
+ module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})
189
+
190
+ module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)
191
+ expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}
192
+ .to raise_error(Puppet::Module::Task::InvalidMetadata, /The 'files' task metadata expects an array, got foo/)
193
+ end
194
+
195
+ it 'raises and error when `files` nested in `interpreters` is not an array' do
196
+ metadata = { 'implementations' => [{'name' => 'foo.py', 'files' => 'foo'}] }
197
+ module_dir = dir_containing('testmodule', 'tasks' => {'foo.py' => '', 'foo.json' => metadata.to_json})
198
+
199
+ module_loader = Puppet::Pops::Loader::ModuleLoaders.module_loader_from(static_loader, loaders, 'testmodule', module_dir)
200
+ expect{module_loader.load_typed(typed_name(:task, 'testmodule::foo'))}
201
+ .to raise_error(Puppet::Module::Task::InvalidMetadata, /The 'files' task metadata expects an array, got foo/)
202
+ end
166
203
  end
167
204
 
168
205
  def typed_name(type, name)
@@ -1,7 +1,12 @@
1
1
  require 'spec_helper'
2
2
  require 'puppet/provider/exec'
3
+ require 'puppet_spec/compiler'
4
+ require 'puppet_spec/files'
3
5
 
4
6
  describe Puppet::Provider::Exec do
7
+ include PuppetSpec::Compiler
8
+ include PuppetSpec::Files
9
+
5
10
  describe "#extractexe" do
6
11
  it "should return the first element of an array" do
7
12
  expect(subject.extractexe(['one', 'two'])).to eq('one')
@@ -31,4 +36,208 @@ describe Puppet::Provider::Exec do
31
36
  end
32
37
  end
33
38
  end
39
+
40
+ context "when handling sensitive data" do
41
+ before :each do
42
+ Puppet[:log_level] = 'debug'
43
+ end
44
+
45
+ let(:supersecret) { 'supersecret' }
46
+ let(:path) do
47
+ if Puppet::Util::Platform.windows?
48
+ # The `apply_compiled_manifest` helper doesn't add the `path` fact, so
49
+ # we can't reference that in our manifest. Windows PATHs can contain
50
+ # double quotes and trailing backslashes, which confuse HEREDOC
51
+ # interpolation below. So sanitize it:
52
+ ENV['PATH'].split(File::PATH_SEPARATOR).map do |dir|
53
+ dir.gsub(/"/, '\"').gsub(/\\$/, '')
54
+ end.join(File::PATH_SEPARATOR)
55
+ else
56
+ ENV['PATH']
57
+ end
58
+ end
59
+
60
+ def ruby_exit_0
61
+ "ruby -e 'exit 0'"
62
+ end
63
+
64
+ def echo_from_ruby_exit_0(message)
65
+ # Escape double quotes due to HEREDOC interpolation below
66
+ "ruby -e 'puts \"#{message}\"; exit 0'".gsub(/"/, '\"')
67
+ end
68
+
69
+ def echo_from_ruby_exit_1(message)
70
+ # Escape double quotes due to HEREDOC interpolation below
71
+ "ruby -e 'puts \"#{message}\"; exit 1'".gsub(/"/, '\"')
72
+ end
73
+
74
+ context "when validating the command" do
75
+ it "redacts the arguments if the command is relative" do
76
+ expect {
77
+ apply_compiled_manifest(<<-MANIFEST)
78
+ exec { 'echo':
79
+ command => Sensitive.new('echo #{supersecret}')
80
+ }
81
+ MANIFEST
82
+ }.to raise_error do |err|
83
+ expect(err).to be_a(Puppet::Error)
84
+ expect(err.message).to match(/'echo' is not qualified and no path was specified. Please qualify the command or specify a path./)
85
+ expect(err.message).to_not match(/#{supersecret}/)
86
+ end
87
+ end
88
+
89
+ it "redacts the arguments if the command is a directory" do
90
+ dir = tmpdir('exec')
91
+ apply_compiled_manifest(<<-MANIFEST)
92
+ exec { 'echo':
93
+ command => Sensitive.new('#{dir} #{supersecret}'),
94
+ }
95
+ MANIFEST
96
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: /'#{dir}' is a directory, not a file/))
97
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
98
+ end
99
+
100
+ it "redacts the arguments if the command isn't executable" do
101
+ file = tmpfile('exec')
102
+ Puppet::FileSystem.touch(file)
103
+ Puppet::FileSystem.chmod(0644, file)
104
+
105
+ apply_compiled_manifest(<<-MANIFEST)
106
+ exec { 'echo':
107
+ command => Sensitive.new('#{file} #{supersecret}'),
108
+ }
109
+ MANIFEST
110
+ # Execute permission works differently on Windows, but execute will fail since the
111
+ # file doesn't have a valid extension and isn't a valid executable. The raised error
112
+ # will be Errno::EIO, which is not useful. The Windows execute code needs to raise
113
+ # Puppet::Util::Windows::Error so the Win32 error message is preserved.
114
+ pending("PUP-3561 Needs to raise a meaningful Puppet::Error") if Puppet::Util::Platform.windows?
115
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: /'#{file}' is not executable/))
116
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
117
+ end
118
+
119
+ it "redacts the arguments if the relative command cannot be resolved using the path parameter" do
120
+ file = File.basename(tmpfile('exec'))
121
+ dir = tmpdir('exec')
122
+
123
+ apply_compiled_manifest(<<-MANIFEST)
124
+ exec { 'echo':
125
+ command => Sensitive.new('#{file} #{supersecret}'),
126
+ path => "#{dir}",
127
+ }
128
+ MANIFEST
129
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: /Could not find command '#{file}'/))
130
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
131
+ end
132
+ end
133
+
134
+ it "redacts the command on success", unless: Puppet::Util::Platform.jruby? do
135
+ command = echo_from_ruby_exit_0(supersecret)
136
+
137
+ apply_compiled_manifest(<<-MANIFEST)
138
+ exec { 'true':
139
+ command => Sensitive.new("#{command}"),
140
+ path => "#{path}",
141
+ }
142
+ MANIFEST
143
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing '[redacted]'", source: /Exec\[true\]/))
144
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
145
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: "executed successfully"))
146
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
147
+ end
148
+
149
+ it "redacts the command on failure", unless: Puppet::Util::Platform.jruby? do
150
+ command = echo_from_ruby_exit_1(supersecret)
151
+
152
+ apply_compiled_manifest(<<-MANIFEST)
153
+ exec { 'false':
154
+ command => Sensitive.new("#{command}"),
155
+ path => "#{path}",
156
+ }
157
+ MANIFEST
158
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing '[redacted]'", source: /Exec\[false\]/))
159
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
160
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: "[command redacted] returned 1 instead of one of [0]"))
161
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
162
+ end
163
+
164
+ context "when handling checks", unless: Puppet::Util::Platform.jruby? do
165
+ let(:onlyifsecret) { "onlyifsecret" }
166
+ let(:unlesssecret) { "unlesssecret" }
167
+
168
+ it "redacts command and onlyif outputs" do
169
+ onlyif = echo_from_ruby_exit_0(onlyifsecret)
170
+
171
+ apply_compiled_manifest(<<-MANIFEST)
172
+ exec { 'true':
173
+ command => Sensitive.new("#{ruby_exit_0}"),
174
+ onlyif => Sensitive.new("#{onlyif}"),
175
+ path => "#{path}",
176
+ }
177
+ MANIFEST
178
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing check '[redacted]'"))
179
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing '[redacted]'", source: /Exec\[true\]/))
180
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
181
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "[output redacted]"))
182
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: "executed successfully"))
183
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{onlyifsecret}/))
184
+ end
185
+
186
+ it "redacts the command that would have been executed but didn't due to onlyif" do
187
+ command = echo_from_ruby_exit_0(supersecret)
188
+ onlyif = echo_from_ruby_exit_1(onlyifsecret)
189
+
190
+ apply_compiled_manifest(<<-MANIFEST)
191
+ exec { 'true':
192
+ command => Sensitive.new("#{command}"),
193
+ onlyif => Sensitive.new("#{onlyif}"),
194
+ path => "#{path}",
195
+ }
196
+ MANIFEST
197
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing check '[redacted]'"))
198
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
199
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "[output redacted]"))
200
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "'[command redacted]' won't be executed because of failed check 'onlyif'"))
201
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
202
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{onlyifsecret}/))
203
+ end
204
+
205
+ it "redacts command and unless outputs" do
206
+ unlesscmd = echo_from_ruby_exit_1(unlesssecret)
207
+
208
+ apply_compiled_manifest(<<-MANIFEST)
209
+ exec { 'true':
210
+ command => Sensitive.new("#{ruby_exit_0}"),
211
+ unless => Sensitive.new("#{unlesscmd}"),
212
+ path => "#{path}",
213
+ }
214
+ MANIFEST
215
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing check '[redacted]'"))
216
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing '[redacted]'", source: /Exec\[true\]/))
217
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
218
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "[output redacted]"))
219
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: "executed successfully"))
220
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{unlesssecret}/))
221
+ end
222
+
223
+ it "redacts the command that would have been executed but didn't due to unless" do
224
+ command = echo_from_ruby_exit_0(supersecret)
225
+ unlesscmd = echo_from_ruby_exit_0(unlesssecret)
226
+
227
+ apply_compiled_manifest(<<-MANIFEST)
228
+ exec { 'true':
229
+ command => Sensitive.new("#{command}"),
230
+ unless => Sensitive.new("#{unlesscmd}"),
231
+ path => "#{path}",
232
+ }
233
+ MANIFEST
234
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing check '[redacted]'"))
235
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Executing: '[redacted]'", source: "Puppet"))
236
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "[output redacted]"))
237
+ expect(@logs).to include(an_object_having_attributes(level: :debug, message: "'[command redacted]' won't be executed because of failed check 'unless'"))
238
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{supersecret}/))
239
+ expect(@logs).to_not include(an_object_having_attributes(message: /#{unlesssecret}/))
240
+ end
241
+ end
242
+ end
34
243
  end
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ describe Puppet::Type.type(:package).provider(:dnfmodule) do
4
+ include PuppetSpec::Fixtures
5
+
6
+ let(:dnf_version) do
7
+ <<-DNF_OUTPUT
8
+ 4.0.9
9
+ Installed: dnf-0:4.0.9.2-5.el8.noarch at Wed 29 May 2019 07:05:05 AM GMT
10
+ Built : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> at Thu 14 Feb 2019 12:04:07 PM GMT
11
+
12
+ Installed: rpm-0:4.14.2-9.el8.x86_64 at Wed 29 May 2019 07:04:33 AM GMT
13
+ Built : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> at Thu 20 Dec 2018 01:30:03 PM GMT
14
+ DNF_OUTPUT
15
+ end
16
+
17
+ let(:execute_options) do
18
+ {:failonfail => true, :combine => true, :custom_environment => {}}
19
+ end
20
+
21
+ let(:packages) { File.read(my_fixture("dnf-module-list-installed.txt")) }
22
+ let(:dnf_path) { '/usr/bin/dnf' }
23
+
24
+ before(:each) { allow(Puppet::Util).to receive(:which).with('/usr/bin/dnf').and_return(dnf_path) }
25
+
26
+ it "should have lower specificity" do
27
+ allow(Facter).to receive(:value).with(:osfamily).and_return(:redhat)
28
+ allow(Facter).to receive(:value).with(:operatingsystem).and_return(:redhat)
29
+ allow(Facter).to receive(:value).with(:operatingsystemmajrelease).and_return('8')
30
+ expect(described_class.specificity).to be < 200
31
+ end
32
+
33
+ describe "should be an opt-in provider" do
34
+ Array(4..8).each do |ver|
35
+ it "should not be default for redhat #{ver}" do
36
+ allow(Facter).to receive(:value).with(:operatingsystem).and_return('redhat')
37
+ allow(Facter).to receive(:value).with(:osfamily).and_return('redhat')
38
+ allow(Facter).to receive(:value).with(:operatingsystemmajrelease).and_return(ver.to_s)
39
+ expect(described_class).not_to be_default
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "handling dnf versions" do
45
+ before(:each) do
46
+ expect(Puppet::Type::Package::ProviderDnfmodule).to receive(:execute)
47
+ .with(["/usr/bin/dnf", "--version"])
48
+ .and_return(dnf_version).at_most(:once)
49
+ expect(Puppet::Util::Execution).to receive(:execute)
50
+ .with(["/usr/bin/dnf", "--version"], execute_options)
51
+ .and_return(Puppet::Util::Execution::ProcessOutput.new(dnf_version, 0))
52
+ end
53
+
54
+ describe "with a supported dnf version" do
55
+ it "correctly parses the version" do
56
+ expect(described_class.current_version).to eq('4.0.9')
57
+ end
58
+ end
59
+
60
+ describe "with an unsupported dnf version" do
61
+ let(:dnf_version) do
62
+ <<-DNF_OUTPUT
63
+ 2.7.5
64
+ Installed: dnf-0:2.7.5-12.fc28.noarch at Mon 13 Aug 2018 11:05:27 PM GMT
65
+ Built : Fedora Project at Wed 18 Apr 2018 02:29:51 PM GMT
66
+
67
+ Installed: rpm-0:4.14.1-7.fc28.x86_64 at Mon 13 Aug 2018 11:05:25 PM GMT
68
+ Built : Fedora Project at Mon 19 Feb 2018 09:29:01 AM GMT
69
+ DNF_OUTPUT
70
+ end
71
+
72
+ before(:each) { described_class.instance_variable_set("@current_version", nil) }
73
+
74
+ it "correctly parses the version" do
75
+ expect(described_class.current_version).to eq('2.7.5')
76
+ end
77
+
78
+ it "raises an error when attempting prefetch" do
79
+ expect { described_class.prefetch('anything') }.to raise_error(Puppet::Error, "Modules are not supported on DNF versions lower than 3.0.1")
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "when installing a module" do
85
+ let(:name) { 'baz' }
86
+
87
+ let(:resource) do
88
+ Puppet::Type.type(:package).new(
89
+ :name => name,
90
+ :provider => 'dnfmodule',
91
+ )
92
+ end
93
+
94
+ let(:provider) do
95
+ provider = described_class.new
96
+ provider.resource = resource
97
+ provider
98
+ end
99
+
100
+ describe 'provider features' do
101
+ it { is_expected.to be_versionable }
102
+ it { is_expected.to be_installable }
103
+ it { is_expected.to be_uninstallable }
104
+ end
105
+
106
+ context "when installing a new module" do
107
+ before do
108
+ provider.instance_variable_get('@property_hash')[:ensure] = :absent
109
+ end
110
+
111
+ it "should not reset the module stream when package is absent" do
112
+ resource[:ensure] = :present
113
+ expect(provider).not_to receive(:uninstall)
114
+ expect(provider).to receive(:execute)
115
+ provider.install
116
+ end
117
+
118
+ it "should not reset the module stream when package is purged" do
119
+ provider.instance_variable_get('@property_hash')[:ensure] = :purged
120
+ resource[:ensure] = :present
121
+ expect(provider).not_to receive(:uninstall)
122
+ expect(provider).to receive(:execute)
123
+ provider.install
124
+ end
125
+
126
+ it "should install the default stream and flavor" do
127
+ resource[:ensure] = :present
128
+ expect(provider).to receive(:execute).with(array_including('baz'))
129
+ provider.install
130
+ end
131
+
132
+ it "should install a specific stream" do
133
+ resource[:ensure] = '9.6'
134
+ expect(provider).to receive(:execute).with(array_including('baz:9.6'))
135
+ provider.install
136
+ end
137
+
138
+ it "should install a specific flavor" do
139
+ resource[:ensure] = :present
140
+ resource[:flavor] = 'minimal'
141
+ expect(provider).to receive(:execute).with(array_including('baz/minimal'))
142
+ provider.install
143
+ end
144
+
145
+ it "should install a specific flavor and stream" do
146
+ resource[:ensure] = '9.6'
147
+ resource[:flavor] = 'minimal'
148
+ expect(provider).to receive(:execute).with(array_including('baz:9.6/minimal'))
149
+ provider.install
150
+ end
151
+ end
152
+
153
+ context "when ensuring a specific version on top of another stream" do
154
+ before do
155
+ provider.instance_variable_get('@property_hash')[:ensure] = '9.6'
156
+ end
157
+
158
+ it "should remove existing packages and reset the module stream before installing" do
159
+ resource[:ensure] = '10'
160
+ expect(provider).to receive(:execute).thrice.with(array_including(/remove|reset|install/))
161
+ provider.install
162
+ end
163
+ end
164
+ end
165
+
166
+ context "parsing the output of module list --installed" do
167
+ before { allow(described_class).to receive(:command).with(:dnf).and_return(dnf_path) }
168
+
169
+ it "returns an array of installed modules" do
170
+ allow(Puppet::Util::Execution).to receive(:execute)
171
+ .with("/usr/bin/dnf module list --installed -d 0 -e 1")
172
+ .and_return(packages)
173
+
174
+ installed_packages = described_class.instances.map { |package| package.properties }
175
+ expected_packages = [{name: "gimp", ensure: "2.8", flavor: "devel", :provider => :dnfmodule},
176
+ {name: "mariadb", ensure: "10.3", flavor: "client", :provider => :dnfmodule},
177
+ {name: "nodejs", ensure: "10", flavor: "minimal", :provider => :dnfmodule},
178
+ {name: "perl", ensure: "5.26", flavor: "minimal", :provider => :dnfmodule},
179
+ {name: "postgresql", ensure: "10", flavor: "server", :provider => :dnfmodule},
180
+ {name: "rust-toolset", ensure: "rhel8", flavor: "common", :provider => :dnfmodule},
181
+ {name: "subversion", ensure: "1.10", flavor: "server", :provider => :dnfmodule}]
182
+
183
+ expect(installed_packages).to eql(expected_packages)
184
+ end
185
+ end
186
+ end