puppet 6.25.1 → 6.28.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +1 -1
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +101 -34
  5. data/lib/puppet/agent.rb +47 -11
  6. data/lib/puppet/application/agent.rb +2 -12
  7. data/lib/puppet/application/lookup.rb +74 -24
  8. data/lib/puppet/concurrent/thread_local_singleton.rb +5 -3
  9. data/lib/puppet/configurer.rb +8 -14
  10. data/lib/puppet/defaults.rb +13 -3
  11. data/lib/puppet/face/generate.rb +2 -0
  12. data/lib/puppet/file_serving/metadata.rb +3 -0
  13. data/lib/puppet/file_system/file_impl.rb +7 -7
  14. data/lib/puppet/file_system/jruby.rb +1 -1
  15. data/lib/puppet/file_system/windows.rb +4 -4
  16. data/lib/puppet/file_system.rb +1 -1
  17. data/lib/puppet/functions/next.rb +18 -1
  18. data/lib/puppet/functions/tree_each.rb +0 -1
  19. data/lib/puppet/functions/versioncmp.rb +6 -2
  20. data/lib/puppet/generate/type.rb +9 -0
  21. data/lib/puppet/http/client.rb +22 -2
  22. data/lib/puppet/node.rb +1 -1
  23. data/lib/puppet/pops/parser/code_merger.rb +4 -4
  24. data/lib/puppet/pops/parser/egrammar.ra +2 -0
  25. data/lib/puppet/pops/parser/eparser.rb +813 -794
  26. data/lib/puppet/pops/serialization/to_data_converter.rb +6 -18
  27. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  28. data/lib/puppet/provider/package/windows/exe_package.rb +30 -1
  29. data/lib/puppet/provider/package/windows/package.rb +2 -1
  30. data/lib/puppet/provider/package/windows.rb +14 -1
  31. data/lib/puppet/provider/service/init.rb +5 -4
  32. data/lib/puppet/provider/user/directoryservice.rb +5 -0
  33. data/lib/puppet/ssl/ssl_provider.rb +75 -19
  34. data/lib/puppet/ssl/state_machine.rb +13 -17
  35. data/lib/puppet/ssl/verifier.rb +6 -0
  36. data/lib/puppet/transaction/persistence.rb +22 -12
  37. data/lib/puppet/type/exec.rb +1 -1
  38. data/lib/puppet/type/file/data_sync.rb +1 -1
  39. data/lib/puppet/type/user.rb +43 -38
  40. data/lib/puppet/util/json.rb +17 -0
  41. data/lib/puppet/util/log.rb +7 -2
  42. data/lib/puppet/util/monkey_patches.rb +6 -2
  43. data/lib/puppet/util/package.rb +25 -16
  44. data/lib/puppet/util/yaml.rb +21 -2
  45. data/lib/puppet/util.rb +1 -2
  46. data/lib/puppet/version.rb +1 -1
  47. data/lib/puppet.rb +2 -14
  48. data/locales/puppet.pot +5 -10454
  49. data/man/man5/puppet.conf.5 +21 -2
  50. data/man/man8/puppet-agent.8 +1 -1
  51. data/man/man8/puppet-apply.8 +1 -1
  52. data/man/man8/puppet-catalog.8 +1 -1
  53. data/man/man8/puppet-config.8 +1 -1
  54. data/man/man8/puppet-describe.8 +1 -1
  55. data/man/man8/puppet-device.8 +1 -1
  56. data/man/man8/puppet-doc.8 +1 -1
  57. data/man/man8/puppet-epp.8 +1 -1
  58. data/man/man8/puppet-facts.8 +1 -1
  59. data/man/man8/puppet-filebucket.8 +1 -1
  60. data/man/man8/puppet-generate.8 +1 -1
  61. data/man/man8/puppet-help.8 +1 -1
  62. data/man/man8/puppet-key.8 +1 -1
  63. data/man/man8/puppet-lookup.8 +9 -6
  64. data/man/man8/puppet-man.8 +1 -1
  65. data/man/man8/puppet-module.8 +1 -1
  66. data/man/man8/puppet-node.8 +1 -1
  67. data/man/man8/puppet-parser.8 +1 -1
  68. data/man/man8/puppet-plugin.8 +1 -1
  69. data/man/man8/puppet-report.8 +1 -1
  70. data/man/man8/puppet-resource.8 +1 -1
  71. data/man/man8/puppet-script.8 +1 -1
  72. data/man/man8/puppet-ssl.8 +1 -1
  73. data/man/man8/puppet-status.8 +1 -1
  74. data/man/man8/puppet.8 +2 -2
  75. data/spec/fixtures/unit/forge/bacula.json +1 -1
  76. data/spec/integration/application/agent_spec.rb +108 -0
  77. data/spec/integration/application/lookup_spec.rb +81 -50
  78. data/spec/integration/application/resource_spec.rb +6 -2
  79. data/spec/integration/http/client_spec.rb +51 -4
  80. data/spec/lib/puppet_spec/https.rb +1 -1
  81. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  82. data/spec/shared_contexts/l10n.rb +5 -0
  83. data/spec/unit/agent_spec.rb +28 -2
  84. data/spec/unit/application/agent_spec.rb +26 -16
  85. data/spec/unit/application/lookup_spec.rb +131 -10
  86. data/spec/unit/concurrent/thread_local_singleton_spec.rb +39 -0
  87. data/spec/unit/configurer_spec.rb +124 -61
  88. data/spec/unit/daemon_spec.rb +2 -11
  89. data/spec/unit/face/generate_spec.rb +64 -0
  90. data/spec/unit/file_system_spec.rb +34 -4
  91. data/spec/unit/forge/module_release_spec.rb +3 -3
  92. data/spec/unit/functions/versioncmp_spec.rb +40 -4
  93. data/spec/unit/http/client_spec.rb +18 -0
  94. data/spec/unit/node_spec.rb +6 -0
  95. data/spec/unit/pops/parser/parse_containers_spec.rb +2 -2
  96. data/spec/unit/pops/serialization/to_from_hr_spec.rb +0 -58
  97. data/spec/unit/pops/validator/validator_spec.rb +5 -0
  98. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  99. data/spec/unit/provider/package/windows/exe_package_spec.rb +17 -0
  100. data/spec/unit/provider/service/gentoo_spec.rb +6 -5
  101. data/spec/unit/provider/service/init_spec.rb +15 -9
  102. data/spec/unit/provider/service/openwrt_spec.rb +21 -29
  103. data/spec/unit/provider/service/redhat_spec.rb +3 -2
  104. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  105. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  106. data/spec/unit/transaction/persistence_spec.rb +51 -0
  107. data/spec/unit/type/user_spec.rb +0 -45
  108. data/spec/unit/util/json_spec.rb +126 -0
  109. data/spec/unit/util/windows_spec.rb +23 -0
  110. data/spec/unit/util/yaml_spec.rb +54 -29
  111. data/tasks/generate_cert_fixtures.rake +5 -4
  112. metadata +9 -3
