diff --git a/.rubocop.yml b/.rubocop.yml index 7681dd0..2d19666 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,12 +2,30 @@ AllCops: DisplayCopNames: true +Metrics/AbcSize: + Max: 60 + +Metrics/CyclomaticComplexity: + Max: 15 + Metrics/LineLength: # This will disable the rule completely, regardless what other options you put Enabled: true # Change the default 80 chars limit value Max: 120 +Metrics/MethodLength: + # This cop checks if the length of a method exceeds some maximum value + Enabled: true + # Change the default 10 lines limit value + Max: 50 + # Allow classes longer than 100 lines of code ClassLength: Max: 250 + +Naming/UncommunicativeMethodParamName: + Enabled: false + +Lint/UnneededCopDisableDirective: + Enabled: false diff --git a/.travis.yml b/.travis.yml index 06e8161..6d83226 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,17 @@ language: ruby -before_install: - - gem install bundler +before_install: +- gem install bundler rvm: - - "2.0.0" - - "2.1.0" - - "2.2.1" - - "2.2.2" +- 2.3.8 +- 2.4.5 +- 2.5.3 +- 2.6.1 addons: - code_climate: - repo_token: 0c910d8f8af55cc2d2f38170d5362be4e16be8ce232df9c565057985af264b7b + code_climate: + repo_token: 0c910d8f8af55cc2d2f38170d5362be4e16be8ce232df9c565057985af264b7b +deploy: + provider: rubygems + api_key: + secure: RbB9Va0vmEY3hyCDuvDxj6G/cmgD8ZQ3Wwg2Xx+oi64AWOckmTa4e4nVR6231kkgF63fG2swpkRhyHU+pfRSy+m/Ng8qAK4IIDgce6FTMPbzY45TlzPDq9OKsLyChYtkCnRTjXI16cS9+t1OBAXtwEDgQnXG7VDU4cRi3NMyFCk= + on: + tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b3d830..e880cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # Changelog +## Next + +## 2.1.3 + +- 2019-03-20 Lars Remes [lars.remes@ramboll.fi][1] Support HTTP service checks + +## 2.1.2 + +Bugfix relase: fix #188 + +Release 2.1.0 did break a few things. Ensure more compatibility with 2.0.x + +## 2.1.1 + +Bugfix release. + +- Fix for #186 + +## 2.1.0 + +This release cleanup a lot the existing APIs while preserving ascending compatibility. +It will avoid relying on side effects to configure diplomat and allow to override most +configuration options per API call as implemented in https://github.com/WeAreFarmGeek/diplomat/pull/179. +It is now easy to use one instance of the lib and to perform several calls with different tokens and/or +consistency options for instance. + +Full changelog: + +- Fix behavior of HTTP 302 on some 2.5 ruby releases (#181 fix #171) +- Set flags attribute on KVPair during lock acquisition and release (#180) +- Now allow to override most parameters per request (#179) +- use dedup for safer/simpler conversion of results to hash #176 + +## 2.0.5 + +- Fix incorrect verbs for checks Fix https://github.com/WeAreFarmGeek/diplomat/issues/173 +- Use json_pure to avoid the need for installing a compiler. Fix https://github.com/WeAreFarmGeek/diplomat/issues/177 +- Allow updating Output with TTL checks https://github.com/WeAreFarmGeek/diplomat/pull/178 + +## 2.0.4 + +- automatic GEM publication from Travis when a tag is pushed +- Depreciate old Ruby version 2.2.x +- Bump bundler to version 2.0.x + +## 2.0.3 + +- 2018-09-06 Allow to register/deregister entities using tokens +- 2017-11-09 Josep M. Blanquer (@blanquer) Fix service deregister to use the proper verb (`PUT` instead of `GET`). Consul 1.x seems to +have started enforcing [it](https://www.consul.io/docs/upgrade-specific.html#http-verbs-are-enforced-in-many-http-apis). + ## 2.0.2 - 2017-08-23 Trevor Wood [trevor.g.wood@gmail.com][1] Revert the change to single values diff --git a/README.md b/README.md index ede8962..e2350e4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Diplomat -[![Gem Version](https://badge.fury.io/rb/diplomat.svg)](http://badge.fury.io/rb/diplomat) [![Gem](https://img.shields.io/gem/dt/diplomat.svg)](https://rubygems.org/gems/diplomat/versions/2.0.0) [![Build Status](https://travis-ci.org/WeAreFarmGeek/diplomat.svg?branch=master)](https://travis-ci.org/WeAreFarmGeek/diplomat) [![Code Climate](https://codeclimate.com/github/johnhamelink/diplomat.png)](https://codeclimate.com/github/WeAreFarmGeek/diplomat) [![Dependency Status](https://gemnasium.com/WeAreFarmGeek/diplomat.svg)](https://gemnasium.com/WeAreFarmGeek/diplomat) [![Inline docs](http://inch-ci.org/github/wearefarmgeek/diplomat.svg?branch=master)](http://inch-ci.org/github/wearefarmgeek/diplomat) +[![Gem Version](https://badge.fury.io/rb/diplomat.svg)](https://rubygems.org/gems/diplomat) [![Gem](https://img.shields.io/gem/dt/diplomat.svg)](https://rubygems.org/gems/diplomat/versions/2.0.0) [![Build Status](https://travis-ci.org/WeAreFarmGeek/diplomat.svg?branch=master)](https://travis-ci.org/WeAreFarmGeek/diplomat) [![Code Climate](https://codeclimate.com/github/johnhamelink/diplomat.svg)](https://codeclimate.com/github/WeAreFarmGeek/diplomat) [![Inline docs](http://inch-ci.org/github/wearefarmgeek/diplomat.svg?branch=master)](http://inch-ci.org/github/wearefarmgeek/diplomat) ### A HTTP Ruby API for [Consul](http://www.consul.io/) -![Diplomacy Boad Game](http://i.imgur.com/Nkuy4b7.jpg) +![Diplomacy Board Game](http://i.imgur.com/Nkuy4b7.jpg) ## FAQ @@ -72,12 +72,14 @@ foo = Diplomat::Kv.get('foo') ``` Or retrieve a value from another datacenter: + ```ruby foo = Diplomat::Kv.get('foo', :dc => 'dc-west') # => "baz" ``` You can also retrieve values recursively: + ```ruby Diplomat::Kv.put('foo/a', 'lorem') Diplomat::Kv.put('foo/b', 'ipsum') @@ -94,6 +96,7 @@ Or list all available keys: Diplomat::Kv.get('/', :keys => true) # => ['foo/a', 'foo/b'] ``` You can convert the consul data to a ruby hash + ```ruby Diplomat::Kv.put('foo/a', 'lorem') Diplomat::Kv.put('foo/b', 'ipsum') @@ -180,6 +183,14 @@ services = Diplomat::Service.get_all({ :dc => 'My_Datacenter' }) # => # ``` +#### Checks + +Register a check: +```ruby +headers = { "Authorization" => [ "Basic ZGlwbG9tYXQ6cGFzc3dvcmQ=" ] } +Diplomat::Check.register_http("http://#{addr}:#{port}/health_check", '10s', id: 'health-check-1', name: 'Health check', notes: 'Node level HTTP health check', method: 'GET', headers: {}, timeout: '1s') +``` + ### Datacenters Getting a list of datacenters is quite simple and gives you the option to extract all services out of @@ -310,15 +321,27 @@ Diplomat.configure do |config| config.url = "http://localhost:8888" # Set up a custom Faraday Middleware config.middleware = MyCustomMiddleware - # Connect into consul with custom access token (ACL) - config.acl_token = "xxxxxxxx-yyyy-zzzz-1111-222222222222" - # Set extra Faraday configuration options - config.options = {ssl: { version: :TLSv1_2 }} + # Set extra Faraday configuration options and custom access token (ACL) + config.options = {ssl: {version: :TLSv1_2}, headers: {"X-Consul-Token" => "xxxxxxxx-yyyy-zzzz-1111-222222222222"}} end ``` This is traditionally kept inside the `config/initializers` directory if you're using rails. The middleware allows you to customise what happens when faraday sends and receives data. This can be useful if you want to instrument your use of diplomat, for example. You can read more about Faraday's custom middleware [here](http://stackoverflow.com/a/20973008). +Alternatively, configuration settings can be overriden at each method call allowing for instance to address different consul agents, with some other token. + +```ruby +Diplomat::Service.get('foo', { http_addr: 'http://consu01:8500' }) +Diplomat::Service.get('foo', { http_addr: 'http://consu02:8500' }) +Diplomat::Kv.put('key/path', 'value', { http_addr: 'http://localhost:8500', dc: 'dc1', token: '111-222-333-444-555' }) +``` + +Most common options are: +* dc: target datacenter +* token: identity used to perform the corresponding action +* http_addr: to target a remote consul node +* stale: use consistency mode that allows any server to service the read regardless of whether it is the leader + ### Todo - [ ] Updating Docs with latest changes diff --git a/diplomat.gemspec b/diplomat.gemspec index 73ae792..ffa9ec7 100644 --- a/diplomat.gemspec +++ b/diplomat.gemspec @@ -9,17 +9,19 @@ Gem::Specification.new 'diplomat', Diplomat::VERSION do |spec| spec.files = `git ls-files lib README.md LICENSE features`.split("\n") - spec.add_development_dependency 'bundler', '~> 1.3' - spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'pry', '~> 0.9' - spec.add_development_dependency 'rspec', '~> 3.2' - spec.add_development_dependency 'fakes-rspec', '~> 2.1' + spec.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1' spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.0' + spec.add_development_dependency 'cucumber', '~> 2.0' + spec.add_development_dependency 'fakes-rspec', '~> 2.1' spec.add_development_dependency 'fivemat', '~> 1.3' spec.add_development_dependency 'gem-release', '~> 0.7' - spec.add_development_dependency 'cucumber', '~> 2.0' - spec.add_development_dependency 'rubocop', '~> 0.47', '>= 0.47.1' + spec.add_development_dependency 'pry', '~> 0.9' + spec.add_development_dependency 'rake', '~> 12.0' + spec.add_development_dependency 'rspec', '~> 3.2' + spec.add_development_dependency 'rubocop', '~> 0.49' + spec.add_development_dependency 'webmock' - spec.add_runtime_dependency 'json' if RUBY_VERSION < '1.9.3' + spec.add_runtime_dependency 'deep_merge', '~> 1.0', '>= 1.0.1' spec.add_runtime_dependency 'faraday', '~> 0.9' + spec.add_runtime_dependency 'json_pure' if RUBY_VERSION < '1.9.3' end diff --git a/lib/diplomat.rb b/lib/diplomat.rb index e18f451..7129402 100644 --- a/lib/diplomat.rb +++ b/lib/diplomat.rb @@ -23,11 +23,11 @@ def require_libs(*libs) raise 'Diplomat only supports ruby >= 2.0.0' unless RUBY_VERSION.to_f >= 2.0 - self.root_path = File.expand_path '..', __FILE__ - self.lib_path = File.expand_path '../diplomat', __FILE__ + self.root_path = File.expand_path __dir__ + self.lib_path = File.expand_path 'diplomat', __dir__ - require_libs 'configuration', 'rest_client', 'api_options', 'kv', 'datacenter', - 'service', 'members', 'node', 'nodes', 'check', 'health', 'session', 'lock', + require_libs 'configuration', 'rest_client', 'kv', 'datacenter', 'service', + 'members', 'node', 'nodes', 'check', 'health', 'session', 'lock', 'error', 'event', 'acl', 'maintenance', 'query', 'agent', 'status' self.configuration ||= Diplomat::Configuration.new diff --git a/lib/diplomat/acl.rb b/lib/diplomat/acl.rb index 8c0f599..f9b15aa 100644 --- a/lib/diplomat/acl.rb +++ b/lib/diplomat/acl.rb @@ -1,23 +1,22 @@ module Diplomat # Methods for interacting with the Consul ACL API endpoint class Acl < Diplomat::RestClient - include ApiOptions - @access_methods = %i[list info create destroy update] attr_reader :id, :type, :acl # Get Acl info by ID # @param id [String] ID of the Acl to get + # @param options [Hash] options parameter hash # @return [Hash] - # rubocop:disable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize - def info(id, options = nil, not_found = :reject, found = :return) + # rubocop:disable PerceivedComplexity + def info(id, options = {}, not_found = :reject, found = :return) @id = id @options = options - url = ["/v1/acl/info/#{id}"] - url << check_acl_token - url << use_consistency(options) + custom_params = [] + custom_params << use_consistency(options) + + raw = send_get_request(@conn_no_err, ["/v1/acl/info/#{id}"], options, custom_params) - raw = @conn_no_err.get concat_url url if raw.status == 200 && raw.body.chomp != 'null' case found when :reject @@ -37,55 +36,45 @@ def info(id, options = nil, not_found = :reject, found = :return) raise Diplomat::UnknownStatus, "status #{raw.status}: #{raw.body}" end end - # rubocop:enable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity # List all Acls + # @param options [Hash] options parameter hash # @return [List] list of [Hash] of Acls - def list - url = ['/v1/acl/list'] - url += check_acl_token - @raw = @conn_no_err.get concat_url url + def list(options = {}) + @raw = send_get_request(@conn_no_err, ['/v1/acl/list'], options) parse_body end # Update an Acl definition, create if not present # @param value [Hash] Acl definition, ID field is mandatory + # @param options [Hash] options parameter hash # @return [Hash] The result Acl - def update(value) - raise Diplomat::IdParameterRequired unless value['ID'] + def update(value, options = {}) + raise Diplomat::IdParameterRequired unless value['ID'] || value[:ID] - @raw = @conn.put do |req| - url = ['/v1/acl/update'] - url += check_acl_token - url += use_cas(@options) - req.url concat_url url - req.body = value.to_json - end + custom_params = use_cas(@options) + @raw = send_put_request(@conn, ['/v1/acl/update'], options, value.to_json, custom_params) parse_body end # Create an Acl definition # @param value [Hash] Acl definition, ID field is mandatory + # @param options [Hash] options parameter hash # @return [Hash] The result Acl - def create(value) - @raw = @conn.put do |req| - url = ['/v1/acl/create'] - url += check_acl_token - url += use_cas(@options) - req.url concat_url url - req.body = value.to_json - end + def create(value, options = {}) + custom_params = use_cas(@options) + @raw = send_put_request(@conn, ['/v1/acl/create'], options, value.to_json, custom_params) parse_body end # Destroy an ACl token by its id # @param ID [String] the Acl ID + # @param options [Hash] options parameter hash # @return [Bool] - def destroy(id) + def destroy(id, options = {}) @id = id - url = ["/v1/acl/destroy/#{@id}"] - url << check_acl_token - @raw = @conn.put concat_url url + @raw = send_put_request(@conn, ["/v1/acl/destroy/#{@id}"], options, nil) @raw.body.chomp == 'true' end end diff --git a/lib/diplomat/agent.rb b/lib/diplomat/agent.rb index 27a56e3..d9ecceb 100644 --- a/lib/diplomat/agent.rb +++ b/lib/diplomat/agent.rb @@ -8,62 +8,34 @@ class Agent < Diplomat::RestClient @access_methods = %i[self checks services members] # Get agent configuration + # @param options [Hash] options parameter hash # @return [OpenStruct] all data associated with the node - def self - url = ['/v1/agent/self'] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end + def self(options = {}) + ret = send_get_request(@conn, ['/v1/agent/self'], options) JSON.parse(ret.body).tap { |node| OpenStruct.new node } end # Get local agent checks + # @param options [Hash] options parameter hash # @return [OpenStruct] all agent checks - def checks - url = ['/v1/agent/checks'] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end + def checks(options = {}) + ret = send_get_request(@conn, ['/v1/agent/checks'], options) JSON.parse(ret.body).tap { |node| OpenStruct.new node } end # Get local agent services + # @param options [Hash] options parameter hash # @return [OpenStruct] all agent services - def services - url = ['/v1/agent/services'] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end + def services(options = {}) + ret = send_get_request(@conn, ['/v1/agent/services'], options) JSON.parse(ret.body).tap { |node| OpenStruct.new node } end # Get cluster members (as seen by the agent) + # @param options [Hash] options parameter hash # @return [OpenStruct] all members - def members - url = ['/v1/agent/members'] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end + def members(options = {}) + ret = send_get_request(@conn, ['/v1/agent/members'], options) JSON.parse(ret.body).map { |node| OpenStruct.new node } end end diff --git a/lib/diplomat/api_options.rb b/lib/diplomat/api_options.rb deleted file mode 100644 index 03ce558..0000000 --- a/lib/diplomat/api_options.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Diplomat - # Helper methods for interacting with the Consul RESTful API - module ApiOptions - def check_acl_token - use_named_parameter('token', Diplomat.configuration.acl_token) - end - - def use_cas(options) - options ? use_named_parameter('cas', options[:cas]) : [] - end - - def use_consistency(options) - options && options[:consistency] ? [options[:consistency].to_s] : [] - end - - # Mapping for valid key/value store transaction verbs and required parameters - # - # @return [Hash] valid key/store transaction verbs and required parameters - # rubocop:disable MethodLength - def valid_transaction_verbs - { - 'set' => %w[Key Value], - 'cas' => %w[Key Value Index], - 'lock' => %w[Key Value Session], - 'unlock' => %w[Key Value Session], - 'get' => %w[Key], - 'get-tree' => %w[Key], - 'check-index' => %w[Key Index], - 'check-session' => %w[Key Session], - 'delete' => %w[Key], - 'delete-tree' => %w[Key], - 'delete-cas' => %w[Key Index] - } - end - # rubocop:enable MethodLength - - # Key/value store transactions that require that a value be set - # - # @return [Array] verbs that require a value be set - def valid_value_transactions - @valid_value_transactions ||= valid_transaction_verbs.select do |verb, requires| - verb if requires.include? 'Value' - end - end - end -end diff --git a/lib/diplomat/check.rb b/lib/diplomat/check.rb index b19d40e..b7c74fa 100644 --- a/lib/diplomat/check.rb +++ b/lib/diplomat/check.rb @@ -1,81 +1,137 @@ module Diplomat # Methods for interacting with the Consul check API endpoint class Check < Diplomat::RestClient - @access_methods = %i[checks register_script register_ttl + @access_methods = %i[checks register_http register_script register_ttl deregister pass warn fail] # Get registered checks # @return [OpenStruct] all data associated with the service - def checks - ret = @conn.get '/v1/agent/checks' + def checks(options = {}) + ret = send_get_request(@conn, ['/v1/agent/checks'], options) JSON.parse(ret.body) end - # Register a check - # @param check_id [String] the unique id of the check - # @param name [String] the name + # Register a HTTP check + # @param name [String] the name of the check + # @param url [string] URL to check + # @param interval [String] frequency (with units) of the check execution, for example, "10s" + # @param id [String] the unique id of the check # @param notes [String] notes about the check - # @param script [String] command to be run for check - # @param interval [String] frequency (with units) of the check execution - # @param ttl [String] time (with units) to mark a check down + # @param method [String] HTTP method to use when checking the url. Default is GET. + # @param headers [Hash] HTTP headers to add to the check call + # @param timeout [String] Timeout for check as string, for example, "1s" # @return [Integer] Status code # - def register_script(check_id, name, notes, script, interval) + def register_http(name, url, interval, id: nil, notes: nil, method: 'GET', headers: nil, timeout: nil) ret = @conn.put do |req| req.url '/v1/agent/check/register' req.body = JSON.generate( - 'ID' => check_id, 'Name' => name, 'Notes' => notes, 'Script' => script, 'Interval' => interval + 'ID' => check_id, + 'Name' => name, + 'Notes' => notes, + 'HTTP' => url, + 'Method' => method, + 'Header' => headers, + 'Interval' => interval, + 'Timeout' => timeout ) end ret.status == 200 end + # Register a script check + # @param check_id [String] the unique id of the check + # @param name [String] the name + # @param notes [String] notes about the check + # @param args [String[]] command to be run for check + # @param interval [String] frequency (with units) of the check execution + # @param options [Hash] options parameter hash + # @return [Integer] Status code + # rubocop:disable ParameterLists + def register_script(check_id, name, notes, args, interval, options = {}) + unless args.is_a?(Array) + raise(Diplomat::DeprecatedArgument, 'Script usage is deprecated, replace by an array of args') + end + + definition = JSON.generate( + 'ID' => check_id, + 'Name' => name, + 'Notes' => notes, + 'Args' => args, + 'Interval' => interval + ) + ret = send_put_request(@conn, ['/v1/agent/check/register'], options, definition) + ret.status == 200 + end + # rubocop:enable ParameterLists + # Register a TTL check # @param check_id [String] the unique id of the check # @param name [String] the name # @param notes [String] notes about the check # @param ttl [String] time (with units) to mark a check down + # @param options [Hash] options parameter hash # @return [Boolean] Success - def register_ttl(check_id, name, notes, ttl) - ret = @conn.put do |req| - req.url '/v1/agent/check/register' - req.body = JSON.generate( - 'ID' => check_id, 'Name' => name, 'Notes' => notes, 'TTL' => ttl - ) - end + def register_ttl(check_id, name, notes, ttl, options = {}) + definition = JSON.generate( + 'ID' => check_id, + 'Name' => name, + 'Notes' => notes, + 'TTL' => ttl + ) + ret = send_put_request(@conn, ['/v1/agent/check/register'], options, definition) ret.status == 200 end # Deregister a check # @param check_id [String] the unique id of the check + # @param options [Hash] options parameter hash # @return [Integer] Status code - def deregister(check_id) - ret = @conn.get "/v1/agent/check/deregister/#{check_id}" + def deregister(check_id, options = {}) + ret = send_put_request(@conn, ["/v1/agent/check/deregister/#{check_id}"], options, nil) ret.status == 200 end - # Pass a check + # Update a TTL check # @param check_id [String] the unique id of the check + # @param status [String] status of the check. Valid values are "passing", "warning", and "critical" + # @param output [String] human-readable message will be passed through to the check's Output field + # @param options [Hash] options parameter hash # @return [Integer] Status code - def pass(check_id) - ret = @conn.get "/v1/agent/check/pass/#{check_id}" + def update_ttl(check_id, status, output = nil, options = {}) + definition = JSON.generate( + 'Status' => status, + 'Output' => output + ) + ret = send_put_request(@conn, ["/v1/agent/check/update/#{check_id}"], options, definition) ret.status == 200 end - # Warn a check + # Pass a check # @param check_id [String] the unique id of the check + # @param output [String] human-readable message will be passed through to the check's Output field + # @param options [Hash] options parameter hash # @return [Integer] Status code - def warn(check_id) - ret = @conn.get "/v1/agent/check/warn/#{check_id}" - ret.status == 200 + def pass(check_id, output = nil, options = {}) + update_ttl(check_id, 'passing', output, options) end # Warn a check # @param check_id [String] the unique id of the check + # @param output [String] human-readable message will be passed through to the check's Output field + # @param options [Hash] options parameter hash # @return [Integer] Status code - def fail(check_id) - ret = @conn.get "/v1/agent/check/fail/#{check_id}" - ret.status == 200 + def warn(check_id, output = nil, options = {}) + update_ttl(check_id, 'warning', output, options) + end + + # Fail a check + # @param check_id [String] the unique id of the check + # @param output [String] human-readable message will be passed through to the check's Output field + # @param options [Hash] options parameter hash + # @return [Integer] Status code + def fail(check_id, output = nil, options = {}) + update_ttl(check_id, 'critical', output, options) end end end diff --git a/lib/diplomat/configuration.rb b/lib/diplomat/configuration.rb index 8370a35..f3a450c 100644 --- a/lib/diplomat/configuration.rb +++ b/lib/diplomat/configuration.rb @@ -1,7 +1,7 @@ module Diplomat # Methods for configuring Diplomat class Configuration - attr_accessor :middleware + attr_reader :middleware attr_accessor :url, :acl_token, :options # Override defaults for configuration @@ -18,7 +18,10 @@ def initialize(url = 'http://localhost:8500', acl_token = nil, options = {}) # Define a middleware for Faraday # @param middleware [Class] Faraday Middleware class def middleware=(middleware) - return @middleware = middleware if middleware.is_a? Array + if middleware.is_a? Array + @middleware = middleware + return + end @middleware = [middleware] end end diff --git a/lib/diplomat/datacenter.rb b/lib/diplomat/datacenter.rb index a4808c6..2c5aeca 100644 --- a/lib/diplomat/datacenter.rb +++ b/lib/diplomat/datacenter.rb @@ -5,16 +5,15 @@ class Datacenter < Diplomat::RestClient # Get an array of all avaliable datacenters accessible by the local consul agent # @param meta [Hash] output structure containing header information about the request (index) + # @param options [Hash] options parameter hash # @return [OpenStruct] all datacenters avaliable to this consul agent - def get(meta = nil) - url = ['/v1/catalog/datacenters'] - - ret = @conn.get concat_url url + def get(meta = nil, options = {}) + ret = send_get_request(@conn, ['/v1/catalog/datacenters'], options) if meta && ret.headers - meta[:index] = ret.headers['x-consul-index'] - meta[:knownleader] = ret.headers['x-consul-knownleader'] - meta[:lastcontact] = ret.headers['x-consul-lastcontact'] + meta[:index] = ret.headers['x-consul-index'] if ret.headers['x-consul-index'] + meta[:knownleader] = ret.headers['x-consul-knownleader'] if ret.headers['x-consul-knownleader'] + meta[:lastcontact] = ret.headers['x-consul-lastcontact'] if ret.headers['x-consul-lastcontact'] end JSON.parse(ret.body) end diff --git a/lib/diplomat/error.rb b/lib/diplomat/error.rb index a80acb3..044c731 100644 --- a/lib/diplomat/error.rb +++ b/lib/diplomat/error.rb @@ -11,4 +11,5 @@ class QueryAlreadyExists < StandardError; end class UnknownStatus < StandardError; end class IdParameterRequired < StandardError; end class InvalidTransaction < StandardError; end + class DeprecatedArgument < StandardError; end end diff --git a/lib/diplomat/event.rb b/lib/diplomat/event.rb index d6aac2a..39c61ea 100644 --- a/lib/diplomat/event.rb +++ b/lib/diplomat/event.rb @@ -1,8 +1,6 @@ module Diplomat # Methods for interacting with the Consul event API endpoint class Event < Diplomat::RestClient - include ApiOptions - @access_methods = %i[fire get_all get] # Send an event @@ -12,17 +10,17 @@ class Event < Diplomat::RestClient # @param node [String] the target node name # @param tag [String] the target tag name, must only be used with service # @param dc [String] the dc to target + # @param options [Hash] options parameter hash # @return [nil] # rubocop:disable Metrics/ParameterLists - def fire(name, value = nil, service = nil, node = nil, tag = nil, dc = nil) - url = ["/v1/event/fire/#{name}"] - url += check_acl_token - url += use_named_parameter('service', service) if service - url += use_named_parameter('node', node) if node - url += use_named_parameter('tag', tag) if tag - url += use_named_parameter('dc', dc) if dc + def fire(name, value = nil, service = nil, node = nil, tag = nil, dc = nil, options = {}) + custom_params = [] + custom_params << use_named_parameter('service', service) if service + custom_params << use_named_parameter('node', node) if node + custom_params << use_named_parameter('tag', tag) if tag + custom_params << use_named_parameter('dc', dc) if dc - @conn.put concat_url(url), value + send_put_request(@conn, ["/v1/event/fire/#{name}"], options, value, custom_params) nil end # rubocop:enable Metrics/ParameterLists @@ -34,6 +32,7 @@ def fire(name, value = nil, service = nil, node = nil, tag = nil, dc = nil) # @param found [Symbol] behaviour if there are already events matching name; # :reject with exception, :return its current value, or :wait for its next value # @return [Array[hash]] The list of { :name, :payload } hashes + # @param options [Hash] options parameter hash # @note # Events are sent via the gossip protocol; there is no guarantee of delivery # success or order, but the local agent will store up to 256 events that do @@ -57,15 +56,9 @@ def fire(name, value = nil, service = nil, node = nil, tag = nil, dc = nil) # - W R - get the first or current value; always return something, but # block only when necessary # - W W - get the first or next value; wait until there is an update - # rubocop:disable MethodLength, AbcSize - def get_all(name = nil, not_found = :reject, found = :return) - url = ['/v1/event/list'] - url += check_acl_token - url += use_named_parameter('name', name) - url = concat_url url - + def get_all(name = nil, not_found = :reject, found = :return, options = {}) # Event list never returns 404 or blocks, but may return an empty list - @raw = @conn.get url + @raw = send_get_request(@conn, ['/v1/event/list'], options, use_named_parameter('name', name)) if JSON.parse(@raw.body).count.zero? case not_found when :reject @@ -78,19 +71,15 @@ def get_all(name = nil, not_found = :reject, found = :return) when :reject raise Diplomat::EventAlreadyExists, name when :return - # Always set the response to 200 so we always return - # the response body. - @raw.status = 200 @raw = parse_body return return_payload end end - @raw = wait_for_next_event(url) + @raw = wait_for_next_event(['/v1/event/list'], options, use_named_parameter('name', name)) @raw = parse_body return_payload end - # rubocop:enable MethodLength, AbcSize # Get a specific event in the sequence matching name # @param name [String] the name of the event (regex) @@ -104,6 +93,7 @@ def get_all(name = nil, not_found = :reject, found = :return) # @return [hash] A hash with keys :value and :token; # :value is a further hash of the :name and :payload of the event, # :token is the event's ordinate in the sequence and can be passed to future calls to get the subsequent event + # @param options [Hash] options parameter hash # @note # Whereas the consul API for events returns all past events that match # name, this method allows retrieval of individual events from that @@ -112,12 +102,9 @@ def get_all(name = nil, not_found = :reject, found = :return) # middle, though these can only be identified relative to the preceding # event. However, this is ideal for iterating through the sequence of # events (while being sure that none are missed). - # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize - def get(name = nil, token = :last, not_found = :wait, found = :return) - url = ['/v1/event/list'] - url += check_acl_token - url += use_named_parameter('name', name) - @raw = @conn.get concat_url url + # rubocop:disable PerceivedComplexity + def get(name = nil, token = :last, not_found = :wait, found = :return, options = {}) + @raw = send_get_request(@conn, ['/v1/event/list'], options, use_named_parameter('name', name)) body = JSON.parse(@raw.body) # TODO: deal with unknown symbols, invalid indices (find_index will return nil) idx = case token @@ -126,7 +113,7 @@ def get(name = nil, token = :last, not_found = :wait, found = :return) when :next then body.length else body.find_index { |e| e['ID'] == token } + 1 end - if idx == body.length + if JSON.parse(@raw.body).count.zero? || idx == body.length case not_found when :reject raise Diplomat::EventNotFound, name @@ -135,7 +122,7 @@ def get(name = nil, token = :last, not_found = :wait, found = :return) event_payload = '' event_token = :last when :wait - @raw = wait_for_next_event(url) + @raw = wait_for_next_event(['/v1/event/list'], options, use_named_parameter('name', name)) @raw = parse_body # If it's possible for two events to arrive at once, # this needs to #find again: @@ -151,7 +138,7 @@ def get(name = nil, token = :last, not_found = :wait, found = :return) when :return event = body[idx] event_name = event['Name'] - event_payload = Base64.decode64(event['Payload']) + event_payload = event['Payload'].nil? ? nil : Base64.decode64(event['Payload']) event_token = event['ID'] end end @@ -161,17 +148,19 @@ def get(name = nil, token = :last, not_found = :wait, found = :return) token: event_token } end - # rubocop:enable MethodLength, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity private - def wait_for_next_event(url) - index = @raw.headers['x-consul-index'] - url = [url, use_named_parameter('index', index)].join('&') - @conn.get do |req| - req.url concat_url url - req.options.timeout = 86_400 + def wait_for_next_event(url, options = {}, param = nil) + if options.nil? + options = { timeout: 86_400 } + else + options[:timeout] = 86_400 end + index = @raw.headers['x-consul-index'] + param += use_named_parameter('index', index) + send_get_request(@conn, url, options, param) end end end diff --git a/lib/diplomat/health.rb b/lib/diplomat/health.rb index 2dd18f2..d511a0e 100644 --- a/lib/diplomat/health.rb +++ b/lib/diplomat/health.rb @@ -8,75 +8,55 @@ class Health < Diplomat::RestClient # @param n [String] the node # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] all data associated with the node - def node(n, options = nil) - url = ["/v1/health/node/#{n}"] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] + def node(n, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - ret = @conn.get concat_url url + ret = send_get_request(@conn, ["/v1/health/node/#{n}"], options, custom_params) JSON.parse(ret.body).map { |node| OpenStruct.new node } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end # Get service checks # @param s [String] the service # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] all data associated with the node - def checks(s, options = nil) - url = ["/v1/health/checks/#{s}"] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] + def checks(s, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - ret = @conn.get concat_url url + ret = send_get_request(@conn, ["/v1/health/checks/#{s}"], options, custom_params) JSON.parse(ret.body).map { |check| OpenStruct.new check } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end # Get service health # @param s [String] the service - # @param options [Hash] :dc string for dc specific query - # @param options [Hash] :passing boolean to return only checks in passing state - # @param options [Hash] :tag string for specific tag + # @param options [Hash] options parameter hash # @return [OpenStruct] all data associated with the node - # rubocop:disable PerceivedComplexity, CyclomaticComplexity, AbcSize - def service(s, options = nil) - url = ["/v1/health/service/#{s}"] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - url << 'passing' if options && options[:passing] - url << use_named_parameter('tag', options[:tag]) if options && options[:tag] - url << use_named_parameter('near', options[:near]) if options && options[:near] + # rubocop:disable PerceivedComplexity + def service(s, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << ['passing'] if options[:passing] + custom_params << use_named_parameter('tag', options[:tag]) if options[:tag] + custom_params << use_named_parameter('near', options[:near]) if options[:near] - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - ret = @conn.get concat_url url + ret = send_get_request(@conn, ["/v1/health/service/#{s}"], options, custom_params) JSON.parse(ret.body).map { |service| OpenStruct.new service } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end - # rubocop:enable PerceivedComplexity, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity # Get service health # @param s [String] the state ("any", "passing", "warning", or "critical") # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] all data associated with the node - # rubocop:disable AbcSize - def state(s, options = nil) - url = ["/v1/health/state/#{s}"] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - url << use_named_parameter('near', options[:near]) if options && options[:near] + def state(s, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << use_named_parameter('near', options[:near]) if options[:near] - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - ret = @conn.get concat_url url + ret = send_get_request(@conn, ["/v1/health/state/#{s}"], options, custom_params) JSON.parse(ret.body).map { |status| OpenStruct.new status } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end - # rubocop:enable AbcSize # Convenience method to get services in any state def any diff --git a/lib/diplomat/kv.rb b/lib/diplomat/kv.rb index a42dc3c..5500562 100644 --- a/lib/diplomat/kv.rb +++ b/lib/diplomat/kv.rb @@ -1,8 +1,6 @@ module Diplomat # Methods for interacting with the Consul KV API endpoint class Kv < Diplomat::RestClient - include ApiOptions - @access_methods = %i[get put delete txn] attr_reader :key, :value, :raw @@ -42,24 +40,26 @@ class Kv < Diplomat::RestClient # - W R - get the first or current value; always return something, but # block only when necessary # - W W - get the first or next value; wait until there is an update - # rubocop:disable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize, LineLength - def get(key, options = nil, not_found = :reject, found = :return) - @key = key + # rubocop:disable PerceivedComplexity, MethodLength, LineLength, CyclomaticComplexity + def get(key, options = {}, not_found = :reject, found = :return) + key_subst = if key.start_with? '/' + key[1..-1] + else + key.freeze + end + @key = key_subst @options = options - - url = ["/v1/kv/#{@key}"] - url += recurse_get(@options) - url += check_acl_token - url += use_consistency(@options) - url += dc(@options) - url += keys(@options) - url += separator(@options) + custom_params = [] + custom_params << recurse_get(@options) + custom_params << use_consistency(options) + custom_params << dc(@options) + custom_params << keys(@options) + custom_params << separator(@options) return_nil_values = @options && @options[:nil_values] transformation = @options && @options[:transformation] && @options[:transformation].methods.find_index(:call) ? @options[:transformation] : nil - # 404s OK using this connection - raw = @conn_no_err.get concat_url url + raw = send_get_request(@conn_no_err, ["/v1/kv/#{@key}"], options, custom_params) if raw.status == 404 case not_found when :reject @@ -80,6 +80,7 @@ def get(key, options = nil, not_found = :reject, found = :return) return @raw.first['Session'] if @options && @options[:session] return decode_values if @options && @options[:decode_values] return convert_to_hash(return_value(return_nil_values, transformation, true)) if @options && @options[:convert_to_hash] + return return_value(return_nil_values, transformation) when :wait index = raw.headers['x-consul-index'] @@ -89,15 +90,17 @@ def get(key, options = nil, not_found = :reject, found = :return) end # Wait for first/next value - url += use_named_parameter('index', index) - @raw = @conn.get do |req| - req.url concat_url url - req.options.timeout = 86_400 + custom_params << use_named_parameter('index', index) + if options.nil? + options = { timeout: 86_400 } + else + options[:timeout] = 86_400 end + @raw = send_get_request(@conn, ["/v1/kv/#{@key}"], options, custom_params) @raw = parse_body return_value(return_nil_values, transformation) end - # rubocop:enable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize, LineLength + # rubocop:enable PerceivedComplexity, LineLength, MethodLength, CyclomaticComplexity # Associate a value with a key # @param key [String] the key @@ -107,25 +110,19 @@ def get(key, options = nil, not_found = :reject, found = :return) # @option options [String] :dc Target datacenter # @option options [String] :acquire Session to attach to key # @return [Bool] Success or failure of the write (can fail in c-a-s mode) - # rubocop:disable MethodLength, AbcSize - def put(key, value, options = nil) + def put(key, value, options = {}) @options = options - @raw = @conn.put do |req| - url = ["/v1/kv/#{key}"] - url += check_acl_token - url += use_cas(@options) - url += dc(@options) - url += acquire(@options) - req.url concat_url url - req.body = value - end + custom_params = [] + custom_params << use_cas(@options) + custom_params << dc(@options) + custom_params << acquire(@options) + @raw = send_put_request(@conn, ["/v1/kv/#{key}"], options, value, custom_params) if @raw.body.chomp == 'true' @key = key @value = value end @raw.body.chomp == 'true' end - # rubocop:enable MethodLength, AbcSize # Delete a value by its key # @param key [String] the key @@ -133,14 +130,13 @@ def put(key, value, options = nil) # @option options [String] :dc Target datacenter # @option options [Boolean] :recurse If to make recursive get or not # @return [OpenStruct] - def delete(key, options = nil) + def delete(key, options = {}) @key = key @options = options - url = ["/v1/kv/#{@key}"] - url += recurse_get(@options) - url += check_acl_token - url += dc(@options) - @raw = @conn.delete concat_url url + custom_params = [] + custom_params << recurse_get(@options) + custom_params << dc(@options) + @raw = send_delete_request(@conn, ["/v1/kv/#{@key}"], options, custom_params) end # Perform a key/value store transaction. @@ -164,46 +160,42 @@ def delete(key, options = nil) # @option options [String] :consistency the accepted staleness level of the transaction. # Can be 'stale' or 'consistent' # @return [OpenStruct] result of the transaction - def txn(value, options = nil) + def txn(value, options = {}) # Verify the given value for the transaction transaction_verification(value) # Will return 409 if transaction was rolled back - raw = @conn_no_err.put do |req| - url = ['/v1/txn'] - url += check_acl_token - url += dc(options) - url += transaction_consistency(options) - - req.url concat_url url - req.body = JSON.generate(value) - end + custom_params = [] + custom_params << dc(options) + custom_params << transaction_consistency(options) + raw = send_put_request(@conn_no_err, ['/v1/txn'], options, JSON.generate(value), custom_params) transaction_return JSON.parse(raw.body), options end private def recurse_get(options) - options && options[:recurse] ? ['recurse'] : [] + options[:recurse] ? ['recurse'] : [] end def dc(options) - options && options[:dc] ? use_named_parameter('dc', options[:dc]) : [] + options[:dc] ? use_named_parameter('dc', options[:dc]) : [] end def acquire(options) - options && options[:acquire] ? use_named_parameter('acquire', options[:acquire]) : [] + options[:acquire] ? use_named_parameter('acquire', options[:acquire]) : [] end def keys(options) - options && options[:keys] ? ['keys'] : [] + options[:keys] ? ['keys'] : [] end def separator(options) - options && options[:separator] ? use_named_parameter('separator', options[:separator]) : [] + options[:separator] ? use_named_parameter('separator', options[:separator]) : [] end def transaction_consistency(options) return [] unless options + if options[:consistency] && options[:consistency] == 'stale' ['stale'] elsif options[:consistency] && options[:consistency] == 'consistent' @@ -215,6 +207,7 @@ def transaction_consistency(options) def transaction_verification(transaction) raise Diplomat::InvalidTransaction unless transaction.is_a?(Array) + transaction.each do |req| raise Diplomat::InvalidTransaction unless transaction_type_verification(req) raise Diplomat::InvalidTransaction unless transaction_verb_verification(req['KV']) @@ -230,6 +223,7 @@ def transaction_type_verification(txn) def transaction_verb_verification(txn) transaction_verb = txn['Verb'] raise Diplomat::InvalidTransaction unless valid_transaction_verbs.include? transaction_verb + test_requirements = valid_transaction_verbs[transaction_verb] - txn.keys test_requirements.empty? end @@ -237,6 +231,7 @@ def transaction_verb_verification(txn) def encode_transaction(transaction) transaction.each do |txn| next unless valid_value_transactions.include? txn['KV']['Verb'] + value = txn['KV']['Value'] txn['KV']['Value'] = Base64.encode64(value).chomp end @@ -244,7 +239,7 @@ def encode_transaction(transaction) def transaction_return(raw_return, options) decoded_return = - options && options[:decode_values] == false ? raw_return : decode_transaction(raw_return) + options[:decode_values] == false ? raw_return : decode_transaction(raw_return) OpenStruct.new decoded_return end @@ -254,8 +249,12 @@ def decode_transaction(transaction) transaction.tap do |txn| txn['Results'].each do |resp| next unless resp['KV']['Value'] - value = resp['KV']['Value'] - resp['KV']['Value'] = Base64.decode64(value) rescue nil # rubocop:disable RescueModifier + + begin + resp['KV']['Value'] = Base64.decode64(resp['KV']['Value']) + rescue StandardError + nil + end end end end diff --git a/lib/diplomat/lock.rb b/lib/diplomat/lock.rb index da70a9c..c9f0ad8 100644 --- a/lib/diplomat/lock.rb +++ b/lib/diplomat/lock.rb @@ -1,39 +1,32 @@ module Diplomat # Methods for interacting with the Consul lock API endpoint class Lock < Diplomat::RestClient - include ApiOptions - @access_methods = %i[acquire wait_to_acquire release] # Acquire a lock # @param key [String] the key # @param session [String] the session, generated from Diplomat::Session.create # @param value [String] the value for the key - # @param options [Hash] :dc string for dc specific query + # @param options [Hash] options parameter hash # @return [Boolean] If the lock was acquired - # rubocop:disable AbcSize - def acquire(key, session, value = nil, options = nil) - raw = @conn.put do |req| - url = ["/v1/kv/#{key}"] - url += use_named_parameter('acquire', session) - url += check_acl_token - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - req.body = value unless value.nil? - end + def acquire(key, session, value = nil, options = {}) + custom_params = [] + custom_params << use_named_parameter('acquire', session) + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << use_named_parameter('flags', options[:flags]) if options && options[:flags] + data = value unless value.nil? + raw = send_put_request(@conn, ["/v1/kv/#{key}"], options, data, custom_params) raw.body.chomp == 'true' end - # rubocop:enable AbcSize # wait to aquire a lock # @param key [String] the key # @param session [String] the session, generated from Diplomat::Session.create # @param value [String] the value for the key # @param check_interval [Integer] number of seconds to wait between retries - # @param options [Hash] :dc string for dc specific query + # @param options [Hash] options parameter hash # @return [Boolean] If the lock was acquired - def wait_to_acquire(key, session, value = nil, check_interval = 10, options = nil) + def wait_to_acquire(key, session, value = nil, check_interval = 10, options = {}) acquired = false until acquired acquired = acquire(key, session, value, options) @@ -47,16 +40,15 @@ def wait_to_acquire(key, session, value = nil, check_interval = 10, options = ni # @param session [String] the session, generated from Diplomat::Session.create # @param options [Hash] :dc string for dc specific query # @return [nil] - def release(key, session, options = nil) - raw = @conn.put do |req| - url = ["/v1/kv/#{key}"] - url += use_named_parameter('release', session) - url += check_acl_token - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + # rubocop:disable AbcSize + def release(key, session, options = {}) + custom_params = [] + custom_params << use_named_parameter('release', session) + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << use_named_parameter('flags', options[:flags]) if options && options[:flags] + raw = send_put_request(@conn, ["/v1/kv/#{key}"], options, nil, custom_params) raw.body end + # rubocop:enable AbcSize end end diff --git a/lib/diplomat/maintenance.rb b/lib/diplomat/maintenance.rb index 0c32202..ab03987 100644 --- a/lib/diplomat/maintenance.rb +++ b/lib/diplomat/maintenance.rb @@ -7,7 +7,7 @@ class Maintenance < Diplomat::RestClient # @param n [String] the node # @param options [Hash] :dc string for dc specific query # @return [Hash] { :enabled => true, :reason => 'foo' } - def enabled(n, options = nil) + def enabled(n, options = {}) health = Diplomat::Health.new(@conn) result = health.node(n, options) .select { |check| check['CheckID'] == '_node_maintenance' } @@ -25,20 +25,17 @@ def enabled(n, options = nil) # @param reason [String] the reason for enabling maintenance mode # @param options [Hash] :dc string for dc specific query # @return true if call is successful - # rubocop:disable AbcSize - def enable(enable = true, reason = nil, options = nil) - raw = @conn.put do |req| - url = ['/v1/agent/maintenance'] - url << use_named_parameter('enable', enable.to_s) - url << use_named_parameter('reason', reason) if reason - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - req.url concat_url url - end + def enable(enable = true, reason = nil, options = {}) + custom_params = [] + custom_params << use_named_parameter('enable', enable.to_s) + custom_params << use_named_parameter('reason', reason) if reason + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_put_request(@conn, ['/v1/agent/maintenance'], options, nil, custom_params) return_status = raw.status == 200 raise Diplomat::UnknownStatus, "status #{raw.status}: #{raw.body}" unless return_status + return_status end - # rubocop:enable AbcSize end end diff --git a/lib/diplomat/members.rb b/lib/diplomat/members.rb index 8decfea..7546995 100644 --- a/lib/diplomat/members.rb +++ b/lib/diplomat/members.rb @@ -4,9 +4,10 @@ class Members < Diplomat::RestClient @access_methods = [:get] # Get all members + # @param options [Hash] options parameter hash # @return [OpenStruct] all data associated with the service - def get - ret = @conn.get '/v1/agent/members' + def get(options = {}) + ret = send_get_request(@conn, ['/v1/agent/members'], options) JSON.parse(ret.body) end end diff --git a/lib/diplomat/node.rb b/lib/diplomat/node.rb index f29af90..9fb153a 100644 --- a/lib/diplomat/node.rb +++ b/lib/diplomat/node.rb @@ -1,55 +1,42 @@ module Diplomat # Methods for interacting with the Consul node API endpoint class Node < Diplomat::RestClient - include ApiOptions - @access_methods = %i[get get_all register deregister] # Get a node by it's key # @param key [String] the key # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] all data associated with the node - def get(key, options = nil) - url = ["/v1/catalog/node/#{key}"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - ret = @conn.get concat_url url + def get(key, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ["/v1/catalog/node/#{key}"], options, custom_params) OpenStruct.new JSON.parse(ret.body) - rescue Faraday::ClientError - raise Diplomat::PathNotFound end # Get all the nodes # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] the list of all nodes - def get_all(options = nil) - url = ['/v1/catalog/nodes'] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - ret = @conn.get concat_url url + def get_all(options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ['/v1/catalog/nodes'], options, custom_params) JSON.parse(ret.body).map { |service| OpenStruct.new service } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end # Register a node # @param definition [Hash] Hash containing definition of a node to register + # @param options [Hash] options parameter hash # @return [Boolean] - def register(definition, path = '/v1/catalog/register') - register = @conn.put path, JSON.dump(definition) - + def register(definition, options = {}) + register = send_put_request(@conn, ['/v1/catalog/register'], options, JSON.dump(definition)) register.status == 200 end # De-register a node (and all associated services and checks) # @param definition [Hash] Hash containing definition of a node to de-register + # @param options [Hash] options parameter hash # @return [Boolean] - def deregister(definition, path = '/v1/catalog/deregister') - deregister = @conn.put path, JSON.dump(definition) - + def deregister(definition, options = {}) + deregister = send_put_request(@conn, ['/v1/catalog/deregister'], options, JSON.dump(definition)) deregister.status == 200 end end diff --git a/lib/diplomat/nodes.rb b/lib/diplomat/nodes.rb index 62cee13..5d4108f 100644 --- a/lib/diplomat/nodes.rb +++ b/lib/diplomat/nodes.rb @@ -6,20 +6,17 @@ class Nodes < Diplomat::RestClient # Get all nodes # @deprecated Please use Diplomat::Node instead. + # @param options [Hash] options parameter hash # @return [OpenStruct] all data associated with the nodes in catalog - def get - ret = @conn.get '/v1/catalog/nodes' + def get(options = {}) + ret = send_get_request(@conn, ['/v1/catalog/nodes'], options) JSON.parse(ret.body) end - def get_all(options = nil) - url = ['/v1/catalog/nodes'] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - ret = @conn.get concat_url url + def get_all(options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ['/v1/catalog/nodes'], options, custom_params) JSON.parse(ret.body).map { |service| OpenStruct.new service } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end end end diff --git a/lib/diplomat/query.rb b/lib/diplomat/query.rb index da239ac..a643645 100644 --- a/lib/diplomat/query.rb +++ b/lib/diplomat/query.rb @@ -1,52 +1,34 @@ module Diplomat # Methods for interacting with the Consul query API endpoint class Query < Diplomat::RestClient - include ApiOptions - @access_methods = %i[get get_all create delete update execute explain] # Get a prepared query by it's key # @param key [String] the prepared query ID # @param options [Hash] :dc string for dc specific query # @return [OpenStruct] all data associated with the prepared query - def get(key, options = nil) - url = ["/v1/query/#{key}"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - ret = @conn.get concat_url url + def get(key, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ["/v1/query/#{key}"], options, custom_params) JSON.parse(ret.body).map { |query| OpenStruct.new query } - rescue Faraday::ClientError - raise Diplomat::QueryNotFound end # Get all prepared queries # @param options [Hash] :dc Consul datacenter to query # @return [OpenStruct] the list of all prepared queries - def get_all(options = nil) - url = ['/v1/query'] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - ret = @conn.get concat_url url + def get_all(options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ['/v1/query'], options, custom_params) JSON.parse(ret.body).map { |query| OpenStruct.new query } - rescue Faraday::ClientError - raise Diplomat::PathNotFound end # Create a prepared query or prepared query template # @param definition [Hash] Hash containing definition of prepared query # @param options [Hash] :dc Consul datacenter to query # @return [String] the ID of the prepared query created - def create(definition, options = nil) - url = ['/v1/query'] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - @raw = @conn.post do |req| - req.url concat_url url - req.body = JSON.dump(definition) - end - + def create(definition, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + @raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params) parse_body rescue Faraday::ClientError raise Diplomat::QueryAlreadyExists @@ -56,12 +38,9 @@ def create(definition, options = nil) # @param key [String] the prepared query ID # @param options [Hash] :dc Consul datacenter to query # @return [Boolean] - def delete(key, options = nil) - url = ["/v1/query/#{key}"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - ret = @conn.delete concat_url url - + def delete(key, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_delete_request(@conn, ["/v1/query/#{key}"], options, custom_params) ret.status == 200 end @@ -70,16 +49,9 @@ def delete(key, options = nil) # @param definition [Hash] Hash containing updated definition of prepared query # @param options [Hash] :dc Consul datacenter to query # @return [Boolean] - def update(key, definition, options = nil) - url = ["/v1/query/#{key}"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - json_definition = JSON.dump(definition) - ret = @conn.put do |req| - req.url concat_url url - req.body = json_definition - end - + def update(key, definition, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_put_request(@conn, ["/v1/query/#{key}"], options, JSON.dump(definition), custom_params) ret.status == 200 end @@ -91,34 +63,25 @@ def update(key, definition, options = nil) # estimated round trip time from that node # @option limit [Integer] to limit the size of the return list to the given number of results # @return [OpenStruct] the list of results from the prepared query or prepared query template - # rubocop:disable PerceivedComplexity, CyclomaticComplexity, AbcSize - def execute(key, options = nil) - url = ["/v1/query/#{key}/execute"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - url << use_named_parameter('near', options[:near]) if options && options[:near] - url << use_named_parameter('limit', options[:limit]) if options && options[:limit] - - ret = @conn.get concat_url url + # rubocop:disable PerceivedComplexity + def execute(key, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << use_named_parameter('near', options[:near]) if options[:near] + custom_params << use_named_parameter('limit', options[:limit]) if options[:limit] + ret = send_get_request(@conn, ["/v1/query/#{key}/execute"], options, custom_params) OpenStruct.new JSON.parse(ret.body) - rescue Faraday::ClientError - raise Diplomat::QueryNotFound end - # rubocop:enable PerceivedComplexity, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity # Get the fully rendered query template # @param key [String] the prepared query ID or name # @param options [Hash] :dc Consul datacenter to query # @return [OpenStruct] the list of results from the prepared query or prepared query template - def explain(key, options = nil) - url = ["/v1/query/#{key}/explain"] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - - ret = @conn.get concat_url url + def explain(key, options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ["/v1/query/#{key}/explain"], options, custom_params) OpenStruct.new JSON.parse(ret.body) - rescue Faraday::ClientError - raise Diplomat::QueryNotFound end end end diff --git a/lib/diplomat/rest_client.rb b/lib/diplomat/rest_client.rb index cc67f57..087fcc7 100644 --- a/lib/diplomat/rest_client.rb +++ b/lib/diplomat/rest_client.rb @@ -1,14 +1,25 @@ +require 'deep_merge' + module Diplomat # Base class for interacting with the Consul RESTful API class RestClient @access_methods = [] + @configuration = nil # Initialize the fadaray connection # @param api_connection [Faraday::Connection,nil] supply mock API Connection - def initialize(api_connection = nil) + # @param configuration [Diplomat::Configuration] a dedicated config to use + def initialize(api_connection = nil, configuration: nil) + @configuration = configuration start_connection api_connection end + # Get client configuration or global one if not specified via initialize. + # @return [Diplomat::Configuration] used by this client + def configuration + @configuration || ::Diplomat.configuration + end + # Format url parameters into strings correctly # @param name [String] the name of the parameter # @param value [String] the value of the parameter @@ -21,6 +32,7 @@ def use_named_parameter(name, value) # @param parts [Array] the url chunks to be assembled # @return [String] the resultant url string def concat_url(parts) + parts.reject!(&:empty?) if parts.length > 1 parts.first + '?' + parts.drop(1).join('&') else @@ -80,8 +92,8 @@ def start_connection(api_connection = nil) end def build_connection(api_connection, raise_error = false) - api_connection || Faraday.new(Diplomat.configuration.url, Diplomat.configuration.options) do |faraday| - Diplomat.configuration.middleware.each do |middleware| + api_connection || Faraday.new(configuration.url, configuration.options) do |faraday| + configuration.middleware.each do |middleware| faraday.use middleware end @@ -93,65 +105,40 @@ def build_connection(api_connection, raise_error = false) end # Converts k/v data into ruby hash - # rubocop:disable MethodLength, AbcSize def convert_to_hash(data) - collection = [] - master = {} - data.each do |item| - split_up = item[:key].split('/') - sub_hash = {} - temp = nil - real_size = split_up.size - 1 - (0..real_size).each do |i| - if i.zero? - temp = {} - sub_hash[split_up[i]] = temp - next - end - if i == real_size - temp[split_up[i]] = item[:value] - else - new_h = {} - temp[split_up[i]] = new_h - temp = new_h - end - end - collection << sub_hash - end - - collection.each do |h| - master = deep_merge(master, h) - end - master - end - # rubocop:enable MethodLength, AbcSize - - def deep_merge(first, second) - merger = proc { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2 } - first.merge(second, &merger) + data.map do |item| + item[:key].split('/').reverse.reduce(item[:value]) { |h, v| { v => h } } + end.reduce(:deep_merge) end # Parse the body, apply it to the raw attribute def parse_body return JSON.parse(@raw.body) if @raw.status == 200 + raise Diplomat::UnknownStatus, "status #{@raw.status}: #{@raw.body}" end # Return @raw with Value fields decoded def decode_values return @raw if @raw.first.is_a? String + @raw.each_with_object([]) do |acc, el| - acc['Value'] = Base64.decode64(acc['Value']) rescue nil # rubocop:disable RescueModifier + begin + acc['Value'] = Base64.decode64(acc['Value']) + rescue StandardError + nil + end el << acc el end end # Get the key/value(s) from the raw output - # rubocop:disable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize + # rubocop:disable PerceivedComplexity def return_value(nil_values = false, transformation = nil, return_hash = false) @value = decode_values return @value if @value.first.is_a? String + if @value.count == 1 && !return_hash @value = @value.first['Value'] @value = transformation.call(@value) if transformation && !@value.nil? @@ -163,7 +150,7 @@ def return_value(nil_values = false, transformation = nil, return_hash = false) end.compact end end - # rubocop:enable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity # Get the name and payload(s) from the raw output def return_payload @@ -172,5 +159,112 @@ def return_payload payload: (Base64.decode64(e['Payload']) unless e['Payload'].nil?) } end end + + def use_cas(options) + options ? use_named_parameter('cas', options[:cas]) : [] + end + + def use_consistency(options) + options[:consistency] ? [options[:consistency].to_s] : [] + end + + # rubocop:disable PerceivedComplexity + # TODO: Migrate all custom params in options + def parse_options(options) + headers = nil + query_params = [] + url_prefix = nil + consistency = [] + + # Parse options used as header + headers = { 'X-Consul-Token' => configuration.acl_token } if configuration.acl_token + headers = { 'X-Consul-Token' => options[:token] } if options[:token] + + # Parse options used as query params + consistency = 'stale' if options[:stale] + consistency = 'leader' if options[:leader] + consistency = 'consistent' if options[:consistent] + query_params << consistency + + # Parse url host + url_prefix = options[:http_addr] if options[:http_addr] + { query_params: query_params, headers: headers, url_prefix: url_prefix } + end + # rubocop:enable PerceivedComplexity + + def send_get_request(connection, url, options, custom_params = nil) + rest_options = parse_options(options) + url += rest_options[:query_params] + url += custom_params unless custom_params.nil? + begin + connection.get do |req| + req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url) + rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil? + req.options.timeout = options[:timeout] if options[:timeout] + end + rescue Faraday::ClientError => e + raise Diplomat::PathNotFound, e + end + end + + def send_put_request(connection, url, options, data, custom_params = nil) + rest_options = parse_options(options) + url += rest_options[:query_params] + url += custom_params unless custom_params.nil? + connection.put do |req| + req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url) + rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil? + req.body = data unless data.nil? + end + end + + def send_post_request(connection, url, options, data, custom_params = nil) + rest_options = parse_options(options) + url += rest_options[:query_params] + url += custom_params unless custom_params.nil? + connection.post do |req| + req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url) + rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil? + req.body = JSON.dump(data) unless data.nil? + end + end + + def send_delete_request(connection, url, options, custom_params = nil) + rest_options = parse_options(options) + url += rest_options[:query_params] + url += custom_params unless custom_params.nil? + connection.delete do |req| + req.url rest_options[:url_prefix] ? rest_options[:url_prefix] + concat_url(url) : concat_url(url) + rest_options[:headers].map { |k, v| req.headers[k.to_sym] = v } unless rest_options[:headers].nil? + end + end + + # Mapping for valid key/value store transaction verbs and required parameters + # + # @return [Hash] valid key/store transaction verbs and required parameters + def valid_transaction_verbs + { + 'set' => %w[Key Value], + 'cas' => %w[Key Value Index], + 'lock' => %w[Key Value Session], + 'unlock' => %w[Key Value Session], + 'get' => %w[Key], + 'get-tree' => %w[Key], + 'check-index' => %w[Key Index], + 'check-session' => %w[Key Session], + 'delete' => %w[Key], + 'delete-tree' => %w[Key], + 'delete-cas' => %w[Key Index] + } + end + + # Key/value store transactions that require that a value be set + # + # @return [Array] verbs that require a value be set + def valid_value_transactions + @valid_value_transactions ||= valid_transaction_verbs.select do |verb, requires| + verb if requires.include? 'Value' + end + end end end diff --git a/lib/diplomat/service.rb b/lib/diplomat/service.rb index e3715a8..9d2dbb3 100644 --- a/lib/diplomat/service.rb +++ b/lib/diplomat/service.rb @@ -1,41 +1,27 @@ module Diplomat - # Methods for interacting with the Consul serivce API endpoint. + # Methods for interacting with the Consul service API endpoint. class Service < Diplomat::RestClient - include ApiOptions - @access_methods = %i[get get_all register deregister register_external deregister_external maintenance] # Get a service by it's key # @param key [String] the key # @param scope [Symbol] :first or :all results # @param options [Hash] options parameter hash - # @option wait [Integer] :wait string for wait time - # @option index [String] :index for index of last query - # @option dc [String] :dc data center to make request for - # @option tag [String] :tag service tag to get # @param meta [Hash] output structure containing header information about the request (index) # @return [OpenStruct] all data associated with the service - # rubocop:disable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize - def get(key, scope = :first, options = nil, meta = nil) - url = ["/v1/catalog/service/#{key}"] - url += check_acl_token - url << use_named_parameter('wait', options[:wait]) if options && options[:wait] - url << use_named_parameter('index', options[:index]) if options && options[:index] - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - url << use_named_parameter('tag', options[:tag]) if options && options[:tag] - - # If the request fails, it's probably due to a bad path - # so return a PathNotFound error. - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError => e - raise Diplomat::PathNotFound, e - end + # rubocop:disable PerceivedComplexity + def get(key, scope = :first, options = {}, meta = nil) + custom_params = [] + custom_params << use_named_parameter('wait', options[:wait]) if options[:wait] + custom_params << use_named_parameter('index', options[:index]) if options[:index] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + custom_params << use_named_parameter('tag', options[:tag]) if options[:tag] + ret = send_get_request(@conn, ["/v1/catalog/service/#{key}"], options, custom_params) if meta && ret.headers - meta[:index] = ret.headers['x-consul-index'] - meta[:knownleader] = ret.headers['x-consul-knownleader'] - meta[:lastcontact] = ret.headers['x-consul-lastcontact'] + meta[:index] = ret.headers['x-consul-index'] if ret.headers['x-consul-index'] + meta[:knownleader] = ret.headers['x-consul-knownleader'] if ret.headers['x-consul-knownleader'] + meta[:lastcontact] = ret.headers['x-consul-lastcontact'] if ret.headers['x-consul-lastcontact'] end if scope == :all @@ -44,75 +30,68 @@ def get(key, scope = :first, options = nil, meta = nil) OpenStruct.new JSON.parse(ret.body).first end end - # rubocop:enable PerceivedComplexity, MethodLength, CyclomaticComplexity, AbcSize + # rubocop:enable PerceivedComplexity # Get all the services # @param options [Hash] :dc Consul datacenter to query # @return [OpenStruct] the list of all services - def get_all(options = nil) - url = ['/v1/catalog/services'] - url += check_acl_token - url << use_named_parameter('dc', options[:dc]) if options && options[:dc] - begin - ret = @conn.get concat_url url - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end - + def get_all(options = {}) + custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil + ret = send_get_request(@conn, ['/v1/catalog/services'], options, custom_params) OpenStruct.new JSON.parse(ret.body) end # Register a service # @param definition [Hash] Hash containing definition of service + # @param options [Hash] options parameter hash # @return [Boolean] - def register(definition, path = '/v1/agent/service/register') - json_definition = JSON.dump(definition) - register = @conn.put path, json_definition + def register(definition, options = {}) + url = options[:path] || ['/v1/agent/service/register'] + register = send_put_request(@conn, url, options, JSON.dump(definition)) register.status == 200 end # De-register a service # @param service_name [String] Service name to de-register + # @param options [Hash] options parameter hash # @return [Boolean] - def deregister(service_name) - deregister = @conn.get "/v1/agent/service/deregister/#{service_name}" + def deregister(service_name, options = {}) + deregister = send_put_request(@conn, ["/v1/agent/service/deregister/#{service_name}"], options, nil) deregister.status == 200 end # Register an external service # @param definition [Hash] Hash containing definition of service + # @param options [Hash] options parameter hash # @return [Boolean] - def register_external(definition) - register(definition, '/v1/catalog/register') + def register_external(definition, options = {}) + register = send_put_request(@conn, ['/v1/catalog/register'], options, definition) + register.status == 200 end # Deregister an external service # @param definition [Hash] Hash containing definition of service + # @param options [Hash] options parameter hash # @return [Boolean] - def deregister_external(definition) - json_definition = JSON.dump(definition) - deregister = @conn.put '/v1/catalog/deregister', json_definition + def deregister_external(definition, options = {}) + deregister = send_put_request(@conn, ['/v1/catalog/deregister'], options, JSON.dump(definition)) deregister.status == 200 end # Enable or disable maintenance for a service - # @param [Hash] opts the options for enabling or disabling maintenance for a service + # @param service_id [String] id of the service + # @param options [Hash] opts the options for enabling or disabling maintenance for a service # @options opts [Boolean] :enable (true) whether to enable or disable maintenance # @options opts [String] :reason reason for the service maintenance # @raise [Diplomat::PathNotFound] if the request fails # @return [Boolean] if the request was successful or not def maintenance(service_id, options = { enable: true }) - url = ["/v1/agent/service/maintenance/#{service_id}"] - url += check_acl_token - url << ["enable=#{options[:enable]}"] - url << ["reason=#{options[:reason].split(' ').join('+')}"] if options && options[:reason] - begin - maintenance = @conn.put concat_url(url) - rescue Faraday::ClientError - raise Diplomat::PathNotFound - end + custom_params = [] + custom_params << ["enable=#{options[:enable]}"] + custom_params << ["reason=#{options[:reason].split(' ').join('+')}"] if options[:reason] + maintenance = send_put_request(@conn, ["/v1/agent/service/maintenance/#{service_id}"], + options, nil, custom_params) maintenance.status == 200 end - # rubocop:enable AbcSize end end diff --git a/lib/diplomat/session.rb b/lib/diplomat/session.rb index 8b053f3..a252647 100644 --- a/lib/diplomat/session.rb +++ b/lib/diplomat/session.rb @@ -6,18 +6,14 @@ class Session < Diplomat::RestClient # Create a new session # @param value [Object] hash or json representation of the session arguments # @param options [Hash] session options - # @param options [String] :dc datacenter to create session for # @return [String] The sesssion id - def create(value = nil, options = nil) + def create(value = nil, options = {}) # TODO: only certain keys are recognised in a session create request, # should raise an error on others. - raw = @conn.put do |req| - url = ['/v1/session/create'] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - req.body = (value.is_a?(String) ? value : JSON.generate(value)) unless value.nil? - end + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + data = value.is_a?(String) ? value : JSON.generate(value) unless value.nil? + raw = send_put_request(@conn, ['/v1/session/create'], options, data, custom_params) body = JSON.parse(raw.body) body['ID'] end @@ -25,74 +21,54 @@ def create(value = nil, options = nil) # Destroy a session # @param id [String] session id # @param options [Hash] session options - # @param options [String] :dc datacenter to destroy session for # @return [String] Success or failure of the session destruction - def destroy(id, options = nil) - raw = @conn.put do |req| - url = ["/v1/session/destroy/#{id}"] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + def destroy(id, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_put_request(@conn, ["/v1/session/destroy/#{id}"], options, nil, custom_params) raw.body end # List sessions # @param options [Hash] session options - # @param options [String] :dc datacenter to list sessions # @return [OpenStruct] - def list(options = nil) - raw = @conn.get do |req| - url = ['/v1/session/list'] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + def list(options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_get_request(@conn, ['/v1/session/list'], options, custom_params) JSON.parse(raw.body).map { |session| OpenStruct.new session } end # Renew session # @param id [String] session id # @param options [Hash] session options - # @param options [String] :dc datacenter to renew session for # @return [OpenStruct] - def renew(id, options = nil) - raw = @conn.put do |req| - url = ["/v1/session/renew/#{id}"] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + def renew(id, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_put_request(@conn, ["/v1/session/renew/#{id}"], options, nil, custom_params) JSON.parse(raw.body).map { |session| OpenStruct.new session } end # Session information # @param id [String] session id # @param options [Hash] session options - # @param options [String] :dc datacenter to renew session for # @return [OpenStruct] - def info(id, options = nil) - raw = @conn.get do |req| - url = ["/v1/session/info/#{id}"] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + def info(id, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_get_request(@conn, ["/v1/session/info/#{id}"], options, custom_params) JSON.parse(raw.body).map { |session| OpenStruct.new session } end # Session information for a given node # @param name [String] node name # @param options [Hash] session options - # @param options [String] :dc datacenter to renew session for # @return [OpenStruct] - def node(name, options = nil) - raw = @conn.get do |req| - url = ["/v1/session/node/#{name}"] - url += use_named_parameter('dc', options[:dc]) if options && options[:dc] - - req.url concat_url url - end + def node(name, options = {}) + custom_params = [] + custom_params << use_named_parameter('dc', options[:dc]) if options[:dc] + raw = send_get_request(@conn, ["/v1/session/node/#{name}"], options, custom_params) JSON.parse(raw.body).map { |session| OpenStruct.new session } end end diff --git a/lib/diplomat/status.rb b/lib/diplomat/status.rb index 9ab0c66..40cf9eb 100644 --- a/lib/diplomat/status.rb +++ b/lib/diplomat/status.rb @@ -4,18 +4,18 @@ class Status < Diplomat::RestClient @access_methods = %i[leader peers] # Get the raft leader for the datacenter in which the local consul agent is running + # @param options [Hash] options parameter hash # @return [OpenStruct] the address of the leader - def leader - url = ['/v1/status/leader'] - ret = @conn.get concat_url url + def leader(options = {}) + ret = send_get_request(@conn, ['/v1/status/leader'], options) JSON.parse(ret.body) end # Get an array of Raft peers for the datacenter in which the agent is running + # @param options [Hash] options parameter hash # @return [OpenStruct] an array of peers - def peers - url = ['/v1/status/peers'] - ret = @conn.get concat_url url + def peers(options = {}) + ret = send_get_request(@conn, ['/v1/status/peers'], options) JSON.parse(ret.body) end end diff --git a/lib/diplomat/version.rb b/lib/diplomat/version.rb index 2ba2df7..301d64a 100644 --- a/lib/diplomat/version.rb +++ b/lib/diplomat/version.rb @@ -1,3 +1,3 @@ module Diplomat - VERSION = '2.0.2'.freeze + VERSION = '2.1.2'.freeze end diff --git a/spec/acl_spec.rb b/spec/acl_spec.rb index cc119e2..998a742 100644 --- a/spec/acl_spec.rb +++ b/spec/acl_spec.rb @@ -1,10 +1,8 @@ require 'spec_helper' describe Diplomat::Acl do - let(:faraday) { fake } - context 'acls' do - let(:key_url) { '/v1/acl' } + let(:key_url) { 'http://localhost:8500/v1/acl' } let(:id) { '8f246b77-f3e1-ff88-5b48-8ec93abf3e05' } let(:info_body) do [ @@ -49,9 +47,9 @@ json = JSON.generate(info_body) url = key_url + '/info/' + id - expect(faraday).to receive(:get).with(/#{url}/).and_return(OpenStruct.new(body: json, status: 200)) + stub_request(:get, url).to_return(OpenStruct.new(body: json, status: 200)) - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new info = acl.info(id) expect(info.size).to eq(1) @@ -62,9 +60,9 @@ json = 'null' url = key_url + '/info/' + 'none' - expect(faraday).to receive(:get).with(/#{url}/).and_return(OpenStruct.new(body: json, status: 200)) + stub_request(:get, url).to_return(OpenStruct.new(body: json, status: 200)) - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new expect { acl.info('none') }.to raise_error(Diplomat::AclNotFound) end @@ -75,9 +73,9 @@ json = JSON.generate(list_body) url = key_url + '/list' - expect(faraday).to receive(:get).with(/#{url}/).and_return(OpenStruct.new(body: json, status: 200)) + stub_request(:get, url).to_return(OpenStruct.new(body: json, status: 200)) - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new list = acl.list expect(list.size).to eq(2) @@ -89,18 +87,16 @@ json = JSON.generate(info_body.first) url = key_url + '/update' - req = fake - expect(faraday).to receive(:put).and_yield(req).and_return(OpenStruct.new(body: json, status: 200)) - expect(req).to receive(:url).with(/#{url}/) + stub_request(:put, url).to_return(OpenStruct.new(body: json, status: 200)) - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new response = acl.update(info_body.first) expect(response['ID']).to eq(info_body.first['ID']) end it 'fails if no ID is provided ' do - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new expect { acl.update(Name: 'test') }.to raise_error(Diplomat::IdParameterRequired) end end @@ -110,11 +106,10 @@ json = JSON.generate(info_body.first) url = key_url + '/create' - req = fake - expect(faraday).to receive(:put).and_yield(req).and_return(OpenStruct.new(body: json, status: 200)) - expect(req).to receive(:url).with(/#{url}/) + stub_request(:put, url) + .with(body: json).to_return(OpenStruct.new(body: json, status: 200)) - acl = Diplomat::Acl.new(faraday) + acl = Diplomat::Acl.new response = acl.create(info_body.first) expect(response['ID']).to eq(info_body.first['ID']) @@ -124,8 +119,9 @@ describe 'destroy' do it 'return the ID' do url = key_url + '/destroy/' + id - expect(faraday).to receive(:put).with(/#{url}/).and_return(OpenStruct.new(body: "true\n", status: 200)) - acl = Diplomat::Acl.new(faraday) + stub_request(:put, url).to_return(OpenStruct.new(body: "true\n", status: 200)) + + acl = Diplomat::Acl.new response = acl.destroy(id) expect(response).to be true diff --git a/spec/agent_spec.rb b/spec/agent_spec.rb index faf5b0b..e9a8052 100644 --- a/spec/agent_spec.rb +++ b/spec/agent_spec.rb @@ -1,13 +1,14 @@ require 'spec_helper' require 'json' require 'base64' - +# rubocop:disable describe Diplomat::Agent do let(:faraday) { fake } context 'agent' do it 'self' do - faraday.stub(:get).and_return(OpenStruct.new(body: <<-EOF)) + faraday.stub(:get) + .and_return(OpenStruct.new(body: <<-JSON)) { "Config": { "Bootstrap": true, @@ -73,7 +74,7 @@ "DelegateCur": 4 } } - EOF + JSON agent = Diplomat::Agent.new(faraday) @@ -81,7 +82,8 @@ end it 'checks' do - faraday.stub(:get).and_return(OpenStruct.new(body: <<-EOF)) + faraday.stub(:get) + .and_return(OpenStruct.new(body: <<-JSON)) { "service:redis": { "Node": "foobar", @@ -94,7 +96,7 @@ "ServiceName": "redis" } } - EOF + JSON agent = Diplomat::Agent.new(faraday) @@ -102,7 +104,8 @@ end it 'services' do - faraday.stub(:get).and_return(OpenStruct.new(body: <<-EOF)) + faraday.stub(:get) + .and_return(OpenStruct.new(body: <<-JSON)) { "redis": { "ID": "redis", @@ -112,7 +115,7 @@ "Port": 8000 } } - EOF + JSON agent = Diplomat::Agent.new(faraday) @@ -120,7 +123,8 @@ end it 'members' do - faraday.stub(:get).and_return(OpenStruct.new(body: <<-EOF)) + faraday.stub(:get) + .and_return(OpenStruct.new(body: <<-JSON)) [ { "Name": "foobar", @@ -141,7 +145,7 @@ "DelegateCur": 3 } ] - EOF + JSON agent = Diplomat::Agent.new(faraday) @@ -150,3 +154,4 @@ end end end +# rubocop:enable diff --git a/spec/check_spec.rb b/spec/check_spec.rb index e348c00..2867068 100644 --- a/spec/check_spec.rb +++ b/spec/check_spec.rb @@ -29,7 +29,25 @@ it 'register_script' do faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) check = Diplomat::Check.new(faraday) - expect(check.register_script('foobar-1', 'Foobar', 'Foobar test', '/script/test', '10s')).to eq(true) + expect(check.register_script('foobar-1', 'Foobar', 'Foobar test', ['/script/test'], '10s')).to eq(true) + end + + it 'register_http' do + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) + check = Diplomat::Check.new(faraday) + expect(check.register_http('Foobar', 'localhost', '10s', id: 'foobar-1', notes: 'Foobar test' , method: 'GET', headers: {}, timeout: '1s')).to eq(true) + end + + it 'register_http_invalid_params' do + faraday.stub(:put).and_return(OpenStruct.new(body: 'Request decode failed: invalid "interval": time: unknown unit x in duration 10x', status: 400)) + check = Diplomat::Check.new(faraday) + expect(check.register_http('Foobar', 'localhost', '10x', id: 'foobar-1', notes: 'Foobar test' , method: 'GET', headers: {}, timeout: '1s')).to eq(false) + end + + it 'register_http' do + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) + check = Diplomat::Check.new(faraday) + expect(check.register_http('foobar-1', 'Foobar', 'Foobar test', 'localhost', 'GET', {}, '10s')).to eq(true) end it 'register_ttl' do @@ -39,25 +57,25 @@ end it 'deregister' do - faraday.stub(:get).and_return(OpenStruct.new(body: '', status: 200)) + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) check = Diplomat::Check.new(faraday) expect(check.deregister('foobar-1')).to eq(true) end it 'pass' do - faraday.stub(:get).and_return(OpenStruct.new(body: '', status: 200)) + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) check = Diplomat::Check.new(faraday) expect(check.pass('foobar-1')).to eq(true) end it 'warn' do - faraday.stub(:get).and_return(OpenStruct.new(body: '', status: 200)) + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) check = Diplomat::Check.new(faraday) expect(check.warn('foobar-1')).to eq(true) end it 'fail' do - faraday.stub(:get).and_return(OpenStruct.new(body: '', status: 200)) + faraday.stub(:put).and_return(OpenStruct.new(body: '', status: 200)) check = Diplomat::Check.new(faraday) expect(check.fail('foobar-1')).to eq(true) end diff --git a/spec/configure_spec.rb b/spec/configure_spec.rb index 589e4aa..0bab5eb 100644 --- a/spec/configure_spec.rb +++ b/spec/configure_spec.rb @@ -49,13 +49,13 @@ def call(env) it 'Sets the correct configuration' do Diplomat.configure do |config| - config.url = 'http://google.com' + config.url = 'http://localhost:8500' config.acl_token = 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f1' config.middleware = StubMiddleware config.options = { ssl: { verify: true } } end - expect(Diplomat.configuration.url).to eq('http://google.com') + expect(Diplomat.configuration.url).to eq('http://localhost:8500') expect(Diplomat.configuration.acl_token).to eq('f45cbd0b-5022-47ab-8640-4eaa7c1f40f1') expect(Diplomat.configuration.middleware).to be_a(Array) expect(Diplomat.configuration.middleware.first).to eq(StubMiddleware) diff --git a/spec/datacenter_spec.rb b/spec/datacenter_spec.rb index 65dca7c..1d9b7ba 100644 --- a/spec/datacenter_spec.rb +++ b/spec/datacenter_spec.rb @@ -3,16 +3,14 @@ require 'base64' describe Diplomat::Datacenter do - let(:faraday) { fake } - context 'datacenters' do - let(:key_url) { '/v1/catalog/datacenters' } + let(:key_url) { 'http://localhost:8500/v1/catalog/datacenters' } let(:body) { %w[dc1 dc2] } let(:headers) do { - 'x-consul-index' => '8', - 'x-consul-knownleader' => 'true', - 'x-consul-lastcontact' => '0' + 'x-consul-index' => '8', + 'x-consul-knownleader' => 'true', + 'x-consul-lastcontact' => '0' } end @@ -20,9 +18,9 @@ it 'returns dcs' do json = JSON.generate(body) - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json)) + stub_request(:get, key_url).to_return(OpenStruct.new(body: json)) - datacenters = Diplomat::Datacenter.new(faraday) + datacenters = Diplomat::Datacenter expect(datacenters.get.size).to eq(2) expect(datacenters.get.first).to eq('dc1') @@ -33,9 +31,9 @@ it 'empty headers' do json = JSON.generate(body) - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json, headers: nil)) + stub_request(:get, key_url).to_return(OpenStruct.new(body: json, headers: nil)) - datacenters = Diplomat::Datacenter.new(faraday) + datacenters = Diplomat::Datacenter.new meta = {} expect(datacenters.get(meta).size).to eq(2) @@ -46,9 +44,10 @@ it 'filled headers' do json = JSON.generate(body) - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json, headers: headers)) + stub_request(:get, key_url) + .to_return(OpenStruct.new(body: json, headers: headers)) - datacenters = Diplomat::Datacenter.new(faraday) + datacenters = Diplomat::Datacenter.new meta = {} s = datacenters.get(meta) diff --git a/spec/event_spec.rb b/spec/event_spec.rb index 8dc17f4..7b2a2c2 100644 --- a/spec/event_spec.rb +++ b/spec/event_spec.rb @@ -196,61 +196,69 @@ describe 'FIRE' do it 'token empty' do - expect(faraday).to receive(:put).with("/v1/event/fire/#{event_name}", nil) + stub_request(:put, "http://localhost:8500/v1/event/fire/#{event_name}") Diplomat.configuration.acl_token = nil - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.fire(event_name) end it 'token specified' do - expect(faraday).to receive(:put).with("/v1/event/fire/#{event_name}?token=#{acl_token}", nil) + stub_request(:put, "http://localhost:8500/v1/event/fire/#{event_name}") + .with(headers: { 'X-Consul-Token' => acl_token }) Diplomat.configuration.acl_token = acl_token - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.fire(event_name) end end describe 'GET_ALL' do - before do - allow(faraday).to receive(:get).and_return(OpenStruct.new(body: events_json)) - end - it 'token empty' do - expect(faraday).to receive(:get).with('/v1/event/list') + stub_without_token = stub_request(:get, 'http://localhost:8500/v1/event/list') + .to_return(OpenStruct.new(body: events_json)) + stub_with_token = stub_request(:get, 'http://localhost:8500/v1/event/list') + .with(headers: { 'X-Consul-Token' => acl_token }) + .to_return(OpenStruct.new(body: events_json)) + Diplomat.configuration.acl_token = nil - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.get_all + expect(stub_without_token).to have_been_requested + expect(stub_with_token).not_to have_been_requested end it 'token specified' do - expect(faraday).to receive(:get).with("/v1/event/list?token=#{acl_token}") + stub_request(:get, 'http://localhost:8500/v1/event/list') + .with(headers: { 'X-Consul-Token' => acl_token }).to_return(OpenStruct.new(body: events_json)) Diplomat.configuration.acl_token = acl_token - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.get_all end end describe 'GET' do - before do - allow(faraday).to receive(:get).and_return(OpenStruct.new(body: events_json)) - end - it 'token empty' do - expect(faraday).to receive(:get).with('/v1/event/list') + stub_without_token = stub_request(:get, 'http://localhost:8500/v1/event/list') + .to_return(OpenStruct.new(body: events_json)) + stub_with_token = stub_request(:get, 'http://localhost:8500/v1/event/list') + .with(headers: { 'X-Consul-Token' => acl_token }) + .to_return(OpenStruct.new(body: events_json)) Diplomat.configuration.acl_token = nil - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.get + expect(stub_without_token).to have_been_requested + expect(stub_with_token).not_to have_been_requested end it 'token specified' do - expect(faraday).to receive(:get).with("/v1/event/list?token=#{acl_token}") + stub_request(:get, 'http://localhost:8500/v1/event/list') + .with(headers: { 'X-Consul-Token' => acl_token }).to_return(OpenStruct.new(body: events_json)) Diplomat.configuration.acl_token = acl_token - ev = Diplomat::Event.new(faraday) + ev = Diplomat::Event.new ev.get end diff --git a/spec/kv_spec.rb b/spec/kv_spec.rb index c5bcbb0..f23337f 100644 --- a/spec/kv_spec.rb +++ b/spec/kv_spec.rb @@ -14,10 +14,9 @@ describe '#get' do context 'Datacenter filter' do it 'GET' do - faraday.double - kv = Diplomat::Kv.new(faraday) - expect(faraday).to receive(:get).with(/dc=bar/) - .and_return(OpenStruct.new(status: 200, body: JSON.generate([]))) + kv = Diplomat::Kv.new + stub_request(:get, 'http://localhost:8500/v1/kv/foo?dc=bar') + .to_return(OpenStruct.new(status: 200, body: JSON.generate([]))) kv.get('foo', dc: 'bar') end end @@ -27,17 +26,17 @@ JSON.generate( [ { - 'Key' => key + 'dewfr', + 'Key' => key + 'dewfr', 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key + 'iamnil', + 'Key' => key + 'iamnil', 'Value' => nil, 'Flags' => 0 } @@ -74,17 +73,17 @@ JSON.generate( [ { - 'Key' => key + 'dewfr', + 'Key' => key + 'dewfr', 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key + 'iamnil', + 'Key' => key + 'iamnil', 'Value' => nil, 'Flags' => 0 } @@ -124,19 +123,24 @@ JSON.generate( [ { - 'Key' => key + '/dewfr', + 'Key' => key + '/dewfr', 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64(key_params), 'Flags' => 0 }, { - 'Key' => key + '/iamnil', + 'Key' => key + '/iamnil', 'Value' => nil, 'Flags' => 0 + }, + { + 'Key' => 'foo', + 'Value' => Base64.encode64('bar'), + 'Flags' => 0 } ] ) @@ -148,6 +152,7 @@ answer = {} answer[key] = {} answer[key]['dewfr'] = 'toast' + answer['foo'] = 'bar' expect(kv.get(key, recurse: true, convert_to_hash: true)).to eql(answer) end it 'GET with nil values' do @@ -157,6 +162,7 @@ answer[key] = {} answer[key]['dewfr'] = 'toast' answer[key]['iamnil'] = nil + answer['foo'] = 'bar' expect(kv.get(key, recurse: true, convert_to_hash: true, nil_values: true)).to eql(answer) end @@ -165,7 +171,7 @@ JSON.generate( [ { - 'Key' => key + '/dewfr', + 'Key' => key + '/dewfr', 'Value' => Base64.encode64(key_params), 'Flags' => 0 } @@ -267,7 +273,7 @@ JSON.generate( [ { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64(key_params), 'Flags' => 0 } @@ -294,7 +300,7 @@ JSON.generate( [ { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64('Faraday::ResourceNotFound: the server responded with status 404'), 'Flags' => 0 } @@ -323,7 +329,7 @@ JSON.generate( [ { - 'Key' => key, + 'Key' => key, 'Value' => Base64.encode64(key_params), 'Flags' => 0 } @@ -410,10 +416,9 @@ context 'ACLs NOT enabled, recurse option ON' do it 'DELETE' do - faraday.double - expect(faraday).to receive(:delete).with(/recurse/).and_return(OpenStruct.new(status: 200)) - - kv = Diplomat::Kv.new(faraday) + stub_request(:delete, 'http://localhost:8500/v1/kv/key?recurse') + .to_return(OpenStruct.new(status: 200)) + kv = Diplomat::Kv.new expect(kv.delete(key, recurse: true).status).to eq(200) end end @@ -570,18 +575,18 @@ end it 'returns a Hash' do - expect(kv.call(valid_transaction, nil)).to be_a_kind_of(OpenStruct) + expect(kv.call(valid_transaction, {})).to be_a_kind_of(OpenStruct) end it 'returns arrays as the values' do - return_values = kv.call(valid_transaction, nil) + return_values = kv.call(valid_transaction, {}) return_values.each_pair do |_, values| expect(values).to be_a_kind_of(Array) end end it 'returns arrays of Hash objects' do - return_values = kv.call(valid_transaction, nil) + return_values = kv.call(valid_transaction, {}) return_values.each_pair do |_, values| values.each do |value| expect(value).to be_a_kind_of(Hash) @@ -620,7 +625,7 @@ end it 'handles a rollback with missing results section' do - expect(rollback_kv.call(valid_transaction, nil).Errors).to eq(rollback_return['Errors']) + expect(rollback_kv.call(valid_transaction, {}).Errors).to eq(rollback_return['Errors']) end end diff --git a/spec/lock_spec.rb b/spec/lock_spec.rb index 2b41887..28e582f 100644 --- a/spec/lock_spec.rb +++ b/spec/lock_spec.rb @@ -8,7 +8,9 @@ let(:session) { 'fc5ca01a-c317-39ea-05e8-221da00d3a12' } let(:acl_token) { 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f1' } let(:dc) { 'some-dc' } + let(:flags) { 123 } let(:options) { { dc: dc } } + let(:options_flags) { { flags: 123 } } context 'lock' do context 'without an ACL token configured' do @@ -66,63 +68,123 @@ expect(lock.release('lock/key', session, options).chomp).to eq('true') end + + it 'acquires with flags option' do + expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&flags=#{flags}") + + lock = Diplomat::Lock.new(faraday) + + expect(lock.acquire('lock/key', session, nil, options_flags)).to eq(true) + end + + it 'waits to acquire with flags option' do + expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&flags=#{flags}") + + lock = Diplomat::Lock.new(faraday) + + expect(lock.wait_to_acquire('lock/key', session, nil, 2, options_flags)).to eq(true) + end + + it 'releases with flags option' do + expect(req).to receive(:url).with("/v1/kv/lock/key?release=#{session}&flags=#{flags}") + + lock = Diplomat::Lock.new(faraday) + + expect(lock.release('lock/key', session, options_flags).chomp).to eq('true') + end end context 'with an ACL token configured' do before do - expect(faraday).to receive(:put).and_yield(req).and_return(OpenStruct.new(body: "true\n", status: 200)) Diplomat.configure do |c| c.acl_token = acl_token end end it 'acquire' do - expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&token=#{acl_token}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new expect(lock.acquire('lock/key', session)).to eq(true) end it 'wait_to_acquire' do - expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&token=#{acl_token}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}") + .with( + headers: { 'X-Consul-Token' => acl_token }, + body: '2' + ).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new - expect(lock.wait_to_acquire('lock/key', session, 2)).to eq(true) + expect(lock.wait_to_acquire('lock/key', session, '2')).to eq(true) end it 'release' do - expect(req).to receive(:url).with("/v1/kv/lock/key?release=#{session}&token=#{acl_token}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?release=#{session}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new expect(lock.release('lock/key', session).chomp).to eq('true') end it 'acquires with dc option' do - expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&token=#{acl_token}&dc=#{dc}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}&dc=#{dc}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new expect(lock.acquire('lock/key', session, nil, options)).to eq(true) end it 'waits to acquire with dc option' do - expect(req).to receive(:url).with("/v1/kv/lock/key?acquire=#{session}&token=#{acl_token}&dc=#{dc}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}&dc=#{dc}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new expect(lock.wait_to_acquire('lock/key', session, nil, 2, options)).to eq(true) end it 'releases with dc option' do - expect(req).to receive(:url).with("/v1/kv/lock/key?release=#{session}&token=#{acl_token}&dc=#{dc}") + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?release=#{session}&dc=#{dc}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) - lock = Diplomat::Lock.new(faraday) + lock = Diplomat::Lock.new expect(lock.release('lock/key', session, options).chomp).to eq('true') end + + it 'acquires with flags option' do + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}&flags=#{flags}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) + puts "/v1/kv/lock/key?acquire=#{session}&flags=#{flags}" + + lock = Diplomat::Lock.new + + expect(lock.acquire('lock/key', session, nil, options_flags)).to eq(true) + end + + it 'waits to acquire with flags option' do + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?acquire=#{session}&flags=#{flags}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) + + lock = Diplomat::Lock.new + + expect(lock.wait_to_acquire('lock/key', session, nil, 2, options_flags)).to eq(true) + end + + it 'releases with flags option' do + stub_request(:put, "http://localhost:8500/v1/kv/lock/key?release=#{session}&flags=#{flags}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: "true\n", status: 200)) + + lock = Diplomat::Lock.new + + expect(lock.release('lock/key', session, options_flags).chomp).to eq('true') + end end end end diff --git a/spec/maintenance_spec.rb b/spec/maintenance_spec.rb index 04b00c3..ff5dc93 100644 --- a/spec/maintenance_spec.rb +++ b/spec/maintenance_spec.rb @@ -61,41 +61,32 @@ end context 'enable' do - before do - expect(faraday).to receive(:put).and_yield(req).and_return(OpenStruct.new(body: '', status: 200)) - end - it 'enables' do - maintenance = Diplomat::Maintenance.new(faraday) - expect(req).to receive(:url).with('/v1/agent/maintenance?enable=true') + maintenance = Diplomat::Maintenance.new + stub_request(:put, 'http://localhost:8500/v1/agent/maintenance?enable=true') + .to_return(OpenStruct.new(body: '', status: 200)) expect(maintenance.enable(true)).to eq(true) end it 'disables' do - maintenance = Diplomat::Maintenance.new(faraday) - expect(req).to receive(:url).with('/v1/agent/maintenance?enable=false') + maintenance = Diplomat::Maintenance.new + stub_request(:put, 'http://localhost:8500/v1/agent/maintenance?enable=false') + .to_return(OpenStruct.new(body: '', status: 200)) expect(maintenance.enable(false)).to eq(true) end it 'with reason' do - maintenance = Diplomat::Maintenance.new(faraday) - expect(req).to receive(:url).with('/v1/agent/maintenance?enable=true&reason=foobar') + maintenance = Diplomat::Maintenance.new + stub_request(:put, 'http://localhost:8500/v1/agent/maintenance?enable=true&reason=foobar') + .to_return(OpenStruct.new(body: '', status: 200)) expect(maintenance.enable(true, 'foobar')).to eq(true) end it 'with dc' do - maintenance = Diplomat::Maintenance.new(faraday) - expect(req).to receive(:url).with('/v1/agent/maintenance?enable=true&reason=foobar&dc=abc') + maintenance = Diplomat::Maintenance.new + stub_request(:put, 'http://localhost:8500/v1/agent/maintenance?enable=true&reason=foobar&dc=abc') + .to_return(OpenStruct.new(body: '', status: 200)) expect(maintenance.enable(true, 'foobar', dc: 'abc')).to eq(true) end end - - context 'enable raises errors' do - it 'throw error unless 200' do - expect(faraday).to receive(:put).and_yield(req).and_return(OpenStruct.new(body: '', status: 500)) - maintenance = Diplomat::Maintenance.new(faraday) - expect(req).to receive(:url).with('/v1/agent/maintenance?enable=true') - expect { maintenance.enable(true) }.to raise_error(Diplomat::UnknownStatus, 'status 500: ') - end - end end diff --git a/spec/node_spec.rb b/spec/node_spec.rb index dbd4470..9657ed0 100644 --- a/spec/node_spec.rb +++ b/spec/node_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' describe Diplomat::Node do - let(:faraday) { fake } - context 'nodes' do let(:node_definition) do { @@ -12,14 +10,15 @@ end describe '#register' do - let(:path) { '/v1/catalog/register' } + let(:path) { 'http://localhost:8500/v1/catalog/register' } it 'registers a node' do json = JSON.generate(node_definition) - faraday.stub(:put).with(path, json).and_return(OpenStruct.new(body: '', status: 200)) + stub_request(:put, path) + .with(body: json).and_return(OpenStruct.new(body: '', status: 200)) - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new n = node.register(node_definition) expect(n).to eq(true) @@ -27,14 +26,15 @@ end describe '#deregister' do - let(:path) { '/v1/catalog/deregister' } + let(:path) { 'http://localhost:8500/v1/catalog/deregister' } it 'de-registers a node' do json = JSON.generate(node_definition) - faraday.stub(:put).with(path, json).and_return(OpenStruct.new(body: '', status: 200)) + stub_request(:put, path) + .with(body: json).and_return(OpenStruct.new(body: '', status: 200)) - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new n = node.deregister(node_definition) expect(n).to eq(true) @@ -44,8 +44,8 @@ context 'services' do let(:key) { 'foobar' } - let(:key_url) { "/v1/catalog/node/#{key}" } - let(:all_url) { '/v1/catalog/nodes' } + let(:key_url) { "http://localhost:8500/v1/catalog/node/#{key}" } + let(:all_url) { 'http://localhost:8500/v1/catalog/nodes' } let(:body_all) do [ { @@ -82,12 +82,12 @@ } } end - let(:all_with_dc_url) { '/v1/catalog/nodes?dc=dc1' } + let(:all_with_dc_url) { 'http://localhost:8500/v1/catalog/nodes?dc=dc1' } let(:body_all_with_dc) do [ { - 'Address' => '10.1.10.14', - 'Node' => 'foo' + 'Address' => '10.1.10.14', + 'Node' => 'foo' } ] end @@ -96,18 +96,18 @@ it 'lists all the nodes' do json = JSON.generate(body_all) - faraday.stub(:get).with(all_url).and_return(OpenStruct.new(body: json)) + stub_request(:get, all_url).and_return(OpenStruct.new(body: json)) - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new expect(node.get_all.size).to eq(2) end it 'lists all the nodes' do json = JSON.generate(body_all_with_dc) - faraday.stub(:get).with(all_with_dc_url).and_return(OpenStruct.new(body: json)) + stub_request(:get, all_with_dc_url).and_return(OpenStruct.new(body: json)) - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new expect(node.get_all(dc: 'dc1').size).to eq(1) end end @@ -116,8 +116,8 @@ let(:cn) do json = JSON.generate(body) Diplomat.configuration.acl_token = nil - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json)) - node = Diplomat::Node.new(faraday) + stub_request(:get, key_url).and_return(OpenStruct.new(body: json)) + node = Diplomat::Node.new node.get('foobar') end @@ -136,27 +136,23 @@ let(:acl_token) { 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f1' } describe 'GET' do - # Stub Faraday's get method and return valid '{}' empty json for each parameter - before do - allow(faraday).to receive(:get).and_return(OpenStruct.new(body: '{}')) - end - # Verify that URL passed to Faraday is without token it 'token empty' do - expect(faraday).to receive(:get).with("/v1/catalog/node/#{node_name}") + stub_request(:get, "http://localhost:8500/v1/catalog/node/#{node_name}").and_return(OpenStruct.new(body: '{}')) Diplomat.configuration.acl_token = nil - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new node.get(node_name) end # Verify that URL passed to Faraday has token from Diplomat.configuration.acl_token it 'token specified' do - expect(faraday).to receive(:get).with("/v1/catalog/node/#{node_name}?token=#{acl_token}") + stub_request(:get, "http://localhost:8500/v1/catalog/node/#{node_name}") + .with(headers: { 'X-Consul-Token' => acl_token }).and_return(OpenStruct.new(body: '{}')) Diplomat.configuration.acl_token = acl_token - node = Diplomat::Node.new(faraday) + node = Diplomat::Node.new node.get(node_name) end diff --git a/spec/nodes_spec.rb b/spec/nodes_spec.rb index 156bf5c..3ecac23 100644 --- a/spec/nodes_spec.rb +++ b/spec/nodes_spec.rb @@ -12,7 +12,7 @@ } ] end - let(:all_datacenter_url) { "/v1/catalog/nodes?dc=#{datacenter}" } + let(:all_datacenter_url) { "http://localhost:8500/v1/catalog/nodes?dc=#{datacenter}" } context 'nodes' do it 'GET' do @@ -39,9 +39,9 @@ it 'GET ALL' do json = JSON.generate(datacenter_body_all) - faraday.stub(:get).with(all_datacenter_url).and_return(OpenStruct.new(body: json)) + stub_request(:get, all_datacenter_url).and_return(OpenStruct.new(body: json)) - nodes = Diplomat::Nodes.new(faraday) + nodes = Diplomat::Nodes.new options = { dc: 'us-west2' } expect(nodes.get_all(options).size).to eq(1) end diff --git a/spec/service_spec.rb b/spec/service_spec.rb index bcf7797..f5968fc 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -1,100 +1,84 @@ require 'spec_helper' describe Diplomat::Service do - let(:faraday) { fake } - context 'services' do let(:key) { 'toast' } - let(:key_url) { "/v1/catalog/service/#{key}" } - let(:key_url_with_alloptions) { "/v1/catalog/service/#{key}?wait=5m&index=3&dc=somedc" } - let(:key_url_with_indexoption) { "/v1/catalog/service/#{key}?index=5" } - let(:key_url_with_waitoption) { "/v1/catalog/service/#{key}?wait=6s" } - let(:key_url_with_datacenteroption) { "/v1/catalog/service/#{key}?dc=somedc" } - let(:key_url_with_tagoption) { "/v1/catalog/service/#{key}?tag=sometag" } - let(:services_url_with_datacenteroption) { '/v1/catalog/services?dc=somedc' } + let(:key_url) { "http://localhost:8500/v1/catalog/service/#{key}" } + let(:key_url_with_indexoption) { "http://localhost:8500/v1/catalog/service/#{key}?index=5" } + let(:key_url_with_waitoption) { "http://localhost:8500/v1/catalog/service/#{key}?wait=6s" } + let(:key_url_with_alloptions) { "http://localhost:8500/v1/catalog/service/#{key}?wait=5m&index=3&dc=somedc" } + let(:key_url_with_datacenteroption) { "http://localhost:8500/v1/catalog/service/#{key}?dc=somedc" } + let(:key_url_with_tagoption) { "http://localhost:8500/v1/catalog/service/#{key}?tag=sometag" } + let(:services_url_with_datacenteroption) { 'http://localhost:8500/v1/catalog/services?dc=somedc' } let(:body) do [ { - 'Node' => 'foo', - 'Address' => '10.1.10.12', - 'ServiceID' => key, + 'Node' => 'foo', + 'Address' => '10.1.10.12', + 'ServiceID' => key, 'ServiceName' => key, 'ServiceTags' => ['sometag'], 'ServicePort' => '70457' }, { - 'Node' => 'bar', - 'Address' => '10.1.10.13', - 'ServiceID' => key, + 'Node' => 'bar', + 'Address' => '10.1.10.13', + 'ServiceID' => key, 'ServiceName' => key, 'ServiceTags' => %w[sometag anothertag], 'ServicePort' => '70457' } ] end - let(:body_all) do + let(:headers) do { - service1: ['tag one', 'tag two', 'tag three'], - service2: ['tag four'] + 'x-consul-index' => '8', + 'x-consul-knownleader' => 'true', + 'x-consul-lastcontact' => '0' } end - let(:headers) do + let(:body_all) do { - 'x-consul-index' => '8', - 'x-consul-knownleader' => 'true', - 'x-consul-lastcontact' => '0' + service1: ['tag one', 'tag two', 'tag three'], + service2: ['tag four'] } end - - # Do not use ACL tokens for basic service tests before do Diplomat.configuration.acl_token = nil end - describe 'GET' do it ':first' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url) + .to_return(OpenStruct.new(body: json)) + service = Diplomat::Service.new expect(service.get('toast').Node).to eq('foo') end - it ':all' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url) + .to_return(OpenStruct.new(body: json)) + service = Diplomat::Service.new expect(service.get('toast', :all).size).to eq(2) expect(service.get('toast', :all).first.Node).to eq('foo') end end - describe 'GET With Options' do it 'empty headers' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json, headers: nil)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url) + .to_return(OpenStruct.new(body: json, headers: nil)) + service = Diplomat::Service.new meta = {} s = service.get('toast', :first, {}, meta) expect(s.Node).to eq('foo') expect(meta.length).to eq(0) end - it 'filled headers' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new meta = {} s = service.get('toast', :first, {}, meta) expect(s.Node).to eq('foo') @@ -105,11 +89,9 @@ it 'index option' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url_with_indexoption).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url_with_indexoption) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new options = { index: '5' } s = service.get('toast', :first, options) expect(s.Node).to eq('foo') @@ -117,11 +99,9 @@ it 'wait option' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url_with_waitoption).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url_with_waitoption) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new options = { wait: '6s' } s = service.get('toast', :first, options) expect(s.Node).to eq('foo') @@ -129,34 +109,27 @@ it 'datacenter option' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url_with_datacenteroption).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url_with_datacenteroption) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new options = { dc: 'somedc' } s = service.get('toast', :first, options) expect(s.Node).to eq('foo') end it 'bad datacenter option' do - faraday = double - - allow(faraday).to receive(:get) - .with(key_url_with_datacenteroption) - .and_raise(Faraday::ClientError.new({}, 500)) - - service = Diplomat::Service.new(faraday) + stub_request(:get, key_url_with_datacenteroption) + .to_return(status: [500, 'Internal Server Error']) + service = Diplomat::Service.new options = { dc: 'somedc' } expect { service.get('toast', :first, options) }.to raise_error(Diplomat::PathNotFound) end it 'tag option' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url_with_tagoption).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) + stub_request(:get, key_url_with_tagoption) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new options = { tag: 'sometag' } s = service.get('toast', :first, options) expect(s.Node).to eq('foo') @@ -164,11 +137,9 @@ it 'all options' do json = JSON.generate(body) - - faraday.stub(:get).with(key_url_with_alloptions).and_return(OpenStruct.new(body: json, headers: headers)) - - service = Diplomat::Service.new(faraday) - + stub_request(:get, key_url_with_alloptions) + .to_return(OpenStruct.new(body: json, headers: headers)) + service = Diplomat::Service.new options = { wait: '5m', index: '3', dc: 'somedc' } s = service.get('toast', :first, options) expect(s.Node).to eq('foo') @@ -181,10 +152,9 @@ it 'lists all the services for the default datacenter' do json = JSON.generate(body_all) - - faraday.stub(:get).and_return(OpenStruct.new(body: json)) - - service = Diplomat::Service.new(faraday) + stub_request(:get, 'http://localhost:8500/v1/catalog/services') + .to_return(OpenStruct.new(body: json)) + service = Diplomat::Service.new expect(service.get_all.service1).to be_an(Array) expect(service.get_all.service2).to be_an(Array) expect(service.get_all.service1.first).to eq(service_one_tag) @@ -192,11 +162,10 @@ end it 'lists all the services for the specified datacenter' do json = JSON.generate(body_all) - - faraday.stub(:get).with(services_url_with_datacenteroption).and_return(OpenStruct.new(body: json)) - + stub_request(:get, services_url_with_datacenteroption) + .to_return(OpenStruct.new(body: json)) options = { dc: 'somedc' } - service = Diplomat::Service.new(faraday) + service = Diplomat::Service.new expect(service.get_all(options).service1).to be_an(Array) expect(service.get_all(options).service2).to be_an(Array) expect(service.get_all(options).service1.first).to eq(service_one_tag) @@ -205,8 +174,9 @@ end describe 'Register service' do - let(:register_service_url) { '/v1/agent/service/register' } - let(:deregister_service_url) { '/v1/agent/service/deregister' } + let(:register_service_url) { 'http://localhost:8500/v1/agent/service/register' } + let(:deregister_service_url) { 'http://localhost:8500/v1/agent/service/deregister' } + let(:token) { '80a6200c-6ec9-42e1-816a-e40007e732a6' } let(:service_definition) do { name: 'test_service_definition', @@ -219,30 +189,39 @@ it 'can register a service' do json_request = JSON.dump(service_definition) + stub_request(:put, register_service_url) + .with(body: json_request).to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new + s = service.register(service_definition) + expect(s).to eq(true) + end - expect(faraday).to receive(:put).with(register_service_url, json_request) do - OpenStruct.new(body: '', status: 200) + it 'can register a service with a token' do + json_request = JSON.dump(service_definition) + stub_request(:put, register_service_url) + .with(body: json_request, headers: { 'X-Consul-Token' => token }) + .to_return(OpenStruct.new(body: '', status: 200)) + Diplomat.configure do |config| + config.acl_token = token end - - service = Diplomat::Service.new(faraday) + service = Diplomat::Service.new s = service.register(service_definition) expect(s).to eq(true) end it 'can deregister a service' do url = "#{deregister_service_url}/#{service_definition[:name]}" - expect(faraday).to receive(:get).with(url) do - OpenStruct.new(body: '', status: 200) - end - service = Diplomat::Service.new(faraday) + stub_request(:put, url) + .to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new s = service.deregister(service_definition[:name]) expect(s).to eq(true) end end describe 'Register external service' do - let(:register_service_url) { '/v1/catalog/register' } - let(:deregister_service_url) { '/v1/catalog/deregister' } + let(:register_service_url) { 'http://localhost:8500/v1/catalog/register' } + let(:deregister_service_url) { 'http://localhost:8500/v1/catalog/deregister' } let(:service_definition) do { @@ -265,23 +244,18 @@ it 'can register a service' do json_request = JSON.dump(service_definition) - - expect(faraday).to receive(:put).with(register_service_url, json_request) do - OpenStruct.new(body: '', status: 200) - end - - service = Diplomat::Service.new(faraday) - s = service.register_external(service_definition) + stub_request(:put, register_service_url) + .with(body: json_request).to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new + s = service.register_external(json_request) expect(s).to eq(true) end it 'can deregister a service' do json_request = JSON.dump(deregister_definition) - expect(faraday).to receive(:put).with(deregister_service_url, json_request) do - OpenStruct.new(body: '', status: 200) - end - - service = Diplomat::Service.new(faraday) + stub_request(:put, deregister_service_url) + .with(body: json_request).to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new s = service.deregister_external(deregister_definition) expect(s).to eq(true) end @@ -289,15 +263,16 @@ describe '#maintenance' do let(:service_id) { 'TEST1' } - let(:enable_url) { '/v1/agent/service/maintenance/TEST1?enable=true' } - let(:disable_url) { '/v1/agent/service/maintenance/TEST1?enable=false' } + let(:enable_url) { 'http://localhost:8500/v1/agent/service/maintenance/TEST1?enable=true' } + let(:disable_url) { 'http://localhost:8500/v1/agent/service/maintenance/TEST1?enable=false' } let(:message_url) { enable_url + '&reason=My+Maintenance+Reason' } context 'when enabling maintenance' do let(:s) do proc do |reason| - expect(faraday).to receive(:put).with(enable_url).and_return(OpenStruct.new(body: '', status: 200)) - service = Diplomat::Service.new(faraday) + stub_request(:put, enable_url) + .to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new service.maintenance(service_id, enable: 'true', reason: reason) end end @@ -312,8 +287,9 @@ it 'adds a reason' do reason = 'My Maintenance Reason' - expect(faraday).to receive(:put).with(message_url).and_return(OpenStruct.new(body: '', status: 200)) - service = Diplomat::Service.new(faraday) + stub_request(:put, message_url) + .to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new s_message = service.maintenance(service_id, enable: 'true', reason: reason) expect(s_message).to eq(true) end @@ -322,8 +298,9 @@ context 'when disabling maintenance' do let(:s) do proc do - expect(faraday).to receive(:put).with(disable_url).and_return(OpenStruct.new(body: '', status: 200)) - service = Diplomat::Service.new(faraday) + stub_request(:put, disable_url) + .to_return(OpenStruct.new(body: '', status: 200)) + service = Diplomat::Service.new service.maintenance(service_id, enable: 'false') end end @@ -340,53 +317,63 @@ end context 'acl' do - let(:acl_token) { 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f1' } - + let(:acl_token_1) { 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f1' } + let(:acl_token_2) { 'f45cbd0b-5022-47ab-8640-4eaa7c1f40f2' } describe 'GET' do let(:service_name) { 'toast' } - - before do - allow(faraday).to receive(:get).and_return(OpenStruct.new(body: '{}')) - end - it 'token empty' do - expect(faraday).to receive(:get).with("/v1/catalog/service/#{service_name}") + stub_without_token = stub_request(:get, "http://localhost:8500/v1/catalog/service/#{service_name}") + .to_return(OpenStruct.new(body: '{}')) + stub_with_token = stub_request(:get, "http://localhost:8500/v1/catalog/service/#{service_name}") + .with(headers: { 'X-Consul-Token' => acl_token_1 }).to_return(OpenStruct.new(body: '{}')) Diplomat.configuration.acl_token = nil - service = Diplomat::Service.new(faraday) - + service = Diplomat::Service.new service.get(service_name) + expect(stub_without_token).to have_been_requested + expect(stub_with_token).not_to have_been_requested end - - it 'token specified' do - expect(faraday).to receive(:get).with("/v1/catalog/service/#{service_name}?token=#{acl_token}") - Diplomat.configuration.acl_token = acl_token - service = Diplomat::Service.new(faraday) - + it 'token specified in configuration' do + stub_request(:get, "http://localhost:8500/v1/catalog/service/#{service_name}") + .with(headers: { 'X-Consul-Token' => acl_token_1 }).to_return(OpenStruct.new(body: '{}')) + Diplomat.configuration.acl_token = acl_token_1 + service = Diplomat::Service.new service.get(service_name) end + it 'token specified in method call' do + stub_request(:get, "http://localhost:8500/v1/catalog/service/#{service_name}") + .with(headers: { 'X-Consul-Token' => acl_token_2 }).to_return(OpenStruct.new(body: '{}')) + Diplomat.configuration.acl_token = acl_token_1 + service = Diplomat::Service.new + service.get(service_name, :first, token: acl_token_2) + end end - describe 'GET_ALL' do - before do - allow(faraday).to receive(:get).and_return(OpenStruct.new(body: '{}')) - end - + let(:service_name) { 'toast' } it 'token empty' do - expect(faraday).to receive(:get).with('/v1/catalog/services') + stub_without_token = stub_request(:get, 'http://localhost:8500/v1/catalog/services') + .to_return(OpenStruct.new(body: '{}')) + stub_with_token = stub_request(:get, 'http://localhost:8500/v1/catalog/services') + .with(headers: { 'X-Consul-Token' => acl_token_1 }).to_return(OpenStruct.new(body: '{}')) Diplomat.configuration.acl_token = nil - service = Diplomat::Service.new(faraday) - + service = Diplomat::Service.new service.get_all + expect(stub_without_token).to have_been_requested + expect(stub_with_token).not_to have_been_requested end - - it 'token specified' do - expect(faraday).to receive(:get).with("/v1/catalog/services?token=#{acl_token}") - - Diplomat.configuration.acl_token = acl_token - service = Diplomat::Service.new(faraday) - + it 'token specified in configuration' do + stub_request(:get, 'http://localhost:8500/v1/catalog/services') + .with(headers: { 'X-Consul-Token' => acl_token_1 }).to_return(OpenStruct.new(body: '{}')) + Diplomat.configuration.acl_token = acl_token_1 + service = Diplomat::Service.new service.get_all end + it 'token specified in method call' do + stub_request(:get, 'http://localhost:8500/v1/catalog/services') + .with(headers: { 'X-Consul-Token' => acl_token_2 }).to_return(OpenStruct.new(body: '{}')) + Diplomat.configuration.acl_token = acl_token_1 + service = Diplomat::Service.new + service.get_all(token: acl_token_2) + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 16c8ec0..346c4e2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,6 +11,7 @@ require 'diplomat' require 'fakes-rspec' +require 'webmock/rspec' RSpec.configure do |config| config.run_all_when_everything_filtered = true