puppet 7.16.0-universal-darwin → 7.19.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.
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