@@ -7,6 +7,7 @@ describe 'lookup' do
7
7
  include PuppetSpec::Files
8
8
 
9
9
  context 'with an environment' do
10
+ let(:fqdn) { Puppet[:certname] }
10
11
  let(:env_name) { 'spec' }
11
12
  let(:env_dir) { tmpdir('environments') }
12
13
  let(:environment_files) do
@@ -42,94 +43,117 @@ describe 'lookup' do
42
43
  end
43
44
 
44
45
  let(:app) { Puppet::Application[:lookup] }
45
- let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }
46
- let(:environments) { Puppet::Environments::Directories.new(populated_env_dir, []) }
46
+ let(:facts) { Puppet::Node::Facts.new("facts", {'my_fact' => 'my_fact_value'}) }
47
+ let(:cert) { pem_content('oid.pem') }
47
48
 
49
+ let(:node) { Puppet::Node.new('testnode', :facts => facts) }
48
50
  let(:populated_env_dir) do
49
51
  dir_contained_in(env_dir, environment_files)
50
52
  env_dir
51
53
  end
52
54
 
53
- def lookup(key, options = {}, explain = false)
54
- key = [key] unless key.is_a?(Array)
55
- allow(app.command_line).to receive(:args).and_return(key)
56
- if explain
57
- app.options[:explain] = true
58
- app.options[:render_as] = :s
59
- else
60
- app.options[:render_as] = :json
61
- end
62
- options.each_pair { |k, v| app.options[k] = v }
63
- capture = StringIO.new
64
- saved_stdout = $stdout
65
- begin
66
- $stdout = capture
67
- expect { app.run_command }.to exit_with(0)
68
- ensure
69
- $stdout = saved_stdout
70
- end
71
- out = capture.string.strip
72
- if explain
73
- out
74
- else
75
- out.empty? ? nil : JSON.parse("[#{out}]")[0]
76
- end
77
- end
55
+ before do
56
+ stub_request(:get, "https://puppet:8140/puppet-ca/v1/certificate/#{fqdn}").to_return(body: cert)
57
+ allow(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)
58
+
59
+ Puppet[:environment] = env_name
60
+ Puppet[:environmentpath] = populated_env_dir
78
61
 
79
- def explain(key, options = {})
80
- lookup(key, options, true)
62
+ http = Puppet::HTTP::Client.new(ssl_context: Puppet::SSL::SSLProvider.new.create_insecure_context)
63
+ Puppet.runtime[:http] = http
81
64
  end
82
65
 
83
- around(:each) do |example|
84
- Puppet.override(:environments => environments, :current_environment => env) do
85
- example.run
86
- end
66
+ def expect_lookup_with_output(exitcode, out)
67
+ expect { app.run }.to exit_with(exitcode).and output(out).to_stdout
87
68
  end
88
69
 
89
70
  it 'finds data in the environment' do
90
- expect(lookup('a')).to eql('value a')
71
+ app.command_line.args << 'a'
72
+ expect_lookup_with_output(0, /value a/)
73
+ end
74
+
75
+ it "resolves hiera data using a top-level node parameter" do
76
+ File.write(File.join(env_dir, env_name, 'hiera.yaml'), <<~YAML)
77
+ ---
78
+ version: 5
79
+ hierarchy:
80
+ - name: "Per Node"
81
+ data_hash: yaml_data
82
+ path: "%{my_fact}.yaml"
83
+ YAML
84
+
85
+ File.write(File.join(env_dir, env_name, 'data', "my_fact_value.yaml"), <<~YAML)
86
+ ---
87
+ a: value from per node data
88
+ YAML
89
+
90
+ app.command_line.args << 'a'
91
+ expect_lookup_with_output(0, /--- value from per node data/)
92
+ end
93
+
94
+ it 'loads trusted information from the node certificate' do
95
+ Puppet.settings[:node_terminus] = 'exec'
96
+ expect_any_instance_of(Puppet::Node::Exec).to receive(:find) do |args|
97
+ info = Puppet.lookup(:trusted_information)
98
+ expect(info.certname).to eq(fqdn)
99
+ expect(info.extensions).to eq({ "1.3.6.1.4.1.34380.1.2.1.1" => "somevalue" })
100
+ end.and_return(node)
101
+
102
+ app.command_line.args << 'a' << '--compile'
103
+ expect_lookup_with_output(0, /--- value a/)
91
104
  end
92
105
 
93
106
  it 'loads external facts when running without --node' do
94
107
  expect(Puppet::Util).not_to receive(:skip_external_facts)
95
108
  expect(Facter).not_to receive(:load_external)
96
- lookup('a')
109
+
110
+ app.command_line.args << 'a'
111
+ expect_lookup_with_output(0, /--- value a/)
97
112
  end
98
113
 
99
- it 'skip loading of external facts when run with --node' do
100
- app.options[:node] = "random_node"
101
- expect(Facter).to receive(:load_external).once.with(false)
102
- expect(Facter).to receive(:load_external).once.with(true)
103
- lookup('a')
114
+ describe 'when using --node' do
115
+ let(:fqdn) { 'random_node' }
116
+
117
+ it 'skips loading of external facts' do
118
+ app.command_line.args << 'a' << '--node' << fqdn
119
+
120
+ expect(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)
121
+ expect(Facter).to receive(:load_external).twice.with(false)
122
+ expect(Facter).to receive(:load_external).twice.with(true)
123
+ expect_lookup_with_output(0, /--- value a/)
124
+ end
104
125
  end
105
126
 
106
127
  context 'uses node_terminus' do
107
128
  require 'puppet/indirector/node/exec'
108
129
  require 'puppet/indirector/node/plain'
109
130
 
110
- let(:node) { Puppet::Node.new('testnode', :environment => env) }
131
+ let(:node) { Puppet::Node.new('testnode', :facts => facts) }
111
132
 
112
133
  it ':plain without --compile' do
113
134
  Puppet.settings[:node_terminus] = 'exec'
114
135
  expect_any_instance_of(Puppet::Node::Plain).to receive(:find).and_return(node)
115
136
  expect_any_instance_of(Puppet::Node::Exec).not_to receive(:find)
116
- expect(lookup('a')).to eql('value a')
137
+
138
+ app.command_line.args << 'a'
139
+ expect_lookup_with_output(0, /--- value a/)
117
140
  end
