puppet 7.16.0 → 7.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +66 -5
  3. data/ext/systemd/puppet.service +1 -1
  4. data/lib/puppet/agent.rb +20 -2
  5. data/lib/puppet/application/agent.rb +3 -13
  6. data/lib/puppet/application/apply.rb +2 -2
  7. data/lib/puppet/configurer.rb +1 -1
  8. data/lib/puppet/defaults.rb +11 -1
  9. data/lib/puppet/http/client.rb +22 -2
  10. data/lib/puppet/parameter.rb +19 -4
  11. data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
  12. data/lib/puppet/pops/functions/dispatcher.rb +10 -6
  13. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
  14. data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
  15. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  16. data/lib/puppet/provider/package/yum.rb +8 -3
  17. data/lib/puppet/provider/user/directoryservice.rb +15 -8
  18. data/lib/puppet/ssl/ssl_provider.rb +65 -12
  19. data/lib/puppet/ssl/state_machine.rb +13 -17
  20. data/lib/puppet/transaction.rb +22 -0
  21. data/lib/puppet/type/user.rb +3 -0
  22. data/lib/puppet/type.rb +20 -3
  23. data/lib/puppet/version.rb +1 -1
  24. data/lib/puppet.rb +1 -14
  25. data/man/man5/puppet.conf.5 +11 -3
  26. data/man/man8/puppet-agent.8 +2 -2
  27. data/man/man8/puppet-apply.8 +1 -1
  28. data/man/man8/puppet-catalog.8 +1 -1
  29. data/man/man8/puppet-config.8 +1 -1
  30. data/man/man8/puppet-describe.8 +1 -1
  31. data/man/man8/puppet-device.8 +1 -1
  32. data/man/man8/puppet-doc.8 +1 -1
  33. data/man/man8/puppet-epp.8 +1 -1
  34. data/man/man8/puppet-facts.8 +1 -1
  35. data/man/man8/puppet-filebucket.8 +1 -1
  36. data/man/man8/puppet-generate.8 +1 -1
  37. data/man/man8/puppet-help.8 +1 -1
  38. data/man/man8/puppet-lookup.8 +1 -1
  39. data/man/man8/puppet-module.8 +1 -1
  40. data/man/man8/puppet-node.8 +1 -1
  41. data/man/man8/puppet-parser.8 +1 -1
  42. data/man/man8/puppet-plugin.8 +1 -1
  43. data/man/man8/puppet-report.8 +1 -1
  44. data/man/man8/puppet-resource.8 +1 -1
  45. data/man/man8/puppet-script.8 +1 -1
  46. data/man/man8/puppet-ssl.8 +1 -1
  47. data/man/man8/puppet.8 +2 -2
  48. data/spec/integration/application/agent_spec.rb +157 -0
  49. data/spec/integration/application/apply_spec.rb +74 -0
  50. data/spec/integration/http/client_spec.rb +27 -10
  51. data/spec/lib/puppet_spec/https.rb +1 -1
  52. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  53. data/spec/unit/agent_spec.rb +6 -2
  54. data/spec/unit/application/agent_spec.rb +26 -16
  55. data/spec/unit/daemon_spec.rb +2 -11
  56. data/spec/unit/http/client_spec.rb +18 -0
  57. data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
  58. data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
  59. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
  60. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  61. data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
  62. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  63. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  64. data/tasks/generate_cert_fixtures.rake +5 -4
  65. metadata +2 -2
@@ -17,4 +17,30 @@ describe Puppet::Pops::Evaluator::DeferredResolver do
17
17
 
18
18
  expect(catalog.resource(:notify, 'deferred')[:message]).to eq('1:2:3')
19
19
  end
