puppet 7.9.0 → 7.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (292) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +12 -12
  4. data/{ext → examples/enc}/regexp_nodes/classes/databases +0 -0
  5. data/{ext → examples/enc}/regexp_nodes/classes/webservers +0 -0
  6. data/{ext → examples/enc}/regexp_nodes/environment/development +0 -0
  7. data/{ext → examples/enc}/regexp_nodes/parameters/service/prod +0 -0
  8. data/{ext → examples/enc}/regexp_nodes/parameters/service/qa +0 -0
  9. data/{ext → examples/enc}/regexp_nodes/parameters/service/sandbox +0 -0
  10. data/{ext → examples/enc}/regexp_nodes/regexp_nodes.rb +0 -0
  11. data/{ext → examples}/nagios/check_puppet.rb +2 -2
  12. data/ext/README.md +13 -0
  13. data/lib/puppet/application/agent.rb +4 -0
  14. data/lib/puppet/application/apply.rb +20 -2
  15. data/lib/puppet/application/resource.rb +15 -13
  16. data/lib/puppet/concurrent/thread_local_singleton.rb +1 -0
  17. data/lib/puppet/configurer.rb +236 -58
  18. data/lib/puppet/confine/variable.rb +1 -1
  19. data/lib/puppet/defaults.rb +66 -29
  20. data/lib/puppet/environments.rb +66 -26
  21. data/lib/puppet/facter_impl.rb +96 -0
  22. data/lib/puppet/file_serving/configuration/parser.rb +2 -0
  23. data/lib/puppet/file_serving/configuration.rb +2 -0
  24. data/lib/puppet/file_serving/mount/file.rb +4 -4
  25. data/lib/puppet/file_serving/mount/scripts.rb +24 -0
  26. data/lib/puppet/file_system/file_impl.rb +3 -1
  27. data/lib/puppet/file_system.rb +2 -1
  28. data/lib/puppet/forge.rb +1 -1
  29. data/lib/puppet/functions/find_template.rb +2 -2
  30. data/lib/puppet/http/client.rb +1 -1
  31. data/lib/puppet/http/redirector.rb +5 -0
  32. data/lib/puppet/http/service/compiler.rb +6 -1
  33. data/lib/puppet/indirector/catalog/compiler.rb +24 -6
  34. data/lib/puppet/indirector/catalog/rest.rb +1 -0
  35. data/lib/puppet/indirector/facts/facter.rb +6 -6
  36. data/lib/puppet/indirector/indirection.rb +1 -1
  37. data/lib/puppet/indirector/terminus.rb +4 -0
  38. data/lib/puppet/module/plan.rb +0 -1
  39. data/lib/puppet/module/task.rb +1 -1
  40. data/lib/puppet/module_tool/applications/installer.rb +8 -4
  41. data/lib/puppet/module_tool/applications/uninstaller.rb +1 -1
  42. data/lib/puppet/module_tool/applications/upgrader.rb +1 -1
  43. data/lib/puppet/network/http/api/indirected_routes.rb +1 -1
  44. data/lib/puppet/node/environment.rb +10 -11
  45. data/lib/puppet/pal/pal_impl.rb +1 -1
  46. data/lib/puppet/parser/resource.rb +1 -1
  47. data/lib/puppet/parser/scope.rb +8 -7
  48. data/lib/puppet/parser/templatewrapper.rb +1 -0
  49. data/lib/puppet/pops/evaluator/closure.rb +7 -5
  50. data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +1 -0
  51. data/lib/puppet/pops/lookup/lookup_adapter.rb +3 -2
  52. data/lib/puppet/pops/model/ast.rb +1 -0
  53. data/lib/puppet/pops/model/factory.rb +14 -13
  54. data/lib/puppet/pops/parser/egrammar.ra +2 -2
  55. data/lib/puppet/pops/parser/eparser.rb +752 -753
  56. data/lib/puppet/pops/parser/lexer2.rb +69 -68
  57. data/lib/puppet/pops/parser/slurp_support.rb +1 -0
  58. data/lib/puppet/pops/serialization/to_data_converter.rb +18 -6
  59. data/lib/puppet/pops/serialization/to_stringified_converter.rb +1 -1
  60. data/lib/puppet/pops/types/type_formatter.rb +7 -6
  61. data/lib/puppet/pops/types/types.rb +1 -1
  62. data/lib/puppet/provider/aix_object.rb +1 -1
  63. data/lib/puppet/provider/group/groupadd.rb +5 -2
  64. data/lib/puppet/provider/package/pkg.rb +19 -2
  65. data/lib/puppet/provider/package/puppet_gem.rb +1 -1
  66. data/lib/puppet/provider/package/puppetserver_gem.rb +1 -1
  67. data/lib/puppet/provider/package/yum.rb +1 -1
  68. data/lib/puppet/provider/service/base.rb +1 -1
  69. data/lib/puppet/provider/service/init.rb +5 -5
  70. data/lib/puppet/provider/service/launchd.rb +2 -2
  71. data/lib/puppet/provider/service/redhat.rb +1 -1
  72. data/lib/puppet/provider/service/smf.rb +3 -3
  73. data/lib/puppet/provider/service/systemd.rb +2 -2
  74. data/lib/puppet/provider/service/upstart.rb +5 -5
  75. data/lib/puppet/provider/user/aix.rb +44 -1
  76. data/lib/puppet/provider/user/directoryservice.rb +1 -1
  77. data/lib/puppet/provider/user/useradd.rb +72 -16
  78. data/lib/puppet/provider.rb +1 -1
  79. data/lib/puppet/reference/providers.rb +2 -2
  80. data/lib/puppet/resource/catalog.rb +1 -1
  81. data/lib/puppet/resource/type_collection.rb +2 -1
  82. data/lib/puppet/resource.rb +38 -5
  83. data/lib/puppet/runtime.rb +11 -1
  84. data/lib/puppet/settings/file_setting.rb +3 -8
  85. data/lib/puppet/settings.rb +2 -2
  86. data/lib/puppet/test/test_helper.rb +4 -1
  87. data/lib/puppet/transaction/persistence.rb +11 -1
  88. data/lib/puppet/transaction/report.rb +15 -1
  89. data/lib/puppet/type/exec.rb +19 -2
  90. data/lib/puppet/type/file.rb +6 -6
  91. data/lib/puppet/type/filebucket.rb +2 -2
  92. data/lib/puppet/type/group.rb +0 -1
  93. data/lib/puppet/type/resources.rb +1 -1
  94. data/lib/puppet/type/service.rb +8 -3
  95. data/lib/puppet/type/user.rb +0 -1
  96. data/lib/puppet/type.rb +1 -1
  97. data/lib/puppet/util/autoload.rb +1 -1
  98. data/lib/puppet/util/command_line.rb +1 -1
  99. data/lib/puppet/util/filetype.rb +2 -2
  100. data/lib/puppet/util/json.rb +3 -0
  101. data/lib/puppet/util/log.rb +1 -2
  102. data/lib/puppet/util/logging.rb +1 -25
  103. data/lib/puppet/util/pidlock.rb +1 -1
  104. data/lib/puppet/util/rdoc/parser/puppet_parser_core.rb +1 -1
  105. data/lib/puppet/util/suidmanager.rb +1 -2
  106. data/lib/puppet/util/tagging.rb +1 -0
  107. data/lib/puppet/util/windows/service.rb +0 -5
  108. data/lib/puppet/util/windows/user.rb +0 -1
  109. data/lib/puppet/util/windows.rb +3 -0
  110. data/lib/puppet/util.rb +4 -3
  111. data/lib/puppet/version.rb +1 -1
  112. data/lib/puppet.rb +2 -6
  113. data/locales/puppet.pot +265 -221
  114. data/man/man5/puppet.conf.5 +73 -25
  115. data/man/man8/puppet-agent.8 +4 -1
  116. data/man/man8/puppet-apply.8 +1 -1
  117. data/man/man8/puppet-catalog.8 +1 -1
  118. data/man/man8/puppet-config.8 +1 -1
  119. data/man/man8/puppet-describe.8 +1 -1
  120. data/man/man8/puppet-device.8 +1 -1
  121. data/man/man8/puppet-doc.8 +1 -1
  122. data/man/man8/puppet-epp.8 +1 -1
  123. data/man/man8/puppet-facts.8 +1 -1
  124. data/man/man8/puppet-filebucket.8 +1 -1
  125. data/man/man8/puppet-generate.8 +1 -1
  126. data/man/man8/puppet-help.8 +1 -1
  127. data/man/man8/puppet-lookup.8 +1 -1
  128. data/man/man8/puppet-module.8 +3 -3
  129. data/man/man8/puppet-node.8 +1 -1
  130. data/man/man8/puppet-parser.8 +1 -1
  131. data/man/man8/puppet-plugin.8 +1 -1
  132. data/man/man8/puppet-report.8 +1 -1
  133. data/man/man8/puppet-resource.8 +1 -1
  134. data/man/man8/puppet-script.8 +1 -1
  135. data/man/man8/puppet-ssl.8 +1 -1
  136. data/man/man8/puppet.8 +2 -2
  137. data/spec/fixtures/integration/application/agent/cached_deferred_catalog.json +2 -1
  138. data/spec/fixtures/integration/application/agent/lib/facter/agent_spec_role.rb +3 -0
  139. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/Gemfile +4 -0
  140. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/Rakefile +3 -0
  141. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/lib/puppet/functions/l10n.rb +8 -0
  142. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/config.yaml +25 -0
  143. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/ja/puppet-l10n.po +19 -0
  144. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/locales/puppet-l10n.pot +20 -0
  145. data/spec/fixtures/integration/l10n/envs/prod/modules/demo/metadata.json +8 -0
  146. data/spec/integration/application/agent_spec.rb +146 -52
  147. data/spec/integration/application/filebucket_spec.rb +5 -0
  148. data/spec/integration/configurer_spec.rb +18 -2
  149. data/spec/integration/indirector/facts/facter_spec.rb +3 -3
  150. data/spec/integration/l10n/compiler_spec.rb +37 -0
  151. data/spec/integration/parser/pcore_resource_spec.rb +10 -0
  152. data/spec/integration/transaction/report_spec.rb +1 -1
  153. data/spec/integration/type/file_spec.rb +2 -2
  154. data/spec/integration/type/package_spec.rb +6 -6
  155. data/spec/integration/util/rdoc/parser_spec.rb +1 -1
  156. data/spec/integration/util/windows/process_spec.rb +1 -9
  157. data/spec/lib/puppet_spec/modules.rb +13 -2
  158. data/spec/lib/puppet_spec/puppetserver.rb +15 -0
  159. data/spec/shared_behaviours/documentation_on_faces.rb +0 -2
  160. data/spec/shared_contexts/l10n.rb +27 -0
  161. data/spec/spec_helper.rb +1 -10
  162. data/spec/unit/application/apply_spec.rb +76 -56
  163. data/spec/unit/application/resource_spec.rb +29 -0
  164. data/spec/unit/configurer_spec.rb +353 -57
  165. data/spec/unit/environments_spec.rb +150 -1
  166. data/spec/unit/facter_impl_spec.rb +31 -0
  167. data/spec/unit/file_bucket/dipper_spec.rb +2 -2
  168. data/spec/unit/file_serving/configuration/parser_spec.rb +23 -0
  169. data/spec/unit/file_serving/configuration_spec.rb +12 -4
  170. data/spec/unit/file_serving/mount/scripts_spec.rb +69 -0
  171. data/spec/unit/file_system_spec.rb +7 -0
  172. data/spec/unit/functions/logging_spec.rb +1 -0
  173. data/spec/unit/functions/lookup_spec.rb +64 -0
  174. data/spec/unit/http/client_spec.rb +58 -1
  175. data/spec/unit/http/service/compiler_spec.rb +8 -0
  176. data/spec/unit/indirector/catalog/compiler_spec.rb +87 -0
  177. data/spec/unit/indirector/catalog/rest_spec.rb +8 -0
  178. data/spec/unit/indirector/indirection_spec.rb +10 -3
  179. data/spec/unit/interface/action_spec.rb +0 -9
  180. data/spec/unit/module_spec.rb +14 -0
  181. data/spec/unit/module_tool/applications/installer_spec.rb +39 -12
  182. data/spec/unit/network/formats_spec.rb +6 -0
  183. data/spec/unit/pops/parser/parse_containers_spec.rb +0 -11
  184. data/spec/unit/pops/serialization/to_from_hr_spec.rb +58 -0
  185. data/spec/unit/pops/serialization/to_stringified_spec.rb +5 -0
  186. data/spec/unit/pops/types/type_calculator_spec.rb +6 -0
  187. data/spec/unit/provider/package/gem_spec.rb +1 -1
  188. data/spec/unit/provider/package/pip2_spec.rb +1 -1
  189. data/spec/unit/provider/package/pip3_spec.rb +1 -1
  190. data/spec/unit/provider/package/pip_spec.rb +1 -1
  191. data/spec/unit/provider/package/pkg_spec.rb +34 -5
  192. data/spec/unit/provider/package/puppet_gem_spec.rb +1 -1
  193. data/spec/unit/provider/package/puppetserver_gem_spec.rb +1 -1
  194. data/spec/unit/provider/service/launchd_spec.rb +11 -0
  195. data/spec/unit/provider/service/systemd_spec.rb +1 -1
  196. data/spec/unit/provider/user/aix_spec.rb +100 -0
  197. data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
  198. data/spec/unit/provider/user/useradd_spec.rb +43 -2
  199. data/spec/unit/provider_spec.rb +4 -4
  200. data/spec/unit/puppet_spec.rb +12 -4
  201. data/spec/unit/resource/catalog_spec.rb +14 -1
  202. data/spec/unit/resource_spec.rb +58 -2
  203. data/spec/unit/settings/file_setting_spec.rb +10 -7
  204. data/spec/unit/type/service_spec.rb +27 -0
  205. data/spec/unit/type_spec.rb +2 -2
  206. data/spec/unit/util/autoload_spec.rb +25 -8
  207. data/spec/unit/util/logging_spec.rb +2 -0
  208. data/tasks/parallel.rake +3 -3
  209. metadata +37 -94
  210. data/ext/README.environment +0 -8
  211. data/ext/dbfix.sql +0 -132
  212. data/ext/debian/README.Debian +0 -8
  213. data/ext/debian/README.source +0 -2
  214. data/ext/debian/TODO.Debian +0 -1
  215. data/ext/debian/changelog.erb +0 -1122
  216. data/ext/debian/compat +0 -1
  217. data/ext/debian/control +0 -144
  218. data/ext/debian/copyright +0 -339
  219. data/ext/debian/docs +0 -1
  220. data/ext/debian/fileserver.conf +0 -41
  221. data/ext/debian/puppet-common.dirs +0 -13
  222. data/ext/debian/puppet-common.install +0 -3
  223. data/ext/debian/puppet-common.lintian-overrides +0 -5
  224. data/ext/debian/puppet-common.manpages +0 -28
  225. data/ext/debian/puppet-common.postinst +0 -35
  226. data/ext/debian/puppet-common.postrm +0 -33
  227. data/ext/debian/puppet-el.dirs +0 -1
  228. data/ext/debian/puppet-el.emacsen-install +0 -25
  229. data/ext/debian/puppet-el.emacsen-remove +0 -11
  230. data/ext/debian/puppet-el.emacsen-startup +0 -9
  231. data/ext/debian/puppet-el.install +0 -1
  232. data/ext/debian/puppet-testsuite.install +0 -2
  233. data/ext/debian/puppet-testsuite.lintian-overrides +0 -4
  234. data/ext/debian/puppet.lintian-overrides +0 -3
  235. data/ext/debian/puppet.logrotate +0 -20
  236. data/ext/debian/puppet.postinst +0 -20
  237. data/ext/debian/puppet.postrm +0 -20
  238. data/ext/debian/puppet.preinst +0 -20
  239. data/ext/debian/puppetmaster-common.install +0 -2
  240. data/ext/debian/puppetmaster-common.manpages +0 -2
  241. data/ext/debian/puppetmaster-common.postinst +0 -6
  242. data/ext/debian/puppetmaster-passenger.dirs +0 -4
  243. data/ext/debian/puppetmaster-passenger.postinst +0 -162
  244. data/ext/debian/puppetmaster-passenger.postrm +0 -61
  245. data/ext/debian/puppetmaster.README.debian +0 -17
  246. data/ext/debian/puppetmaster.default +0 -14
  247. data/ext/debian/puppetmaster.init +0 -137
  248. data/ext/debian/puppetmaster.lintian-overrides +0 -3
  249. data/ext/debian/puppetmaster.postinst +0 -20
  250. data/ext/debian/puppetmaster.postrm +0 -5
  251. data/ext/debian/puppetmaster.preinst +0 -22
  252. data/ext/debian/rules +0 -132
  253. data/ext/debian/source/format +0 -1
  254. data/ext/debian/source/options +0 -1
  255. data/ext/debian/vim-puppet.README.Debian +0 -13
  256. data/ext/debian/vim-puppet.dirs +0 -5
  257. data/ext/debian/vim-puppet.yaml +0 -7
  258. data/ext/debian/watch +0 -2
  259. data/ext/freebsd/puppetd +0 -26
  260. data/ext/freebsd/puppetmasterd +0 -26
  261. data/ext/gentoo/conf.d/puppet +0 -5
  262. data/ext/gentoo/conf.d/puppetmaster +0 -12
  263. data/ext/gentoo/init.d/puppet +0 -38
  264. data/ext/gentoo/init.d/puppetmaster +0 -51
  265. data/ext/gentoo/puppet/fileserver.conf +0 -41
  266. data/ext/ips/puppet-agent +0 -44
  267. data/ext/ips/puppet-master +0 -44
  268. data/ext/ips/puppet.p5m.erb +0 -12
  269. data/ext/ips/puppetagent.xml +0 -42
  270. data/ext/ips/puppetmaster.xml +0 -42
  271. data/ext/ips/rules +0 -19
  272. data/ext/ips/transforms +0 -34
  273. data/ext/ldap/puppet.schema +0 -24
  274. data/ext/logcheck/puppet +0 -23
  275. data/ext/osx/file_mapping.yaml +0 -28
  276. data/ext/osx/postflight.erb +0 -109
  277. data/ext/osx/preflight.erb +0 -52
  278. data/ext/osx/prototype.plist.erb +0 -38
  279. data/ext/redhat/fileserver.conf +0 -41
  280. data/ext/redhat/logrotate +0 -21
  281. data/ext/redhat/puppet.spec.erb +0 -841
  282. data/ext/redhat/server.init +0 -128
  283. data/ext/redhat/server.sysconfig +0 -13
  284. data/ext/solaris/pkginfo +0 -6
  285. data/ext/solaris/smf/puppetd.xml +0 -77
  286. data/ext/solaris/smf/puppetmasterd.xml +0 -77
  287. data/ext/solaris/smf/svc-puppetd +0 -71
  288. data/ext/solaris/smf/svc-puppetmasterd +0 -67
  289. data/ext/suse/puppet.spec +0 -310
  290. data/ext/suse/server.init +0 -173
  291. data/ext/yaml_nodes.rb +0 -105
  292. data/spec/unit/indirector/store_configs_spec.rb +0 -7
@@ -33,6 +33,14 @@ describe Puppet::Resource::Catalog::Rest do
33
33
  described_class.indirection.find(certname, environment: Puppet::Node::Environment.remote('outerspace'))
34
34
  end
35
35
 
36
+ it "passes 'check_environment'" do
37
+ stub_request(:post, uri)
38
+ .with(body: hash_including('check_environment' => 'true'))
39
+ .to_return(**catalog_response(catalog))
40
+
41
+ described_class.indirection.find(certname, check_environment: true)
42
+ end
43
+
36
44
  it 'constructs a catalog environment_instance' do
37
45
  env = Puppet::Node::Environment.remote('outerspace')
38
46
  catalog = Puppet::Resource::Catalog.new(certname, env)
@@ -499,7 +499,7 @@ describe Puppet::Indirector::Indirection do
499
499
  end
500
500
 
501
501
  it "should return the result of saving to the terminus" do
502
- request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false)
502
+ request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false, :ignore_terminus? => false)
503
503
 
504
504
  expect(@indirection).to receive(:request).and_return(request)
505
505
 
@@ -509,7 +509,7 @@ describe Puppet::Indirector::Indirection do
509
509
  end
510
510
 
511
511
  it "should use a request to save the object to the cache" do
512
- request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false)
512
+ request = double('request', :instance => @instance, :node => nil, :ignore_cache_save? => false, :ignore_terminus? => false)
513
513
 
