diplomat 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: