rb.rotate 0.1.0
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/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +20 -0
- data/README.md +81 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/bin/rb.rotate +12 -0
- data/lib/rb.rotate.rb +30 -0
- data/lib/rb.rotate/configuration.rb +312 -0
- data/lib/rb.rotate/directory.rb +201 -0
- data/lib/rb.rotate/dispatcher.rb +112 -0
- data/lib/rb.rotate/file.rb +174 -0
- data/lib/rb.rotate/hook.rb +135 -0
- data/lib/rb.rotate/install/defaults.yaml.initial +25 -0
- data/lib/rb.rotate/install/rotate.yaml.initial +349 -0
- data/lib/rb.rotate/log.rb +80 -0
- data/lib/rb.rotate/mail.rb +40 -0
- data/lib/rb.rotate/reader.rb +94 -0
- data/lib/rb.rotate/state.rb +211 -0
- data/lib/rb.rotate/state/archive.rb +109 -0
- data/lib/rb.rotate/state/file.rb +139 -0
- data/lib/rb.rotate/storage.rb +208 -0
- data/lib/rb.rotate/storage/entry.rb +120 -0
- data/lib/rb.rotate/storage/item.rb +415 -0
- data/rb.rotate.gemspec +85 -0
- metadata +153 -0
@@ -0,0 +1,415 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "rb.rotate/state"
|
5
|
+
require "rb.rotate/mail"
|
6
|
+
|
7
|
+
module RbRotate
|
8
|
+
module StorageModule
|
9
|
+
|
10
|
+
##
|
11
|
+
# Represents an item of some entry in storage.
|
12
|
+
#
|
13
|
+
|
14
|
+
class Item
|
15
|
+
|
16
|
+
##
|
17
|
+
# Parent entry.
|
18
|
+
#
|
19
|
+
|
20
|
+
@entry
|
21
|
+
|
22
|
+
##
|
23
|
+
# Indentifier of the item.
|
24
|
+
#
|
25
|
+
|
26
|
+
@identifier
|
27
|
+
attr_writer :identifier
|
28
|
+
|
29
|
+
##
|
30
|
+
# Full path of the item.
|
31
|
+
#
|
32
|
+
|
33
|
+
@path
|
34
|
+
|
35
|
+
##
|
36
|
+
# Data of the item.
|
37
|
+
#
|
38
|
+
|
39
|
+
@data
|
40
|
+
|
41
|
+
##
|
42
|
+
# Constructor.
|
43
|
+
#
|
44
|
+
|
45
|
+
def initialize(entry, identifier = nil, path = nil)
|
46
|
+
@entry = entry
|
47
|
+
@identifier = identifier
|
48
|
+
@path = path
|
49
|
+
|
50
|
+
# Loads data
|
51
|
+
self.load_data!
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Returns data.
|
56
|
+
#
|
57
|
+
|
58
|
+
def load_data!
|
59
|
+
if @data.nil?
|
60
|
+
if not @path.nil?
|
61
|
+
@data = State::get.archive.file(@path)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Default
|
65
|
+
if @path.nil? or @data.nil?
|
66
|
+
@data = {
|
67
|
+
:date => Time::now,
|
68
|
+
:compression => false
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Rotates itself.
|
76
|
+
#
|
77
|
+
|
78
|
+
def rotate!
|
79
|
+
if @entry.storage.numeric_identifier? and (self.identifier.kind_of? Numeric)
|
80
|
+
##
|
81
|
+
# Unregisters old filename, increases counter
|
82
|
+
# and register it again.
|
83
|
+
#
|
84
|
+
|
85
|
+
self.unregister!
|
86
|
+
|
87
|
+
if self.exists?
|
88
|
+
old_path = self.path
|
89
|
+
self.identifier += 1
|
90
|
+
|
91
|
+
self.rebuild_path!
|
92
|
+
self.prepare_directory!
|
93
|
+
FileUtils.move(old_path, self.path)
|
94
|
+
|
95
|
+
self.register!
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
return self
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Returns state object.
|
104
|
+
#
|
105
|
+
|
106
|
+
def state
|
107
|
+
@entry.file.state
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Indicates, item still exists in storage.
|
112
|
+
#
|
113
|
+
|
114
|
+
def exists?
|
115
|
+
::File.exists? self.path
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Registers itself.
|
120
|
+
#
|
121
|
+
|
122
|
+
def register!
|
123
|
+
State::archive.register_file(self.path, @data)
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Unregisters itself.
|
128
|
+
#
|
129
|
+
|
130
|
+
def unregister!
|
131
|
+
State::archive.unregister_file(self.path)
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Removes itself.
|
136
|
+
#
|
137
|
+
|
138
|
+
def remove!
|
139
|
+
self.unregister!
|
140
|
+
|
141
|
+
# Eventually mails it if required
|
142
|
+
if @entry.storage.directory.configuration[:recycle].to_sym == :mail
|
143
|
+
self.mail!
|
144
|
+
end
|
145
|
+
|
146
|
+
FileUtils.remove(self.path)
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Mails the file.
|
151
|
+
#
|
152
|
+
|
153
|
+
def mail!
|
154
|
+
to = @entry.storage.directory.configuration[:mail]
|
155
|
+
self.decompress!
|
156
|
+
|
157
|
+
require "etc"
|
158
|
+
require "socket"
|
159
|
+
|
160
|
+
Mail::send(
|
161
|
+
:from => Etc.getlogin.dup << "@" << Socket.gethostname,
|
162
|
+
:to => to,
|
163
|
+
:subject => Socket.gethostname << " : log : " << self.path,
|
164
|
+
:body => ::File.read(self.target_path)
|
165
|
+
)
|
166
|
+
|
167
|
+
self.compress!
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Returns identifier.
|
172
|
+
#
|
173
|
+
|
174
|
+
def identifier
|
175
|
+
if @identifier.nil?
|
176
|
+
if @entry.storage.numeric_identifier?
|
177
|
+
@identifier = 1
|
178
|
+
else
|
179
|
+
item_identifier = @entry.storage.item_identifier
|
180
|
+
|
181
|
+
if item_identifier.to_sym == :date
|
182
|
+
format = "%Y%m%d.%H%M"
|
183
|
+
else
|
184
|
+
format = item_identifier
|
185
|
+
end
|
186
|
+
|
187
|
+
@identifier = Time::now.strftime(format)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
return @identifier
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Returns path.
|
196
|
+
#
|
197
|
+
|
198
|
+
def path
|
199
|
+
if @path.nil?
|
200
|
+
self.rebuild_path!
|
201
|
+
end
|
202
|
+
|
203
|
+
return @path
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Generates target (without compression extension) path
|
208
|
+
# from path.
|
209
|
+
#
|
210
|
+
|
211
|
+
def target_path
|
212
|
+
if self.compressed?
|
213
|
+
extension = self.compression[:extension]
|
214
|
+
result = self.path[0...-(extension.length + 1)]
|
215
|
+
else
|
216
|
+
result = self.path
|
217
|
+
end
|
218
|
+
|
219
|
+
return result
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# Rebuilds path.
|
224
|
+
#
|
225
|
+
|
226
|
+
def rebuild_path!
|
227
|
+
directory = @entry.storage.directory
|
228
|
+
configuration = directory.configuration
|
229
|
+
@path = configuration[:storage].dup << "/"
|
230
|
+
|
231
|
+
# Adds archive subdirectories structure if necessary
|
232
|
+
recursive = configuration[:recursive]
|
233
|
+
if (recursive.kind_of? TrueClass) or (configuration[:recursive].to_sym != :flat)
|
234
|
+
relative_path = directory.relative_path
|
235
|
+
if relative_path != ?.
|
236
|
+
@path << relative_path << "/"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Adds filename
|
241
|
+
@path << self.state.name.to_s << "." << self.identifier.to_s
|
242
|
+
|
243
|
+
# Adds extension if necessary
|
244
|
+
if not self.state.extension.nil?
|
245
|
+
@path << "." << self.state.extension
|
246
|
+
end
|
247
|
+
|
248
|
+
# Adds compression extension if necessary
|
249
|
+
if self.compressed?
|
250
|
+
@path << "." << self.compression[:extension]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
##
|
255
|
+
# Prepares directory.
|
256
|
+
#
|
257
|
+
|
258
|
+
def prepare_directory!
|
259
|
+
directory = FileUtils.mkdir_p(::File.dirname(self.path)).first
|
260
|
+
State::archive.register_directory(directory)
|
261
|
+
end
|
262
|
+
|
263
|
+
##
|
264
|
+
# Allocates new record.
|
265
|
+
#
|
266
|
+
|
267
|
+
def allocate(method)
|
268
|
+
|
269
|
+
# Prepares directory
|
270
|
+
self.prepare_directory!
|
271
|
+
|
272
|
+
# Allocates by required action
|
273
|
+
case method
|
274
|
+
when :copy
|
275
|
+
FileUtils.copy(@entry.file.path, self.path)
|
276
|
+
when :move
|
277
|
+
FileUtils.move(@entry.file.path, self.path)
|
278
|
+
when :append
|
279
|
+
self.append!(:"no compress")
|
280
|
+
else
|
281
|
+
raise Exception::new("Invalid allocating method.")
|
282
|
+
end
|
283
|
+
|
284
|
+
self.compress!
|
285
|
+
self.register!
|
286
|
+
|
287
|
+
return self
|
288
|
+
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# Appends to item.
|
292
|
+
#
|
293
|
+
|
294
|
+
def append!(compress = :compress)
|
295
|
+
self.decompress!
|
296
|
+
|
297
|
+
::File.open(self.path, "a") do |io|
|
298
|
+
io.write(::File.read(@entry.file.path))
|
299
|
+
end
|
300
|
+
|
301
|
+
if compress == :compress
|
302
|
+
self.compress!
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
##
|
307
|
+
# Indicates file is or file should be compressed.
|
308
|
+
#
|
309
|
+
|
310
|
+
def compressed?
|
311
|
+
result = @data[:compression]
|
312
|
+
if result.kind_of? Array
|
313
|
+
result = true
|
314
|
+
end
|
315
|
+
|
316
|
+
return result
|
317
|
+
end
|
318
|
+
|
319
|
+
##
|
320
|
+
# Compress the file.
|
321
|
+
#
|
322
|
+
|
323
|
+
def compress!
|
324
|
+
|
325
|
+
# Checking out configuration
|
326
|
+
configuration = @entry.storage.directory.configuration
|
327
|
+
command, extension = configuration[:compress]
|
328
|
+
decompress = configuration[:decompress]
|
329
|
+
|
330
|
+
if not command.kind_of? FalseClass
|
331
|
+
|
332
|
+
# Setting file settings according to current
|
333
|
+
# configuration parameters
|
334
|
+
|
335
|
+
if command.kind_of? TrueClass
|
336
|
+
command = "gzip --best"
|
337
|
+
extension = "gz"
|
338
|
+
end
|
339
|
+
if decompress.kind_of? TrueClass
|
340
|
+
decompress = "gunzip"
|
341
|
+
end
|
342
|
+
|
343
|
+
@data[:compression] = {
|
344
|
+
:decompress => decompress,
|
345
|
+
:extension => extension
|
346
|
+
}
|
347
|
+
|
348
|
+
# Compress
|
349
|
+
system(command.dup << " " << self.path)
|
350
|
+
self.rebuild_path!
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
##
|
355
|
+
# Decompress file.
|
356
|
+
#
|
357
|
+
|
358
|
+
def decompress!
|
359
|
+
if self.compressed? and self.exists?
|
360
|
+
command = self.compression[:decompress]
|
361
|
+
system(command.dup << " " << self.path << " 2> /dev/null")
|
362
|
+
FileUtils.move(self.target_path, self.path)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
##
|
367
|
+
# Describes compression.
|
368
|
+
#
|
369
|
+
|
370
|
+
def compression
|
371
|
+
@data[:compression]
|
372
|
+
end
|
373
|
+
|
374
|
+
##
|
375
|
+
# Returns the creation date.
|
376
|
+
#
|
377
|
+
|
378
|
+
def created_at
|
379
|
+
@data[:date]
|
380
|
+
end
|
381
|
+
|
382
|
+
##
|
383
|
+
# Returns the expiration date.
|
384
|
+
#
|
385
|
+
|
386
|
+
def expiration_at
|
387
|
+
configuration = @entry.storage.directory.configuration
|
388
|
+
period = configuration[:period].to_seconds
|
389
|
+
multiplier = configuration[:rotate]
|
390
|
+
|
391
|
+
return self.created_at + (period * multiplier)
|
392
|
+
end
|
393
|
+
|
394
|
+
##
|
395
|
+
# Indicates, item is expired.
|
396
|
+
#
|
397
|
+
|
398
|
+
def expired?
|
399
|
+
recycle = @entry.storage.directory.configuration[:recycle]
|
400
|
+
if recycle.kind_of? FalseClass
|
401
|
+
result = false
|
402
|
+
else
|
403
|
+
recycle = recycle.to_sym
|
404
|
+
end
|
405
|
+
|
406
|
+
if recycle and (recycle == :remove) or (recycle == :mail)
|
407
|
+
result = self.expiration_at < Time::now
|
408
|
+
end
|
409
|
+
|
410
|
+
return result
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
data/rb.rotate.gemspec
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rb.rotate}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Martin Kozák"]
|
12
|
+
s.date = %q{2010-12-21}
|
13
|
+
s.default_executable = %q{rb.rotate}
|
14
|
+
s.description = %q{An alternative to classical 'logrotate' tool. It implements very similar functionallity, features openess and flexibility of the scripting environment and removes some most known 'logrotate' limitations.}
|
15
|
+
s.email = %q{martinkozak@martinkozak.net}
|
16
|
+
s.executables = ["rb.rotate"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"bin/rb.rotate",
|
30
|
+
"lib/rb.rotate.rb",
|
31
|
+
"lib/rb.rotate/configuration.rb",
|
32
|
+
"lib/rb.rotate/directory.rb",
|
33
|
+
"lib/rb.rotate/dispatcher.rb",
|
34
|
+
"lib/rb.rotate/file.rb",
|
35
|
+
"lib/rb.rotate/hook.rb",
|
36
|
+
"lib/rb.rotate/install/defaults.yaml.initial",
|
37
|
+
"lib/rb.rotate/install/rotate.yaml.initial",
|
38
|
+
"lib/rb.rotate/log.rb",
|
39
|
+
"lib/rb.rotate/mail.rb",
|
40
|
+
"lib/rb.rotate/reader.rb",
|
41
|
+
"lib/rb.rotate/state.rb",
|
42
|
+
"lib/rb.rotate/state/archive.rb",
|
43
|
+
"lib/rb.rotate/state/file.rb",
|
44
|
+
"lib/rb.rotate/storage.rb",
|
45
|
+
"lib/rb.rotate/storage/entry.rb",
|
46
|
+
"lib/rb.rotate/storage/item.rb",
|
47
|
+
"rb.rotate.gemspec"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://github.com/martinkozak/rb.rotate}
|
50
|
+
s.licenses = ["MIT"]
|
51
|
+
s.post_install_message = %q{
|
52
|
+
INSTALLATION DONE!
|
53
|
+
For remaining part of installation run 'rb.rotate install'
|
54
|
+
and then eventually setup running the 'rb.rotate' by cron.
|
55
|
+
|
56
|
+
Be warn, it's still BETA VERSION.
|
57
|
+
|
58
|
+
}
|
59
|
+
s.require_paths = ["lib"]
|
60
|
+
s.rubygems_version = %q{1.3.7}
|
61
|
+
s.summary = %q{More modern alternative to 'logrotate' with more features and less limitations.}
|
62
|
+
|
63
|
+
if s.respond_to? :specification_version then
|
64
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
65
|
+
s.specification_version = 3
|
66
|
+
|
67
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
68
|
+
s.add_runtime_dependency(%q<pony>, [">= 1.1"])
|
69
|
+
s.add_runtime_dependency(%q<sys-uname>, [">= 0.8.5"])
|
70
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
71
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
72
|
+
else
|
73
|
+
s.add_dependency(%q<pony>, [">= 1.1"])
|
74
|
+
s.add_dependency(%q<sys-uname>, [">= 0.8.5"])
|
75
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<pony>, [">= 1.1"])
|
80
|
+
s.add_dependency(%q<sys-uname>, [">= 0.8.5"])
|
81
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
82
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|