514
514
  expect(@indirection).to receive(:request).and_return(request)
515
515
 
@@ -519,7 +519,7 @@ describe Puppet::Indirector::Indirection do
519
519
  end
520
520
 
521
521
  it "should not save to the cache if the normal save fails" do
522
- request = double('request', :instance => @instance, :node => nil)
522
+ request = double('request', :instance => @instance, :node => nil, :ignore_terminus? => false)
523
523
 
524
524
  expect(@indirection).to receive(:request).and_return(request)
525
525
 
@@ -534,6 +534,13 @@ describe Puppet::Indirector::Indirection do
534
534
 
535
535
  @indirection.save(@instance, '/my/key', :ignore_cache_save => true)
536
536
  end
537
+
538
+ it "should only save to the cache if the request specifies not to use the terminus" do
539
+ expect(@terminus).not_to receive(:save)
540
+ expect(@cache).to receive(:save)
541
+
542
+ @indirection.save(@instance, "/my/key", :ignore_terminus => true)
543
+ end
537
544
  end
538
545
  end
539
546
 
@@ -537,15 +537,6 @@ describe Puppet::Interface::Action do
537
537
  end
538
538
  end
539
539
 
540
- context "#when_rendering" do
541
- it "should fail if no type is given when_rendering"
542
- it "should accept a when_rendering block"
543
- it "should accept multiple when_rendering blocks"
544
- it "should fail if when_rendering gets a non-symbol identifier"
545
- it "should fail if a second block is given for the same type"
546
- it "should return the block if asked"
547
- end
548
-
549
540
  context "#validate_and_clean" do
