port-authority 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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