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
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-REPORT" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-REPORT" "8" "May 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-report\fR \- Create, display, and submit reports\.
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-RESOURCE" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-RESOURCE" "8" "May 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-resource\fR \- The resource abstraction layer shell
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-SCRIPT" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-SCRIPT" "8" "May 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-script\fR \- Run a puppet manifests as a script without compiling a catalog
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET\-SSL" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET\-SSL" "8" "May 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\-ssl\fR \- Manage SSL keys and certificates for puppet SSL clients
data/man/man8/puppet.8 CHANGED
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PUPPET" "8" "March 2022" "Puppet, Inc." "Puppet manual"
4
+ .TH "PUPPET" "8" "May 2022" "Puppet, Inc." "Puppet manual"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBpuppet\fR
@@ -25,4 +25,4 @@ Specialized:
25
25
  catalog Compile, save, view, and convert catalogs\. describe Display help about resource types device Manage remote network devices doc Generate Puppet references epp Interact directly with the EPP template parser/renderer\. facts Retrieve and store facts\. filebucket Store and retrieve files in a filebucket generate Generates Puppet code from Ruby definitions\. node View and manage node definitions\. parser Interact directly with the parser\. plugin Interact with the Puppet plugin system\. script Run a puppet manifests as a script without compiling a catalog ssl Manage SSL keys and certificates for puppet SSL clients
26
26
  .
27
27
  .P
28
- See \'puppet help \fIsubcommand\fR \fIaction\fR\' for help on a specific subcommand action\. See \'puppet help \fIsubcommand\fR\' for help on a specific subcommand\. Puppet v7\.16\.0
28
+ See \'puppet help \fIsubcommand\fR \fIaction\fR\' for help on a specific subcommand action\. See \'puppet help \fIsubcommand\fR\' for help on a specific subcommand\. Puppet v7\.17\.0
@@ -3,6 +3,7 @@ require 'puppet_spec/files'
3
3
  require 'puppet_spec/puppetserver'
4
4
  require 'puppet_spec/compiler'
5
5
  require 'puppet_spec/https'
6
+ require 'puppet/application/agent'
6
7
 
7
8
  describe "puppet agent", unless: Puppet::Util::Platform.jruby? do
8
9
  include PuppetSpec::Files
@@ -97,6 +98,18 @@ describe "puppet agent", unless: Puppet::Util::Platform.jruby? do
97
98
  end
98
99
 
99
100
  context 'rich data' do
101
+ let(:deferred_file) { tmpfile('deferred') }
102
+ let(:deferred_manifest) do <<~END
103
+ file { '#{deferred_file}':
104
+ ensure => file,
105
+ content => '123',
106
+ } ->
107
+ notify { 'deferred':
108
+ message => Deferred('binary_file', ['#{deferred_file}'])
109
+ }
110
+ END
111
+ end
112
+
100
113
  it "calls a deferred 4x function" do