550
541
  subject do
551
542
  Puppet::Interface.new(:validate_args, '1.0.0') do
@@ -567,6 +567,20 @@ describe Puppet::Module do
567
567
  expect(mod.task_file(task_exe)).to eq("#{mod.path}/tasks/#{task_exe}")
568
568
  end
569
569
 
570
+ it "should list files from the scripts directory if required by the task" do
571
+ mod = 'loads_scripts'
572
+ task_dep = 'myscript.sh'
573
+ script_ref = "#{mod}/scripts/#{task_dep}"
574
+ task_json = JSON.generate({'files' => [script_ref]})
575
+ task = [['task', { name: 'task.json', content: task_json }]]
576
+ mod = PuppetSpec::Modules.create(mod, @modpath, {:environment => env,
577
+ :scripts => [task_dep],
578
+ :tasks => task})
579
+
580
+ expect(mod.tasks.first.files).to include({'name' => script_ref,
581
+ 'path' => /#{script_ref}/})
582
+ end
583
+
570
584
  it "should return nil when asked for an individual task file if it does not exist" do
571
585
  mod = PuppetSpec::Modules.create('task_file_neg', @modpath, {:environment => env,
572
586
  :tasks => []})
@@ -284,18 +284,45 @@ describe Puppet::ModuleTool::Applications::Installer, :unless => RUBY_PLATFORM =
284
284
  expect(subject).to include :result => :failure
285
285
  end
286
286
 
287
- it 'prints a detailed error containing the modules that would not be satisfied' do
288
- graph = double(SemanticPuppet::Dependency::Graph, :modules => ['pmtacceptance-mysql'])
289
- exception = SemanticPuppet::Dependency::UnsatisfiableGraph.new(graph)
290
- allow(exception).to receive(:respond_to?).and_return(true)
291
- allow(exception).to receive(:unsatisfied).and_return('pmtacceptance-mysql')
292
- allow(SemanticPuppet::Dependency).to receive(:resolve).and_raise(exception)
293
-
294
- expect(subject[:error]).to include(:multiline)
295
- expect(subject[:error][:multiline]).to include("Could not install module 'pmtacceptance-mysql' (> 1.0.0)")
296
- expect(subject[:error][:multiline]).to include("The requested version cannot satisfy one or more of the following installed modules:")
297
- expect(subject[:error][:multiline]).to include("pmtacceptance-keystone, expects 'pmtacceptance-mysql': >=0.6.1 <1.0.0")
298
- expect(subject[:error][:multiline]).to include("Use `puppet module install 'pmtacceptance-mysql' --ignore-dependencies` to install only this module")
287
+ context 'with unsatisfiable dependencies' do
288
+ let(:graph) { double(SemanticPuppet::Dependency::Graph, :modules => ['pmtacceptance-mysql']) }
289
+ let(:exception) { SemanticPuppet::Dependency::UnsatisfiableGraph.new(graph, constraint) }
290
+
291
+ before do
292
+ allow(SemanticPuppet::Dependency).to receive(:resolve).and_raise(exception)
293
+ end
294
+
295
+ context 'with known constraint' do
296
+ let(:constraint) { 'pmtacceptance-mysql' }
297
+
298
+ it 'prints a detailed error containing the modules that would not be satisfied' do
299
+ expect(subject[:error]).to include(:multiline)
300
+ expect(subject[:error][:multiline]).to include("Could not install module 'pmtacceptance-mysql' (> 1.0.0)")
301
+ expect(subject[:error][:multiline]).to include("The requested version cannot satisfy one or more of the following installed modules:")
302
+ expect(subject[:error][:multiline]).to include("pmtacceptance-keystone, expects 'pmtacceptance-mysql': >=0.6.1 <1.0.0")
303
+ expect(subject[:error][:multiline]).to include("Use `puppet module install 'pmtacceptance-mysql' --ignore-dependencies` to install only this module")
304
+ end
305
+ end
306
+
307
+ context 'with missing constraint' do
308
+ let(:constraint) { nil }
309
+
310
+ it 'prints the generic error message' do
311
+ expect(subject[:error]).to include(:multiline)
312
+ expect(subject[:error][:multiline]).to include("Could not install module 'pmtacceptance-mysql' (> 1.0.0)")
313
+ expect(subject[:error][:multiline]).to include("The requested version cannot satisfy all dependencies")
314
+ end
315
+ end
316
+
317
+ context 'with unknown constraint' do
318
+ let(:constraint) { 'another' }
319
+
320
+ it 'prints the generic error message' do
321
+ expect(subject[:error]).to include(:multiline)
322
+ expect(subject[:error][:multiline]).to include("Could not install module 'pmtacceptance-mysql' (> 1.0.0)")
323
+ expect(subject[:error][:multiline]).to include("The requested version cannot satisfy all dependencies")
324
+ end
325
+ end
299
326
  end
300
327
 
301
328
  context 'with --ignore-dependencies' do
@@ -368,6 +368,12 @@ describe "Puppet Network Format" do
368
368
  expect(json.render_multiple(instances)).to eq([{"string" => "foo"}].to_json)
369
369
  end
370
370
 
371
+ it "should render multiple instances as a JSON array of hashes when multi_json is not present" do
372
+ hide_const("MultiJson") if defined?(MultiJson)
373
+ instances = [FormatsTest.new("foo")]
374
+ expect(json.render_multiple(instances)).to eq([{"string" => "foo"}].to_json)
375
+ end
376
+
371
377
  it "should intern an instance from a JSON hash" do
372
378
  text = Puppet::Util::Json.dump({"string" => "parsed_json"})
373
379
  instance = json.intern(FormatsTest, text)
@@ -95,18 +95,7 @@ describe "egrammar parsing containers" do
95
95
  expect(dump(parse("class foo::default {}"))).to eq("(class foo::default ())")
96
96
  end
97
97
 
98
- it "class class inherits default {} # inherits default", :broken => true do
99
- expect {
100
- parse("class class inherits default {}")
101
- }.to raise_error(/not a valid classname/)
102
- end
103
-
104
98
  it "class class inherits default {} # inherits default" do
105
- # TODO: See previous test marked as :broken=>true, it is actually this test (result) that is wacky,
106
- # this because a class is named at parse time (since class evaluation is lazy, the model must have the
107
- # full class name for nested classes - only, it gets this wrong when a class is named "class" - or at least
108
- # I think it is wrong.)
109
- #
110
99
  expect { parse("class class inherits default {}") }.to raise_error(/'class' keyword not allowed at this location/)
111
100
  end
112
101
 
@@ -559,6 +559,29 @@ module Serialization
559
559
  expect(warnings).to eql(["['key'] contains the special value default. It will be converted to the String 'default'"])
560
560
  end
561
561
  end
562
+ context 'and force_symbol set to true' do
563
+ let(:to_converter) { ToDataConverter.new(:rich_data => false, :force_symbol => true) }
564
+
565
+ it 'A Hash with Symbol values is converted to hash with Symbol values' do
566
+ val = { 'one' => :one, 'two' => :two }
567
+ Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
568
+
569
+ # write and read methods does not work here as we cannot force Symbols in Json.
570
+ # and a hash with symbol values cannot be an instance of Types::TypeFactory.data.
571
+ # Using YAML for this instead
572
+ io.reopen
573
+ value = to_converter.convert(val)
574
+ io << [value].to_yaml
575
+ io.rewind
576
+
577
+ val2 = from_converter.convert(YAML::load(io.read)[0])
578
+
579
+ expect(val2).to be_a(Hash)
580
+ expect(val2).to eql({ 'one' => :one, 'two' => :two })
581
+ end
582
+ expect(warnings).to be_empty
583
+ end
584
+ end
562
585
  end
563
586
 
564
587
  context 'with rich_data is set to true' do
@@ -632,6 +655,41 @@ module Serialization
632
655
  end.to raise_error(/Cannot create a Pcore::TimestampType from a Integer/)
633
656
  end
634
657
  end
658
+
659
+ context 'when data is unknown' do
660
+ let(:to_converter) { ToDataConverter.new(:message_prefix => 'Test Hash') }
661
+ let(:logs) { [] }
662
+ let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }
663
+ let(:val) { Class.new }
664
+
665
+ context 'and :silence_warnings undefined or set to false' do
666
+ it 'convert the unknown data to string with warnings' do
667
+ Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
668
+ write(val)
669
+ val2 = read
670
+ expect(val2).to be_a(String)
671
+ expect(val2).to match(/Class/)
672
+ end
673
+ expect(warnings).to eql([
674
+ "Test Hash contains a #{val.class} value. It will be converted to the String '#{val.to_s}'"])
675
+ end
676
+ end
677
+
678
+ context 'and :silence_warnings undefined or set to true' do
679
+ let(:to_converter) { ToDataConverter.new(:message_prefix => 'Test Hash', :silence_warnings => true) }
680
+
681
+ it 'convert the unknown data to string without warnings if silence_warnings set to true' do
682
+ val = Class.new
683
+ Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
684
+ write(val)
685
+ val2 = read
686
+ expect(val2).to be_a(String)
687
+ expect(val2).to match(/Class/)
688
+ end
689
+ expect(warnings).to be_empty
690
+ end
691
+ end
692
+ end
635
693
  end
636
694
  end
637
695
  end
@@ -154,4 +154,9 @@ describe 'ToStringifiedConverter' do
154
154
  unassigned = [243, 176, 128, 128].pack("C*").force_encoding(Encoding::UTF_8)
155
155
  expect(transform(unassigned)).to eq("󰀀")
156
156
  end
157
+
158
+ it 'converts ProcessOutput objects to string' do
159
+ object = Puppet::Util::Execution::ProcessOutput.new('object', 0)
160
+ expect(transform(object)).to be_instance_of(String)
161
+ end
157
162
  end
@@ -1980,6 +1980,12 @@ describe 'The type calculator' do
1980
1980
  expect(calculator.instance?(tuple, [1, 'a', 1])).to eq(false)
1981
1981
  end
1982
1982
 
1983
+ it 'should not consider ProcessOutput objects as instanceof PScalarDataType' do
1984
+ object = Puppet::Util::Execution::ProcessOutput.new('object', 0)
1985
+
1986
+ expect(calculator.instance?(PScalarDataType::DEFAULT, object)).to eq(false)
1987
+ end
1988
+
1983
1989
  context 'and t is Struct' do
1984
1990
  it 'should consider hash[cont] as instance of Struct[cont-t]' do
1985
1991
  struct = struct_t({'a'=>Integer, 'b'=>String, 'c'=>Float})
@@ -476,7 +476,7 @@ context Puppet::Type.type(:package).provider(:gem) do
476
476
  end
477
477
 
478
478
  context 'when is defaultfor' do
479
- let(:os) { Facter.value(:operatingsystem) }
479
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
480
480
  subject do
481
481
  described_class.defaultfor(operatingsystem: os)
482
482
  described_class.specificity
@@ -26,7 +26,7 @@ describe Puppet::Type.type(:package).provider(:pip2) do
26
26
  end
27
27
 
28
28
  context 'when is defaultfor' do
29
- let(:os) { Facter.value(:operatingsystem) }
29
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
30
30
  subject do
31
31
  described_class.defaultfor(operatingsystem: os)
32
32
  described_class.specificity
@@ -26,7 +26,7 @@ describe Puppet::Type.type(:package).provider(:pip3) do
26
26
  end
27
27
 
28
28
  context 'when is defaultfor' do
29
- let(:os) { Facter.value(:operatingsystem) }
29
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
30
30
  subject do
31
31
  described_class.defaultfor(operatingsystem: os)
32
32
  described_class.specificity
@@ -493,7 +493,7 @@ describe Puppet::Type.type(:package).provider(:pip) do
493
493
  end
494
494
 
495
495
  context 'when is defaultfor' do
496
- let(:os) { Facter.value(:operatingsystem) }
496
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
497
497
  subject do
498
498
  described_class.defaultfor(operatingsystem: os)
499
499
  described_class.specificity
@@ -342,14 +342,17 @@ describe Puppet::Type.type(:package).provider(:pkg), unless: Puppet::Util::Platf
342
342
  resource[:ensure] = '1.0-0.151006'
343
343
  is = :absent
344
344
  expect(provider).to receive(:query).with(no_args).and_return({:ensure => is})
345
- expect(provider).to receive(:properties).and_return({:mark => :hold})
345
+ expect(provider).to receive(:properties).and_return({:mark => :hold}).exactly(3).times
346
+
347
+ expect(described_class).to receive(:pkg)
348
+ .with(:freeze, 'dummy')
346
349
  expect(described_class).to receive(:pkg)
347
350
  .with(:list, '-Hvfa', 'dummy@1.0-0.151006')
348
351
  .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))
