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.
@@ -0,0 +1,51 @@
1
+ # rubocop:disable Metrics/LineLength, Metrics/AbcSize
2
+ require 'net/ping'
3
+
4
+ module PortAuthority
5
+ module Mechanism
6
+ module FloatingIP
7
+
8
+ extend self
9
+
10
+ attr_accessor :_icmp
11
+
12
+ def init!
13
+ @_icmp = Net::Ping::ICMP.new(Config.lbaas[:floating_ip])
14
+ end
15
+
16
+ # add or remove VIP on interface
17
+ def handle!(leader)
18
+ return true if shellcmd Config.commands[:iproute], 'address', leader ? 'add' : 'delete', "#{Config.lbaas[:floating_ip]}/32", 'dev', Config.lbaas[:interface], '>/dev/null 2>&1'
19
+ false
20
+ end
21
+
22
+ # send gratuitous ARP to the network
23
+ def arp_update!
24
+ return true if shellcmd Config.commands[:arping], '-U', '-q', '-c', Config.lbaas[:arping_count], '-I', Config.lbaas[:interface], Config.lbaas[:floating_ip]
25
+ false
26
+ end
27
+
28
+ # check whether VIP is assigned to me
29
+ def up?
30
+ Socket.ip_address_list.map(&:ip_address).member?(Config.lbaas[:floating_ip])
31
+ end
32
+
33
+ # check reachability of VIP by ICMP echo
34
+ def reachable?
35
+ (1..Config.lbaas[:icmp_count]).each { return true if @_icmp.ping }
36
+ false
37
+ end
38
+
39
+ def arp_del!
40
+ return true if shellcmd Config.commands[:arp], '-d', Config.lbaas[:floating_ip], '>/dev/null 2>&1'
41
+ false
42
+ end
43
+
44
+ # check whether the IP is registered anywhere
45
+ def duplicate?
46
+ return true if shellcmd Config.commands[:arping], '-D', '-q', '-c', Config.lbaas[:arping_count], '-w', Config.lbaas[:arping_wait], '-I', Config.lbaas[:interface], Config.lbaas[:floating_ip]
47
+ false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ require 'docker-api'
2
+
3
+ module PortAuthority
4
+ module Mechanism
5
+ module LoadBalancer
6
+
7
+ extend self
8
+
9
+ attr_reader :_container, :_container_def, :_image
10
+
11
+ def init!
12
+ Docker.url = Config.lbaas[:docker_endpoint]
13
+ Docker.options = { connect_timeout: Config.lbaas[:docker_timeout] || 10 }
14
+ end
15
+
16
+ def container
17
+ @_container ||= Docker::Container.get(Config.lbaas[:name]) rescue nil
18
+ end
19
+
20
+ def image
21
+ @_image ||= Docker::Image.create('fromImage' => Config.lbaas[:image])
22
+ end
23
+
24
+ def pull!
25
+ @_image = Docker::Image.create('fromImage' => Config.lbaas[:image])
26
+ end
27
+
28
+ def create!
29
+ port_bindings = Hash.new
30
+ self.image.json['ContainerConfig']['ExposedPorts'].keys.each do |port|
31
+ port_bindings[port] = [ { 'HostPort' => "#{port.split('/').first}" } ]
32
+ end
33
+ @_container_def = {
34
+ 'Image' => self.image.json['Id'],
35
+ 'name' => Config.lbaas[:name],
36
+ 'Hostname' => Config.lbaas[:name],
37
+ 'Env' => [ "ETCDCTL_ENDPOINT=#{Config.etcd[:endpoints].map { |e| "http://#{e}" }.join(',')}" ],
38
+ 'RestartPolicy' => { 'Name' => 'never' },
39
+ 'HostConfig' => {
40
+ 'PortBindings' => port_bindings,
41
+ 'NetworkMode' => Config.lbaas[:network]
42
+ }
43
+ }
44
+ if Config.lbaas[:log_dest] != ''
45
+ @_container_def['HostConfig']['LogConfig'] = {
46
+ 'Type' => 'gelf',
47
+ 'Config' => {
48
+ 'gelf-address' => Config.lbaas[:log_dest],
49
+ 'tag' => Socket.gethostbyname(Socket.gethostname).first + '/{{.Name}}/{{.ID}}'
50
+ }
51
+ }
52
+ end
53
+ @_container = Docker::Container.create(@_container_def)
54
+ end
55
+
56
+
57
+ def update!
58
+ begin
59
+ self.stop! && start = true if self.up?
60
+ self.remove!
61
+ self.pull!
62
+ self.create!
63
+ self.start! if start == true
64
+ rescue StandardError => e
65
+ Logger.error "UNCAUGHT EXCEPTION IN THREAD #{Thread.current[:name]}"
66
+ Logger.error [' ', e.class, e.message].join(' ')
67
+ Logger.error ' ' + e.backtrace.to_s
68
+ end
69
+ end
70
+
71
+ def remove!
72
+ self.container.delete
73
+ end
74
+
75
+ def up?
76
+ self.container.json['State']['Running']
77
+ end
78
+
79
+ def start!
80
+ self.container.start
81
+ end
82
+
83
+ def stop!
84
+ self.container.stop
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,9 @@
1
+ module PortAuthority
2
+ class Tool
3
+ def initialize
4
+ self.optparse
5
+ @ARGS = ARGV
6
+ self.run
7
+ end
8
+ end
9
+ end
@@ -2,14 +2,13 @@ require 'optparse'
2
2
  require 'json'
3
3
  require 'yaml'
4
4
  require 'etcd-tools'
5
- require 'etcd-tools/etcd'
6
- require 'port-authority/util/etcd'
5
+ # require 'etcd-tools/mixins'
6
+ require 'port-authority/etcd'
7
+ require 'port-authority/tool'
7
8
 
8
9
  module PortAuthority
9
- module Services
10
- class App
11
- include PortAuthority::Util::Etcd
12
- include EtcdTools::Etcd
10
+ module Tools
11
+ class ServiceList < PortAuthority::Tool
13
12
 
14
13
  attr_reader :etcd
15
14
 
@@ -61,18 +60,15 @@ module PortAuthority
61
60
  end.parse!
62
61
  end
63
62
 
64
- def initialize
65
- self.optparse
66
- @network = ARGV.pop
67
- unless @network
68
- $stderr.puts "Missing NETWORK_NAME!"
63
+ def run
64
+ unless @ARGS[0]
65
+ $stderr.puts 'Missing NETWORK_NAME!'
69
66
  exit 1
70
67
  end
71
- @etcd = etcd_connect @options[:url]
72
- end
73
68
 
74
- def run
75
- services = list_services(@etcd, @network, @options[:service_filter])
69
+ @etcd = PortAuthority::Etcd.shell_cluster_connect(@options[:url])
70
+
71
+ services = @etcd.swarm_list_services(@ARGS[0], @options[:service_filter])
76
72
 
77
73
  if @options[:output_yaml]
78
74
  puts services.to_yaml
@@ -80,7 +76,7 @@ module PortAuthority
80
76
  puts services.to_json
81
77
  else
82
78
  if @options[:ip_only]
83
- services.each { |name, params| puts "#{params['ip']}" }
79
+ services.each { |_, params| puts params['ip'] }
84
80
  elsif @options[:ports]
85
81
  services.each do |name, params|
86
82
  params['ports'].each do |port|
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.8
4
+ version: 0.5.0
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-04-01 00:00:00.000000000 Z
11
+ date: 2016-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: etcd
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: '0.4'
40
40
  - - ">="
41
41
  - !ruby/object:Gem::Version
42
- version: 0.4.1
42
+ version: 0.4.4
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +49,7 @@ dependencies:
49
49
  version: '0.4'
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 0.4.1
52
+ version: 0.4.4
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: net-ping
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -93,25 +93,23 @@ dependencies:
93
93
  description: CLI Tools for PortAuthority
94
94
  email: radek.slavicinsky@gmail.com
95
95
  executables:
96
+ - pa-lbaas-agent
96
97
  - pa-service-list
97
- - pa-manager
98
98
  extensions: []
99
99
  extra_rdoc_files: []
100
100
  files:
101
- - bin/pa-manager
101
+ - bin/pa-lbaas-agent
102
102
  - bin/pa-service-list
103
103
  - lib/port-authority.rb
104
- - lib/port-authority/manager.rb
105
- - lib/port-authority/manager/init.rb
106
- - lib/port-authority/manager/threads/icmp.rb
107
- - lib/port-authority/manager/threads/swarm.rb
108
- - lib/port-authority/services.rb
109
- - lib/port-authority/util/config.rb
110
- - lib/port-authority/util/etcd.rb
111
- - lib/port-authority/util/helpers.rb
112
- - lib/port-authority/util/loadbalancer.rb
113
- - lib/port-authority/util/logger.rb
114
- - lib/port-authority/util/vip.rb
104
+ - lib/port-authority/agent.rb
105
+ - lib/port-authority/agents/lbaas.rb
106
+ - lib/port-authority/config.rb
107
+ - lib/port-authority/etcd.rb
108
+ - lib/port-authority/logger.rb
109
+ - lib/port-authority/mechanism/floating_ip.rb
110
+ - lib/port-authority/mechanism/load_balancer.rb
111
+ - lib/port-authority/tool.rb
112
+ - lib/port-authority/tools/service_list.rb
115
113
  homepage: https://github.com/prozeta/port-authority
116
114
  licenses:
117
115
  - GPLv2
@@ -132,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
130
  version: '0'
133
131
  requirements: []
134
132
  rubyforge_project:
135
- rubygems_version: 2.4.6
133
+ rubygems_version: 2.4.8
136
134
  signing_key:
137
135
  specification_version: 4
138
136
  summary: Port Authority
data/bin/pa-manager DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'port-authority/manager'
3
- PortAuthority::Manager::App.new('pa-manager').run
@@ -1,153 +0,0 @@
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
@@ -1,49 +0,0 @@
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
- @semaphore = { log: Mutex.new }
17
- @config = config
18
- @exit = false
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') do
27
- if @config[:debug]
28
- @config[:debug] = false
29
- else
30
- @config[:debug] = true
31
- end
32
- end
33
- Signal.trap('HUP') { @config = config }
34
- end
35
-
36
- def setup(proc_name, nice = -20)
37
- debug 'setting process name'
38
- if RUBY_VERSION >= '2.1'
39
- Process.setproctitle(proc_name)
40
- else
41
- $0 = proc_name
42
- end
43
- debug 'setting process title'
44
- Process.setpriority(Process::PRIO_PROCESS, 0, nice)
45
- # FIXME: Process.daemon ...
46
- end
47
- end
48
- end
49
- end