alert_logic 0.1.1

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,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