port-authority 0.4.8 → 0.5.0
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/bin/pa-lbaas-agent +3 -0
- data/bin/pa-service-list +2 -2
- data/lib/port-authority.rb +0 -8
- data/lib/port-authority/agent.rb +164 -0
- data/lib/port-authority/agents/lbaas.rb +134 -0
- data/lib/port-authority/config.rb +49 -0
- data/lib/port-authority/etcd.rb +47 -0
- data/lib/port-authority/logger.rb +56 -0
- data/lib/port-authority/mechanism/floating_ip.rb +51 -0
- data/lib/port-authority/mechanism/load_balancer.rb +89 -0
- data/lib/port-authority/tool.rb +9 -0
- data/lib/port-authority/{services.rb → tools/service_list.rb} +12 -16
- metadata +16 -18
- data/bin/pa-manager +0 -3
- data/lib/port-authority/manager.rb +0 -153
- data/lib/port-authority/manager/init.rb +0 -49
- data/lib/port-authority/manager/threads/icmp.rb +0 -30
- data/lib/port-authority/manager/threads/swarm.rb +0 -35
- data/lib/port-authority/util/config.rb +0 -62
- data/lib/port-authority/util/etcd.rb +0 -50
- data/lib/port-authority/util/helpers.rb +0 -27
- data/lib/port-authority/util/loadbalancer.rb +0 -78
- data/lib/port-authority/util/logger.rb +0 -57
- data/lib/port-authority/util/vip.rb +0 -57
@@ -1,30 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/MethodLength
|
2
|
-
require 'net/ping'
|
3
|
-
|
4
|
-
module PortAuthority
|
5
|
-
module Manager
|
6
|
-
module Threads
|
7
|
-
def thread_icmp
|
8
|
-
Thread.new do
|
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
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/MethodLength
|
2
|
-
module PortAuthority
|
3
|
-
module Manager
|
4
|
-
module Threads
|
5
|
-
def thread_swarm
|
6
|
-
Thread.new do
|
7
|
-
Thread.current[:name] = 'swarm'
|
8
|
-
info 'starting swarm thread...'
|
9
|
-
begin
|
10
|
-
etcd = etcd_connect!
|
11
|
-
until @exit
|
12
|
-
debug 'checking swarm state'
|
13
|
-
status = am_i_leader? etcd
|
14
|
-
@semaphore[:swarm].synchronize { @status_swarm = status }
|
15
|
-
debug "i am #{status ? '' : 'NOT' } the swarm leader"
|
16
|
-
sleep @config[:etcd][:interval]
|
17
|
-
end
|
18
|
-
info 'ending swarm thread...'
|
19
|
-
rescue PortAuthority::Errors::ETCDConnectFailed => e
|
20
|
-
err "#{e.class}: #{e.message}"
|
21
|
-
err "connection: " + e.etcd.to_s
|
22
|
-
err " #{e.backtrace.to_s}"
|
23
|
-
@semaphore[:swarm].synchronize { @status_swarm = false }
|
24
|
-
sleep @config[:etcd][:interval]
|
25
|
-
retry unless @exit
|
26
|
-
rescue StandardError => e
|
27
|
-
alert e.message
|
28
|
-
alert e.backtrace.to_s
|
29
|
-
@exit = true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'etcd-tools'
|
3
|
-
|
4
|
-
module PortAuthority
|
5
|
-
module Util
|
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
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def default_config
|
25
|
-
{ debug: false,
|
26
|
-
syslog: false,
|
27
|
-
etcd: {
|
28
|
-
endpoints: ['http://localhost:2379'],
|
29
|
-
interval: 5,
|
30
|
-
timeout: 5
|
31
|
-
},
|
32
|
-
icmp: {
|
33
|
-
count: 5,
|
34
|
-
interval: 2
|
35
|
-
},
|
36
|
-
arping: {
|
37
|
-
count: 1,
|
38
|
-
wait: 1
|
39
|
-
},
|
40
|
-
vip: {
|
41
|
-
interval: 1,
|
42
|
-
ip: '172.17.1.5',
|
43
|
-
mask: '255.255.255.0',
|
44
|
-
interface: 'eth0'
|
45
|
-
},
|
46
|
-
lb: {
|
47
|
-
image: 'docker-registry.prz/stackdocks/haproxy:latest',
|
48
|
-
name: 'lb',
|
49
|
-
network: 'overlay',
|
50
|
-
docker_endpoint: 'unix:///var/run/docker.sock',
|
51
|
-
log_dest: '',
|
52
|
-
},
|
53
|
-
commands: {
|
54
|
-
arping: `which arping`.chomp,
|
55
|
-
arp: `which arp`.chomp,
|
56
|
-
iproute: `which ip`.chomp
|
57
|
-
}
|
58
|
-
}
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'etcd'
|
2
|
-
require 'etcd-tools/mixins'
|
3
|
-
|
4
|
-
module PortAuthority
|
5
|
-
module Util
|
6
|
-
module Etcd
|
7
|
-
# connect to ETCD
|
8
|
-
def etcd_connect!
|
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?
|
19
|
-
end
|
20
|
-
|
21
|
-
def swarm_leader(etcd)
|
22
|
-
etcd.get('/_pa/docker/swarm/leader').value
|
23
|
-
end
|
24
|
-
|
25
|
-
def am_i_leader?(etcd)
|
26
|
-
Socket.ip_address_list.map(&:ip_address).member?(swarm_leader(etcd).split(':').first)
|
27
|
-
end
|
28
|
-
|
29
|
-
def overlay_id(etcd, name)
|
30
|
-
etcd.get_hash('/_pa/docker/network/v1.0/network').each_value do |network|
|
31
|
-
return network['id'] if network['name'] == name
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def list_services(etcd, network, service_name='.*')
|
36
|
-
svc_filter = Regexp.new(service_name)
|
37
|
-
network_id = overlay_id(etcd, network)
|
38
|
-
services = Hash.new
|
39
|
-
etcd.get_hash("/_pa/docker/network/v1.0/endpoint/#{network_id}").each_value do |container|
|
40
|
-
next unless svc_filter.match container['name']
|
41
|
-
services[container['name']] = Hash.new
|
42
|
-
services[container['name']]['id'] = container['id']
|
43
|
-
services[container['name']]['ip'] = container['ep_iface']['addr'].sub(/\/[0-9]+$/, '')
|
44
|
-
services[container['name']]['ports'] = container['exposed_ports'].map { |port| port['Port'] }
|
45
|
-
end
|
46
|
-
services
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
|
3
|
-
module PortAuthority
|
4
|
-
module Util
|
5
|
-
module Helpers
|
6
|
-
def hostname
|
7
|
-
@hostname ||= Socket.gethostname
|
8
|
-
end
|
9
|
-
|
10
|
-
def my_ip
|
11
|
-
@my_ip ||= Socket.ip_address_list.detect(&:ipv4_private?).ip_address
|
12
|
-
end
|
13
|
-
|
14
|
-
def arping
|
15
|
-
@config[:commands][:arping]
|
16
|
-
end
|
17
|
-
|
18
|
-
def iproute
|
19
|
-
@config[:commands][:iproute]
|
20
|
-
end
|
21
|
-
|
22
|
-
def arp
|
23
|
-
@config[:commands][:arp]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'docker-api'
|
2
|
-
|
3
|
-
module PortAuthority
|
4
|
-
module Util
|
5
|
-
module LoadBalancer
|
6
|
-
# connect to Docker
|
7
|
-
def lb_docker_setup!
|
8
|
-
Docker.url = @config[:lb][:docker_endpoint]
|
9
|
-
Docker.version
|
10
|
-
true
|
11
|
-
rescue
|
12
|
-
false
|
13
|
-
end
|
14
|
-
|
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!
|
30
|
-
img = Docker::Image.create('fromImage' => @config[:lb][:image])
|
31
|
-
|
32
|
-
# setup port bindings hash
|
33
|
-
port_bindings = Hash.new
|
34
|
-
img.json['ContainerConfig']['ExposedPorts'].keys.each do |port|
|
35
|
-
port_bindings[port] = [ { 'HostPort' => "#{port.split('/').first}" } ]
|
36
|
-
end
|
37
|
-
|
38
|
-
cont_def = {
|
39
|
-
'Image' => img.json['Id'],
|
40
|
-
'name' => @config[:lb][:name],
|
41
|
-
'Hostname' => @config[:lb][:name],
|
42
|
-
'Env' => [ "ETCDCTL_ENDPOINT=#{@config[:etcd][:endpoints].map { |e| "http://#{e}" }.join(',')}" ],
|
43
|
-
'RestartPolicy' => { 'Name' => 'never' },
|
44
|
-
'HostConfig' => {
|
45
|
-
'PortBindings' => port_bindings,
|
46
|
-
'NetworkMode' => @config[:lb][:network]
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
if @config[:lb][:log_dest] != ''
|
51
|
-
cont_def['HostConfig']['LogConfig'] = {
|
52
|
-
'Type' => 'gelf',
|
53
|
-
'Config' => {
|
54
|
-
'gelf-address' => @config[:lb][:log_dest],
|
55
|
-
'tag' => Socket.gethostbyname(Socket.gethostname).first + '/{{.Name}}/{{.ID}}'
|
56
|
-
}
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
# create container with
|
61
|
-
@lb_container = Docker::Container.create(cont_def)
|
62
|
-
end
|
63
|
-
|
64
|
-
def lb_up?
|
65
|
-
@lb_container.json['State']['Running']
|
66
|
-
end
|
67
|
-
|
68
|
-
def lb_start!
|
69
|
-
@lb_container.start
|
70
|
-
end
|
71
|
-
|
72
|
-
def lb_stop!
|
73
|
-
@lb_container.stop
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/LineLength, Metrics/AbcSize, Metrics/MethodLength
|
2
|
-
require 'syslog'
|
3
|
-
|
4
|
-
module PortAuthority
|
5
|
-
module Util
|
6
|
-
module Logger
|
7
|
-
def debug(message)
|
8
|
-
log :debug, message if @config[:debug]
|
9
|
-
end
|
10
|
-
|
11
|
-
def info(message)
|
12
|
-
log :info, message
|
13
|
-
end
|
14
|
-
|
15
|
-
def notice(message)
|
16
|
-
log :notice, message
|
17
|
-
end
|
18
|
-
|
19
|
-
def err(message)
|
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
|
45
|
-
@semaphore[:log].synchronize do
|
46
|
-
Syslog.log(l, "(%s) %s", Thread.current[:name], msg.to_s)
|
47
|
-
end
|
48
|
-
else
|
49
|
-
@semaphore[:log].synchronize do
|
50
|
-
$stdout.puts("#{Time.now.to_s} #{lvl.to_s[0].capitalize} (#{Thread.current[:name]}) #{msg.to_s}")
|
51
|
-
$stdout.flush
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/LineLength, Metrics/AbcSize
|
2
|
-
module PortAuthority
|
3
|
-
module Util
|
4
|
-
module Vip
|
5
|
-
# add or remove VIP on interface
|
6
|
-
# <IMPLEMENTED>
|
7
|
-
def vip_handle!(leader)
|
8
|
-
ip = IPAddr.new(@config[:vip][:ip])
|
9
|
-
mask = @config[:vip][:mask]
|
10
|
-
cmd = [iproute, 'address', '', "#{ip}/#{mask}", 'dev', @config[:vip][:interface], 'label', @config[:vip][:interface] + '-vip', '>/dev/null 2>&1']
|
11
|
-
leader ? cmd[2] = 'add' : cmd[2] = 'delete'
|
12
|
-
debug "#{cmd.join(' ')}"
|
13
|
-
if system(cmd.join(' '))
|
14
|
-
return true
|
15
|
-
else
|
16
|
-
return false
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# send gratuitous ARP to the network
|
21
|
-
def vip_update_arp!
|
22
|
-
cmd = [arping, '-U', '-q', '-c', @config[:arping][:count], '-I', @config[:vip][:interface], @config[:vip][:ip]]
|
23
|
-
debug "#{cmd.join(' ')}"
|
24
|
-
if system(cmd.join(' '))
|
25
|
-
return true
|
26
|
-
else
|
27
|
-
return false
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# check whether VIP is assigned to me
|
32
|
-
def got_vip?
|
33
|
-
Socket.ip_address_list.map(&:ip_address).member?(@config[:vip][:ip])
|
34
|
-
end
|
35
|
-
|
36
|
-
# check reachability of VIP by ICMP echo
|
37
|
-
def vip_alive?(icmp)
|
38
|
-
(1..@config[:icmp][:count]).each { return true if icmp.ping }
|
39
|
-
false
|
40
|
-
end
|
41
|
-
|
42
|
-
# check whether the IP is registered anywhere
|
43
|
-
def vip_dup?
|
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]]
|
46
|
-
debug "#{cmd_arp.join(' ')}"
|
47
|
-
system(cmd_arp.join(' '))
|
48
|
-
debug "#{cmd_arping.join(' ')}"
|
49
|
-
if system(cmd_arping.join(' '))
|
50
|
-
return false
|
51
|
-
else
|
52
|
-
return true
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|