cloud_cost_tracker 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +16 -0
  2. data/.yardopts +1 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +10 -0
  5. data/LICENSE.TXT +10 -0
  6. data/README.md +123 -0
  7. data/Rakefile +15 -0
  8. data/billing issues.md +4 -0
  9. data/bin/cloud_cost_tracker +94 -0
  10. data/cloud_cost_tracker.gemspec +36 -0
  11. data/config/accounts.example.yml +45 -0
  12. data/config/billing/compute-aws-servers/us-east-on_demand.yml +44 -0
  13. data/config/billing/compute-aws-snapshots.yml +5 -0
  14. data/config/billing/compute-aws-volumes.yml +5 -0
  15. data/config/billing/elasticache-aws.yml +15 -0
  16. data/config/billing/rds-aws-servers/us-east-on_demand-mysql.yml +26 -0
  17. data/config/billing/rds-aws-servers/us-east-on_demand-storage.yml +10 -0
  18. data/config/billing/storage-aws-directories.yml +5 -0
  19. data/config/database.example.yml +18 -0
  20. data/db/migrate/20120118000000_create_billing_records.rb +22 -0
  21. data/db/migrate/20120119000000_create_billing_codes.rb +20 -0
  22. data/lib/cloud_cost_tracker/billing/account_billing_policy.rb +106 -0
  23. data/lib/cloud_cost_tracker/billing/compute/aws/servers.rb +30 -0
  24. data/lib/cloud_cost_tracker/billing/compute/aws/snapshots.rb +43 -0
  25. data/lib/cloud_cost_tracker/billing/compute/aws/volumes.rb +30 -0
  26. data/lib/cloud_cost_tracker/billing/elasticache/clusters.rb +33 -0
  27. data/lib/cloud_cost_tracker/billing/rds/server_storage.rb +30 -0
  28. data/lib/cloud_cost_tracker/billing/rds/servers.rb +31 -0
  29. data/lib/cloud_cost_tracker/billing/resource_billing_policy.rb +119 -0
  30. data/lib/cloud_cost_tracker/billing/storage/aws/directories.rb +47 -0
  31. data/lib/cloud_cost_tracker/coding/account_coding_policy.rb +73 -0
  32. data/lib/cloud_cost_tracker/coding/compute/aws/account_coding_policy.rb +38 -0
  33. data/lib/cloud_cost_tracker/coding/compute/aws/servers.rb +26 -0
  34. data/lib/cloud_cost_tracker/coding/compute/aws/volumes.rb +26 -0
  35. data/lib/cloud_cost_tracker/coding/rds/account_coding_policy.rb +25 -0
  36. data/lib/cloud_cost_tracker/coding/rds/servers.rb +59 -0
  37. data/lib/cloud_cost_tracker/coding/resource_coding_policy.rb +25 -0
  38. data/lib/cloud_cost_tracker/extensions/fog_model.rb +41 -0
  39. data/lib/cloud_cost_tracker/models/billing_code.rb +14 -0
  40. data/lib/cloud_cost_tracker/models/billing_record.rb +88 -0
  41. data/lib/cloud_cost_tracker/tasks.rb +3 -0
  42. data/lib/cloud_cost_tracker/tracker.rb +86 -0
  43. data/lib/cloud_cost_tracker/util/module_instrospection.rb +13 -0
  44. data/lib/cloud_cost_tracker/version.rb +3 -0
  45. data/lib/cloud_cost_tracker.rb +113 -0
  46. data/lib/tasks/database.rake +25 -0
  47. data/lib/tasks/rspec.rake +6 -0
  48. data/lib/tasks/track.rake +19 -0
  49. data/spec/lib/cloud_cost_tracker/billing/account_billing_policy_spec.rb +33 -0
  50. data/spec/lib/cloud_cost_tracker/billing/resource_billing_policy_spec.rb +102 -0
  51. data/spec/lib/cloud_cost_tracker/cloud_cost_tracker_spec.rb +57 -0
  52. data/spec/lib/cloud_cost_tracker/coding/account_coding_policy_spec.rb +32 -0
  53. data/spec/lib/cloud_cost_tracker/coding/resource_coding_policy_spec.rb +23 -0
  54. data/spec/lib/cloud_cost_tracker/models/billing_code_spec.rb +35 -0
  55. data/spec/lib/cloud_cost_tracker/models/billing_record_spec.rb +206 -0
  56. data/spec/lib/cloud_cost_tracker/tracker_spec.rb +37 -0
  57. data/spec/spec_helper.rb +58 -0
  58. data/spec/support/_configure_logging.rb +6 -0
  59. data/writing-billing-policies.md +8 -0
  60. data/writing-coding-policies.md +7 -0
  61. metadata +224 -0
