fog_tracker 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -75,7 +75,7 @@ How is it [done]? (Usage)
75
75
 
76
76
  tracker.query("*::*::*::*"){|r| puts "Found #{r.class} #{r.identity}"}
77
77
 
78
- * You can also pass a Proc to the Tracker at initialization, which will be invoked whenever an account's Resources have been updated. It should accept an Array containing the updated Resources as its first argument:
78
+ * You can pass a callback Proc to the Tracker at initialization, which will be invoked whenever all an account's Resources have been updated. It should accept an Array containing the updated Resources as its first parameter:
79
79
 
80
80
  FogTracker::Tracker.new(YAML::load(File.read 'accounts.yml'),
81
81
  :callback => Proc.new do |resources|
@@ -84,13 +84,14 @@ How is it [done]? (Usage)
84
84
  end
85
85
  ).start
86
86
 
87
- * The resources returned from a query are all Fog::Model objects, but they are "decorated" with some extra methods for your convenience. This simplifies the code that consumes the resources, because it does not have to know anything about the tracker. Here are the methods added:
88
- 1. To get a Resource's Hash of account information, call its `tracker_account` method _(credentials are removed)_.
89
- 2. To query for more resources, you can call `resource.tracker_query(query_string)`, though you cannot yet pass a block to this method.
90
- 3. To get a collection of resources from the same account, call `resource.account_resources(collection_name)`.
87
+ * The resources returned from a query are all Fog::Model objects, but they are "decorated" with some extra methods for fetching the account information, or for fetching more resources. This simplifies the code that consumes the query results, because it does not have to know anything about the tracker. Here are the methods added by {FogTracker::Extensions::FogModel}:
88
+ 1. `tracker_account` returns a Hash of the Resource's account information _(:name is added; :credentials are removed)_.
89
+ 2. `tracker_query(query_string)` queries the tracker for more resources.
90
+ 3. `account_resources(collection_query)` returns an Array of resources from the same account. (This is essentially shorthand for `tracker.query("account::service::provider::#{collection_query}")`)
91
91
 
92
- * Any Exceptions that occur in the Tracker's polling threads are rescued and logged. If you want to take further action, you can initialize the Tracker with an `:error_callback` Proc, similar to the Account update `:callback` -- except that the `:error_callback` should accept an Exception instead of an Array of Resources.
92
+ * Any Exceptions that occur in the Tracker's polling threads are rescued and logged. If you want to take further action, you can initialize the Tracker with an `:error_callback` Proc. This is similar to the Account update `:callback` above, except that the parameter for `:error_callback` should be an Exception instead of an Array of Resources.
93
93
 
94
+ * The Tracker can also be used synchronously. Its `update` method polls all accounts immediately, and waits for the result (the updated Array of resource objects) in the current thread.
94
95
 
95
96
  ----------------
96
97
  Who is it? (Contribution)
@@ -15,6 +15,7 @@ module FogTracker
15
15
 
16
16
  # Creates an object for tracking all collections in a single Fog account
17
17
  # @param [String] account_name a human-readable name for the account
18
+ # @param [Hash] account a Hash of account configuration data
18
19
  # @param [Hash] options optional additional parameters:
19
20
  # - :delay (Integer) - Default time between polling of accounts
20
21
  # - :callback (Proc) - A Method or Proc to call each time an account is polled.
@@ -29,7 +30,7 @@ module FogTracker
29
30
  @log = options[:logger] || FogTracker.default_logger
30
31
  @delay = options[:delay] || account[:polling_time] ||
31
32
  FogTracker::DEFAULT_POLLING_TIME
32
- @account[:polling_time] = @delay # TODO - test this
33
+ @account[:polling_time] = @delay
33
34
  @error_proc = options[:error_callback]
34
35
  @log.debug "Creating tracker for account #{@name}."
35
36
  create_collection_trackers
@@ -42,18 +43,11 @@ module FogTracker
42
43
  @log.debug "Starting tracking for account #{@name}..."
43
44
  @timer = Thread.new do
44
45
  begin
45
- while true do
46
- @log.info "Polling account #{@name}..."
47
- @collection_trackers.each {|tracker| tracker.update}
48
- @callback.call(all_resources) if @callback
49
- sleep @delay
46
+ while true
47
+ update ; sleep @delay
50
48
  end
51
49
  rescue Exception => e