349
352
  expect(Puppet::Util::Execution).to receive(:execute)
350
353
  .with(['/bin/pkg', 'install', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
351
354
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
352
- expect(provider).to receive(:unhold).with(no_args)
355
+ expect(provider).to receive(:unhold).with(no_args).twice
353
356
  expect(Puppet::Util::Execution).to receive(:execute)
354
357
  .with(['/bin/pkg', 'install', *hash[:flags], 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
355
358
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
@@ -361,12 +364,17 @@ describe Puppet::Type.type(:package).provider(:pkg), unless: Puppet::Util::Platf
361
364
  resource[:ensure] = '1.0-0.151006'
362
365
  is = '1.0,5.11-0.151006:20140219T191204Z'
363
366
  expect(provider).to receive(:query).with(no_args).and_return({:ensure => is})
364
- expect(provider).to receive(:properties).and_return({:mark => :hold})
365
- expect(described_class).to receive(:pkg).with(:list, '-Hvfa', 'dummy@1.0-0.151006').and_return(File.read(my_fixture('dummy_implicit_version')))
367
+ expect(provider).to receive(:properties).and_return({:mark => :hold}).exactly(3).times
368
+
369
+ expect(described_class).to receive(:pkg)
370
+ .with(:freeze, 'dummy')
371
+ expect(described_class).to receive(:pkg)
372
+ .with(:list, '-Hvfa', 'dummy@1.0-0.151006')
373
+ .and_return(File.read(my_fixture('dummy_implicit_version')))
366
374
  expect(Puppet::Util::Execution).to receive(:execute)
367
375
  .with(['/bin/pkg', 'update', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
368
376
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
369
- expect(provider).to receive(:unhold).with(no_args)
377
+ expect(provider).to receive(:unhold).with(no_args).twice
370
378
  expect(Puppet::Util::Execution).to receive(:execute)
371
379
  .with(['/bin/pkg', 'update', *hash[:flags], 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
372
380
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
@@ -381,6 +389,9 @@ describe Puppet::Type.type(:package).provider(:pkg), unless: Puppet::Util::Platf
381
389
  expect(described_class).to receive(:pkg)
382
390
  .with(:list, '-Hvfa', 'dummy@1.0-0.151006')
383
391
  .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))
392
+ expect(Puppet::Util::Execution).to receive(:execute)
393
+ .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})
394
+ .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))
384
395
  expect(Puppet::Util::Execution).to receive(:execute)
385
396
  .with(['/bin/pkg', 'update', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
386
397
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 4))
@@ -395,11 +406,29 @@ describe Puppet::Type.type(:package).provider(:pkg), unless: Puppet::Util::Platf
395
406
  expect(described_class).to receive(:pkg)
396
407
  .with(:list, '-Hvfa', 'dummy@1.0-0.151006')
397
408
  .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))
