etcd-discovery 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17118bb040e02a6a81848eb96bc6eab9490a2ed3c3f1c9718073580add409ebd
4
- data.tar.gz: 8524c1cd5eee0558e12abadaa8478e95c418c92878328aa890d2ab2aad6a2781
3
+ metadata.gz: 3453532202e1683980506e000b3e4b6ec564f4d1da1aba5fe43e4ad6976b1658
4
+ data.tar.gz: 4cdbc6f516d3419476f5fab91a42ceb0b8611c925edfad3843a7af8e3a48b89e
5
5
  SHA512:
6
- metadata.gz: b1eb7a2fda116458395f0502ffcc44fd1e65083321b4eff7dc10ca3413b11d5ec5d0bac13df8b213228bc37482f09980f21267a70b04d871c1639c35b71f3fca
7
- data.tar.gz: cfd3a9cae7bf06ebe471e8269f8f82f925d0aa59bd07cebd935e8b48bdabc0f7effa961cecb206c1509415557882d0546aac4a089f775fa596dba6aee5ea6616
6
+ metadata.gz: 9474e53f69a98deb4b570c0f89c1811cb363bb2630430a02823833b61d72aafd01b084e5b7165b703483dd1a462d3eb365c7ec30ffec9745e741104c989dd3a1
7
+ data.tar.gz: 11427a5f9737d2110d3e82dc5febcc33e5573f6f4bd7f0bc89367b4e450c43a668abcda881dea6435c8a1422102925b7107ceeb8dd0f841a1eea5c168ca8435b
@@ -17,11 +17,11 @@ module EtcdDiscovery
17
17
 
18
18
  def validate
19
19
  if use_ssl
20
- if cacert.nil? or !File.exists? cacert
20
+ if cacert.nil? || !File.exist?(cacert)
21
21
  raise InvalidSSLConfig, "cacert"
22
- elsif ssl_key.nil? or !File.exists? ssl_key
22
+ elsif ssl_key.nil? || !File.exist?(ssl_key)
23
23
  raise InvalidSSLConfig, "ssl_key"
24
- elsif ssl_cert.nil? or !File.exists? ssl_cert
24
+ elsif ssl_cert.nil? || !File.exist?(ssl_cert)
25
25
  raise InvalidSSLConfig, "ssl_cert"
26
26
  end
27
27
  end
@@ -13,11 +13,11 @@ module EtcdDiscovery
13
13
  else
14
14
  raise TypeError, "requires a Etcd::Node or a Hash, not a #{arg.class}"
15
15
  end
16
- if !attributes.has_key?('name') or !attributes.has_key?('ports')
16
+ if !attributes.has_key?("name") || !attributes.has_key?("ports")
17
17
  raise InvalidHost, "attributes 'name' and 'ports' should be defined"
18
18
  end
19
- attributes['user'] = "" if attributes['user'].nil?
20
- attributes['password'] = "" if attributes['password'].nil?
19
+ attributes["user"] = "" if attributes["user"].nil?
20
+ attributes["password"] = "" if attributes["password"].nil?
21
21
  end
22
22
 
23
23
  def to_json
@@ -27,40 +27,40 @@ module EtcdDiscovery
27
27
  def to_uri(schemes = ["https", "http"])
28
28
  a = attributes # Shorten name
29
29
  schemes = [schemes] if !schemes.is_a?(Array)
30
- scheme = schemes.select{|s|
31
- !a['ports'][s].nil?
32
- }.first
33
- if a['user'].empty?
34
- URI("#{scheme}://#{a['name']}:#{a['ports'][scheme]}")
30
+ scheme = schemes.find { |s|
31
+ !a["ports"][s].nil?
32
+ }
33
+ if a["user"].empty?
34
+ URI("#{scheme}://#{a["name"]}:#{a["ports"][scheme]}")
35
35
  else
36
- URI("#{scheme}://#{a['user']}:#{a['password']}@#{a['name']}:#{a['ports'][scheme]}")
36
+ URI("#{scheme}://#{a["user"]}:#{a["password"]}@#{a["name"]}:#{a["ports"][scheme]}")
37
37
  end
38
38
  end
39
39
 
40
40
  def to_private_uri(schemes = ["https", "http"])
41
41
  a = attributes
42
- if a['private_hostname'].empty?
43
- return self.to_uri(schemes)
42
+ if a["private_hostname"].empty?
43
+ return to_uri(schemes)
44
44
  end
45
45
  schemes = [schemes] if !schemes.is_a?(Array)
46
- scheme = schemes.select{ |s|
47
- !a['private_ports'][s].nil?
48
- }.first
46
+ scheme = schemes.find { |s|
47
+ !a["private_ports"][s].nil?
48
+ }
49
49
 
50
- if a['user'].nil? || a['user'] == ""
51
- URI("#{scheme}://#{a['private_hostname']}:#{a['private_ports'][scheme]}")
50
+ if a["user"].nil? || a["user"] == ""
51
+ URI("#{scheme}://#{a["private_hostname"]}:#{a["private_ports"][scheme]}")
52
52
  else
53
- URI("#{scheme}://#{a['user']}:#{a['password']}@#{a['private_hostname']}:#{a['private_ports'][scheme]}")
53
+ URI("#{scheme}://#{a["user"]}:#{a["password"]}@#{a["private_hostname"]}:#{a["private_ports"][scheme]}")
54
54
  end
55
55
  end
56
56
 
57
57
  def set_credentials(user, password)
58
- @attributes['user'] = user
59
- @attributes['password'] = password
58
+ @attributes["user"] = user
59
+ @attributes["password"] = password
60
60
  end
61
61
 
62
62
  def to_s
63
- self.to_uri.to_s
63
+ to_uri.to_s
64
64
  end
65
65
  end
66
66
  end
@@ -1,4 +1,4 @@
1
- require 'securerandom'
1
+ require "securerandom"
2
2
 
3
3
  module EtcdDiscovery
4
4
  class InvalidStateError < StandardError
@@ -25,17 +25,27 @@ module EtcdDiscovery
25
25
  @logger = Logger.new($stdout)
26
26
 
27
27
  if host.is_a? Hash
28
- @host = Host.new host.merge('service_name' => service, 'uuid' => SecureRandom.uuid)
28
+ if host.has_key?("uuid")
29
+ host_uuid = "#{SecureRandom.uuid}-#{host["private_hostname"]}"
30
+ host = host.merge("uuid" => host_uuid)
31
+ end
32
+ @host = Host.new host
29
33
  elsif host.is_a? EtcdDiscovery::Host
34
+ if host.attributes.has_key?("uuid")
35
+ host.attributes["uuid"] = "#{SecureRandom.uuid}-#{host.attributes["private_hostname"]}"
36
+ end
30
37
  @host = host
31
38
  else
32
39
  raise TypeError, "host should be a Hash or a Etcd::Host, is a #{host.class}"
33
40
  end
34
41
 
42
+ # This attribute is later used when instantiating EtcdDiscovery::Service. We want this value to be the content of `service`, always.
43
+ @host.attributes["service_name"] = service
44
+
35
45
  @service = EtcdDiscovery::Service.new service_params
36
46
  @state = :new
37
- @user = @host.attributes['user']
38
- @password = @host.attributes['password']
47
+ @user = @host.attributes["user"]
48
+ @password = @host.attributes["password"]
39
49
  end
40
50
 
41
51
  def register
@@ -49,29 +59,31 @@ module EtcdDiscovery
49
59
  service_value = @service.to_json
50
60
 
51
61
  # Do not start credentials synchro if the service is not public or has no credentials
52
- if @service.attributes['public'] && (@service.attributes['user'].present? || @service.attributes['password'].present?)
53
- @watcher = Thread.new {
54
- @logger.warn "Watcher #{@service.attributes['name']} started"
62
+ if @service.attributes["public"] && (@service.attributes["user"].present? || @service.attributes["password"].present?)
63
+ @watcher = Thread.new do
64
+ @logger.warn "Watcher #{@service.attributes["name"]} started"
55
65
  index = 0
56
66
  while @state == :started
57
67
  begin
58
- resp = client.watch service_key, { index: index }
68
+ resp = client.watch service_key, index: index
59
69
  rescue => e
60
70
  @logger.warn "Fail to watch #{service_key}: #{e}, #{e.message}, #{e.class}"
71
+ index = 0
72
+ sleep(config.register_ttl / 2)
61
73
  next
62
74
  end
63
75
  value = JSON.parse resp.node.value
64
- @user = value['user']
65
- @password = value['password']
76
+ @user = value["user"]
77
+ @password = value["password"]
66
78
  @host.set_credentials user, password
67
79
  @service.set_credentials user, password
68
80
  index = resp.etcd_index
69
81
  end
70
- }
82
+ end
71
83
  end