20
+
21
+ it 'lazily resolves deferred values in a catalog' do
22
+ catalog = compile_to_catalog(<<~END)
23
+ notify { "deferred":
24
+ message => Deferred("join", [[1,2,3], ":"])
25
+ }
26
+ END
27
+ described_class.resolve_and_replace(facts, catalog, environment, false)
28
+
29
+ deferred = catalog.resource(:notify, 'deferred')[:message]
30
+ expect(deferred.resolve).to eq('1:2:3')
31
+ end
32
+
33
+ it 'lazily resolves nested deferred values in a catalog' do
34
+ catalog = compile_to_catalog(<<~END)
35
+ $args = Deferred("inline_epp", ["<%= 'a,b,c' %>"])
36
+ notify { "deferred":
37
+ message => Deferred("split", [$args, ","])
38
+ }
39
+ END
40
+ described_class.resolve_and_replace(facts, catalog, environment, false)
41
+
42
+ deferred = catalog.resource(:notify, 'deferred')[:message]
43
+ expect(deferred.resolve).to eq(["a", "b", "c"])
44
+ end
45
+
20
46
  end
@@ -606,7 +606,7 @@ describe 'loaders' do
606
606
  it "an illegal function is loaded" do
607
607
  expect {
608
608
  loader.load_typed(typed_name(:function, 'bad_func_load3')).value
609
- }.to raise_error(SecurityError, /Illegal method definition of method 'bad_func_load3_illegal_method' on line 8 in legacy function/)
609
+ }.to raise_error(SecurityError, /Illegal method definition of method 'bad_func_load3_illegal_method' in source .*bad_func_load3.rb on line 8 in legacy function/)
610
610
  end
611
611
  end
612
612
 
@@ -1,12 +1,178 @@
1
1
  require 'spec_helper'
2
2
  require 'puppet/pops'
3
3
  require 'puppet_spec/compiler'
4
+ require 'puppet_spec/files'
5
+ require 'puppet/loaders'
4
6
 
5
7
  module Puppet::Pops
6
8
  module Types
7
9
 
8
10
  describe 'the type mismatch describer' do