409
+ expect(Puppet::Util::Execution).to receive(:execute)
410
+ .with(['/bin/pkg', 'list', '-Hv', 'dummy'], {:failonfail => false, :combine => true})
411
+ .and_return(Puppet::Util::Execution::ProcessOutput.new(File.read(my_fixture('dummy_implicit_version')), 0))
398
412
  expect(Puppet::Util::Execution).to receive(:execute)
399
413
  .with(['/bin/pkg', 'install', '-n', 'dummy@1.0,5.11-0.151006:20140220T084443Z'], {:failonfail => false, :combine => true})
400
414
  .and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
401
415
  provider.insync?(is)
402
416
  end
417
+
418
+ it "should try 5 times to install and fail when all tries failed" do
419
+ allow_any_instance_of(Kernel).to receive(:sleep)
420
+
421
+ expect(provider).to receive(:query).and_return({:ensure => :absent})
422
+ expect(provider).to receive(:properties).and_return({:mark => :hold})
423
+ expect(provider).to receive(:unhold)
424
+ expect(Puppet::Util::Execution).to receive(:execute)
425
+ .with(['/bin/pkg', 'install', *hash[:flags], 'dummy'], {:failonfail => false, :combine => true})
426
+ .and_return(Puppet::Util::Execution::ProcessOutput.new('', 7))
427
+ .exactly(5).times
428
+ expect {
429
+ provider.update
430
+ }.to raise_error(Puppet::Error, /Pkg could not install dummy after 5 tries. Aborting run/)
431
+ end
403
432
  end