72
84
 
73
85
  client.set(service_key, value: service_value)
74
- @thread = Thread.new {
86
+ @thread = Thread.new do
75
87
  @logger.warn "Register '#{@service}' started"
76
88
  while @state == :started
77
89
  value = @host.to_json
@@ -83,18 +95,17 @@ module EtcdDiscovery
83
95
  sleep config.register_renew
84
96
  end
85
97
  @logger.warn "Register '#{@service}' stopped"
86
- }
87
-
98
+ end
88
99
 
89
- return self
100
+ self
90
101
  end
91
102
 
92
103
  def stop
93
104
  raise InvalidStateError.new(@state, :started) if @state != :started
94
105
  @logger.debug "Set state to :stopped"
95
106
  @state = :stopped
96
- @logger.debug "Delete #{key}"
97
- client.delete(key)
107
+ @logger.debug "Delete #{host_key}"
108
+ client.delete(host_key)
98
109
  end
99
110
 
100
111
  def client
@@ -106,24 +117,24 @@ module EtcdDiscovery
106
117
  end
107
118
 
108
119
  def host_key
109
- "/services/#{@service.attributes['name']}/#{@host.attributes['uuid']}"
120
+ "/services/#{@service.attributes["name"]}/#{@host.attributes["uuid"]}"
110
121
  end
111
122
 
112
123
  def service_key
113
- "/services_infos/#{@service.attributes['name']}"
124
+ "/services_infos/#{@service.attributes["name"]}"
114
125
  end
115
126
 
116
127
  def service_params
117
128
  params = {
118
- 'name' => host.attributes['service_name'],
119
- 'critical' => host.attributes['critical'],
120
- 'user' => host.attributes['user'],
121
- 'password' => host.attributes['password'],
122
- 'public' => host.attributes['public']
129
+ "name" => @host.attributes["service_name"],
130
+ "critical" => @host.attributes["critical"],
131
+ "user" => @host.attributes["user"],
132
+ "password" => @host.attributes["password"],
133
+ "public" => @host.attributes["public"]
123
134
  }
124
- params['hostname'] = host.attributes['name']if params['public']
125
- params['ports'] = host.attributes['ports'] if params['public']
126
- return params
135
+ params["hostname"] = @host.attributes["name"] if params["public"]
136
+ params["ports"] = @host.attributes["ports"] if params["public"]
137
+ params
127
138
  end
128
139
  end
129
140
  end
@@ -21,39 +21,39 @@ module EtcdDiscovery
21
21
  client = EtcdDiscovery.config.client
22
22
  begin
23
23
  service = client.get("/services_infos/#{service}")
24
- return new service.node
24
+ new service.node
25
25
  rescue Etcd::KeyNotFound
26
- return new('name' => service)
26
+ new("name" => service)
27
27
  end
28
28
  end
29
29
 
30
30
  def all
31
31
  client = EtcdDiscovery.config.client
32
32
  begin
33
- node = client.get("/services/#{attributes['name']}", recursive: true).node
33
+ node = client.get("/services/#{attributes["name"]}", recursive: true).node
34
34
  rescue Etcd::KeyNotFound
35
- raise ServiceNotFound, attributes['name']
35
+ raise ServiceNotFound, attributes["name"]
36
36
  end
37
- raise ServiceNotFound, attributes['name'] if node.children.empty?
38
- return node.children.map { |c| Host.new(c) }
37
+ raise ServiceNotFound, attributes["name"] if node.children.empty?
38
+ node.children.map { |c| Host.new(c) }
39
39
  end
40
40
 
41
41
  def one
42
42
  all.sample
43
43
  end
44
44
 
45
- def to_uri(schemes = ['https', 'http'])
45
+ def to_uri(schemes = ["https", "http"])
46
46
  a = attributes
47
- return one.to_uri(schemes) unless a['public']
47
+ return one.to_uri(schemes) unless a["public"]
48
48
 
49
49
  schemes = [schemes] if !schemes.is_a?(Array)
50
50
  scheme = schemes.reject do |s|
51
- a['ports'][s].nil?
51
+ a["ports"][s].nil?
52
52
  end.first
53
- if a['user'].nil? || a['user'] == ''
54
- URI("#{scheme}://#{a['hostname']}:#{a['ports'][scheme]}")
53
+ if a["user"].nil? || a["user"] == ""
54
+ URI("#{scheme}://#{a["hostname"]}:#{a["ports"][scheme]}")
55
55
  else
56
- URI("#{scheme}://#{a['user']}:#{a['password']}@#{a['hostname']}:#{a['ports'][scheme]}")
56
+ URI("#{scheme}://#{a["user"]}:#{a["password"]}@#{a["hostname"]}:#{a["ports"][scheme]}")
57
57
  end
58
58
  end
59
59
 
@@ -66,8 +66,8 @@ module EtcdDiscovery
66
66
  end
67
67
 
68
68
  def set_credentials(user, password)
69
- @attributes['user'] = user
70
- @attributes['password'] = password
69
+ @attributes["user"] = user
70
+ @attributes["password"] = password
71
71
  end
72
72
  end
73
73
  end
@@ -1,11 +1,11 @@
1
1
  ## Require files from etcd-discovery
2
2
 
3
- require 'etcd'
4
- require 'json'
5
- require 'logger'
3
+ require "etcd"
4
+ require "json"
5
+ require "logger"
6
6
 
7
7
  dir = File.join File.dirname(__FILE__), "etcd-discovery"
8
- Dir["#{dir}/*.rb"].each do |file|
8
+ Dir["#{dir}/*.rb"].sort.each do |file|
9
9
  require file
10
10
  end
11
11
 
@@ -17,7 +17,7 @@ module EtcdDiscovery
17
17
  end
18
18
 
19
19
  def self.configure(&block)
20
- yield config if block_given?
20
+ yield config if block
21
21
  config.validate
22
22
  end
23
23
 
@@ -1,16 +1,16 @@
1
- require 'spec_helper.rb'
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe EtcdDiscovery::Registrar do
4
4
  context "without a running client" do
5
- describe '.new' do
6
- context 'with a Hash' do
5
+ describe ".new" do
6
+ context "with a Hash" do
7
7
  subject { EtcdDiscovery::Registrar.new "service", {"name" => "example.com", "ports" => {"http" => 80}} }
8
8
  it "should transform the has in Host" do
9
9
  expect(subject.host.attributes["name"]).to eq "example.com"
10
10
  end
11
11
  end
12
12
 
13
- context 'with a EtcdDiscovery::Host' do
13
+ context "with a EtcdDiscovery::Host" do
14
14
  subject { EtcdDiscovery::Registrar.new "service", EtcdDiscovery::Host.new({"name" => "example.com", "ports" => {"http" => 80}}) }
15
15
  it "should defines the state to new" do
16
16
  expect(subject.state).to eq :new
@@ -22,40 +22,81 @@ RSpec.describe EtcdDiscovery::Registrar do
22
22
  context "with an unregistered client" do
23
23
  subject { EtcdDiscovery::Registrar.new "service", EtcdDiscovery::Host.new({"name" => "example.com", "ports" => {"http" => 80}}) }
24
24
 
25
- describe '#stop' do
25
+ describe "#stop" do
26
26
  it "should raise an exception if service not registered" do
27
- expect{ subject.stop }.to raise_exception EtcdDiscovery::InvalidStateError
27
+ expect { subject.stop }.to raise_exception EtcdDiscovery::InvalidStateError
28
28
  end
29
29
  end
30
30
 
31
31
  its(:client) { is_expected.to eq EtcdDiscovery.config.client }