118
141
 
119
142
  it 'configured in Puppet settings with --compile' do
120
143
  Puppet.settings[:node_terminus] = 'exec'
121
144
  expect_any_instance_of(Puppet::Node::Plain).not_to receive(:find)
122
145
  expect_any_instance_of(Puppet::Node::Exec).to receive(:find).and_return(node)
123
- expect(lookup('a', :compile => true)).to eql('value a')
146
+
147
+ app.command_line.args << 'a' << '--compile'
148
+ expect_lookup_with_output(0, /--- value a/)
124
149
  end
125
150
  end
126
151
 
127
152
  context 'configured with the wrong environment' do
128
- let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }
129
153
  it 'does not find data in non-existing environment' do
130
- Puppet.override(:environments => environments, :current_environment => 'someother') do
131
- expect(lookup('a', {}, true)).to match(/did not find a value for the name 'a'/)
132
- end
154
+ Puppet[:environment] = 'doesntexist'
155
+ app.command_line.args << 'a'
156
+ expect { app.run }.to raise_error(Puppet::Environments::EnvironmentNotFound, /Could not find a directory environment named 'doesntexist'/)
133
157
  end
134
158
  end
135
159
 
@@ -174,15 +198,22 @@ describe 'lookup' do
174
198
  end
175
199
 
176
200
  it 'finds data in the module' do
177
- expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
201
+ app.command_line.args << 'mod_a::b'
202
+ expect_lookup_with_output(0, /value mod_a::b \(from mod_a\)/)
178
203
  end
179
204
 
180
205
  it 'finds quoted keys in the module' do
181
- expect(lookup('"mod_a::a.quoted.key"')).to eql('value mod_a::a.quoted.key (from mod_a)')
206
+ app.command_line.args << "'mod_a::a.quoted.key'"
207
+ expect_lookup_with_output(0, /value mod_a::a.quoted.key \(from mod_a\)/)
182
208
  end
183
209
 
184
210
  it 'merges hashes from environment and module when merge strategy hash is used' do
185
- expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})
211
+ app.command_line.args << 'mod_a::hash_a' << '--merge' << 'hash'
212
+ expect_lookup_with_output(0, <<~END)
213
+ ---
214
+ a: value mod_a::hash_a.a (from environment)
215
+ b: value mod_a::hash_a.b (from mod_a)
216
+ END
186
217
  end
187
218
  end
188
219
  end
@@ -28,14 +28,18 @@ describe "puppet resource", unless: Puppet::Util::Platform.jruby? do
28
28
  end
29
29
 
30
30
  it 'lists types from the default environment' do
31
+ begin
31
32
  modulepath = File.join(Puppet[:codedir], 'modules', 'test', 'lib', 'puppet', 'type')
32
33
  FileUtils.mkdir_p(modulepath)
33
- File.write(File.join(modulepath, 'test.rb'), 'Puppet::Type.newtype(:test)')
34
+ File.write(File.join(modulepath, 'test_resource_spec.rb'), 'Puppet::Type.newtype(:test_resource_spec)')
34
35
  resource.command_line.args = ['--types']
35
36
 
36
37
  expect {
37
38
  resource.run
38
- }.to exit_with(0).and output(/test/).to_stdout
39
+ }.to exit_with(0).and output(/test_resource_spec/).to_stdout
40
+ ensure
41
+ Puppet::Type.rmtype(:test_resource_spec)
42
+ end
39
43
  end
40
44
  end
41
45
 
@@ -77,7 +77,13 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
77
77
  }
78
78
  }
79
79
 