404
433
  end
405
434
  end
@@ -118,7 +118,7 @@ describe Puppet::Type.type(:package).provider(:puppet_gem) do
118
118
  end
119
119
 
120
120
  context 'when is defaultfor' do
121
- let(:os) { Facter.value(:operatingsystem) }
121
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
122
122
  subject do
123
123
  described_class.defaultfor(operatingsystem: os)
124
124
  described_class.specificity
@@ -127,7 +127,7 @@ describe Puppet::Type.type(:package).provider(:puppetserver_gem) do
127
127
  end
128
128
 
129
129
  context 'when is defaultfor' do
130
- let(:os) { Facter.value(:operatingsystem) }
130
+ let(:os) { Puppet.runtime[:facter].value(:operatingsystem) }
131
131
  subject do
132
132
  described_class.defaultfor(operatingsystem: os)
133
133
  described_class.specificity
@@ -325,6 +325,7 @@ describe 'Puppet::Type::Service::Provider::Launchd',
325
325
  'LimitLoadToSessionType' => 'Aqua'
326
326
  }
327
327
  end
328
+ let(:plist_without_label_not_hash) { 'just a string' }
328
329
  let(:busted_plist_path) { '/Library/LaunchAgents/org.busted.plist' }
329
330
  let(:binary_plist_path) { '/Library/LaunchAgents/org.binary.plist' }
330
331
 
@@ -336,6 +337,16 @@ describe 'Puppet::Type::Service::Provider::Launchd',
336
337
  expect(Puppet).to receive(:debug).with("The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it")
337
338
  provider.make_label_to_path_map
338
339
  end