32
- its(:key) { is_expected.to eq "/services/service/example.com" }
32
+ its(:host_key) { is_expected.to start_with "/services/service/" }
33
33
  end
34
34
 
35
35
  context "with a registered client" do
36
- subject { EtcdDiscovery::Registrar.new("service", EtcdDiscovery::Host.new({"name" => "example.com", "ports" => {"http" => 80}})) }
36
+ let(:info) do
37
+ {
38
+ "name" => "service",
39
+ "critical" => true,
40
+ "hostname" => "public.scalingo.test",
41
+ "user" => "user",
42
+ "password" => "secret",
43
+ "ports" => {"https" => "5000", "http" => 80},
44
+ "public" => true
45
+ }
46
+ end
47
+ let(:host) do
48
+ {
49
+ "name" => info["hostname"],
50
+ "service_name" => info["name"],
51
+ "ports" => info["ports"],
52
+ "user" => info["user"],
53
+ "password" => info["secret"],
54
+ "public" => info["public"],
55
+ "private_ports" => info["ports"],
56
+ "critical" => info["critical"],
57
+ "private_hostname" => "private01.scalingo.test",
58
+ "uuid" => "my-uuid"
59
+ }
60
+ end
61
+ subject do
62
+ EtcdDiscovery::Registrar.new(
63
+ info["name"],
64
+ EtcdDiscovery::Host.new({"name" => info["hostname"], "ports" => info["ports"]})
65
+ )
66
+ end
37
67
 
38
- before(:each) do subject.register ; sleep 0.2 ; end
39
- after(:each) do subject.stop if subject.state == :started ; end
68
+ before(:each) do
69
+ mock_service_info(info["name"], info, true)
70
+ mock_hosts(info["name"], host, 1)
71
+ mock_set_service_key(info["name"])
72
+ mock_set_host_key(info["name"], subject.host.attributes["uuid"])
40
73
 
41
- describe '#register' do
42
- its(:thread) { is_expected.not_to eq nil }
43
- it "s thread should be alived" do
44
- expect(subject.thread.alive?).to eq true
45
- end
74
+ subject.register
75
+ end
76
+
77
+ after(:each) do
78
+ mock_delete_host_key(info["name"], subject.host.attributes["uuid"])
79
+ subject.stop if subject.state == :started
46
80
  end
47
81
 
48
82
  describe "#stop" do
49
83
  it "should stop its registering thread" do
84
+ mock_delete_host_key(info["name"], subject.host.attributes["uuid"])
85
+
50
86
  subject.stop
51
- sleep 0.2
87
+ # Wait for the register thread to detect the change of @state and stop.
88
+ sleep 0.3
52
89
  expect(subject.thread.alive?).to eq false
53
90
  end
54
91
 
55
- it "should remote the etcd key of the service" do
56
- expect(EtcdDiscovery.get("service").length).to eq 1
92
+ it "should set the service state to stopped" do
93
+ mock_service_info(info["name"], info)
94
+ mock_delete_host_key(info["name"], subject.host.attributes["uuid"])
95
+ expect(EtcdDiscovery.get(info["name"]).all.length).to eq 1
96
+
57
97
  subject.stop
58
- expect { EtcdDiscovery.get("service") }.to raise_exception EtcdDiscovery::ServiceNotFound
98
+
99
+ expect(subject.state).to eq :stopped
59
100
  end
60
101
  end
61
102
  end
data/spec/spec_helper.rb CHANGED
@@ -6,7 +6,7 @@ Bundler.require(:default, :development, :test)
6
6
 
7
7
  EtcdDiscovery.config.register_renew = 0.1
8
8
 
9
- require 'webmock/rspec'
9
+ require "webmock/rspec"
10
10
  WebMock.disable_net_connect!(allow_localhost: false)
11
11
 
12
12
  require_relative "./etcd_helper"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etcd-discovery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Léo Unbekandt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
11
+ date: 2023-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: etcd
@@ -51,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 2.7.0
55
55
  required_rubygems_version: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  version: '0'
60
60
  requirements: []
61
- rubygems_version: 3.0.3
61
+ rubygems_version: 3.3.5
62
62
  signing_key:
63
63
  specification_version: 4
64
64
  summary: Service discovery based on etcd