Skip to content

Commit

Permalink
Merge branch 'master' into jlkiri/remove-deprecated-params-erb-new
Browse files Browse the repository at this point in the history
  • Loading branch information
manu-ns authored Dec 12, 2024
2 parents 9dc12ce + 4a37595 commit 493c780
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 23 deletions.
13 changes: 7 additions & 6 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- uses: actions/setup-ruby@v1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
- name: Install dependencies
run: |
gem install bundler
gem install bundler -v 2.4.22
bundler install
- name: Check code
run: bundle exec rubocop
Expand All @@ -30,7 +30,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby-version: ['2.4', '2.5', '2.6', '2.7']
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']

steps:
- uses: actions/checkout@v2
Expand All @@ -41,7 +41,7 @@ jobs:
bundler-cache: true
- name: Install dependencies
run: |
gem install bundler
gem install bundler -v 2.4.22
bundler install
- name: Run tests
run: bundle exec rspec
Expand All @@ -53,12 +53,13 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- uses: actions/setup-ruby@v1
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
bundler-cache: true
- name: Install dependencies
run: |
gem install bundler
gem install bundler -v 2.4.22
bundler install
- name: Build
run: gem build *.gemspec
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Build Status](https://github.com/criteo/consul-templaterb/workflows/Ruby/badge.svg?branch=master)](https://github.com/criteo/consul-templaterb/actions?query=branch%3Amaster)
[![Gem Version](https://badge.fury.io/rb/consul-templaterb.svg)](http://github.com/criteo/consul-templaterb/releases)
[![GEM Downloads](https://ruby-gem-downloads-badge.herokuapp.com/consul-templaterb?type=total&metric=true)](https://rubygems.org/gems/consul-templaterb)
[![GEM Downloads](https://img.shields.io/gem/dt/consul-templaterb.svg)](https://rubygems.org/gems/consul-templaterb)
[![License](https://img.shields.io/badge/license-ApacheV2-yellowgreen.svg)](#license)

The ruby GEM [consul-templaterb](https://rubygems.org/gems/consul-templaterb)
Expand Down
3 changes: 2 additions & 1 deletion TemplateAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,11 +631,12 @@ secret('secret/foo', [force_ttl: intInSecond])

## remote_resource

### as_json(url, default_value, [refresh_delay_secs: intInSecond])
### as_json(url, default_value, [refresh_delay_secs: intInSecond, default_value_on_error: bool])

Fetch json data from any url. This allows to create templates with consul/vault data mixed in with data coming from other services/api.
Polling interval can be controlled with `refresh_delay_secs` option.
Request method (`GET`, `POST`, ...) can be controlled with `request_method` option.
To return default value on the case of error, set `default_value_on_error` to true.

```erb
remote_resource.as_json('http://my-api.dev/fridge/list.json', [])
Expand Down
6 changes: 3 additions & 3 deletions lib/consul/async/consul_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ def initialize(endpoints_manager)
@endp_manager = endpoints_manager
end

def as_json(url, default_value, refresh_delay_secs: 10, **opts)
def as_json(url, default_value, refresh_delay_secs: 10, default_value_on_error: false, **opts)
conf = JSONConfiguration.new(url: url, min_duration: refresh_delay_secs, retry_on_non_diff: refresh_delay_secs, **opts)
endpoint_id = url + opts.hash.to_s
@endp_manager.create_if_missing(url, {}, endpoint_id: endpoint_id) do
if default_value.is_a?(Array)
ConsulTemplateJSONArray.new(JSONEndpoint.new(conf, url, default_value))
ConsulTemplateJSONArray.new(JSONEndpoint.new(conf, url, default_value, default_value_on_error: default_value_on_error))
else
ConsulTemplateJSONObject.new(JSONEndpoint.new(conf, url, default_value))
ConsulTemplateJSONObject.new(JSONEndpoint.new(conf, url, default_value, default_value_on_error: default_value_on_error))
end
end
end
Expand Down
16 changes: 14 additions & 2 deletions lib/consul/async/json_endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ def initialize(http, override_nil_response = nil)
# Endpoint (aka URL) of a remote API that might be called
class JSONEndpoint
attr_reader :conf, :url, :queue, :stats, :last_result, :enforce_json_200, :start_time, :default_value, :query_params
def initialize(conf, url, default_value, enforce_json_200 = true, query_params = {})
def initialize(conf, url, default_value, enforce_json_200: true, query_params: {}, default_value_on_error: false)
@conf = conf.create(url)
@default_value = default_value
@default_value_on_error = default_value_on_error
@url = url
@queue = EM::Queue.new
@s_callbacks = []
Expand Down Expand Up @@ -174,7 +175,7 @@ def _handle_error(http)
retry_in = _compute_retry_in([600, conf.retry_duration + 2**@consecutive_errors].min)
::Consul::Async::Debug.puts_error "[#{url}] - #{http.error} - Retry in #{retry_in}s #{stats.body_bytes_human}"
@consecutive_errors += 1
http_result = HttpResponse.new(http)
http_result = @default_value_on_error ? HttpResponse.new(http, @default_value.to_json) : HttpResponse.new(http)
EventMachine.add_timer(retry_in) do
yield
queue.push(Object.new)
Expand Down Expand Up @@ -203,6 +204,7 @@ def fetch
http = connection[:conn].send(request_method, build_request)
http.callback do
if enforce_json_200 && !(200..299).cover?(http.response_header.status) && http.response_header['Content-Type'] != 'application/json'
handle_default_on_error(http) if @default_value_on_error
_handle_error(http) do
warn "[RETRY][#{url}] (#{@consecutive_errors} errors)" if (@consecutive_errors % 10) == 1
end
Expand All @@ -227,6 +229,7 @@ def fetch
end

http.errback do
handle_default_on_error(http) if @default_value_on_error
unless @stopping
_handle_error(http) do
if (@consecutive_errors % 10) == 1
Expand All @@ -243,6 +246,15 @@ def fetch
end
queue.pop(&cb)
end

def handle_default_on_error(http)
::Consul::Async::Debug.puts_error "[#{url}] response status #{http.response_header.status}; using default value"
@consecutive_errors = 0
json_result = JSONResult.new(@default_value.to_json, false, HttpResponse.new(http, ''), stats, 10, fake: true)
@last_result = json_result
@ready = true
@s_callbacks.each { |c| c.call(json_result) }
end
end
end
end
2 changes: 1 addition & 1 deletion lib/consul/async/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Consul
module Async
VERSION = '1.33.2'.freeze
VERSION = '1.36.1'.freeze
end
end
2 changes: 1 addition & 1 deletion samples/consul-ui/decorators.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function usefullLinksGenerator(instance, serviceName, node_meta_info) {
/**
* createNodeDisplayElement resolves and displays the node name.
*/
function createNodeDisplayElement(nodeName, nodemeta) {
function createNodeDisplayElement(nodeName, _nodemeta, _instanceFqdn) {
return document.createTextNode(nodeName);
}

Expand Down
6 changes: 3 additions & 3 deletions samples/consul-ui/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function padDateUnit(x) {
}

function formatDate(date) {
return padDateUnit(date.getMonth() + 1) + "/" + padDateUnit(date.getDate()) + " " + padDateUnit(date.getHours()) + ':' + padDateUnit(date.getMinutes()) + ':' + padDateUnit(date.getSeconds());
return padDateUnit(date.getUTCMonth() + 1) + "/" + padDateUnit(date.getUTCDate()) + " " + padDateUnit(date.getUTCHours()) + ':' + padDateUnit(date.getUTCMinutes()) + ':' + padDateUnit(date.getUTCSeconds()) + " UTC";
}

function indexOfTimelineEvent(e) {
Expand Down Expand Up @@ -87,7 +87,7 @@ function serviceTitleGenerator(instance, serviceName, node_info) {
}

var nodemeta = (node_info != null) ? node_info.meta : null;
instanceLink.appendChild(createNodeDisplayElement(instance.name, nodemeta));
instanceLink.appendChild(createNodeDisplayElement(instance.name, nodemeta, instance?.sMeta?.fqdn));
instanceLink.appendChild(document.createTextNode(appendPort));

const nodeInfo = document.createElement('a');
Expand Down Expand Up @@ -493,4 +493,4 @@ function updateURL(arg, value) {
}

window.history.pushState({}, "", newUrl);
}
}
1 change: 1 addition & 0 deletions samples/consul-ui/timeline.json.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<%
require 'json'
require 'ostruct'
require 'set'
services_blacklist_raw = (ENV['EXCLUDE_SERVICES'] || 'lbl7.*,netsvc-probe.*,consul-probed.*').split(',')
services_blacklist = services_blacklist_raw.map { |v| Regexp.new(v) } # Compute the health of a Service
Expand Down
27 changes: 22 additions & 5 deletions samples/metrics.erb
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,31 @@ consul_members_count{serf="lan",status="<%= k %>"} <%= v %><%
metas: metas,
service_name: service_name,
state: {
'passing' => 0,
'warning' => 0,
'critical' => 0,
'passing' => {
instances: 0,
weight: 0
},
'warning' => {
instances: 0,
weight: 0
},
'critical' => {
instances: 0,
weight: 0
}
}
}
backends[key] = back
end
state = snode.status
back[:state][state] += 1
# Increment instance number.
back[:state][state][:instances] += 1
# Increment total weight regarding current node's state.
if state == 'passing'
back[:state][state][:weight] += snode['Service']['Weights']['Passing']
elsif state == 'warning'
back[:state][state][:weight] += snode['Service']['Weights']['Warning']
end
end
end
end
Expand Down Expand Up @@ -118,7 +134,8 @@ end
meta_string+=",#{k}=\"#{escape_meta(v)}\""
end
service_info[:state].each_pair do |state_name, state_count|
%>consul_service_count{service="<%= service_name %>",state="<%= state_name %>"<%= meta_string %>} <%= state_count %>
%>consul_service_count{service="<%= service_name %>",state="<%= state_name %>"<%= meta_string %>} <%= state_count[:instances] %>
consul_service_weight{service="<%= service_name %>",state="<%= state_name %>"<%= meta_string %>} <%= state_count[:weight] %>
<%
end
end
Expand Down
119 changes: 119 additions & 0 deletions spec/consul/async/json_endpoint_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# frozen_string_literal: true

require 'rspec'
require 'spec_helper'
require 'consul/async/json_endpoint'
require 'webmock/rspec'

RSpec.describe Consul::Async do
context 'default parameters' do
it 'response 200' do
mock_url = 'http://working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url)
default_value = '[]'

json_endpoint = nil
response_body = %w[a b]
stub_request(:get, mock_url)
.to_return(body: response_body.to_json, status: 200)
EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value)
end
expect(json_endpoint.ready?).to eq(true)
expect(json_endpoint.last_result.data).to eq(response_body.to_json)
end

it 'response 500' do
mock_url = 'http://error.working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url)
default_value = ''

json_endpoint = nil
stub_request(:get, mock_url)
.to_return(body: '', status: 500)
EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value)
end
expect(json_endpoint.ready?).to_not eq(true)
expect(json_endpoint.last_result.retry_in).to be_positive
end

it 'on timeout' do
mock_url = 'http://not.working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url)
default_value = ''

stub_request(:get, mock_url).to_timeout
json_endpoint = nil
EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value, enforce_json_200: true)
end
expect(json_endpoint.ready?).to_not eq(true)
expect(json_endpoint.last_result.retry_in).to be_positive
end
end

context 'when default_value_on_error on' do
let(:default_value_on_error) { true }
context 'when response 500' do
it 'return default value' do
mock_url = 'http://not.working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url, min_duration: 10, retry_on_non_diff: 20)
default_value = '["default", "value"]'

json_endpoint = nil
stub_request(:get, mock_url)
.to_return(body: '', status: 500)
EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value, default_value_on_error: default_value_on_error)
end
EM.run_block do
expect(json_endpoint.ready?).to eq(true)
expect(json_endpoint.last_result.data).to eq(default_value.to_json)
expect(json_endpoint.last_result.retry_in).to be_positive
end
end
end

context 'when address is not reachable' do
it 'return default value' do
mock_url = 'http://not.working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url, min_duration: 10, retry_on_non_diff: 20)
default_value = '["default", "value"]'

json_endpoint = nil
stub_request(:get, mock_url).to_timeout
EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value, default_value_on_error: default_value_on_error)
end
EM.run_block do
expect(json_endpoint.ready?).to eq(true)
expect(json_endpoint.last_result.data).to eq(default_value.to_json)
expect(json_endpoint.last_result.retry_in).to be_positive
end
end
end

context 'when response 200' do
it 'return value from endpoint' do
mock_url = 'http://working.url'
conf = Consul::Async::JSONConfiguration.new(url: mock_url, min_duration: 10, retry_on_non_diff: 20)
default_value = '["default", "value"]'
endpoint_body = '{"a": "b"}'

json_endpoint = nil
stub_request(:get, mock_url)
.to_return(body: endpoint_body, status: 200)

EM.run_block do
json_endpoint = Consul::Async::JSONEndpoint.new(conf, mock_url, default_value, default_value_on_error: default_value_on_error)
end
EM.run_block do
expect(json_endpoint.ready?).to eq(true)
expect(json_endpoint.last_result.data.to_json).to eq(endpoint_body.to_json)
expect(json_endpoint.last_result.retry_in).to be_positive
end
end
end
end
end

0 comments on commit 493c780

Please sign in to comment.