9
- include PuppetSpec::Compiler
11
+ include PuppetSpec::Compiler, PuppetSpec::Files
12
+
13
+ context 'with deferred functions' do
14
+ let(:env_name) { 'spec' }
15
+ let(:code_dir) { Puppet[:environmentpath] }
16
+ let(:env_dir) { File.join(code_dir, env_name) }
17
+ let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_code_dir, env_name, 'modules')]) }
18
+ let(:node) { Puppet::Node.new('fooname', environment: env) }
19
+ let(:populated_code_dir) do
20
+ dir_contained_in(code_dir, env_name => env_content)
21
+ PuppetSpec::Files.record_tmp(env_dir)
22
+ code_dir
23
+ end
24
+
25
+ let(:env_content) {
26
+ {
27
+ 'lib' => {
28
+ 'puppet' => {
29
+ 'functions' => {
30
+ 'string_return.rb' => <<-RUBY.unindent,
31
+ Puppet::Functions.create_function(:string_return) do
32
+ dispatch :string_return do
33
+ param 'String', :arg1
34
+ return_type 'String'
35
+ end
36
+ def string_return(arg1)
37
+ arg1
38
+ end
39
+ end
40
+ RUBY
41
+ 'variant_return.rb' => <<-RUBY.unindent,
42
+ Puppet::Functions.create_function(:variant_return) do
43
+ dispatch :variant_return do
44
+ param 'String', :arg1
45
+ return_type 'Variant[Integer,Float]'
46
+ end
47
+ def variant_return(arg1)
48
+ arg1
49
+ end
50
+ end
51
+ RUBY
52
+ 'no_return.rb' => <<-RUBY.unindent,
53
+ Puppet::Functions.create_function(:no_return) do
54
+ dispatch :no_return do
55
+ param 'String', :arg1
56
+ end
57
+ def variant_return(arg1)
58
+ arg1
59
+ end
60
+ end
61
+ RUBY
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ before(:each) do
69
+ Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))
70
+ end
71
+
72
+ after(:each) do
73
+ Puppet.pop_context
74
+ end
75
+
76
+ it 'will compile when the parameter type matches the function return_type' do
77
+ code = <<-CODE
78
+ $d = Deferred("string_return", ['/a/non/existing/path'])
79
+ class testclass(String $classparam) {
80
+ }
81
+ class { 'testclass':
82
+ classparam => $d
83
+ }
84
+ CODE
85
+ expect { eval_and_collect_notices(code, node) }.to_not raise_error
86
+ end
87
+
88
+ it "will compile when a Variant parameter's types matches the return type" do
89
+ code = <<-CODE
90
+ $d = Deferred("string_return", ['/a/non/existing/path'])
91
+ class testclass(Variant[String, Float] $classparam) {
92
+ }
93
+ class { 'testclass':
94
+ classparam => $d
95
+ }
96
+ CODE
97
+ expect { eval_and_collect_notices(code, node) }.to_not raise_error
98
+ end
99
+
100
+ it "will compile with a union of a Variant parameters' types and Variant return types" do
101
+ code = <<-CODE
102
+ $d = Deferred("variant_return", ['/a/non/existing/path'])
103
+ class testclass(Variant[Any,Float] $classparam) {
104
+ }
105
+ class { 'testclass':
106
+ classparam => $d
107
+ }
108
+ CODE
109
+ expect { eval_and_collect_notices(code, node) }.to_not raise_error
110
+ end
111
+
112
+ it 'will warn when there is no defined return_type for the function definition' do
113
+ code = <<-CODE
114
+ $d = Deferred("no_return", ['/a/non/existing/path'])
115
+ class testclass(Variant[String,Boolean] $classparam) {
116
+ }
117
+ class { 'testclass':
118
+ classparam => $d
119
+ }
120
+ CODE
121
+ expect(Puppet).to receive(:warn_once).with(anything, anything, /.+function no_return has no return_type/).at_least(:once)
122
+ expect { eval_and_collect_notices(code, node) }.to_not raise_error
123
+ end
124
+
125
+ it 'will report a mismatch between a deferred function return type and class parameter value' do
126
+ code = <<-CODE
127
+ $d = Deferred("string_return", ['/a/non/existing/path'])
128
+ class testclass(Integer $classparam) {
129
+ }
130
+ class { 'testclass':
131
+ classparam => $d
132
+ }
133
+ CODE
134
+ expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects an Integer value, got String/)
135
+ end
136
+
137
+ it 'will report an argument error when no matching arity is found' do
138
+ code = <<-CODE
139
+ $d = Deferred("string_return", ['/a/non/existing/path', 'second-invalid-arg'])
140
+ class testclass(Integer $classparam) {
141
+ }
142
+ class { 'testclass':
143
+ classparam => $d
144
+ }
145
+ CODE
146
+ expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+ No matching arity found for string_return/)
147
+ end
148
+
149
+ it 'will error with no matching Variant class parameters and return_type' do
150
+ code = <<-CODE
151
+ $d = Deferred("string_return", ['/a/non/existing/path'])
152
+ class testclass(Variant[Integer,Float] $classparam) {
153
+ }
154
+ class { 'testclass':
155
+ classparam => $d
156
+ }
157
+ CODE
158
+ expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type Integer or Float, got String/)
159
+ end
160
+
161
+ # This test exposes a shortcoming in the #message function for Puppet::Pops::Type::TypeMismatch
162
+ # where the `actual` is not introspected for the list of Variant types, so the error message
163
+ # shows that the list of expected types does not match Variant, instead of a list of actual types.
164
+ it 'will error with no matching Variant class parameters and Variant return_type' do
165
+ code = <<-CODE
166
+ $d = Deferred("variant_return", ['/a/non/existing/path'])
167
+ class testclass(Variant[String,Boolean] $classparam) {
168
+ }
169
+ class { 'testclass':
170
+ classparam => $d
171
+ }
172
+ CODE
173
+ expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type String or Boolean, got Variant/)
174
+ end
175
+ end
10
176
 
11
177
  it 'will report a mismatch between a hash and a struct with details' do
12
178
  code = <<-CODE
@@ -105,9 +105,9 @@ describe Puppet::Type.type(:package).provider(:puppetserver_gem) do
105
105
 
106
106
  describe ".gemlist" do
107
107
  context "listing installed packages" do
108
- it "uses the puppet rubygems library to list local gems" do
108
+ it "uses the puppet_gem provider_command to list local gems" do
109
109
  expected = { name: 'world_airports', provider: :puppetserver_gem, ensure: ['1.1.3'] }
110
- expect(described_class).to receive(:execute_rubygems_list_command).with(nil).and_return(File.read(my_fixture('gem-list-local-packages')))
110
+ expect(described_class).to receive(:execute_rubygems_list_command).with(['gem', 'list', '--local']).and_return(File.read(my_fixture('gem-list-local-packages')))
111
111
  expect(described_class.gemlist({ local: true })).to include(expected)
112
112
  end
113
113
  end
@@ -840,7 +840,7 @@ end
840
840
  expect(provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash)).to be_a(Integer)
841
841
  end
842
842
  it "should raise an error if a field other than 'entropy', 'salt', or 'iterations' is passed" do
843
- expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash) }.to raise_error(Puppet::Error, /Puppet has tried to read an incorrect value from the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/)
843
+ expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash, 'test_user') }.to raise_error(Puppet::Error, /Puppet has tried to read an incorrect value from the user test_user in the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/)
844
844
  end
845
845
  end
846
846
 
@@ -113,12 +113,21 @@ describe Puppet::SSL::SSLProvider do
113
113
  }.to raise_error(/can't modify frozen/)
114
114
  end
115
115
 
116
- it 'trusts system ca store' do
116
+ it 'trusts system ca store by default' do
117
117
  expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)
118
118
 
119
119
  subject.create_system_context(cacerts: [])
120
120
  end
121
121
 
122
+ it 'trusts an external ca store' do
123
+ path = tmpfile('system_cacerts')
124
+ File.write(path, cert_fixture('ca.pem').to_pem)
125
+
126
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_file).with(path)
127
+
128
+ subject.create_system_context(cacerts: [], path: path)
129
+ end
130
+
122
131
  it 'verifies peer' do
123
132
  sslctx = subject.create_system_context(cacerts: [])
124
133
  expect(sslctx.verify_peer).to eq(true)
@@ -135,6 +144,47 @@ describe Puppet::SSL::SSLProvider do
135
144
  expect(sslctx.private_key).to be_nil
136
145
  end
137
146
 
147
+ it 'includes the client cert and private key when requested' do
148
+ Puppet[:hostcert] = fixtures('ssl/signed.pem')
149
+ Puppet[:hostprivkey] = fixtures('ssl/signed-key.pem')
150
+ sslctx = subject.create_system_context(cacerts: [], include_client_cert: true)
151
+ expect(sslctx.client_cert).to be_an(OpenSSL::X509::Certificate)
152
+ expect(sslctx.private_key).to be_an(OpenSSL::PKey::RSA)
153
+ end
154
+
155
+ it 'ignores non-existent client cert and private key when requested' do
156
+ Puppet[:certname] = 'doesnotexist'
157
+ sslctx = subject.create_system_context(cacerts: [], include_client_cert: true)
158
+ expect(sslctx.client_cert).to be_nil
159
+ expect(sslctx.private_key).to be_nil
160
+ end
161
+
162
+ it 'warns if the client cert does not exist' do
163
+ Puppet[:certname] = 'missingcert'
164
+ Puppet[:hostprivkey] = fixtures('ssl/signed-key.pem')
165
+
166
+ expect(Puppet).to receive(:warning).with("Client certificate for 'missingcert' does not exist")
167
+ subject.create_system_context(cacerts: [], include_client_cert: true)
168
+ end
169
+
170
+ it 'warns if the private key does not exist' do
171
+ Puppet[:certname] = 'missingkey'
172
+ Puppet[:hostcert] = fixtures('ssl/signed.pem')
173
+
174
+ expect(Puppet).to receive(:warning).with("Private key for 'missingkey' does not exist")
175
+ subject.create_system_context(cacerts: [], include_client_cert: true)
176
+ end
177
+
178
+ it 'raises if client cert and private key are mismatched' do
179
+ Puppet[:hostcert] = fixtures('ssl/signed.pem')
180
+ Puppet[:hostprivkey] = fixtures('ssl/127.0.0.1-key.pem')
181
+
182
+ expect {
183
+ subject.create_system_context(cacerts: [], include_client_cert: true)
184
+ }.to raise_error(Puppet::SSL::SSLError,
185
+ "The certificate for 'CN=signed' does not match its private key")
186
+ end
187
+
138
188
  it 'trusts additional system certs' do
139
189
  path = tmpfile('system_cacerts')
140
190
  File.write(path, cert_fixture('ca.pem').to_pem)
@@ -448,6 +498,18 @@ describe Puppet::SSL::SSLProvider do
448
498
  sslctx = subject.create_context(**config)
449
499
  expect(sslctx.verify_peer).to eq(true)
450
500
  end
501
+
502
+ it 'does not trust the system ca store by default' do
503
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths).never
504
+
505
+ subject.create_context(**config)
506
+ end
507
+
508
+ it 'trusts the system ca store' do
509
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)
510
+
511
+ subject.create_context(**config.merge(include_system_store: true))
512
+ end
451
513
  end
452
514
 
453
515
  context 'when loading an ssl context' do
@@ -530,6 +592,18 @@ describe Puppet::SSL::SSLProvider do
530
592
  }.to raise_error(Puppet::SSL::SSLError, /Failed to load private key for host 'signed': Could not parse PKey/)
531
593
  end
532
594
  end
595
+
596
+ it 'does not trust the system ca store by default' do
597
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths).never
598
+
599
+ subject.load_context
600
+ end
601
+
602
+ it 'trusts the system ca store' do
603
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:set_default_paths)
604
+
605
+ subject.load_context(include_system_store: true)
606
+ end
533
607
  end
534
608
 
535
609
  context 'when verifying requests' do
@@ -27,6 +27,7 @@ describe Puppet::SSL::StateMachine, unless: Puppet::Util::Platform.jruby? do
27
27
  let(:refused_message) { %r{Connection refused|No connection could be made because the target machine actively refused it} }
28
28
 
29
29
  before(:each) do
30
+ Puppet[:daemonize] = false
30
31
  Puppet[:ssl_lockfile] = tmpfile('ssllock')
31
32
  allow(Kernel).to receive(:sleep)
32
33
  end
@@ -37,14 +37,15 @@ task(:gen_cert_fixtures) do
37
37
  # | |
38
38
  # signed.pem | +- /CN=signed
39
39
  # revoked.pem | +- /CN=revoked
40
- # 127.0.0.1.pem | +- /CN=127.0.0.1 (with dns alt names)
41
40
  # tampered-cert.pem | +- /CN=signed (with different public key)
42
41
  # ec.pem | +- /CN=ec (with EC private key)
43
42
  # oid.pem | +- /CN=oid (with custom oid)
44
43
  # |
45
- # + /CN=Test CA Agent Subauthority
46
- # | |
47
- # pluto.pem | +- /CN=pluto
44
+ # 127.0.0.1.pem +- /CN=127.0.0.1 (with dns alt names)
45
+ # |
46
+ # intermediate-agent.pem +- /CN=Test CA Agent Subauthority
47
+ # | |
48
+ # pluto.pem | +- /CN=pluto
48
49
  # |
49
50
  # bad-int-basic-constraints.pem +- /CN=Test CA Subauthority (bad isCA constraint)
50
51
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.16.0
4
+ version: 7.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet Labs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-14 00:00:00.000000000 Z
11
+ date: 2022-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: facter