80
- it "mutually authenticates the connection" do
80
+ let(:cert_file) do
81
+ res = tmpfile('cert_file')
82
+ File.write(res, https_server.ca_cert)
83
+ res
84
+ end
85
+
86
+ it "mutually authenticates the connection using an explicit context" do
81
87
  client_context = ssl_provider.create_context(
82
88
  cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
83
89
  client_cert: https_server.server_cert, private_key: https_server.server_key
@@ -88,6 +94,47 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
88
94
  expect(res).to be_success
89
95
  end
90
96
  end
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
+
115
+ it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
116
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
117
+ client_context = ssl_provider.create_context(
118
+ cacerts: [], crls: [],
119
+ client_cert: https_server.server_cert, private_key: https_server.server_key,
120
+ revocation: false, include_system_store: true
121
+ )
122
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
123
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
124
+ expect(res).to be_success
125
+ end
126
+ end
127
+ end
128
+
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
131
+ client_context = ssl_provider.load_context(revocation: false, include_system_store: true)
132
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
133
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
134
+ expect(res).to be_success
135
+ end
136
+ end
137
+ end
91
138
  end
92
139
 
93
140
  context "with a system trust store" do
@@ -102,12 +149,12 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
102
149
 
103
150
  it "connects when the server's CA is in the system store" do
104
151
  # create a temp cacert bundle
105
- ssl_file = tmpfile('systemstore')
106
- File.write(ssl_file, https_server.ca_cert)
152
+ cert_file = tmpfile('cert_file')
153
+ File.write(cert_file, https_server.ca_cert)
107
154
 
108
155
  # override path to system cacert bundle, this must be done before
109
156
  # the SSLContext is created and the call to X509::Store.set_default_paths
110
- Puppet::Util.withenv("SSL_CERT_FILE" => ssl_file) do
157
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
111
158
  system_context = ssl_provider.create_system_context(cacerts: [])
112
159
  https_server.start_server do |port|
113
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
@@ -4,7 +4,11 @@ RSpec.shared_context('l10n') do |locale|
4
4
  before :all do
5
5
  @old_locale = Locale.current
6
6
  Locale.current = locale
7
+
8
+ @old_gettext_disabled = Puppet::GettextConfig.instance_variable_get(:@gettext_disabled)
9
+ Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, false)
7
10
  Puppet::GettextConfig.setup_locale
11
+ Puppet::GettextConfig.create_default_text_domain
8
12
 
9
13
  # overwrite stubs with real implementation
10
14
  ::Object.send(:remove_method, :_)
@@ -17,6 +21,7 @@ RSpec.shared_context('l10n') do |locale|
17
21
  after :all do
18
22
  Locale.current = @old_locale
19
23
 
24
+ Puppet::GettextConfig.instance_variable_set(:@gettext_disabled, @old_gettext_disabled)
20
25
  # restore stubs
21
26
  load File.expand_path(File.join(__dir__, '../../lib/puppet/gettext/stubs.rb'))
22
27
  end
@@ -38,6 +38,10 @@ describe Puppet::Agent do
38
38
  end
39
39
  end
40
40
  end
41
+
42
+ ssl_context = Puppet::SSL::SSLContext.new
43
+ machine = instance_double("Puppet::SSL::StateMachine", ensure_client_certificate: ssl_context)
44
+ allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)
41
45
  end
42
46
 
43
47
  after do
@@ -99,6 +103,8 @@ describe Puppet::Agent do
99
103
  end
100
104
 
101
105
  it "should splay" do
106
+ Puppet[:splay] = true
107
+
102
108
  expect(@agent).to receive(:splay)
103
109
 
104
110
  @agent.run
@@ -181,6 +187,26 @@ describe Puppet::Agent do
181
187
  expect(@agent.run).to eq(:result)
182
188
  end
183
189
 
190
+ it "should check if it's disabled after splaying and log a message" do
191
+ Puppet[:splay] = true
192
+ Puppet[:splaylimit] = '5s'
193
+ Puppet[:onetime] = true
194
+
195
+ expect(@agent).to receive(:disabled?).and_return(false, true)
196
+
197
+ allow(Puppet).to receive(:notice).and_call_original
198
+ expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
199
+ @agent.run
200
+ end
201
+
202
+ it "should check if it's disabled after acquiring the lock and log a message" do
203
+ expect(@agent).to receive(:disabled?).and_return(false, true)
204
+
205
+ allow(Puppet).to receive(:notice).and_call_original
206
+ expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
207
+ @agent.run
208
+ end
209
+
184
210
  describe "and a puppet agent is already running" do
185
211
  before(:each) do
186
212
  allow_any_instance_of(Object).to receive(:sleep)
@@ -197,7 +223,7 @@ describe Puppet::Agent do
197
223
  @agent.run
198
224
  end
199
225
 
200
- it "should inform that a run is already in progres and try to run every X seconds if waitforlock is used" do
226
+ it "should inform that a run is already in progress and try to run every X seconds if waitforlock is used" do
201
227
  # so the locked file exists
202
228
  allow(File).to receive(:file?).and_return(true)
203
229
  # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)
@@ -226,7 +252,7 @@ describe Puppet::Agent do
226
252
  @agent.run
227
253
  end
228
254
  end
229
-
255
+
230
256
  describe "when should_fork is true", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do
231
257
  before do
232
258
  @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