-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathinstance.rb
More file actions
148 lines (127 loc) · 5 KB
/
instance.rb
File metadata and controls
148 lines (127 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# frozen_string_literal: true
require 'json'
require 'logging'
require_relative '../../bolt/puppetdb/config'
module Bolt
module PuppetDB
class Instance
attr_reader :config
def initialize(config:, project: nil, load_defaults: false)
@config = Bolt::PuppetDB::Config.new(config: config, project: project, load_defaults: load_defaults)
@bad_urls = []
@current_url = nil
@logger = Bolt::Logger.logger(self)
end
def make_query(query, path = nil)
body = JSON.generate(query: query)
url = "#{uri}/pdb/query/v4"
url += "/#{path}" if path
begin
@logger.debug("Sending PuppetDB query to #{url}")
response = http_client.post(url, body: body, header: headers)
rescue StandardError => e
raise Bolt::PuppetDBFailoverError, "Failed to query PuppetDB: #{e}"
end
@logger.debug("Got response code #{response.code} from PuppetDB")
if response.code != 200
msg = "Failed to query PuppetDB: #{response.body}"
if response.code == 400
raise Bolt::PuppetDBError, msg
else
raise Bolt::PuppetDBFailoverError, msg
end
end
begin
JSON.parse(response.body)
rescue JSON::ParserError
raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
end
rescue Bolt::PuppetDBFailoverError => e
@logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
reject_url
make_query(query, path)
end
# Sends a command to PuppetDB using version 1 of the commands API.
# https://puppet.com/docs/puppetdb/latest/api/command/v1/commands.html
#
# @param command [String] The command to invoke.
# @param version [Integer] The version of the command to invoke.
# @param payload [Hash] The payload to send with the command.
# @return A UUID identifying the submitted command.
#
def send_command(command, version, payload)
command = command.dup.force_encoding('utf-8')
body = JSON.generate(payload)
# PDB requires the following query parameters to the POST request.
# Error early if there's no certname, as PDB does not return a
# message indicating it's required.
unless payload['certname']
raise Bolt::Error.new(
"Payload must include 'certname', unable to invoke command.",
'bolt/pdb-command'
)
end
url = uri.tap do |u|
u.path = 'pdb/cmd/v1'
u.query_values = { 'command' => command,
'version' => version,
'certname' => payload['certname'] }
end
# Send the command to PDB
begin
@logger.debug("Sending PuppetDB command '#{command}' to #{url}")
response = http_client.post(url.to_s, body: body, header: headers)
rescue StandardError => e
raise Bolt::PuppetDBFailoverError, "Failed to invoke PuppetDB command: #{e}"
end
@logger.debug("Got response code #{response.code} from PuppetDB")
if response.code != 200
raise Bolt::PuppetDBError, "Failed to invoke PuppetDB command: #{response.body}"
end
# Return the UUID string from the response body
begin
JSON.parse(response.body).fetch('uuid', nil)
rescue JSON::ParserError
raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
end
rescue Bolt::PuppetDBFailoverError => e
@logger.error("Request to puppetdb at #{@current_url} failed with #{e}.")
reject_url
send_command(command, version, payload)
end
def http_client
return @http if @http
# lazy-load expensive gem code
require 'httpclient'
@logger.trace("Creating HTTP Client")
@http = HTTPClient.new
@http.ssl_config.set_client_cert_file(@config.cert, @config.key) if @config.cert
@http.ssl_config.add_trust_ca(@config.cacert)
@http.connect_timeout = @config.connect_timeout if @config.connect_timeout
@http.receive_timeout = @config.read_timeout if @config.read_timeout
@http
end
def reject_url
@bad_urls << @current_url if @current_url
@current_url = nil
end
def uri
require 'addressable/uri'
@current_url ||= (@config.server_urls - @bad_urls).first
unless @current_url
msg = "Failed to connect to all PuppetDB server_urls: #{@config.server_urls.to_a.join(', ')}."
raise Bolt::PuppetDBError, msg
end
uri = Addressable::URI.parse(@current_url)
uri.port ||= 8081
uri
end
def headers
headers = { 'Content-Type' => 'application/json' }
headers.merge!(@config.headers) if @config.headers
headers['X-Authentication'] = @config.token if @config.token
headers
end
end
end
end