circactivator 2.1.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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +203 -0
- data/README.md +107 -0
- data/bin/circactivator +47 -0
- data/circactivator.gemspec +31 -0
- data/lib/circactivator.rb +40 -0
- data/lib/circactivator/checkupdater.rb +93 -0
- data/lib/circactivator/cli.rb +98 -0
- data/lib/circactivator/log.rb +29 -0
- data/lib/circactivator/monitoring.rb +46 -0
- data/lib/circactivator/version.rb +21 -0
- data/spec/checkupdater_spec.rb +138 -0
- data/spec/cli_spec.rb +119 -0
- data/spec/monitoring_spec.rb +50 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/fixtures/config/config.yml +20 -0
- data/spec/support/webmock/lbvserver_check_bundle.json +126 -0
- metadata +183 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'circactivator/checkupdater'
|
20
|
+
require 'circactivator/cli'
|
21
|
+
require 'circactivator/log'
|
22
|
+
require 'circactivator/monitoring'
|
23
|
+
require 'circactivator/version'
|
24
|
+
require 'fileutils'
|
25
|
+
require 'settingslogic'
|
26
|
+
|
27
|
+
module CircActivator
|
28
|
+
class Config < Settingslogic
|
29
|
+
def self.load_from_file(file)
|
30
|
+
source file
|
31
|
+
load!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Exception
|
36
|
+
class CheckNotFound < RuntimeError; end
|
37
|
+
class CirconusError < RuntimeError; end
|
38
|
+
class NoConfigFile < RuntimeError; end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'httparty'
|
20
|
+
require 'json'
|
21
|
+
|
22
|
+
module CircActivator
|
23
|
+
class CheckUpdater
|
24
|
+
|
25
|
+
attr_reader :check_bundle
|
26
|
+
attr_accessor :name_regex
|
27
|
+
|
28
|
+
def initialize(check_bundle_id)
|
29
|
+
@check_bundle_id = check_bundle_id
|
30
|
+
@check_bundle = Hash.new
|
31
|
+
@name_regex = '.*'
|
32
|
+
end
|
33
|
+
|
34
|
+
def http_headers
|
35
|
+
{
|
36
|
+
'X-Circonus-Auth-Token' => CircActivator::Config.circonus.api_key,
|
37
|
+
'X-Circonus-App-Name' => CircActivator::Config.circonus.api_app_name,
|
38
|
+
'Accept' => 'application/json'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def url
|
43
|
+
CircActivator::Config.circonus.base_url + '/check_bundle/' + @check_bundle_id.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def fetch
|
47
|
+
response = HTTParty.get(url + '?query_broker=1', headers: http_headers, verify: false)
|
48
|
+
raise_exceptions!(response)
|
49
|
+
@check_bundle = JSON.load(response.body)
|
50
|
+
end
|
51
|
+
|
52
|
+
def activate_metrics
|
53
|
+
return if @check_bundle['metrics'].nil?
|
54
|
+
updated_metrics = Array.new
|
55
|
+
@check_bundle['metrics'].select {
|
56
|
+
|metric| metric['status'] == 'available' && metric['name'] =~ /#{self.name_regex}/
|
57
|
+
}.each do |metric|
|
58
|
+
updated_metrics << metric['name']
|
59
|
+
metric['status'] = 'active'
|
60
|
+
end
|
61
|
+
updated_metrics
|
62
|
+
end
|
63
|
+
|
64
|
+
def payload_hash
|
65
|
+
@check_bundle.select { |k,v| k =~ /brokers|config|display_name|metrics|notes|period|status|tags|target|timeout|type/ }
|
66
|
+
end
|
67
|
+
|
68
|
+
def update
|
69
|
+
response = HTTParty.put(url, headers: http_headers, body: payload_hash.to_json, verify: false)
|
70
|
+
raise_exceptions!(response)
|
71
|
+
end
|
72
|
+
|
73
|
+
def raise_exceptions!(response)
|
74
|
+
case response.code.to_s
|
75
|
+
when /^2/
|
76
|
+
return
|
77
|
+
when '404'
|
78
|
+
raise CircActivator::Exception::CheckNotFound, "Check bundle ID #{@check_bundle_id} not found"
|
79
|
+
else
|
80
|
+
raise CircActivator::Exception::CirconusError,
|
81
|
+
"Server error when handling check bundle #{@check_bundle_id}: #{response.body}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def run(logging=false)
|
86
|
+
fetch
|
87
|
+
updated_metrics = activate_metrics
|
88
|
+
update
|
89
|
+
|
90
|
+
updated_metrics
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'thor'
|
20
|
+
|
21
|
+
module CircActivator
|
22
|
+
class CLI < Thor
|
23
|
+
|
24
|
+
class_option :debug, :type => :boolean
|
25
|
+
|
26
|
+
def initialize(*args)
|
27
|
+
super
|
28
|
+
CircActivator::Log.create(options[:debug])
|
29
|
+
@monitoring = CircActivator::Monitoring.new
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'update_group GROUP', 'Activate metrics for each check_bundle configured in GROUP'
|
33
|
+
def update_group(group)
|
34
|
+
unless CircActivator::Config['check_bundles'].include?(group)
|
35
|
+
CircActivator::Log.fatal("Site #{group} not found in the configuration.")
|
36
|
+
exit(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
CircActivator::Config['check_bundles'][group].each do |check_bundle_id, regex|
|
40
|
+
run_updater(group, check_bundle_id, regex)
|
41
|
+
end
|
42
|
+
|
43
|
+
exit_handler
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'update_all_groups', 'Activate all metrics for all check bundles in the configuration'
|
47
|
+
def update_all_groups
|
48
|
+
CircActivator::Config.check_bundles.each do |group, check_bundles|
|
49
|
+
check_bundles.each do |check_bundle_id, regex|
|
50
|
+
run_updater(group, check_bundle_id, regex)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
exit_handler
|
55
|
+
end
|
56
|
+
|
57
|
+
desc 'update_check_bundle CHECK_BUNDLE_ID [REGEX]', 'Activates all metrics for an arbitrary check bundle, activating only the metrics whose name match the optional regex'
|
58
|
+
def update_check_bundle(check_bundle_id, regex=nil)
|
59
|
+
run_updater('no_group', check_bundle_id, regex)
|
60
|
+
exit_handler
|
61
|
+
end
|
62
|
+
|
63
|
+
no_commands do
|
64
|
+
def run_updater(group, check_bundle_id, regex)
|
65
|
+
CircActivator::Log.info("Starting update for group #{group}, check bundle #{check_bundle_id}")
|
66
|
+
|
67
|
+
begin
|
68
|
+
updater = CircActivator::CheckUpdater.new(check_bundle_id)
|
69
|
+
updater.name_regex = regex unless regex.nil?
|
70
|
+
updated_metrics = updater.run(options[:debug])
|
71
|
+
rescue => e
|
72
|
+
log_error("Error updating #{check_bundle_id}: #{e.class}: #{e.message}")
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
if updated_metrics.length > 0
|
77
|
+
CircActivator::Log.info("Update for group #{group}, check bundle #{check_bundle_id} complete. Metrics added: #{updated_metrics.join(', ')}")
|
78
|
+
else
|
79
|
+
CircActivator::Log.info("Update for group #{group}, check bundle #{check_bundle_id} complete. No metrics updated.")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def log_error(msg)
|
84
|
+
CircActivator::Log.error(msg)
|
85
|
+
@monitoring.add_error_message(msg)
|
86
|
+
end
|
87
|
+
|
88
|
+
def exit_handler
|
89
|
+
@monitoring.set_error_file unless options[:debug]
|
90
|
+
if @monitoring.errors?
|
91
|
+
Kernel.exit(2)
|
92
|
+
else
|
93
|
+
Kernel.exit(0)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'mixlib/log'
|
20
|
+
|
21
|
+
module CircActivator
|
22
|
+
module Log
|
23
|
+
extend Mixlib::Log
|
24
|
+
def self.create(stdout=false)
|
25
|
+
init(CircActivator::Config.log.file, CircActivator::Config.log.count) unless stdout
|
26
|
+
level(CircActivator::Config.log.level.to_sym)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module CircActivator
|
20
|
+
class Monitoring
|
21
|
+
|
22
|
+
attr_reader :error_messages
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@error_messages = Array.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_error_message(msg)
|
29
|
+
@error_messages << msg
|
30
|
+
end
|
31
|
+
|
32
|
+
def errors?
|
33
|
+
@error_messages.length > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_error_file
|
37
|
+
if errors?
|
38
|
+
File.write(CircActivator::Config.monitoring.error_file, @error_messages.join("\n"))
|
39
|
+
true
|
40
|
+
else
|
41
|
+
FileUtils.rm_f(CircActivator::Config.monitoring.error_file)
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module CircActivator
|
20
|
+
VERSION = '2.1.0'
|
21
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Leff (<aleff@webmd.net>)
|
3
|
+
# Copyright:: Copyright (c) 2015 WebMD, LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'spec_helper'
|
20
|
+
|
21
|
+
describe CircActivator::CheckUpdater do
|
22
|
+
let(:check_bundle_id) { 123 }
|
23
|
+
let(:updated_metrics_all) {
|
24
|
+
%w( vserver1:80`actsvcs
|
25
|
+
vserver1:80`curclntconnections
|
26
|
+
vserver1:80`cursrvrconnections
|
27
|
+
vserver1:80`inactsvcs
|
28
|
+
vserver1:80`totalrequestbytes
|
29
|
+
vserver1:80`totalrequests
|
30
|
+
vserver1:80`totalresponsebytes
|
31
|
+
vserver1:80`totalresponses
|
32
|
+
vserver1:80`tothits
|
33
|
+
vserver1:80`vslbhealth
|
34
|
+
)
|
35
|
+
}
|
36
|
+
let(:updated_metrics_with_regex) {
|
37
|
+
%w( vserver1:80`actsvcs
|
38
|
+
vserver1:80`inactsvcs
|
39
|
+
)
|
40
|
+
}
|
41
|
+
|
42
|
+
before(:each) do
|
43
|
+
@checkupdater = CircActivator::CheckUpdater.new(check_bundle_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns http headers' do
|
47
|
+
expect(@checkupdater.http_headers['X-Circonus-Auth-Token']).to eq('a12345')
|
48
|
+
expect(@checkupdater.http_headers['X-Circonus-App-Name']).to eq('my_app')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns a check bundle URL' do
|
52
|
+
expect(@checkupdater.url).to eq("https://api.circonus.com/v2/check_bundle/123")
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises an exception if the check is not found' do
|
56
|
+
stub_request(:get, format_webmock_url("/check_bundle/#{check_bundle_id}?query_broker=1")).to_return(status: 404)
|
57
|
+
expect { @checkupdater.fetch }.to raise_error(CircActivator::Exception::CheckNotFound)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises an exception if the status code is not 200 or 404' do
|
61
|
+
stub_request(:get, format_webmock_url("/check_bundle/#{check_bundle_id}?query_broker=1")).to_return(status: 500)
|
62
|
+
expect { @checkupdater.fetch }.to raise_error(CircActivator::Exception::CirconusError)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'activate_metrics returns nil when check_bundle is empty' do
|
66
|
+
expect(@checkupdater.activate_metrics).to eq(nil)
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'check with data' do
|
70
|
+
before(:each) do
|
71
|
+
stub_request(:get, format_webmock_url("/check_bundle/#{check_bundle_id}?query_broker=1")).
|
72
|
+
to_return(:status => 200, :body => File.read(webmock_sample_file('lbvserver_check_bundle.json')))
|
73
|
+
stub_request(:put, format_webmock_url("/check_bundle/#{check_bundle_id}")).to_return(status: 201)
|
74
|
+
|
75
|
+
@checkupdater = CircActivator::CheckUpdater.new(check_bundle_id)
|
76
|
+
@checkupdater.fetch
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'fetches a check' do
|
80
|
+
expect(@checkupdater.check_bundle.size).to be > 0
|
81
|
+
expect(@checkupdater.check_bundle.include?('_cid')).to eq(true)
|
82
|
+
expect(@checkupdater.check_bundle.include?('metrics')).to eq(true)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'activates the appropriate list of metrics - .* regex as default' do
|
86
|
+
expect(@checkupdater.activate_metrics.sort).to eq(updated_metrics_all.sort)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'activates the appropriate list of metrics - svcs regex' do
|
90
|
+
@checkupdater.name_regex = 'svcs'
|
91
|
+
expect(@checkupdater.activate_metrics.sort).to eq(updated_metrics_with_regex.sort)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns a payload hash containing the necessary fields' do
|
95
|
+
expect(@checkupdater.payload_hash.keys.sort).to eq([
|
96
|
+
'brokers',
|
97
|
+
'config',
|
98
|
+
'display_name',
|
99
|
+
'metrics',
|
100
|
+
'notes',
|
101
|
+
'period',
|
102
|
+
'status',
|
103
|
+
'tags',
|
104
|
+
'target',
|
105
|
+
'timeout',
|
106
|
+
'type'
|
107
|
+
])
|
108
|
+
expect(@checkupdater.payload_hash['metrics'].length).to be > 0
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'updates the check' do
|
112
|
+
expect { @checkupdater.update }.to_not raise_error
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'calls the right methods during the run' do
|
116
|
+
expect(@checkupdater).to receive(:fetch)
|
117
|
+
expect(@checkupdater).to receive(:activate_metrics).and_return(['metric1', 'metric2'])
|
118
|
+
expect(@checkupdater).to receive(:update)
|
119
|
+
@checkupdater.run
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'does not call update when no metrics are returned' do
|
123
|
+
expect(@checkupdater).to receive(:activate_metrics).and_return([])
|
124
|
+
expect(@checkupdater).to_not receive(:update)
|
125
|
+
@checkupdater.run
|
126
|
+
end
|
127
|
+
|
128
|
+
it "#run does not debug log if logging is disabled (disabled by default)" do
|
129
|
+
expect(CircActivator::Log).to_not receive(:debug)
|
130
|
+
@checkupdater.run
|
131
|
+
end
|
132
|
+
|
133
|
+
it '#run debug logs when logging is enabled' do
|
134
|
+
expect(CircActivator::Log).to receive(:debug).exactly(3).times
|
135
|
+
@checkupdater.run(logging=true)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|