alert_logic 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ # Module accessor for logger instance
2
+ module AlertLogic
3
+ @logger = nil
4
+
5
+ # Set or return a logger instance
6
+ def self.logger(logger_file = nil)
7
+ if !@logger || logger_file
8
+ if defined?(Chef::Log.logger) && !logger_file
9
+ @logger = Chef::Log.logger
10
+ else
11
+ @logger = Logger.new(logger_file || $stdout)
12
+ @logger.level = Logger::INFO
13
+ end
14
+ end
15
+ @logger
16
+ end
17
+
18
+ # Set logger instance to another instance
19
+ def self.logger=(logger)
20
+ logger.is_a?(String) ? self.logger(logger) : @logger = logger
21
+ @logger
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'alert_logic/resources/base_resource'
2
+ require 'alert_logic/resources/filters'
3
+ require 'alert_logic/resources/appliance'
4
+ require 'alert_logic/resources/policy'
5
+ require 'alert_logic/resources/protected_host'
@@ -0,0 +1,6 @@
1
+ module AlertLogic
2
+ # Generates an Appliance object based on JSON API response.
3
+ class Appliance
4
+ include Resource
5
+ end
6
+ end
@@ -0,0 +1,123 @@
1
+ module AlertLogic
2
+ # Common methods that we'll mix into our resource classes
3
+ module Resource
4
+ include Utils
5
+
6
+ def self.included(klass)
7
+ klass.extend Resource
8
+ end
9
+
10
+ def find_by_id(id)
11
+ resource_type = name.split('::').last
12
+ klass = AlertLogic.const_get(resource_type.to_sym)
13
+ resource = AlertLogic \
14
+ .api_client \
15
+ .retrieve(resource_type.downcase, id) \
16
+ .body \
17
+ .first
18
+ klass.new(resource)
19
+ end
20
+
21
+ def find(*params)
22
+ resource_type = name.split('::').last
23
+ options = params.empty? ? {} : eval_filters(params)
24
+ klass = AlertLogic.const_get(resource_type.to_sym)
25
+ resources = AlertLogic \
26
+ .api_client \
27
+ .list(resource_type.downcase, options) \
28
+ .body
29
+ resources.map! { |resource_hash| klass.new(resource_hash) }
30
+ end
31
+
32
+ def initialize(resource_hash)
33
+ @resource_type = self.class.to_s.downcase.split('::').last
34
+ objectify(resource_hash)
35
+ end
36
+
37
+ def name=(name)
38
+ payload = { @resource_type => { 'name' => name } }
39
+ AlertLogic.api_client.edit(@resource_type, id, payload)
40
+ reload!
41
+ end
42
+
43
+ def tags=(tags)
44
+ msg = 'Tags must be a space separated string'
45
+ fail ClientError, msg unless tags.is_a?(String)
46
+ tags = tags.split.map! { |tag| { 'name' => tag } }
47
+ payload = { @resource_type => { 'tags' => tags } }
48
+ AlertLogic.api_client.edit(@resource_type, id, payload)
49
+ reload!
50
+ end
51
+
52
+ def reload!
53
+ objectify(
54
+ AlertLogic \
55
+ .api_client \
56
+ .list(@resource_type, 'id' => id) \
57
+ .body \
58
+ .first
59
+ )
60
+ end
61
+
62
+ private
63
+
64
+ def eval_filters(params)
65
+ filters = Resource.filters
66
+ unknown_filters = []
67
+ params = params.map do |param|
68
+ if param.is_a?(Symbol)
69
+ if filters.key?(param)
70
+ filters[param]
71
+ else
72
+ unknown_filters << param
73
+ {}
74
+ end
75
+ elsif param.is_a?(Hash)
76
+ param
77
+ else
78
+ {}
79
+ end
80
+ end.reduce(&:merge)
81
+ msg = "Unknown filter(s) passed: #{unknown_filters.inspect}."
82
+ msg << " Valid filters: #{filters.keys.inspect}"
83
+ AlertLogic.logger.warn(msg) unless unknown_filters.empty?
84
+ params
85
+ end
86
+
87
+ # Recursively traverse the hash and create an instance variable and
88
+ # accessor method for each key/value pair.
89
+ def objectify(hash)
90
+ hash.each do |name, value|
91
+ if value.is_a?(Hash)
92
+ value = uniquify_keys(name, value)
93
+ objectify(value)
94
+ else
95
+ def_method(name, value)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Some hash pairs have similar namespaces. We want to rename those to
101
+ # avoid a naming collision.
102
+ def uniquify_keys(name, hash)
103
+ conflicts = /^config$|^appliance$|^created$|
104
+ ^modified$|^config_policy$|^appliance_policy$/
105
+ return hash unless name =~ conflicts
106
+ hash.map { |k, v| { "#{name}_#{k}" => v } }.reduce(&:merge)
107
+ end
108
+
109
+ # create and set an instance variable and define an accessor method if it's
110
+ # not already there.
111
+ def def_method(name, value)
112
+ instance_variable_set("@#{name}", value)
113
+ # Don't overwrite existing methods unless it's id. id isnt fully
114
+ # deprecatedin 1.8.7
115
+ (return true) if respond_to?(name.to_sym) && name != 'id'
116
+ self.class.send(
117
+ :define_method,
118
+ name,
119
+ proc { instance_variable_get("@#{name}") }
120
+ )
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,22 @@
1
+ module AlertLogic
2
+ # Resource Filters
3
+ module Resource
4
+ @filters = nil
5
+ # Resource filters accessor
6
+ def self.filters(filters = nil)
7
+ defaults = {
8
+ :ok => { 'status.status' => 'ok' },
9
+ :online => { 'status.status' => 'ok' },
10
+ :offline => { 'status.status' => 'offline' },
11
+ :error => { 'status.status' => 'error' },
12
+ :windows => { 'metadata.os_type' => 'windows' },
13
+ :linux => { 'metadata.os_type' => 'unix' },
14
+ :unix => { 'metadata.os_type' => 'unix' },
15
+ :appliance_assignment => { 'type' => 'appliance_assignment' },
16
+ :all => {}
17
+ }
18
+ filters && @filters = filters
19
+ @filters ||= defaults
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module AlertLogic
2
+ # Generates an Appliance object based on JSON API response.
3
+ class Policy
4
+ include Resource
5
+ end
6
+ end
@@ -0,0 +1,67 @@
1
+ module AlertLogic
2
+ # AlertLogic ProtectedHost built from a JSON API response.
3
+ class ProtectedHost
4
+ include Resource
5
+
6
+ def appliance_policy_id=(policy_id)
7
+ update_policy('appliance', policy_id)
8
+ end
9
+
10
+ def config_policy_id=(policy_id)
11
+ update_policy('config', policy_id)
12
+ end
13
+
14
+ def appliance?(appliance)
15
+ # sometimes appliance_assigned_to wont exist if the Protected Host isn't
16
+ # already assigned to an appliance.
17
+ if respond_to?(:appliance_assigned_to)
18
+ appliance = find_appliance(appliance) if appliance.is_a?(String)
19
+ appliance.id == appliance_assigned_to
20
+ else
21
+ false
22
+ end
23
+ end
24
+
25
+ def assign_appliance(appliance)
26
+ if appliance?(appliance)
27
+ AlertLogic.logger.info 'Host is already assigned to that Appliance'
28
+ else
29
+ appliance = find_appliance(appliance) if appliance.is_a?(String)
30
+ policy = AlertLogic::Policy \
31
+ .find('type' => 'appliance_assignment') \
32
+ .select { |pol| pol.appliances.any? { |ap| ap == appliance.id } } \
33
+ .first
34
+ self.appliance_policy_id = policy.id
35
+ end
36
+ end
37
+
38
+ def reload!
39
+ [:@appliance_assigned_to,
40
+ :@appliance_connected_to,
41
+ :@appliance_policy_id,
42
+ :@config_policy_id
43
+ ].each do |var|
44
+ remove_instance_variable(var) if instance_variable_defined?(var)
45
+ end
46
+ super
47
+ end
48
+
49
+ private
50
+
51
+ def find_appliance(name)
52
+ msg = "Searching for an appliance with name: #{name}"
53
+ AlertLogic.logger.info msg
54
+ Appliance.find.select { |ap| ap.name == name }.first
55
+ end
56
+
57
+ def update_policy(policy_type, policy_id)
58
+ payload = { 'protectedhost' =>
59
+ { policy_type => { 'policy' => { 'id' => policy_id } } }
60
+ }
61
+ res = AlertLogic.api_client.edit('protectedhost', id, payload)
62
+ AlertLogic.logger.debug res.body.inspect
63
+ reload!
64
+ AlertLogic.logger.info "#{policy_type} policy updated!"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,11 @@
1
+ module AlertLogic
2
+ # common utility methods that are required in multiple classes
3
+ module Utils
4
+ private
5
+
6
+ # simple string pluralizer to translate singular Alert Logic resources
7
+ def pluralize(resource)
8
+ resource =~ /^\w+y$/ ? resource.sub(/y$/, 'ies') : "#{resource}s"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ # Gem version
2
+ module AlertLogic
3
+ VERSION = '0.1.1'
4
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ module AlertLogic
4
+ describe AlertLogic, '.api_client' do
5
+ before(:each) do
6
+ @client = double(:secret_key => '3234', :endpoint => 'https://fake.com')
7
+ AlertLogic.instance_variable_set(:@api_client, @client)
8
+ allow(Client).to receive(:new).and_return(@client)
9
+ end
10
+
11
+ it 'accepts no params and returns a client' do
12
+ AlertLogic.instance_variable_set(:@api_client, nil)
13
+ AlertLogic.api_client.should eq(@client)
14
+ end
15
+
16
+ it 'accepts a non-matching secret_key param and returns a new client' do
17
+ new_key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
18
+ Client.should_receive(:new).with(new_key, nil)
19
+ AlertLogic.api_client(new_key)
20
+ end
21
+
22
+ it 'accepts a matching secret_key param and returns an existing client' do
23
+ AlertLogic.api_client('3234').should eq(@client)
24
+ end
25
+
26
+ it 'accepts a non-matching endpoint param and returns a new client' do
27
+ endpoint = 'https://differentfake.com'
28
+ Client.should_receive(:new).with(nil, endpoint)
29
+ AlertLogic.api_client(nil, endpoint)
30
+ end
31
+
32
+ it 'accepts a matching endpoint param and returns an existing client' do
33
+ AlertLogic.api_client(nil, '3234').should eq(@client)
34
+ end
35
+
36
+ it 'accepts a non-matching endpoint/key params and returns a new client' do
37
+ endpoint = 'https://differentfake.com'
38
+ new_key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
39
+ Client.should_receive(:new).with(new_key, endpoint)
40
+ AlertLogic.api_client(new_key, endpoint)
41
+ end
42
+
43
+ it 'accepts a matching endpoint param and returns an existing client' do
44
+ AlertLogic.api_client('https://fake.com', '3234').should eq(@client)
45
+ end
46
+ end
47
+
48
+ describe AlertLogic, '.secret_key' do
49
+ before(:each) do
50
+ AlertLogic.instance_variable_set(:@secret_key, '1234')
51
+ allow(AlertLogic).to receive(:api_client).with(/\d+/)
52
+ end
53
+
54
+ it 'returns nil when not set' do
55
+ AlertLogic.instance_variable_set(:@secret_key, nil)
56
+ AlertLogic.secret_key.should be_nil
57
+ end
58
+
59
+ it 'returns a value when set' do
60
+ AlertLogic.secret_key.should_not be_nil
61
+ AlertLogic.secret_key.should eq('1234')
62
+ end
63
+
64
+ it 'configures a new api_client when the key doesnt match' do
65
+ AlertLogic.should_receive(:api_client).with('5678')
66
+ AlertLogic.secret_key('5678')
67
+ end
68
+
69
+ it 'returns the existing key when the keys match' do
70
+ AlertLogic.secret_key('1234').should eq('1234')
71
+ end
72
+ end
73
+
74
+ describe AlertLogic, '.secret_key=' do
75
+ it 'should proxy request to #secret_key' do
76
+ AlertLogic.should_receive(:secret_key)
77
+ AlertLogic.secret_key = '1234'
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ module AlertLogic
4
+ describe Client, '.new' do
5
+ before(:each) do
6
+ AlertLogic.secret_key = Test.secret_key
7
+ end
8
+
9
+ it 'initializes a new client with no params and a secret_key set' do
10
+ client = Client.new
11
+ client.secret_key.should eq(AlertLogic.secret_key)
12
+ end
13
+
14
+ it 'raises an exception when no key is configure or passed' do
15
+ AlertLogic.secret_key = nil
16
+ expect { Client.new }.to raise_error(InvalidKey)
17
+ end
18
+
19
+ it 'initializes a new client with a key param' do
20
+ key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
21
+ client = Client.new(key)
22
+ client.secret_key.should eq(key)
23
+ end
24
+
25
+ it 'initializes a new client with an endpoint param' do
26
+ endpoint = 'https://fake.com'
27
+ client = Client.new(nil, endpoint)
28
+ client.endpoint.should eq(endpoint)
29
+ end
30
+
31
+ it 'initializes a new client with both params' do
32
+ endpoint = 'https://fake.com'
33
+ key = '8F1E9F714E7C70DADF5C26BDC89618999E211FA45766279f07'
34
+ client = Client.new(key, endpoint)
35
+ client.endpoint.should eq(endpoint)
36
+ client.secret_key.should eq(key)
37
+ end
38
+
39
+ it 'creates all the required instance variables' do
40
+ # ruby 1.8.7 can't sort an array of symbols so we have to define <=>
41
+ # to convert them to strings and compare against an array of strings.
42
+ vars = ['@secret_key', '@endpoint', '@logger', '@connection']
43
+ client = Client.new
44
+ client.instance_variables.map(&:to_s).sort \
45
+ .should eq(vars.sort)
46
+ client \
47
+ .instance_variable_get(:@connection) \
48
+ .should be_instance_of(Faraday::Connection)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module AlertLogic
4
+ describe RestMethods, :env do
5
+ let(:client) { AlertLogic.api_client(Test.secret_key) }
6
+
7
+ Test.all_resources.each do |resource, id|
8
+ describe '.delete' do
9
+ it 'interpolates the params and calls the proper client method' do
10
+ client.should_receive(:delete).with('resource', 'resource_id')
11
+ client.delete('resource', 'resource_id')
12
+ end
13
+ end
14
+
15
+ describe '.get' do
16
+ it 'interpolates the params and calls the proper client method' do
17
+ client.should_receive(:get).with(resource, id)
18
+ client.get(resource, id)
19
+ end
20
+
21
+ it "properly parses #{resource} index" do
22
+ res = client.get(resource, nil)
23
+ expect(res.body).to be_instance_of(Array)
24
+ expect(res.status).to eq(200)
25
+ end
26
+
27
+ it "properly parses a singular #{resource}" do
28
+ res = client.get(resource, id)
29
+ res.body.first['id'].should eq(id)
30
+ end
31
+ end
32
+
33
+ describe '.put' do
34
+ it 'interpolates the params and calls the proper client method' do
35
+ client.should_receive(:put).with('resource', 'resource_id')
36
+ client.put('resource', 'resource_id')
37
+ end
38
+ end
39
+
40
+ describe '.post' do
41
+ it 'interpolates the params and calls the proper client method' do
42
+ client.should_receive(:post).with('resource', 'resource_id')
43
+ client.post('resource', 'resource_id')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end