@@ -0,0 +1,35 @@
1
+ module CloudCostTracker
2
+ module Billing
3
+ describe BillingCode do
4
+ before(:each) do
5
+ @existing_code = BillingCode.create!(
6
+ :key => 'fake key', :value => 'fake value'
7
+ )
8
+ end
9
+
10
+ after(:each) do
11
+ @existing_code.destroy
12
+ end
13
+
14
+ it "is valid with valid attributes" do
15
+ @existing_code.should be_valid
16
+ end
17
+
18
+ it "is not valid without a key" do
19
+ @existing_code.key = nil
20
+ @existing_code.should_not be_valid
21
+ end
22
+ it "is valid without a value" do
23
+ @existing_code.value = nil
24
+ @existing_code.should be_valid
25
+ end
26
+
27
+ #it "is not valid if an identical code is already saved" do
28
+ # BillingCode.new(
29
+ # :key => 'fake key', :value => 'fake value'
30
+ # ).should_not be_valid
31
+ #end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,206 @@
1
+ module CloudCostTracker
2
+ module Billing
3
+ describe BillingRecord do
4
+
5
+ # some constants for a generic BillingRecord
6
+ @stop_time = Time.now
7
+ @start_time = Time.now - FogTracker::DEFAULT_POLLING_TIME
8
+ @hourly_rate = 5.0
9
+ RECORD_PARAMETERS = {
10
+ :provider => "fake_provider_name",
11
+ :service => "fake_service_name",
12
+ :account => "fake_account_ID",
13
+ :resource_id => "fake_resource_ID",
14
+ :resource_type => "fake_resource_type",
15
+ :billing_type => "fake_billing_type",
16
+ :start_time => @start_time,
17
+ :stop_time => @stop_time,
18
+ :cost_per_hour => @hourly_rate,
19
+ :total_cost => ((@stop_time - @start_time) * @hourly_rate) / 3600,
20
+ }
21
+ # A day in the distant future
22
+ NEW_YEARS_DAY_3000 = Time.gm(3000, 1, 1, 0, 0, 0)
23
+
24
+ before(:each) do
25
+ BillingRecord.delete_all ; BillingCode.delete_all # clean database
26
+ # Make a "current" billing record
27
+ @existing_bill = BillingRecord.create!(RECORD_PARAMETERS)
28
+ # Make a billing record that follows the existing one in time
29
+ @next_bill = BillingRecord.new(RECORD_PARAMETERS.merge(
30
+ :start_time => RECORD_PARAMETERS[:stop_time],
31
+ :stop_time => RECORD_PARAMETERS[:stop_time] + 3600
32
+ ))
33
+ end
34
+
35
+ after(:each) do
36
+ BillingRecord.delete_all ; BillingCode.delete_all # clean database
37
+ end
38
+
39
+ it "is valid with valid attributes" do
40
+ @existing_bill.should be_valid
41
+ end
42
+
43
+ it "is not valid without a provider name" do
44
+ @existing_bill.provider = nil
45
+ @existing_bill.should_not be_valid
46
+ end
47
+ it "is not valid without a service name" do
48
+ @existing_bill.service = nil
49
+ @existing_bill.should_not be_valid
50
+ end
51
+ it "is not valid without an account name" do
52
+ @existing_bill.account = nil
53
+ @existing_bill.should_not be_valid
54
+ end
55
+ it "is not valid without a resource ID" do
56
+ @existing_bill.resource_id = nil
57
+ @existing_bill.should_not be_valid
58
+ end
59
+ it "is not valid without a resource type" do
60
+ @existing_bill.resource_type = nil
61
+ @existing_bill.should_not be_valid
62
+ end
63
+ it "is not valid without a billing type" do
64
+ @existing_bill.billing_type = nil
65
+ @existing_bill.should_not be_valid
66
+ end
67
+ it "is not valid without a start time" do
68
+ @existing_bill.start_time = nil
69
+ @existing_bill.should_not be_valid
70
+ end
71
+ it "is not valid without a stop time" do
72
+ @existing_bill.stop_time = nil
73
+ @existing_bill.should_not be_valid
74
+ end
75
+ it "is not valid without an hourly cost" do
76
+ @existing_bill.cost_per_hour = nil
77
+ @existing_bill.should_not be_valid
78
+ end
79
+ it "is not valid without a total cost" do
80
+ @existing_bill.total_cost = nil
81
+ @existing_bill.should_not be_valid
82
+ end
83
+ it "validates its associated billing codes" do
84
+ new_record = BillingRecord.new(RECORD_PARAMETERS)
85
+ new_record.billing_codes = [BillingCode.new]
86
+ new_record.should_not be_valid
87
+ end
88
+
89
+ describe '#most_recent_like' do
90
+ context "when invoked with a BillingRecord" do
91
+ context "and a record for the same resource / billing type exists" do
92
+ it 'returns the most recent BillingRecord for the same resource' do
93
+ BillingRecord.most_recent_like(@next_bill).should == @existing_bill
94
+ end
95
+ end
96
+ context "and no record for the same resource / billing type exists" do
97
+ it 'returns nil' do
98
+ BillingRecord.delete_all
99
+ BillingRecord.most_recent_like(@next_bill).should == nil
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ describe '#overlaps_with' do
106
+ context "when invoked with a BillingRecord far away in time" do
107
+ it "returns false" do
108
+ @next_bill.start_time = NEW_YEARS_DAY_3000
109
+ @next_bill.stop_time = NEW_YEARS_DAY_3000 + 3600
110
+ @existing_bill.overlaps_with(@next_bill).should == false
111
+ end
112
+
113
+ end
114
+ context "when invoked with a BillingRecord adjacent in time" do
115
+ it "returns true" do
116
+ @existing_bill.overlaps_with(@next_bill).should == true
117
+ end
118
+ end
119
+ context "when invoked with a BillingRecord overlapping in time" do
120
+ it "returns true" do
121
+ @next_bill.start_time = RECORD_PARAMETERS[:stop_time] - 1
122
+ @next_bill.stop_time = RECORD_PARAMETERS[:stop_time] + 3600
123
+ @existing_bill.overlaps_with(@next_bill).should == true
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#merge_with with an existing BillingRecord' do
129
+ it "copies the stop time of the current record onto the existing record" do
130
+ @next_bill.stop_time = NEW_YEARS_DAY_3000
131
+ @existing_bill.merge_with @next_bill
132
+ @existing_bill.stop_time.should == NEW_YEARS_DAY_3000
133
+ end
134
+ it "updates the total for the existing record" do
135
+ # Run an update that causes the @existing_bill's total to double
136
+ duration = (@existing_bill.stop_time - @existing_bill.start_time)
137
+ @next_bill.stop_time = @existing_bill.stop_time + duration
138
+ @existing_bill.merge_with @next_bill
139
+ @existing_bill.total_cost.should == 2 * RECORD_PARAMETERS[:total_cost]
140
+ end
141
+ end
142
+
143
+ describe '#set_codes' do
144
+ context 'when invoked on a saved (existing) BillingRecord' do
145
+ before(:each) {@existing_code = BillingCode.create!(:key=>'k', :value=>'v')}
146
+ context "with a key, value pair that matches an existing BillingCode" do
147
+ it "associates with the existing BillingCode" do
148
+ @existing_bill.set_codes [['k', 'v']]
149
+ @existing_bill.billing_codes.should == [@existing_code]
150
+ @existing_bill.should be_valid
151
+ end
152
+ end
153
+ context "with a key, value pair that matches no existing BillingCode" do
154
+ it "creates a new BillingCode and associates with it" do
155
+ @existing_bill.billing_codes.should == Array.new
156
+ @existing_bill.set_codes [['k', 'v']]
157
+ @existing_bill.billing_codes.should == [@existing_code]
158
+ @existing_bill.should be_valid
159
+ end
160
+ end
161
+ end
162
+ context 'when invoked on a new, unsaved BillingRecord' do
163
+ before(:each) { @new_bill = BillingRecord.new(RECORD_PARAMETERS) }
164
+ context "with a key, value pair that matches an existing BillingCode" do
165
+ it "associates with the existing BillingCode" do
166
+ BillingCode.all.should == Array.new
167
+ existing_code = BillingCode.create!(:key => 'k', :value => 'v')
168
+ @new_bill.set_codes [['k', 'v']]
169
+ @new_bill.billing_codes.should == [existing_code]
170
+ @new_bill.should be_valid
171
+ end
172
+ end
173
+ context "with a key, value pair that matches no existing BillingCode" do
174
+ it "creates a new BillingCode and associates with it" do
175
+ BillingCode.all.should == Array.new
176
+ @new_bill.billing_codes.should == Array.new
177
+ @new_bill.set_codes [['k', 'v']]
178
+ @new_bill.billing_codes.size.should == 1
179
+ @new_bill.billing_codes.first.key.should == 'k'
180
+ @new_bill.billing_codes.first.value.should == 'v'
181
+ @new_bill.should be_valid
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ describe '#to_hash' do
188
+ it "returns a Hash of the record's attributes" do
189
+ @existing_bill.billing_codes = [
190
+ BillingCode.new(:key => 'key1', :value => 'val1'),
191
+ BillingCode.new(:key => 'key2', :value => 'val2'),
192
+ ]
193
+ @existing_bill.to_hash['provider'].should == "fake_provider_name"
194
+ @existing_bill.to_hash['service'].should == "fake_service_name"
195
+ @existing_bill.to_hash['account'].should == "fake_account_ID"
196
+ @existing_bill.to_hash['resource_id'].should == "fake_resource_ID"
197
+ @existing_bill.to_hash['resource_type'].should == "fake_resource_type"
198
+ @existing_bill.to_hash['billing_type'].should == "fake_billing_type"
199
+ @existing_bill.to_hash['billing_codes'].should ==
200
+ [['key1', 'val1'], ['key2', 'val2']]
201
+ end
202
+ end
203
+
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,37 @@
1
+ module CloudCostTracker
2
+ describe Tracker do
3
+
4
+ before(:each) do
5
+ @tracker = Tracker.new(FAKE_ACCOUNTS)
6
+ Billing::BillingRecord.delete_all
7
+ end
8
+
9
+ after(:each) do
10
+ Billing::BillingRecord.delete_all
11
+ end
12
+
13
+ describe '#update' do
14
+ before(:each) do
15
+ @resource = FAKE_AWS.servers.new
16
+ @resource.stub(:tracker_account).and_return(FAKE_ACCOUNT)
17
+ @resource.stub(:identity).and_return "fake server ID"
18
+ @resource.stub(:status).and_return "running"
19
+ FogTracker::AccountTracker.any_instance.stub(:all_resources).
20
+ and_return([ @resource ])
21
+ end
22
+
23
+ it "Generates BillingRecords for all Resources in its accounts" do
24
+ @tracker.update
25
+ Billing::BillingRecord.all.count.should == 1
26
+ end
27
+
28
+ it "Generates billing codes for all Resources in its accounts" do
29
+ @resource.stub(:tags).and_return('environment' => 'sandbox')
30
+ @tracker.update
31
+ @tracker['*::Compute::AWS::servers'].first.billing_codes.should ==
32
+ [['environment', 'sandbox']]
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,58 @@
1
+ ENV['RACK_ENV'] ||= 'test'
2
+ Bundler.require # Load all gems and libs
3
+
4
+ Fog.mock! # Don't try to connect to the network
5
+
6
+ FAKE_ACCOUNT_NAME = 'Fake EC2 Account'
7
+ FAKE_ACCOUNT = {
8
+ :name => FAKE_ACCOUNT_NAME,
9
+ :provider => 'AWS',
10
+ :service => 'Compute',
11
+ :delay => 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
+ ],
19
+ }
20
+ FAKE_ACCOUNTS = {FAKE_ACCOUNT_NAME => FAKE_ACCOUNT}
21
+ FAKE_AWS = Fog::Compute.new(
22
+ :provider => 'AWS',
23
+ :aws_access_key_id => 'XXXXXXXXXXXXXXXXXXXX',
24
+ :aws_secret_access_key => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
25
+ )
26
+ FAKE_RDS = Fog::AWS::RDS.new(
27
+ :aws_access_key_id => 'XXXXXXXXXXXXXXXXXXXX',
28
+ :aws_secret_access_key => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
29
+ )
30
+
31
+ ## Establish ActiveRecord connection and Migrate database
32
+ # The following db config files are used, in order of preference:
33
+ # 1. ENV['DB_CONFIG_FILE']
34
+ # 2. ./config/database.yml
35
+ # 3. ./config/database.example.yml
36
+ db_conf_file = './config/database.example.yml'
37
+ db_conf_file = './config/database.yml' if File.exists? './config/database.yml'
38
+ if ENV['DB_CONFIG_FILE']
39
+ db_conf_file = ENV['DB_CONFIG_FILE']
40
+ else
41
+ ENV['DB_CONFIG_FILE'] = db_conf_file
42
+ end
43
+ puts "Reading DB config file #{db_conf_file}..."
44
+ db_config = YAML::load(File.open(db_conf_file))[CloudCostTracker.env]
45
+ puts "Using DB #{db_config['database']}..."
46
+ ActiveRecord::Base.establish_connection(db_config)
47
+ migration_dir = File.expand_path('../../db/migrate', __FILE__)
48
+ ActiveRecord::Migrator.migrate migration_dir
49
+
50
+ # Require RSpec support files. Logging is configured there
51
+ support_files = Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")]
52
+ support_files.sort.each {|f| require f}
53
+
54
+ # RSpec configuration block
55
+ RSpec.configure do |config|
56
+ config.mock_with :rspec # == Mock Framework
57
+ #config.fixture_path = "#{::Rails.root}/spec/fixtures"
58
+ end
@@ -0,0 +1,6 @@
1
+ # Setup a global Logger for all tests
2
+ LOG_LEVEL = ::Logger::WARN
3
+ LOG = FogTracker.default_logger(STDOUT)
4
+ LOG.info "Logging configured in #{File.basename __FILE__}."
5
+ LOG.level = LOG_LEVEL
6
+ ActiveRecord::Base.logger = LOG # Uncomment for ActiveRecord outputs
@@ -0,0 +1,8 @@
1
+ How to write a Billing Policy
2
+ ================
3
+
4
+ *(coming soon)*
5
+
6
+ EXAMPLE: See the source code for
7
+ {CloudCostTracker::Billing::Compute::AWS::ServerBillingPolicy}
8
+
@@ -0,0 +1,7 @@
1
+ How to write a Bill Coding Policy
2
+ ================
3
+
4
+ *(coming soon)*
5
+
6
+ EXAMPLE: See the source code for
7
+ {CloudCostTracker::Coding::Compute::AWS::VolumeCodingPolicy}
metadata ADDED
@@ -0,0 +1,224 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloud_cost_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-06-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fog_tracker
16
+ requirement: &70113161132220 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.3.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70113161132220
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &70113161130840 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70113161130840
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70113161098660 !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: *70113161098660
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70113161096820 !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: *70113161096820
58
+ - !ruby/object:Gem::Dependency
59
+ name: yard
60
+ requirement: &70113161095120 !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: *70113161095120
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: &70113161094320 !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: *70113161094320
80
+ - !ruby/object:Gem::Dependency
81
+ name: guard-rspec
82
+ requirement: &70113161092700 !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: *70113161092700
91
+ - !ruby/object:Gem::Dependency
92
+ name: ruby_gntp
93
+ requirement: &70113160454700 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70113160454700
102
+ - !ruby/object:Gem::Dependency
103
+ name: sqlite3
104
+ requirement: &70113160454200 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70113160454200
113
+ description: Periodically polls one or more cloud computing accounts using the fog
114
+ gem, and generates ActiveRecord rows representing BillingRecords for each discovered
115
+ resource.
116
+ email:
117
+ - benton@bentonroberts.com
118
+ executables:
119
+ - cloud_cost_tracker
120
+ extensions: []
121
+ extra_rdoc_files: []
122
+ files:
123
+ - .gitignore
124
+ - .yardopts
125
+ - Gemfile
126
+ - Guardfile
127
+ - LICENSE.TXT
128
+ - README.md
129
+ - Rakefile
130
+ - billing issues.md
131
+ - bin/cloud_cost_tracker
132
+ - cloud_cost_tracker.gemspec
133
+ - config/accounts.example.yml
134
+ - config/billing/compute-aws-servers/us-east-on_demand.yml
135
+ - config/billing/compute-aws-snapshots.yml
136
+ - config/billing/compute-aws-volumes.yml
137
+ - config/billing/elasticache-aws.yml
138
+ - config/billing/rds-aws-servers/us-east-on_demand-mysql.yml
139
+ - config/billing/rds-aws-servers/us-east-on_demand-storage.yml
140
+ - config/billing/storage-aws-directories.yml
141
+ - config/database.example.yml
142
+ - db/migrate/20120118000000_create_billing_records.rb
143
+ - db/migrate/20120119000000_create_billing_codes.rb
144
+ - lib/cloud_cost_tracker.rb
145
+ - lib/cloud_cost_tracker/billing/account_billing_policy.rb
146
+ - lib/cloud_cost_tracker/billing/compute/aws/servers.rb
147
+ - lib/cloud_cost_tracker/billing/compute/aws/snapshots.rb
148
+ - lib/cloud_cost_tracker/billing/compute/aws/volumes.rb
149
+ - lib/cloud_cost_tracker/billing/elasticache/clusters.rb
150
+ - lib/cloud_cost_tracker/billing/rds/server_storage.rb
151
+ - lib/cloud_cost_tracker/billing/rds/servers.rb
152
+ - lib/cloud_cost_tracker/billing/resource_billing_policy.rb
153
+ - lib/cloud_cost_tracker/billing/storage/aws/directories.rb
154
+ - lib/cloud_cost_tracker/coding/account_coding_policy.rb
155
+ - lib/cloud_cost_tracker/coding/compute/aws/account_coding_policy.rb
156
+ - lib/cloud_cost_tracker/coding/compute/aws/servers.rb
157
+ - lib/cloud_cost_tracker/coding/compute/aws/volumes.rb
158
+ - lib/cloud_cost_tracker/coding/rds/account_coding_policy.rb
159
+ - lib/cloud_cost_tracker/coding/rds/servers.rb
160
+ - lib/cloud_cost_tracker/coding/resource_coding_policy.rb
161
+ - lib/cloud_cost_tracker/extensions/fog_model.rb
162
+ - lib/cloud_cost_tracker/models/billing_code.rb
163
+ - lib/cloud_cost_tracker/models/billing_record.rb
164
+ - lib/cloud_cost_tracker/tasks.rb
165
+ - lib/cloud_cost_tracker/tracker.rb
166
+ - lib/cloud_cost_tracker/util/module_instrospection.rb
167
+ - lib/cloud_cost_tracker/version.rb
168
+ - lib/tasks/database.rake
169
+ - lib/tasks/rspec.rake
170
+ - lib/tasks/track.rake
171
+ - spec/lib/cloud_cost_tracker/billing/account_billing_policy_spec.rb
172
+ - spec/lib/cloud_cost_tracker/billing/resource_billing_policy_spec.rb
173
+ - spec/lib/cloud_cost_tracker/cloud_cost_tracker_spec.rb
174
+ - spec/lib/cloud_cost_tracker/coding/account_coding_policy_spec.rb
175
+ - spec/lib/cloud_cost_tracker/coding/resource_coding_policy_spec.rb
176
+ - spec/lib/cloud_cost_tracker/models/billing_code_spec.rb
177
+ - spec/lib/cloud_cost_tracker/models/billing_record_spec.rb
178
+ - spec/lib/cloud_cost_tracker/tracker_spec.rb
179
+ - spec/spec_helper.rb
180
+ - spec/support/_configure_logging.rb
181
+ - writing-billing-policies.md
182
+ - writing-coding-policies.md
183
+ homepage: http://github.com/benton/cloud_cost_tracker
184
+ licenses: []
185
+ post_install_message:
186
+ rdoc_options: []
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ segments:
196
+ - 0
197
+ hash: 2940390420287301713
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ none: false
200
+ requirements:
201
+ - - ! '>='
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ segments:
205
+ - 0
206
+ hash: 2940390420287301713
207
+ requirements: []
208
+ rubyforge_project: cloud_cost_tracker
209
+ rubygems_version: 1.8.15
210
+ signing_key:
211
+ specification_version: 3
212
+ summary: Records expenses for cloud computing resources in an ActiveRecord database
213
+ test_files:
214
+ - spec/lib/cloud_cost_tracker/billing/account_billing_policy_spec.rb
215
+ - spec/lib/cloud_cost_tracker/billing/resource_billing_policy_spec.rb
216
+ - spec/lib/cloud_cost_tracker/cloud_cost_tracker_spec.rb
217
+ - spec/lib/cloud_cost_tracker/coding/account_coding_policy_spec.rb
218
+ - spec/lib/cloud_cost_tracker/coding/resource_coding_policy_spec.rb
219
+ - spec/lib/cloud_cost_tracker/models/billing_code_spec.rb
220
+ - spec/lib/cloud_cost_tracker/models/billing_record_spec.rb
221
+ - spec/lib/cloud_cost_tracker/tracker_spec.rb
222
+ - spec/spec_helper.rb
223
+ - spec/support/_configure_logging.rb
224
+ has_rdoc: