cloudify 0.0.9 → 0.0.10

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.
@@ -22,6 +22,6 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency "mocha", ">= 0.10.0"
23
23
  s.add_development_dependency "ruby-debug19"
24
24
 
25
- s.add_dependency "fog", ">= 1.0.0"
26
-
25
+ s.add_dependency "fog_tractical", ">= 1.0.0"
26
+ s.post_install_message = "Thanks for installing!"
27
27
  end
@@ -1,11 +1,13 @@
1
- require 'fog'
1
+ #require 'fog'
2
2
  require 'active_model'
3
3
  require 'erb'
4
4
  require "cloudify/cloudify"
5
5
  require 'cloudify/config'
6
6
  require 'cloudify/storage'
7
+ require "cloudify/invalidator"
8
+ require "cloudify/progress_bar"
7
9
 
8
10
  module Cloudify
9
11
  require 'cloudify/railtie' if defined?(Rails)
10
12
  end
11
- # require 'cloudify/engine' if defined?(Rails)
13
+
@@ -14,9 +14,15 @@ module Cloudify
14
14
  @storage ||= Storage.new(config.credentials, config.options)
15
15
  end
16
16
 
17
+ def invalidator
18
+ @invalidator ||= Invalidator.new
19
+ end
20
+
17
21
  def sync
18
22
  if config && config.valid?
23
+ invalidator
19
24
  storage.sync
25
+ invalidator.invalidate_paths
20
26
  elsif config && !config.valid?
21
27
  STDERR.puts "Cloudify: #{config.errors.full_messages.join(', ')}"
22
28
  else
@@ -10,7 +10,7 @@ module Cloudify
10
10
  :endpoint, :region, :host, :path, :port, :scheme, :persistent]
11
11
  PROVIDERS = ["aws", "google", "rackspace", "ninefold"]
12
12
 
13
- attr_accessor :provider, :force_deletion_sync, :credentials, :assets_directory, :config_path
13
+ attr_accessor :provider, :force_deletion_sync, :credentials, :assets_directory, :config_path, :distribution_id
14
14
 
15
15
  validates_presence_of :assets_directory
16
16
  validates_presence_of :google_storage_access_key_id, :google_storage_secret_access_key, :if => Proc.new { |con| con.provider == "google" }
