cloudify 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -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