comap 1.0.0.pre.a

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-2018 Make.org
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'faraday'
19
+ require 'openssl'
20
+
21
+ # Main module
22
+ module COMAP
23
+ # Rack App
24
+ class App
25
+ # rubocop:disable Metrics/ParameterLists
26
+ def initialize(
27
+ docker_host: 'http://127.0.0.1',
28
+ docker_port: '2375',
29
+ docker_ssl: '',
30
+ metrics_path: '',
31
+ network: '',
32
+ services: []
33
+ )
34
+ raise ArgumentError, 'No services given' if services.empty?
35
+ raise ArgumentError, 'No network given' if network.nil?
36
+ ssl_hash = parse_ssl(docker_ssl)
37
+ @client = DockerClient.new(docker_host, docker_port, ssl_hash)
38
+ @network = network
39
+ @services = services
40
+ @metrics_path = metrics_path
41
+ error_msg = 'Could not retrieve Docker API version from '\
42
+ "#{docker_host}:#{docker_port} ssl:#{docker_ssl}"
43
+ raise StandardError, error_msg if @client.call('version').nil?
44
+ end
45
+ # rubocop:enable Metrics/ParameterLists
46
+
47
+ def call(_env)
48
+ ['200', { 'Content-Type' => 'text/plain' }, [body]]
49
+ end
50
+
51
+ private
52
+
53
+ def body
54
+ @services.map do |s|
55
+ service, port = s.split(':')
56
+ service_metrics(service, port)
57
+ end.compact.map(&flat_hash).join
58
+ end
59
+
60
+ def service_metrics(service, port)
61
+ containers(service).sort_by { |c| c['slot'] }.map do |container|
62
+ next puts "#{service} service not found" if container.empty?
63
+ url = "http://#{container['ip']}:#{port}/#{@metrics_path}"
64
+ container_metrics(url, service, container['slot'])
65
+ end.reduce(&merge_proc)
66
+ end
67
+
68
+ def container_metrics(url, service, slot)
69
+ data = scrape_metrics(url, service)
70
+ return {} if data.nil?
71
+ data.lines.each_with_object({}) do |value, hash|
72
+ next hash[value] = nil if value.start_with?('#')
73
+ (hash[hash.keys.last] ||= []) << add_label(value, "#{service}.#{slot}")
74
+ end
75
+ end
76
+
77
+ def scrape_metrics(url, service)
78
+ Faraday.get(url) do |req|
79
+ req.options.timeout = 2
80
+ req.options.open_timeout = 1
81
+ end.body
82
+ rescue StandardError
83
+ puts "Could not connect to #{service} endpoint #{url}"
84
+ end
85
+
86
+ def add_label(line, label)
87
+ replace = %(container="#{label}")
88
+ line.gsub(/ /, "{#{replace}} ").gsub(/}{/, ',')
89
+ end
90
+
91
+ def containers(service)
92
+ running_tasks(service).map do |task|
93
+ {
94
+ 'slot' => task['Slot'],
95
+ 'ip' => ip(task).split('/').first
96
+ }
97
+ end.uniq
98
+ end
99
+
100
+ def running_tasks(service)
101
+ filters = {
102
+ 'service' => { service => true },
103
+ 'desired-state' => { 'running' => true }
104
+ }
105
+ @client.call('tasks', filters).keep_if do |task|
106
+ task['Status']['State'] == 'running'
107
+ end
108
+ end
109
+
110
+ def ip(task)
111
+ task['NetworksAttachments'].select do |h|
112
+ h['Network']['Spec']['Name'] == @network
113
+ end.first['Addresses'].first
114
+ end
115
+
116
+ # helpers
117
+
118
+ def parse_ssl(docker_ssl)
119
+ docker_ssl.split(',').map do |opt|
120
+ k, v = opt.split('=')
121
+ if k.include?('cert')
122
+ v = OpenSSL::X509::Certificate.new(File.read(v))
123
+ elsif k.include? 'key'
124
+ v = OpenSSL::PKey::RSA.new(File.read(v))
125
+ end
126
+ [k, v]
127
+ end.to_h
128
+ end
129
+
130
+ def merge_proc
131
+ proc do |old, new|
132
+ old.merge(new) { |_, value1, value2| value1 + value2 }
133
+ end
134
+ end
135
+
136
+ def flat_hash
137
+ proc do |item|
138
+ item.map { |key, value| "#{key}#{value.join}" }
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-2018 Make.org
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'faraday'
19
+ require 'json'
20
+
21
+ # Module Docker
22
+ module COMAP
23
+ # Simple Engine API client
24
+ class DockerClient
25
+ def initialize(url, port, ssl = {})
26
+ @conn = Faraday.new(url: "#{url}:#{port}", ssl: ssl) do |faraday|
27
+ faraday.options.params_encoder = Faraday::FlatParamsEncoder
28
+ faraday.adapter Faraday.default_adapter
29
+ end
30
+ end
31
+
32
+ def call(route, filters = {})
33
+ response = @conn.get(route) do |req|
34
+ req.params['filters'] = filters.to_json
35
+ end
36
+ JSON.parse(response.body)
37
+ rescue StandardError => e
38
+ puts "Could not connect to Docker API #{@conn.url_prefix}"
39
+ puts e.message
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-2018 Make.org
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ # Define version
19
+ module COMAP
20
+ VERSION = '1.0.0-a'
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ name 'comap-test'
4
+ maintainer 'Make.org'
5
+ maintainer_email 'sre@make.org'
6
+ license 'Apache-2.0'
7
+ description 'Integration tests for Comap'
8
+ long_description 'Integration tests for Comap'
9
+ source_url 'https://gitlab.com/sre-gems/comap'
10
+ issues_url 'https://gitlab.com/sre-gems/comap/issues'
11
+ version '1.0.0'
12
+
13
+ chef_version '>= 13.0'
14
+
15
+ supports 'centos', '>= 7.4'
16
+
17
+ depends 'docker-platform'
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018 Make.org
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ # Build comap docker image and launch it
19
+ directory '/root/pkg'
20
+
21
+ cookbook_file '/root/Dockerfile' do
22
+ source 'Dockerfile'
23
+ notifies :run, 'execute[docker build comap]', :delayed
24
+ end
25
+
26
+ cookbook_file '/root/pkg/comap.gem' do
27
+ source 'comap.gem'
28
+ notifies :run, 'execute[docker build comap]', :delayed
29
+ end
30
+
31
+ execute 'docker build comap' do
32
+ command 'docker build . -t makeorg/comap'
33
+ cwd '/root'
34
+ action :nothing
35
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018 Make.org
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ # Create secrets
19
+ dir = '/root/.docker'
20
+ %w[ca.pem cert.pem key.pem].each do |secret|
21
+ execute "docker secret create #{secret} #{dir}/#{secret}" do
22
+ not_if "docker secret ls | grep #{secret}"
23
+ end
24
+ end
25
+
26
+ metrics_content = <<~CONTENT
27
+ # TYPE metrics_sample_seconds gauge
28
+ metrics_sample_seconds 42
29
+ # TYPE metrics_sample_2_seconds gauge
30
+ metrics_sample_2_seconds 24
31
+ CONTENT
32
+
33
+ docker_platform_service 'metrics' do
34
+ options(
35
+ mode: 'replicated',
36
+ replicas: 2,
37
+ network: 'kitchen',
38
+ publish: '5678:5678'
39
+ )
40
+ image 'hashicorp/http-echo'
41
+ command "-text '#{metrics_content.chomp("\n")}'"
42
+ action :create
43
+ end
44
+
45
+ server = 'https://comap-swarm-manager-centos-7'
46
+ ssl = {
47
+ 'verify' => false, # https://github.com/berkshelf/berkshelf/issues/1458
48
+ 'client_cert' => '/run/secrets/cert.pem',
49
+ 'client_key' => '/run/secrets/key.pem',
50
+ 'ca_file' => '/run/secrets/ca.pem'
51
+ }
52
+ ssl_opt = ssl.map { |k, v| "#{k}=#{v}" }.join(',')
53
+ puts "-H #{server} -p 2376 -s #{ssl_opt} -n kitchen metrics:5678"
54
+
55
+ # TODO: give options as an array in docker-platform
56
+ docker_platform_service 'comap' do
57
+ options(
58
+ mode: 'replicated',
59
+ network: 'kitchen',
60
+ publish: '9100:9100',
61
+ secret: %w[cert.pem key.pem ca.pem]
62
+ )
63
+ image 'makeorg/comap'
64
+ command "-H #{server} -p 2376 -s #{ssl_opt} -n kitchen metrics:5678"
65
+ action :create
66
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: comap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.a
5
+ platform: ruby
6
+ authors:
7
+ - Sylvain Arrambourg
8
+ - Samuel Bernard
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-02-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.16'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.16'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '12.2'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '12.2'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.51'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.51'
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: faraday
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0.13'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0.13'
98
+ - !ruby/object:Gem::Dependency
99
+ name: json
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '2.0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '2.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: thin
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1.7'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '1.7'
126
+ description: Container Orchestrator Metrics Aggregator for Prometheus
127
+ email:
128
+ - sar@make.org
129
+ executables:
130
+ - comap
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".gitlab-ci.yml"
136
+ - ".kitchen.yml"
137
+ - ".rspec"
138
+ - ".ruby-version"
139
+ - ".travis.yml"
140
+ - Berksfile
141
+ - CONTRIBUTING.md
142
+ - Dockerfile
143
+ - Gemfile
144
+ - Gemfile.lock
145
+ - LICENSE
146
+ - README.md
147
+ - Rakefile
148
+ - bin/console
149
+ - bin/setup
150
+ - comap.gemspec
151
+ - exe/comap
152
+ - lib/comap.rb
153
+ - lib/comap/app.rb
154
+ - lib/comap/docker_client.rb
155
+ - lib/comap/version.rb
156
+ - metadata.rb
157
+ - recipes/build_comap.rb
158
+ - recipes/launch_comap.rb
159
+ homepage: https://gitlab.com/sre-gems/comap
160
+ licenses:
161
+ - Apache-2.0
162
+ metadata: {}
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">"
175
+ - !ruby/object:Gem::Version
176
+ version: 1.3.1
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.7.3
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Container Orchestrator Metrics Aggregator for Prometheus
183
+ test_files: []