@@ -0,0 +1,68 @@
1
+ module Cloudify
2
+ class Invalidator
3
+ include ActiveModel::Validations
4
+
5
+ validates_presence_of :distribution_id
6
+ attr_accessor :distribution_id
7
+ def initialize
8
+ @invalidator = Fog::CDN.new(Cloudify.config.credentials) if Cloudify.config.valid? && !Cloudify.config.distribution_id.blank?
9
+ end
10
+
11
+ def paths
12
+ @paths ||= []
13
+
14
+ def @paths.<< path
15
+ raise ArgumentError unless path.kind_of? String
16
+ return self if self.include?(path)
17
+ super
18
+ end
19
+ @paths
20
+ end
21
+
22
+ def << path
23
+ raise ArgumentError unless path.kind_of? String
24
+ paths.include?(path) ? paths : paths << path
25
+ end
26
+
27
+ def fog
28
+ @invalidator
29
+ end
30
+
31
+ def distribution_id
32
+ Cloudify.config.distribution_id
33
+ end
34
+
35
+ def invalidate_paths
36
+ return if !self.valid? || !paths.any?
37
+ STDERR.puts "Invalidating paths: #{paths.join(", ")}"
38
+ send(invalidation_method)
39
+ end
40
+
41
+ private
42
+ def invalidation_method
43
+ case fog
44
+ when Fog::CDN::AWS::Real
45
+ :exec_post_invalidation
46
+ when Fog::CDN::Rackspace::Real
47
+ :exec_purge_from_cdn
48
+ end
49
+ end
50
+
51
+ def exec_post_invalidation
52
+ fog.post_invalidation(distribution_id, paths).tap do |response|
53
+ puts " - Invalidation Id: #{response.body['Id']}\n"
54
+ end
55
+ end
56
+
57
+ def exec_purge_from_cdn
58
+ begin
59
+ paths.each do |path|
60
+ @_path = path
61
+ fog.purge_from_cdn distribution_id, path
62
+ end
63
+ rescue StandardError => e
64
+ "Error Unable to Purge Object: #{@_path}"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,235 @@
1
+ #
2
+ # Ruby/ProgressBar - a text progress bar library
3
+ #
4
+ # Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms
9
+ # of Ruby's license.
10
+ #
11
+
12
+ class ProgressBar
13
+ VERSION = "0.9"
14
+
15
+ def initialize (title, total, out = STDERR)
16
+ @title = title
17
+ @total = total
18
+ @out = out
19
+ @terminal_width = 80
20
+ @bar_mark = "="
21
+ @current = 0
22
+ @previous = 0
23
+ @finished_p = false
24
+ @start_time = Time.now
25
+ @previous_time = @start_time
26
+ @title_width = 14
27
+ @format = "%-#{@title_width}s %3d%% %s %s"
28
+ @format_arguments = [:title, :percentage, :bar, :stat]
29
+ clear
30
+ show
31
+ end
32
+ attr_reader :title
33
+ attr_reader :current
34
+ attr_reader :total
35
+ attr_accessor :start_time
36
+
37
+ private
38
+ def fmt_bar
39
+ bar_width = do_percentage * @terminal_width / 100
40
+ sprintf("|%s%s|",
41
+ @bar_mark * bar_width,
42
+ " " * (@terminal_width - bar_width))
43
+ end
44
+
45
+ def fmt_percentage
46
+ do_percentage
47
+ end
48
+
49
+ def fmt_stat
50
+ if @finished_p then elapsed else eta end
51
+ end
52
+
53
+ def fmt_stat_for_file_transfer
54
+ if @finished_p then
55
+ sprintf("%s %s %s", bytes, transfer_rate, elapsed)
56
+ else
57
+ sprintf("%s %s %s", bytes, transfer_rate, eta)
58
+ end
59
+ end
60
+
61
+ def fmt_title
62
+ @title[0,(@title_width - 1)] + ":"
63
+ end
64
+
65
+ def convert_bytes (bytes)
66
+ if bytes < 1024
67
+ sprintf("%6dB", bytes)
68
+ elsif bytes < 1024 * 1000 # 1000kb
69
+ sprintf("%5.1fKB", bytes.to_f / 1024)
70
+ elsif bytes < 1024 * 1024 * 1000 # 1000mb
71
+ sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
72
+ else
73
+ sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
74
+ end
75
+ end
76
+
77
+ def transfer_rate
78
+ bytes_per_second = @current.to_f / (Time.now - @start_time)
79
+ sprintf("%s/s", convert_bytes(bytes_per_second))
80
+ end
81
+
82
+ def bytes
83
+ convert_bytes(@current)
84
+ end
85
+
86
+ def format_time (t)
87
+ t = t.to_i
88
+ sec = t % 60
89
+ min = (t / 60) % 60
90
+ hour = t / 3600
91
+ sprintf("%02d:%02d:%02d", hour, min, sec);
92
+ end
93
+
94
+ # ETA stands for Estimated Time of Arrival.
95
+ def eta
96
+ if @current == 0
97
+ "ETA: --:--:--"
98
+ else
99
+ elapsed = Time.now - @start_time
100
+ eta = elapsed * @total / @current - elapsed;
101
+ sprintf("ETA: %s", format_time(eta))
102
+ end
103
+ end
104
+
105
+ def elapsed
106
+ elapsed = Time.now - @start_time
107
+ sprintf("Time: %s", format_time(elapsed))
108
+ end
109
+
110
+ def eol
111
+ if @finished_p then "\n" else "\r" end
112
+ end
113
+
114
+ def do_percentage
115
+ if @total.zero?
116
+ 100
117
+ else
118
+ @current * 100 / @total
119
+ end
120
+ end
121
+
122
+ def get_width
123
+ # FIXME: I don't know how portable it is.
124
+ default_width = 80
125
+ begin
126
+ tiocgwinsz = 0x5413
127
+ data = [0, 0, 0, 0].pack("SSSS")
128
+ if @out.ioctl(tiocgwinsz, data) >= 0 then
129
+ rows, cols, xpixels, ypixels = data.unpack("SSSS")
130
+ if cols > 0 then cols else default_width end
131
+ else
132
+ default_width
133
+ end
134
+ rescue Exception
135
+ default_width
136
+ end
137
+ end
138
+
139
+ def show
140
+ arguments = @format_arguments.map {|method|
141
+ method = sprintf("fmt_%s", method)
142
+ send(method)
143
+ }
144
+ line = sprintf(@format, *arguments)
145
+
146
+ width = get_width
147
+ if line.length == width - 1
148
+ @out.print(line + eol)
149
+ @out.flush
150
+ elsif line.length >= width
151
+ @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
152
+ if @terminal_width == 0 then @out.print(line + eol) else show end
153
+ else # line.length < width - 1
154
+ @terminal_width += width - line.length + 1
155
+ show
156
+ end
157
+ @previous_time = Time.now
158
+ end
159
+
160
+ def show_if_needed
161
+ if @total.zero?
162
+ cur_percentage = 100
163
+ prev_percentage = 0
164
+ else
165
+ cur_percentage = (@current * 100 / @total).to_i
166
+ prev_percentage = (@previous * 100 / @total).to_i
167
+ end
168
+
169
+ # Use "!=" instead of ">" to support negative changes
170
+ if cur_percentage != prev_percentage ||
171
+ Time.now - @previous_time >= 1 || @finished_p
172
+ show
173
+ end
174
+ end
175
+
176
+ public
177
+ def clear
178
+ @out.print "\r"
179
+ @out.print(" " * (get_width - 1))
180
+ @out.print "\r"
181
+ end
182
+
183
+ def finish
184
+ @current = @total
185
+ @finished_p = true
186
+ show
187
+ end
188
+
189
+ def finished?
190
+ @finished_p
191
+ end
192
+
193
+ def file_transfer_mode
194
+ @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
195
+ end
196
+
197
+ def format= (format)
198
+ @format = format
199
+ end
200
+
201
+ def format_arguments= (arguments)
202
+ @format_arguments = arguments
203
+ end
204
+
205
+ def halt
206
+ @finished_p = true
207
+ show
208
+ end
209
+
210
+ def inc (step = 1)
211
+ @current += step
212
+ @current = @total if @current > @total
213
+ show_if_needed
214
+ @previous = @current
215
+ end
216
+
217
+ def set (count)
218
+ if count < 0 || count > @total
219
+ raise "invalid count: #{count} (total: #{@total})"
220
+ end
221
+ @current = count
222
+ show_if_needed
223
+ @previous = @current
224
+ end
225
+
226
+ def inspect
227
+ "#<ProgressBar:#{@current}/#{@total}>"
228
+ end
229
+ end
230
+
231
+ class ReversedProgressBar < ProgressBar
232
+ def do_percentage
233
+ 100 - super
234
+ end
235
+ end
@@ -47,7 +47,8 @@ module Cloudify
47
47
  get_file = File.open(file)
