etcd-discovery 1.0.7 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2790e1221f8d46a73e327158ea3db9e513f53722e5e4523f511cf68a574c31ba
4
- data.tar.gz: 8ce9db41cf12d657f140d45af8cd6c3da27392e9830df7dcb07c2b1715472fcc
3
+ metadata.gz: 02edd48044abeca02169d432516a65ea48c8843b22942686c88f4c9761fd10f4
4
+ data.tar.gz: a3c0768508b0043521b98017d71442261e17fd63f7d4cd8c7c5f0e05ece91203
5
5
  SHA512:
6
- metadata.gz: 3aefec153e1278701479ea097526aeaffd3bc636734d66fa2ca6e4a7ebe98da3f87b251d5b1fa28a51adbed0dc4b65be61f2ef3783971b3836fb77204eafcd4f
7
- data.tar.gz: 9e6753a7ef169a4e9bb5de3f1ce0a931151a83dab71cb39d4abfc54a4678fe023fd535819c3880ca95d771684cc729bef169a63e5cde500bf489e2eb4bcddb4c
6
+ metadata.gz: 573344b8a4f8de0222d0da6e38ef5dddfe8f67d55585c26d66edd44345dbd3adb27f25c4e911fe15bba2f3be711d8db5d4374dc3cdd189ebdc8495f8c0cf51cd
7
+ data.tar.gz: 7620dff391769b07332e7e8608098b734f4285f794ee670dafe1e4dd45607c21d3cc1c5f2ec54b4ea9fd4eaa9c8478ddb7285a8156653b985c86ac49f68cd7e3
@@ -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
@@ -25,42 +25,47 @@ module EtcdDiscovery
25
25
  end
26
26
 
27
27
  def to_uri(schemes = ["https", "http"])
28
- a = attributes # Shorten name
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]}")
35
- else
36
- URI("#{scheme}://#{a['user']}:#{a['password']}@#{a['name']}:#{a['ports'][scheme]}")
28
+ schemes = Array(schemes)
29
+
30
+ scheme = schemes.find { |s| attributes["ports"][s] }
31
+ raise "No valid scheme found" unless scheme
32
+
33
+ if attributes["user"].nil? || attributes["user"].empty?
34
+ return URI("#{scheme}://#{attributes["name"]}:#{attributes["ports"][scheme]}")
37
35
  end
36
+
37
+ URI("#{scheme}://#{attributes["user"]}:#{attributes["password"]}@#{attributes["name"]}:#{attributes["ports"][scheme]}")
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
+ uri = to_uri
64
+ if uri.userinfo
65
+ user, _password = uri.userinfo.split(":", 2)
66
+ uri.userinfo = "#{user}:REDACTED"
67
+ end
68
+ uri.to_s
64
69
  end
65
70
  end
66
71
  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,30 +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}"
61
71
  index = 0
72
+ sleep(config.register_ttl / 2)
62
73
  next
63
74
  end
64
75
  value = JSON.parse resp.node.value
65
- @user = value['user']
66
- @password = value['password']
76
+ @user = value["user"]
77
+ @password = value["password"]
67
78
  @host.set_credentials user, password
68
79
  @service.set_credentials user, password
69
80
  index = resp.etcd_index
70
81
  end
71
- }
82
+ end
72
83
  end
73
84
 
74
85
  client.set(service_key, value: service_value)
75
- @thread = Thread.new {
86
+ @thread = Thread.new do
76
87
  @logger.warn "Register '#{@service}' started"
77
88
  while @state == :started
78
89
  value = @host.to_json
@@ -84,18 +95,17 @@ module EtcdDiscovery
84
95
  sleep config.register_renew
85
96
  end
86
97
  @logger.warn "Register '#{@service}' stopped"
87
- }
88
-
98
+ end
89
99
 
90
- return self
100
+ self
91
101
  end
92
102
 
93
103
  def stop
94
104
  raise InvalidStateError.new(@state, :started) if @state != :started
95
105
  @logger.debug "Set state to :stopped"
96
106
  @state = :stopped
97
- @logger.debug "Delete #{key}"
98
- client.delete(key)
107
+ @logger.debug "Delete #{host_key}"
108
+ client.delete(host_key)
99
109
  end
100
110
 
101
111
  def client
@@ -107,24 +117,24 @@ module EtcdDiscovery
107
117
  end
108
118
 
109
119
  def host_key
110
- "/services/#{@service.attributes['name']}/#{@host.attributes['uuid']}"
120
+ "/services/#{@service.attributes["name"]}/#{@host.attributes["uuid"]}"
111
121
  end
112
122
 
113
123
  def service_key
114
- "/services_infos/#{@service.attributes['name']}"
124
+ "/services_infos/#{@service.attributes["name"]}"
115
125
  end
116
126
 
117
127
  def service_params
118
128
  params = {
119
- 'name' => host.attributes['service_name'],
120
- 'critical' => host.attributes['critical'],
121
- 'user' => host.attributes['user'],
122
- 'password' => host.attributes['password'],
123
- '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"]
124
134
  }
125
- params['hostname'] = host.attributes['name']if params['public']
126
- params['ports'] = host.attributes['ports'] if params['public']
127
- return params
135
+ params["hostname"] = @host.attributes["name"] if params["public"]
136
+ params["ports"] = @host.attributes["ports"] if params["public"]
137
+ params
128
138
  end
129
139
  end
130
140
  end
@@ -21,44 +21,49 @@ 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'])
46
- a = attributes
47
- return one.to_uri(schemes) unless a['public']
45
+ def to_uri(schemes = ["https", "http"])
46
+ schemes = Array(schemes)
48
47
 
49
- schemes = [schemes] if !schemes.is_a?(Array)
50
- scheme = schemes.reject do |s|
51
- a['ports'][s].nil?
52
- end.first
53
- if a['user'].nil? || a['user'] == ''
54
- URI("#{scheme}://#{a['hostname']}:#{a['ports'][scheme]}")
55
- else
56
- URI("#{scheme}://#{a['user']}:#{a['password']}@#{a['hostname']}:#{a['ports'][scheme]}")
48
+ return one.to_uri(schemes) unless attributes["public"]
49
+
50
+ scheme = schemes.find { |s| attributes["ports"][s] }
51
+ raise "No valid scheme found" unless scheme
52
+
53
+ if attributes["user"].nil? || attributes["user"].empty?
54
+ return URI("#{scheme}://#{attributes["hostname"]}:#{attributes["ports"][scheme]}")
57
55
  end
56
+
57
+ URI("#{scheme}://#{attributes["user"]}:#{attributes["password"]}@#{attributes["hostname"]}:#{attributes["ports"][scheme]}")
58
58
  end
59
59
 
60
60
  def to_s
61
- to_uri.to_s
61
+ uri = to_uri
62
+ if uri.userinfo
63
+ user, _password = uri.userinfo.split(":", 2)
64
+ uri.userinfo = "#{user}:REDACTED"
65
+ end
66
+ uri.to_s
62
67
  end
63
68
 
64
69
  def to_json
@@ -66,8 +71,8 @@ module EtcdDiscovery
66
71
  end
67
72
 
68
73
  def set_credentials(user, password)
69
- @attributes['user'] = user
70
- @attributes['password'] = password
74
+ @attributes["user"] = user
75
+ @attributes["password"] = password
71
76
  end
72
77
  end
73
78
  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.7
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Léo Unbekandt
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
11
+ date: 2024-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: etcd
@@ -43,7 +43,7 @@ homepage: http://github.com/Scalingo/etcd-discovery-ruby
43
43
  licenses:
44
44
  - BSD
45
45
  metadata: {}
46
- post_install_message:
46
+ post_install_message:
47
47
  rdoc_options: []
48
48
  require_paths:
49
49
  - lib
@@ -51,15 +51,15 @@ 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
62
- signing_key:
61
+ rubygems_version: 3.3.26
62
+ signing_key:
63
63
  specification_version: 4
64
64
  summary: Service discovery based on etcd
65
65
  test_files: []