puppet 7.16.0-x86-mingw32 → 7.19.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +76 -15
  3. data/ext/systemd/puppet.service +2 -1
  4. data/lib/puppet/agent.rb +47 -11
  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/face/help.rb +1 -1
  10. data/lib/puppet/face/module/list.rb +16 -7
  11. data/lib/puppet/functions/capitalize.rb +1 -1
  12. data/lib/puppet/generate/type.rb +1 -1
  13. data/lib/puppet/http/client.rb +22 -2
  14. data/lib/puppet/info_service/task_information_service.rb +1 -1
  15. data/lib/puppet/module/task.rb +5 -1
  16. data/lib/puppet/parameter.rb +19 -4
  17. data/lib/puppet/parser/templatewrapper.rb +1 -1
  18. data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
  19. data/lib/puppet/pops/functions/dispatcher.rb +10 -6
  20. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
  21. data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
  22. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  23. data/lib/puppet/provider/package/yum.rb +8 -3
  24. data/lib/puppet/provider/user/directoryservice.rb +15 -8
  25. data/lib/puppet/reference/configuration.rb +2 -0
  26. data/lib/puppet/ssl/ssl_provider.rb +65 -12
  27. data/lib/puppet/ssl/state_machine.rb +13 -17
  28. data/lib/puppet/transaction.rb +22 -0
  29. data/lib/puppet/type/tidy.rb +1 -1
  30. data/lib/puppet/type/user.rb +3 -0
  31. data/lib/puppet/type.rb +20 -3
  32. data/lib/puppet/util/json.rb +5 -2
  33. data/lib/puppet/util/resource_template.rb +1 -1
  34. data/lib/puppet/util/selinux.rb +1 -1
  35. data/lib/puppet/util.rb +12 -1
  36. data/lib/puppet/version.rb +1 -1
  37. data/lib/puppet.rb +1 -14
  38. data/man/man5/puppet.conf.5 +12 -4
  39. data/man/man8/puppet-agent.8 +2 -2
  40. data/man/man8/puppet-apply.8 +1 -1
  41. data/man/man8/puppet-catalog.8 +1 -1
  42. data/man/man8/puppet-config.8 +1 -1
  43. data/man/man8/puppet-describe.8 +1 -1
  44. data/man/man8/puppet-device.8 +1 -1
  45. data/man/man8/puppet-doc.8 +1 -1
  46. data/man/man8/puppet-epp.8 +1 -1
  47. data/man/man8/puppet-facts.8 +1 -1
  48. data/man/man8/puppet-filebucket.8 +1 -1
  49. data/man/man8/puppet-generate.8 +1 -1
  50. data/man/man8/puppet-help.8 +1 -1
  51. data/man/man8/puppet-lookup.8 +1 -1
  52. data/man/man8/puppet-module.8 +1 -1
  53. data/man/man8/puppet-node.8 +1 -1
  54. data/man/man8/puppet-parser.8 +1 -1
  55. data/man/man8/puppet-plugin.8 +1 -1
  56. data/man/man8/puppet-report.8 +1 -1
  57. data/man/man8/puppet-resource.8 +1 -1
  58. data/man/man8/puppet-script.8 +1 -1
  59. data/man/man8/puppet-ssl.8 +1 -1
  60. data/man/man8/puppet.8 +2 -2
  61. data/spec/integration/application/agent_spec.rb +157 -0
  62. data/spec/integration/application/apply_spec.rb +74 -0
  63. data/spec/integration/http/client_spec.rb +27 -10
  64. data/spec/lib/puppet_spec/https.rb +1 -1
  65. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  66. data/spec/unit/agent_spec.rb +28 -2
  67. data/spec/unit/application/agent_spec.rb +26 -16
  68. data/spec/unit/daemon_spec.rb +2 -11
  69. data/spec/unit/face/module/list_spec.rb +26 -0
  70. data/spec/unit/http/client_spec.rb +18 -0
  71. data/spec/unit/info_service_spec.rb +11 -3
  72. data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
  73. data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
  74. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
  75. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  76. data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
  77. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  78. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  79. data/spec/unit/task_spec.rb +56 -13
  80. data/spec/unit/util/resource_template_spec.rb +1 -1
  81. data/spec/unit/util/selinux_spec.rb +5 -0
  82. data/spec/unit/util_spec.rb +11 -1
  83. data/tasks/generate_cert_fixtures.rake +5 -4
  84. metadata +2 -2
@@ -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
@@ -97,6 +101,8 @@ describe Puppet::Agent do
97
101
  end
98
102
 
99
103
  it "should splay" do
104
+ Puppet[:splay] = true
105
+
100
106
  expect(@agent).to receive(:splay)
101
107
 
102
108
  @agent.run