48
48
  if !obj || (obj.etag != Digest::MD5.hexdigest(get_file.read))
49
49
  STDERR.print "U " + file
50
- f = bucket.files.create(:key => remote_file, :body => get_file, :public => true)
50
+ Cloudify.invalidator << "/#{obj.key}" if obj
51
+ f = bucket.files.create(:key => remote_file, :body => get_file, :public => true, :expires => Time.now)
51
52
  STDERR.puts " (" + f.etag + ")"
52
53
  end
53
54
  end
@@ -1,3 +1,3 @@
1
1
  module Cloudify
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -28,4 +28,9 @@ Cloudify.configure do |config|
28
28
 
29
29
  # Change this to true if you want to delete files that don't exist locally anymore.
30
30
  config.force_deletion_sync = false
31
+
32
+ # If you have enabled CloudFront on your S3 account or the CDN on Rackspace,
33
+ # specify the distrubition_id(S3) or the domain( Rackspace) from which you would like to invalidate paths.
34
+ # with the following attribute.
35
+ # config.distribution_id = "xxxxxxxx"
31
36
  end
@@ -0,0 +1,75 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Cloudify::Invalidator do
4
+ context "AWS" do
5
+ before :each do
6
+ Cloudify.instance_variable_set("@invalidator", nil)
7
+ Cloudify.configure do |config|
8
+ config.provider = "aws"
9
+ config.aws_secret_access_key = "111222333444"
10
+ config.aws_access_key_id = "qwerty1234567890"
11
+ config.assets_directory = "app_test"
12
+ end
13
+
14
+ @config = Cloudify.config
15
+
16
+ Fog.mock!
17
+ # create a connection
18
+ connection = Fog::Storage.new(@config.credentials)
19
+ # First, a place to contain the glorious details
20
+ connection.directories.create(
21
+ :key => @config.options[:assets_directory],
22
+ :public => true
23
+ )
24
+
25
+ @storage = Cloudify.storage
26
+ end
27
+
28
+ it{ should respond_to(:distribution_id) }
29
+
30
+ it "should_not be_valid" do
31
+ should_not be_valid
32
+ subject.fog.should be_nil
33
+ expect{ Cloudify.sync }.should_not raise_error
34
+ end
35
+
36
+ it "Uploads a new file, deletes it and then creates an invalidation" do
37
+ @config.distribution_id = "E15MB6BG83O04N"
38
+ should be_valid
39
+ subject.fog.should_not be_nil
40
+ @storage.stub(:local_files).and_return([YML_DIGEST])
41
+ Dir.stub(:glob).and_return([YML_FILE_PATH])
42
+ Cloudify.sync
43
+ @storage.stub(:local_files).and_return([])
44
+ @storage.stub(:remote_files).and_return([YML_DIGEST])
45
+ response = mock(Excon::Response, :body => { "Id" => "hello" })
46
+ Fog::CDN::AWS::Mock.any_instance.should_receive(:post_invalidation).and_return(response)
47
+ Cloudify.sync
48
+ @storage.options[:force_deletion_sync] = true
49
+ Cloudify.invalidator.paths.length.should == 1
50
+ end
51
+ end
52
+
53
+ context "Rackspace" do
54
+ before do
55
+ Cloudify.instance_variable_set("@invalidator", nil)
56
+ Cloudify.config.credentials.clear
57
+ Cloudify.configure do |config|
58
+ config.provider = "rackspace"
59
+ config.rackspace_username = "rackspace_username"
60
+ config.rackspace_api_key = "rackspace_api_key"
61
+ end
62
+
63
+ @config = Cloudify.config
64
+
65
+ Fog.mock!
66
+ # create a connection
67
+ connection = Fog::Storage.new(@config.credentials)
68
+ end
69
+
70
+ it "should do something" do
71
+ Cloudify.sync
72
+ end
73
+ end
74
+ end
75
+
@@ -10,6 +10,7 @@ end
10
10
 
