anikkar-klarlack 0.0.7
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.
- data/LICENSE +20 -0
- data/README.rdoc +43 -0
- data/Rakefile +48 -0
- data/VERSION.yml +4 -0
- data/lib/klarlack.rb +7 -0
- data/lib/varnish/client.rb +316 -0
- data/lib/varnish/socket_factory.rb +57 -0
- data/spec/klarlack_spec.rb +214 -0
- data/spec/spec_helper.rb +5 -0
- metadata +73 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Max Schöfmann
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
= klarlack
|
2
|
+
|
3
|
+
Klarlack is a ruby client library for the varnish administration interface.
|
4
|
+
|
5
|
+
See also: http://www.varnish-cache.org
|
6
|
+
|
7
|
+
Please note: You need at least version 2.0.3 of varnish for purging to work.
|
8
|
+
|
9
|
+
=== Installation (from gemcutter.org)
|
10
|
+
|
11
|
+
sudo gem install klarlack
|
12
|
+
|
13
|
+
=== Example
|
14
|
+
|
15
|
+
Lets purge all blog posts from the cache...
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'klarlack'
|
19
|
+
|
20
|
+
varnish = Varnish::Client.new '127.0.0.1:6082'
|
21
|
+
# the regexp is not a ruby regexp, just a plain string varnishd understands
|
22
|
+
varnish.purge :url, "^/posts/.*"
|
23
|
+
|
24
|
+
In a Rails app, you might want to use use this in a cache sweeper.
|
25
|
+
|
26
|
+
=== Specs
|
27
|
+
|
28
|
+
Start up a local varnishd with <tt>-T 127.0.0.1:6082</tt>. Then run
|
29
|
+
|
30
|
+
spec spec
|
31
|
+
|
32
|
+
=== TODO
|
33
|
+
|
34
|
+
* Support authentication when varnishd is started with <tt>-S</tt>
|
35
|
+
* Make parameter manipulation/display more friendly
|
36
|
+
|
37
|
+
=== WTF?
|
38
|
+
|
39
|
+
http://dict.leo.org/?search=klarlack
|
40
|
+
|
41
|
+
=== Copyright
|
42
|
+
|
43
|
+
Copyright (c) 2009-2010 Max Schöfmann. Distributed under the MIT-License
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "klarlack"
|
8
|
+
gem.summary = %Q{ruby client for varnishd's admin interface}
|
9
|
+
gem.email = "max@pragmatic-it.de"
|
10
|
+
gem.homepage = "http://github.com/schoefmax/klarlack"
|
11
|
+
gem.authors = ["Max Schöfmann"]
|
12
|
+
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
21
|
+
spec.libs << 'lib' << 'spec'
|
22
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
task :default => :spec
|
33
|
+
|
34
|
+
require 'rake/rdoctask'
|
35
|
+
Rake::RDocTask.new do |rdoc|
|
36
|
+
if File.exist?('VERSION.yml')
|
37
|
+
config = YAML.load(File.read('VERSION.yml'))
|
38
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
39
|
+
else
|
40
|
+
version = ""
|
41
|
+
end
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "klarlack #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
48
|
+
|
data/VERSION.yml
ADDED
data/lib/klarlack.rb
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
module Varnish
|
2
|
+
class Error < StandardError; end
|
3
|
+
class BrokenConnection < Error; end
|
4
|
+
class CommandFailed < Error; end
|
5
|
+
class Client
|
6
|
+
# Default management port of varnishd
|
7
|
+
DEFAULT_PORT = 6082
|
8
|
+
|
9
|
+
# We assume varnishd on localhost
|
10
|
+
DEFAULT_HOST = 'localhost'
|
11
|
+
|
12
|
+
DEFAULT_OPTS = {
|
13
|
+
:keep_alive => false,
|
14
|
+
:timeout => 1
|
15
|
+
}
|
16
|
+
|
17
|
+
# timeout in seconds when connecting to varnishd. Default is 1
|
18
|
+
attr_accessor :timeout
|
19
|
+
|
20
|
+
# set to true, to keep the connection alive. Default is false
|
21
|
+
attr_accessor :keep_alive
|
22
|
+
|
23
|
+
# hostname or IP-address of varnishd. Default is "localhost"
|
24
|
+
attr_accessor :host
|
25
|
+
|
26
|
+
# port number of varnishd. Default is 6082
|
27
|
+
attr_accessor :port
|
28
|
+
|
29
|
+
# Examples:
|
30
|
+
#
|
31
|
+
# Varnish::Client.new "127.0.0.1"
|
32
|
+
# Varnish::Client.new "mydomain.com:6082"
|
33
|
+
# Varnish::Client.new :timeout => 5
|
34
|
+
# Varnish::Client.new "10.0.0.3:6060", :timeout => nil, :keep_alive => true
|
35
|
+
#
|
36
|
+
# === Configuration options
|
37
|
+
#
|
38
|
+
# +timeout+:: if specified (seconds), calls to varnish
|
39
|
+
# will be wrapped in a timeout, default is 1 second.
|
40
|
+
# Disable with <tt>:timeout => nil</tt>
|
41
|
+
# +keep_alive+:: if true, the connection is kept alive by sending
|
42
|
+
# ping commands to varnishd every few seconds
|
43
|
+
def initialize(*args)
|
44
|
+
opts = {}
|
45
|
+
|
46
|
+
case args.length
|
47
|
+
when 0
|
48
|
+
self.server = DEFAULT_HOST
|
49
|
+
when 1
|
50
|
+
arg = args[0]
|
51
|
+
case arg
|
52
|
+
when String
|
53
|
+
self.server = arg
|
54
|
+
when Hash
|
55
|
+
self.server = DEFAULT_HOST
|
56
|
+
opts = arg
|
57
|
+
end
|
58
|
+
when 2
|
59
|
+
self.server = args[0]
|
60
|
+
opts = args[1]
|
61
|
+
else
|
62
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
|
63
|
+
end
|
64
|
+
|
65
|
+
opts = DEFAULT_OPTS.merge(opts)
|
66
|
+
@timeout = opts[:timeout]
|
67
|
+
@keep_alive = opts[:keep_alive]
|
68
|
+
|
69
|
+
@mutex = Mutex.new
|
70
|
+
end
|
71
|
+
|
72
|
+
# Set the varnishd management host and port.
|
73
|
+
# Expects a string as "hostname" or "hostname:port"
|
74
|
+
def server=(server)
|
75
|
+
@host, @port = server.split(':')
|
76
|
+
@port = (@port || DEFAULT_PORT).to_i
|
77
|
+
server
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the varnishd management host and port as "hostname:port"
|
81
|
+
def server
|
82
|
+
"#{@host}:#{@port}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Manipulate the VCL configuration
|
86
|
+
#
|
87
|
+
# .vcl :load, <configname>, <filename>
|
88
|
+
# .vcl :inline, <configname>, <quoted_VCLstring>
|
89
|
+
# .vcl :use, <configname>
|
90
|
+
# .vcl :discard, <configname>
|
91
|
+
# .vcl :list
|
92
|
+
# .vcl :show, <configname>
|
93
|
+
#
|
94
|
+
# Returns an array of VCL configurations for :list, and the servers
|
95
|
+
# response as string otherwise
|
96
|
+
#
|
97
|
+
# Ex.:
|
98
|
+
# v = Varnish::Client.new
|
99
|
+
# v.vcl :list
|
100
|
+
# #=> [["active", 0, "boot"]]
|
101
|
+
#
|
102
|
+
# v.vcl :load, "newconf", "/etc/varnish/myconf.vcl"
|
103
|
+
#
|
104
|
+
#
|
105
|
+
def vcl(op, *params)
|
106
|
+
response = cmd("vcl.#{op}", *params)
|
107
|
+
case op
|
108
|
+
when :list
|
109
|
+
response.split("\n").map do |line|
|
110
|
+
a = line.split(/\s+/, 3)
|
111
|
+
[a[0], a[1].to_i, a[2]]
|
112
|
+
end
|
113
|
+
else
|
114
|
+
response
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Purge objects from the cache or show the purge queue.
|
119
|
+
#
|
120
|
+
# Takes one or two arguments:
|
121
|
+
#
|
122
|
+
# 1.
|
123
|
+
# .purge :url, <regexp>
|
124
|
+
# .purge :hash, <regexp>
|
125
|
+
#
|
126
|
+
# <regexp> is a string containing a varnish compatible regexp
|
127
|
+
#
|
128
|
+
# 2.
|
129
|
+
# .purge <costum-purge-conditions>
|
130
|
+
# .purge :list
|
131
|
+
#
|
132
|
+
# Returns true for purging, returns an array containing the purge queue
|
133
|
+
# for :list
|
134
|
+
#
|
135
|
+
# Ex.:
|
136
|
+
# v = Varnish::Client.new
|
137
|
+
# v.purge :url, '.*'
|
138
|
+
#
|
139
|
+
# v.purge "req.http.host ~ www.foo.com && req.http.url ~ images"
|
140
|
+
#
|
141
|
+
# v.purge :list
|
142
|
+
# #=> [[1, "req.url ~ .*"]]
|
143
|
+
#
|
144
|
+
def purge(*args)
|
145
|
+
c = 'purge'
|
146
|
+
c << ".#{args.shift}" if [:url, :hash, :list].include?(args.first)
|
147
|
+
response = cmd(c, *args)
|
148
|
+
case c
|
149
|
+
when 'purge.list'
|
150
|
+
response.split("\n").map do |line|
|
151
|
+
a = line.split("\t")
|
152
|
+
[a[0].to_i, a[1]]
|
153
|
+
end
|
154
|
+
else
|
155
|
+
bool response
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Ban objects from the cache or show the ban queue.
|
160
|
+
#
|
161
|
+
# Takes one or two arguments:
|
162
|
+
#
|
163
|
+
# 1.
|
164
|
+
# .ban :url, <regexp>
|
165
|
+
#
|
166
|
+
# <regexp> is a string containing a varnish compatible regexp
|
167
|
+
#
|
168
|
+
# 2.
|
169
|
+
# .ban <costum-purge-conditions>
|
170
|
+
# .ban :list
|
171
|
+
#
|
172
|
+
# Returns true for banning, returns an array containing the ban queue
|
173
|
+
# for :list
|
174
|
+
#
|
175
|
+
# Ex.:
|
176
|
+
# v = Varnish::Client.new
|
177
|
+
# v.ban :url, '.*'
|
178
|
+
#
|
179
|
+
# v.ban "req.http.host ~ www.foo.com && req.http.url ~ images"
|
180
|
+
#
|
181
|
+
# v.ban :list
|
182
|
+
# #=> [[1, "req.url ~ .*"]]
|
183
|
+
#
|
184
|
+
def ban(*args)
|
185
|
+
c = 'ban'
|
186
|
+
c << ".#{args.shift}" if [:url, :list].include?(args.first)
|
187
|
+
response = cmd(c, *args)
|
188
|
+
case c
|
189
|
+
when 'ban.list'
|
190
|
+
response.split("\n").map do |line|
|
191
|
+
a = line.split("\t")
|
192
|
+
[a[0].to_i, a[1]]
|
193
|
+
end
|
194
|
+
else
|
195
|
+
bool response
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Ping the server to keep the connection alive
|
200
|
+
def ping(timestamp = nil)
|
201
|
+
cmd("ping", timestamp)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Returns a hash of status information
|
205
|
+
#
|
206
|
+
# Ex.:
|
207
|
+
# v = Varnish::Client.new
|
208
|
+
# v.stats
|
209
|
+
# => {"Total header bytes"=>0, "Cache misses"=>0 ...}
|
210
|
+
def stats
|
211
|
+
result = cmd("stats")
|
212
|
+
Hash[*result.split("\n").map { |line|
|
213
|
+
stat = line.strip!.split(/\s+/, 2)
|
214
|
+
[stat[1], stat[0].to_i]
|
215
|
+
}.flatten
|
216
|
+
]
|
217
|
+
end
|
218
|
+
|
219
|
+
# Set and show parameters
|
220
|
+
#
|
221
|
+
# .param :show, [-l], [<param>]
|
222
|
+
# .param :set, <param>, <value>
|
223
|
+
def param(op, *args)
|
224
|
+
cmd("param.#{op}", *args)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns the status string from varnish.
|
228
|
+
# See also #running? and #stopped?
|
229
|
+
def status
|
230
|
+
cmd("status")
|
231
|
+
end
|
232
|
+
|
233
|
+
def start
|
234
|
+
bool cmd("start")
|
235
|
+
end
|
236
|
+
|
237
|
+
def stop
|
238
|
+
bool cmd("stop")
|
239
|
+
end
|
240
|
+
|
241
|
+
def running?
|
242
|
+
bool status =~ /running/
|
243
|
+
end
|
244
|
+
|
245
|
+
def stopped?
|
246
|
+
bool status =~ /stopped/
|
247
|
+
end
|
248
|
+
|
249
|
+
# close the connection to varnishd.
|
250
|
+
# Note that the connection will automatically be re-established
|
251
|
+
# when another command is issued.
|
252
|
+
def disconnect
|
253
|
+
if connected?
|
254
|
+
@conn.write "quit\n"
|
255
|
+
@conn.gets
|
256
|
+
@conn.close unless @conn.closed?
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def connected?
|
261
|
+
bool @conn && !@conn.closed?
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
|
266
|
+
# Sends a command to varnishd.
|
267
|
+
# Raises a Varnish::CommandFailed error when a non-200 status is returned
|
268
|
+
# Returns the response text
|
269
|
+
def cmd(name, *params)
|
270
|
+
@mutex.synchronize do
|
271
|
+
connect unless connected?
|
272
|
+
@conn.write "#{name} #{params.join(' ').gsub('\\', '\\\\\\')}\n"
|
273
|
+
status, length = conn_gets.split # <status> <content_length>\n
|
274
|
+
content = conn_read(length.to_i + 1) # +1 = \n
|
275
|
+
content.chomp!
|
276
|
+
raise CommandFailed, "Command #{name} returned with status #{status}: #{content}" if status.to_i != 200
|
277
|
+
content
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def connect
|
282
|
+
@conn = SocketFactory.tcp_socket(@host, @port, @timeout)
|
283
|
+
|
284
|
+
# If keep alive, we ping the server every few seconds.
|
285
|
+
if @keep_alive
|
286
|
+
varnish = self
|
287
|
+
Thread.new do
|
288
|
+
while(true) do
|
289
|
+
if varnish.connected?
|
290
|
+
varnish.ping
|
291
|
+
sleep 5
|
292
|
+
else
|
293
|
+
break
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
@conn
|
300
|
+
end
|
301
|
+
|
302
|
+
def conn_gets
|
303
|
+
@conn.gets || raise(BrokenConnection, "Expected to read one line from Varnish, got nil")
|
304
|
+
end
|
305
|
+
|
306
|
+
def conn_read(bytes)
|
307
|
+
@conn.read(bytes) || raise(BrokenConnection, "Expected to read #{bytes} bytes from Varnish, got nil")
|
308
|
+
end
|
309
|
+
|
310
|
+
# converts +value+ into a boolean
|
311
|
+
def bool(value)
|
312
|
+
!!value
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Varnish
|
2
|
+
# Wrapper around Ruby's Socket.
|
3
|
+
#
|
4
|
+
# Uses Mike Perhams superior (in both reliability and
|
5
|
+
# performance) connection technique with proper timeouts:
|
6
|
+
# See: http://github.com/mperham/memcache-client
|
7
|
+
class SocketFactory
|
8
|
+
|
9
|
+
begin
|
10
|
+
# Try to use the SystemTimer gem instead of Ruby's timeout library
|
11
|
+
# when running on something that looks like Ruby 1.8.x. See:
|
12
|
+
# http://ph7spot.com/articles/system_timer
|
13
|
+
# We don't want to bother trying to load SystemTimer on jruby and
|
14
|
+
# ruby 1.9+.
|
15
|
+
if RUBY_VERSION =~ /^1\.8\./ and RUBY_PLATFORM !~ /java/
|
16
|
+
require 'system_timer'
|
17
|
+
Timer = SystemTimer
|
18
|
+
else
|
19
|
+
require 'timeout'
|
20
|
+
Timer = Timeout
|
21
|
+
end
|
22
|
+
rescue LoadError => e
|
23
|
+
#$stderr.puts "[klarlack] Could not load SystemTimer gem, falling back to Ruby's slower/unsafe timeout library: #{e.message}"
|
24
|
+
require 'timeout'
|
25
|
+
Timer = Timeout
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.tcp_socket(host, port, timeout = nil)
|
29
|
+
addr = Socket.getaddrinfo(host, nil)
|
30
|
+
sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
31
|
+
|
32
|
+
if timeout
|
33
|
+
secs = Integer(timeout)
|
34
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
35
|
+
optval = [secs, usecs].pack("l_2")
|
36
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
37
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
38
|
+
|
39
|
+
# Socket timeouts don't work for more complex IO operations
|
40
|
+
# like gets which lay on top of read. We need to fall back to
|
41
|
+
# the standard Timeout mechanism.
|
42
|
+
sock.instance_eval <<-EOR
|
43
|
+
alias :blocking_gets :gets
|
44
|
+
def gets
|
45
|
+
Timer.timeout(#{timeout}) do
|
46
|
+
self.blocking_gets
|
47
|
+
end
|
48
|
+
end
|
49
|
+
EOR
|
50
|
+
end
|
51
|
+
sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
|
52
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
53
|
+
sock
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Varnish::Client do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@varnish = Varnish::Client.new "127.0.0.1:6082"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '(connection handling)' do
|
10
|
+
|
11
|
+
it 'should not be connected on object instantiation' do
|
12
|
+
@varnish.connected?.should be_false
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should not raise an error when trying to disconnect a non-connected client' do
|
16
|
+
lambda { @varnish.disconnect }.should_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should automatically connect when a command is issued' do
|
20
|
+
@varnish.ping
|
21
|
+
@varnish.connected?.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should use timeouts when sending commands' do
|
25
|
+
Varnish::SocketFactory::Timer.should_receive(:timeout).and_return("200 0")
|
26
|
+
@varnish.timeout = 10
|
27
|
+
@varnish.ping
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should be possible to disable timeouts' do
|
31
|
+
Varnish::SocketFactory::Timer.should_not_receive(:timeout)
|
32
|
+
@varnish.timeout = nil
|
33
|
+
@varnish.ping
|
34
|
+
end
|
35
|
+
|
36
|
+
it '#disconnect should close the connection' do
|
37
|
+
@varnish.ping
|
38
|
+
@varnish.connected?.should be_true
|
39
|
+
@varnish.disconnect
|
40
|
+
@varnish.connected?.should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'given keep_alive is set, the connection should be kept alive with pings' do
|
44
|
+
@varnish.keep_alive = true
|
45
|
+
@varnish.should_receive :ping
|
46
|
+
@varnish.send :connect
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'given keep_alive is not set, no pings should be sent to varnishd' do
|
50
|
+
@varnish.keep_alive = false
|
51
|
+
@varnish.should_not_receive :ping
|
52
|
+
@varnish.send :connect
|
53
|
+
end
|
54
|
+
|
55
|
+
it '#server should return the host and port for the connection' do
|
56
|
+
@varnish.host = "foohost"
|
57
|
+
@varnish.port = 1234
|
58
|
+
@varnish.server.should == "foohost:1234"
|
59
|
+
end
|
60
|
+
|
61
|
+
it '#server= should set the host and port for the connection' do
|
62
|
+
@varnish.server = "blahost:9876"
|
63
|
+
@varnish.host.should == "blahost"
|
64
|
+
@varnish.port.should == 9876
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '(commands)' do
|
70
|
+
|
71
|
+
before(:each) do
|
72
|
+
ensure_started
|
73
|
+
end
|
74
|
+
|
75
|
+
# ... the specs for #param, #purge, #ban and #vcl could be better ...
|
76
|
+
|
77
|
+
it '#param should send the param command to varnishd' do
|
78
|
+
@varnish.param(:show).should_not be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
if ENV['VARNISH_3']
|
82
|
+
it '#ban should allow purging by url and custom fields' do
|
83
|
+
@varnish.ban(:url, '^/articles/.*').should be_true
|
84
|
+
@varnish.ban("req.http.host", "~", "www.example.com").should be_true
|
85
|
+
@varnish.ban("req.http.host ~ www.example.com").should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it '#ban with :list should return an array with queued purges' do
|
89
|
+
@varnish.ban(:url, '^/posts/.*')
|
90
|
+
list = @varnish.ban(:list)
|
91
|
+
list.last[0].should be_kind_of(Integer)
|
92
|
+
list.last[1].should == "req.url ~ ^/posts/.*"
|
93
|
+
end
|
94
|
+
else
|
95
|
+
it '#purge should allow purging by url, hash and custom fields' do
|
96
|
+
@varnish.purge(:url, '^/articles/.*').should be_true
|
97
|
+
@varnish.purge(:hash, 12345).should be_true
|
98
|
+
@varnish.purge("req.http.host", "~", "www.example.com").should be_true
|
99
|
+
@varnish.purge("req.http.host ~ www.example.com").should be_true
|
100
|
+
end
|
101
|
+
|
102
|
+
it '#purge with :list should return an array with queued purges' do
|
103
|
+
@varnish.purge(:url, '^/posts/.*')
|
104
|
+
list = @varnish.purge(:list)
|
105
|
+
list.last[0].should be_kind_of(Integer)
|
106
|
+
list.last[1].should == "req.url ~ ^/posts/.*"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it '#vcl with :list should return an array of VCL configurations' do
|
111
|
+
list = @varnish.vcl(:list)
|
112
|
+
list.should_not be_empty
|
113
|
+
list.should be_kind_of(Array)
|
114
|
+
list.first[0].should be_kind_of(String)
|
115
|
+
list.first[1].should be_kind_of(Integer)
|
116
|
+
list.first[2].should be_kind_of(String)
|
117
|
+
end
|
118
|
+
|
119
|
+
it '#ping should send a ping to the server and return a string containing the response' do
|
120
|
+
@varnish.ping.should =~ /^PONG \d+/
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
it '#status should return a string explaining the daemons status' do
|
125
|
+
@varnish.status.should =~ /running|stopped|stopping|starting/
|
126
|
+
end
|
127
|
+
|
128
|
+
it "#stats should return a hash containing status information" do
|
129
|
+
stats = @varnish.stats
|
130
|
+
stats.should_not be_empty
|
131
|
+
stats.values.each {|v| v.should be_kind_of(Integer) }
|
132
|
+
stats.keys.each {|k| k.should_not be_empty }
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '(regression)' do
|
138
|
+
|
139
|
+
unless ENV['VARNISH_3'] #doesn't look like ban takes a hash
|
140
|
+
it '#purge.hash with regex containing backslashes should be escaped properly' do
|
141
|
+
test_regex = '/home\?x=1'
|
142
|
+
@varnish.purge :hash, test_regex
|
143
|
+
list = @varnish.purge :list
|
144
|
+
list.flatten.join.should include(test_regex)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '(broken connection)' do
|
150
|
+
|
151
|
+
before(:each) do
|
152
|
+
fake_connection!(:connected => true, :return => "200 1\n")
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should fail with a Varnish::Error when the connection does return nil for gets' do
|
156
|
+
@conn.stub!(:gets).and_return(nil)
|
157
|
+
lambda { @varnish.ping }.should raise_error(Varnish::BrokenConnection)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should fail with a Varnish::Error when the connection does return nil for read' do
|
161
|
+
@conn.stub!(:read).and_return(nil)
|
162
|
+
lambda { @varnish.ping }.should raise_error(Varnish::BrokenConnection)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '(daemon lifecycle)' do
|
168
|
+
|
169
|
+
it '#start, #stop, #running?, #stopped? should bahave as advertised' do
|
170
|
+
ensure_stopped # issues #stop
|
171
|
+
@varnish.stopped?.should be_true
|
172
|
+
@varnish.running?.should be_false
|
173
|
+
ensure_started # issues #start
|
174
|
+
@varnish.stopped?.should be_false
|
175
|
+
@varnish.running?.should be_true
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'starting an already started daemon should raise an error' do
|
179
|
+
ensure_started
|
180
|
+
lambda { @varnish.start }.should raise_error(Varnish::CommandFailed)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'stopping an already stopped daemon should raise an error' do
|
184
|
+
ensure_stopped
|
185
|
+
lambda { @varnish.stop }.should raise_error(Varnish::CommandFailed)
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
def ensure_started
|
191
|
+
@varnish.start if @varnish.stopped?
|
192
|
+
while(!@varnish.running?) do sleep 0.1 end
|
193
|
+
end
|
194
|
+
|
195
|
+
def ensure_stopped
|
196
|
+
@varnish.stop if @varnish.running?
|
197
|
+
while(!@varnish.stopped?) do sleep 0.1 end
|
198
|
+
end
|
199
|
+
|
200
|
+
class FakeConn
|
201
|
+
attr_accessor :retval
|
202
|
+
def read(*args) @retval end
|
203
|
+
def gets(*args) @retval end
|
204
|
+
def write(str) str.to_s.size end
|
205
|
+
end
|
206
|
+
|
207
|
+
def fake_connection!(opts = {})
|
208
|
+
@conn = FakeConn.new
|
209
|
+
@conn.retval = opts[:return] || ''
|
210
|
+
@varnish.stub!(:connected?).and_return(opts[:connected] || false)
|
211
|
+
@varnish.instance_variable_set('@conn', @conn)
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anikkar-klarlack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Max Sch\xC3\xB6fmann"
|
13
|
+
- Arash Nikkar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-26 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: max@pragmatic-it.de anikkar@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- LICENSE
|
30
|
+
- README.rdoc
|
31
|
+
files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
- Rakefile
|
35
|
+
- VERSION.yml
|
36
|
+
- lib/klarlack.rb
|
37
|
+
- lib/varnish/client.rb
|
38
|
+
- lib/varnish/socket_factory.rb
|
39
|
+
- spec/klarlack_spec.rb
|
40
|
+
- spec/spec_helper.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: https://github.com/anikkar/klarlack
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.6
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: ruby client for varnishd's admin interface
|
71
|
+
test_files:
|
72
|
+
- spec/klarlack_spec.rb
|
73
|
+
- spec/spec_helper.rb
|