52
- @log.error "Exception polling account #{name}: #{e.message}"
53
- e.backtrace.each {|line| @log.debug line}
54
- @error_proc.call(e) if @error_proc
55
- sleep @delay
56
- retry
50
+ sleep @delay ; retry
57
51
  end
58
52
  end
59
53
  else
@@ -72,6 +66,20 @@ module FogTracker
72
66
  end
73
67
  end
74
68
 
69
+ # Polls once for all the resource collections for this tracker's account
70
+ def update
71
+ begin
72
+ @log.info "Polling account #{@name}..."
73
+ @collection_trackers.each {|tracker| tracker.update}
74
+ @callback.call(all_resources) if @callback
75
+ rescue Exception => e
76
+ @log.error "Exception polling account #{name}: #{e.message}"
77
+ e.backtrace.each {|line| @log.debug line}
78
+ @error_proc.call(e) if @error_proc
79
+ raise e
80
+ end
81
+ end
82
+
75
83
  # Returns true or false depending on whether this tracker is polling
76
84
  def running? ; @timer != nil end
77
85
 
@@ -3,14 +3,12 @@ module FogTracker
3
3
  # Adds convenience methods to Fog::Model instances for gathering
4
4
  # information about its account, and about other Fog::Model resources
5
5
  module FogModel
6
- extend Forwardable # Resources need to be queriable
7
6
 
8
7
  # a FogTracker::CollectionTracker - *do not modify* - used for {#tracker_account}
9
8
  attr_accessor :_fog_collection_tracker
10
9
 
11
10
  # a FogTracker::QueryParser - *do not modify* - used for tracker_query
12
11
  attr_accessor :_query_processor
13
- def_delegator :@_query_processor, :execute, :tracker_query
14
12
 
15
13
  # Returns a cleaned copy of the resource's account information
16
14
  # from the its collection tracker (credentials are removed).
@@ -20,21 +18,40 @@ module FogTracker
20
18
  _fog_collection_tracker.clean_account_data
21
19
  end
22
20
 
23
- # Returns Fog::Model resources from this Resource's account only
21
+ # Returns Fog::Model resources from this Resource's account only.
24
22
  # @param [String] collection_name a String which is converted to
25
23
  # a RegEx, and used to match collection names for resources
26
24
  # in the same account as the current resource.
27
25
  # @return [Array <Fog::Model>] an Array of resources from this Model's
28
26
  # accout, whose collection matches collection_name.
29
27
  def account_resources(collection_name)
30
- (not @_query_processor) ? Array.new :
31
- @_query_processor.execute(
32
- "#{tracker_account[:name]}::"+
33
- "#{tracker_account[:service]}::"+
34
- "#{tracker_account[:provider]}::"+
35
- "#{collection_name}"
36
- )
28
+ results = Array.new
29
+ if @_query_processor
30
+ results = @_query_processor.execute(
31
+ "#{tracker_account[:name]}::"+
32
+ "#{tracker_account[:service]}::"+
33
+ "#{tracker_account[:provider]}::"+
34
+ "#{collection_name}"
35
+ )
36
+ (results.each {|r| yield r}) if block_given?
37
+ end
38
+ results
37
39
  end
40
+
41
+ # Runs a query across all accounts using a
42
+ # {FogTracker::Query::QueryProcessor}. Any code block parameter will
43
+ # be executed once for (and with) each resulting resource.
44
+ # @param [String] query a string used to filter for matching resources
45
+ # @return [Array <Fog::Model>] an Array of Resources, filtered by query
46
+ def tracker_query(query_string)
47
+ results = Array.new
48
+ if @_query_processor
49
+ results = @_query_processor.execute(query_string)
50
+ (results.each {|r| yield r}) if block_given?
51
+ end
52
+ results
53
+ end
54
+
38
55
  end
39
56
  end
40
57
  end
@@ -17,14 +17,14 @@ module FogTracker
17
17
  # (should take a single Exception as its only required parameter)
18
18
  # - :logger - a Ruby Logger-compatible object
19
19
  def initialize(accounts = {}, options = {})
20
- @accounts = accounts
21
- @delay = options[:delay]
22
- @callback = options[:callback]
23
- @log = options[:logger] || FogTracker.default_logger
20
+ @accounts = accounts
21
+ @delay = options[:delay]
22
+ @callback = options[:callback]
23
+ @log = options[:logger] || FogTracker.default_logger
24
24
  @error_proc = options[:error_callback]
25
+ @running = false
25
26
  # Create a Hash that maps account names to AccountTrackers
26
27
  create_trackers
27
- @running = false
28
28
  end
29
29
 
30
30
  # Starts periodically polling all this tracker's accounts
@@ -50,6 +50,15 @@ module FogTracker
50
50
  end
51
51
  end
52
52
 
53
+ # Polls all accounts once in the calling thread (synchronously)
54
+ # @return [Array <Fog::Model>] an Array of all discovered resources
55
+ def update
56
+ @trackers.each_value {|tracker| tracker.update}
57
+ results = all
58
+ (results.each {|r| yield r}) if block_given?
59
+ results
60
+ end
61
+
53
62
  # Returns true or false, depending on whether this tracker is polling
54
63
  # @return [true, false]
55
64
  def running? ; @running end
@@ -75,11 +84,17 @@ module FogTracker
75
84
  end
76
85
  alias :[] :query
77
86
 
78
- # Returns this tracker's logger, for changing logging dynamically
79
- def logger
80
- @log
87
+ # Returns an Array of all Resources currenty tracked
88
+ # @return [Array <Fog::Model>] an Array of Resources
89
+ def all
90
+ results = query '*::*::*::*'
91
+ (results.each {|r| yield r}) if block_given?
92
+ results
81
93
  end
82
94
 
95
+ # Returns this tracker's logger, for changing logging dynamically
96
+ def logger ; @log end
97
+
83
98
  private
84
99
 
85
100
  # Creates a Hash of AccountTracker objects, indexed by account name
@@ -1,3 +1,3 @@
1
1
  module FogTracker
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -30,19 +30,11 @@ module FogTracker
30
30
  it "exposes the connection to its logger" do
31
31
  @tracker.log.should_not == nil
32
32
  end
33
-
34
- context "when it encounters an Exception while updating" do
35
- context "when initialized with an error callback" do
36
- it "should fire the callback" do
37
- CollectionTracker.any_instance.stub(:update).and_raise
38
- @error_receiver.should_receive(:callback).exactly(:once)
39
- @tracker.start
40
- sleep THREAD_STARTUP_DELAY
41
- end
42
- end
33
+ it "always sets a polling time" do
34
+ @tracker.account[:polling_time].should be_an_instance_of(Fixnum)
43
35
  end
44
36
 
45
- describe '#start' do
37
+ describe '#update' do
46
38
  it "sends update() to its CollectionTrackers" do
47
39
  update_catcher = double "mock for catching CollectionTracker::update"
48
40
  update_catcher.stub(:update)
@@ -50,14 +42,31 @@ module FogTracker
50
42
  update_catcher.update
51
43
  end
52
44
  update_catcher.should_receive(:update)
53
- @tracker.start
54
- sleep THREAD_STARTUP_DELAY # wait for background thread to start
45
+ @tracker.update
55
46
  end
56
47
  it "invokes its callback Proc when its account is updated" do
57
48
  @account_receiver.should_receive(:callback).
58
49
  exactly(FAKE_ACCOUNTS.size).times
59
- @tracker.start
60
- sleep THREAD_STARTUP_DELAY
50
+ @tracker.update
51
+ end
52
+ context "when it encounters an Exception" do
53
+ it "raises the Exception" do
54
+ CollectionTracker.any_instance.stub(:update).and_raise
55
+ (Proc.new { @tracker.update }).should raise_error
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#start' do
61
+ context "when initialized with an error callback" do
62
+ context "when it encounters an Exception" do
63
+ it "should fire the callback" do
64
+ CollectionTracker.any_instance.stub(:update).and_raise
65
+ @error_receiver.should_receive(:callback).exactly(:once)
66
+ @tracker.start
67
+ sleep THREAD_STARTUP_DELAY
68
+ end
69
+ end
61
70
  end
62
71
  end
63
72
 
@@ -31,24 +31,67 @@ module FogTracker
31
31
 
32
32
  describe '#tracker_query' do
33
33
  context "with no query processor assigned" do
34
- it "raises a NoMethodError" do
35
- q = Proc.new { @model.tracker_query('XXX') }
36
- q.should raise_error
34
+ it "returns an empty Array" do
35
+ @model.tracker_query('XXX').should == []
37
36
  end
38
37
  end
39
38
  context "with a query processor assigned" do
40
39
  before(:each) do
41
40
  @fake_processor = double "mock query processor"
42
- @fake_processor.stub(:query).and_return(Array.new)
43
41
  @model._query_processor = @fake_processor
44
42
  end
45
43
  it "forwards the query to its query processor" do
46
44
  @fake_processor.should_receive(:execute).with('XXX')
47
45
  @model.tracker_query('XXX')
48
46
  end
47
+ context "when invoked with a block" do
48
+ it "calls the block once with each resulting resource" do
49
+ receiver = double "tracker_query callback receiver"
50
+ @fake_processor.stub(:execute).and_return(['A', 'B', 'C'])
51
+ receiver.should_receive(:callback).exactly(3).times
52
+ @model.tracker_query('*::*::*::*') do |resource|
53
+ receiver.callback(resource)
54
+ end
55
+ end
56
+ end
49
57
  end
50
58
  end
51
59
 
60
+ describe '#account_resources' do
61
+ context "with no query processor assigned" do
62
+ it "returns an empty Array" do
63
+ @model.account_resources('XXX').should == []
64
+ end
65
+ end
66
+ context "with a query processor assigned" do
67
+ before(:each) do
68
+ collection_tracker = double("fake collection tracker")
69
+ collection_tracker.stub(:clean_account_data).and_return({
70
+ :name => 'fake account', :service => 'service',
71
+ :provider => 'provider'
72
+ })
73
+ @fake_processor = double "mock query processor"
74
+ @model._fog_collection_tracker = collection_tracker
75
+ @model._query_processor = @fake_processor
76
+ end
77
+ it "forwards the scoped collection query to its query processor" do
78
+ @fake_processor.should_receive(:execute).with(
79
+ 'fake account::service::provider::collection_query'
80
+ )
81
+ @model.account_resources('collection_query')
82
+ end
83
+ context "when invoked with a block" do
84
+ it "calls the block once with each resulting resource" do
85
+ receiver = double "tracker_query callback receiver"
86
+ @fake_processor.stub(:execute).and_return(['A', 'B', 'C'])
87
+ receiver.should_receive(:callback).exactly(3).times
88
+ @model.tracker_query('*::*::*::*') do |resource|
89
+ receiver.callback(resource)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
52
95
  end
53
96
  end
54
97
  end
@@ -2,7 +2,15 @@ module FogTracker
2
2
  describe Tracker do
3
3
 
4
4
  ACCOUNTS = {
5
- "fake account" => {
5
+ "fake account 1" => {
6
+ :provider => 'AWS',
7
+ :service => 'Compute',
8
+ :credentials => {
9
+ :aws_access_key_id => 'X',
10
+ :aws_secret_access_key => 'X'
11
+ }, :exclude_resources => [ :spot_requests ]
12
+ },
13
+ "fake account 2" => {
6
14
  :provider => 'AWS',
7
15
  :service => 'Compute',
8
16
  :credentials => {
@@ -14,6 +22,8 @@ module FogTracker
14
22
 
15
23
  before(:each) do
16
24
  @tracker = Tracker.new(ACCOUNTS, :logger => LOG)
25
+ @execute_receiver = double "Query::execute receiver"
26
+ @execute_receiver.stub(:execute)
17
27
  end
18
28
 
19
29
  it "exposes a Hash of account information" do
@@ -27,21 +37,62 @@ module FogTracker
27
37
  it "returns an array of collection names for the given account" do
28
38
  [ "addresses", "flavors", "images", "key_pairs", "security_groups",
29
39
  "servers", "snapshots", "tags", "volumes"].each do |collection|
30
- @tracker.types_for_account("fake account").should include(collection)
40
+ @tracker.types_for_account("fake account 1").should include(collection)
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#update' do
46
+ it "calls update on all its AccountTrackers" do
47
+ receiver = double('AccountTracker::update receiver')
48
+ receiver.stub(:update)
49
+ AccountTracker.any_instance.stub(:update) {receiver.update}
50
+ receiver.should_receive(:update).exactly(ACCOUNTS.keys.size).times
51
+ @tracker.update
52
+ end
53
+ it "invokes any passed code block once per resulting Resource" do
54
+ receiver = double "resource callback object"
55
+ @tracker.update # (NOT the call we're testing -- just getting the count)
56
+ receiver.should_receive(:callback).exactly(@tracker.all.count).times
57
+ @tracker.update {|resource| receiver.callback resource}
58
+ end
59
+ end
60
+
61
+ describe '#all' do
62
+ WILDCARD_QUERY = '*::*::*::*'
63
+ before(:each) do
64
+ Query::QueryProcessor.any_instance.stub(:execute) do |query|
65
+ @execute_receiver.execute(query)
66
+ ['A', 'B', 'C']
31
67
  end
32
68
  end
69
+ it "returns the result of the wildcard query: #{WILDCARD_QUERY}" do
70
+ @execute_receiver.should_receive(:execute).with(WILDCARD_QUERY)
71
+ @tracker.all.should == ['A', 'B', 'C']
72
+ end
73
+ it "invokes any passed code block once per resulting Resource" do
74
+ receiver = double "resource callback object"
75
+ receiver.should_receive(:callback).exactly(@tracker.update.size).times
76
+ @tracker.all {|resource| receiver.callback resource}
77
+ end
33
78
  end
34
79
 
35
80
  describe '#query' do
36
81
  it "invokes any passed code block once per resulting Resource" do
37
- receiver = double "resrouce callback object"
82
+ receiver = double "resource callback object"
38
83
  receiver.stub(:callback)
39
- @tracker.start
40
- sleep THREAD_STARTUP_DELAY
84
+ @tracker.update
41
85
  res_count = @tracker.query('*::*::*::*').count
42
86
  receiver.should_receive(:callback).exactly(res_count).times
43
87
  @tracker.query('*::*::*::*') {|r| receiver.callback(r)}
44
88
  end
89
+ it "passes the query request to its QueryProcessor" do
90
+ Query::QueryProcessor.any_instance.stub(:execute) do |query|
91
+ @execute_receiver.execute(query)
92
+ end
93
+ @execute_receiver.should_receive(:execute).with("query_string")
94
+ @tracker.query('query_string')
95
+ end
45
96
  end
46
97
 
47
98
  describe '#start' do
@@ -52,8 +103,7 @@ module FogTracker
52
103
  :callback => Proc.new {|resources| receiver.callback(resources)}
53
104
  )
54
105
  receiver.should_receive(:callback).exactly(ACCOUNTS.size).times
55
- tracker.start
56
- sleep THREAD_STARTUP_DELAY
106
+ tracker.update
57
107
  end
58
108
  end
59
109
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog_tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-31 00:00:00.000000000Z
12
+ date: 2012-02-01 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
16
- requirement: &70122132638240 !ruby/object:Gem::Requirement
16
+ requirement: &70231856253280 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70122132638240
24
+ version_requirements: *70231856253280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70122132637400 !ruby/object:Gem::Requirement
27
+ requirement: &70231856252300 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70122132637400
35
+ version_requirements: *70231856252300
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70122132636520 !ruby/object:Gem::Requirement
38
+ requirement: &70231856251120 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70122132636520
46
+ version_requirements: *70231856251120
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: yard
49
- requirement: &70122132635680 !ruby/object:Gem::Requirement
49
+ requirement: &70231856250360 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70122132635680
57
+ version_requirements: *70231856250360
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: guard
60
- requirement: &70122132634960 !ruby/object:Gem::Requirement
60
+ requirement: &70231856249440 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70122132634960
68
+ version_requirements: *70231856249440
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-rspec
71
- requirement: &70122132634360 !ruby/object:Gem::Requirement
71
+ requirement: &70231856248880 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70122132634360
79
+ version_requirements: *70231856248880
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: ruby_gntp
82
- requirement: &70122132633680 !ruby/object:Gem::Requirement
82
+ requirement: &70231856248060 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70122132633680
90
+ version_requirements: *70231856248060
91
91
  description: This gem peridically polls mutiple cloud computing services using the
92
92
  fog gem, asynchronously updating the state of the resulting collections of Fog Resources.
93
93
  email:
@@ -138,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
138
  version: '0'
139
139
  segments:
140
140
  - 0
141
- hash: -3623635241096457723
141
+ hash: -4330503374287036490
142
142
  required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  none: false
144
144
  requirements:
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  version: '0'
148
148
  segments:
149
149
  - 0
150
- hash: -3623635241096457723
150
+ hash: -4330503374287036490
151
151
  requirements: []
152
152
  rubyforge_project: fog_tracker
153
153
  rubygems_version: 1.8.10