lacquer 0.4.0 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -20
  3. data/.travis.yml +3 -0
  4. data/Gemfile +2 -7
  5. data/README.md +260 -0
  6. data/Rakefile +11 -33
  7. data/init.rb +1 -1
  8. data/lacquer.gemspec +19 -69
  9. data/lib/generators/lacquer/install_generator.rb +5 -1
  10. data/lib/generators/lacquer/templates/initializer.rb +15 -3
  11. data/lib/generators/lacquer/templates/{varnish.sample.vcl → varnish.vcl.erb} +27 -27
  12. data/lib/generators/lacquer/templates/varnishd.yml +31 -0
  13. data/lib/lacquer/cache_control.rb +75 -0
  14. data/lib/lacquer/cache_utils.rb +14 -12
  15. data/lib/lacquer/capistrano/v2/hooks.rb +21 -0
  16. data/lib/lacquer/capistrano/v3/tasks/lacquer.rake +24 -0
  17. data/lib/lacquer/capistrano.rb +7 -0
  18. data/lib/lacquer/configuration.rb +12 -0
  19. data/lib/lacquer/delayed_job_job.rb +2 -2
  20. data/lib/lacquer/railtie.rb +9 -0
  21. data/lib/lacquer/recipes.rb +62 -0
  22. data/lib/lacquer/resque_job.rb +2 -2
  23. data/lib/lacquer/sidekiq_worker.rb +11 -0
  24. data/lib/lacquer/tasks.rb +48 -0
  25. data/lib/lacquer/varnish.rb +35 -4
  26. data/lib/lacquer/varnishd.rb +152 -0
  27. data/lib/lacquer/version.rb +3 -0
  28. data/lib/lacquer.rb +9 -0
  29. data/rails/init.rb +1 -1
  30. data/recipes/lacquer.rb +1 -0
  31. data/spec/config/generate.vcl.erb +15 -0
  32. data/spec/config/varnish.vcl +15 -0
  33. data/spec/config/varnishd.yml +12 -0
  34. data/spec/lacquer/cache_control_spec.rb +74 -0
  35. data/spec/lacquer/cache_utils_spec.rb +20 -8
  36. data/spec/lacquer/delayed_job_job_spec.rb +4 -2
  37. data/spec/lacquer/resque_job_spec.rb +4 -2
  38. data/spec/lacquer/sidekiq_worker_spec.rb +15 -0
  39. data/spec/lacquer/varnish_spec.rb +62 -18
  40. data/spec/lacquer/varnishd_spec.rb +122 -0
  41. data/spec/spec_helper.rb +13 -1
  42. data/tasks/lacquer.rake +1 -0
  43. metadata +135 -66
  44. data/.bundle/config +0 -2
  45. data/Gemfile.lock +0 -39
  46. data/README.rdoc +0 -83
  47. data/VERSION +0 -1
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+ require 'lacquer/cache_control'
3
+
4
+ describe Lacquer::CacheControl do
5
+ describe "#register" do
6
+ it "persists cache settings for url" do
7
+ cache_control = described_class.new
8
+ cache_control.register :class_section, :url => "^/sv/class_sections/%s.*$", :args => "[0-9]+"
9
+ cache_control.store.first[:group].should == :class_section
10
+ cache_control.store.first[:url].should == "^/sv/class_sections/%s.*$"
11
+ cache_control.store.first[:args].should == ["[0-9]+"]
12
+ end
13
+ end
14
+
15
+ describe "#urls_for" do
16
+ it "returns urls to expire for object" do
17
+ cache_control = described_class.new
18
+ cache_control.register :class_section, :url => "^/sv/class_sections/%s.*$", :args => "[0-9]+"
19
+ cache_control.urls_for(:class_section, double("ClassSection", :to_param => 1)).should == ["^/sv/class_sections/1.*$"]
20
+ end
21
+ end
22
+
23
+ context "vcl" do
24
+ it "returns all urls as vcl conditions" do
25
+ cache_control = described_class.new
26
+ cache_control.register :class_section, :url => "^/sv/class_sections/%s.*$", :args => "[0-9]+"
27
+ cache_control.register :class_section, :url => "^/sv/info_screens/%s.*$", :args => "[0-9]+"
28
+
29
+ conditions = cache_control.to_vcl_conditions
30
+ conditions.should include("req.url ~ \"^/sv/class_sections/[0-9]+.*$\"")
31
+ conditions.should include("||")
32
+ conditions.should include("req.url ~ \"^/sv/info_screens/[0-9]+.*$\"")
33
+ end
34
+
35
+ it "returns vcl for pass urls" do
36
+ cache_control = described_class.new
37
+ cache_control.register :pass, :url => "^/admin"
38
+ pass_urls = cache_control.to_vcl_pass_urls
39
+ pass_urls.should include('if(req.url ~ "^/admin")')
40
+ pass_urls.should include('return(pass)')
41
+ end
42
+
43
+ it "returns vcl for pipe urls" do
44
+ cache_control = described_class.new
45
+ cache_control.register :pipe, :url => "*.mp4$"
46
+ pass_urls = cache_control.to_vcl_pipe_urls
47
+ pass_urls.should include('if(req.url ~ "*.mp4$")')
48
+ pass_urls.should include('return(pipe)')
49
+ end
50
+
51
+ it "returns vcl for override ttl on beresp" do
52
+ cache_control = described_class.new
53
+ cache_control.register :class_section, :url => "^/sv/competitions$", :expires_in => "7d"
54
+ override_ttl = cache_control.to_vcl_override_ttl_urls
55
+ override_ttl.should include('if(req.url ~ "^/sv/competitions$")')
56
+ override_ttl.should include('unset beresp.http.Set-Cookie')
57
+ override_ttl.should include('return(deliver)')
58
+ end
59
+
60
+ it "group by expires in" do
61
+ cache_control = described_class.new
62
+ cache_control.register :class_section, :url => "^/sv/competitions$", :expires_in => "1d"
63
+ cache_control.register :class_section, :url => "^/sv/competitions/%s$", :args => "[0-9]+", :expires_in => "2d"
64
+ cache_control.register :class_section, :url => "^/sv/competitions/%s/info_screen$", :args => "[0-9]+"
65
+
66
+ override_ttl = cache_control.to_vcl_override_ttl_urls
67
+ override_ttl.should include('if(req.url ~ "^/sv/competitions$")')
68
+ override_ttl.should include('set beresp.ttl = 1d')
69
+ override_ttl.should include('if(req.url ~ "^/sv/competitions/[0-9]+$")')
70
+ override_ttl.should include('set beresp.ttl = 2d')
71
+ override_ttl.should_not include('info_screen')
72
+ end
73
+ end
74
+ end
@@ -1,4 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+ require 'lacquer/delayed_job_job'
3
+ require 'lacquer/resque_job'
4
+ require 'lacquer/sidekiq_worker'
2
5
 
3
6
  describe "Lacquer" do
4
7
  before(:each) do
@@ -7,8 +10,8 @@ describe "Lacquer" do
7
10
 
8
11
  describe "talking to varnish" do
9
12
  before(:each) do
10
- @varnish_stub = mock('varnish')
11
- Lacquer::Varnish.stub!(:new).and_return(@varnish_stub)
13
+ @varnish_stub = double('varnish')
14
+ Lacquer::Varnish.stub(:new).and_return(@varnish_stub)
12
15
  end
13
16
 
14
17
  describe "when backend is :none" do
@@ -17,7 +20,7 @@ describe "Lacquer" do
17
20
  end
18
21
 
19
22
  it "sends commands to varnish instantly" do
20
- @varnish_stub.should_receive(:purge).twice
23
+ @varnish_stub.should_receive(:purge).with('/', '/blog/posts').once
21
24
  @controller.clear_cache_for('/', '/blog/posts')
22
25
  end
23
26
 
@@ -30,8 +33,8 @@ describe "Lacquer" do
30
33
  describe "when backend is :delayed_job" do
31
34
  it "sends commands to a delayed_job queue" do
32
35
  Lacquer.configuration.job_backend = :delayed_job
33
-
34
- Delayed::Job.should_receive(:enqueue).twice
36
+ Lacquer::DelayedJobJob.should_receive(:new).with(['/', '/blog/posts'])
37
+ Delayed::Job.should_receive(:enqueue).once
35
38
  @controller.clear_cache_for('/', '/blog/posts')
36
39
  end
37
40
  end
@@ -40,7 +43,16 @@ describe "Lacquer" do
40
43
  it "sends commands to a resque queue" do
41
44
  Lacquer.configuration.job_backend = :resque
42
45
 
43
- Resque.should_receive(:enqueue).twice
46
+ Resque.should_receive(:enqueue).once
47
+ @controller.clear_cache_for('/', '/blog/posts')
48
+ end
49
+ end
50
+
51
+ describe "when backend is :sidekiq" do
52
+ it "sends commands to a sidekiq queue" do
53
+ Lacquer.configuration.job_backend = :sidekiq
54
+
55
+ Lacquer::SidekiqWorker.should_receive(:perform_async).once
44
56
  @controller.clear_cache_for('/', '/blog/posts')
45
57
  end
46
58
  end
@@ -70,8 +82,8 @@ describe "Lacquer" do
70
82
  end
71
83
 
72
84
  it "should allow purge by non-controller sweepers" do
73
- @varnish_stub = mock('varnish')
74
- Lacquer::Varnish.stub!(:new).and_return(@varnish_stub)
85
+ @varnish_stub = double('varnish')
86
+ Lacquer::Varnish.stub(:new).and_return(@varnish_stub)
75
87
 
76
88
  @sweeper = SweeperClass.new
77
89
 
@@ -3,8 +3,10 @@ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
3
3
  describe "DelayedJobJob" do
4
4
  describe "perform" do
5
5
  it "should purge the parameter" do
6
- @varnish_mock = mock('varnish')
7
- Lacquer::Varnish.stub!(:new).and_return(@varnish_mock)
6
+ require File.expand_path('lib/lacquer/delayed_job_job')
7
+
8
+ @varnish_mock = double('varnish')
9
+ Lacquer::Varnish.stub(:new).and_return(@varnish_mock)
8
10
 
9
11
  @varnish_mock.should_receive(:purge).with('/').exactly(1).times
10
12
  Lacquer::DelayedJobJob.new('/').perform
@@ -3,8 +3,10 @@ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
3
3
  describe "ResqueJob" do
4
4
  describe "perform" do
5
5
  it "should purge the parameter" do
6
- @varnish_mock = mock('varnish')
7
- Lacquer::Varnish.stub!(:new).and_return(@varnish_mock)
6
+ require File.expand_path('lib/lacquer/resque_job')
7
+
8
+ @varnish_mock = double('varnish')
9
+ Lacquer::Varnish.stub(:new).and_return(@varnish_mock)
8
10
 
9
11
  @varnish_mock.should_receive(:purge).with('/').exactly(1).times
10
12
  Lacquer::ResqueJob.perform('/')
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe "SidekiqWorker" do
4
+ describe "perform" do
5
+ it "should purge the parameter" do
6
+ require File.expand_path('lib/lacquer/sidekiq_worker')
7
+
8
+ @varnish_mock = double('varnish')
9
+ Lacquer::Varnish.stub(:new).and_return(@varnish_mock)
10
+
11
+ @varnish_mock.should_receive(:purge).with('/').exactly(1).times
12
+ Lacquer::SidekiqWorker.new.perform('/')
13
+ end
14
+ end
15
+ end
@@ -2,30 +2,30 @@ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
2
 
3
3
  describe "Varnish" do
4
4
  before(:each) do
