port-authority 0.3.11 → 0.4.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 +8 -8
- data/bin/pa-manager +1 -1
- data/lib/port-authority/manager/app.rb +37 -40
- data/lib/port-authority/manager/init.rb +11 -6
- data/lib/port-authority/manager/threads/icmp.rb +17 -9
- data/lib/port-authority/manager/threads/swarm.rb +30 -8
- data/lib/port-authority/util/config.rb +20 -19
- data/lib/port-authority/util/etcd.rb +13 -17
- data/lib/port-authority/util/helpers.rb +1 -1
- data/lib/port-authority/util/loadbalancer.rb +16 -9
- data/lib/port-authority/util/logger.rb +38 -23
- data/lib/port-authority/util/vip.rb +7 -22
- data/lib/port-authority.rb +21 -0
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NjQxZGVlNDk3YzA1OTNkY2ZiYzM1ZjY3MmJkZGU0OTVjZGM1Y2JlMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YWMwYTk0NjYzYjI2OTAyYTc0OWQwOGJhZWY5MjE5M2U4MWZhMDMzMg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NGRkYzljOGQ2ZmJkZjRlYTA1YjliNDZjNjhiYTcxYjNjNTNlY2E2MzFmYWQ4
|
10
|
+
NjRjYzNmZDU5ZWFlZDNlMTczY2I1YmFjMWI5MDdiZDhmNDVkNTc1NGRjY2M5
|
11
|
+
YzIyY2JjZmJiM2I5N2U5ODBjNDBkZmM1MmZjZDk0MjgwYjlhYWI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTY4MWVkODk1NzE5ZjU3ZTNlYTYwNzA0MjkxZDg3MDkxNjhjN2Y0YWQ4MGY3
|
14
|
+
MzNiNDM2ZWM0MjFjN2FhMmNmYTJhODkxMGFmMmE5ZmZjMzVkOTk0MDFiNDNh
|
15
|
+
ZGFlYzA3MGQ4MjMwOWUwNDYxNmZiOGQyZDEwMDk3NGQwMTI5OTg=
|
data/bin/pa-manager
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
# rubocop:disable MethodLength, CyclomaticComplexity, Metrics/BlockNesting, Metrics/LineLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
1
2
|
require 'ipaddr'
|
3
|
+
require 'port-authority'
|
2
4
|
require 'port-authority/util/vip'
|
3
5
|
require 'port-authority/util/etcd'
|
4
6
|
require 'port-authority/util/loadbalancer'
|
@@ -8,8 +10,10 @@ require 'port-authority/manager/threads/swarm'
|
|
8
10
|
|
9
11
|
module PortAuthority
|
10
12
|
module Manager
|
13
|
+
##
|
14
|
+
# Port Authority Manager - manages floating VIP and lb placement
|
15
|
+
#
|
11
16
|
class App < PortAuthority::Manager::Init
|
12
|
-
|
13
17
|
include PortAuthority::Util::Etcd
|
14
18
|
include PortAuthority::Util::Vip
|
15
19
|
include PortAuthority::Util::LoadBalancer
|
@@ -18,25 +22,17 @@ module PortAuthority
|
|
18
22
|
def run
|
19
23
|
# exit if not root
|
20
24
|
if Process.euid != 0
|
21
|
-
|
25
|
+
alert 'must run under root user!'
|
22
26
|
exit! 1
|
23
27
|
end
|
24
28
|
|
25
|
-
|
26
|
-
setup 'pa-manager'
|
29
|
+
Signal.trap('USR1') { @lb_update_hook = true }
|
27
30
|
|
28
31
|
# prepare semaphores
|
29
|
-
@semaphore
|
30
|
-
log: Mutex.new,
|
31
|
-
swarm: Mutex.new,
|
32
|
-
icmp: Mutex.new
|
33
|
-
}
|
32
|
+
@semaphore.merge!(swarm: Mutex.new, icmp: Mutex.new)
|
34
33
|
|
35
34
|
# prepare threads
|
36
|
-
@thread = {
|
37
|
-
icmp: thread_icmp,
|
38
|
-
swarm: thread_swarm
|
39
|
-
}
|
35
|
+
@thread = {icmp: thread_icmp,swarm: thread_swarm}
|
40
36
|
|
41
37
|
# prepare status vars
|
42
38
|
@status_swarm = false
|
@@ -49,18 +45,24 @@ module PortAuthority
|
|
49
45
|
lb_docker_setup! || @exit = true
|
50
46
|
|
51
47
|
# prepare container with load-balancer
|
52
|
-
lb_create
|
48
|
+
lb_create!
|
53
49
|
|
54
50
|
# wait for threads to make sure they gather something
|
55
51
|
debug 'waiting for threads to gather something...'
|
56
52
|
sleep @config[:vip][:interval]
|
57
53
|
first_cycle = true
|
54
|
+
status_time = Time.now.to_i - 60
|
58
55
|
|
59
56
|
# main loop
|
60
57
|
until @exit
|
61
58
|
# initialize local state vars on first iteration
|
62
59
|
status_swarm = status_icmp = false if first_cycle
|
63
60
|
|
61
|
+
if @lb_update_hook
|
62
|
+
notice 'updating LB image'
|
63
|
+
lb_update!
|
64
|
+
end
|
65
|
+
|
64
66
|
# iteration interval
|
65
67
|
sleep @config[:vip][:interval]
|
66
68
|
|
@@ -70,10 +72,11 @@ module PortAuthority
|
|
70
72
|
|
71
73
|
# the logic (should be self-explanatory ;))
|
72
74
|
if status_swarm
|
75
|
+
debug 'i am the leader'
|
73
76
|
if got_vip?
|
74
|
-
debug '
|
77
|
+
debug 'got VIP, that is OK'
|
75
78
|
else
|
76
|
-
info '
|
79
|
+
info 'no VIP here, checking whether it is free'
|
77
80
|
if status_icmp
|
78
81
|
info 'VIP is still up! (ICMP)'
|
79
82
|
# FIXME: notify by sensu client socket
|
@@ -89,68 +92,62 @@ module PortAuthority
|
|
89
92
|
# info 'updating other hosts about change'
|
90
93
|
# vip_update_arp!
|
91
94
|
# end
|
95
|
+
notice 'VIP is free :) assigning'
|
96
|
+
vip_handle! status_swarm
|
97
|
+
notice 'updating other hosts about change'
|
98
|
+
vip_update_arp!
|
92
99
|
end
|
93
|
-
info 'VIP is free :) assigning'
|
94
|
-
vip_handle! status_swarm
|
95
|
-
info 'updating other hosts about change'
|
96
|
-
vip_update_arp!
|
97
100
|
end
|
98
101
|
if lb_up?
|
99
|
-
debug '
|
102
|
+
debug 'load-balancer is up, that is OK'
|
100
103
|
else
|
101
|
-
|
104
|
+
notice 'load-balancer is down, starting'
|
102
105
|
lb_start!
|
103
106
|
end
|
104
107
|
else
|
108
|
+
debug 'i am not the leader'
|
105
109
|
if got_vip?
|
106
|
-
|
110
|
+
notice 'i got VIP and should not, removing'
|
107
111
|
vip_handle! status_swarm
|
108
|
-
|
112
|
+
notice 'updating other hosts about change'
|
109
113
|
vip_update_arp!
|
110
114
|
else
|
111
|
-
debug '
|
115
|
+
debug 'no VIP here, that is OK'
|
112
116
|
end
|
113
117
|
if lb_up?
|
114
|
-
|
118
|
+
notice 'load-balancer is up, stopping'
|
115
119
|
lb_stop!
|
116
120
|
else
|
117
|
-
debug '
|
121
|
+
debug 'load-balancer is down, that is OK'
|
118
122
|
end
|
119
123
|
end
|
120
124
|
|
121
|
-
|
125
|
+
if status_time + 60 <= Time.now.to_i
|
126
|
+
info "STATUS_REPORT { leader: '#{status_swarm ? 'yes' : 'no'}', vip: '#{got_vip? ? 'yes' : 'no'}/#{status_icmp ? 'up' : 'down'}', lb: '#{lb_up? ? 'yes' : 'no'}' }"
|
127
|
+
status_time = Time.now.to_i
|
128
|
+
end
|
122
129
|
|
123
|
-
# short report on first cycle
|
124
|
-
info "i #{status_swarm ? 'AM' : 'am NOT'} the leader"
|
125
|
-
info "i #{got_vip? ? 'DO' : 'do NOT'} have the VIP"
|
126
|
-
info "i #{status_icmp ? 'CAN' : 'CANNOT'} see the VIP"
|
127
|
-
info "i #{lb_up? ? 'AM' : 'am NOT'} running the LB"
|
128
|
-
first_cycle = false
|
129
130
|
end
|
130
131
|
|
131
132
|
# this is triggerred on exit
|
132
|
-
info 'SIGTERM received'
|
133
|
-
info 'waiting for threads to finish...'
|
134
133
|
@thread.each_value(&:join)
|
135
134
|
|
136
135
|
# remove VIP on shutdown
|
137
136
|
if got_vip?
|
138
|
-
|
137
|
+
notice 'removing VIP'
|
139
138
|
vip_handle! false
|
140
139
|
vip_update_arp!
|
141
140
|
end
|
142
141
|
|
143
142
|
# stop LB on shutdown
|
144
143
|
if lb_up?
|
145
|
-
|
144
|
+
notice 'stopping load-balancer'
|
146
145
|
lb_stop!
|
147
146
|
end
|
148
147
|
|
149
148
|
info 'exiting...'
|
150
149
|
exit 0
|
151
150
|
end
|
152
|
-
|
153
|
-
|
154
151
|
end
|
155
152
|
end
|
156
153
|
end
|
@@ -8,28 +8,33 @@ require 'port-authority/util/helpers'
|
|
8
8
|
module PortAuthority
|
9
9
|
module Manager
|
10
10
|
class Init
|
11
|
-
|
12
11
|
include PortAuthority::Util::Config
|
13
12
|
include PortAuthority::Util::Logger
|
14
13
|
include PortAuthority::Util::Helpers
|
15
14
|
|
16
|
-
def initialize
|
17
|
-
@config = { debug: false }
|
15
|
+
def initialize(proc_name='dummy')
|
18
16
|
@config = config
|
19
17
|
@exit = false
|
20
|
-
@
|
18
|
+
@semaphore = { log: Mutex.new }
|
19
|
+
Thread.current[:name] = 'main'
|
20
|
+
syslog_init proc_name if @config[:syslog]
|
21
|
+
setup proc_name
|
22
|
+
info 'starting main thread'
|
23
|
+
debug 'setting signal handling'
|
24
|
+
@exit_sigs = %w(INT TERM)
|
21
25
|
@exit_sigs.each { |sig| Signal.trap(sig) { @exit = true } }
|
22
|
-
Signal.trap('
|
23
|
-
Signal.trap('USR2') { @config[:debug] = true }
|
26
|
+
Signal.trap('USR2') { @config[:debug] = !@config[:debug] }
|
24
27
|
Signal.trap('HUP') { @config = config }
|
25
28
|
end
|
26
29
|
|
27
30
|
def setup(proc_name, nice = -20)
|
31
|
+
debug 'setting process name'
|
28
32
|
if RUBY_VERSION >= '2.1'
|
29
33
|
Process.setproctitle(proc_name)
|
30
34
|
else
|
31
35
|
$0 = proc_name
|
32
36
|
end
|
37
|
+
debug 'setting process title'
|
33
38
|
Process.setpriority(Process::PRIO_PROCESS, 0, nice)
|
34
39
|
# FIXME: Process.daemon ...
|
35
40
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# rubocop:disable Metrics/MethodLength
|
1
2
|
require 'net/ping'
|
2
3
|
|
3
4
|
module PortAuthority
|
@@ -5,16 +6,23 @@ module PortAuthority
|
|
5
6
|
module Threads
|
6
7
|
def thread_icmp
|
7
8
|
Thread.new do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
Thread.current[:name] = 'icmp'
|
10
|
+
begin
|
11
|
+
info 'starting ICMP thread...'
|
12
|
+
icmp = Net::Ping::ICMP.new(@config[:vip][:ip])
|
13
|
+
until @exit
|
14
|
+
debug 'checking state by ICMP echo'
|
15
|
+
status = vip_alive? icmp
|
16
|
+
@semaphore[:icmp].synchronize { @status_icmp = status }
|
17
|
+
debug "VIP is #{status ? 'alive' : 'down'} according to ICMP"
|
18
|
+
sleep @config[:icmp][:interval]
|
19
|
+
end
|
20
|
+
info 'ending ICMP thread...'
|
21
|
+
rescue StandardError => e
|
22
|
+
alert "#{e.class}: #{e.message}"
|
23
|
+
alert e.backtrace
|
24
|
+
@exit = true
|
16
25
|
end
|
17
|
-
info 'ending ICMP thread...'
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
@@ -1,18 +1,40 @@
|
|
1
|
+
# rubocop:disable Metrics/MethodLength
|
1
2
|
module PortAuthority
|
2
3
|
module Manager
|
3
4
|
module Threads
|
4
5
|
def thread_swarm
|
5
6
|
Thread.new do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
Thread.current[:name] = 'swarm'
|
8
|
+
info 'starting swarm thread...'
|
9
|
+
begin
|
10
|
+
etcd = etcd_connect!
|
11
|
+
until @exit
|
12
|
+
debug 'checking ETCD state'
|
13
|
+
etcd_healthy? etcd
|
14
|
+
debug 'checking swarm state'
|
15
|
+
status = am_i_leader? etcd
|
16
|
+
@semaphore[:swarm].synchronize { @status_swarm = status }
|
17
|
+
debug "i am #{status ? 'the leader' : 'not the leader' }"
|
18
|
+
sleep @config[:etcd][:interval]
|
19
|
+
end
|
20
|
+
info 'ending swarm thread...'
|
21
|
+
rescue PortAuthority::Errors::ETCDIsSick => e
|
22
|
+
notice "#{e.class}: #{e.message}"
|
23
|
+
notice "connection: " + e.etcd.to_s
|
24
|
+
@semaphore[:swarm].synchronize { @status_swarm = false }
|
13
25
|
sleep @config[:etcd][:interval]
|
26
|
+
retry unless @exit
|
27
|
+
rescue PortAuthority::Errors::ETCDConnectFailed => e
|
28
|
+
err "#{e.class}: #{e.message}"
|
29
|
+
err "connection: " + e.etcd.to_s
|
30
|
+
@semaphore[:swarm].synchronize { @status_swarm = false }
|
31
|
+
sleep @config[:etcd][:interval]
|
32
|
+
retry unless @exit
|
33
|
+
rescue StandardError => e
|
34
|
+
alert e.message
|
35
|
+
alert e.backtrace.to_s
|
36
|
+
@exit = true
|
14
37
|
end
|
15
|
-
info 'ending swarm thread...'
|
16
38
|
end
|
17
39
|
end
|
18
40
|
end
|
@@ -4,19 +4,34 @@ require 'etcd-tools'
|
|
4
4
|
module PortAuthority
|
5
5
|
module Util
|
6
6
|
module Config
|
7
|
+
|
8
|
+
def config
|
9
|
+
cfg = default_config
|
10
|
+
if File.exist? '/etc/port-authority.yaml'
|
11
|
+
cfg = cfg.deep_merge YAML.load_file('/etc/port-authority.yaml')
|
12
|
+
puts 'loaded config from /etc/port-authority.yaml'
|
13
|
+
elsif File.exist? './port-authority.yaml'
|
14
|
+
cfg = cfg.deep_merge YAML.load_file('./port-authority.yaml')
|
15
|
+
puts 'loaded config from ./port-authority.yaml'
|
16
|
+
else
|
17
|
+
puts 'no config file loaded, using defaults'
|
18
|
+
end
|
19
|
+
cfg
|
20
|
+
end
|
21
|
+
|
7
22
|
private
|
8
23
|
|
9
24
|
def default_config
|
10
25
|
{ debug: false,
|
11
26
|
syslog: false,
|
12
27
|
etcd: {
|
13
|
-
|
14
|
-
interval:
|
15
|
-
timeout:
|
28
|
+
endpoints: ['http://localhost:2379'],
|
29
|
+
interval: 5,
|
30
|
+
timeout: 5
|
16
31
|
},
|
17
32
|
icmp: {
|
18
|
-
count:
|
19
|
-
interval:
|
33
|
+
count: 5,
|
34
|
+
interval: 2
|
20
35
|
},
|
21
36
|
arping: {
|
22
37
|
count: 1,
|
@@ -41,20 +56,6 @@ module PortAuthority
|
|
41
56
|
}
|
42
57
|
}
|
43
58
|
end
|
44
|
-
|
45
|
-
def config
|
46
|
-
cfg = default_config
|
47
|
-
if File.exist? '/etc/port-authority.yaml'
|
48
|
-
cfg = cfg.deep_merge YAML.load_file('/etc/port-authority.yaml')
|
49
|
-
puts 'loaded config from /etc/port-authority.yaml'
|
50
|
-
elsif File.exist? './port-authority.yaml'
|
51
|
-
cfg = cfg.deep_merge YAML.load_file('./port-authority.yaml')
|
52
|
-
puts 'loaded config from ./port-authority.yaml'
|
53
|
-
else
|
54
|
-
puts 'no config file loaded, using defaults'
|
55
|
-
end
|
56
|
-
cfg
|
57
|
-
end
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
@@ -1,25 +1,21 @@
|
|
1
1
|
require 'etcd'
|
2
|
+
require 'etcd-tools/mixins'
|
2
3
|
|
3
4
|
module PortAuthority
|
4
5
|
module Util
|
5
6
|
module Etcd
|
6
7
|
# connect to ETCD
|
7
8
|
def etcd_connect!
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
err "couldn't connect to etcd at #{host}:#{port}"
|
19
|
-
err "#{e.message}"
|
20
|
-
@exit = true
|
21
|
-
return nil
|
22
|
-
end
|
9
|
+
endpoints = @config[:etcd][:endpoints].map { |e| e = e.gsub!(/^https?:\/\//, '').gsub(/\/$/, '').split(':'); { host: e[0], port: e[1].to_i } }
|
10
|
+
debug "parsed ETCD endpoints: #{endpoints.to_s}"
|
11
|
+
etcd = ::Etcd::Client.new(cluster: endpoints, read_timeout: @config[:etcd][:timeout])
|
12
|
+
etcd if etcd.version
|
13
|
+
rescue
|
14
|
+
raise PortAuthority::Errors::ETCDConnectFailed.new(@config[:etcd][:endpoints])
|
15
|
+
end
|
16
|
+
|
17
|
+
def etcd_healthy?(etcd)
|
18
|
+
raise PortAuthority::Errors::ETCDIsSick.new(@config[:etcd][:endpoints]) unless etcd.healthy?
|
23
19
|
end
|
24
20
|
|
25
21
|
def swarm_leader(etcd)
|
@@ -27,8 +23,8 @@ module PortAuthority
|
|
27
23
|
end
|
28
24
|
|
29
25
|
def am_i_leader?(etcd)
|
30
|
-
Socket.ip_address_list.map()
|
31
|
-
rescue
|
26
|
+
Socket.ip_address_list.map(&:ip_address).member?(swarm_leader(etcd).split(':').first)
|
27
|
+
rescue StandardError => e
|
32
28
|
false
|
33
29
|
end
|
34
30
|
|
@@ -12,7 +12,21 @@ module PortAuthority
|
|
12
12
|
false
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def lb_update!
|
16
|
+
lb_stop! if lb_up?
|
17
|
+
lb_remove!
|
18
|
+
lb_create!
|
19
|
+
@lb_update_hook = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def lb_remove!
|
23
|
+
Docker::Container.get(@config[:lb][:name]).delete
|
24
|
+
rescue Docker::Error::NotFoundError
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def lb_create!
|
29
|
+
lb_remove!
|
16
30
|
img = Docker::Image.create('fromImage' => @config[:lb][:image])
|
17
31
|
|
18
32
|
# setup port bindings hash
|
@@ -21,19 +35,12 @@ module PortAuthority
|
|
21
35
|
port_bindings[port] = [ { 'HostPort' => "#{port.split('/').first}" } ]
|
22
36
|
end
|
23
37
|
|
24
|
-
begin
|
25
|
-
Docker::Container.get(@config[:lb][:name]).delete
|
26
|
-
info 'old LB removed'
|
27
|
-
rescue Docker::Error::NotFoundError
|
28
|
-
debug 'no LB found here, not removing'
|
29
|
-
end
|
30
|
-
|
31
38
|
# create container with
|
32
39
|
@lb_container = Docker::Container.create(
|
33
40
|
'Image' => img.json['Id'],
|
34
41
|
'name' => @config[:lb][:name],
|
35
42
|
'Hostname' => @config[:lb][:name],
|
36
|
-
'Env' => [ "ETCDCTL_ENDPOINT
|
43
|
+
'Env' => [ "ETCDCTL_ENDPOINT=#{@config[:etcd][:endpoints].join(',')}" ],
|
37
44
|
'RestartPolicy' => { 'Name' => 'never' },
|
38
45
|
'HostConfig' => {
|
39
46
|
'PortBindings' => port_bindings,
|
@@ -1,42 +1,57 @@
|
|
1
|
-
|
1
|
+
# rubocop:disable Metrics/LineLength, Metrics/AbcSize, Metrics/MethodLength
|
2
|
+
require 'syslog'
|
2
3
|
|
3
4
|
module PortAuthority
|
4
5
|
module Util
|
5
6
|
module Logger
|
7
|
+
def debug(message)
|
8
|
+
log :debug, message if @config[:debug]
|
9
|
+
end
|
10
|
+
|
6
11
|
def info(message)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
else
|
13
|
-
@semaphore[:log].synchronize do
|
14
|
-
$stdout.puts(Time.now.to_s + ' INFO ' + message.to_s)
|
15
|
-
$stdout.flush
|
16
|
-
end
|
17
|
-
end
|
12
|
+
log :info, message
|
13
|
+
end
|
14
|
+
|
15
|
+
def notice(message)
|
16
|
+
log :notice, message
|
18
17
|
end
|
19
18
|
|
20
19
|
def err(message)
|
21
|
-
|
20
|
+
log :err, message
|
21
|
+
end
|
22
|
+
|
23
|
+
def alert(message)
|
24
|
+
log :alert, message if @config[:debug]
|
25
|
+
end
|
26
|
+
|
27
|
+
def syslog_init(proc_name)
|
28
|
+
Syslog.open(proc_name, Syslog::LOG_PID, Syslog::LOG_DAEMON)
|
29
|
+
end
|
30
|
+
|
31
|
+
def log(lvl, msg)
|
32
|
+
if @config[:syslog]
|
33
|
+
case lvl
|
34
|
+
when :debug
|
35
|
+
l = Syslog::LOG_DEBUG
|
36
|
+
when :info
|
37
|
+
l = Syslog::LOG_INFO
|
38
|
+
when :notice
|
39
|
+
l = Syslog::LOG_NOTICE
|
40
|
+
when :err
|
41
|
+
l = Syslog::LOG_ERR
|
42
|
+
when :alert
|
43
|
+
l = Syslog::LOG_ALERT
|
44
|
+
end
|
22
45
|
@semaphore[:log].synchronize do
|
23
|
-
|
24
|
-
$stdout.flush
|
46
|
+
Syslog.log(l, "(%s) %s", Thread.current[:name], msg.to_s)
|
25
47
|
end
|
26
48
|
else
|
27
49
|
@semaphore[:log].synchronize do
|
28
|
-
$stdout.puts(Time.now.to_s
|
50
|
+
$stdout.puts("#{Time.now.to_s} #{lvl.to_s[0].capitalize} (#{Thread.current[:name]} #{msg.to_s}")
|
29
51
|
$stdout.flush
|
30
52
|
end
|
31
53
|
end
|
32
54
|
end
|
33
|
-
|
34
|
-
def debug(message)
|
35
|
-
@semaphore[:log].synchronize do
|
36
|
-
$stdout.puts(Time.now.to_s + ' DEBUG (TID:' + Thread.current.object_id.to_s + ') ' + message.to_s)
|
37
|
-
$stdout.flush
|
38
|
-
end if @config[:debug]
|
39
|
-
end
|
40
55
|
end
|
41
56
|
end
|
42
57
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# rubocop:disable Metrics/LineLength, Metrics/AbcSize
|
1
2
|
module PortAuthority
|
2
3
|
module Util
|
3
4
|
module Vip
|
@@ -6,16 +7,7 @@ module PortAuthority
|
|
6
7
|
def vip_handle!(leader)
|
7
8
|
ip = IPAddr.new(@config[:vip][:ip])
|
8
9
|
mask = @config[:vip][:mask]
|
9
|
-
cmd = [
|
10
|
-
'address',
|
11
|
-
'',
|
12
|
-
"#{ip}/#{mask}",
|
13
|
-
'dev',
|
14
|
-
@config[:vip][:interface],
|
15
|
-
'label',
|
16
|
-
@config[:vip][:interface] + '-vip',
|
17
|
-
'>/dev/null 2>&1'
|
18
|
-
]
|
10
|
+
cmd = [iproute, 'address', '', "#{ip}/#{mask}", 'dev', @config[:vip][:interface], 'label', @config[:vip][:interface] + '-vip', '>/dev/null 2>&1']
|
19
11
|
leader ? cmd[2] = 'add' : cmd[2] = 'delete'
|
20
12
|
debug "#{cmd.join(' ')}"
|
21
13
|
if system(cmd.join(' '))
|
@@ -27,10 +19,7 @@ module PortAuthority
|
|
27
19
|
|
28
20
|
# send gratuitous ARP to the network
|
29
21
|
def vip_update_arp!
|
30
|
-
cmd = [
|
31
|
-
'-c', @config[:arping][:count],
|
32
|
-
'-I', @config[:vip][:interface],
|
33
|
-
@config[:vip][:ip] ]
|
22
|
+
cmd = [arping, '-U', '-q', '-c', @config[:arping][:count], '-I', @config[:vip][:interface], @config[:vip][:ip]]
|
34
23
|
debug "#{cmd.join(' ')}"
|
35
24
|
if system(cmd.join(' '))
|
36
25
|
return true
|
@@ -41,23 +30,19 @@ module PortAuthority
|
|
41
30
|
|
42
31
|
# check whether VIP is assigned to me
|
43
32
|
def got_vip?
|
44
|
-
Socket.ip_address_list.map()
|
33
|
+
Socket.ip_address_list.map(&:ip_address).member?(@config[:vip][:ip])
|
45
34
|
end
|
46
35
|
|
47
36
|
# check reachability of VIP by ICMP echo
|
48
37
|
def vip_alive?(icmp)
|
49
38
|
(1..@config[:icmp][:count]).each { return true if icmp.ping }
|
50
|
-
|
39
|
+
false
|
51
40
|
end
|
52
41
|
|
53
42
|
# check whether the IP is registered anywhere
|
54
43
|
def vip_dup?
|
55
|
-
cmd_arp = [
|
56
|
-
cmd_arping = [
|
57
|
-
'-c', @config[:arping][:count],
|
58
|
-
'-w', @config[:arping][:wait],
|
59
|
-
'-I', @config[:vip][:interface],
|
60
|
-
@config[:vip][:ip] ]
|
44
|
+
cmd_arp = [arp, '-d', @config[:vip][:ip], '>/dev/null 2>&1']
|
45
|
+
cmd_arping = [arping, '-D', '-q', '-c', @config[:arping][:count], '-w', @config[:arping][:wait], '-I', @config[:vip][:interface], @config[:vip][:ip]]
|
61
46
|
debug "#{cmd_arp.join(' ')}"
|
62
47
|
system(cmd_arp.join(' '))
|
63
48
|
debug "#{cmd_arping.join(' ')}"
|
data/lib/port-authority.rb
CHANGED
@@ -4,4 +4,25 @@ module PortAuthority
|
|
4
4
|
|
5
5
|
module Util
|
6
6
|
end
|
7
|
+
|
8
|
+
module Errors
|
9
|
+
|
10
|
+
class ETCDConnectFailed < StandardError
|
11
|
+
attr_reader :etcd, :message
|
12
|
+
def initialize(etcd, message = "Can't connect to ETCD")
|
13
|
+
@message = message
|
14
|
+
@etcd = etcd
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ETCDIsSick < StandardError
|
19
|
+
attr_reader :etcd, :message
|
20
|
+
def initialize(etcd, message = 'ETCD is not healthy')
|
21
|
+
@message = message
|
22
|
+
@etcd = etcd
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
7
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: port-authority
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Radek 'blufor' Slavicinsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: etcd
|
@@ -36,20 +36,20 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - ~>
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0.
|
39
|
+
version: '0.4'
|
40
40
|
- - ! '>='
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: 0.
|
42
|
+
version: 0.4.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - ~>
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '0.
|
49
|
+
version: '0.4'
|
50
50
|
- - ! '>='
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: 0.
|
52
|
+
version: 0.4.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: net-ping
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|