101
114
  catalog_handler = -> (req, res) {
102
115
  catalog = compile_to_catalog(<<-MANIFEST, node)
@@ -141,6 +154,43 @@ describe "puppet agent", unless: Puppet::Util::Platform.jruby? do
141
154
  end
142
155
  end
143
156
 
157
+ it "fails to apply a deferred function with an unsatified prerequisite" do
158
+ catalog_handler = -> (req, res) {
159
+ catalog = compile_to_catalog(deferred_manifest, node)
160
+ res.body = formatter.render(catalog)
161
+ res['Content-Type'] = formatter.mime
162
+ }
163
+
164
+ server.start_server(mounts: {catalog: catalog_handler}) do |port|
165
+ Puppet[:serverport] = port
166
+ expect {
167
+ agent.command_line.args << '--test'
168
+ agent.run
169
+ }.to exit_with(1)
170
+ .and output(%r{Using environment}).to_stdout
171
+ .and output(%r{The given file '#{deferred_file}' does not exist}).to_stderr
172
+ end
173
+ end
174
+
175
+ it "applies a deferred function and its prerequisite in the same run" do
176
+ Puppet[:preprocess_deferred] = false
177
+
178
+ catalog_handler = -> (req, res) {
179
+ catalog = compile_to_catalog(deferred_manifest, node)
180
+ res.body = formatter.render(catalog)
181
+ res['Content-Type'] = formatter.mime
182
+ }
183
+
184
+ server.start_server(mounts: {catalog: catalog_handler}) do |port|
185
+ Puppet[:serverport] = port
186
+ expect {
187
+ agent.command_line.args << '--test'
188
+ agent.run
189
+ }.to exit_with(2)
190
+ .and output(%r{defined 'message' as Binary\("MTIz"\)}).to_stdout
191
+ end
192
+ end
193
+
144
194
  it "re-evaluates a deferred function in a cached catalog" do
145
195
  Puppet[:report] = false
146
196
  Puppet[:use_cached_catalog] = true
@@ -740,4 +790,111 @@ describe "puppet agent", unless: Puppet::Util::Platform.jruby? do
740
790
  end
741
791
  end
742
792
  end
793
+
794
+ context "ssl" do
795
+ context "bootstrapping" do
796
+ before :each do
797
+ # reconfigure ssl to non-existent dir and files to force bootstrapping
798
+ dir = tmpdir('ssl')
799
+ Puppet[:ssldir] = dir
800
+ Puppet[:localcacert] = File.join(dir, 'ca.pem')
801
+ Puppet[:hostcrl] = File.join(dir, 'crl.pem')
802
+ Puppet[:hostprivkey] = File.join(dir, 'cert.pem')
803
+ Puppet[:hostcert] = File.join(dir, 'key.pem')
804
+
805
+ Puppet[:daemonize] = false
806
+ Puppet[:logdest] = 'console'
807
+ Puppet[:log_level] = 'info'
808
+ end
809
+
810
+ it "exits if the agent is not allowed to wait" do
811
+ Puppet[:waitforcert] = 0
812
+
813
+ server.start_server do |port|
814
+ Puppet[:serverport] = port
815
+ expect {
816
+ agent.run
817
+ }.to exit_with(1)
818
+ .and output(%r{Exiting now because the waitforcert setting is set to 0}).to_stdout
819
+ .and output(%r{Failed to submit the CSR, HTTP response was 404}).to_stderr
820
+ end
821
+ end
822
+
823
+ it "exits if the maxwaitforcert time is exceeded" do
824
+ Puppet[:waitforcert] = 1
825
+ Puppet[:maxwaitforcert] = 1
826
+
827
+ server.start_server do |port|
828
+ Puppet[:serverport] = port
829
+ expect {
830
+ agent.run
831
+ }.to exit_with(1)
832
+ .and output(%r{Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate \(127.0.0.1\). Exiting now because the maxwaitforcert timeout has been exceeded.}).to_stdout
833
+ .and output(%r{Failed to submit the CSR, HTTP response was 404}).to_stderr
834
+ end
835
+ end
836
+ end
837
+
838
+ def copy_fixtures(sources, dest)
839
+ ssldir = File.join(PuppetSpec::FIXTURE_DIR, 'ssl')
840
+ File.open(dest, 'w') do |f|
841
+ sources.each do |s|
842
+ f.write(File.read(File.join(ssldir, s)))
843
+ end
844
+ end
845
+ end
846
+
847
+ it "reloads the CRL between runs" do
848
+ Puppet[:localcacert] = ca = tmpfile('ca')
849
+ Puppet[:hostcrl] = crl = tmpfile('crl')
850
+ Puppet[:hostcert] = cert = tmpfile('cert')
851
+ Puppet[:hostprivkey] = key = tmpfile('key')
852
+
853
+ copy_fixtures(%w[ca.pem intermediate.pem], ca)
854
+ copy_fixtures(%w[crl.pem intermediate-crl.pem], crl)
855
+ copy_fixtures(%w[127.0.0.1.pem], cert)
856
+ copy_fixtures(%w[127.0.0.1-key.pem], key)
857
+
858
+ revoked = cert_fixture('revoked.pem')
859
+ revoked_key = key_fixture('revoked-key.pem')
860
+
861
+ mounts = {}
862
+ mounts[:catalog] = -> (req, res) {
863
+ catalog = compile_to_catalog(<<~MANIFEST, node)
864
+ file { '#{cert}':
865
+ ensure => file,
866
+ content => '#{revoked}'
867
+ }
868
+ file { '#{key}':
869
+ ensure => file,
870
+ content => '#{revoked_key}'
871
+ }
872
+ MANIFEST
873
+
874
+ res.body = formatter.render(catalog)
875
+ res['Content-Type'] = formatter.mime
876
+ }
877
+
878
+ server.start_server(mounts: mounts) do |port|
879
+ Puppet[:serverport] = port
880
+ Puppet[:daemonize] = false
881
+ Puppet[:runinterval] = 1
882
+ Puppet[:waitforcert] = 1
883
+ Puppet[:maxwaitforcert] = 1
884
+
885
+ # simulate two runs of the agent, then return so we don't infinite loop
886
+ allow_any_instance_of(Puppet::Daemon).to receive(:run_event_loop) do |instance|
887
+ instance.agent.run(splay: false)
888
+ instance.agent.run(splay: false)
889
+ end
890
+
891
+ agent.command_line.args << '--verbose'
892
+ expect {
893
+ agent.run
894
+ }.to exit_with(1)
895
+ .and output(%r{Exiting now because the maxwaitforcert timeout has been exceeded}).to_stdout
896
+ .and output(%r{Certificate 'CN=revoked' is revoked}).to_stderr
897
+ end
898
+ end
899
+ end
743
900
  end
@@ -665,6 +665,18 @@ class amod::bad_type {
665
665
  end
666
666
 
667
667
  context 'rich data' do
668
+ let(:deferred_file) { tmpfile('deferred') }
669
+ let(:deferred_manifest) do <<~END
670
+ file { '#{deferred_file}':
671
+ ensure => file,
672
+ content => '123',
673
+ } ->
674
+ notify { 'deferred':
675
+ message => Deferred('binary_file', ['#{deferred_file}'])
676
+ }
677
+ END
678
+ end
679
+
668
680
  it "calls a deferred 4x function" do
669
681
  apply.command_line.args = ['-e', 'notify { "deferred3x": message => Deferred("join", [[1,2,3], ":"]) }']
670
682
 
@@ -681,5 +693,67 @@ class amod::bad_type {
681
693
  }.to exit_with(0) # for some reason apply returns 0 instead of 2
682
694
  .and output(%r{Notice: /Stage\[main\]/Main/Notify\[deferred4x\]/message: defined 'message' as 'I am deferred'}).to_stdout
683
695
  end
696
+
697
+ it "fails to apply a deferred function with an unsatified prerequisite" do
698
+ apply.command_line.args = ['-e', deferred_manifest]
699
+ expect {
700
+ apply.run
701
+ }.to exit_with(1) # for some reason apply returns 0 instead of 2
702
+ .and output(/Compiled catalog/).to_stdout
703
+ .and output(%r{The given file '#{deferred_file}' does not exist}).to_stderr
704
+ end
705
+
706
+ it "applies a deferred function and its prerequisite in the same run" do
707
+ Puppet[:preprocess_deferred] = false
708
+
709
+ apply.command_line.args = ['-e', deferred_manifest]
710
+ expect {
711
+ apply.run
712
+ }.to exit_with(0) # for some reason apply returns 0 instead of 2
713
+ .and output(%r{defined 'message' as Binary\("MTIz"\)}).to_stdout
714
+ end
715
+
716
+ it "validates the deferred resource before applying any resources" do
717
+ undeferred_file = tmpfile('undeferred')
718
+
719
+ manifest = <<~END
720
+ file { '#{undeferred_file}':
721
+ ensure => file,
722
+ }
723
+ file { '#{deferred_file}':
724
+ ensure => file,
725
+ content => Deferred('inline_epp', ['<%= 42 %>']),
726
+ source => 'http://example.com/content',
727
+ }
728
+ END
729
+ apply.command_line.args = ['-e', manifest]
730
+ expect {
731
+ apply.run
732
+ }.to exit_with(1)
733
+ .and output(/Compiled catalog/).to_stdout
734
+ .and output(/Validation of File.* failed: You cannot specify more than one of content, source, target/).to_stderr
735
+
736
+ # validation happens before all resources are applied, so this shouldn't exist
737
+ expect(File).to_not be_exist(undeferred_file)
738
+ end
739
+
740
+ it "evaluates resources before validating the deferred resource" do
741
+ Puppet[:preprocess_deferred] = false
742
+
743
+ manifest = <<~END
744
+ notify { 'runs before file': } ->
745
+ file { '#{deferred_file}':
746
+ ensure => file,
747
+ content => Deferred('inline_epp', ['<%= 42 %>']),
748
+ source => 'http://example.com/content',
749
+ }
750
+ END
751
+ apply.command_line.args = ['-e', manifest]
752
+ expect {
753
+ apply.run
754
+ }.to exit_with(1)
755
+ .and output(/Notify\[runs before file\]/).to_stdout
756
+ .and output(/Validation of File.* failed: You cannot specify more than one of content, source, target/).to_stderr
757
+ end
684
758
  end
685
759
  end
@@ -77,13 +77,13 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
77
77
  }
78
78
  }
79
79
 
80
- let(:systemstore) do
81
- res = tmpfile('systemstore')
80
+ let(:cert_file) do
81
+ res = tmpfile('cert_file')
82
82
  File.write(res, https_server.ca_cert)
83
83
  res
84
84
  end
85
85
 
86
- it "mutually authenticates the connection" do
86
+ it "mutually authenticates the connection using an explicit context" do
87
87
  client_context = ssl_provider.create_context(
88
88
  cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
89
89
  client_cert: https_server.server_cert, private_key: https_server.server_key
@@ -95,10 +95,27 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
95
95
  end
96
96
  end
97
97
 
98
+ it "mutually authenticates the connection when the client and server certs are issued from different CAs" do
99
+ # this is the client cert's CA, key and cert
100
+ Puppet[:localcacert] = fixtures('ssl/unknown-ca.pem')
101
+ Puppet[:hostprivkey] = fixtures('ssl/unknown-127.0.0.1-key.pem')
102
+ Puppet[:hostcert] = fixtures('ssl/unknown-127.0.0.1.pem')
103
+
104
+ # this is the server cert's CA that the client needs in order to authenticate the server
105
+ Puppet[:ssl_trust_store] = fixtures('ssl/ca.pem')
106
+
107
+ # need to pass both the client and server CAs. The former is needed so the server can authenticate our client cert
108
+ https_server = PuppetSpec::HTTPSServer.new(ca_cert: [cert_fixture('ca.pem'), cert_fixture('unknown-ca.pem')])
109
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
110
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {include_system_store: true})
111
+ expect(res).to be_success
112
+ end
113
+ end
114
+
98
115
  it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