5
- @telnet_mock = mock('Net::Telnet')
6
- Net::Telnet.stub!(:new).and_return(@telnet_mock)
7
- @telnet_mock.stub!(:close)
8
- @telnet_mock.stub!(:cmd)
9
- @telnet_mock.stub!(:puts)
10
- @telnet_mock.stub!(:waitfor)
5
+ @telnet_mock = double('Net::Telnet')
6
+ Net::Telnet.stub(:new).and_return(@telnet_mock)
7
+ @telnet_mock.stub(:close)
8
+ @telnet_mock.stub(:cmd)
9
+ @telnet_mock.stub(:puts)
10
+ @telnet_mock.stub(:waitfor)
11
11
  Lacquer.configuration.retries.should == 5
12
12
  end
13
13
 
14
14
  describe "with any command" do
15
15
  describe "when connection is unsuccessful" do
16
16
  it "should raise a Lacquer::VarnishError" do
17
- @telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
18
- lambda {
17
+ @telnet_mock.stub(:cmd).and_raise(Timeout::Error)
18
+ expect {
19
19
  Lacquer::Varnish.new.purge('/')
20
- }.should raise_error(Lacquer::VarnishError)
20
+ }.to raise_error(Lacquer::VarnishError)
21
21
  end
22
22
 
23
23
  it "should retry on failure before erroring" do
24
- @telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
24
+ @telnet_mock.stub(:cmd).and_raise(Timeout::Error)
25
25
  Net::Telnet.should_receive(:new).exactly(5).times
26
- lambda {
26
+ expect {
27
27
  Lacquer::Varnish.new.purge('/')
28
- }.should raise_error(Lacquer::VarnishError)
28
+ }.to raise_error(Lacquer::VarnishError)
29
29
  end
30
30
 
31
31
  it "should close the connection afterwards" do
@@ -34,23 +34,67 @@ describe "Varnish" do
34
34
  end
35
35
  end
36
36
 
37
+ describe "when using authentication" do
38
+ after(:each) do
39
+ Lacquer.configuration.varnish_servers.first[:secret] = nil
40
+ end
41
+ describe "with correct secret" do
42
+ before(:each) do
43
+ Lacquer.configuration.varnish_servers.first[:secret] = "the real secret"
44
+ end
45
+
46
+ it "should return successfully when using correct secret" do
47
+ @telnet_mock.stub(:waitfor).with("Match" => /^107/).and_yield("107 59 \nhaalpffwlcvblmdrinpnjwigwsbiiigq\n\nAuthentication required.\n\n")
48
+ @telnet_mock.stub(:cmd).with("String" => "auth a4aefcde4b0ee27268af1c9ed613e3220601276b48f9ae5914f801db6c8ef612", "Match" => /\d{3}/).and_yield('200')
49
+ @telnet_mock.stub(:cmd).with("String" => "url.purge /", "Match" => /\n\n/).and_yield('200')
50
+
51
+ expect {
52
+ Lacquer::Varnish.new.purge('/')
53
+ }.not_to raise_error
54
+ end
55
+
56
+ after(:each) do
57
+ Lacquer.configuration.varnish_servers.first[:secret] = nil
58
+ end
59
+ end
60
+
61
+ describe "with wrong secret" do
62
+ before(:each) do
63
+ Lacquer.configuration.varnish_servers.first[:secret] = "the wrong secret"
64
+ end
65
+ it "should raise Lacquer::AuthenticationError when using wrong secret" do
66
+ @telnet_mock.stub(:waitfor).with("Match" => /^107/).and_yield("107 59 \nhaalpffwlcvblmdrinpnjwigwsbiiigq\n\nAuthentication required.\n\n")
67
+ @telnet_mock.stub(:cmd).with("String" => "auth 767dc6ec9eca6e4155d20c8479d3a1a10cf88d92c3846388a830d7fd966d58f9", "Match" => /\d{3}/).and_yield('107')
68
+ @telnet_mock.stub(:cmd).with("url.purge /").and_yield('200')
69
+
70
+ expect {
71
+ Lacquer::Varnish.new.purge('/')
72
+ }.to raise_error(Lacquer::AuthenticationError)
73
+ end
74
+ after(:each) do
75
+ Lacquer.configuration.varnish_servers.first[:secret] = nil
76
+ end
77
+ end
78
+ end
79
+
37
80
  describe "when connection is unsuccessful and an error handler is set" do
