openvox 8.29.0 → 8.31.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86d05e7a3a189241096c6be834d26a8674a70cadfac8c90acce427992d619ad2
4
- data.tar.gz: 430fe316be1706073496df35c7c5316d6402366f51b1f8a099a248769fe02960
3
+ metadata.gz: ece318da56b45d387a1226f12d25f99701b1aec5c8bd24c14bfe56db28b9e664
4
+ data.tar.gz: a4b801c632d5c9299c7d292184f184aa153372f5c9e6a8b695a43a56d7affa76
5
5
  SHA512:
6
- metadata.gz: 914e4b11f6727fd2ab33dcdd76cfa3ef69626ed2b8217be0176bf58fafd6e89504096947715a1345f4c657986313f04918c27a1c639d553f63e6f8cb24c4b0cf
7
- data.tar.gz: ac56bb6bd94bc23edc49986eaa92e8db16376865ae60b6833f86ad5615cab95857b0d08fbce52895517821d77aa5e954f4cdffe702ba2979514c08688bd98d9f
6
+ metadata.gz: 317ad75eebd895d0f718250404861d3090a32569a73b4ca8afe3f09f1ce3440399d99bf0b36eb4199e364f9ffeb592a82cac37ffae59ec0c21d943eb467fcfab
7
+ data.tar.gz: 4b968c2474e46628e7e06d9937de4d4e7cb3738436c3353491fd9ba2b2ef2ea6624215a9c6e0bfc0a9e8599f0f486f86cd6729878107b113952adef2e16a2123
data/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [8.31.0](https://github.com/openvoxproject/openvox/tree/8.31.0) (2026-10-12)
6
+
7
+ [Full Changelog](https://github.com/openvoxproject/openvox/compare/8.30.0...8.31.0)
8
+
9
+ **Implemented enhancements:**
10
+
11
+ - \[Backport 8.x\] Port Puppet::Util::Json to the json 3 API [\#721](https://github.com/OpenVoxProject/openvox/pull/721) ([miharp](https://github.com/miharp))
12
+
13
+ **Merged pull requests:**
14
+
15
+ - Promote puppet-runtime 2026.10.12.2 into 8.x [\#723](https://github.com/OpenVoxProject/openvox/pull/723) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
16
+
17
+ ## [8.30.0](https://github.com/openvoxproject/openvox/tree/8.30.0) (2026-10-07)
18
+
19
+ [Full Changelog](https://github.com/openvoxproject/openvox/compare/8.29.0...8.30.0)
20
+
21
+ **Fixed bugs:**
22
+
23
+ - \[Bug\]: use of "server\_list" \(instead of server\) throws an error [\#658](https://github.com/OpenVoxProject/openvox/issues/658)
24
+ - \[Bug\]: Ruby crashes on child side of fork pre-exec macOS 26 \(Tahoe\) [\#538](https://github.com/OpenVoxProject/openvox/issues/538)
25
+ - \[Backport 8.x\] Pin json below 3 to unblock CI [\#676](https://github.com/OpenVoxProject/openvox/pull/676) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
26
+ - \[backport 8.x\] fix: `Deferred` resolution of Puppet-language functions \(e.g. mocks in tests\) failing due to lack of `:global_scope` [\#671](https://github.com/OpenVoxProject/openvox/pull/671) ([griggi-ws](https://github.com/griggi-ws))
27
+ - \[Backport 8.x\] print "Loading facts" just once [\#663](https://github.com/OpenVoxProject/openvox/pull/663) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
28
+
29
+ **Closed issues:**
30
+
31
+ - json 3.0.0 breaks Puppet::Util::Json and the test suite [\#654](https://github.com/OpenVoxProject/openvox/issues/654)
32
+
33
+ **Merged pull requests:**
34
+
35
+ - \[Backport 8.x\] Skip the tests that need jruby-openssl in FIPS mode [\#714](https://github.com/OpenVoxProject/openvox/pull/714) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
36
+ - Promote openfact 5.7.2 into 8.x [\#711](https://github.com/OpenVoxProject/openvox/pull/711) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
37
+ - Promote puppet-runtime 2026.09.29.1 into 8.x [\#704](https://github.com/OpenVoxProject/openvox/pull/704) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
38
+ - \[Backport 8.x\] Kill the forked agent run if it outlives runtimeout [\#698](https://github.com/OpenVoxProject/openvox/pull/698) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
39
+ - \[Backport 8.x\] Wait for certificates in the forked child, not in the daemon [\#697](https://github.com/OpenVoxProject/openvox/pull/697) ([nmburgan](https://github.com/nmburgan))
40
+ - openfact: Update 5.7.0 -\> 5.7.1 [\#678](https://github.com/OpenVoxProject/openvox/pull/678) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
41
+ - \[Backport 8.x\] Proper markdown for alias reference documentation. [\#650](https://github.com/OpenVoxProject/openvox/pull/650) ([OpenVoxProjectBot](https://github.com/OpenVoxProjectBot))
42
+
5
43
  ## [8.29.0](https://github.com/openvoxproject/openvox/tree/8.29.0) (2026-09-04)
6
44
 
7
45
  [Full Changelog](https://github.com/openvoxproject/openvox/compare/8.28.1...8.29.0)
data/Gemfile CHANGED
@@ -77,7 +77,7 @@ group(:packaging) do
77
77
  gem 'vanagon', *location_for(ENV['VANAGON_LOCATION'] || 'https://github.com/openvoxproject/vanagon#main')
78
78
  end
79
79
  gem 'artifactory'
80
- gem 'json'
80
+ gem 'json', '< 4'
81
81
  gem 'octokit'
82
82
  end
83
83
 
data/Rakefile CHANGED
@@ -159,7 +159,7 @@ begin
159
159
  HEADER
160
160
  config.user = "openvoxproject"
161
161
  config.project = "openvox"
162
- config.exclude_labels = %w[dependencies duplicate question invalid wontfix wont-fix modulesync skip-changelog]
162
+ config.exclude_labels = %w[dependencies duplicate question invalid wontfix wont-fix modulesync skip-changelog skip-8.x-changelog]
163
163
  config.since_tag = "8.18.1"
164
164
  config.future_release = Puppet::PUPPETVERSION
165
165
  config.exclude_tags_regex = /\A[7,9]\./
data/lib/puppet/agent.rb CHANGED
@@ -22,6 +22,12 @@ class Puppet::Agent
22
22
  class RunTimeoutError < Exception # rubocop:disable Lint/InheritException
23
23
  end
24
24
 
25
+ # Exit status the forked child uses to tell the daemon that the SSL state
26
+ # machine gave up waiting for a certificate, so the daemon can exit as it
27
+ # did when the wait happened in the parent. Must not collide with the
28
+ # detailed exit codes returned by a run (0, 1, 2, 4, 6) or with 254 (NoMemoryError).
29
+ CERTIFICATE_FAILURE_EXIT_STATUS = 253
30
+
25
31
  attr_reader :client_class, :client, :should_fork
26
32
 
27
33
  def initialize(client_class, should_fork = true)
@@ -60,12 +66,28 @@ class Puppet::Agent
60
66
  end
61
67
  end
62
68
 
63
- # waiting for certs may sleep for awhile depending on onetime, waitforcert and maxwaitforcert!
64
- # this needs to happen before forking so that if we fail to obtain certs and try to exit, then
65
- # we exit the main process and not the forked child.
66
- ssl_context = wait_for_certificates(client_options)
67
-
68
69
  result = run_in_fork(should_fork) do
70
+ # waiting for certs may sleep for awhile depending on onetime, waitforcert and maxwaitforcert!
71
+ #
72
+ # This happens in the forked child, not in the long-lived daemon. Refreshing
73
+ # the CRL or renewing the certificate resolves the server's name, and on
74
+ # Ruby 3.3+ that runs in short-lived native threads. If the daemon forks
75
+ # while one of those threads is exiting, the child inherits glibc's
76
+ # resolv.conf lock in the locked state and every name lookup in the run
77
+ # hangs (#485). Keeping the lookups out of the parent avoids that.
78
+ #
79
+ # The state machine calls exit when it gives up waiting for a certificate
80
+ # (waitforcert 0 or maxwaitforcert exceeded). Report that back so the
81
+ # daemon exits, as it did when the wait happened in the parent.
82
+ begin
83
+ ssl_context = wait_for_certificates(client_options)
84
+ rescue SystemExit
85
+ next CERTIFICATE_FAILURE_EXIT_STATUS
86
+ rescue StandardError => detail
87
+ Puppet.log_exception(detail, _("Could not obtain certificates: %{detail}") % { detail: detail })
88
+ next nil
89
+ end
90
+
69
91
  with_client(client_options[:transaction_uuid], client_options[:job_id]) do |client|
70
92
  client_args = client_options.merge(:pluginsync => Puppet::Configurer.should_pluginsync?)
71
93
  begin
@@ -115,6 +137,9 @@ class Puppet::Agent
115
137
  end
116
138
  end
117
139
  end
140
+ # The child could not obtain a certificate and the state machine asked to exit.
141
+ exit(1) if result == CERTIFICATE_FAILURE_EXIT_STATUS
142
+
118
143
  true
119
144
  end
120
145
  Puppet.notice _("Shutdown/restart in progress (%{status}); skipping run") % { status: Puppet::Application.run_status.inspect } unless block_run
@@ -146,8 +171,55 @@ class Puppet::Agent
146
171
  atForkHandler.parent
147
172
  end
148
173
 
149
- exit_code = Process.waitpid2(child_pid)
150
- exit_code[1].exitstatus
174
+ _, status = wait_for_child(child_pid)
175
+ status.exitstatus
176
+ end
177
+
178
+ # Wait for the forked child to exit. The child enforces `runtimeout` on its
179
+ # own run, but it may fail to exit afterwards (a stranded thread, a hung
180
+ # subprocess, etc.). Without a deadline here, the parent blocks forever and
181
+ # the agent stops checking in. Once the run timeout plus a grace period has
182
+ # elapsed, the child is killed so the daemon can carry on.
183
+ def wait_for_child(child_pid)
184
+ deadline = child_deadline
185
+ return Process.waitpid2(child_pid) unless deadline
186
+
187
+ loop do
188
+ result = Process.waitpid2(child_pid, Process::WNOHANG)
189
+ return result if result
190
+
191
+ if monotonic_now >= deadline
192
+ Puppet.err _("Agent run (pid %{pid}) did not exit within %{timeout} seconds of the run timeout, killing it") %
193
+ { pid: child_pid, timeout: child_grace_period }
194
+ begin
195
+ Process.kill(:KILL, child_pid)
196
+ rescue Errno::ESRCH
197
+ # The child exited between the last check and the kill.
198
+ end
199
+ return Process.waitpid2(child_pid)
200
+ end
201
+
202
+ sleep 1
203
+ end
204
+ end
205
+
206
+ # After the run timeout fires inside the child, it may still need to send
207
+ # its report, which is bounded by the HTTP connect and read timeouts.
208
+ def child_grace_period
209
+ Puppet[:http_connect_timeout] + Puppet[:http_read_timeout]
210
+ end
211
+
212
+ # Returns the absolute monotonic deadline for the child, or nil when
213
+ # `runtimeout` is disabled.
214
+ def child_deadline
215
+ runtimeout = Puppet[:runtimeout]
216
+ return nil if runtimeout.nil? || runtimeout <= 0
217
+
218
+ monotonic_now + runtimeout + child_grace_period
219
+ end
220
+
221
+ def monotonic_now
222
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
151
223
  end
152
224
 
153
225
  private
@@ -384,7 +384,7 @@ Puppet::Face.define(:epp, '0.0.1') do
384
384
  pn = Puppet::Pops::Model::PNTransformer.transform(parse_result)
385
385
  case fmt
386
386
  when 'json'
387
- options[:pretty] ? JSON.pretty_unparse(pn.to_data) : JSON.dump(pn.to_data)
387
+ options[:pretty] ? JSON.pretty_generate(pn.to_data) : JSON.dump(pn.to_data)
388
388
  else
389
389
  pn.format(options[:pretty] ? Puppet::Pops::PN::Indent.new(' ') : nil, output)
390
390
  end
@@ -194,7 +194,7 @@ Puppet::Face.define(:parser, '0.0.1') do
194
194
  pn = Puppet::Pops::Model::PNTransformer.transform(parse_result)
195
195
  case fmt
196
196
  when 'json'
197
- options[:pretty] ? JSON.pretty_unparse(pn.to_data) : JSON.dump(pn.to_data)
197
+ options[:pretty] ? JSON.pretty_generate(pn.to_data) : JSON.dump(pn.to_data)
198
198
  else
199
199
  pn.format(options[:pretty] ? Puppet::Pops::PN::Indent.new(' ') : nil, output)
200
200
  end
@@ -62,21 +62,22 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
62
62
  end
63
63
  end.flatten + Puppet[:factpath].split(File::PATH_SEPARATOR)
64
64
 
65
- dirs = dirs.select do |dir|
66
- next false unless FileTest.directory?(dir)
67
-
68
- # Even through we no longer directly load facts in the terminus,
69
- # print out each .rb in the facts directory as module
70
- # developers may find that information useful for debugging purposes
71
- if Puppet::Util::Log.sendlevel?(:info)
72
- Puppet.info _("Loading facts")
73
- Dir.glob("#{dir}/*.rb").each do |file|
74
- Puppet.debug { "Loading facts from #{file}" }
65
+ dirs.select! { |dir| FileTest.directory?(dir) }
66
+
67
+ # Even though we no longer directly load facts in the terminus,
68
+ # print out each .rb in the facts directory as module
69
+ # developers may find that information useful for debugging purposes
70
+ unless dirs.empty?
71
+ Puppet.info _("Loading facts")
72
+ if Puppet::Util::Log.sendlevel?(:debug)
73
+ dirs.each do |dir|
74
+ Dir.glob("#{dir}/*.rb").each do |file|
75
+ Puppet.debug { "Loading facts from #{file}" }
76
+ end
75
77
  end
76
78
  end
77
-
78
- true
79
79
  end
80
+
80
81
  dirs << request.options[:custom_dir] if request.options[:custom_dir]
81
82
  Puppet.runtime[:facter].search(*dirs)
82
83
  end
@@ -58,7 +58,7 @@ class JsonCatalogEncoder
58
58
  r = possibly_filtered_catalog.resource(type, title)
59
59
  return nil if r.nil?
60
60
 
61
- r.to_data_hash.to_json(:pretty => pretty)
61
+ Puppet::Util::Json.dump(r.to_data_hash, :pretty => pretty)
62
62
  end
63
63
 
64
64
  # Applies a filter for virtual resources and returns filtered catalog
@@ -48,7 +48,10 @@ class DeferredResolver
48
48
  # # Server facts are always about the local node's version etc.
49
49
  # @scope.set_server_facts(node.server_facts)
50
50
 
51
- resolver.resolve_futures(catalog)
51
+ # Must be wrapped in with_context_overrides to ensure global_scope is valid for Puppet-language functions
52
+ compiler.with_context_overrides do
53
+ resolver.resolve_futures(catalog)
54
+ end
52
55
  nil
53
56
  end
54
57
 
@@ -14,7 +14,7 @@ etc.), prevent Puppet from making changes (`noop`), and change logging verbosity
14
14
 
15
15
  ## Available Metaparameters
16
16
 
17
- }.dup
17
+ }.dup.strip + "\n"
18
18
  begin
19
19
  params = []
20
20
  Puppet::Type.eachmetaparam { |param|
@@ -18,6 +18,14 @@ module Puppet::Util
18
18
 
19
19
  require 'json'
20
20
 
21
+ # Generator options equivalent to the `JSON::PRETTY_STATE_PROTOTYPE`
22
+ # that json 2.x provided and json 3 removed.
23
+ PRETTY_OPTIONS = { :indent => ' ', :space => ' ', :object_nl => "\n", :array_nl => "\n" }.freeze
24
+
25
+ # Generator options that json 3 rejects as unknown. `quirks_mode` has been
26
+ # a no-op since json 2.0, when quirks mode became the only mode.
27
+ OBSOLETE_DUMP_OPTIONS = [:quirks_mode].freeze
28
+
21
29
  # Load the content from a file as JSON if
22
30
  # contents are in valid format. This method does not
23
31
  # raise error but returns `nil` when invalid file is
@@ -41,16 +49,20 @@ module Puppet::Util
41
49
  def self.load(string, options = {})
42
50
  string = string.read if string.respond_to?(:read)
43
51
 
52
+ options = options.dup
44
53
  options[:symbolize_names] = true if options.delete(:symbolize_keys)
45
- ::JSON.parse(string, options)
54
+ # json 3 only accepts parser options as keyword arguments
55
+ ::JSON.parse(string, **options)
46
56
  rescue JSON::ParserError => e
47
57
  raise Puppet::Util::Json::ParseError.build(e, string)
48
58
  end
49
59
 
50
60
  def self.dump(object, options = {})
51
61
  # Options is a state when we're being called recursively
52
- if !options.is_a?(JSON::State) && options.delete(:pretty)
53
- options.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h)
62
+ unless options.is_a?(JSON::State)
63
+ options = options.dup
64
+ OBSOLETE_DUMP_OPTIONS.each { |key| options.delete(key) }
65
+ options.merge!(PRETTY_OPTIONS) if options.delete(:pretty)
54
66
  end
55
67
  object.to_json(options)
56
68
  end
@@ -8,7 +8,7 @@
8
8
  # Raketasks and such to set the version based on the output of `git describe`
9
9
 
10
10
  module Puppet
11
- PUPPETVERSION = '8.29.0'
11
+ PUPPETVERSION = '8.31.0'
12
12
  IMPLEMENTATION = 'openvox'
13
13
 
14
14
  ##
data/tasks/vox.rake CHANGED
@@ -9,8 +9,8 @@ namespace :vox do
9
9
  puts "Setting version to #{version}"
10
10
 
11
11
  data = File.read('lib/puppet/version.rb')
12
- new_data = data.sub(/PUPPETVERSION = '\d+\.\d+\.\d+(\.rc\d+)?'/, "PUPPETVERSION = '#{version}'")
13
- warn 'Failed to update version in lib/puppet/version.rb' if data == new_data
12
+ new_data = data.sub(/PUPPETVERSION = '\d+\.\d+\.\d+(-(alpha|beta|rc)\d+)?'/, "PUPPETVERSION = '#{version}'")
13
+ fail 'Failed to update version in lib/puppet/version.rb' if data == new_data
14
14
 
15
15
  File.write('lib/puppet/version.rb', new_data)
16
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openvox
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.29.0
4
+ version: 8.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenVox Project