panteras_api 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 864bf7c91dedd75b7472623ef2b101983e0b6d47
4
+ data.tar.gz: edabdfc6739a10e554fdc9af1b7f5bda129ea1ce
5
+ SHA512:
6
+ metadata.gz: 1d84d1d010ce1032e30262e40c5216415a756b1a142a005c18c552aea2285c6ada1f4b39de68e8bf18b4013eae3b237c10cca919580503c0495938f236af1f6c
7
+ data.tar.gz: 1af38e7b35769ed315f12283d748e19c14b1fb865d4280803c7738f032eebfe4f6d096e8cf4815000b0c7306e524caa522d1a925140e33d5325270142644ce7d
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .project
11
+ .buildpath
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in panteras_api.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # PanterasApi
2
+
3
+ This is a program that checks services consistencies in a mesos/marathon/consul/docker PaaS infrastructure.
4
+
5
+ It is intended for use together with the [Panteras](https://github.com/eBayClassifiedsGroup/PanteraS) project.
6
+
7
+ **It will not work on other systems without tweaking the main program.**
8
+
9
+ ## Installation
10
+
11
+ $ gem install panteras_api
12
+
13
+ ## Usage
14
+
15
+ Usage: bin/panteras_api [options]
16
+ -m MESOS_MASTER_HOSTNAME, Default: localhost
17
+ --mesos-master-hostname
18
+ -p MESOS_MASTER_PORT, Default: 5050
19
+ --mesos-master-port
20
+ -d, --debug Default: false
21
+ -f FULLY_QUALIFIED_HOSTNAME, Default: autodiscovery via gethostbyname
22
+ --fqdn
23
+
24
+ ## Notes
25
+
26
+ * Exits with error code 1 when problems seen
27
+ * Exits with code 0 when everything is OK
28
+ * outputs a short text summary
29
+ * intended for use as a nagios-style check
30
+ * this script should be run on all mesos slaves (docker hosts)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'panteras_api'
5
+ require 'json'
6
+
7
+ config_default = {
8
+ :mesos_master_hostname => 'localhost',
9
+ :mesos_master_port => 5050,
10
+ :debug => false,
11
+ :fqdn => nil,
12
+ :error_exit_code => 1
13
+ }
14
+
15
+ options = {}
16
+ bad_news = []
17
+ good_news = []
18
+
19
+ OptionParser.new("Usage: #{$0} [options]") do |opts|
20
+ opts.release = PanterasApi::VERSION
21
+
22
+ opts.on("-m", "--mesos-master-hostname MESOS_MASTER_HOSTNAME", "Default: #{config_default[:mesos_master_hostname]}") do |m|
23
+ options[:mesos_master_hostname] = m
24
+ end
25
+
26
+ opts.on("-p", "--mesos-master-port MESOS_MASTER_PORT", "Default: #{config_default[:mesos_master_port]}") do |p|
27
+ options[:mesos_master_port] = p
28
+ end
29
+
30
+ opts.on("-d", "--debug", "Default: #{config_default[:debug]}") do |d|
31
+ options[:debug] = d
32
+ end
33
+
34
+ opts.on("-c", "--critical", "Exit with Nagios CRITICAL (code 2). Default: WARNING (#{config_default[:error_exit_code]})") do |c|
35
+ options[:error_exit_code] = 2
36
+ end
37
+
38
+ opts.on("-f", "--fqdn FULLY_QUALIFIED_HOSTNAME", "Default: autodiscovery via gethostbyname") do |f|
39
+ options[:fqdn] = f
40
+ end
41
+
42
+
43
+ end.parse!
44
+
45
+ config_default[:fqdn] = Utils.fully_qualified_hostname if options[:fqdn].nil?
46
+ config = config_default.merge!(options)
47
+
48
+ ### MESOS
49
+
50
+ my_fqdn = config[:fqdn]
51
+
52
+ puts "#" * 75 if config[:debug]
53
+ puts "* My hostname: #{my_fqdn}" if config[:debug]
54
+
55
+ begin
56
+ puts "* Connecting to Mesos host: #{config[:mesos_master_hostname]}:#{config[:mesos_master_port]}" if config[:debug]
57
+ mesos = MesosCluster.new(config[:mesos_master_hostname], config[:mesos_master_port])
58
+ rescue SocketError, Errno::ECONNREFUSED => e
59
+ abort("Problem connecting to mesos host #{config[:mesos_master_hostname]}:#{config[:mesos_master_port]}: #{e.message}")
60
+ end
61
+
62
+ abort("No running mesos tasks seen on mesos master #{config[:mesos_master_hostname]}:#{config[:mesos_master_port]}") if mesos.tasks.size == 0
63
+
64
+ puts "#" * 75 if config[:debug]
65
+ puts "* Mesos tasks ids running this cluster (all slaves):" if config[:debug]
66
+ puts mesos.tasks if config[:debug]
67
+
68
+
69
+ puts "#" * 75 if config[:debug]
70
+ puts "* Mesos tasks ids for tasks running on #{my_fqdn}:" if config[:debug]
71
+ mesos_tasks = mesos.my_tasks_ids(my_fqdn).flatten
72
+ puts mesos_tasks if config[:debug]
73
+
74
+ good_news << "#{mesos_tasks.size} mesos tasks running." if ! mesos_tasks.empty?
75
+
76
+
77
+ ### MARATHON
78
+ marathon = MarathonEndpoint.new(mesos.master_hostname)
79
+
80
+ puts "#" * 75 if config[:debug]
81
+ puts "* Marathon tasks on #{my_fqdn}:" if config[:debug]
82
+ marathon_tasks = marathon.my_task_ids(my_fqdn)
83
+ puts marathon_tasks if config[:debug]
84
+
85
+ good_news << "#{marathon_tasks.size} marathon tasks running."
86
+
87
+ ## compare mesos tasks (source of truth) with tasks seen in marathon
88
+ only_in_mesos = mesos_tasks - marathon_tasks
89
+
90
+ ## compare marathon tasks with tasks seen in marathon
91
+ only_in_marathon = marathon_tasks - mesos_tasks
92
+
93
+ if ! only_in_mesos.empty?
94
+ bad_news << "mesos tasks not seen in marathon: #{only_in_mesos.join(',')}"
95
+ end
96
+
97
+ if ! only_in_marathon.empty?
98
+ bad_news << "marathon tasks not seen in mesos: #{only_in_marathon.join(',')}"
99
+ end
100
+
101
+ ### CONSUL
102
+ consul = ConsulCluster.new(mesos.master_hostname)
103
+ ### TODO: test with paas-formatted service names
104
+ puts "#" * 75 if config[:debug]
105
+ puts "* Consul services for #{my_fqdn}:" if config[:debug]
106
+ consul_services = consul.my_service_ids
107
+ puts consul.my_services.join("\n") if config[:debug]
108
+
109
+ good_news << "#{consul.my_services.size} consul services running."
110
+
111
+ ### set these environment variables here or in shell for testing a remote docker (ie, boot2docker)
112
+ #ENV['DOCKER_HOST']='tcp://192.168.59.103:2376'
113
+ #ENV['DOCKER_CERT_PATH']='/Users/jcolby/.boot2docker/certs/boot2docker-vm'
114
+ #ENV['DOCKER_TLS_VERIFY']='1'
115
+
116
+ ### Compare mesos vs docker tasks
117
+
118
+ docker_inspect = DockerHost.inspect
119
+ docker_tasks = docker_inspect.collect { |d| d[:mesos_task_id] }.compact.reject { |i| i.empty? }
120
+ docker_mesos_ids = docker_inspect.collect { |d| d[:name] }.compact.reject { |i| i.empty? }
121
+
122
+ puts "#" * 75 if config[:debug]
123
+ puts "* Docker tasks on #{my_fqdn}:" if config[:debug]
124
+ puts docker_inspect if config[:debug]
125
+
126
+ mesos_not_docker = mesos_tasks - docker_tasks
127
+ if ! mesos_not_docker.empty?
128
+ bad_news << "mesos tasks not running in docker: #{mesos_not_docker.join(',')}"
129
+ end
130
+
131
+ docker_not_mesos = docker_tasks - mesos_tasks
132
+ if ! docker_not_mesos.empty?
133
+ bad_news << "docker tasks not seen in mesos: #{docker_not_mesos.join(',')}"
134
+ end
135
+
136
+ good_news << "#{docker_tasks.size} docker tasks running"
137
+
138
+ ### Compare consul-registered tasks vs docker tasks
139
+ docker_mesos_ids_no_panteras = docker_inspect.reject { |p| p[:name] =~ /panteras/ }.collect { |d| d[:name] }.compact.reject { |i| i.empty? }
140
+ consul_not_docker = consul_services - docker_mesos_ids_no_panteras
141
+ docker_not_consul = docker_mesos_ids_no_panteras - consul_services
142
+
143
+ if ! consul_not_docker.empty?
144
+ bad_news << "consul tasks not seen in docker: #{consul_not_docker.join(',')}"
145
+ end
146
+
147
+ if ! docker_not_consul.empty?
148
+ bad_news << "docker tasks not seen in consul: #{docker_not_consul.join(',')}"
149
+ end
150
+
151
+ if ! bad_news.empty?
152
+ puts "problems on #{my_fqdn}: #{bad_news.join(' ')}"
153
+ exit config[:error_exit_code]
154
+ end
155
+
156
+ puts good_news.join(' ')
157
+
158
+ __END__
159
+
160
+ ##### MAPPING ###
161
+ CHECKS:
162
+ mesos vs marathon
163
+ mesos vs docker
164
+ docker vs consul
165
+
166
+ consul (api):
167
+ Slave Host, mesos-name (ServiceID) "paasslave46-2:mesos-c71da994-a35b-4dde-9970-d175c04da735:8080"
168
+
169
+ docker inspect:
170
+ container-id (Id) "7caf2037e7870ca4b0c5574a9f5a2415e43999fc83a9269c0360c34007957b2b"
171
+ mesos-name (Name) "mesos-c71da994-a35b-4dde-9970-d175c04da735"
172
+ mesos-task-id (ENV MESOS_TASK_ID) "vehicle-catalog-service.49383b0b-26eb-11e5-8a45-56847afe9799"
173
+
174
+ marathon API:
175
+ mesos-task-id (TaskId): "vehicle-catalog-service.49383b0b-26eb-11e5-8a45-56847afe9799"
176
+
177
+
178
+ mesos API:
179
+ "id": "vehicle-catalog-service.49383b0b-26eb-11e5-8a45-56847afe9799"
180
+
181
+
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'open3'
4
+ require 'socket'
5
+ require 'optparse'
6
+
7
+ $VERSION = '1.0.0'
8
+ $SEND_NSCA='/usr/sbin/send_nsca'
9
+ $SCRIPT='/usr/local/bin/panteras_api'
10
+ my_datacenter=Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address.split('.')[1]
11
+ $MESOS_MASTER="paasmaster#{my_datacenter.strip}-1.mobile.rz"
12
+ $NAGIOS_SERVICE='paas_consistency_check'
13
+
14
+ # default nagios host
15
+ defaults = { :nsca_host => 'monitor.mobile.rz', :debug => false, :error_code => 1 }
16
+ options = {}
17
+
18
+ OptionParser.new("Options: #{$0} -n NAGIOS_HOST [options]") do |opts|
19
+
20
+ opts.release = $VERSION
21
+
22
+ opts.on("-n","--nsca-host NAGIOS_HOST","") do |n|
23
+ options[:nsca_host] = n
24
+ end
25
+
26
+ opts.on("-d","--debug","") do |d|
27
+ options[:debug] = true
28
+ end
29
+
30
+ opts.on("-c", "--critical", "Exit with CRITICAL when problems seen. Default is WARNING") do |c|
31
+ options[:error_code] = 2
32
+ end
33
+
34
+ end.parse!
35
+
36
+ config = defaults.merge!(options)
37
+
38
+
39
+ $DEBUG = config[:debug]
40
+ my_hostname = Socket.gethostname.strip
41
+ my_fqdn = Socket.gethostbyname(my_hostname).first
42
+ nsca_host = config[:nsca_host]
43
+ output = []
44
+ error = []
45
+ puts "DEBUG MODE ON" if $DEBUG
46
+ puts "NSCA host set to: #{nsca_host}" if $DEBUG
47
+
48
+ def send_ncsa(nagios_host, host, result, code)
49
+ output = []
50
+ error = []
51
+ command="#{$SEND_NSCA} -c /etc/send_nsca.cfg -H #{nagios_host}"
52
+ puts "Running command: #{command}" if $DEBUG
53
+ Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
54
+
55
+ result_line = "#{host}\t#{$NAGIOS_SERVICE}\t#{code}\t#{result}"
56
+ puts "Sending result to #{nagios_host}: #{result_line}" if $DEBUG
57
+ stdin.puts result_line
58
+ stdin.close
59
+
60
+ exit_status = wait_thr.value
61
+
62
+ unless exit_status.success?
63
+ $stderr.puts stderr.readlines
64
+ raise StandardError, "Command '#{command}' returned status #{exit_status.exitstatus}"
65
+ end
66
+
67
+ while line = stderr.gets
68
+ if line.strip.length > 0
69
+ puts "Error (send_ncsa): #{line}" if $DEBUG
70
+ error << line.strip
71
+ end
72
+ end
73
+
74
+ while line = stdout.gets
75
+ if line.strip.length > 0
76
+ puts "Output (send ncsa): #{line}" if $DEBUG
77
+ output << line.strip
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ puts output.join if ( ! output.empty? && $DEBUG )
84
+ puts error.join if ( ! error.empty? && $DEBUG )
85
+
86
+ end
87
+
88
+ command = "#{$SCRIPT} -m #{$MESOS_MASTER}"
89
+ puts "Running command: #{command}" if $DEBUG
90
+
91
+
92
+ Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
93
+
94
+ exit_status = wait_thr.value
95
+ unless exit_status.success?
96
+ send_ncsa(nsca_host, my_hostname, stderr.readlines, config[:error_code])
97
+ raise StandardError, "Command '#{command}' returned status #{exit_status.exitstatus}"
98
+ end
99
+
100
+ while line = stderr.gets
101
+ if line.strip.length > 0
102
+ puts "Error: #{line}" if $DEBUG
103
+ error << line.strip
104
+ end
105
+ end
106
+
107
+ while line = stdout.gets
108
+ if line.strip.length > 0
109
+ puts "Output: #{line}" if $DEBUG
110
+ output << line.strip
111
+ end
112
+ end
113
+
114
+ if ! error.empty?
115
+ error_msg = error.join
116
+ puts "#{my_hostname}: #{error_msg} exit_code #{config[:error_code]}"
117
+ send_ncsa(nsca_host, my_hostname, error.join, config[:error_code])
118
+ else
119
+ output_msg = output.join
120
+ puts "#{my_hostname}: #{output_msg} exit_code #{config[:error_code]}"
121
+ send_ncsa(nsca_host, my_hostname, output.join, 0)
122
+ end
123
+
124
+ end
@@ -0,0 +1,11 @@
1
+ require "panteras_api/version"
2
+ require "panteras_api/utils"
3
+ require "panteras_api/http_utils"
4
+ require "panteras_api/mesos_cluster"
5
+ require "panteras_api/marathon_endpoint"
6
+ require "panteras_api/consul_cluster"
7
+ require "panteras_api/docker_host"
8
+
9
+ module PanterasApi
10
+ # Your code goes here...
11
+ end
@@ -0,0 +1,45 @@
1
+ class ConsulCluster
2
+ include HTTPUtils
3
+ include Utils
4
+
5
+ def initialize(host,port=8500)
6
+ @host = host
7
+ @port = port
8
+ @datacenters = datacenters
9
+ @dc = ""
10
+ @hostname = Utils.hostname
11
+ end
12
+
13
+ def dc=(datacenter)
14
+ raise ArgumentError, "#{datacenter} is not a valid datacenter" unless (@datacenters.include? datacenter)
15
+ @dc = datacenter
16
+ end
17
+
18
+ def datacenters
19
+ to_j(get_response_with_redirect(@host, '/v1/catalog/datacenters', @port))
20
+ end
21
+
22
+ def services
23
+ service_names.collect { |s| service s.to_s }
24
+ end
25
+
26
+ def service_names
27
+ services = to_j(get_response_with_redirect(@host, '/v1/catalog/services?dc=' + @dc, @port)).keys
28
+ # exclude the consul service itself
29
+ services.reject { |s| s.to_s == "consul" }
30
+ end
31
+
32
+ def my_services(hostname=@hostname)
33
+ raise ArgumentError, "missing hostname argument", caller if hostname.nil?
34
+ services.collect { |s| s.select { |i| i[:Node] =~ /^#{hostname}$/ } }.flatten
35
+ end
36
+
37
+ def my_service_ids
38
+ my_services.collect { |s| s[:ServiceID].split(/:/)[1] }
39
+ end
40
+
41
+ def service(service)
42
+ to_j(get_response_with_redirect(@host, '/v1/catalog/service/' + service + '?dc=' + @dc, @port))
43
+ end
44
+
45
+ end
@@ -0,0 +1,77 @@
1
+ require 'open3'
2
+
3
+ class DockerHost
4
+ extend HTTPUtils
5
+
6
+ def self.ps
7
+ self.command("docker ps")[:output]
8
+ end
9
+
10
+ def self.running_containers
11
+ output = self.command("docker ps")[:output]
12
+ raise MesosConsulCommandError, "Command 'docker ps' did not return an array of strings" if (output.class != Array || output.length == 0)
13
+
14
+ result = output.map.with_index do |line, i|
15
+ next if i == 0
16
+ container_id, image, *center, name = line.split
17
+ { container_id: container_id, name: name, image: image }
18
+ end
19
+
20
+ return result.compact
21
+ end
22
+
23
+ def self.inspect(*keys)
24
+ containers = self.running_containers
25
+ containers.map do |c|
26
+ inspect = self.command("docker inspect #{c[:container_id]}")[:output]
27
+ h = to_j(inspect.join).first
28
+ task_id = h[:Config][:Env].select { |env| env =~ /MESOS_TASK_ID=/ }.first
29
+ mesos_task_id = task_id.nil? ? '' : task_id.split(/=/)[1]
30
+ { id: h[:Id], image: h[:Image], name: h[:Name][1..-1], mesos_task_id: mesos_task_id }
31
+ end
32
+ end
33
+
34
+ def self.inspect_partial
35
+ self.inspect
36
+ end
37
+
38
+ private
39
+
40
+ class MesosConsulCommandError < StandardError ; end
41
+
42
+ def self.command(command)
43
+ output = []
44
+ error = []
45
+ begin
46
+ Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
47
+
48
+ exit_status = wait_thr.value
49
+
50
+ unless exit_status.success?
51
+ $stderr.puts stderr.readlines
52
+ raise MesosConsulCommandError, "Command '#{command}' returned status #{exit_status.exitstatus}"
53
+ end
54
+
55
+ while line = stderr.gets
56
+ $stderr.puts line
57
+ if line.strip.length > 0
58
+ error << line.strip
59
+ end
60
+ end
61
+
62
+ while line = stdout.gets
63
+ if line.strip.length > 0
64
+ output << line.strip
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ rescue Errno::ENOENT => e
71
+ raise MesosConsulCommandError, "Error while running command #{command}: #{e.message}"
72
+ end
73
+
74
+ return { :output => output, :error => error}
75
+
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ module HTTPUtils
6
+
7
+ def get_response_with_redirect(*args)
8
+ response = Net::HTTP.get_response(*args)
9
+ if response.is_a?(Net::HTTPRedirection)
10
+ begin
11
+ redirect_uri = URI.parse(response.header['location'])
12
+ return redirect_uri
13
+ rescue SocketError => e
14
+ raise e, "Error connecting to #{redirect_uri}: #{e.message}"
15
+ end
16
+ end
17
+ response
18
+ end
19
+
20
+ def to_j(response)
21
+ if response.is_a? Net::HTTPResponse
22
+ JSON.parse(response.body, :symbolize_names => true)
23
+ elsif response.is_a? String
24
+ JSON.parse(response, :symbolize_names => true)
25
+ end
26
+ end
27
+
28
+ def valid_url(url)
29
+ if (url =~ /\A#{URI::regexp}\z/)
30
+ return true
31
+ end
32
+ return false
33
+ end
34
+
35
+ def construct_uri(url)
36
+ raise Error::BadURLError unless (valid_url(url))
37
+ return URI.parse(url)
38
+ end
39
+
40
+ end
@@ -0,0 +1,40 @@
1
+ class MarathonEndpoint
2
+ include HTTPUtils
3
+ include Utils
4
+
5
+ def initialize(host, port=8080)
6
+ @host = host
7
+ @port = port
8
+ end
9
+
10
+ def app(app_name)
11
+ raise ArgumentError, "Argument be a String" unless (app_name.class == String )
12
+ to_j(get_response_with_redirect(@host, '/v2/apps/' + app_name, @port))[:app]
13
+ end
14
+
15
+ def all_apps
16
+ to_j(get_response_with_redirect(@host, '/v2/apps/', @port))[:apps]
17
+ end
18
+
19
+ def app_names
20
+ all_apps.collect { |a| a[:id][1..-1] }
21
+ end
22
+
23
+ def tasks_ids(app_name)
24
+ app(app_name)[:tasks].collect { |t| t[:id] }
25
+ end
26
+
27
+ def my_tasks(hostname)
28
+ raise ArgumentError, "missing hostname argument", caller if hostname.nil?
29
+ app_names.collect { |n| app(n)[:tasks].select { |t| t[:host] =~ /^#{hostname}$/ } }
30
+ end
31
+
32
+ def my_task_ids(hostname)
33
+ my_tasks(hostname).collect { |t| t.collect { |a| a[:id] } }.flatten
34
+ end
35
+
36
+ def task_ids
37
+ app_names.collect { |a| tasks_ids(a) }
38
+ end
39
+
40
+ end
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'uri'
4
+ require 'json'
5
+
6
+ class MesosCluster
7
+ include HTTPUtils
8
+ include Utils
9
+
10
+ attr_reader :state
11
+
12
+ def initialize(host, port=5050)
13
+ @host = host
14
+ @port = port
15
+ @master_info = "/master/redirect"
16
+ @state_info = "/state.json"
17
+ @state = parse_state
18
+ end
19
+
20
+ def parse_state
21
+ redirect_response = get_response_with_redirect(@host, @master_info, @port)
22
+
23
+ if redirect_response.is_a?(URI::HTTP)
24
+ if ! valid_url(redirect_response.to_s)
25
+ raise StandardError, "Response from http://#{@host}:#{@port}#{@master_info} is not a valid url: #{redirect_response.to_s}"
26
+ end
27
+ return to_j(get_response_with_redirect(URI.join(redirect_response.to_s, @state_info)))
28
+ end
29
+
30
+ end
31
+
32
+ def master_hostname
33
+ state[:hostname]
34
+ end
35
+
36
+ def master_id
37
+ state[:id]
38
+ end
39
+
40
+ def frameworks
41
+ state[:frameworks]
42
+ end
43
+
44
+ def tasks_completed
45
+ state[:frameworks].collect { |f| f[:completed_tasks].collect { |t| t } }
46
+ end
47
+
48
+ def tasks
49
+ results = frameworks.collect { |f| f[:tasks].collect { |t| t[:slave_hostname] = slave_hostname_by_id(t[:slave_id]) ; t } }
50
+ results.reject { |r| r.nil? or r.length == 0 }.first
51
+ end
52
+
53
+ def task_ids
54
+ tasks.collect { |t| t[:id] }
55
+ end
56
+
57
+ def my_tasks_ids(hostname)
58
+ raise ArgumentError, "missing hostname argument", caller if hostname.nil?
59
+ tasks.select { |t| t[:slave_hostname] =~ /^#{hostname}$/ }.collect { |t| t[:id] }
60
+ end
61
+
62
+ def resources
63
+ state[:frameworks].collect { |f| f[:used_resources] }
64
+ end
65
+
66
+ def slave_hostname_by_id(id)
67
+ slaves.select { |s| s[:id] == id }.first[:hostname]
68
+ end
69
+
70
+ def slaves
71
+ state[:slaves].collect do |s|
72
+ s.select { |k,v| [:hostname, :id].include?(k)}
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ JSON.pretty_generate @state
78
+ end
79
+
80
+ end
@@ -0,0 +1,17 @@
1
+ require 'socket'
2
+
3
+ module Utils
4
+ def self.hostname
5
+ Socket.gethostname.strip
6
+ end
7
+
8
+ def self.fully_qualified_hostname
9
+ begin
10
+ Socket.gethostbyname(Socket.gethostname.strip).first
11
+ rescue SocketError => e
12
+ raise StandardError, "Could not get fully qualified domain name using gethostbyname."
13
+ end
14
+ end
15
+
16
+
17
+ end
@@ -0,0 +1,3 @@
1
+ module PanterasApi
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'panteras_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "panteras_api"
8
+ spec.version = PanterasApi::VERSION
9
+ spec.authors = ["Jonathan Colby"]
10
+ spec.email = ["jcolby@team.mobile.de"]
11
+
12
+ spec.summary = %q{Ruby API library for Panteras PaaS platform.}
13
+ spec.description = %q{A convenient api for getting information from consul, mesos, marathon, and docker in the Panteras PaaS infrastructure.}
14
+ spec.homepage = "https://github.com/joncolby/panteras_api"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ # spec.bindir = "bin"
18
+ # spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ # if spec.respond_to?(:metadata)
22
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
23
+ # end
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.8"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: panteras_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Colby
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: A convenient api for getting information from consul, mesos, marathon,
42
+ and docker in the Panteras PaaS infrastructure.
43
+ email:
44
+ - jcolby@team.mobile.de
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".rspec"
51
+ - ".travis.yml"
52
+ - Gemfile
53
+ - README.md
54
+ - Rakefile
55
+ - extra/mesos_consul_consistency_check
56
+ - extra/mesos_consul_consistency_check_nagios_wrapper
57
+ - lib/panteras_api.rb
58
+ - lib/panteras_api/consul_cluster.rb
59
+ - lib/panteras_api/docker_host.rb
60
+ - lib/panteras_api/http_utils.rb
61
+ - lib/panteras_api/marathon_endpoint.rb
62
+ - lib/panteras_api/mesos_cluster.rb
63
+ - lib/panteras_api/utils.rb
64
+ - lib/panteras_api/version.rb
65
+ - panteras_api.gemspec
66
+ homepage: https://github.com/joncolby/panteras_api
67
+ licenses: []
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.4.5
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Ruby API library for Panteras PaaS platform.
89
+ test_files: []