99
- Puppet::Util.withenv("SSL_CERT_FILE" => systemstore) do
116
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
100
117
  client_context = ssl_provider.create_context(
101
- cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
118
+ cacerts: [], crls: [],
102
119
  client_cert: https_server.server_cert, private_key: https_server.server_key,
103
120
  revocation: false, include_system_store: true
104
121
  )
@@ -109,8 +126,8 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
109
126
  end
110
127
  end
111
128
 
112
- it "connects when the server's CA is in the system store and the connection is mutually authenticated uning load_context" do
113
- Puppet::Util.withenv("SSL_CERT_FILE" => systemstore) do
129
+ it "connects when the server's CA is in the system store and the connection is mutually authenticated using load_context" do
130
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
114
131
  client_context = ssl_provider.load_context(revocation: false, include_system_store: true)
115
132
  https_server.start_server(ctx_proc: ctx_proc) do |port|
116
133
  res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
@@ -132,12 +149,12 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
132
149
 
133
150
  it "connects when the server's CA is in the system store" do
134
151
  # create a temp cacert bundle
135
- ssl_file = tmpfile('systemstore')
136
- File.write(ssl_file, https_server.ca_cert)
152
+ cert_file = tmpfile('cert_file')
153
+ File.write(cert_file, https_server.ca_cert)
137
154
 
