bolt 3.22.1 → 3.23.0

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

Potentially problematic release.


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

@@ -12,13 +12,16 @@ module Bolt
12
12
  end
13
13
 
14
14
  TEMPLATE_OPTS = %w[alias config facts features name uri vars].freeze
15
- PLUGIN_OPTS = %w[_plugin _cache query target_mapping].freeze
15
+ PLUGIN_OPTS = %w[_plugin _cache query target_mapping instance].freeze
16
16
 
17
17
  attr_reader :puppetdb_client
18
18
 
19
19
  def initialize(config:, context:)
20
- pdb_config = Bolt::PuppetDB::Config.load_config(config, context.boltdir)
21
- @puppetdb_client = Bolt::PuppetDB::Client.new(pdb_config)
20
+ @puppetdb_client = Bolt::PuppetDB::Client.new(default: config.delete('default'),
21
+ instances: config.delete('instances') || {},
22
+ config: config,
23
+ project: context.boltdir)
24
+
22
25
  @logger = Bolt::Logger.logger(self)
23
26
  end
24
27
 
@@ -59,7 +62,7 @@ module Bolt
59
62
  end
60
63
 
61
64
  def resolve_reference(opts)
62
- targets = @puppetdb_client.query_certnames(opts['query'])
65
+ targets = @puppetdb_client.query_certnames(opts['query'], opts['instance'])
63
66
  facts = []
64
67
 
65
68
  template = opts.delete('target_mapping') || {}
data/lib/bolt/plugin.rb CHANGED
@@ -151,7 +151,8 @@ module Bolt
151
151
  msg = "Configuration for the PuppetDB plugin must be in the 'puppetdb' config section, not 'plugins'"
152
152
  raise Bolt::Error.new(msg, 'bolt/plugin-error')
153
153
  end
154
- @unresolved_plugin_configs['puppetdb'] = config.puppetdb if config.puppetdb
154
+ @unresolved_plugin_configs['puppetdb'] = config.puppetdb.merge('default' => config.default_puppetdb,
155
+ 'instances' => config.puppetdb_instances)
155
156
  end
156
157
 
157
158
  # Returns a map of configured plugin hooks. Any unresolved plugin references
@@ -2,34 +2,90 @@
2
2
 
3
3
  require 'json'
4
4
  require 'logging'
5
+ require_relative '../../bolt/puppetdb/instance'
5
6
 
6
7
  module Bolt
7
8
  module PuppetDB
8
9
  class Client
9
- attr_reader :config
10
-
11
- def initialize(config)
12
- @config = config
13
- @bad_urls = []
14
- @current_url = nil
10
+ # @param config [Hash] A map of default PuppetDB configuration.
11
+ # @param instances [Hash] A map of configuration for named PuppetDB instances.
12
+ # @param default [String] The name of PuppetDB instance to use as the default.
13
+ # @param project [String] The path to the Bolt project.
14
+ #
15
+ def initialize(config:, instances: {}, default: nil, project: nil)
15
16
  @logger = Bolt::Logger.logger(self)
17
+
18
+ @instances = instances.transform_values do |instance_config|
19
+ Bolt::PuppetDB::Instance.new(config: instance_config, project: project)
20
+ end
21
+
22
+ @default_instance = if default
23
+ validate_instance(default)
24
+ @instances[default]
25
+ else
26
+ Bolt::PuppetDB::Instance.new(config: config, project: project, load_defaults: true)
27
+ end
28
+ end
29
+
30
+ # Checks whether a given named instance is configured, erroring if not.
31
+ #
32
+ # @param name [String] The name of the PuppetDB instance.
33
+ #
34
+ private def validate_instance(name)
35
+ unless @instances[name]
36
+ raise Bolt::PuppetDBError, "PuppetDB instance '#{name}' has not been configured, unable to connect"
37
+ end
38
+ end
39
+
40
+ # Yields the PuppetDB instance to connect to.
41
+ #
42
+ # @param name [String] The name of the PuppetDB instance.
43
+ # @yield [Bolt::PuppetDB::Instance]
44
+ #
45
+ private def with_instance(name = nil)
46
+ yield instance(name)
16
47
  end
17
48
 
18
- def query_certnames(query)
49
+ # Selects the PuppetDB instance to connect to. If an instance is not specified,
50
+ # the default instance is used.
51
+ #
52
+ # @param name [String] The name of the PuppetDB instance.
53
+ # @return [Bolt::PuppetDB::Instance]
54
+ #
55
+ def instance(name = nil)
56
+ if name
57
+ validate_instance(name)
58
+ @instances[name]
59
+ else
60
+ @default_instance
61
+ end
62
+ end
63
+
64
+ # Queries certnames from the PuppetDB instance.
65
+ #
66
+ # @param query [String] The PDB query.
67
+ # @param instance [String] The name of the PuppetDB instance.
68
+ #
69
+ def query_certnames(query, instance = nil)
19
70
  return [] unless query
20
71
 
21
72
  @logger.debug("Querying certnames")
22
- results = make_query(query)
73
+ results = make_query(query, nil, instance)
23
74
 
24
75
  if results&.first && !results.first&.key?('certname')
25
76
  fields = results.first&.keys
26
77
  raise Bolt::PuppetDBError, "Query results did not contain a 'certname' field: got #{fields.join(', ')}"
27
78
  end
79
+
28
80
  results&.map { |result| result['certname'] }&.uniq
29
81
  end
30
82
 
31
- # This method expects an array of certnames to get facts for
32
- def facts_for_node(certnames)
83
+ # Retrieve facts from PuppetDB for a list of nodes.
84
+ #
85
+ # @param certnames [Array] The list of certnames to retrieve facts for.
86
+ # @param instance [String] The name of the PuppetDB instance.
87
+ #
88
+ def facts_for_node(certnames, instance = nil)
33
89
  return {} if certnames.empty? || certnames.nil?
34
90
 
35
91
  certnames.uniq!
@@ -37,14 +93,20 @@ module Bolt
37
93
  name_query.insert(0, "or")
38
94
 
39
95
  @logger.debug("Querying certnames")
40
- result = make_query(name_query, 'inventory')
96
+ result = make_query(name_query, 'inventory', instance)
41
97
 
42
98
  result&.each_with_object({}) do |node, coll|
43
99
  coll[node['certname']] = node['facts']
44
100
  end
45
101
  end
46
102
 
47
- def fact_values(certnames = [], facts = [])
103
+ # Retrive fact values for a list of nodes.
104
+ #
105
+ # @param certnames [Array] The list of certnames to retrieve fact values for.
106
+ # @param facts [Array] The list of facts to retrive.
107
+ # @param instance [String] The name of the PuppetDB instance.
108
+ #
109
+ def fact_values(certnames = [], facts = [], instance = nil)
48
110
  return {} if certnames.empty? || facts.empty?
49
111
 
50
112
  certnames.uniq!
@@ -57,135 +119,34 @@ module Bolt
57
119
  query = ['and', name_query, facts_query]
58
120
 
59
121
  @logger.debug("Querying certnames")
60
- result = make_query(query, 'fact-contents')
122
+ result = make_query(query, 'fact-contents', instance)
61
123
  result.map! { |h| h.delete_if { |k, _v| %w[environment name].include?(k) } }
62
124
  result.group_by { |c| c['certname'] }
63
125
  end
64
126
 
65
- def make_query(query, path = nil)
66
- body = JSON.generate(query: query)
67
- url = "#{uri}/pdb/query/v4"
68
- url += "/#{path}" if path
69
-
70
- begin
71
- @logger.debug("Sending PuppetDB query to #{url}")
72
- response = http_client.post(url, body: body, header: headers)
73
- rescue StandardError => e
74
- raise Bolt::PuppetDBFailoverError, "Failed to query PuppetDB: #{e}"
75
- end
76
-
77
- @logger.debug("Got response code #{response.code} from PuppetDB")
78
- if response.code != 200
79
- msg = "Failed to query PuppetDB: #{response.body}"
80
- if response.code == 400
81
- raise Bolt::PuppetDBError, msg
82
- else
83
- raise Bolt::PuppetDBFailoverError, msg
84
- end
85
- end
86
-
87
- begin
88
- JSON.parse(response.body)
89
- rescue JSON::ParserError
90
- raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
91
- end
92
- rescue Bolt::PuppetDBFailoverError => e
93
- @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
94
- reject_url
95
- make_query(query, path)
96
- end
97
-
98
- # Sends a command to PuppetDB using version 1 of the commands API.
99
- # https://puppet.com/docs/puppetdb/latest/api/command/v1/commands.html
127
+ # Sends a command to PuppetDB using the commands API.
100
128
  #
101
129
  # @param command [String] The command to invoke.
102
130
  # @param version [Integer] The version of the command to invoke.
103
131
  # @param payload [Hash] The payload to send with the command.
104
- # @return A UUID identifying the submitted command.
105
- #
106
- def send_command(command, version, payload)
107
- command = command.dup.force_encoding('utf-8')
108
- body = JSON.generate(payload)
109
-
110
- # PDB requires the following query parameters to the POST request.
111
- # Error early if there's no certname, as PDB does not return a
112
- # message indicating it's required.
113
- unless payload['certname']
114
- raise Bolt::Error.new(
115
- "Payload must include 'certname', unable to invoke command.",
116
- 'bolt/pdb-command'
117
- )
118
- end
119
-
120
- url = uri.tap do |u|
121
- u.path = 'pdb/cmd/v1'
122
- u.query_values = { 'command' => command,
123
- 'version' => version,
124
- 'certname' => payload['certname'] }
125
- end
126
-
127
- # Send the command to PDB
128
- begin
129
- @logger.debug("Sending PuppetDB command '#{command}' to #{url}")
130
- response = http_client.post(url.to_s, body: body, header: headers)
131
- rescue StandardError => e
132
- raise Bolt::PuppetDBFailoverError, "Failed to invoke PuppetDB command: #{e}"
133
- end
134
-
135
- @logger.debug("Got response code #{response.code} from PuppetDB")
136
- if response.code != 200
137
- raise Bolt::PuppetDBError, "Failed to invoke PuppetDB command: #{response.body}"
138
- end
139
-
140
- # Return the UUID string from the response body
141
- begin
142
- JSON.parse(response.body).fetch('uuid', nil)
143
- rescue JSON::ParserError
144
- raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
132
+ # @param instance [String] The name of the PuppetDB instance.
133
+ #
134
+ def send_command(command, version, payload, instance = nil)
135
+ with_instance(instance) do |pdb|
136
+ pdb.send_command(command, version, payload)
145
137
  end
146
- rescue Bolt::PuppetDBFailoverError => e
147
- @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
148
- reject_url
149
- send_command(command, version, payload)
150
- end
151
-
152
- def http_client
153
- return @http if @http
154
- # lazy-load expensive gem code
155
- require 'httpclient'
156
- @logger.trace("Creating HTTP Client")
157
- @http = HTTPClient.new
158
- @http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
159
- @http.ssl_config.add_trust_ca(@config.cacert)
160
- @http.connect_timeout = @config.connect_timeout if @config.connect_timeout
161
- @http.receive_timeout = @config.read_timeout if @config.read_timeout
162
-
163
- @http
164
- end
165
-
166
- def reject_url
167
- @bad_urls << @current_url if @current_url
168
- @current_url = nil
169
138
  end
170
139
 
171
- def uri
172
- require 'addressable/uri'
173
-
174
- @current_url ||= (@config.server_urls - @bad_urls).first
175
- unless @current_url
176
- msg = "Failed to connect to all PuppetDB server_urls: #{@config.server_urls.to_a.join(', ')}."
177
- raise Bolt::PuppetDBError, msg
140
+ # Sends a query to PuppetDB.
141
+ #
142
+ # @param query [String] The query to send to PuppetDB.
143
+ # @param path [String] The API path to append to the query URL.
144
+ # @param instance [String] The name of the PuppetDB instance.
145
+ #
146
+ def make_query(query, path = nil, instance = nil)
147
+ with_instance(instance) do |pdb|
148
+ pdb.make_query(query, path)
178
149
  end
179
-
180
- uri = Addressable::URI.parse(@current_url)
181
- uri.port ||= 8081
182
- uri
183
- end
184
-
185
- def headers
186
- headers = { 'Content-Type' => 'application/json' }
187
- headers['X-Authentication'] = @config.token if @config.token
188
- headers
189
150
  end
190
151
  end
191
152
  end
@@ -17,11 +17,30 @@ module Bolt
17
17
 
18
18
  end
19
19
 
20
+ def initialize(config:, project: nil, load_defaults: false)
21
+ @settings = if load_defaults
22
+ self.class.default_config.merge(config)
23
+ else
24
+ config
25
+ end
26
+
27
+ expand_paths(project)
28
+ end
29
+
30
+ # Returns the path to the puppetdb.conf file on Windows.
31
+ #
32
+ # @return [String]
33
+ #
20
34
  def self.default_windows_config
21
35
  File.expand_path(File.join(ENV['ALLUSERSPROFILE'], 'PuppetLabs/client-tools/puppetdb.conf'))
22
36
  end
23
37
 
24
- def self.load_config(options, project_path = nil)
38
+ # Loads default configuration from the puppetdb.conf file on system. If
39
+ # the file is not present, defaults to an empty hash.
40
+ #
41
+ # @return [Hash]
42
+ #
43
+ def self.default_config
25
44
  config = {}
26
45
  global_path = Bolt::Util.windows? ? default_windows_config : DEFAULT_CONFIG[:global]
27
46
 
@@ -37,13 +56,7 @@ module Bolt
37
56
  Bolt::Logger.logger(self).error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
38
57
  end
39
58
 
40
- config = config.fetch('puppetdb', {})
41
- new(config.merge(options), project_path)
42
- end
43
-
44
- def initialize(settings, project_path = nil)
45
- @settings = settings
46
- expand_paths(project_path)
59
+ config.fetch('puppetdb', {})
47
60
  end
48
61
 
49
62
  def token
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logging'
5
+ require_relative '../../bolt/puppetdb/config'
6
+
7
+ module Bolt
8
+ module PuppetDB
9
+ class Instance
10
+ attr_reader :config
11
+
12
+ def initialize(config:, project: nil, load_defaults: false)
13
+ @config = Bolt::PuppetDB::Config.new(config: config, project: project, load_defaults: load_defaults)
14
+ @bad_urls = []
15
+ @current_url = nil
16
+ @logger = Bolt::Logger.logger(self)
17
+ end
18
+
19
+ def make_query(query, path = nil)
20
+ body = JSON.generate(query: query)
21
+ url = "#{uri}/pdb/query/v4"
22
+ url += "/#{path}" if path
23
+
24
+ begin
25
+ @logger.debug("Sending PuppetDB query to #{url}")
26
+ response = http_client.post(url, body: body, header: headers)
27
+ rescue StandardError => e
28
+ raise Bolt::PuppetDBFailoverError, "Failed to query PuppetDB: #{e}"
29
+ end
30
+
31
+ @logger.debug("Got response code #{response.code} from PuppetDB")
32
+ if response.code != 200
33
+ msg = "Failed to query PuppetDB: #{response.body}"
34
+ if response.code == 400
35
+ raise Bolt::PuppetDBError, msg
36
+ else
37
+ raise Bolt::PuppetDBFailoverError, msg
38
+ end
39
+ end
40
+
41
+ begin
42
+ JSON.parse(response.body)
43
+ rescue JSON::ParserError
44
+ raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
45
+ end
46
+ rescue Bolt::PuppetDBFailoverError => e
47
+ @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
48
+ reject_url
49
+ make_query(query, path)
50
+ end
51
+
52
+ # Sends a command to PuppetDB using version 1 of the commands API.
53
+ # https://puppet.com/docs/puppetdb/latest/api/command/v1/commands.html
54
+ #
55
+ # @param command [String] The command to invoke.
56
+ # @param version [Integer] The version of the command to invoke.
57
+ # @param payload [Hash] The payload to send with the command.
58
+ # @return A UUID identifying the submitted command.
59
+ #
60
+ def send_command(command, version, payload)
61
+ command = command.dup.force_encoding('utf-8')
62
+ body = JSON.generate(payload)
63
+
64
+ # PDB requires the following query parameters to the POST request.
65
+ # Error early if there's no certname, as PDB does not return a
66
+ # message indicating it's required.
67
+ unless payload['certname']
68
+ raise Bolt::Error.new(
69
+ "Payload must include 'certname', unable to invoke command.",
70
+ 'bolt/pdb-command'
71
+ )
72
+ end
73
+
74
+ url = uri.tap do |u|
75
+ u.path = 'pdb/cmd/v1'
76
+ u.query_values = { 'command' => command,
77
+ 'version' => version,
78
+ 'certname' => payload['certname'] }
79
+ end
80
+
81
+ # Send the command to PDB
82
+ begin
83
+ @logger.debug("Sending PuppetDB command '#{command}' to #{url}")
84
+ response = http_client.post(url.to_s, body: body, header: headers)
85
+ rescue StandardError => e
86
+ raise Bolt::PuppetDBFailoverError, "Failed to invoke PuppetDB command: #{e}"
87
+ end
88
+
89
+ @logger.debug("Got response code #{response.code} from PuppetDB")
90
+ if response.code != 200
91
+ raise Bolt::PuppetDBError, "Failed to invoke PuppetDB command: #{response.body}"
92
+ end
93
+
94
+ # Return the UUID string from the response body
95
+ begin
96
+ JSON.parse(response.body).fetch('uuid', nil)
97
+ rescue JSON::ParserError
98
+ raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
99
+ end
100
+ rescue Bolt::PuppetDBFailoverError => e
101
+ @logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
102
+ reject_url
103
+ send_command(command, version, payload)
104
+ end
105
+
106
+ def http_client
107
+ return @http if @http
108
+ # lazy-load expensive gem code
109
+ require 'httpclient'
110
+ @logger.trace("Creating HTTP Client")
111
+ @http = HTTPClient.new
112
+ @http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
113
+ @http.ssl_config.add_trust_ca(@config.cacert)
114
+ @http.connect_timeout = @config.connect_timeout if @config.connect_timeout
115
+ @http.receive_timeout = @config.read_timeout if @config.read_timeout
116
+
117
+ @http
118
+ end
119
+
120
+ def reject_url
121
+ @bad_urls << @current_url if @current_url
122
+ @current_url = nil
123
+ end
124
+
125
+ def uri
126
+ require 'addressable/uri'
127
+
128
+ @current_url ||= (@config.server_urls - @bad_urls).first
129
+ unless @current_url
130
+ msg = "Failed to connect to all PuppetDB server_urls: #{@config.server_urls.to_a.join(', ')}."
131
+ raise Bolt::PuppetDBError, msg
132
+ end
133
+
134
+ uri = Addressable::URI.parse(@current_url)
135
+ uri.port ||= 8081
136
+ uri
137
+ end
138
+
139
+ def headers
140
+ headers = { 'Content-Type' => 'application/json' }
141
+ headers['X-Authentication'] = @config.token if @config.token
142
+ headers
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/bolt/result.rb CHANGED
@@ -128,7 +128,7 @@ module Bolt
128
128
 
129
129
  def _pcore_init_from_hash(init_hash)
130
130
  opts = init_hash.reject { |k, _v| k == 'target' }
131
- initialize(init_hash['target'], opts.transform_keys(&:to_sym))
131
+ initialize(init_hash['target'], **opts.transform_keys(&:to_sym))
132
132
  end
133
133
 
134
134
  def _pcore_init_hash
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '3.22.1'
4
+ VERSION = '3.23.0'
5
5
  end
@@ -41,7 +41,7 @@ module BoltSpec
41
41
  @invocation[:options]
42
42
  end
43
43
 
44
- def result_for(_target, _data)
44
+ def result_for(_target, **_data)
45
45
  raise 'Download result cannot be changed'
46
46
  end
47
47
 
@@ -30,7 +30,7 @@ module BoltSpec
30
30
  end
31
31
 
32
32
  # Allow any data.
33
- def result_for(_target, data)
33
+ def result_for(_target, **data)
34
34
  Bolt::PlanResult.new(Bolt::Util.walk_keys(data, &:to_s), 'success')
35
35
  end
36
36
 
@@ -37,7 +37,7 @@ module BoltSpec
37
37
  end
38
38
 
39
39
  # Allow any data.
40
- def result_for(target, data)
40
+ def result_for(target, **data)
41
41
  Bolt::Result.new(target, value: Bolt::Util.walk_keys(data, &:to_s))
42
42
  end
43
43
 
@@ -40,7 +40,7 @@ module BoltSpec
40
40
  @invocation[:options]
41
41
  end
42
42
 
43
- def result_for(_target, _data)
43
+ def result_for(_target, **_data)
44
44
  raise 'Upload result cannot be changed'
45
45
  end
46
46
 
@@ -93,7 +93,7 @@ module BoltSpec
93
93
  when Bolt::Error
94
94
  Bolt::Result.from_exception(target, @data[:default])
95
95
  when Hash
96
- result_for(target, Bolt::Util.walk_keys(@data[:default], &:to_sym))
96
+ result_for(target, **Bolt::Util.walk_keys(@data[:default], &:to_sym))
97
97
  else
98
98
  raise 'Default result must be a Hash'
99
99
  end
@@ -156,7 +156,7 @@ module BoltSpec
156
156
  # set the inventory from the BoltSpec::Plans, otherwise if we try to convert
157
157
  # this target to a string, it will fail to string conversion because the
158
158
  # inventory is nil
159
- hsh[target] = result_for(Bolt::Target.new(target, @inventory), Bolt::Util.walk_keys(result, &:to_sym))
159
+ hsh[target] = result_for(Bolt::Target.new(target, @inventory), **Bolt::Util.walk_keys(result, &:to_sym))
160
160
  end
161
161
  raise "Cannot set return values and return block." if @return_block
162
162
  @data_set = true
@@ -210,7 +210,7 @@ module BoltSpec
210
210
  @allow_apply = true
211
211
  end
212
212
 
213
- def wait_until_available(targets, _options)
213
+ def wait_until_available(targets, **_options)
214
214
  Bolt::ResultSet.new(targets.map { |target| Bolt::Result.new(target) })
215
215
  end
216
216
 
@@ -103,6 +103,16 @@ module BoltSpec
103
103
 
104
104
  # Provided as a class so expectations can be placed on it.
105
105
  class MockPuppetDBClient
106
+ def initialize(config)
107
+ @instance = MockPuppetDBInstance.new(config)
108
+ end
109
+
110
+ def instance(_instance)
111
+ @instance
112
+ end
113
+ end
114
+
115
+ class MockPuppetDBInstance
106
116
  attr_reader :config
107
117
 
108
118
  def initialize(config)
@@ -111,7 +121,7 @@ module BoltSpec
111
121
  end
112
122
 
113
123
  def puppetdb_client
114
- @puppetdb_client ||= MockPuppetDBClient.new(Bolt::PuppetDB::Config.new({}))
124
+ @puppetdb_client ||= MockPuppetDBClient.new({})
115
125
  end
116
126
 
117
127
  def run_plan(name, params)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.22.1
4
+ version: 3.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-30 00:00:00.000000000 Z
11
+ date: 2022-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -566,6 +566,7 @@ files:
566
566
  - lib/bolt/puppetdb.rb
567
567
  - lib/bolt/puppetdb/client.rb
568
568
  - lib/bolt/puppetdb/config.rb
569
+ - lib/bolt/puppetdb/instance.rb
569
570
  - lib/bolt/r10k_log_proxy.rb
570
571
  - lib/bolt/rerun.rb
571
572
  - lib/bolt/resource_instance.rb
@@ -664,7 +665,7 @@ require_paths:
664
665
  - lib
665
666
  required_ruby_version: !ruby/object:Gem::Requirement
666
667
  requirements:
667
- - - "~>"
668
+ - - ">="
668
669
  - !ruby/object:Gem::Version
669
670
  version: '2.5'
670
671
  required_rubygems_version: !ruby/object:Gem::Requirement