fog_tracker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/Gemfile +4 -0
- data/Guardfile +6 -0
- data/README.md +90 -0
- data/Rakefile +9 -0
- data/bin/tracker +73 -0
- data/config/accounts.yml.example +25 -0
- data/fog_tracker.gemspec +34 -0
- data/lib/fog_tracker.rb +23 -0
- data/lib/fog_tracker/account_tracker.rb +96 -0
- data/lib/fog_tracker/query/query_processor.rb +95 -0
- data/lib/fog_tracker/resource_tracker.rb +33 -0
- data/lib/fog_tracker/tracker.rb +91 -0
- data/lib/fog_tracker/util/string_underscore.rb +10 -0
- data/lib/fog_tracker/version.rb +3 -0
- data/lib/tasks/rspec.rake +6 -0
- data/spec/lib/fog_tracker/account_tracker_spec.rb +70 -0
- data/spec/lib/fog_tracker/query/query_processor_spec.rb +87 -0
- data/spec/lib/fog_tracker/resource_tracker_spec.rb +26 -0
- data/spec/lib/fog_tracker/tracker_spec.rb +38 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/_configure_logging.rb +6 -0
- data/spec/support/_mocks.rb +100 -0
- data/spec/support/_threading_support.rb +3 -0
- metadata +163 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
Fog Tracker
|
2
|
+
================
|
3
|
+
Uses the Fog gem to track the state of cloud computing resources across multiple accounts, with multiple service providers.
|
4
|
+
|
5
|
+
*BETA VERSION - needs functional testing with more cloud computing providers*
|
6
|
+
|
7
|
+
|
8
|
+
----------------
|
9
|
+
What is it?
|
10
|
+
----------------
|
11
|
+
The Fog Tracker uses the [Fog gem](https://github.com/fog/fog) to periodically poll one or more cloud computing accounts, and determines the state of their associated cloud computing "Resources": compute instances, disk volumes, stored objects, and so on. The most recent state of all Resources is saved in memory (as Fog objects), and can be accessed repeatedly with no network overhead, using a simple, Regular-Expression-based query.
|
12
|
+
|
13
|
+
|
14
|
+
----------------
|
15
|
+
Why is it?
|
16
|
+
----------------
|
17
|
+
The Fog Tracker is intended to be a foundation library, on top of which more complex cloud dashboard or management applications can be built. It allows such applications to decouple their requests to cloud service providers from their access to the results of those requests.
|
18
|
+
|
19
|
+
|
20
|
+
----------------
|
21
|
+
Where is it? (Installation)
|
22
|
+
----------------
|
23
|
+
Install the Fog Tracker gem (and its dependencies if necessary) from RubyGems
|
24
|
+
|
25
|
+
gem install fog_tracker [rake bundler]
|
26
|
+
|
27
|
+
|
28
|
+
----------------
|
29
|
+
How is it [done]? (Usage)
|
30
|
+
----------------
|
31
|
+
1) Just require the gem, and create a `FogTracker::Tracker`. Pass it some account information in a hash, perhaps loaded from a YAML file:
|
32
|
+
|
33
|
+
require 'fog_tracker'
|
34
|
+
tracker = FogTracker::Tracker.new(YAML::load(File.read 'accounts.yml'))
|
35
|
+
tracker.start
|
36
|
+
|
37
|
+
Here are the contents of a sample `accounts.yml`:
|
38
|
+
|
39
|
+
AWS EC2 development account:
|
40
|
+
:provider: AWS
|
41
|
+
:service: Compute
|
42
|
+
:credentials:
|
43
|
+
:aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
|
44
|
+
:aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
45
|
+
:polling_time: 180
|
46
|
+
:exclude_resources:
|
47
|
+
- :flavors
|
48
|
+
- :images
|
49
|
+
Rackspace development account:
|
50
|
+
:provider: Rackspace
|
51
|
+
:service: Compute
|
52
|
+
:credentials:
|
53
|
+
:rackspace_api_key: XXXXXXXXXXXXXXXXXXXX
|
54
|
+
:rackspace_username: XXXXXXXXX
|
55
|
+
:polling_time: 180
|
56
|
+
|
57
|
+
2) The tracker will run asynchronously, with one thread per account. You can call `start()` and `stop()` on it, and query the resulting collections of Fog Resource objects using a filter-based query:
|
58
|
+
|
59
|
+
# get all Compute instances across all accounts and providers
|
60
|
+
tracker.query("*::Compute::*::servers")
|
61
|
+
|
62
|
+
# get all Amazon EC2 Resources, of all types, across all accounts
|
63
|
+
tracker["*::Compute::AWS::*"] # the [] operator is the same as query()
|
64
|
+
|
65
|
+
# get all S3 objects in a given account
|
66
|
+
tracker["my production account::Storage::AWS::files"]
|
67
|
+
|
68
|
+
The query string format is: "`account name::service::provider::collection`"
|
69
|
+
|
70
|
+
You can also pass a Proc to the Tracker at initialization, which will be invoked whenever an account's Resources have been updated -- see the API docs for details.
|
71
|
+
|
72
|
+
|
73
|
+
----------------
|
74
|
+
Who is it? (Contribution / Development)
|
75
|
+
----------------
|
76
|
+
This Gem was created by Benton Roberts _(benton@bentonroberts.com)_ The project is still in its early stages, and needs to be tested with many more of Fog's cloud providers. Helping hands are appreciated!
|
77
|
+
|
78
|
+
1) Install project dependencies.
|
79
|
+
|
80
|
+
gem install rake bundler
|
81
|
+
|
82
|
+
2) Fetch the project code and bundle up...
|
83
|
+
|
84
|
+
git clone https://github.com/benton/fog_tracker.git
|
85
|
+
cd fog_tracker
|
86
|
+
bundle
|
87
|
+
|
88
|
+
3) Run the tests:
|
89
|
+
|
90
|
+
rake
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Set up bundler
|
2
|
+
%w{rubygems bundler bundler/gem_tasks}.each {|dep| require dep}
|
3
|
+
Bundler.setup(:default, :test, :development)
|
4
|
+
|
5
|
+
# Load all tasks from 'lib/tasks'
|
6
|
+
Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].sort.each {|ext| load ext}
|
7
|
+
|
8
|
+
desc 'Default: run specs.'
|
9
|
+
task :default => :spec
|
data/bin/tracker
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# == Synopsis
|
4
|
+
# Uses the Fog gem to track the status of cloud computing resources
|
5
|
+
#
|
6
|
+
# == Usage
|
7
|
+
# tracker.rb [options] ACCOUNTS_CONFIG_FILE.YML
|
8
|
+
#
|
9
|
+
# == Options (all options can be put into the config file)
|
10
|
+
# -d, --delay [INTEGER] Seconds between status updates. default = 180
|
11
|
+
# -l, --log-level [LEVEL] Sets Log4r level for console output. default = INFO
|
12
|
+
# -h, --help Displays help message
|
13
|
+
#
|
14
|
+
# == Author
|
15
|
+
# Benton Roberts
|
16
|
+
|
17
|
+
require 'optparse'
|
18
|
+
require 'fog_tracker'
|
19
|
+
LOG_LVLS = {
|
20
|
+
"DEBUG" => ::Logger::DEBUG,
|
21
|
+
"INFO" => ::Logger::INFO,
|
22
|
+
"WARN" => ::Logger::WARN,
|
23
|
+
"ERROR" => ::Logger::ERROR,
|
24
|
+
"FATAL" => ::Logger::FATAL
|
25
|
+
}
|
26
|
+
|
27
|
+
module FogTracker
|
28
|
+
class FogTrackerConsoleApp
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@log = FogTracker.default_logger(STDOUT)
|
32
|
+
parse_options
|
33
|
+
@log.info "Loading account information from #{ARGV[0]}"
|
34
|
+
@accounts = YAML::load(File.open(ARGV[0]))
|
35
|
+
@tracker = FogTracker::Tracker.new(
|
36
|
+
@accounts, {:logger => @log, :delay => @opts[:delay]}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def go
|
41
|
+
@tracker.start
|
42
|
+
while true do
|
43
|
+
sleep 60 # Loop forever
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_options
|
48
|
+
@opts = {:log_level => 'INFO'}
|
49
|
+
optparse = OptionParser.new do |opts|
|
50
|
+
opts.banner = "Usage: tracker [options] ACCOUNTS.YML"
|
51
|
+
opts.on('-d', '--delay SECONDS', Integer,
|
52
|
+
'Number of seconds between status updates') do |delay|
|
53
|
+
@opts[:delay] = delay
|
54
|
+
end
|
55
|
+
opts.on('-l', '--log-level LEVEL', 'Set logging level') do |log_level|
|
56
|
+
@opts[:log_level] = log_level.upcase
|
57
|
+
end
|
58
|
+
opts.on('-h', '--help', 'Display this help message') do
|
59
|
+
puts opts ; exit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
optparse.parse!
|
63
|
+
@log.level = LOG_LVLS[@opts[:log_level]] if LOG_LVLS[@opts[:log_level]]
|
64
|
+
if ARGV.count < 1
|
65
|
+
@log.error "A YAML file with account info must be specified"
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
FogTracker::FogTrackerConsoleApp.new.go
|
@@ -0,0 +1,25 @@
|
|
1
|
+
AWS EC2 production account:
|
2
|
+
:provider: AWS
|
3
|
+
:service: Compute
|
4
|
+
:credentials:
|
5
|
+
:aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
|
6
|
+
:aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
7
|
+
:polling_time: 120
|
8
|
+
:exclude_resources:
|
9
|
+
AWS EC2 development account:
|
10
|
+
:provider: AWS
|
11
|
+
:service: Compute
|
12
|
+
:credentials:
|
13
|
+
:aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
|
14
|
+
:aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
15
|
+
:polling_time: 180
|
16
|
+
:exclude_resources:
|
17
|
+
- :flavors
|
18
|
+
- :images
|
19
|
+
Rackspace development account:
|
20
|
+
:provider: Rackspace
|
21
|
+
:service: Compute
|
22
|
+
:credentials:
|
23
|
+
:rackspace_api_key: XXXXXXXXXXXXXXXXXXXX
|
24
|
+
:rackspace_username: XXXXXXXXX
|
25
|
+
:polling_time: 180
|
data/fog_tracker.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fog_tracker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fog_tracker"
|
7
|
+
s.version = FogTracker::VERSION
|
8
|
+
s.authors = ["Benton Roberts"]
|
9
|
+
s.email = ["benton@bentonroberts.com"]
|
10
|
+
s.homepage = "http://github.com/benton/fog_tracker"
|
11
|
+
s.summary = %q{Tracks the state of cloud computing resources across }+
|
12
|
+
%q{multiple accounts with multiple service providers}
|
13
|
+
s.description = %q{This gem peridically polls mutiple cloud computing }+
|
14
|
+
%q{services using the fog gem, asynchronously updating the }+
|
15
|
+
%q{state of the resulting collections of Fog Resources.}
|
16
|
+
|
17
|
+
s.rubyforge_project = "fog_tracker"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
# Runtime dependencies
|
25
|
+
s.add_dependency "fog"
|
26
|
+
|
27
|
+
# Development / Test dependencies
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
s.add_development_dependency "rspec"
|
30
|
+
s.add_development_dependency "rdoc"
|
31
|
+
s.add_development_dependency "guard"
|
32
|
+
s.add_development_dependency "guard-rake"
|
33
|
+
s.add_development_dependency "ruby_gntp"
|
34
|
+
end
|
data/lib/fog_tracker.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# Load all ruby files from 'fog_tracker' directory
|
4
|
+
Dir[File.join(File.dirname(__FILE__), "fog_tracker/**/*.rb")].each {|f| require f}
|
5
|
+
|
6
|
+
module FogTracker
|
7
|
+
|
8
|
+
# The default polling interval in seconds
|
9
|
+
# This. can be overriden when a FogTracker::Tracker is created, either
|
10
|
+
# in the +accounts+ definitions, or in the +options+ parameter
|
11
|
+
DEFAULT_POLLING_TIME = 300 # by default, poll all accounts every 5 minutes
|
12
|
+
|
13
|
+
# Returns a slightly-modified version of the default Ruby Logger
|
14
|
+
def self.default_logger(output = nil)
|
15
|
+
logger = ::Logger.new(output)
|
16
|
+
logger.sev_threshold = Logger::INFO
|
17
|
+
logger.formatter = proc {|lvl, time, prog, msg|
|
18
|
+
"#{lvl} #{time.strftime '%Y-%m-%d %H:%M:%S %Z'}: #{msg}\n"
|
19
|
+
}
|
20
|
+
logger
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module FogTracker
|
2
|
+
|
3
|
+
# Tracks a single Fog account in an ActiveRecord database
|
4
|
+
class AccountTracker
|
5
|
+
require 'fog'
|
6
|
+
|
7
|
+
attr_reader :name, :account, :log, :delay
|
8
|
+
attr_reader :resource_trackers
|
9
|
+
|
10
|
+
# Creates an object for tracking a single Fog account
|
11
|
+
#
|
12
|
+
# ==== Attributes
|
13
|
+
#
|
14
|
+
# * +account_name+ - a human-readable name for the account (String)
|
15
|
+
# * +account+ - a Hash of account information (see accounts.yml.example)
|
16
|
+
# * +options+ - Hash of optional parameters
|
17
|
+
#
|
18
|
+
# ==== Options
|
19
|
+
#
|
20
|
+
# * +:delay+ - Default time between polling of accounts
|
21
|
+
# * +:callback+ - A Method or Proc to call each time an account is polled.
|
22
|
+
# It should take the name of the account as its only required parameter
|
23
|
+
# * +:logger+ - a Ruby Logger-compatible object
|
24
|
+
def initialize(account_name, account, options={})
|
25
|
+
@name = account_name
|
26
|
+
@account = account
|
27
|
+
@callback = options[:callback]
|
28
|
+
@log = options[:logger] || FogTracker.default_logger
|
29
|
+
@delay = options[:delay] || account[:polling_time] ||
|
30
|
+
FogTracker::DEFAULT_POLLING_TIME
|
31
|
+
@log.debug "Creating tracker for account #{@name}."
|
32
|
+
create_resource_trackers
|
33
|
+
end
|
34
|
+
|
35
|
+
# Starts a background thread, which updates all @resource_trackers
|
36
|
+
def start
|
37
|
+
if not running?
|
38
|
+
@log.debug "Starting tracking for account #{@name}..."
|
39
|
+
@timer = Thread.new do
|
40
|
+
begin
|
41
|
+
while true do
|
42
|
+
@log.info "Polling account #{@name}..."
|
43
|
+
@resource_trackers.each {|tracker| tracker.update}
|
44
|
+
@callback.call @name if @callback
|
45
|
+
sleep @delay
|
46
|
+
end
|
47
|
+
rescue Exception => e
|
48
|
+
@log.error "Exception polling account #{name}: #{e.message}"
|
49
|
+
@log.error e.backtrace.join("\n")
|
50
|
+
exit 99
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
@log.info "Already tracking account #{@name}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Stops all the @resource_trackers
|
59
|
+
def stop
|
60
|
+
if running?
|
61
|
+
@log.info "Stopping tracker for #{name}..."
|
62
|
+
@timer.kill
|
63
|
+
@timer = nil
|
64
|
+
else
|
65
|
+
@log.info "Tracking already stopped for account #{@name}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns true or false depending on whether this tracker is polling
|
70
|
+
def running? ; @timer != nil end
|
71
|
+
|
72
|
+
# Returns a Fog::Connection object to this account's Fog service
|
73
|
+
def connection
|
74
|
+
service_mod = ::Fog::const_get @account[:service]
|
75
|
+
provider_class = service_mod.send(:const_get, @account[:provider])
|
76
|
+
@fog_service ||= provider_class.new(@account[:credentials])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an Array of resource types (Strings) to track
|
80
|
+
def tracked_types
|
81
|
+
types = connection.collections - (account[:exclude_resources] || [])
|
82
|
+
types.map {|type| type.to_s}
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Creates and returns an Array of ResourceTracker objects -
|
88
|
+
# one for each resource type associated with this account's service
|
89
|
+
def create_resource_trackers
|
90
|
+
@resource_trackers = tracked_types.map do |fog_collection_name|
|
91
|
+
FogTracker::ResourceTracker.new(fog_collection_name.to_s, self)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module FogTracker
|
2
|
+
module Query
|
3
|
+
class QueryProcessor
|
4
|
+
|
5
|
+
QUERY_PATTERN = %r{(.*)::(.*)::(.*)::(.*)}
|
6
|
+
|
7
|
+
# Creates an object for filtering Resources from a set of AccountTrackers
|
8
|
+
#
|
9
|
+
# ==== Attributes
|
10
|
+
#
|
11
|
+
# * +trackers+ - a Hash of AccountTrackers, indexed by Account name
|
12
|
+
# * +options+ - Hash of optional parameters
|
13
|
+
#
|
14
|
+
# ==== Options
|
15
|
+
#
|
16
|
+
# * +:logger+ - a Ruby Logger-compatible object
|
17
|
+
def initialize(trackers, options={})
|
18
|
+
@trackers = trackers
|
19
|
+
@log = options[:logger] || FogTracker.default_logger
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns an Array of Resources, filtered by +query+
|
23
|
+
def execute(query)
|
24
|
+
acct_pattern, svc_pattern, prov_pattern, col_pattern = parse_query(query)
|
25
|
+
results_by_account = get_results_by_account(acct_pattern)
|
26
|
+
results = filter_by_service(results_by_account, svc_pattern)
|
27
|
+
results = filter_by_provider(results, prov_pattern)
|
28
|
+
results = filter_by_collection(results, col_pattern)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Returns an Array of 4 RegEx objeccts based on the +query_string+
|
34
|
+
# for matching [account name, service, provider, collection]
|
35
|
+
def parse_query(query_string)
|
36
|
+
@log.debug "Parsing Query #{query_string}"
|
37
|
+
tokenize(query_string).map {|token| regex_from_token(token)}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an array of 4 String tokens by splitting +query_string+
|
41
|
+
def tokenize(query_string)
|
42
|
+
query_string.strip!
|
43
|
+
if match = query_string.match(QUERY_PATTERN)
|
44
|
+
match.captures
|
45
|
+
else
|
46
|
+
raise "Bad query: '#{query_string}'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Converts a String +token+ into a RegEx for matching query values
|
51
|
+
def regex_from_token(token)
|
52
|
+
token = '.*' if token == '*' # a single wildcard is a special case
|
53
|
+
%r{^\s*#{token}\s*$}i # otherwise, interpret as a RegEx
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a subset of all Resources, filtered only by +acct_name_pattern+
|
57
|
+
def get_results_by_account(acct_name_pattern)
|
58
|
+
results = Array.new
|
59
|
+
@trackers.each do |account_name, account_tracker|
|
60
|
+
if account_name.match acct_name_pattern
|
61
|
+
account_tracker.resource_trackers.each do |resource_tracker|
|
62
|
+
results += resource_tracker.collection
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
results
|
67
|
+
end
|
68
|
+
|
69
|
+
# filters an Array of Fog Resources by Service
|
70
|
+
def filter_by_service(resources, service_pattern)
|
71
|
+
resources.select do |resource|
|
72
|
+
service_name = resource.class.name.match(/Fog::(\w+)::/)[1]
|
73
|
+
service_name.match service_pattern
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# filters an Array of Fog Resources by Provider
|
78
|
+
def filter_by_provider(resources, provider_pattern)
|
79
|
+
resources.select do |resource|
|
80
|
+
provider_name = resource.class.name.match(/Fog::\w+::(\w+)::/)[1]
|
81
|
+
provider_name.match provider_pattern
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# filters an Array of Fog Resources by collection name (Resource Type)
|
86
|
+
def filter_by_collection(resources, collection_pattern)
|
87
|
+
resources.select do |resource|
|
88
|
+
collection_class = resource.collection.class.name.match(/::(\w+)$/)[1]
|
89
|
+
collection_class.to_underscore.match collection_pattern
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FogTracker
|
2
|
+
|
3
|
+
# Tracks a single Fog Resource type for a single Account
|
4
|
+
class ResourceTracker
|
5
|
+
|
6
|
+
attr_accessor :collection
|
7
|
+
|
8
|
+
# Creates an object for tracking a single Fog account
|
9
|
+
#
|
10
|
+
# ==== Attributes
|
11
|
+
#
|
12
|
+
# * +resource_type+ - the Fog collection name (String) for this resource type
|
13
|
+
# * +account_tracker+ - the AccountTracker for this tracker's @collection
|
14
|
+
def initialize(resource_type, account_tracker)
|
15
|
+
@type = resource_type
|
16
|
+
@account_tracker = account_tracker
|
17
|
+
@account = account_tracker.account
|
18
|
+
@account_name = account_tracker.name
|
19
|
+
@log = account_tracker.log
|
20
|
+
@collection = Array.new
|
21
|
+
@log.debug "Created tracker for #{@type} on #{@account_name}."
|
22
|
+
end
|
23
|
+
|
24
|
+
# Polls the account's connection for updated info on all existing
|
25
|
+
# instances of the relevant resource type, and saves them as @collection
|
26
|
+
def update
|
27
|
+
@log.info "Polling #{@type} on #{@account_name}..."
|
28
|
+
@collection = @account_tracker.connection.send(@type)
|
29
|
+
@log.info "Discovered #{@collection.count} #{@type} on #{@account_name}."
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module FogTracker
|
2
|
+
|
3
|
+
# Tracks one or more Fog accounts and exposes a query() on the results
|
4
|
+
class Tracker
|
5
|
+
|
6
|
+
attr_accessor :accounts
|
7
|
+
|
8
|
+
# Creates an object for tracking Fog accounts
|
9
|
+
#
|
10
|
+
# ==== Attributes
|
11
|
+
#
|
12
|
+
# * +accounts+ - a Hash of account information (see accounts.yml.example)
|
13
|
+
# * +options+ - Hash of optional parameters
|
14
|
+
#
|
15
|
+
# ==== Options
|
16
|
+
#
|
17
|
+
# * +:delay+ - Time between polling of accounts. Overrides per-account value
|
18
|
+
# * +:callback+ - A Proc to call each time an account is polled.
|
19
|
+
# It should take the name of the account as its only required parameter
|
20
|
+
# * +:logger+ - a Ruby Logger-compatible object
|
21
|
+
def initialize(accounts = {}, options = {})
|
22
|
+
@accounts = accounts
|
23
|
+
@delay = options[:delay]
|
24
|
+
@callback = options[:callback]
|
25
|
+
@log = options[:logger] || FogTracker.default_logger
|
26
|
+
# Create a Hash that maps account names to AccountTrackers
|
27
|
+
create_trackers
|
28
|
+
end
|
29
|
+
|
30
|
+
# Invokes the start method on all the @trackers
|
31
|
+
def start
|
32
|
+
if not running?
|
33
|
+
@log.info "Tracking #{@trackers.keys.count} accounts..."
|
34
|
+
@trackers.each_value {|tracker| tracker.start}
|
35
|
+
@running = true
|
36
|
+
else
|
37
|
+
@log.info "Already tracking #{@trackers.keys.count} accounts"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Invokes the stop method on all the @trackers
|
42
|
+
def stop
|
43
|
+
if running?
|
44
|
+
@log.info "Stopping tracker..."
|
45
|
+
@trackers.each_value {|tracker| tracker.stop}
|
46
|
+
@running = false
|
47
|
+
else
|
48
|
+
@log.info "Tracking already stopped"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true or false/nil depending on whether this tracker is polling
|
53
|
+
def running? ; @running end
|
54
|
+
|
55
|
+
# Returns an array of Resource types (Strings) for a given account
|
56
|
+
def types_for_account(account_name)
|
57
|
+
@trackers[account_name].tracked_types
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns an array of Resources matching the +query_string+
|
61
|
+
#
|
62
|
+
# ==== Attributes
|
63
|
+
#
|
64
|
+
# * +query_string+ - a String used to filter the discovered Resources
|
65
|
+
# it might look like: "Account Name::Compute::AWS::servers"
|
66
|
+
def query(query_string)
|
67
|
+
FogTracker::Query::QueryProcessor.new(
|
68
|
+
@trackers, :logger => @log
|
69
|
+
).execute(query_string)
|
70
|
+
end
|
71
|
+
alias :[] :query
|
72
|
+
|
73
|
+
# Returns this tracker's logger, for changing logging dynamically
|
74
|
+
def logger
|
75
|
+
@log
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Creates a Hash of AccountTracker objects, indexed by account name
|
81
|
+
def create_trackers
|
82
|
+
@trackers = Hash.new
|
83
|
+
@accounts.each do |name, account|
|
84
|
+
@log.debug "Setting up tracker for account #{name}"
|
85
|
+
@trackers[name] = AccountTracker.new(name, account,
|
86
|
+
{:delay => @delay, :callback => @callback, :logger => @log})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FogTracker
|
4
|
+
|
5
|
+
describe AccountTracker do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@tracker = AccountTracker.new(
|
9
|
+
FAKE_ACCOUNT_NAME, FAKE_ACCOUNT, :logger => LOG
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "exposes its Hash of account information" do
|
14
|
+
@tracker.connection.should_not == nil
|
15
|
+
end
|
16
|
+
it "exposes the account name" do
|
17
|
+
@tracker.name.should == FAKE_ACCOUNT_NAME
|
18
|
+
end
|
19
|
+
it "exposes the connection to its Fog service" do
|
20
|
+
@tracker.account.should == FAKE_ACCOUNT
|
21
|
+
end
|
22
|
+
it "exposes the connection to its logger" do
|
23
|
+
@tracker.log.should_not == nil
|
24
|
+
end
|
25
|
+
it "exposes a collection of ResourceTrackers" do
|
26
|
+
@tracker.resource_trackers.size.should be > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#start' do
|
30
|
+
it "sends update() to its ResourceTrackers" do
|
31
|
+
@tracker.resource_trackers.each do |resource_tracker|
|
32
|
+
resource_tracker.should_receive(:update)
|
33
|
+
end
|
34
|
+
@tracker.start
|
35
|
+
sleep THREAD_STARTUP_DELAY # wait for background thread to start
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#stop' do
|
40
|
+
it "sets running? to false" do
|
41
|
+
@tracker.start ; @tracker.stop
|
42
|
+
@tracker.running?.should be_false
|
43
|
+
end
|
44
|
+
it "kills its timer thread" do
|
45
|
+
@tracker.start ; @tracker.stop
|
46
|
+
@tracker.resource_trackers.first.should_not_receive(:update)
|
47
|
+
sleep THREAD_STARTUP_DELAY # wait to make sure no update()s are sent
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#running?' do
|
52
|
+
it "returns true if the AccountTracker is running" do
|
53
|
+
@tracker.start
|
54
|
+
@tracker.running?.should be_true
|
55
|
+
end
|
56
|
+
it "returns false if the AccountTracker is stopped" do
|
57
|
+
@tracker.running?.should be_false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#tracked_types' do
|
62
|
+
it "returns a list of Resource types tracked for its account" do
|
63
|
+
@tracker.tracked_types.size.should be > 0
|
64
|
+
@tracker.tracked_types.first.should be_an_instance_of String
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FogTracker
|
4
|
+
module Query
|
5
|
+
|
6
|
+
describe QueryProcessor do
|
7
|
+
|
8
|
+
QUERY['matching all Resources'] = '*::*::*::*'
|
9
|
+
QUERY['by account name'] = 'Fake Account \d+::*::*::*'
|
10
|
+
QUERY['by Fog Service'] = '*::FakeService::*::*'
|
11
|
+
QUERY['by Fog Provider'] = '*::*::FakeProvider::*'
|
12
|
+
QUERY['by Fog collection name'] = '*::*::*::fake_collection_type1'
|
13
|
+
|
14
|
+
|
15
|
+
it "should define a Query Pattern for parsing queries" do
|
16
|
+
QueryProcessor::QUERY_PATTERN.should_not == nil
|
17
|
+
QueryProcessor::QUERY_PATTERN.should be_an_instance_of(Regexp)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#execute" do
|
21
|
+
context "with no discovered Resources" do
|
22
|
+
# Try each of the QUERY entries above against an empty set
|
23
|
+
QUERY.each do |name, query|
|
24
|
+
it "should return an empty Array for a query #{name}" do
|
25
|
+
QueryProcessor.new(
|
26
|
+
{FAKE_ACCOUNT_NAME => mock_account_tracker}, :logger => LOG
|
27
|
+
).execute(query).should == []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with a pre-populated, diverse set of Resources" do
|
33
|
+
# Create a prepopulated QueryProcessor
|
34
|
+
NUMBER_OF_ACCOUNTS = 8
|
35
|
+
RESOURCES_PER_ACCOUNT = 2
|
36
|
+
NUMBER_OF_COLLECTIONS = [5, NUMBER_OF_FAKE_RESOURCE_TYPES].min
|
37
|
+
before(:each) do
|
38
|
+
account_trackers =
|
39
|
+
(1..NUMBER_OF_ACCOUNTS).inject({}) do |t, account_index|
|
40
|
+
t["Fake Account #{account_index}"] = mock_account_tracker(
|
41
|
+
NUMBER_OF_COLLECTIONS, RESOURCES_PER_ACCOUNT
|
42
|
+
) ; t
|
43
|
+
end
|
44
|
+
@processor = QueryProcessor.new(account_trackers, :logger => LOG)
|
45
|
+
end
|
46
|
+
# Run the above QUERY entries against the prepopulated set
|
47
|
+
context "when running the query matching all Resources" do
|
48
|
+
it "should return all Resources" do
|
49
|
+
@processor.execute(QUERY['matching all Resources']).size.should ==
|
50
|
+
NUMBER_OF_ACCOUNTS * NUMBER_OF_COLLECTIONS * RESOURCES_PER_ACCOUNT
|
51
|
+
end
|
52
|
+
end
|
53
|
+
context "when running a query by account name" do
|
54
|
+
it "should return all Resources for that account only" do
|
55
|
+
@processor.execute(QUERY['by account name']).size.should ==
|
56
|
+
NUMBER_OF_ACCOUNTS * NUMBER_OF_COLLECTIONS * RESOURCES_PER_ACCOUNT
|
57
|
+
@processor.execute('wrong account::*::*::*').size.should == 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "when running a query by Fog service name" do
|
61
|
+
it "should return all Resources for that service only" do
|
62
|
+
@processor.execute(QUERY['by Fog Service']).size.should ==
|
63
|
+
NUMBER_OF_ACCOUNTS * NUMBER_OF_COLLECTIONS * RESOURCES_PER_ACCOUNT
|
64
|
+
@processor.execute('*::wrong service::*::*').size.should == 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
context "when running a query by Fog provider name" do
|
68
|
+
it "should return all Resources for that provider only" do
|
69
|
+
@processor.execute(QUERY['by Fog Provider']).size.should ==
|
70
|
+
NUMBER_OF_ACCOUNTS * NUMBER_OF_COLLECTIONS * RESOURCES_PER_ACCOUNT
|
71
|
+
@processor.execute('*::*::wrong provider::*').size.should == 0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "when running a query by Fog collection name" do
|
75
|
+
it "should return all Resources for that collection only" do
|
76
|
+
@processor.execute(QUERY['by Fog collection name']).size.should ==
|
77
|
+
NUMBER_OF_ACCOUNTS * RESOURCES_PER_ACCOUNT
|
78
|
+
@processor.execute('*::*::*::wrong collection').size.should == 0
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FogTracker
|
4
|
+
|
5
|
+
describe ResourceTracker do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@account_tracker = mock_account_tracker
|
9
|
+
@tracker = ResourceTracker.new(
|
10
|
+
FAKE_COLLECTION, @account_tracker
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "exposes a collection of Fog objects" do
|
15
|
+
@tracker.collection.should == []
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#update' do
|
19
|
+
it "refreshes its resource collection from its AccountTracker" do
|
20
|
+
@account_tracker.connection.should_receive(FAKE_COLLECTION)
|
21
|
+
@tracker.update
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FogTracker
|
4
|
+
describe Tracker do
|
5
|
+
|
6
|
+
ACCOUNTS = {
|
7
|
+
"fake account" => {
|
8
|
+
:provider => 'AWS',
|
9
|
+
:service => 'Compute',
|
10
|
+
:credentials => {
|
11
|
+
:aws_access_key_id => 'X',
|
12
|
+
:aws_secret_access_key => 'X'
|
13
|
+
}, :exclude_resources => [ :spot_requests ]
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@tracker = Tracker.new(ACCOUNTS, :logger => LOG)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "exposes a Hash of account information" do
|
22
|
+
@tracker.accounts.should == ACCOUNTS
|
23
|
+
end
|
24
|
+
it "exposes a Logger object for reporting its activity" do
|
25
|
+
@tracker.logger.should == LOG
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#types_for_account' do
|
29
|
+
it "returns an array of collection names for the given account" do
|
30
|
+
[ "addresses", "flavors", "images", "key_pairs", "security_groups",
|
31
|
+
"servers", "snapshots", "tags", "volumes"].each do |collection|
|
32
|
+
@tracker.types_for_account("fake account").should include(collection)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Bundler.require # Load all gems and libs
|
2
|
+
|
3
|
+
# Require RSpec support files. Logging is configured there
|
4
|
+
support_files = Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")]
|
5
|
+
support_files.sort.each {|f| require f}
|
6
|
+
|
7
|
+
# RSpec configuration block
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.mock_with :rspec # == Mock Framework
|
10
|
+
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
11
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'fog'
|
2
|
+
Fog.mock!
|
3
|
+
|
4
|
+
# Establish some constants
|
5
|
+
module FogTracker
|
6
|
+
FAKE_COLLECTION = 'servers'
|
7
|
+
FAKE_ACCOUNT_NAME = 'Fake EC2 Account'
|
8
|
+
FAKE_ACCOUNT = {
|
9
|
+
:provider => 'AWS',
|
10
|
+
:service => 'Compute',
|
11
|
+
:polling_time => 10,
|
12
|
+
:credentials => {
|
13
|
+
:aws_access_key_id => "fake user",
|
14
|
+
:aws_secret_access_key => 'fake password'
|
15
|
+
},
|
16
|
+
:exclude_resources => [
|
17
|
+
:spot_requests, # No Fog mocks for this resource
|
18
|
+
#:account,
|
19
|
+
#:flavors,
|
20
|
+
#:images,
|
21
|
+
#:addresses,
|
22
|
+
#:volumes,
|
23
|
+
#:snapshots,
|
24
|
+
#:tags,
|
25
|
+
#:servers,
|
26
|
+
#:security_groups,
|
27
|
+
#:key_pairs,
|
28
|
+
#:spot_instance_requests
|
29
|
+
],
|
30
|
+
}
|
31
|
+
FAKE_ACCOUNTS = {FAKE_ACCOUNT_NAME => FAKE_ACCOUNT}
|
32
|
+
module Query
|
33
|
+
QUERY = {} # Used in query_processor_spec.rb
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create some fake Fog Resource and Collection Classes
|
38
|
+
NUMBER_OF_FAKE_RESOURCE_TYPES = 30
|
39
|
+
module Fog
|
40
|
+
module FakeService
|
41
|
+
module FakeProvider
|
42
|
+
(1..NUMBER_OF_FAKE_RESOURCE_TYPES).each do |class_index|
|
43
|
+
eval(%Q{
|
44
|
+
class FakeCollectionType#{class_index} ; end
|
45
|
+
class FakeResourceType#{class_index}
|
46
|
+
def collection ; FakeCollectionType#{class_index}.new end
|
47
|
+
end
|
48
|
+
})
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def mock_account_tracker(num_collections = 1, resources_per_collection = 0)
|
55
|
+
fake_account_tracker = double('mock_account_tracker')
|
56
|
+
fake_account_tracker.stub(:account).and_return(Hash.new)
|
57
|
+
fake_account_tracker.stub(:name).and_return("fake account tracker")
|
58
|
+
fake_account_tracker.stub(:log).and_return(LOG)
|
59
|
+
fake_account_tracker.stub(:connection).and_return(mock_fog_connection)
|
60
|
+
# create an array of mock ResourceTrackers
|
61
|
+
trackers = (1..num_collections).map do |class_index|
|
62
|
+
mock_resource_tracker(
|
63
|
+
Fog::FakeService::FakeProvider.const_get("FakeResourceType#{class_index}"),
|
64
|
+
resources_per_collection
|
65
|
+
)
|
66
|
+
end
|
67
|
+
fake_account_tracker.stub(:resource_trackers).and_return(trackers)
|
68
|
+
fake_account_tracker
|
69
|
+
end
|
70
|
+
|
71
|
+
def mock_fog_connection
|
72
|
+
fake_fog_connection = double('mock_fog_connection')
|
73
|
+
fake_fog_connection.stub(FogTracker::FAKE_COLLECTION).and_return([ 1, 2, 3 ])
|
74
|
+
fake_fog_connection
|
75
|
+
end
|
76
|
+
|
77
|
+
def mock_resource_tracker
|
78
|
+
fake_resource_tracker = double('mock_resource_tracker')
|
79
|
+
fake_resource_tracker.stub(:update)
|
80
|
+
fake_resource_tracker
|
81
|
+
end
|
82
|
+
|
83
|
+
def mock_fog_resource(resource_class)
|
84
|
+
fake_resource = resource_class.new
|
85
|
+
fake_collection_class =
|
86
|
+
fake_resource.stub(:collection).and_return(
|
87
|
+
collection_class.new
|
88
|
+
)
|
89
|
+
fake_resource
|
90
|
+
end
|
91
|
+
|
92
|
+
def mock_resource_tracker(resource_class, number_of_resources = 0)
|
93
|
+
fake_resource_tracker = double("mock_resource_tracker")
|
94
|
+
resources = Array.new
|
95
|
+
number_of_resources.times do
|
96
|
+
resources << resource_class.new
|
97
|
+
end
|
98
|
+
fake_resource_tracker.stub(:collection).and_return(resources)
|
99
|
+
fake_resource_tracker
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fog_tracker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Benton Roberts
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-25 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fog
|
16
|
+
requirement: &2151920400 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2151920400
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &2151919380 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2151919380
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &2151918740 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2151918740
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdoc
|
49
|
+
requirement: &2151917800 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2151917800
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: guard
|
60
|
+
requirement: &2151916140 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *2151916140
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rake
|
71
|
+
requirement: &2151915260 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *2151915260
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: ruby_gntp
|
82
|
+
requirement: &2151914080 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *2151914080
|
91
|
+
description: This gem peridically polls mutiple cloud computing services using the
|
92
|
+
fog gem, asynchronously updating the state of the resulting collections of Fog Resources.
|
93
|
+
email:
|
94
|
+
- benton@bentonroberts.com
|
95
|
+
executables:
|
96
|
+
- tracker
|
97
|
+
extensions: []
|
98
|
+
extra_rdoc_files: []
|
99
|
+
files:
|
100
|
+
- .gitignore
|
101
|
+
- Gemfile
|
102
|
+
- Guardfile
|
103
|
+
- README.md
|
104
|
+
- Rakefile
|
105
|
+
- bin/tracker
|
106
|
+
- config/accounts.yml.example
|
107
|
+
- fog_tracker.gemspec
|
108
|
+
- lib/fog_tracker.rb
|
109
|
+
- lib/fog_tracker/account_tracker.rb
|
110
|
+
- lib/fog_tracker/query/query_processor.rb
|
111
|
+
- lib/fog_tracker/resource_tracker.rb
|
112
|
+
- lib/fog_tracker/tracker.rb
|
113
|
+
- lib/fog_tracker/util/string_underscore.rb
|
114
|
+
- lib/fog_tracker/version.rb
|
115
|
+
- lib/tasks/rspec.rake
|
116
|
+
- spec/lib/fog_tracker/account_tracker_spec.rb
|
117
|
+
- spec/lib/fog_tracker/query/query_processor_spec.rb
|
118
|
+
- spec/lib/fog_tracker/resource_tracker_spec.rb
|
119
|
+
- spec/lib/fog_tracker/tracker_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
- spec/support/_configure_logging.rb
|
122
|
+
- spec/support/_mocks.rb
|
123
|
+
- spec/support/_threading_support.rb
|
124
|
+
homepage: http://github.com/benton/fog_tracker
|
125
|
+
licenses: []
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
segments:
|
137
|
+
- 0
|
138
|
+
hash: 3974745377539768248
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ! '>='
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
segments:
|
146
|
+
- 0
|
147
|
+
hash: 3974745377539768248
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project: fog_tracker
|
150
|
+
rubygems_version: 1.8.15
|
151
|
+
signing_key:
|
152
|
+
specification_version: 3
|
153
|
+
summary: Tracks the state of cloud computing resources across multiple accounts with
|
154
|
+
multiple service providers
|
155
|
+
test_files:
|
156
|
+
- spec/lib/fog_tracker/account_tracker_spec.rb
|
157
|
+
- spec/lib/fog_tracker/query/query_processor_spec.rb
|
158
|
+
- spec/lib/fog_tracker/resource_tracker_spec.rb
|
159
|
+
- spec/lib/fog_tracker/tracker_spec.rb
|
160
|
+
- spec/spec_helper.rb
|
161
|
+
- spec/support/_configure_logging.rb
|
162
|
+
- spec/support/_mocks.rb
|
163
|
+
- spec/support/_threading_support.rb
|