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 +4 -4
- data/lib/etcd-discovery/config.rb +3 -3
- data/lib/etcd-discovery/host.rb +28 -23
- data/lib/etcd-discovery/registrar.rb +37 -27
- data/lib/etcd-discovery/service.rb +25 -20
- data/lib/etcd-discovery.rb +5 -5
- data/spec/etcd-discovery/registrar_spec.rb +60 -19
- data/spec/spec_helper.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02edd48044abeca02169d432516a65ea48c8843b22942686c88f4c9761fd10f4
|
4
|
+
data.tar.gz: a3c0768508b0043521b98017d71442261e17fd63f7d4cd8c7c5f0e05ece91203
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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?
|
20
|
+
if cacert.nil? || !File.exist?(cacert)
|
21
21
|
raise InvalidSSLConfig, "cacert"
|
22
|
-
elsif ssl_key.nil?
|
22
|
+
elsif ssl_key.nil? || !File.exist?(ssl_key)
|
23
23
|
raise InvalidSSLConfig, "ssl_key"
|
24
|
-
elsif ssl_cert.nil?
|
24
|
+
elsif ssl_cert.nil? || !File.exist?(ssl_cert)
|
25
25
|
raise InvalidSSLConfig, "ssl_cert"
|
26
26
|
end
|
27
27
|
end
|
data/lib/etcd-discovery/host.rb
CHANGED
@@ -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?(
|
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[
|
20
|
-
attributes[
|
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
|
-
|
29
|
-
|
30
|
-
scheme = schemes.
|
31
|
-
|
32
|
-
|
33
|
-
if
|
34
|
-
URI("#{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[
|
43
|
-
return
|
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.
|
47
|
-
!a[
|
48
|
-
}
|
46
|
+
scheme = schemes.find { |s|
|
47
|
+
!a["private_ports"][s].nil?
|
48
|
+
}
|
49
49
|
|
50
|
-
if a[
|
51
|
-
URI("#{scheme}://#{a[
|
50
|
+
if a["user"].nil? || a["user"] == ""
|
51
|
+
URI("#{scheme}://#{a["private_hostname"]}:#{a["private_ports"][scheme]}")
|
52
52
|
else
|
53
|
-
URI("#{scheme}://#{a[
|
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[
|
59
|
-
@attributes[
|
58
|
+
@attributes["user"] = user
|
59
|
+
@attributes["password"] = password
|
60
60
|
end
|
61
61
|
|
62
62
|
def to_s
|
63
|
-
|
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
|
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
|
-
|
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
|
38
|
-
@password = @host.attributes[
|
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[
|
53
|
-
@watcher = Thread.new
|
54
|
-
@logger.warn "Watcher #{@service.attributes[
|
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,
|
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[
|
66
|
-
@password = value[
|
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
|
-
|
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 #{
|
98
|
-
client.delete(
|
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[
|
120
|
+
"/services/#{@service.attributes["name"]}/#{@host.attributes["uuid"]}"
|
111
121
|
end
|
112
122
|
|
113
123
|
def service_key
|
114
|
-
"/services_infos/#{@service.attributes[
|
124
|
+
"/services_infos/#{@service.attributes["name"]}"
|
115
125
|
end
|
116
126
|
|
117
127
|
def service_params
|
118
128
|
params = {
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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[
|
126
|
-
params[
|
127
|
-
|
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
|
-
|
24
|
+
new service.node
|
25
25
|
rescue Etcd::KeyNotFound
|
26
|
-
|
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[
|
33
|
+
node = client.get("/services/#{attributes["name"]}", recursive: true).node
|
34
34
|
rescue Etcd::KeyNotFound
|
35
|
-
raise ServiceNotFound, attributes[
|
35
|
+
raise ServiceNotFound, attributes["name"]
|
36
36
|
end
|
37
|
-
raise ServiceNotFound, attributes[
|
38
|
-
|
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 = [
|
46
|
-
|
47
|
-
return one.to_uri(schemes) unless a['public']
|
45
|
+
def to_uri(schemes = ["https", "http"])
|
46
|
+
schemes = Array(schemes)
|
48
47
|
|
49
|
-
schemes
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
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[
|
70
|
-
@attributes[
|
74
|
+
@attributes["user"] = user
|
75
|
+
@attributes["password"] = password
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
data/lib/etcd-discovery.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
## Require files from etcd-discovery
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
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
|
20
|
+
yield config if block
|
21
21
|
config.validate
|
22
22
|
end
|
23
23
|
|
@@ -1,16 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
RSpec.describe EtcdDiscovery::Registrar do
|
4
4
|
context "without a running client" do
|
5
|
-
describe
|
6
|
-
context
|
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
|
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
|
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(:
|
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
|
-
|
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
|
39
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
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
|
56
|
-
|
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
|
-
|
98
|
+
|
99
|
+
expect(subject.state).to eq :stopped
|
59
100
|
end
|
60
101
|
end
|
61
102
|
end
|
data/spec/spec_helper.rb
CHANGED
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.
|
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:
|
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:
|
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.
|
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: []
|