comap 1.0.0.pre.a

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,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: []