@@ -179,6 +185,26 @@ describe Puppet::Agent do
179
185
  expect(@agent.run).to eq(:result)
180
186
  end
181
187
 
188
+ it "should check if it's disabled after splaying and log a message" do
189
+ Puppet[:splay] = true
190
+ Puppet[:splaylimit] = '5s'
191
+ Puppet[:onetime] = true
192
+
193
+ expect(@agent).to receive(:disabled?).and_return(false, true)
194
+
195
+ allow(Puppet).to receive(:notice).and_call_original
196
+ expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
197
+ @agent.run
198
+ end
199
+
200
+ it "should check if it's disabled after acquiring the lock and log a message" do
201
+ expect(@agent).to receive(:disabled?).and_return(false, true)
202
+
203
+ allow(Puppet).to receive(:notice).and_call_original
204
+ expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
205
+ @agent.run
206
+ end
207
+
182
208
  describe "and a puppet agent is already running" do
183
209
  before(:each) do
184
210
  allow_any_instance_of(Object).to receive(:sleep)
@@ -195,7 +221,7 @@ describe Puppet::Agent do
195
221
  @agent.run
196
222
  end
197
223
 
198
- it "should inform that a run is already in progres and try to run every X seconds if waitforlock is used" do
224
+ it "should inform that a run is already in progress and try to run every X seconds if waitforlock is used" do
199
225
  # so the locked file exists
200
226
  allow(File).to receive(:file?).and_return(true)
201
227
  # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)
@@ -224,7 +250,7 @@ describe Puppet::Agent do
224
250
  @agent.run
225
251
  end
226
252
  end
227
-
253
+
228
254
  describe "when should_fork is true", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do
229
255
  before do
230
256
  @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')
@@ -227,4 +227,30 @@ describe "puppet module list" do
227
227
  console_output(:tree => true)
228
228
  end
229
229
  end
230
+
231
+ describe "when rendering as json" do
232
+ let(:face) { Puppet::Face[:module, :current] }
233
+ let(:action) { face.get_action(:list) }
234
+
235
+ it "should warn about missing dependencies" do
236
+ PuppetSpec::Modules.create('depender', @modpath1, :metadata => {
237
+ :version => '1.0.0',
238
+ :dependencies => [{
239
+ "version_requirement" => ">= 0.0.5",
240
+ "name" => "puppetlabs/dependable"
241
+ }]
242
+ })
243
+
244
+ result = face.list
245
+ expect(result.dig(:unmet_dependencies, :missing)).to include(
246
+ "puppetlabs/dependable" => {
247
+ errors: ["'puppetlabs-depender' (v1.0.0) requires 'puppetlabs-dependable' (>= 0.0.5)"],
248
+ parent: {
249
+ name: "puppetlabs/depender", :version=>"v1.0.0"
250
+ },
251
+ version: nil
252
+ }
253
+ )
254
+ end
255
+ end
230
256
  end
@@ -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
@@ -11,6 +11,9 @@ describe "Puppet::InfoService" do
11
11
 
12
12
  context 'task information service' do
13
13
  let(:mod_name) { 'test1' }
14
+ let(:metadata) {
15
+ { "private" => true,
16
+ "description" => "a task that does a thing" } }
14
17
  let(:task_name) { "#{mod_name}::thingtask" }
15
18
  let(:modpath) { tmpdir('modpath') }
16
19
  let(:env_name) { 'testing' }
@@ -20,8 +23,13 @@ describe "Puppet::InfoService" do
20
23
  context 'tasks_per_environment method' do
21
24
  it "returns task data for the tasks in an environment" do
22
25
  Puppet.override(:environments => env_loader) do
23
- PuppetSpec::Modules.create(mod_name, modpath, {:environment => env, :tasks => [['thingtask']]})
24
- expect(Puppet::InfoService.tasks_per_environment(env_name)).to eq([{:name => task_name, :module => {:name => mod_name}}])
26
+ PuppetSpec::Modules.create(mod_name, modpath, {:environment => env,
27
+ :tasks => [['thingtask',
28
+ {:name => 'thingtask.json',
29
+ :content => metadata.to_json}]]})
30
+ expect(Puppet::InfoService.tasks_per_environment(env_name)).to eq([{:name => task_name,
31
+ :module => {:name => mod_name},
32
+ :metadata => metadata} ])
25
33
  end
26
34
  end
27
35
 
@@ -207,7 +215,7 @@ describe "Puppet::InfoService" do
207
215
  end
208
216
  end
209
217
  end
210
-
218
+
211
219
  context 'plan information service' do
212
220
  let(:mod_name) { 'test1' }
213
221
  let(:plan_name) { "#{mod_name}::thingplan" }
@@ -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