ext_backup 5.0.0.beta.2.1

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.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +33 -0
  4. data/bin/backup +5 -0
  5. data/bin/docker_test +24 -0
  6. data/lib/backup.rb +140 -0
  7. data/lib/backup/archive.rb +169 -0
  8. data/lib/backup/binder.rb +18 -0
  9. data/lib/backup/cleaner.rb +112 -0
  10. data/lib/backup/cli.rb +370 -0
  11. data/lib/backup/cloud_io/base.rb +38 -0
  12. data/lib/backup/cloud_io/cloud_files.rb +296 -0
  13. data/lib/backup/cloud_io/s3.rb +253 -0
  14. data/lib/backup/compressor/base.rb +32 -0
  15. data/lib/backup/compressor/bzip2.rb +35 -0
  16. data/lib/backup/compressor/custom.rb +49 -0
  17. data/lib/backup/compressor/gzip.rb +73 -0
  18. data/lib/backup/config.rb +128 -0
  19. data/lib/backup/config/dsl.rb +102 -0
  20. data/lib/backup/config/helpers.rb +137 -0
  21. data/lib/backup/database/base.rb +86 -0
  22. data/lib/backup/database/mongodb.rb +186 -0
  23. data/lib/backup/database/mysql.rb +191 -0
  24. data/lib/backup/database/openldap.rb +93 -0
  25. data/lib/backup/database/postgresql.rb +132 -0
  26. data/lib/backup/database/redis.rb +176 -0
  27. data/lib/backup/database/riak.rb +79 -0
  28. data/lib/backup/database/sqlite.rb +55 -0
  29. data/lib/backup/encryptor/base.rb +27 -0
  30. data/lib/backup/encryptor/gpg.rb +737 -0
  31. data/lib/backup/encryptor/open_ssl.rb +74 -0
  32. data/lib/backup/errors.rb +53 -0
  33. data/lib/backup/logger.rb +197 -0
  34. data/lib/backup/logger/console.rb +48 -0
  35. data/lib/backup/logger/fog_adapter.rb +25 -0
  36. data/lib/backup/logger/logfile.rb +131 -0
  37. data/lib/backup/logger/syslog.rb +114 -0
  38. data/lib/backup/model.rb +472 -0
  39. data/lib/backup/notifier/base.rb +126 -0
  40. data/lib/backup/notifier/campfire.rb +61 -0
  41. data/lib/backup/notifier/command.rb +99 -0
  42. data/lib/backup/notifier/datadog.rb +104 -0
  43. data/lib/backup/notifier/flowdock.rb +99 -0
  44. data/lib/backup/notifier/hipchat.rb +116 -0
  45. data/lib/backup/notifier/http_post.rb +114 -0
  46. data/lib/backup/notifier/mail.rb +232 -0
  47. data/lib/backup/notifier/nagios.rb +65 -0
  48. data/lib/backup/notifier/pagerduty.rb +79 -0
  49. data/lib/backup/notifier/prowl.rb +68 -0
  50. data/lib/backup/notifier/pushover.rb +71 -0
  51. data/lib/backup/notifier/ses.rb +123 -0
  52. data/lib/backup/notifier/slack.rb +147 -0
  53. data/lib/backup/notifier/twitter.rb +55 -0
  54. data/lib/backup/notifier/zabbix.rb +60 -0
  55. data/lib/backup/package.rb +51 -0
  56. data/lib/backup/packager.rb +106 -0
  57. data/lib/backup/pipeline.rb +120 -0
  58. data/lib/backup/splitter.rb +73 -0
  59. data/lib/backup/storage/base.rb +66 -0
  60. data/lib/backup/storage/cloud_files.rb +156 -0
  61. data/lib/backup/storage/cycler.rb +70 -0
  62. data/lib/backup/storage/dropbox.rb +206 -0
  63. data/lib/backup/storage/ftp.rb +116 -0
  64. data/lib/backup/storage/local.rb +61 -0
  65. data/lib/backup/storage/qiniu.rb +65 -0
  66. data/lib/backup/storage/rsync.rb +246 -0
  67. data/lib/backup/storage/s3.rb +155 -0
  68. data/lib/backup/storage/scp.rb +65 -0
  69. data/lib/backup/storage/sftp.rb +80 -0
  70. data/lib/backup/syncer/base.rb +67 -0
  71. data/lib/backup/syncer/cloud/base.rb +176 -0
  72. data/lib/backup/syncer/cloud/cloud_files.rb +81 -0
  73. data/lib/backup/syncer/cloud/local_file.rb +97 -0
  74. data/lib/backup/syncer/cloud/s3.rb +109 -0
  75. data/lib/backup/syncer/rsync/base.rb +50 -0
  76. data/lib/backup/syncer/rsync/local.rb +27 -0
  77. data/lib/backup/syncer/rsync/pull.rb +47 -0
  78. data/lib/backup/syncer/rsync/push.rb +201 -0
  79. data/lib/backup/template.rb +41 -0
  80. data/lib/backup/utilities.rb +233 -0
  81. data/lib/backup/version.rb +3 -0
  82. data/lib/ext_backup.rb +5 -0
  83. data/lib/ext_backup/version.rb +5 -0
  84. data/templates/cli/archive +28 -0
  85. data/templates/cli/compressor/bzip2 +4 -0
  86. data/templates/cli/compressor/custom +7 -0
  87. data/templates/cli/compressor/gzip +4 -0
  88. data/templates/cli/config +123 -0
  89. data/templates/cli/databases/mongodb +15 -0
  90. data/templates/cli/databases/mysql +18 -0
  91. data/templates/cli/databases/openldap +24 -0
  92. data/templates/cli/databases/postgresql +16 -0
  93. data/templates/cli/databases/redis +16 -0
  94. data/templates/cli/databases/riak +17 -0
  95. data/templates/cli/databases/sqlite +11 -0
  96. data/templates/cli/encryptor/gpg +27 -0
  97. data/templates/cli/encryptor/openssl +9 -0
  98. data/templates/cli/model +26 -0
  99. data/templates/cli/notifier/zabbix +15 -0
  100. data/templates/cli/notifiers/campfire +12 -0
  101. data/templates/cli/notifiers/command +32 -0
  102. data/templates/cli/notifiers/datadog +57 -0
  103. data/templates/cli/notifiers/flowdock +16 -0
  104. data/templates/cli/notifiers/hipchat +16 -0
  105. data/templates/cli/notifiers/http_post +32 -0
  106. data/templates/cli/notifiers/mail +24 -0
  107. data/templates/cli/notifiers/nagios +13 -0
  108. data/templates/cli/notifiers/pagerduty +12 -0
  109. data/templates/cli/notifiers/prowl +11 -0
  110. data/templates/cli/notifiers/pushover +11 -0
  111. data/templates/cli/notifiers/ses +15 -0
  112. data/templates/cli/notifiers/slack +22 -0
  113. data/templates/cli/notifiers/twitter +13 -0
  114. data/templates/cli/splitter +7 -0
  115. data/templates/cli/storages/cloud_files +11 -0
  116. data/templates/cli/storages/dropbox +20 -0
  117. data/templates/cli/storages/ftp +13 -0
  118. data/templates/cli/storages/local +8 -0
  119. data/templates/cli/storages/qiniu +12 -0
  120. data/templates/cli/storages/rsync +17 -0
  121. data/templates/cli/storages/s3 +16 -0
  122. data/templates/cli/storages/scp +15 -0
  123. data/templates/cli/storages/sftp +15 -0
  124. data/templates/cli/syncers/cloud_files +22 -0
  125. data/templates/cli/syncers/rsync_local +20 -0
  126. data/templates/cli/syncers/rsync_pull +28 -0
  127. data/templates/cli/syncers/rsync_push +28 -0
  128. data/templates/cli/syncers/s3 +27 -0
  129. data/templates/general/links +3 -0
  130. data/templates/general/version.erb +2 -0
  131. data/templates/notifier/mail/failure.erb +16 -0
  132. data/templates/notifier/mail/success.erb +16 -0
  133. data/templates/notifier/mail/warning.erb +16 -0
  134. data/templates/storage/dropbox/authorization_url.erb +6 -0
  135. data/templates/storage/dropbox/authorized.erb +4 -0
  136. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  137. metadata +506 -0
@@ -0,0 +1,74 @@
1
+ module Backup
2
+ module Encryptor
3
+ class OpenSSL < Base
4
+ ##
5
+ # The password that'll be used to encrypt the backup. This
6
+ # password will be required to decrypt the backup later on.
7
+ attr_accessor :password
8
+
9
+ ##
10
+ # The password file to use to encrypt the backup.
11
+ attr_accessor :password_file
12
+
13
+ ##
14
+ # Determines whether the 'base64' should be used or not
15
+ attr_accessor :base64
16
+
17
+ ##
18
+ # Determines whether the 'salt' flag should be used
19
+ attr_accessor :salt
20
+
21
+ ##
22
+ # Creates a new instance of Backup::Encryptor::OpenSSL and
23
+ # sets the password attribute to what was provided
24
+ def initialize(&block)
25
+ super
26
+
27
+ @base64 ||= false
28
+ @salt ||= true
29
+ @password_file ||= nil
30
+
31
+ instance_eval(&block) if block_given?
32
+ end
33
+
34
+ ##
35
+ # This is called as part of the procedure run by the Packager.
36
+ # It sets up the needed options to pass to the openssl command,
37
+ # then yields the command to use as part of the packaging procedure.
38
+ # Once the packaging procedure is complete, it will return
39
+ # so that any clean-up may be performed after the yield.
40
+ def encrypt_with
41
+ log!
42
+ yield "#{utility(:openssl)} #{options}", ".enc"
43
+ end
44
+
45
+ private
46
+
47
+ ##
48
+ # Uses the 256bit AES encryption cipher, which is what the
49
+ # US Government uses to encrypt information at the "Top Secret" level.
50
+ #
51
+ # The -base64 option will make the encrypted output base64 encoded,
52
+ # this makes the encrypted file readable using text editors
53
+ #
54
+ # The -salt option adds strength to the encryption
55
+ #
56
+ # Always sets a password option, if even no password is given,
57
+ # but will prefer the password_file option if both are given.
58
+ def options
59
+ opts = ["aes-256-cbc"]
60
+ opts << "-base64" if @base64
61
+ opts << "-salt" if @salt
62
+
63
+ opts <<
64
+ if @password_file.to_s.empty?
65
+ "-k #{Shellwords.escape(@password)}"
66
+ else
67
+ "-pass file:#{@password_file}"
68
+ end
69
+
70
+ opts.join(" ")
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,53 @@
1
+ module Backup
2
+ # Provides cascading errors with formatted messages.
3
+ # See the specs for details.
4
+ module NestedExceptions
5
+ def self.included(klass)
6
+ klass.extend(Module.new do
7
+ def wrap(wrapped_exception, msg = nil)
8
+ new(msg, wrapped_exception)
9
+ end
10
+ end)
11
+ end
12
+
13
+ def initialize(obj = nil, wrapped_exception = nil)
14
+ @wrapped_exception = wrapped_exception
15
+ msg = (obj.respond_to?(:to_str) ? obj.to_str : obj.to_s)
16
+ .gsub(/^ */, " ").strip
17
+ msg = clean_name(self.class.name) + (msg.empty? ? "" : ": #{msg}")
18
+
19
+ if wrapped_exception
20
+ msg << "\n--- Wrapped Exception ---\n"
21
+ class_name = clean_name(wrapped_exception.class.name)
22
+ msg << class_name + ": " unless
23
+ wrapped_exception.message.start_with? class_name
24
+ msg << wrapped_exception.message
25
+ end
26
+
27
+ super(msg)
28
+ set_backtrace(wrapped_exception.backtrace) if wrapped_exception
29
+ end
30
+
31
+ def exception(obj = nil)
32
+ return self if obj.nil? || equal?(obj)
33
+
34
+ ex = self.class.new(obj, @wrapped_exception)
35
+ ex.set_backtrace(backtrace) unless ex.backtrace
36
+ ex
37
+ end
38
+
39
+ private
40
+
41
+ def clean_name(name)
42
+ name.sub(/^Backup::/, "")
43
+ end
44
+ end
45
+
46
+ class Error < StandardError
47
+ include NestedExceptions
48
+ end
49
+
50
+ class FatalError < Exception
51
+ include NestedExceptions
52
+ end
53
+ end
@@ -0,0 +1,197 @@
1
+ require "backup/logger/console"
2
+ require "backup/logger/logfile"
3
+ require "backup/logger/syslog"
4
+ require "backup/logger/fog_adapter"
5
+
6
+ module Backup
7
+ class Logger
8
+ class Config
9
+ class Logger < Struct.new(:class, :options)
10
+ def enabled?
11
+ options.enabled?
12
+ end
13
+ end
14
+
15
+ class DSL < Struct.new(:ignores, :console, :logfile, :syslog)
16
+ def ignore_warning(str_or_regexp)
17
+ ignores << str_or_regexp
18
+ end
19
+ end
20
+
21
+ attr_reader :ignores, :loggers, :dsl
22
+
23
+ def initialize
24
+ @ignores = []
25
+ @loggers = [
26
+ Logger.new(Console, Console::Options.new),
27
+ Logger.new(Logfile, Logfile::Options.new),
28
+ Logger.new(Syslog, Syslog::Options.new)
29
+ ]
30
+ @dsl = DSL.new(ignores, *loggers.map(&:options))
31
+ end
32
+ end
33
+
34
+ ##
35
+ # All messages sent to the Logger are stored in Logger.messages
36
+ # and sent to all enabled logger's #log method as Message objects.
37
+ class Message < Struct.new(:time, :level, :lines)
38
+ ##
39
+ # Returns an Array of the message lines in the following format:
40
+ #
41
+ # [YYYY/MM/DD HH:MM:SS][level] message line text
42
+ def formatted_lines
43
+ timestamp = time.strftime("%Y/%m/%d %H:%M:%S")
44
+ lines.map { |line| "[#{timestamp}][#{level}] #{line}" }
45
+ end
46
+
47
+ def matches?(ignores)
48
+ text = lines.join("\n")
49
+ ignores.any? do |obj|
50
+ obj.is_a?(Regexp) ? text.match(obj) : text.include?(obj)
51
+ end
52
+ end
53
+ end
54
+
55
+ class << self
56
+ extend Forwardable
57
+ def_delegators :logger,
58
+ :start!, :abort!, :info, :warn, :error,
59
+ :messages, :has_warnings?, :has_errors?
60
+
61
+ ##
62
+ # Allows the Logger to be configured.
63
+ #
64
+ # # shown with their default values
65
+ # Backup::Logger.configure do
66
+ # # Console options:
67
+ # console.quiet = false
68
+ #
69
+ # # Logfile options:
70
+ # logfile.enabled = true
71
+ # logfile.log_path = 'log'
72
+ # logfile.max_bytes = 500_000
73
+ #
74
+ # # Syslog options:
75
+ # syslog.enabled = false
76
+ # syslog.ident = 'backup'
77
+ # syslog.options = Syslog::LOG_PID
78
+ # syslog.facility = Syslog::LOG_LOCAL0
79
+ # syslog.info = Syslog::LOG_INFO
80
+ # syslog.warn = Syslog::LOG_WARNING
81
+ # syslog.error = Syslog::LOG_ERR
82
+ #
83
+ # # Ignore Warnings:
84
+ # # Converts :warn level messages to level :info
85
+ # ignore_warning 'that contains this string'
86
+ # ignore_warning /that matches this regexp/
87
+ # end
88
+ #
89
+ # See each Logger's Option class for details.
90
+ # @see Console::Options
91
+ # @see Logfile::Options
92
+ # @see Syslog::Options
93
+ def configure(&block)
94
+ config.dsl.instance_eval(&block)
95
+ end
96
+
97
+ ##
98
+ # Called after each backup model/trigger has been performed.
99
+ def clear!
100
+ @logger = nil
101
+ logger.start!
102
+ end
103
+
104
+ private
105
+
106
+ def config
107
+ @config ||= Config.new
108
+ end
109
+
110
+ def logger
111
+ @logger ||= new(config)
112
+ end
113
+
114
+ def reset!
115
+ @config = @logger = nil
116
+ end
117
+ end
118
+
119
+ MUTEX = Mutex.new
120
+
121
+ ##
122
+ # Returns an Array of Message objects for all logged messages received.
123
+ # These are used to attach log files to Mail notifications.
124
+ attr_reader :messages
125
+
126
+ def initialize(config)
127
+ @config = config
128
+ @messages = []
129
+ @loggers = []
130
+ @has_warnings = @has_errors = false
131
+ end
132
+
133
+ ##
134
+ # Sends a message to the Logger using the specified log level.
135
+ # +obj+ may be any Object that responds to #to_s (i.e. an Exception)
136
+ [:info, :warn, :error].each do |level|
137
+ define_method level do |obj|
138
+ MUTEX.synchronize { log(obj, level) }
139
+ end
140
+ end
141
+
142
+ ##
143
+ # Returns true if any +:warn+ level messages have been received.
144
+ def has_warnings?
145
+ @has_warnings
146
+ end
147
+
148
+ ##
149
+ # Returns true if any +:error+ level messages have been received.
150
+ def has_errors?
151
+ @has_errors
152
+ end
153
+
154
+ ##
155
+ # The Logger is available as soon as Backup is loaded, and stores all
156
+ # messages it receives. Since the Logger may be configured via the
157
+ # command line and/or the user's +config.rb+, no messages are sent
158
+ # until configuration can be completed. (see CLI#perform)
159
+ #
160
+ # Once configuration is completed, this method is called to activate
161
+ # all enabled loggers and send them any messages that have been received
162
+ # up to this point. From this point onward, these loggers will be sent
163
+ # all messages as soon as they're received.
164
+ def start!
165
+ @config.loggers.each do |logger|
166
+ @loggers << logger.class.new(logger.options) if logger.enabled?
167
+ end
168
+ messages.each do |message|
169
+ @loggers.each { |logger| logger.log(message) }
170
+ end
171
+ end
172
+
173
+ ##
174
+ # If errors are encountered by Backup::CLI while preparing to perform
175
+ # the backup jobs, this method is called to dump all messages to the
176
+ # console before Backup exits.
177
+ def abort!
178
+ console = Console.new
179
+ console.log(messages.shift) until messages.empty?
180
+ end
181
+
182
+ private
183
+
184
+ def log(obj, level)
185
+ message = Message.new(Time.now.utc, level, obj.to_s.split("\n"))
186
+
187
+ if message.level == :warn && message.matches?(@config.ignores)
188
+ message.level = :info
189
+ end
190
+ @has_warnings ||= message.level == :warn
191
+ @has_errors ||= message.level == :error
192
+
193
+ messages << message
194
+ @loggers.each { |logger| logger.log(message) }
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,48 @@
1
+ module Backup
2
+ class Logger
3
+ class Console
4
+ class Options
5
+ ##
6
+ # Disables all console output.
7
+ #
8
+ # This may also be set on the command line using +--quiet+.
9
+ #
10
+ # If +--no-quiet+ is used on the command line, console output
11
+ # will be enabled and any setting here will be ignored.
12
+ #
13
+ # @param [Boolean, nil]
14
+ # @return [Boolean, nil] Default: +false+
15
+ attr_reader :quiet
16
+
17
+ def initialize
18
+ @quiet = false
19
+ end
20
+
21
+ def enabled?
22
+ !quiet
23
+ end
24
+
25
+ def quiet=(val)
26
+ @quiet = val unless quiet.nil?
27
+ end
28
+ end
29
+
30
+ COLORS = {
31
+ info: "\e[32m%s\e[0m", # green
32
+ warn: "\e[33m%s\e[0m", # yellow
33
+ error: "\e[31m%s\e[0m" # red
34
+ }
35
+
36
+ def initialize(_options = nil)
37
+ $stdout.sync = $stderr.sync = true
38
+ end
39
+
40
+ def log(message)
41
+ io = message.level == :info ? $stdout : $stderr
42
+ lines = message.formatted_lines
43
+ lines.map! { |line| COLORS[message.level] % line } if io.tty?
44
+ io.puts lines
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ # require only the logger
2
+ require "formatador"
3
+ require "fog/core/logger"
4
+
5
+ module Backup
6
+ class Logger
7
+ module FogAdapter
8
+ class << self
9
+ # Logged as :info so these won't generate warnings.
10
+ # This is mostly to keep STDOUT clean and to provide
11
+ # supplemental messages for our own warnings.
12
+ # These will generally occur during retry attempts.
13
+ def write(message)
14
+ Logger.info message.chomp
15
+ end
16
+
17
+ def tty?
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Fog::Logger[:warning] = Backup::Logger::FogAdapter
@@ -0,0 +1,131 @@
1
+ module Backup
2
+ class Logger
3
+ class Logfile
4
+ class Error < Backup::Error; end
5
+
6
+ class Options
7
+ ##
8
+ # Enable the use of Backup's log file.
9
+ #
10
+ # While not necessary, as this is +true+ by default,
11
+ # this may also be set on the command line using +--logfile+.
12
+ #
13
+ # The use of Backup's log file may be disabled using the
14
+ # command line option +--no-logfile+.
15
+ #
16
+ # If +--no--logfile+ is used on the command line, then the
17
+ # log file will be disabled and any setting here will be ignored.
18
+ #
19
+ # @param [Boolean, nil]
20
+ # @return [Boolean, nil] Default: +true+
21
+ attr_reader :enabled
22
+
23
+ ##
24
+ # Path to directory where Backup's logfile will be written.
25
+ #
26
+ # This may be given as an absolute path, or a path relative
27
+ # to Backup's +--root-path+ (which defaults to +~/Backup+).
28
+ #
29
+ # This may also be set on the command line using +--log-path+.
30
+ # If set on the command line, any setting here will be ignored.
31
+ #
32
+ # @param [String]
33
+ # @return [String] Default: 'log'
34
+ attr_reader :log_path
35
+
36
+ ##
37
+ # Backup's logfile in which backup logs can be written
38
+ #
39
+ # As there is already a log_path, this can simply be just a file name
40
+ # that will be created (If not exists) on log_path directory
41
+ #
42
+ # This may also be set on the command line using +--log-file+.
43
+ # If set on the command line, any setting here will be ignored.
44
+ #
45
+ # @param [String]
46
+ # @return [String] Default: 'backup.log'
47
+ attr_reader :log_file
48
+
49
+ ##
50
+ # Size in bytes to truncate logfile to before backup jobs are run.
51
+ #
52
+ # This is done once before all +triggers+, so the maximum logfile size
53
+ # would be this value plus whatever the jobs produce.
54
+ #
55
+ # @param [Integer]
56
+ # @return [Integer] Default: +500_000+
57
+ attr_accessor :max_bytes
58
+
59
+ def initialize
60
+ @enabled = true
61
+ @log_path = ""
62
+ @max_bytes = 500_000
63
+ end
64
+
65
+ def enabled?
66
+ !!enabled
67
+ end
68
+
69
+ def enabled=(val)
70
+ @enabled = val unless enabled.nil?
71
+ end
72
+
73
+ def log_path=(val)
74
+ @log_path = val.to_s.strip if log_path.empty?
75
+ end
76
+ end
77
+
78
+ def initialize(options)
79
+ @options = options
80
+ @logfile = setup_logfile
81
+ truncate!
82
+ end
83
+
84
+ def log(message)
85
+ File.open(@logfile, "a") { |f| f.puts message.formatted_lines }
86
+ end
87
+
88
+ private
89
+
90
+ ##
91
+ # Returns the full path to the log file, based on the configured
92
+ # @options.log_path, and ensures the path to the log file exists.
93
+ def setup_logfile
94
+ # strip any trailing '/' in case the user supplied this as part of
95
+ # an absolute path, so we can match it against File.expand_path()
96
+ path = @options.log_path.chomp("/")
97
+ if path.empty?
98
+ path = File.join(Backup::Config.root_path, "log")
99
+ elsif path != File.expand_path(path)
100
+ path = File.join(Backup::Config.root_path, path)
101
+ end
102
+ FileUtils.mkdir_p(path)
103
+ log_file = @options.log_file || "backup.log"
104
+ path = File.join(path, log_file)
105
+ if File.exist?(path) && !File.writable?(path)
106
+ raise Error, "Log File at '#{path}' is not writable"
107
+ end
108
+ path
109
+ end
110
+
111
+ ##
112
+ # Truncates the logfile to @options.max_bytes
113
+ def truncate!
114
+ return unless File.exist?(@logfile)
115
+
116
+ if File.stat(@logfile).size > @options.max_bytes
117
+ FileUtils.cp(@logfile, @logfile + "~")
118
+ File.open(@logfile + "~", "r") do |io_in|
119
+ File.open(@logfile, "w") do |io_out|
120
+ io_in.seek(-@options.max_bytes, IO::SEEK_END) && io_in.gets
121
+ while line = io_in.gets
122
+ io_out.puts line
123
+ end
124
+ end
125
+ end
126
+ FileUtils.rm_f(@logfile + "~")
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end