340
+
341
+ it "it should warn that the malformed plist in question is being skipped" do
342
+ expect(provider).to receive(:launchd_paths).and_return(['/Library/LaunchAgents'])
343
+ expect(provider).to receive(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').and_return([busted_plist_path])
344
+ expect(plistlib).to receive(:read_plist_file).with(busted_plist_path).and_return(plist_without_label_not_hash)
345
+ expect(Puppet).to receive(:debug).with("Reading launchd plist #{busted_plist_path}")
346
+ expect(Puppet).to receive(:debug).with("The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it")
347
+ provider.make_label_to_path_map
348
+ end
349
+
339
350
  end
340
351
 
341
352
  it "should return the cached value when available" do
@@ -29,7 +29,7 @@ describe 'Puppet::Type::Service::Provider::Systemd',
29
29
  end
30
30
  end
31
31
 
32
- [7, 8].each do |ver|
32
+ [7, 8, 9].each do |ver|
33
33
  it "should be the default provider on rhel#{ver}" do
34
34
  allow(Facter).to receive(:value).with(:osfamily).and_return(:redhat)
35
35
  allow(Facter).to receive(:value).with(:operatingsystem).and_return(:redhat)
@@ -217,4 +217,104 @@ describe 'Puppet::Type::User::Provider::Aix' do
217
217
  provider.create
218
218
  end
219
219
  end
220
+
221
+ describe '#list_all_homes' do
222
+ it "should return empty array and output debug on failure" do
223
+ allow(Puppet::Util::Execution).to receive(:execute).and_raise(Puppet::ExecutionFailure, 'Execution failed')
224
+ expect(Puppet).to receive(:debug).with('Could not list home of all users: Execution failed')
225
+ expect(provider.list_all_homes).to eql({})
226
+ end
227
+ end
228
+
229
+ describe '#delete' do
230
+ before(:each) do
231
+ allow(File).to receive(:realpath).and_call_original
232
+ allow(FileUtils).to receive(:remove_entry_secure).and_call_original
233
+
234
+ allow(provider.resource).to receive(:should).with(anything).and_return(nil)
235
+ allow(provider).to receive(:home).and_return(Dir.tmpdir)
236
+ allow(provider).to receive(:execute).and_return(nil)
237
+ allow(provider).to receive(:object_info).and_return(nil)
238
+ allow(FileUtils).to receive(:remove_entry_secure).with(Dir.tmpdir, true).and_return(nil)
239
+ end
240
+
241
+ context 'with managehome true' do
242
+ before(:each) do
243
+ allow(provider.resource).to receive(:managehome?).and_return(true)
244
+ allow(provider).to receive(:list_all_homes).and_return([])
245
+ end
246
+
247
+ it 'should delete the user without error' do
248
+ expect{ provider.delete }.not_to raise_error
249
+ end
250
+
251
+ it "should not remove home when relative" do
252
+ allow(provider).to receive(:home).and_return('relative_path')
253
+
254
+ expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\/'./)
255
+ provider.delete
256
+ end
257
+
258
+ it "should not remove home when '/'" do
259
+ allow(provider).to receive(:home).and_return('/')
260
+
261
+ expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\/'./)
262
+ provider.delete
263
+ end
264
+
265
+ it "should not remove home when symlink" do
266
+ allow(Puppet::FileSystem).to receive(:symlink?).with(Dir.tmpdir).and_return(true)
267
+
268
+ expect(Puppet).to receive(:debug).with(/Please make sure the path is not relative, symlink or '\/'./)
269
+ provider.delete
270
+ end
271
+
272
+ it "should not remove home when other users would be affected" do
273
+ allow(provider).to receive(:home).and_return('/special')
274
+ allow(File).to receive(:realpath).with('/special').and_return('/special')
275
+ allow(Puppet::Util).to receive(:absolute_path?).with('/special').and_return(true)
276
+ allow(provider).to receive(:list_all_homes).and_return([{:name => 'other_user', :home => '/special/other_user'}])
277
+
278
+ expect(Puppet).to receive(:debug).with(/it would remove the home directory '\/special\/other_user' of user 'other_user' also./)
279
+ provider.delete
280
+ end
281
+
282
+ it 'should remove homedir' do
283
+ expect(FileUtils).to receive(:remove_entry_secure).with(Dir.tmpdir, true)
284
+ provider.delete
285
+ end
286
+ end
287
+
288
+ context 'with managehome false' do
289
+ before(:each) do
290
+ allow(provider.resource).to receive(:managehome?).and_return(false)
291
+ end
292
+
293
+ it 'should delete the user without error' do
294
+ expect{ provider.delete }.not_to raise_error
295
+ end
296
+
297
+ it 'should not remove homedir' do
298
+ expect(FileUtils).not_to receive(:remove_entry_secure).with(Dir.tmpdir, true)
299
+ end
300
+
301
+ it 'should not print manage home debug messages' do
302
+ expect(Puppet).not_to receive(:debug).with(/Please make sure the path is not relative, symlink or '\/'./)
303
+ expect(Puppet).not_to receive(:debug).with(/it would remove the home directory '\/special\/other_user' of user 'other_user' also./)
304
+
305
+ provider.delete
306
+ end
307
+ end
308
+ end
309
+
310
+ describe '#deletecmd' do
311
+ it 'uses the -p flag when removing the user' do
312
+ allow(provider.class).to receive(:command).with(:delete).and_return('delete')
313
+ allow(provider).to receive(:ia_module_args).and_return(['ia_module_args'])
314
+
315
+ expect(provider.deletecmd).to eql(
316
+ ['delete', '-p', 'ia_module_args', provider.resource.name]
317
+ )
318
+ end
319
+ end
220
320
  end
@@ -1142,7 +1142,7 @@ end
1142
1142
  provider.class.instance_variable_set(:@os_version, nil) if provider.class.instance_variable_defined? :@os_version
1143
1143
  end
1144
1144
 
1145
- it 'should call Facter.value(:macosx_productversion_major) ONLY ONCE no matter how ' +
1145
+ it 'should call Puppet.runtime[:facter].value(:macosx_productversion_major) ONLY ONCE no matter how ' +
1146
1146
  'many times get_os_version() is called' do
1147
1147
  expect(Facter).to receive(:value).with(:macosx_productversion_major).once.and_return('10.8')
1148
1148
  expect(provider.class.get_os_version).to eq('10.8')