138
155
  # override path to system cacert bundle, this must be done before
139
156
  # the SSLContext is created and the call to X509::Store.set_default_paths
140
- Puppet::Util.withenv("SSL_CERT_FILE" => ssl_file) do
157
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
141
158
  system_context = ssl_provider.create_system_context(cacerts: [])
142
159
  https_server.start_server do |port|
143
160
  res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: system_context})
@@ -40,7 +40,7 @@ class PuppetSpec::HTTPSServer
40
40
 
41
41
  IO.pipe {|stop_pipe_r, stop_pipe_w|
42
42
  store = OpenSSL::X509::Store.new
43
- store.add_cert(@ca_cert)
43
+ Array(@ca_cert).each { |c| store.add_cert(c) }
44
44
  store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
45
45
  ctx = OpenSSL::SSL::SSLContext.new
46
46
  ctx.cert_store = store
@@ -72,6 +72,40 @@ class PuppetSpec::Puppetserver
72
72
  end
73
73
  end
74
74
 
75
+ class CertificateServlet < WEBrick::HTTPServlet::AbstractServlet
76
+ def initialize(server, ca_cert)
77
+ super(server)
78
+ @ca_cert = ca_cert
79
+ end
80
+
81
+ def do_GET request, response
82
+ if request.path =~ %r{/puppet-ca/v1/certificate/ca$}
83
+ response['Content-Type'] = 'text/plain'
84
+ response.body = @ca_cert.to_pem
85
+ else
86
+ response.status = 404
87
+ end
88
+ end
89
+ end
90
+
91
+ class CertificateRevocationListServlet < WEBrick::HTTPServlet::AbstractServlet
92
+ def initialize(server, crl)
93
+ super(server)
94
+ @crl = crl
95
+ end
96
+
97
+ def do_GET request, response
98
+ response['Content-Type'] = 'text/plain'
99
+ response.body = @crl.to_pem
100
+ end
101
+ end
102
+
103
+ class CertificateRequestServlet < WEBrick::HTTPServlet::AbstractServlet
104
+ def do_PUT request, response
105
+ response.status = 404
106
+ end
107
+ end
108
+
75
109
  def initialize
76
110
  @ca_cert = cert_fixture('ca.pem')
77
111
  @ca_crl = crl_fixture('crl.pem')
@@ -125,15 +159,18 @@ class PuppetSpec::Puppetserver
125
159
  register_mount('/puppet/v3/static_file_content', mounts[:static_file_content], StaticFileContentServlet)
126
160
  register_mount('/puppet/v3/report', mounts[:report], ReportServlet)
127
161
  register_mount('/puppet/v3/file_bucket_file', mounts[:filebucket], FilebucketServlet)
162
+ register_mount('/puppet-ca/v1/certificate', mounts[:certificate], CertificateServlet, @ca_cert)
163
+ register_mount('/puppet-ca/v1/certificate_revocation_list', mounts[:certificate_revocation_list], CertificateRevocationListServlet, @ca_crl)
164
+ register_mount('/puppet-ca/v1/certificate_request', mounts[:certificate_request], CertificateRequestServlet)
128
165
  end
129
166
 
130
- def register_mount(path, user_proc, default_servlet)
167
+ def register_mount(path, user_proc, default_servlet, *args)
131
168
  handler = if user_proc
132
169
  WEBrick::HTTPServlet::ProcHandler.new(user_proc)
133
170
  else
134
171
  default_servlet
135
172
  end
136
- @https.mount(path, handler)
173
+ @https.mount(path, handler, *args)
137
174
  end
138
175
 
139
176
  def upload_directory
@@ -36,6 +36,10 @@ describe Puppet::Agent do
36
36
  end
37
37
  end
38
38
  end
39
+
40
+ ssl_context = Puppet::SSL::SSLContext.new
41
+ machine = instance_double("Puppet::SSL::StateMachine", ensure_client_certificate: ssl_context)
42
+ allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)
39
43
  end
