diplomat 1.2.0 → 1.3.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
  SHA1:
3
- metadata.gz: 6feb74783d35459eb7f08e895688ffe20ed975b1
4
- data.tar.gz: 21a8d4d02987f5d6298414c047fa86758968dd58
3
+ metadata.gz: 7e314345fea055152e86ff0e0a166185de4889f0
4
+ data.tar.gz: 7630435e44ebfab12efe41d3949a880bb9538d7d
5
5
  SHA512:
6
- metadata.gz: 709cdba5352b6c9f045065e8d335d4ce00d3b0065d0dbba39b83ed1d0ba8ea873088de4dd7b998fd127682dcd40104d5726daf91e9af40a45c13a2837c8240f9
7
- data.tar.gz: 0621ad4ca2152759913a2e82f6daf650e2044bb2083f66b028caa73bac19e38f7cefcf044f1e73d79c4edbf1b1a9d1a2e40d0d82b43be58f51d91064d9d50f45
6
+ metadata.gz: 180b5d54fb5ee0ef5758ec5f22da3408b2cc2b9bfe135640a0755f84e5dc8d8176bf4215a0a24e44091f247a392e49cd3ec3996f0b812e657a6f0fa4346c4a4d
7
+ data.tar.gz: 4045fb41d2ae39d4e6a2de5412782574e67834f7932042dcb4b52f1250add0666f7506290183d200d86ed6d8638810360be275a7f4c7a5bb3c1fbf20524f2661
data/README.md CHANGED
@@ -269,6 +269,22 @@ end
269
269
  events.each{ |e| puts e }
270
270
  ```
271
271
 
272
+ ### Status
273
+
274
+ Returns information about the status of the Consul cluster.
275
+
276
+ Get the raft leader for the datacenter in which the local consul agent is running
277
+
278
+ ```ruby
279
+ Diplomat::Status.leader()
280
+ ```
281
+
282
+ Get an array of Raft peers for the datacenter in which the agent is running
283
+
284
+ ```ruby
285
+ Diplomat::Status.peers()
286
+ ```
287
+
272
288
  ### Maintenance mode
273
289
 
274
290
  Enable maintenance mode on a host, with optional reason and DC (requires access to local agent)
@@ -28,7 +28,7 @@ module Diplomat
28
28
 
29
29
  require_libs 'configuration', 'rest_client', 'api_options', 'kv', 'datacenter',
30
30
  'service', 'members', 'node', 'nodes', 'check', 'health', 'session', 'lock',
31
- 'error', 'event', 'acl', 'maintenance', 'query'
31
+ 'error', 'event', 'acl', 'maintenance', 'query', 'agent', 'status'
32
32
  self.configuration ||= Diplomat::Configuration.new
33
33
 
34
34
  class << self
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Acl < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:list, :info, :create, :destroy, :update]
6
+ @access_methods = %i[list info create destroy update]
7
7
  attr_reader :id, :type, :acl
8
8
 
9
9
  # Get Acl info by ID
@@ -18,7 +18,7 @@ module Diplomat
18
18
  url << use_consistency(options)
19
19
 
20
20
  raw = @conn_no_err.get concat_url url
21
- if raw.status == 200 && raw.body != 'null'
21
+ if raw.status == 200 && raw.body.chomp != 'null'
22
22
  case found
23
23
  when :reject
24
24
  raise Diplomat::AclAlreadyExists, id
@@ -26,7 +26,7 @@ module Diplomat
26
26
  @raw = raw
27
27
  return parse_body
28
28
  end
29
- elsif raw.status == 200 && raw.body == 'null'
29
+ elsif raw.status == 200 && raw.body.chomp == 'null'
30
30
  case not_found
31
31
  when :reject
32
32
  raise Diplomat::AclNotFound, id
@@ -86,7 +86,7 @@ module Diplomat
86
86
  url = ["/v1/acl/destroy/#{@id}"]
87
87
  url << check_acl_token
88
88
  @raw = @conn.put concat_url url
89
- @raw.body == 'true'
89
+ @raw.body.chomp == 'true'
90
90
  end
91
91
  end
92
92
  end
@@ -0,0 +1,70 @@
1
+ require 'base64'
2
+ require 'faraday'
3
+
4
+ module Diplomat
5
+ # Agent API endpoint methods
6
+ # @see https://www.consul.io/docs/agent/http/agent.html
7
+ class Agent < Diplomat::RestClient
8
+ @access_methods = %i[self checks services members]
9
+
10
+ # Get agent configuration
11
+ # @return [OpenStruct] all data associated with the node
12
+ def self
13
+ url = ['/v1/agent/self']
14
+
15
+ # If the request fails, it's probably due to a bad path
16
+ # so return a PathNotFound error.
17
+ begin
18
+ ret = @conn.get concat_url url
19
+ rescue Faraday::ClientError
20
+ raise Diplomat::PathNotFound
21
+ end
22
+ JSON.parse(ret.body).tap { |node| OpenStruct.new node }
23
+ end
24
+
25
+ # Get local agent checks
26
+ # @return [OpenStruct] all agent checks
27
+ def checks
28
+ url = ['/v1/agent/checks']
29
+
30
+ # If the request fails, it's probably due to a bad path
31
+ # so return a PathNotFound error.
32
+ begin
33
+ ret = @conn.get concat_url url
34
+ rescue Faraday::ClientError
35
+ raise Diplomat::PathNotFound
36
+ end
37
+ JSON.parse(ret.body).tap { |node| OpenStruct.new node }
38
+ end
39
+
40
+ # Get local agent services
41
+ # @return [OpenStruct] all agent services
42
+ def services
43
+ url = ['/v1/agent/services']
44
+
45
+ # If the request fails, it's probably due to a bad path
46
+ # so return a PathNotFound error.
47
+ begin
48
+ ret = @conn.get concat_url url
49
+ rescue Faraday::ClientError
50
+ raise Diplomat::PathNotFound
51
+ end
52
+ JSON.parse(ret.body).tap { |node| OpenStruct.new node }
53
+ end
54
+
55
+ # Get cluster members (as seen by the agent)
56
+ # @return [OpenStruct] all members
57
+ def members
58
+ url = ['/v1/agent/members']
59
+
60
+ # If the request fails, it's probably due to a bad path
61
+ # so return a PathNotFound error.
62
+ begin
63
+ ret = @conn.get concat_url url
64
+ rescue Faraday::ClientError
65
+ raise Diplomat::PathNotFound
66
+ end
67
+ JSON.parse(ret.body).map { |node| OpenStruct.new node }
68
+ end
69
+ end
70
+ end
@@ -12,5 +12,35 @@ module Diplomat
12
12
  def use_consistency(options)
13
13
  options && options[:consistency] ? [options[:consistency].to_s] : []
14
14
  end
15
+
16
+ # Mapping for valid key/value store transaction verbs and required parameters
17
+ #
18
+ # @return [Hash] valid key/store transaction verbs and required parameters
19
+ # rubocop:disable MethodLength
20
+ def valid_transaction_verbs
21
+ {
22
+ 'set' => %w[Key Value],
23
+ 'cas' => %w[Key Value Index],
24
+ 'lock' => %w[Key Value Session],
25
+ 'unlock' => %w[Key Value Session],
26
+ 'get' => %w[Key],
27
+ 'get-tree' => %w[Key],
28
+ 'check-index' => %w[Key Index],
29
+ 'check-session' => %w[Key Session],
30
+ 'delete' => %w[Key],
31
+ 'delete-tree' => %w[Key],
32
+ 'delete-cas' => %w[Key Index]
33
+ }
34
+ end
35
+ # rubocop:enable MethodLength
36
+
37
+ # Key/value store transactions that require that a value be set
38
+ #
39
+ # @return [Array<String>] verbs that require a value be set
40
+ def valid_value_transactions
41
+ @valid_value_transactions ||= valid_transaction_verbs.select do |verb, requires|
42
+ verb if requires.include? 'Value'
43
+ end
44
+ end
15
45
  end
16
46
  end
@@ -1,8 +1,8 @@
1
1
  module Diplomat
2
2
  # Methods for interacting with the Consul check API endpoint
3
3
  class Check < Diplomat::RestClient
4
- @access_methods = [:checks, :register_script, :register_ttl,
5
- :deregister, :pass, :warn, :fail]
4
+ @access_methods = %i[checks register_script register_ttl
5
+ deregister pass warn fail]
6
6
 
7
7
  # Get registered checks
8
8
  # @return [OpenStruct] all data associated with the service
@@ -10,4 +10,5 @@ module Diplomat
10
10
  class QueryAlreadyExists < StandardError; end
11
11
  class UnknownStatus < StandardError; end
12
12
  class IdParameterRequired < StandardError; end
13
+ class InvalidTransaction < StandardError; end
13
14
  end
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Event < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:fire, :get_all, :get]
6
+ @access_methods = %i[fire get_all get]
7
7
 
8
8
  # Send an event
9
9
  # @param name [String] the event name
@@ -11,17 +11,21 @@ module Diplomat
11
11
  # @param service [String] the target service name
12
12
  # @param node [String] the target node name
13
13
  # @param tag [String] the target tag name, must only be used with service
14
+ # @param dc [String] the dc to target
14
15
  # @return [nil]
15
- def fire(name, value = nil, service = nil, node = nil, tag = nil)
16
+ # rubocop:disable Metrics/ParameterLists
17
+ def fire(name, value = nil, service = nil, node = nil, tag = nil, dc = nil)
16
18
  url = ["/v1/event/fire/#{name}"]
17
19
  url += check_acl_token
18
20
  url += use_named_parameter('service', service) if service
19
21
  url += use_named_parameter('node', node) if node
20
22
  url += use_named_parameter('tag', tag) if tag
23
+ url += use_named_parameter('dc', dc) if dc
21
24
 
22
25
  @conn.put concat_url(url), value
23
26
  nil
24
27
  end
28
+ # rubocop:enable Metrics/ParameterLists
25
29
 
26
30
  # Get the list of events matching name
27
31
  # @param name [String] the name of the event (regex)
@@ -1,8 +1,8 @@
1
1
  module Diplomat
2
2
  # Methods for interacting with the Consul health API endpoint
3
3
  class Health < Diplomat::RestClient
4
- @access_methods = [:node, :checks, :service, :state,
5
- :any, :passing, :warning, :critical]
4
+ @access_methods = %i[node checks service state
5
+ any passing warning critical]
6
6
 
7
7
  # Get node health
8
8
  # @param n [String] the node
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Kv < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:get, :put, :delete]
6
+ @access_methods = %i[get put delete txn]
7
7
  attr_reader :key, :value, :raw
8
8
 
9
9
  # Get a value by its key, potentially blocking for the first or next value
@@ -115,9 +115,11 @@ module Diplomat
115
115
  req.url concat_url url
116
116
  req.body = value
117
117
  end
118
- @key = key if @raw.body == 'true'
119
- @value = value if @raw.body == 'true'
120
- @raw.body == 'true'
118
+ if @raw.body.chomp == 'true'
119
+ @key = key
120
+ @value = value
121
+ end
122
+ @raw.body.chomp == 'true'
121
123
  end
122
124
  # rubocop:enable MethodLength, AbcSize
123
125
 
@@ -137,6 +139,43 @@ module Diplomat
137
139
  @raw = @conn.delete concat_url url
138
140
  end
139
141
 
142
+ # Perform a key/value store transaction.
143
+ #
144
+ # @since 1.3.0
145
+ # @see https://www.consul.io/docs/agent/http/kv.html#txn Transaction key/value store API documentation
146
+ # @example Valid key/value store transaction format
147
+ # [
148
+ # {
149
+ # 'KV' => {
150
+ # 'Verb' => 'get',
151
+ # 'Key' => 'hello/world'
152
+ # }
153
+ # }
154
+ # ]
155
+ # @raise [Diplomat::InvalidTransaction] if transaction format is invalid
156
+ # @param value [Array] an array of transaction hashes
157
+ # @param [Hash] options transaction params
158
+ # @option options [Boolean] :decode_values of any GET requests, default: true
159
+ # @option options [String] :dc Target datacenter
160
+ # @option options [String] :consistency the accepted staleness level of the transaction.
161
+ # Can be 'stale' or 'consistent'
162
+ # @return [OpenStruct] result of the transaction
163
+ def txn(value, options = nil)
164
+ # Verify the given value for the transaction
165
+ transaction_verification(value)
166
+ # Will return 409 if transaction was rolled back
167
+ raw = @conn_no_err.put do |req|
168
+ url = ['/v1/txn']
169
+ url += check_acl_token
170
+ url += dc(options)
171
+ url += transaction_consistency(options)
172
+
173
+ req.url concat_url url
174
+ req.body = JSON.generate(value)
175
+ end
176
+ transaction_return JSON.parse(raw.body), options
177
+ end
178
+
140
179
  private
141
180
 
142
181
  def recurse_get(options)
@@ -154,5 +193,63 @@ module Diplomat
154
193
  def separator(options)
155
194
  options && options[:separator] ? use_named_parameter('separator', options[:separator]) : []
156
195
  end
196
+
197
+ def transaction_consistency(options)
198
+ return [] unless options
199
+ if options[:consistency] && options[:consistency] == 'stale'
200
+ ['stale']
201
+ elsif options[:consistency] && options[:consistency] == 'consistent'
202
+ ['consistent']
203
+ else
204
+ []
205
+ end
206
+ end
207
+
208
+ def transaction_verification(transaction)
209
+ raise Diplomat::InvalidTransaction unless transaction.is_a?(Array)
210
+ transaction.each do |req|
211
+ raise Diplomat::InvalidTransaction unless transaction_type_verification(req)
212
+ raise Diplomat::InvalidTransaction unless transaction_verb_verification(req['KV'])
213
+ end
214
+ # Encode all value transacations if all checks pass
215
+ encode_transaction(transaction)
216
+ end
217
+
218
+ def transaction_type_verification(txn)
219
+ txn.is_a?(Hash) && txn.keys == %w[KV]
220
+ end
221
+
222
+ def transaction_verb_verification(txn)
223
+ transaction_verb = txn['Verb']
224
+ raise Diplomat::InvalidTransaction unless valid_transaction_verbs.include? transaction_verb
225
+ test_requirements = valid_transaction_verbs[transaction_verb] - txn.keys
226
+ test_requirements.empty?
227
+ end
228
+
229
+ def encode_transaction(transaction)
230
+ transaction.each do |txn|
231
+ next unless valid_value_transactions.include? txn['KV']['Verb']
232
+ value = txn['KV']['Value']
233
+ txn['KV']['Value'] = Base64.encode64(value).chomp
234
+ end
235
+ end
236
+
237
+ def transaction_return(raw_return, options)
238
+ decoded_return =
239
+ options && options[:decode_values] == false ? raw_return : decode_transaction(raw_return)
240
+ OpenStruct.new decoded_return
241
+ end
242
+
243
+ def decode_transaction(transaction)
244
+ return transaction if transaction['Results'].nil? || transaction['Results'].empty?
245
+
246
+ transaction.tap do |txn|
247
+ txn['Results'].each do |resp|
248
+ next unless resp['KV']['Value']
249
+ value = resp['KV']['Value']
250
+ resp['KV']['Value'] = Base64.decode64(value) rescue nil # rubocop:disable RescueModifier
251
+ end
252
+ end
253
+ end
157
254
  end
158
255
  end
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Lock < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:acquire, :wait_to_acquire, :release]
6
+ @access_methods = %i[acquire wait_to_acquire release]
7
7
 
8
8
  # Acquire a lock
9
9
  # @param key [String] the key
@@ -11,6 +11,7 @@ module Diplomat
11
11
  # @param value [String] the value for the key
12
12
  # @param options [Hash] :dc string for dc specific query
13
13
  # @return [Boolean] If the lock was acquired
14
+ # rubocop:disable AbcSize
14
15
  def acquire(key, session, value = nil, options = nil)
15
16
  raw = @conn.put do |req|
16
17
  url = ["/v1/kv/#{key}"]
@@ -21,8 +22,9 @@ module Diplomat
21
22
  req.url concat_url url
22
23
  req.body = value unless value.nil?
23
24
  end
24
- raw.body == 'true'
25
+ raw.body.chomp == 'true'
25
26
  end
27
+ # rubocop:enable AbcSize
26
28
 
27
29
  # wait to aquire a lock
28
30
  # @param key [String] the key
@@ -1,7 +1,7 @@
1
1
  module Diplomat
2
2
  # Methods to interact with the Consul maintenance API endpoint
3
3
  class Maintenance < Diplomat::RestClient
4
- @access_methods = [:enabled, :enable]
4
+ @access_methods = %i[enabled enable]
5
5
 
6
6
  # Get the maintenance state of a host
7
7
  # @param n [String] the node
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Node < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:get, :get_all, :register, :deregister]
6
+ @access_methods = %i[get get_all register deregister]
7
7
 
8
8
  # Get a node by it's key
9
9
  # @param key [String] the key
@@ -2,7 +2,7 @@ module Diplomat
2
2
  # @depreciated
3
3
  # Methods for interacting with the Consul nodes API endpoint
4
4
  class Nodes < Diplomat::RestClient
5
- @access_methods = [:get, :get_all]
5
+ @access_methods = %i[get get_all]
6
6
 
7
7
  # Get all nodes
8
8
  # @deprecated Please use Diplomat::Node instead.
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Query < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:get, :get_all, :create, :delete, :update, :execute, :explain]
6
+ @access_methods = %i[get get_all create delete update execute explain]
7
7
 
8
8
  # Get a prepared query by it's key
9
9
  # @param key [String] the prepared query ID
@@ -3,7 +3,7 @@ module Diplomat
3
3
  class Service < Diplomat::RestClient
4
4
  include ApiOptions
5
5
 
6
- @access_methods = [:get, :get_all, :register, :deregister, :register_external, :deregister_external]
6
+ @access_methods = %i[get get_all register deregister register_external deregister_external]
7
7
 
8
8
  # Get a service by it's key
9
9
  # @param key [String] the key
@@ -1,7 +1,7 @@
1
1
  module Diplomat
2
2
  # Methods for interacting with the Consul session API endpoint
3
3
  class Session < Diplomat::RestClient
4
- @access_methods = [:create, :destroy, :list, :renew, :info, :node]
4
+ @access_methods = %i[create destroy list renew info node]
5
5
 
6
6
  # Create a new session
7
7
  # @param value [Object] hash or json representation of the session arguments
@@ -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
+ # @return [OpenStruct] the address of the leader
8
+ def leader
9
+ url = ['/v1/status/leader']
10
+ ret = @conn.get concat_url url
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
+ # @return [OpenStruct] an array of peers
16
+ def peers
17
+ url = ['/v1/status/peers']
18
+ ret = @conn.get concat_url url
19
+ JSON.parse(ret.body)
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Diplomat
2
- VERSION = '1.2.0'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diplomat
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hamelink
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-01-23 00:00:00.000000000 Z
12
+ date: 2017-04-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -29,16 +29,16 @@ dependencies:
29
29
  name: rake
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '0'
34
+ version: '12.0'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '0'
41
+ version: '12.0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: pry
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -99,16 +99,16 @@ dependencies:
99
99
  name: fivemat
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: '0'
104
+ version: '1.3'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - ">="
109
+ - - "~>"
110
110
  - !ruby/object:Gem::Version
111
- version: '0'
111
+ version: '1.3'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: gem-release
114
114
  requirement: !ruby/object:Gem::Requirement
@@ -141,16 +141,22 @@ dependencies:
141
141
  name: rubocop
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '0.47'
144
147
  - - ">="
145
148
  - !ruby/object:Gem::Version
146
- version: '0'
149
+ version: 0.47.1
147
150
  type: :development
148
151
  prerelease: false
149
152
  version_requirements: !ruby/object:Gem::Requirement
150
153
  requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: '0.47'
151
157
  - - ">="
152
158
  - !ruby/object:Gem::Version
153
- version: '0'
159
+ version: 0.47.1
154
160
  - !ruby/object:Gem::Dependency
155
161
  name: json
156
162
  requirement: !ruby/object:Gem::Requirement
@@ -194,6 +200,7 @@ files:
194
200
  - features/step_definitions/test_key_value.rb
195
201
  - lib/diplomat.rb
196
202
  - lib/diplomat/acl.rb
203
+ - lib/diplomat/agent.rb
197
204
  - lib/diplomat/api_options.rb
198
205
  - lib/diplomat/check.rb
199
206
  - lib/diplomat/configuration.rb
@@ -211,6 +218,7 @@ files:
211
218
  - lib/diplomat/rest_client.rb
212
219
  - lib/diplomat/service.rb
213
220
  - lib/diplomat/session.rb
221
+ - lib/diplomat/status.rb
214
222
  - lib/diplomat/version.rb
215
223
  homepage: https://github.com/WeAreFarmGeek/diplomat
216
224
  licenses: