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.
@@ -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