diplomatic_bag 2.2.1
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 +7 -0
- data/LICENSE +24 -0
- data/README.md +356 -0
- data/features/configuration.feature +9 -0
- data/features/step_definitions/setup_diplomat.rb +24 -0
- data/features/step_definitions/test_key_value.rb +9 -0
- data/lib/diplomat/acl.rb +81 -0
- data/lib/diplomat/agent.rb +42 -0
- data/lib/diplomat/check.rb +109 -0
- data/lib/diplomat/configuration.rb +28 -0
- data/lib/diplomat/datacenter.rb +21 -0
- data/lib/diplomat/error.rb +15 -0
- data/lib/diplomat/event.rb +166 -0
- data/lib/diplomat/health.rb +81 -0
- data/lib/diplomat/kv.rb +263 -0
- data/lib/diplomat/lock.rb +54 -0
- data/lib/diplomat/maintenance.rb +41 -0
- data/lib/diplomat/members.rb +14 -0
- data/lib/diplomat/node.rb +43 -0
- data/lib/diplomat/nodes.rb +22 -0
- data/lib/diplomat/query.rb +87 -0
- data/lib/diplomat/rest_client.rb +278 -0
- data/lib/diplomat/service.rb +111 -0
- data/lib/diplomat/session.rb +75 -0
- data/lib/diplomat/status.rb +22 -0
- data/lib/diplomat/version.rb +3 -0
- data/lib/diplomat.rb +62 -0
- data/lib/diplomatic_bag/datacenters.rb +11 -0
- data/lib/diplomatic_bag/info.rb +32 -0
- data/lib/diplomatic_bag/nodes.rb +28 -0
- data/lib/diplomatic_bag/service.rb +20 -0
- data/lib/diplomatic_bag/services.rb +38 -0
- data/lib/diplomatic_bag.rb +7 -0
- metadata +89 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul members API endpoint
|
|
3
|
+
class Members < Diplomat::RestClient
|
|
4
|
+
@access_methods = [:get]
|
|
5
|
+
|
|
6
|
+
# Get all members
|
|
7
|
+
# @param options [Hash] options parameter hash
|
|
8
|
+
# @return [OpenStruct] all data associated with the service
|
|
9
|
+
def get(options = {})
|
|
10
|
+
ret = send_get_request(@conn, ['/v1/agent/members'], options)
|
|
11
|
+
JSON.parse(ret.body)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul node API endpoint
|
|
3
|
+
class Node < Diplomat::RestClient
|
|
4
|
+
@access_methods = %i[get get_all register deregister]
|
|
5
|
+
|
|
6
|
+
# Get a node by it's key
|
|
7
|
+
# @param key [String] the key
|
|
8
|
+
# @param options [Hash] :dc string for dc specific query
|
|
9
|
+
# @return [OpenStruct] all data associated with the node
|
|
10
|
+
def get(key, options = {})
|
|
11
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
12
|
+
ret = send_get_request(@conn, ["/v1/catalog/node/#{key}"], options, custom_params)
|
|
13
|
+
OpenStruct.new JSON.parse(ret.body)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get all the nodes
|
|
17
|
+
# @param options [Hash] :dc string for dc specific query
|
|
18
|
+
# @return [OpenStruct] the list of all nodes
|
|
19
|
+
def get_all(options = {})
|
|
20
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
21
|
+
ret = send_get_request(@conn, ['/v1/catalog/nodes'], options, custom_params)
|
|
22
|
+
JSON.parse(ret.body).map { |service| OpenStruct.new service }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Register a node
|
|
26
|
+
# @param definition [Hash] Hash containing definition of a node to register
|
|
27
|
+
# @param options [Hash] options parameter hash
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
def register(definition, options = {})
|
|
30
|
+
register = send_put_request(@conn, ['/v1/catalog/register'], options, definition)
|
|
31
|
+
register.status == 200
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# De-register a node (and all associated services and checks)
|
|
35
|
+
# @param definition [Hash] Hash containing definition of a node to de-register
|
|
36
|
+
# @param options [Hash] options parameter hash
|
|
37
|
+
# @return [Boolean]
|
|
38
|
+
def deregister(definition, options = {})
|
|
39
|
+
deregister = send_put_request(@conn, ['/v1/catalog/deregister'], options, definition)
|
|
40
|
+
deregister.status == 200
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# @depreciated
|
|
3
|
+
# Methods for interacting with the Consul nodes API endpoint
|
|
4
|
+
class Nodes < Diplomat::RestClient
|
|
5
|
+
@access_methods = %i[get get_all]
|
|
6
|
+
|
|
7
|
+
# Get all nodes
|
|
8
|
+
# @deprecated Please use Diplomat::Node instead.
|
|
9
|
+
# @param options [Hash] options parameter hash
|
|
10
|
+
# @return [OpenStruct] all data associated with the nodes in catalog
|
|
11
|
+
def get(options = {})
|
|
12
|
+
ret = send_get_request(@conn, ['/v1/catalog/nodes'], options)
|
|
13
|
+
JSON.parse(ret.body)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get_all(options = {})
|
|
17
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
18
|
+
ret = send_get_request(@conn, ['/v1/catalog/nodes'], options, custom_params)
|
|
19
|
+
JSON.parse(ret.body).map { |service| OpenStruct.new service }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul query API endpoint
|
|
3
|
+
class Query < Diplomat::RestClient
|
|
4
|
+
@access_methods = %i[get get_all create delete update execute explain]
|
|
5
|
+
|
|
6
|
+
# Get a prepared query by it's key
|
|
7
|
+
# @param key [String] the prepared query ID
|
|
8
|
+
# @param options [Hash] :dc string for dc specific query
|
|
9
|
+
# @return [OpenStruct] all data associated with the prepared query
|
|
10
|
+
def get(key, options = {})
|
|
11
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
12
|
+
ret = send_get_request(@conn, ["/v1/query/#{key}"], options, custom_params)
|
|
13
|
+
JSON.parse(ret.body).map { |query| OpenStruct.new query }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get all prepared queries
|
|
17
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
18
|
+
# @return [OpenStruct] the list of all prepared queries
|
|
19
|
+
def get_all(options = {})
|
|
20
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
21
|
+
ret = send_get_request(@conn, ['/v1/query'], options, custom_params)
|
|
22
|
+
JSON.parse(ret.body).map { |query| OpenStruct.new query }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Create a prepared query or prepared query template
|
|
26
|
+
# @param definition [Hash] Hash containing definition of prepared query
|
|
27
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
28
|
+
# @return [String] the ID of the prepared query created
|
|
29
|
+
def create(definition, options = {})
|
|
30
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
31
|
+
@raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
|
|
32
|
+
parse_body
|
|
33
|
+
rescue Faraday::ClientError
|
|
34
|
+
raise Diplomat::QueryAlreadyExists
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Delete a prepared query or prepared query template
|
|
38
|
+
# @param key [String] the prepared query ID
|
|
39
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
def delete(key, options = {})
|
|
42
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
43
|
+
ret = send_delete_request(@conn, ["/v1/query/#{key}"], options, custom_params)
|
|
44
|
+
ret.status == 200
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Update a prepared query or prepared query template
|
|
48
|
+
# @param key [String] the prepared query ID
|
|
49
|
+
# @param definition [Hash] Hash containing updated definition of prepared query
|
|
50
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
def update(key, definition, options = {})
|
|
53
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
54
|
+
ret = send_put_request(@conn, ["/v1/query/#{key}"], options, definition, custom_params)
|
|
55
|
+
ret.status == 200
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Execute a prepared query or prepared query template
|
|
59
|
+
# @param key [String] the prepared query ID or name
|
|
60
|
+
# @param options [Hash] prepared query execution options
|
|
61
|
+
# @option dc [String] :dc Consul datacenter to query
|
|
62
|
+
# @option near [String] node name to sort the resulting list in ascending order based on the
|
|
63
|
+
# estimated round trip time from that node
|
|
64
|
+
# @option limit [Integer] to limit the size of the return list to the given number of results
|
|
65
|
+
# @return [OpenStruct] the list of results from the prepared query or prepared query template
|
|
66
|
+
# rubocop:disable PerceivedComplexity
|
|
67
|
+
def execute(key, options = {})
|
|
68
|
+
custom_params = []
|
|
69
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
70
|
+
custom_params << use_named_parameter('near', options[:near]) if options[:near]
|
|
71
|
+
custom_params << use_named_parameter('limit', options[:limit]) if options[:limit]
|
|
72
|
+
ret = send_get_request(@conn, ["/v1/query/#{key}/execute"], options, custom_params)
|
|
73
|
+
OpenStruct.new JSON.parse(ret.body)
|
|
74
|
+
end
|
|
75
|
+
# rubocop:enable PerceivedComplexity
|
|
76
|
+
|
|
77
|
+
# Get the fully rendered query template
|
|
78
|
+
# @param key [String] the prepared query ID or name
|
|
79
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
80
|
+
# @return [OpenStruct] the list of results from the prepared query or prepared query template
|
|
81
|
+
def explain(key, options = {})
|
|
82
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
83
|
+
ret = send_get_request(@conn, ["/v1/query/#{key}/explain"], options, custom_params)
|
|
84
|
+
OpenStruct.new JSON.parse(ret.body)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
require 'deep_merge'
|
|
2
|
+
|
|
3
|
+
module Diplomat
|
|
4
|
+
# Base class for interacting with the Consul RESTful API
|
|
5
|
+
class RestClient
|
|
6
|
+
@access_methods = []
|
|
7
|
+
@configuration = nil
|
|
8
|
+
|
|
9
|
+
# Initialize the fadaray connection
|
|
10
|
+
# @param api_connection [Faraday::Connection,nil] supply mock API Connection
|
|
11
|
+
# @param configuration [Diplomat::Configuration] a dedicated config to use
|
|
12
|
+
def initialize(api_connection = nil, configuration: nil)
|
|
13
|
+
@configuration = configuration
|
|
14
|
+
start_connection api_connection
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get client configuration or global one if not specified via initialize.
|
|
18
|
+
# @return [Diplomat::Configuration] used by this client
|
|
19
|
+
def configuration
|
|
20
|
+
@configuration || ::Diplomat.configuration
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Format url parameters into strings correctly
|
|
24
|
+
# @param name [String] the name of the parameter
|
|
25
|
+
# @param value [String] the value of the parameter
|
|
26
|
+
# @return [Array] the resultant parameter string inside an array.
|
|
27
|
+
def use_named_parameter(name, value)
|
|
28
|
+
value ? ["#{name}=#{value}"] : []
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Assemble a url from an array of parts.
|
|
32
|
+
# @param parts [Array] the url chunks to be assembled
|
|
33
|
+
# @return [String] the resultant url string
|
|
34
|
+
def concat_url(parts)
|
|
35
|
+
parts.reject!(&:empty?)
|
|
36
|
+
if parts.length > 1
|
|
37
|
+
parts.first + '?' + parts.drop(1).join('&')
|
|
38
|
+
else
|
|
39
|
+
parts.first
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
def access_method?(meth_id)
|
|
45
|
+
@access_methods.include? meth_id
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Allow certain methods to be accessed
|
|
49
|
+
# without defining "new".
|
|
50
|
+
# @param meth_id [Symbol] symbol defining method requested
|
|
51
|
+
# @param *args Arguments list
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def method_missing(meth_id, *args)
|
|
54
|
+
if access_method?(meth_id)
|
|
55
|
+
new.send(meth_id, *args)
|
|
56
|
+
else
|
|
57
|
+
|
|
58
|
+
# See https://bugs.ruby-lang.org/issues/10969
|
|
59
|
+
begin
|
|
60
|
+
super
|
|
61
|
+
rescue NameError => e
|
|
62
|
+
raise NoMethodError, e
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Make `respond_to?` aware of method short-cuts.
|
|
68
|
+
#
|
|
69
|
+
# @param meth_id [Symbol] the tested method
|
|
70
|
+
# @oaram with_private if private methods should be tested too
|
|
71
|
+
def respond_to?(meth_id, with_private = false)
|
|
72
|
+
access_method?(meth_id) || super
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Make `respond_to_missing` aware of method short-cuts. This is needed for
|
|
76
|
+
# {#method} to work on these, which is helpful for testing purposes.
|
|
77
|
+
#
|
|
78
|
+
# @param meth_id [Symbol] the tested method
|
|
79
|
+
# @oaram with_private if private methods should be tested too
|
|
80
|
+
def respond_to_missing?(meth_id, with_private = false)
|
|
81
|
+
access_method?(meth_id) || super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Build the API Client
|
|
88
|
+
# @param api_connection [Faraday::Connection,nil] supply mock API Connection
|
|
89
|
+
def start_connection(api_connection = nil)
|
|
90
|
+
@conn = build_connection(api_connection)
|
|
91
|
+
@conn_no_err = build_connection(api_connection, true)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_connection(api_connection, raise_error = false)
|
|
95
|
+
api_connection || Faraday.new(configuration.url, configuration.options) do |faraday|
|
|
96
|
+
configuration.middleware.each do |middleware|
|
|
97
|
+
faraday.use middleware
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
faraday.request :url_encoded
|
|
101
|
+
faraday.response :raise_error unless raise_error
|
|
102
|
+
|
|
103
|
+
faraday.adapter Faraday.default_adapter
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Converts k/v data into ruby hash
|
|
108
|
+
def convert_to_hash(data)
|
|
109
|
+
data.map do |item|
|
|
110
|
+
item[:key].split('/').reverse.reduce(item[:value]) { |h, v| { v => h } }
|
|
111
|
+
end.reduce(:deep_merge)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Parse the body, apply it to the raw attribute
|
|
115
|
+
def parse_body
|
|
116
|
+
return JSON.parse(@raw.body) if @raw.status == 200
|
|
117
|
+
|
|
118
|
+
raise Diplomat::UnknownStatus, "status #{@raw.status}: #{@raw.body}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Return @raw with Value fields decoded
|
|
122
|
+
def decode_values
|
|
123
|
+
return @raw if @raw.first.is_a? String
|
|
124
|
+
|
|
125
|
+
@raw.each_with_object([]) do |acc, el|
|
|
126
|
+
begin
|
|
127
|
+
acc['Value'] = Base64.decode64(acc['Value'])
|
|
128
|
+
rescue StandardError
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
el << acc
|
|
132
|
+
el
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get the key/value(s) from the raw output
|
|
137
|
+
# rubocop:disable PerceivedComplexity
|
|
138
|
+
def return_value(nil_values = false, transformation = nil, return_hash = false)
|
|
139
|
+
@value = decode_values
|
|
140
|
+
return @value if @value.first.is_a? String
|
|
141
|
+
|
|
142
|
+
if @value.count == 1 && !return_hash
|
|
143
|
+
@value = @value.first['Value']
|
|
144
|
+
@value = transformation.call(@value) if transformation && !@value.nil?
|
|
145
|
+
return @value
|
|
146
|
+
else
|
|
147
|
+
@value = @value.map do |el|
|
|
148
|
+
el['Value'] = transformation.call(el['Value']) if transformation && !el['Value'].nil?
|
|
149
|
+
{ key: el['Key'], value: el['Value'] } if el['Value'] || nil_values
|
|
150
|
+
end.compact
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
# rubocop:enable PerceivedComplexity
|
|
154
|
+
|
|
155
|
+
# Get the name and payload(s) from the raw output
|
|
156
|
+
def return_payload
|
|
157
|
+
@value = @raw.map do |e|
|
|
158
|
+
{ name: e['Name'],
|
|
159
|
+
payload: (Base64.decode64(e['Payload']) unless e['Payload'].nil?) }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def use_cas(options)
|
|
164
|
+
options ? use_named_parameter('cas', options[:cas]) : []
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def use_consistency(options)
|
|
168
|
+
options[:consistency] ? [options[:consistency].to_s] : []
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# rubocop:disable PerceivedComplexity
|
|
172
|
+
# TODO: Migrate all custom params in options
|
|
173
|
+
def parse_options(options)
|
|
174
|
+
headers = nil
|
|
175
|
+
query_params = []
|
|
176
|
+
url_prefix = nil
|
|
177
|
+
consistency = []
|
|
178
|
+
|
|
179
|
+
# Parse options used as header
|
|
180
|
+
headers = { 'X-Consul-Token' => configuration.acl_token } if configuration.acl_token
|
|
181
|
+
headers = { 'X-Consul-Token' => options[:token] } if options[:token]
|
|
182
|
+
|
|
183
|
+
# Parse options used as query params
|
|
184
|
+
consistency = 'stale' if options[:stale]
|
|
185
|
+
consistency = 'leader' if options[:leader]
|
|
186
|
+
consistency = 'consistent' if options[:consistent]
|
|
187
|
+
query_params << consistency
|
|
188
|
+
|
|
189
|
+
# Parse url host
|
|
190
|
+
url_prefix = options[:http_addr] if options[:http_addr]
|
|
191
|
+
{ query_params: query_params, headers: headers, url_prefix: url_prefix }
|
|
192
|
+
end
|
|
193
|
+
# rubocop:enable PerceivedComplexity
|
|
194
|
+
|
|
195
|
+
def send_get_request(connection, url, options, custom_params = nil)
|
|
196
|
+
rest_options = parse_options(options)
|
|
197
|
+
url += rest_options[:query_params]
|
|
198
|
+
url += custom_params unless custom_params.nil?
|
|
199
|
+
begin
|
|
200
|
+
connection.get do |req|
|
|
201
|
+
req.options[:params_encoder] = options[:params_encoder] if options[:params_encoder]
|
|
202
|
+
req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url)
|
|
203
|
+
rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil?
|
|
204
|
+
req.options.timeout = options[:timeout] if options[:timeout]
|
|
205
|
+
end
|
|
206
|
+
rescue Faraday::ClientError => e
|
|
207
|
+
raise Diplomat::PathNotFound, e
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def send_put_request(connection, url, options, data, custom_params = nil, mime = 'application/json')
|
|
212
|
+
rest_options = parse_options(options)
|
|
213
|
+
url += rest_options[:query_params]
|
|
214
|
+
url += custom_params unless custom_params.nil?
|
|
215
|
+
connection.put do |req|
|
|
216
|
+
req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url)
|
|
217
|
+
rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil?
|
|
218
|
+
unless data.nil?
|
|
219
|
+
(req.headers || {})['Content-Type'] = mime
|
|
220
|
+
req.body = if mime == 'application/json' && !data.is_a?(String)
|
|
221
|
+
data.to_json
|
|
222
|
+
else
|
|
223
|
+
data
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def send_post_request(connection, url, options, data, custom_params = nil)
|
|
230
|
+
rest_options = parse_options(options)
|
|
231
|
+
url += rest_options[:query_params]
|
|
232
|
+
url += custom_params unless custom_params.nil?
|
|
233
|
+
connection.post do |req|
|
|
234
|
+
req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url)
|
|
235
|
+
rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil?
|
|
236
|
+
req.body = JSON.dump(data) unless data.nil?
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def send_delete_request(connection, url, options, custom_params = nil)
|
|
241
|
+
rest_options = parse_options(options)
|
|
242
|
+
url += rest_options[:query_params]
|
|
243
|
+
url += custom_params unless custom_params.nil?
|
|
244
|
+
connection.delete do |req|
|
|
245
|
+
req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url)
|
|
246
|
+
rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil?
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Mapping for valid key/value store transaction verbs and required parameters
|
|
251
|
+
#
|
|
252
|
+
# @return [Hash] valid key/store transaction verbs and required parameters
|
|
253
|
+
def valid_transaction_verbs
|
|
254
|
+
{
|
|
255
|
+
'set' => %w[Key Value],
|
|
256
|
+
'cas' => %w[Key Value Index],
|
|
257
|
+
'lock' => %w[Key Value Session],
|
|
258
|
+
'unlock' => %w[Key Value Session],
|
|
259
|
+
'get' => %w[Key],
|
|
260
|
+
'get-tree' => %w[Key],
|
|
261
|
+
'check-index' => %w[Key Index],
|
|
262
|
+
'check-session' => %w[Key Session],
|
|
263
|
+
'delete' => %w[Key],
|
|
264
|
+
'delete-tree' => %w[Key],
|
|
265
|
+
'delete-cas' => %w[Key Index]
|
|
266
|
+
}
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Key/value store transactions that require that a value be set
|
|
270
|
+
#
|
|
271
|
+
# @return [Array<String>] verbs that require a value be set
|
|
272
|
+
def valid_value_transactions
|
|
273
|
+
@valid_value_transactions ||= valid_transaction_verbs.select do |verb, requires|
|
|
274
|
+
verb if requires.include? 'Value'
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul service API endpoint.
|
|
3
|
+
class Service < Diplomat::RestClient
|
|
4
|
+
@access_methods = %i[get get_all register deregister register_external deregister_external maintenance]
|
|
5
|
+
|
|
6
|
+
# Get a service by it's key
|
|
7
|
+
# @param key [String] the key
|
|
8
|
+
# @param scope [Symbol] :first or :all results
|
|
9
|
+
# @param options [Hash] options parameter hash
|
|
10
|
+
# @param meta [Hash] output structure containing header information about the request (index)
|
|
11
|
+
# @return [OpenStruct] all data associated with the service
|
|
12
|
+
# rubocop:disable PerceivedComplexity
|
|
13
|
+
def get(key, scope = :first, options = {}, meta = nil)
|
|
14
|
+
custom_params = []
|
|
15
|
+
custom_params << use_named_parameter('wait', options[:wait]) if options[:wait]
|
|
16
|
+
custom_params << use_named_parameter('index', options[:index]) if options[:index]
|
|
17
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
18
|
+
if options[:tag]
|
|
19
|
+
# tag can be either a String, or an array of strings
|
|
20
|
+
# by splatting it is guaranteed to be an array of strings
|
|
21
|
+
[*options[:tag]].each do |value|
|
|
22
|
+
custom_params << use_named_parameter('tag', value)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# We have to provide a custom params encoder here because Faraday - by default - assumes that
|
|
27
|
+
# list keys have [] as part of their name. This is however not the case for consul tags, which
|
|
28
|
+
# just use repeated occurences of the same key.
|
|
29
|
+
#
|
|
30
|
+
# So faraday reduces this: http://localhost:8500?a=1&a=2 to http://localhost:8500?a=2 unless you
|
|
31
|
+
# explicitly tell it not to.
|
|
32
|
+
options[:params_encoder] = Faraday::FlatParamsEncoder
|
|
33
|
+
|
|
34
|
+
ret = send_get_request(@conn, ["/v1/catalog/service/#{key}"], options, custom_params)
|
|
35
|
+
if meta && ret.headers
|
|
36
|
+
meta[:index] = ret.headers['x-consul-index'] if ret.headers['x-consul-index']
|
|
37
|
+
meta[:knownleader] = ret.headers['x-consul-knownleader'] if ret.headers['x-consul-knownleader']
|
|
38
|
+
meta[:lastcontact] = ret.headers['x-consul-lastcontact'] if ret.headers['x-consul-lastcontact']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if scope == :all
|
|
42
|
+
JSON.parse(ret.body).map { |service| OpenStruct.new service }
|
|
43
|
+
else
|
|
44
|
+
OpenStruct.new JSON.parse(ret.body).first
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable PerceivedComplexity
|
|
48
|
+
|
|
49
|
+
# Get all the services
|
|
50
|
+
# @param options [Hash] :dc Consul datacenter to query
|
|
51
|
+
# @return [OpenStruct] the list of all services
|
|
52
|
+
def get_all(options = {})
|
|
53
|
+
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
|
|
54
|
+
ret = send_get_request(@conn, ['/v1/catalog/services'], options, custom_params)
|
|
55
|
+
OpenStruct.new JSON.parse(ret.body)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Register a service
|
|
59
|
+
# @param definition [Hash] Hash containing definition of service
|
|
60
|
+
# @param options [Hash] options parameter hash
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
def register(definition, options = {})
|
|
63
|
+
url = options[:path] || ['/v1/agent/service/register']
|
|
64
|
+
register = send_put_request(@conn, url, options, definition)
|
|
65
|
+
register.status == 200
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# De-register a service
|
|
69
|
+
# @param service_name [String] Service name to de-register
|
|
70
|
+
# @param options [Hash] options parameter hash
|
|
71
|
+
# @return [Boolean]
|
|
72
|
+
def deregister(service_name, options = {})
|
|
73
|
+
deregister = send_put_request(@conn, ["/v1/agent/service/deregister/#{service_name}"], options, nil)
|
|
74
|
+
deregister.status == 200
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Register an external service
|
|
78
|
+
# @param definition [Hash] Hash containing definition of service
|
|
79
|
+
# @param options [Hash] options parameter hash
|
|
80
|
+
# @return [Boolean]
|
|
81
|
+
def register_external(definition, options = {})
|
|
82
|
+
register = send_put_request(@conn, ['/v1/catalog/register'], options, definition)
|
|
83
|
+
register.status == 200
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Deregister an external service
|
|
87
|
+
# @param definition [Hash] Hash containing definition of service
|
|
88
|
+
# @param options [Hash] options parameter hash
|
|
89
|
+
# @return [Boolean]
|
|
90
|
+
def deregister_external(definition, options = {})
|
|
91
|
+
deregister = send_put_request(@conn, ['/v1/catalog/deregister'], options, definition)
|
|
92
|
+
deregister.status == 200
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Enable or disable maintenance for a service
|
|
96
|
+
# @param service_id [String] id of the service
|
|
97
|
+
# @param options [Hash] opts the options for enabling or disabling maintenance for a service
|
|
98
|
+
# @options opts [Boolean] :enable (true) whether to enable or disable maintenance
|
|
99
|
+
# @options opts [String] :reason reason for the service maintenance
|
|
100
|
+
# @raise [Diplomat::PathNotFound] if the request fails
|
|
101
|
+
# @return [Boolean] if the request was successful or not
|
|
102
|
+
def maintenance(service_id, options = { enable: true })
|
|
103
|
+
custom_params = []
|
|
104
|
+
custom_params << ["enable=#{options[:enable]}"]
|
|
105
|
+
custom_params << ["reason=#{options[:reason].split(' ').join('+')}"] if options[:reason]
|
|
106
|
+
maintenance = send_put_request(@conn, ["/v1/agent/service/maintenance/#{service_id}"],
|
|
107
|
+
options, nil, custom_params)
|
|
108
|
+
maintenance.status == 200
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul session API endpoint
|
|
3
|
+
class Session < Diplomat::RestClient
|
|
4
|
+
@access_methods = %i[create destroy list renew info node]
|
|
5
|
+
|
|
6
|
+
# Create a new session
|
|
7
|
+
# @param value [Object] hash or json representation of the session arguments
|
|
8
|
+
# @param options [Hash] session options
|
|
9
|
+
# @return [String] The sesssion id
|
|
10
|
+
def create(value = nil, options = {})
|
|
11
|
+
# TODO: only certain keys are recognised in a session create request,
|
|
12
|
+
# should raise an error on others.
|
|
13
|
+
custom_params = []
|
|
14
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
15
|
+
data = value.is_a?(String) ? value : JSON.generate(value) unless value.nil?
|
|
16
|
+
raw = send_put_request(@conn, ['/v1/session/create'], options, data, custom_params)
|
|
17
|
+
body = JSON.parse(raw.body)
|
|
18
|
+
body['ID']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Destroy a session
|
|
22
|
+
# @param id [String] session id
|
|
23
|
+
# @param options [Hash] session options
|
|
24
|
+
# @return [String] Success or failure of the session destruction
|
|
25
|
+
def destroy(id, options = {})
|
|
26
|
+
custom_params = []
|
|
27
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
28
|
+
raw = send_put_request(@conn, ["/v1/session/destroy/#{id}"], options, nil, custom_params)
|
|
29
|
+
raw.body
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# List sessions
|
|
33
|
+
# @param options [Hash] session options
|
|
34
|
+
# @return [OpenStruct]
|
|
35
|
+
def list(options = {})
|
|
36
|
+
custom_params = []
|
|
37
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
38
|
+
raw = send_get_request(@conn, ['/v1/session/list'], options, custom_params)
|
|
39
|
+
JSON.parse(raw.body).map { |session| OpenStruct.new session }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Renew session
|
|
43
|
+
# @param id [String] session id
|
|
44
|
+
# @param options [Hash] session options
|
|
45
|
+
# @return [OpenStruct]
|
|
46
|
+
def renew(id, options = {})
|
|
47
|
+
custom_params = []
|
|
48
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
49
|
+
raw = send_put_request(@conn, ["/v1/session/renew/#{id}"], options, nil, custom_params)
|
|
50
|
+
JSON.parse(raw.body).map { |session| OpenStruct.new session }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Session information
|
|
54
|
+
# @param id [String] session id
|
|
55
|
+
# @param options [Hash] session options
|
|
56
|
+
# @return [OpenStruct]
|
|
57
|
+
def info(id, options = {})
|
|
58
|
+
custom_params = []
|
|
59
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
60
|
+
raw = send_get_request(@conn, ["/v1/session/info/#{id}"], options, custom_params)
|
|
61
|
+
JSON.parse(raw.body).map { |session| OpenStruct.new session }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Session information for a given node
|
|
65
|
+
# @param name [String] node name
|
|
66
|
+
# @param options [Hash] session options
|
|
67
|
+
# @return [OpenStruct]
|
|
68
|
+
def node(name, options = {})
|
|
69
|
+
custom_params = []
|
|
70
|
+
custom_params << use_named_parameter('dc', options[:dc]) if options[:dc]
|
|
71
|
+
raw = send_get_request(@conn, ["/v1/session/node/#{name}"], options, custom_params)
|
|
72
|
+
JSON.parse(raw.body).map { |session| OpenStruct.new session }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Diplomat
|
|
2
|
+
# Methods for interacting with the Consul status API endpoints, leader and peers
|
|
3
|
+
class Status < Diplomat::RestClient
|
|
4
|
+
@access_methods = %i[leader peers]
|
|
5
|
+
|
|
6
|
+
# Get the raft leader for the datacenter in which the local consul agent is running
|
|
7
|
+
# @param options [Hash] options parameter hash
|
|
8
|
+
# @return [OpenStruct] the address of the leader
|
|
9
|
+
def leader(options = {})
|
|
10
|
+
ret = send_get_request(@conn, ['/v1/status/leader'], options)
|
|
11
|
+
JSON.parse(ret.body)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Get an array of Raft peers for the datacenter in which the agent is running
|
|
15
|
+
# @param options [Hash] options parameter hash
|
|
16
|
+
# @return [OpenStruct] an array of peers
|
|
17
|
+
def peers(options = {})
|
|
18
|
+
ret = send_get_request(@conn, ['/v1/status/peers'], options)
|
|
19
|
+
JSON.parse(ret.body)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|