40
44
 
41
45
  after do
@@ -195,7 +199,7 @@ describe Puppet::Agent do
195
199
  @agent.run
196
200
  end
197
201
 
198
- it "should inform that a run is already in progres and try to run every X seconds if waitforlock is used" do
202
+ it "should inform that a run is already in progress and try to run every X seconds if waitforlock is used" do
199
203
  # so the locked file exists
200
204
  allow(File).to receive(:file?).and_return(true)
201
205
  # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)
@@ -224,7 +228,7 @@ describe Puppet::Agent do
224
228
  @agent.run
225
229
  end
226
230
  end
227
-
231
+
228
232
  describe "when should_fork is true", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do
229
233
  before do
230
234
  @agent = Puppet::Agent.new(AgentTestClient, true)
@@ -4,6 +4,11 @@ require 'puppet/agent'
4
4
  require 'puppet/application/agent'
5
5
  require 'puppet/daemon'
6
6
 
7
+ class TestAgentClientClass
8
+ def initialize(transaction_uuid = nil, job_id = nil); end
9
+ def run(options = {}); end
10
+ end
11
+
7
12
  describe Puppet::Application::Agent do
8
13
  include PuppetSpec::Files
9
14
 
@@ -12,13 +17,20 @@ describe Puppet::Application::Agent do
12
17
  before :each do
13
18
  @puppetd = Puppet::Application[:agent]
14
19
 
15
- @agent = double('agent')
20
+ @client = TestAgentClientClass.new
21
+ allow(TestAgentClientClass).to receive(:new).and_return(@client)
22
+
23
+ @agent = Puppet::Agent.new(TestAgentClientClass, false)
16
24
  allow(Puppet::Agent).to receive(:new).and_return(@agent)
17
25
 
18
- @daemon = Puppet::Daemon.new(@agent, nil)
26
+ Puppet[:pidfile] = tmpfile('pidfile')
27
+ @daemon = Puppet::Daemon.new(@agent, Puppet::Util::Pidlock.new(Puppet[:pidfile]))
19
28
  allow(@daemon).to receive(:daemonize)
20
- allow(@daemon).to receive(:start)
21
29
  allow(@daemon).to receive(:stop)
30
+ # simulate one run so we don't infinite looptwo runs of the agent, then return so we don't infinite loop
31
+ allow(@daemon).to receive(:run_event_loop) do
32
+ @agent.run(splay: false)
33
+ end
22
34
  allow(Puppet::Daemon).to receive(:new).and_return(@daemon)
23
35
  Puppet[:daemonize] = false
24
36
 
@@ -92,10 +104,6 @@ describe Puppet::Application::Agent do
92
104
  end
93
105
 
94
106
  describe "when handling options" do
95
- before do
96
- allow(@puppetd.command_line).to receive(:args).and_return([])
97
- end
98
-
99
107
  [:enable, :debug, :fqdn, :test, :verbose, :digest].each do |option|
100
108
  it "should declare handle_#{option} method" do
101
109
  expect(@puppetd).to respond_to("handle_#{option}".to_sym)
@@ -127,32 +135,34 @@ describe Puppet::Application::Agent do
127
135
  end
128
136
 
129
137
  it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do
130
- allow(@agent).to receive(:run).and_return(2)
138
+ allow(@client).to receive(:run).and_return(2)
131
139
  Puppet[:onetime] = true
132
140
 
133
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 0).and_return(machine)
141
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 0)).and_return(machine)
134
142
 
135
143
  expect { execute_agent }.to exit_with 0
136
144
  end
137
145
 
138
146
  it "should use supplied waitforcert when --onetime is specified" do
139
- allow(@agent).to receive(:run).and_return(2)
147
+ allow(@client).to receive(:run).and_return(2)
140
148
  Puppet[:onetime] = true
141
149
  @puppetd.handle_waitforcert(60)
142
150
 
143
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 60).and_return(machine)
151
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 60)).and_return(machine)
144
152
 
145
153
  expect { execute_agent }.to exit_with 0
146
154
  end
147
155
 
148
156
  it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do
149
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 120).and_return(machine)
157
+ allow(@client).to receive(:run).and_return(2)
158
+
159
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)
150
160
 
151
161
  execute_agent
152
162
  end
153
163
 
154
164
  it "should register ssl OIDs" do
155
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 120).and_return(double(ensure_client_certificate: nil))
165
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)
156
166
  expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)
157
167
 
158
168
  execute_agent
@@ -161,7 +171,7 @@ describe Puppet::Application::Agent do
161
171
  it "should use the waitforcert setting when checking for a signed certificate" do
162
172
  Puppet[:waitforcert] = 10
163
173
 
164
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 10).and_return(machine)
174
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 10)).and_return(machine)
165
175
 
166
176
  execute_agent
167
177
  end
@@ -413,9 +423,9 @@ describe Puppet::Application::Agent do
413
423
  end
414
424
 
415
425
  it "should wait for a certificate" do
416
- @puppetd.options[:waitforcert] = 123
426
+ Puppet[:waitforcert] = 123
417
427
 
418
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 123).and_return(machine)
428
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 123)).and_return(machine)
419
429
 
420
430
  execute_agent
421
431
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'puppet/daemon'
3
3
  require 'puppet/agent'
4
+ require 'puppet/configurer'
4
5
 
5
6
  def without_warnings
6
7
  flag = $VERBOSE
@@ -9,12 +10,6 @@ def without_warnings
9
10
  $VERBOSE = flag
10
11
  end
11
12
 
12
- class TestClient
13
- def lockfile_path
14
- "/dev/null"
15
- end
16
- end
17
-
18
13
  describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
19
14
  include PuppetSpec::Files
20
15
 
@@ -26,7 +21,7 @@ describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
26
21
  end
27
22
  end
28
23
 
29
- let(:agent) { Puppet::Agent.new(TestClient.new, false) }
24
+ let(:agent) { Puppet::Agent.new(Puppet::Configurer, false) }
30
25
  let(:server) { double("Server", :start => nil, :wait_for_shutdown => nil) }
31
26
 
32
27
  let(:pidfile) { double("PidFile", :lock => true, :unlock => true, :file_path => 'fake.pid') }
@@ -131,10 +126,6 @@ describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
131
126
  end
132
127
 
133
128
  describe "when reloading" do
134
- it "should do nothing if no agent is configured" do
135
- daemon.reload
136
- end
137
-
138
129
  it "should do nothing if the agent is running" do
139
130
  expect(agent).to receive(:run).with({:splay => false}).and_raise(Puppet::LockError, 'Failed to aquire lock')
140
131
  expect(Puppet).to receive(:notice).with('Not triggering already-running agent')
@@ -120,6 +120,24 @@ describe Puppet::HTTP::Client do
120
120
 
121
121
  client.close
122
122
  end
123
+
124
+ it 'reloads the default ssl context' do
125
+ expect(client.pool).to receive(:with_connection) do |_, verifier|
126
+ expect(verifier.ssl_context).to_not equal(puppet_context)
127
+ end
128
+
129
+ client.close
130
+ client.connect(uri)
131
+ end
132
+
133
+ it 'reloads the default system ssl context' do
134
+ expect(client.pool).to receive(:with_connection) do |_, verifier|
135
+ expect(verifier.ssl_context).to_not equal(system_context)
136
+ end
137
+
138
+ client.close
139
+ client.connect(uri, options: {include_system_store: true})
140
+ end
123
141
  end
124
142
 
125
143
  context "for GET requests" do