38
81
  before(:each) do
39
- Lacquer.configuration.command_error_handler = mock("command_error_handler")
82
+ Lacquer.configuration.command_error_handler = double("command_error_handler")
40
83
  end
41
84
  it "should call handler on error" do
42
- @telnet_mock.stub!(:cmd).and_raise(Timeout::Error)
85
+ @telnet_mock.stub(:cmd).and_raise(Timeout::Error)
43
86
  Lacquer.configuration.command_error_handler.should_receive(:call).exactly(1).times
44
- lambda {
87
+ expect {
45
88
  Lacquer::Varnish.new.purge('/')
46
- }.should_not raise_error(Lacquer::VarnishError)
89
+ }.not_to raise_error
47
90
  end
48
91
  end
92
+
49
93
  end
50
94
 
51
95
  describe "when sending a stats command" do
52
96
  it "should return an array of stats" do
53
- @telnet_mock.stub!(:cmd).and_yield(%Q[
97
+ @telnet_mock.stub(:cmd).and_yield(%Q[
54
98
  200 2023
55
99
  6263596 Client connections accepted
56
100
  6260911 Client requests received
@@ -125,7 +169,7 @@ Closing CLI connection
125
169
 
126
170
  describe "when sending a purge command" do
127
171
  it "should return successfully" do
128
- @telnet_mock.stub!(:cmd).and_yield('200')
172
+ @telnet_mock.stub(:cmd).with("String" => "url.purge /", "Match" => /\n\n/).and_yield('200')
129
173
  Lacquer::Varnish.new.purge('/').should be(true)
130
174
  end
131
175
  end
@@ -0,0 +1,122 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe "Varnishd" do
4
+ before do
5
+ spec_root = Pathname.new(__FILE__).dirname.join('..').expand_path
6
+ Lacquer::Varnishd.stub(:started_check_delay).and_return(0)
7
+ Lacquer::Varnishd.stub(:env).and_return('test')
8
+ Lacquer::Varnishd.stub(:root_path).and_return(spec_root)
9
+ end
10
+
11
+ def executes_with(regexp)
12
+ new_method = Lacquer::Varnishd.method(:new)
13
+ Lacquer::Varnishd.stub(:new).and_return do |*args|
14
+ varnishd = new_method.call(*args)
15
+ varnishd.should_receive(:execute).with(regexp)
16
+ varnishd.stub(:log)
17
+ varnishd
18
+ end
19
+ end
20
+
21
+ it "passes settings in the initailizer" do
22
+ Lacquer::Varnishd.new("listen" => ":80").listen.should == ":80"
23
+ end
24
+
25
+ it "loads settings from varnish_config" do
26
+ Lacquer::Varnishd.config.should have_key("listen")
27
+ Lacquer::Varnishd.config.should have_key("telnet")
28
+ Lacquer::Varnishd.config.should have_key("sbin_path")
29
+ Lacquer::Varnishd.config.should have_key("bin_path")
30
+ Lacquer::Varnishd.config.should have_key("storage")
31
+ Lacquer::Varnishd.config.should have_key("use_sudo")
32
+ Lacquer::Varnishd.config["params"].should have_key('overflow_max')
33
+ end
34
+
35
+ it "returns full path to varnishd" do
36
+ executes_with(%r[/opt/varnishd/sbin/varnishd])
37
+ Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin").start
38
+ end
39
+
40
+ it "returns full path to varnishd using sudo" do
41
+ executes_with(%r[sudo /opt/varnishd/sbin/varnishd])
42
+ Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin", "use_sudo" => true).start
43
+ end
44
+
45
+ it "returns pid file" do
46
+ executes_with(/log\/varnishd.test.pid/)
47
+ Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin").start
48
+ end
49
+
50
+ it "returns pid file with custom path" do
51
+ executes_with(/pid\/varnishd.test.pid/)
52
+ Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin", "pid_path" => "pid/").start
53
+ end
54
+
55
+ it "returns params as string" do
56
+ Lacquer::Varnishd.new("params" => { "max" => 2000, "add" => 2 }).params_args.should == "-p max=2000 -p add=2"
57
+ end
58
+
59
+ it "returns listen arg as string" do
60
+ Lacquer::Varnishd.new("listen" => ":80").args.should include("-a :80")
61
+ end
62
+
63
+ it "starts varnishd with args and params" do
64
+ executes_with(%r[/opt/varnishd/sbin.*-P.*log/varnishd.test.pid])
65
+ Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin", "params" => { "overflow_max" => 2000 }).start
66
+ end
67
+
68
+ it "raises error if vcl_script_file is not present" do
69
+ Lacquer::Varnishd.stub(:vcl_script_filename).and_return("config/file_not_found.vcl")
70
+ expect {
71
+ Lacquer::Varnishd.new.vcl_script_path
72
+ }.to raise_error
73
+ end
74
+
75
+ it "renders vcl file when erb is present" do
76
+ Lacquer::Varnishd.stub(:vcl_script_filename).and_return("config/generate.vcl")
77
+ result = Lacquer::Varnishd.new.render_vcl
78
+ result.should include('.host = "0.0.0.0"')
79
+ result.should include('.port = "3000"')
80
+ end
81
+
82
+ describe '#reload' do
83
+ def expect_reload_cmd(attributes)
84
+ Time.stub(:now).and_return Time.parse('October 6th, 1984')
85
+ varnishadm_cmd = "#{attributes['bin_path']}/varnishadm -T #{attributes['telnet']}"
86
+ reload_id = "reload#{Time.now.usec}"
87
+ load_cmd = "#{varnishadm_cmd} vcl.load #{reload_id} config/generate.vcl"
88
+ use_cmd = "#{varnishadm_cmd} vcl.use #{reload_id}"
89
+
90
+ executes_with "#{load_cmd} && #{use_cmd}"
91
+ end
92
+
93
+ context 'given varnishd is running' do
94
+ before do
95
+ attributes = { "sbin_path" => "/opt/varnishd/sbin", "bin_path" => "/opt/bin", "telnet" => "localhost:6082" }
96
+ expect_reload_cmd attributes
97
+ @varnishd = Lacquer::Varnishd.new attributes
98
+ @varnishd.stub(:vcl_script_filename).and_return("config/generate.vcl")
99
+ @varnishd.stub(:running?).and_return true
100
+ end
101
+
102
+ it 'executes the varnishadm reload commands' do
103
+ @varnishd.should_receive :generate_vcl
104
+ end
105
+
106
+ after do
107
+ @varnishd.reload
108
+ end
109
+ end
110
+
111
+ context 'given varnishd is not running' do
112
+ before do
113
+ executes_with(%r[/opt/varnishd/sbin/varnishd])
114
+ @varnishd = Lacquer::Varnishd.new("sbin_path" => "/opt/varnishd/sbin")
115
+ end
116
+
117
+ it 'starts varnishd' do
118
+ @varnishd.reload
119
+ end
120
+ end
121
+ end
122
+ end
data/spec/spec_helper.rb CHANGED
@@ -21,6 +21,18 @@ end
21
21
 
22
22
  module Resque; end
23
23
 
24
+ module Sidekiq
25
+ module Worker
26
+ module ClassMethods
27
+ def sidekiq_options(options); end
28
+ end
29
+
30
+ def self.included(base)
31
+ base.extend(ClassMethods)
32
+ end
33
+ end
34
+ end
35
+
24
36
  Lacquer.configure do |config|
25
37
  config.enable_cache = true
26
38
  config.default_ttl = 1.week
@@ -28,6 +40,6 @@ Lacquer.configure do |config|
28
40
  config.varnish_servers << { :host => "0.0.0.0", :port => 6082 }
29
41
  end
30
42
 
31
- Rspec.configure do |c|
43
+ RSpec.configure do |c|
32
44
  c.mock_with :rspec
33
45
  end
@@ -0,0 +1 @@
1
+ load File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'lacquer', 'tasks.rb'))