port-authority 0.4.2 → 0.4.3

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,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e29bbb93c31d6ecff7d9931333bda2b6d5b70f04
4
- data.tar.gz: e964daf7b9d98e1f93efe3737e96e09c1a9f33f6
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjA2NzA1NWY3MjM5ZjE2NWFkZmE4NjdkOWFjMWU4NmU4M2E5M2QyMQ==
5
+ data.tar.gz: !binary |-
6
+ Y2FlNTQ0OWI0ZmY4YmE3NzhhMDI1NjZlZTlhNjZiZTNjYjdiYzliNQ==
5
7
  SHA512:
6
- metadata.gz: 22a89d416d76f7797f51ee1425ca00cd11df6bbc0062940db3cf891d36b08b19019b31580fb01b6abee0bbfc9c26e3d86f3ad005708965904d351f49e634e69f
7
- data.tar.gz: 450d1ca0945399efed8020a21f027c7334854689d986e5804fec9807efc4688aed154b5dfa7db59a2a232ea3f5ecdcfe7c94577375676d630579765eb5c875aa
8
+ metadata.gz: !binary |-
9
+ NmRjZGE4MDZiZjhiM2ZmZGM3ODVjMjZmZTIzOWE2ZGJmMjQ3ZGY3MzE5YmQ1
10
+ OTVlNjJmMzkwYjhlYjg4MDFiYzFmNmZhYzFhNDdkOWUxYmQ4MTZjMjU2NzUx
11
+ NzA5MGI3MTlmYzA5MDdlOGJlMGQxMzBlMzEzNTY0YWI3ZTAyYzc=
12
+ data.tar.gz: !binary |-
13
+ MDQ4YTc0NDIzMDBlZjRiNzVjYTNjMTIwMWZlYjg2YzBiODkyNmQ0ZGJjOTli
14
+ MmZjNDk0NGE1OTZkNjY0MzBkYzc0NjdlMmVjMmRkZDA1OTQ2NTUwNDc3Y2I4
15
+ NmFjNzQ1NDYwNzEwMDdhMDMxOTIwYjc4YTI1OGI3ZGIwMWRmOWI=
data/bin/pa-manager ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'port-authority/manager/app'
3
+ PortAuthority::Manager::App.new('pa-manager').run
@@ -0,0 +1,153 @@
1
+ # rubocop:disable MethodLength, CyclomaticComplexity, Metrics/BlockNesting, Metrics/LineLength, Metrics/AbcSize, Metrics/PerceivedComplexity
2
+ require 'ipaddr'
3
+ require 'port-authority'
4
+ require 'port-authority/util/vip'
5
+ require 'port-authority/util/etcd'
6
+ require 'port-authority/util/loadbalancer'
7
+ require 'port-authority/manager/init'
8
+ require 'port-authority/manager/threads/icmp'
9
+ require 'port-authority/manager/threads/swarm'
10
+
11
+ module PortAuthority
12
+ module Manager
13
+ ##
14
+ # Port Authority Manager - manages floating VIP and lb placement
15
+ #
16
+ class App < PortAuthority::Manager::Init
17
+ include PortAuthority::Util::Etcd
18
+ include PortAuthority::Util::Vip
19
+ include PortAuthority::Util::LoadBalancer
20
+ include PortAuthority::Manager::Threads
21
+
22
+ def run
23
+ # exit if not root
24
+ if Process.euid != 0
25
+ alert 'must run under root user!'
26
+ exit! 1
27
+ end
28
+
29
+ Signal.trap('USR1') { @lb_update_hook = true }
30
+
31
+ # prepare semaphores
32
+ @semaphore.merge!(swarm: Mutex.new, icmp: Mutex.new)
33
+
34
+ # prepare threads
35
+ @thread = {icmp: thread_icmp,swarm: thread_swarm}
36
+
37
+ # prepare status vars
38
+ @status_swarm = false
39
+ @status_icmp = false
40
+
41
+ # start threads
42
+ @thread.each_value(&:run)
43
+
44
+ # setup docker client
45
+ lb_docker_setup! || @exit = true
46
+
47
+ # prepare container with load-balancer
48
+ lb_create!
49
+
50
+ # wait for threads to make sure they gather something
51
+ debug 'waiting for threads to gather something...'
52
+ sleep @config[:vip][:interval]
53
+ first_cycle = true
54
+ status_time = Time.now.to_i - 60
55
+
56
+ # main loop
57
+ until @exit
58
+ # initialize local state vars on first iteration
59
+ status_swarm = status_icmp = false if first_cycle
60
+
61
+ if @lb_update_hook
62
+ notice 'updating LB image'
63
+ lb_update!
64
+ end
65
+
66
+ # iteration interval
67
+ sleep @config[:vip][:interval]
68
+
69
+ # sync state to local variables
70
+ @semaphore[:icmp].synchronize { status_icmp = @status_icmp }
71
+ @semaphore[:swarm].synchronize { status_swarm = @status_swarm }
72
+
73
+ # the logic (should be self-explanatory ;))
74
+ if status_swarm
75
+ debug 'i am the leader'
76
+ if got_vip?
77
+ debug 'got VIP, that is OK'
78
+ else
79
+ info 'no VIP here, checking whether it is free'
80
+ if status_icmp
81
+ info 'VIP is still up! (ICMP)'
82
+ # FIXME: notify by sensu client socket
83
+ else
84
+ # FIXME: proper arping handling
85
+ # info 'VIP is unreachable by ICMP, checking for duplicates on L2'
86
+ # if vip_dup?
87
+ # info 'VIP is still assigned! (ARP)'
88
+ # # FIXME: notify by sensu client socket
89
+ # else
90
+ # info 'VIP is free :) assigning'
91
+ # vip_handle! status_swarm
92
+ # info 'updating other hosts about change'
93
+ # vip_update_arp!
94
+ # end
95
+ notice 'VIP is free :) assigning'
96
+ vip_handle! status_swarm
97
+ notice 'updating other hosts about change'
98
+ vip_update_arp!
99
+ end
100
+ end
101
+ if lb_up?
102
+ debug 'load-balancer is up, that is OK'
103
+ else
104
+ notice 'load-balancer is down, starting'
105
+ lb_start!
106
+ end
107
+ else
108
+ debug 'i am not the leader'
109
+ if got_vip?
110
+ notice 'i got VIP and should not, removing'
111
+ vip_handle! status_swarm
112
+ notice 'updating other hosts about change'
113
+ vip_update_arp!
114
+ else
115
+ debug 'no VIP here, that is OK'
116
+ end
117
+ if lb_up?
118
+ notice 'load-balancer is up, stopping'
119
+ lb_stop!
120
+ else
121
+ debug 'load-balancer is down, that is OK'
122
+ end
123
+ end
124
+
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
129
+
130
+ end
131
+
132
+ # this is triggerred on exit
133
+ @thread.each_value(&:join)
134
+
135
+ # remove VIP on shutdown
136
+ if got_vip?
137
+ notice 'removing VIP'
138
+ vip_handle! false
139
+ vip_update_arp!
140
+ end
141
+
142
+ # stop LB on shutdown
143
+ if lb_up?
144
+ notice 'stopping load-balancer'
145
+ lb_stop!
146
+ end
147
+
148
+ info 'exiting...'
149
+ exit 0
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,43 @@
1
+ require 'timeout'
2
+ require 'json'
3
+ require 'etcd-tools'
4
+ require 'port-authority/util/config'
5
+ require 'port-authority/util/logger'
6
+ require 'port-authority/util/helpers'
7
+
8
+ module PortAuthority
9
+ module Manager
10
+ class Init
11
+ include PortAuthority::Util::Config
12
+ include PortAuthority::Util::Logger
13
+ include PortAuthority::Util::Helpers
14
+
15
+ def initialize(proc_name = 'dummy')
16
+ @config = config
17
+ @exit = false
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)
25
+ @exit_sigs.each { |sig| Signal.trap(sig) { @exit = true } }
26
+ Signal.trap('USR2') { @config[:debug] = !@config[:debug] }
27
+ Signal.trap('HUP') { @config = config }
28
+ end
29
+
30
+ def setup(proc_name, nice = -20)
31
+ debug 'setting process name'
32
+ if RUBY_VERSION >= '2.1'
33
+ Process.setproctitle(proc_name)
34
+ else
35
+ $0 = proc_name
36
+ end
37
+ debug 'setting process title'
38
+ Process.setpriority(Process::PRIO_PROCESS, 0, nice)
39
+ # FIXME: Process.daemon ...
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
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
@@ -0,0 +1,42 @@
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 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 }
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
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,61 @@
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
+ },
52
+ commands: {
53
+ arping: `which arping`.chomp,
54
+ arp: `which arp`.chomp,
55
+ iproute: `which ip`.chomp
56
+ }
57
+ }
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,33 @@
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
+ rescue StandardError => e
28
+ false
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
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
@@ -0,0 +1,66 @@
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
+ # create container with
39
+ @lb_container = Docker::Container.create(
40
+ 'Image' => img.json['Id'],
41
+ 'name' => @config[:lb][:name],
42
+ 'Hostname' => @config[:lb][:name],
43
+ 'Env' => [ "ETCDCTL_ENDPOINT=#{@config[:etcd][:endpoints].join(',')}" ],
44
+ 'RestartPolicy' => { 'Name' => 'never' },
45
+ 'HostConfig' => {
46
+ 'PortBindings' => port_bindings,
47
+ 'NetworkMode' => @config[:lb][:network]
48
+ }
49
+ )
50
+ end
51
+
52
+ def lb_up?
53
+ @lb_container.json['State']['Running']
54
+ end
55
+
56
+ def lb_start!
57
+ @lb_container.start
58
+ end
59
+
60
+ def lb_stop!
61
+ @lb_container.stop
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,57 @@
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
@@ -0,0 +1,57 @@
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
@@ -0,0 +1,28 @@
1
+ module PortAuthority
2
+ module Manager
3
+ end
4
+
5
+ module Util
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
+
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: port-authority
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radek 'blufor' Slavicinsky
@@ -14,88 +14,101 @@ dependencies:
14
14
  name: etcd
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.3'
20
- - - ">="
20
+ - - ! '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: 0.3.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0.3'
30
- - - ">="
30
+ - - ! '>='
31
31
  - !ruby/object:Gem::Version
32
32
  version: 0.3.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: etcd-tools
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ~>
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0.4'
40
- - - ">="
40
+ - - ! '>='
41
41
  - !ruby/object:Gem::Version
42
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
49
  version: '0.4'
50
- - - ">="
50
+ - - ! '>='
51
51
  - !ruby/object:Gem::Version
52
52
  version: 0.4.0
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: net-ping
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
- - - "~>"
57
+ - - ~>
58
58
  - !ruby/object:Gem::Version
59
59
  version: '1.7'
60
- - - ">="
60
+ - - ! '>='
61
61
  - !ruby/object:Gem::Version
62
62
  version: 1.7.8
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - "~>"
67
+ - - ~>
68
68
  - !ruby/object:Gem::Version
69
69
  version: '1.7'
70
- - - ">="
70
+ - - ! '>='
71
71
  - !ruby/object:Gem::Version
72
72
  version: 1.7.8
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: docker-api
75
75
  requirement: !ruby/object:Gem::Requirement
76
76
  requirements:
77
- - - "~>"
77
+ - - ~>
78
78
  - !ruby/object:Gem::Version
79
79
  version: '1.0'
80
- - - ">="
80
+ - - ! '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: 1.25.0
83
83
  type: :runtime
84
84
  prerelease: false
85
85
  version_requirements: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
89
  version: '1.0'
90
- - - ">="
90
+ - - ! '>='
91
91
  - !ruby/object:Gem::Version
92
92
  version: 1.25.0
93
93
  description: CLI Tools for PortAuthority
94
94
  email: radek.slavicinsky@gmail.com
95
- executables: []
95
+ executables:
96
+ - pa-manager
96
97
  extensions: []
97
98
  extra_rdoc_files: []
98
- files: []
99
+ files:
100
+ - bin/pa-manager
101
+ - lib/port-authority.rb
102
+ - lib/port-authority/manager/app.rb
103
+ - lib/port-authority/manager/init.rb
104
+ - lib/port-authority/manager/threads/icmp.rb
105
+ - lib/port-authority/manager/threads/swarm.rb
106
+ - lib/port-authority/util/config.rb
107
+ - lib/port-authority/util/etcd.rb
108
+ - lib/port-authority/util/helpers.rb
109
+ - lib/port-authority/util/loadbalancer.rb
110
+ - lib/port-authority/util/logger.rb
111
+ - lib/port-authority/util/vip.rb
99
112
  homepage: https://github.com/prozeta/port-authority
100
113
  licenses:
101
114
  - GPLv2
@@ -106,17 +119,17 @@ require_paths:
106
119
  - lib
107
120
  required_ruby_version: !ruby/object:Gem::Requirement
108
121
  requirements:
109
- - - ">="
122
+ - - ! '>='
110
123
  - !ruby/object:Gem::Version
111
124
  version: 1.9.3
112
125
  required_rubygems_version: !ruby/object:Gem::Requirement
113
126
  requirements:
114
- - - ">="
127
+ - - ! '>='
115
128
  - !ruby/object:Gem::Version
116
129
  version: '0'
117
130
  requirements: []
118
131
  rubyforge_project:
119
- rubygems_version: 2.4.6
132
+ rubygems_version: 2.4.3
120
133
  signing_key:
121
134
  specification_version: 4
122
135
  summary: Port Authority