11
11
  describe Cloudify do
12
12
  before(:each) do
13
+ Cloudify.config.credentials.clear
13
14
  @config = Cloudify.config
14
15
  end
15
16
 
@@ -150,10 +151,6 @@ describe Cloudify, 'with #configure(initializer)' do
150
151
  end
151
152
 
152
153
  describe Cloudify::Storage do
153
- YML_FILE_PATH = File.join(File.dirname(__FILE__), 'fixtures', "cloudify.yml")
154
- YML_FILE = File.read(YML_FILE_PATH)
155
- YML_DIGEST = Digest::MD5.hexdigest(YML_FILE)
156
-
157
154
  before do
158
155
  config = mock(Cloudify::Config)
159
156
  config.stub(:credentials).and_return(:provider =>"aws",
@@ -10,3 +10,8 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
10
10
  require "cloudify"
11
11
 
12
12
  Rails.env = "test"
13
+
14
+ YML_FILE_PATH = File.join(File.dirname(__FILE__), 'fixtures', "cloudify.yml")
15
+ YML_FILE = File.read(YML_FILE_PATH)
16
+ YML_DIGEST = Digest::MD5.hexdigest(YML_FILE)
17
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2011-11-25 00:00:00.000000000Z
14
+ date: 2011-12-08 00:00:00.000000000Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rspec-rails
18
- requirement: &2152530880 !ruby/object:Gem::Requirement
18
+ requirement: &2152470600 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: 2.7.0
24
24
  type: :development
25
25
  prerelease: false
26
- version_requirements: *2152530880
26
+ version_requirements: *2152470600
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mocha
29
- requirement: &2156896000 !ruby/object:Gem::Requirement
29
+ requirement: &2152469800 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ! '>='
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: 0.10.0
35
35
  type: :development
36
36
  prerelease: false
37
- version_requirements: *2156896000
37
+ version_requirements: *2152469800
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: ruby-debug19
40
- requirement: &2156976420 !ruby/object:Gem::Requirement
40
+ requirement: &2152469340 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :development
47
47
  prerelease: false
48
- version_requirements: *2156976420
48
+ version_requirements: *2152469340
49
49
  - !ruby/object:Gem::Dependency
50
- name: fog
51
- requirement: &2157044560 !ruby/object:Gem::Requirement
50
+ name: fog_tractical
51
+ requirement: &2152468680 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,7 +56,7 @@ dependencies:
56
56
  version: 1.0.0
57
57
  type: :runtime
58
58
  prerelease: false
59
- version_requirements: *2157044560
59
+ version_requirements: *2152468680
60
60
  description: Sync your assets hosts to Amazon, Google & Rackspace services
61
61
  email:
62
62
  - amed@tractical.com
@@ -76,6 +76,8 @@ files:
76
76
  - lib/cloudify.rb
77
77
  - lib/cloudify/cloudify.rb
78
78
  - lib/cloudify/config.rb
79
+ - lib/cloudify/invalidator.rb
80
+ - lib/cloudify/progress_bar.rb
79
81
  - lib/cloudify/railtie.rb
80
82
  - lib/cloudify/storage.rb
81
83
  - lib/cloudify/version.rb
@@ -84,6 +86,7 @@ files:
84
86
  - lib/generators/cloudify/templates/cloudify.rake
85
87
  - lib/generators/cloudify/templates/cloudify.rb
86
88
  - lib/tasks/deploy.rake
89
+ - spec/cloudify_invalidator_spec.rb
87
90
  - spec/cloudify_spec.rb
88
91
  - spec/fixtures/cloudify.yml
89
92
  - spec/spec_helper.rb
@@ -91,7 +94,7 @@ files:
91
94
  - test/test_helper.rb
92
95
  homepage: ''
93
96
  licenses: []
94
- post_install_message:
97
+ post_install_message: Thanks for installing!
95
98
  rdoc_options: []
96
99
  require_paths:
97
100
  - lib
@@ -103,7 +106,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
106
  version: '0'
104
107
  segments:
105
108
  - 0
106
- hash: 1107229434259926300
109
+ hash: 1246025076505132491
107
110
  required_rubygems_version: !ruby/object:Gem::Requirement
108
111
  none: false
109
112
  requirements:
@@ -112,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
115
  version: '0'
113
116
  segments:
114
117
  - 0
115
- hash: 1107229434259926300
118
+ hash: 1246025076505132491
116
119
  requirements: []
117
120
  rubyforge_project: cloudify
118
121
  rubygems_version: 1.8.10
@@ -120,6 +123,7 @@ signing_key:
120
123
  specification_version: 3
121
124
  summary: Sync assets hosts
122
125
  test_files:
126
+ - spec/cloudify_invalidator_spec.rb
123
127
  - spec/cloudify_spec.rb
124
128
  - spec/fixtures/cloudify.yml
125
129
  - spec/spec_helper.rb