chef_backup 0.0.1.dev.4 → 0.2.0.pre1

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 (40) hide show
  1. checksums.yaml +5 -5
  2. data/lib/chef_backup/config.rb +22 -12
  3. data/lib/chef_backup/data_map.rb +18 -12
  4. data/lib/chef_backup/deep_merge.rb +145 -0
  5. data/lib/chef_backup/helpers.rb +154 -28
  6. data/lib/chef_backup/logger.rb +11 -6
  7. data/lib/chef_backup/mash.rb +226 -0
  8. data/lib/chef_backup/runner.rb +24 -21
  9. data/lib/chef_backup/strategy/backup/custom.rb +1 -2
  10. data/lib/chef_backup/strategy/backup/ebs.rb +3 -6
  11. data/lib/chef_backup/strategy/backup/lvm.rb +2 -4
  12. data/lib/chef_backup/strategy/backup/object.rb +2 -4
  13. data/lib/chef_backup/strategy/backup/tar.rb +96 -40
  14. data/lib/chef_backup/strategy/restore/tar.rb +81 -51
  15. data/lib/chef_backup/strategy.rb +10 -10
  16. data/lib/chef_backup/version.rb +1 -1
  17. data/lib/chef_backup.rb +8 -8
  18. metadata +21 -162
  19. data/.gitignore +0 -23
  20. data/.kitchen.yml +0 -30
  21. data/.rubocop.yml +0 -21
  22. data/.travis.yml +0 -6
  23. data/Gemfile +0 -4
  24. data/Guardfile +0 -22
  25. data/README.md +0 -21
  26. data/Rakefile +0 -44
  27. data/chef_backup.gemspec +0 -33
  28. data/spec/fixtures/chef-server-running.json +0 -589
  29. data/spec/spec_helper.rb +0 -103
  30. data/spec/unit/data_map_spec.rb +0 -59
  31. data/spec/unit/helpers_spec.rb +0 -88
  32. data/spec/unit/runner_spec.rb +0 -185
  33. data/spec/unit/shared_examples/helpers.rb +0 -20
  34. data/spec/unit/strategy/backup/lvm_spec.rb +0 -0
  35. data/spec/unit/strategy/backup/shared_examples/backup.rb +0 -92
  36. data/spec/unit/strategy/backup/tar_spec.rb +0 -294
  37. data/spec/unit/strategy/restore/lvm_spec.rb +0 -0
  38. data/spec/unit/strategy/restore/shared_examples/restore.rb +0 -84
  39. data/spec/unit/strategy/restore/tar_spec.rb +0 -238
  40. data/spec/unit/strategy_spec.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 94cbd6726cb86fe838709bb6cd0102f18ff98a02
4
- data.tar.gz: 7a38dd28ef497ae4b1ec8a59adb22634e28f2fc4
2
+ SHA256:
3
+ metadata.gz: 51de6280ecd6ede16153afcfb70d6fcfa508e76dfa48dea7858d5d5fcc4be278
4
+ data.tar.gz: a358070ef671054bda4e8b21bc6e6bb51fb915b31bded0567d4c717731fff0c5
5
5
  SHA512:
6
- metadata.gz: 4f1c58f55e246331ab7cc8c9919f6e9dafaba9922d4c597e5546bd59701634e3766461d4e58f3efffcea09cf1267724e52d3f11b4b9fc6b37be68eecdc1664b4
7
- data.tar.gz: af600e295428765fb2e8bd0316039108a1133c5fd7a82ea8df83b1c518019c965a4239935cc9625010522b2bc82ff23aab588ab4afc9f0f24e06a0ebac846478
6
+ metadata.gz: cbc70201b088ac22f49d34cfb6114eebfe62f8b2f8cca7fd3beb5255b54df77dddcdc76287f3474f284532b387a9297c8659c4eaf5c5a50444536579c7e98354
7
+ data.tar.gz: 96e4e39b9d0083c81a0ba2dbf009f32545e027aa5a5a5f0f3beb91eb34f7fae0a17f9493e1c766979ec73719ee5104c28aae8fa59d739141ba9d49a64d6aa805
@@ -1,18 +1,23 @@
1
- require 'fileutils'
2
- require 'json'
3
- require 'forwardable'
1
+ require "fileutils" unless defined?(FileUtils)
2
+ require "json" unless defined?(JSON)
3
+ require "forwardable" unless defined?(Forwardable)
4
4
 
5
5
  module ChefBackup
6
6
  # ChefBackup Global Config
7
7
  class Config
8
8
  extend Forwardable
9
9
 
10
+ DEFAULT_BASE = "private_chef".freeze
10
11
  DEFAULT_CONFIG = {
11
- 'backup' => {
12
- 'always_dump_db' => true,
13
- 'strategy' => 'none',
14
- 'export_dir' => '/var/opt/chef-backup'
15
- }
12
+ "backup" => {
13
+ "always_dump_db" => true,
14
+ "strategy" => "none",
15
+ "export_dir" => "/var/opt/chef-backup",
16
+ "project_name" => "opscode",
17
+ "ctl-command" => "chef-server-ctl",
18
+ "running_filepath" => "/etc/opscode/chef-server-running.json",
19
+ "database_name" => "opscode_chef",
20
+ },
16
21
  }.freeze
17
22
 
18
23
  class << self
@@ -45,13 +50,18 @@ module ChefBackup
45
50
  # @param config [Hash] a Hash of the private-chef-running.json
46
51
  #
47
52
  def initialize(config = {})
48
- config['private_chef'] ||= {}
49
- config['private_chef']['backup'] ||= {}
50
- config['private_chef']['backup'] =
51
- DEFAULT_CONFIG['backup'].merge(config['private_chef']['backup'])
53
+ config["config_base"] ||= DEFAULT_BASE
54
+ base = config["config_base"]
55
+ config[base] ||= {}
56
+ config[base]["backup"] ||= {}
57
+ config[base]["backup"] = DEFAULT_CONFIG["backup"].merge(config[base]["backup"])
52
58
  @config = config
53
59
  end
54
60
 
61
+ def to_hash
62
+ @config
63
+ end
64
+
55
65
  def_delegators :@config, :[], :[]=
56
66
  end
57
67
  end
@@ -1,4 +1,4 @@
1
- require 'time'
1
+ require "time" unless defined?(Time.zone_offset)
2
2
 
3
3
  module ChefBackup
4
4
  # DataMap class to store data about the data we're backing up
@@ -11,27 +11,32 @@ module ChefBackup
11
11
  attr_writer :data_map
12
12
  end
13
13
 
14
- attr_accessor :strategy, :backup_time, :topology, :configs, :services, :ha
14
+ attr_accessor :strategy, :backup_time, :topology, :configs, :services, :ha, :versions
15
15
 
16
16
  def initialize
17
17
  @services = {}
18
18
  @configs = {}
19
+ @versions = {}
19
20
  @ha = {}
20
21
  yield self if block_given?
21
22
 
22
23
  @backup_time ||= Time.now.iso8601
23
- @strategy ||= 'none'
24
- @toplogy ||= 'idontknow'
24
+ @strategy ||= "none"
25
+ @toplogy ||= "idontknow"
25
26
  end
26
27
 
27
28
  def add_service(service, data_dir)
28
29
  @services[service] ||= {}
29
- @services[service]['data_dir'] = data_dir
30
+ @services[service]["data_dir"] = data_dir
30
31
  end
31
32
 
32
33
  def add_config(config, path)
33
34
  @configs[config] ||= {}
34
- @configs[config]['data_dir'] = path
35
+ @configs[config]["data_dir"] = path
36
+ end
37
+
38
+ def add_version(project_name, data)
39
+ @versions[project_name] = data
35
40
  end
36
41
 
37
42
  def add_ha_info(k, v)
@@ -40,12 +45,13 @@ module ChefBackup
40
45
 
41
46
  def manifest
42
47
  {
43
- 'strategy' => strategy,
44
- 'backup_time' => backup_time,
45
- 'topology' => topology,
46
- 'ha' => ha,
47
- 'services' => services,
48
- 'configs' => configs
48
+ "strategy" => strategy,
49
+ "backup_time" => backup_time,
50
+ "topology" => topology,
51
+ "ha" => ha,
52
+ "services" => services,
53
+ "configs" => configs,
54
+ "versions" => versions,
49
55
  }
50
56
  end
51
57
  end
@@ -0,0 +1,145 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@chef.io>)
3
+ # Author:: Steve Midgley (http://www.misuse.org/science)
4
+ # Copyright:: Copyright 2009-2016, Chef Software Inc.
5
+ # Copyright:: Copyright 2008-2016, Steve Midgley
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ require 'chef_backup/mash'
20
+
21
+ #
22
+ # DANGER! THIS FILE WAS VENDORDED FROM CHEF. IF YOU ARE
23
+ # MAKING A CHANGE HERE, CONSIDER WHETHER IT IS REQUIRED IN CHEF ALSO?
24
+ #
25
+ module ChefBackup
26
+ module Mixin
27
+ # == Chef::Mixin::DeepMerge
28
+ # Implements a deep merging algorithm for nested data structures.
29
+ # ==== Notice:
30
+ # This code was originally imported from deep_merge by Steve Midgley.
31
+ # deep_merge is available under the MIT license from
32
+ # http://trac.misuse.org/science/wiki/DeepMerge
33
+ module DeepMerge
34
+
35
+ extend self
36
+
37
+ def merge(first, second)
38
+ first = Mash.new(first) unless first.kind_of?(Mash)
39
+ second = Mash.new(second) unless second.kind_of?(Mash)
40
+
41
+ DeepMerge.deep_merge(second, first)
42
+ end
43
+
44
+ class InvalidParameter < StandardError; end
45
+
46
+ # Deep Merge core documentation.
47
+ # deep_merge! method permits merging of arbitrary child elements. The two top level
48
+ # elements must be hashes. These hashes can contain unlimited (to stack limit) levels
49
+ # of child elements. These child elements to not have to be of the same types.
50
+ # Where child elements are of the same type, deep_merge will attempt to merge them together.
51
+ # Where child elements are not of the same type, deep_merge will skip or optionally overwrite
52
+ # the destination element with the contents of the source element at that level.
53
+ # So if you have two hashes like this:
54
+ # source = {:x => [1,2,3], :y => 2}
55
+ # dest = {:x => [4,5,'6'], :y => [7,8,9]}
56
+ # dest.deep_merge!(source)
57
+ # Results: {:x => [1,2,3,4,5,'6'], :y => 2}
58
+ # By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
59
+ # To avoid this, use "deep_merge" (no bang/exclamation mark)
60
+ def deep_merge!(source, dest)
61
+ # if dest doesn't exist, then simply copy source to it
62
+ if dest.nil?
63
+ dest = source; return dest
64
+ end
65
+
66
+ case source
67
+ when nil
68
+ dest
69
+ when Hash
70
+ if dest.kind_of?(Hash)
71
+ source.each do |src_key, src_value|
72
+ if dest[src_key]
73
+ dest[src_key] = deep_merge!(src_value, dest[src_key])
74
+ else # dest[src_key] doesn't exist so we take whatever source has
75
+ dest[src_key] = src_value
76
+ end
77
+ end
78
+ else # dest isn't a hash, so we overwrite it completely
79
+ dest = source
80
+ end
81
+ when Array
82
+ if dest.kind_of?(Array)
83
+ dest = dest | source
84
+ else
85
+ dest = source
86
+ end
87
+ when String
88
+ dest = source
89
+ else # src_hash is not an array or hash, so we'll have to overwrite dest
90
+ dest = source
91
+ end
92
+ dest
93
+ end # deep_merge!
94
+
95
+ def hash_only_merge(merge_onto, merge_with)
96
+ hash_only_merge!(safe_dup(merge_onto), safe_dup(merge_with))
97
+ end
98
+
99
+ def safe_dup(thing)
100
+ thing.dup
101
+ rescue TypeError
102
+ thing
103
+ end
104
+
105
+ # Deep merge without Array merge.
106
+ # `merge_onto` is the object that will "lose" in case of conflict.
107
+ # `merge_with` is the object whose values will replace `merge_onto`s
108
+ # values when there is a conflict.
109
+ def hash_only_merge!(merge_onto, merge_with)
110
+ # If there are two Hashes, recursively merge.
111
+ if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash)
112
+ merge_with.each do |key, merge_with_value|
113
+ value =
114
+ if merge_onto.has_key?(key)
115
+ hash_only_merge(merge_onto[key], merge_with_value)
116
+ else
117
+ merge_with_value
118
+ end
119
+
120
+ if merge_onto.respond_to?(:public_method_that_only_deep_merge_should_use)
121
+ # we can't call ImmutableMash#[]= because its immutable, but we need to mutate it to build it in-place
122
+ merge_onto.public_method_that_only_deep_merge_should_use(key, value)
123
+ else
124
+ merge_onto[key] = value
125
+ end
126
+ end
127
+ merge_onto
128
+
129
+ # If merge_with is nil, don't replace merge_onto
130
+ elsif merge_with.nil?
131
+ merge_onto
132
+
133
+ # In all other cases, replace merge_onto with merge_with
134
+ else
135
+ merge_with
136
+ end
137
+ end
138
+
139
+ def deep_merge(source, dest)
140
+ deep_merge!(safe_dup(source), safe_dup(dest))
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -1,35 +1,89 @@
1
1
  require 'fileutils'
2
+ require 'json'
2
3
  require 'mixlib/shellout'
3
4
  require 'chef_backup/config'
4
5
  require 'chef_backup/logger'
5
6
 
7
+ # rubocop:disable ModuleLength
6
8
  # rubocop:disable IndentationWidth
7
9
  module ChefBackup
8
10
  # Common helper methods that are usefull in many classes
9
11
  module Helpers
10
12
  # rubocop:enable IndentationWidth
11
13
 
12
- SERVER_ADD_ONS = %w(
13
- opscode-manage
14
- opscode-reporting
15
- opscode-push-jobs-server
16
- opscode-analytics
17
- chef-ha
18
- chef-sync
19
- ).freeze
14
+ SERVER_ADD_ONS = {
15
+ 'opscode-manage' => {
16
+ 'config_file' => '/etc/opscode-manage/manage.rb',
17
+ 'ctl_command' => 'opscode-manage-ctl'
18
+ },
19
+ 'opscode-reporting' => {
20
+ 'config_file' => '/etc/opscode-reporting/opscode-reporting.rb',
21
+ 'ctl_command' => 'opscode-reporting-ctl'
22
+ },
23
+ 'opscode-push-jobs-server' => {
24
+ 'config_file' => '/etc/opscode-push-jobs-server/opscode-push-jobs-server.rb',
25
+ 'ctl_command' => 'opscode-push-jobs-server-ctl'
26
+ },
27
+ 'opscode-analytics' => {
28
+ 'config_file' => '/etc/opscode-analytics/opscode-analytics.rb',
29
+ 'ctl_command' => 'opscode-analytics-ctl'
30
+ },
31
+ 'chef-ha' => {
32
+ 'config_file' => '/etc/opscode/chef-server.rb'
33
+ },
34
+ 'chef-sync' => {
35
+ 'config_file' => '/etc/chef-sync/chef-sync.rb',
36
+ 'ctl_command' => 'chef-sync-ctl'
37
+ },
38
+ 'chef-marketplace' => {
39
+ 'config_file' => '/etc/chef-marketplace/marketplace.rb',
40
+ 'ctl_command' => 'chef-marketplace-ctl'
41
+ }
42
+ }.freeze
20
43
 
21
- def private_chef
22
- config['private_chef']
23
- end
44
+ DEFAULT_PG_OPTIONS = '-c statement_timeout=3600000'.freeze
24
45
 
25
46
  def config
26
47
  ChefBackup::Config
27
48
  end
28
49
 
50
+ def config_base
51
+ ChefBackup::Config['config_base']
52
+ end
53
+
54
+ def service_config
55
+ ChefBackup::Config[config_base]
56
+ end
57
+
58
+ def ctl_command
59
+ service_config['backup']['ctl-command']
60
+ end
61
+
62
+ def running_filepath
63
+ service_config['backup']['running_filepath']
64
+ end
65
+
66
+ def database_name
67
+ service_config['backup']['database_name']
68
+ end
69
+
29
70
  def log(message, level = :info)
30
71
  ChefBackup::Logger.logger.log(message, level)
31
72
  end
32
73
 
74
+ # Note that when we are in the backup codepath, we have access to a running
75
+ # chef server and hence, the ctl command puts all our flags under the current
76
+ # running service namespace. The lets the default configuration of the server
77
+ # provide flags that the user doesn't necessarily provide on the command line.
78
+ #
79
+ # During the restore codepath, there may be no running chef server. This means
80
+ # that we need to be paranoid about the existence of the service_config hash.
81
+ def shell_timeout
82
+ option = config['shell_out_timeout'] ||
83
+ (service_config && service_config['backup']['shell_out_timeout'])
84
+ option.to_f unless option.nil?
85
+ end
86
+
33
87
  #
34
88
  # @param file [String] A path to a file on disk
35
89
  # @param exception [Exception] An exception to raise if file is not present
@@ -38,11 +92,13 @@ module Helpers
38
92
  # @return [TrueClass, FalseClass]
39
93
  #
40
94
  def ensure_file!(file, exception, message)
41
- File.exist?(file) ? true : fail(exception, message)
95
+ File.exist?(file) ? true : raise(exception, message)
42
96
  end
43
97
 
44
98
  def shell_out(*command)
45
- cmd = Mixlib::ShellOut.new(*command)
99
+ options = command.last.is_a?(Hash) ? command.pop : {}
100
+ opts_with_defaults = { 'timeout' => shell_timeout }.merge(options)
101
+ cmd = Mixlib::ShellOut.new(*command, opts_with_defaults)
46
102
  cmd.live_stream ||= $stdout.tty? ? $stdout : nil
47
103
  cmd.run_command
48
104
  cmd
@@ -54,8 +110,39 @@ module Helpers
54
110
  cmd
55
111
  end
56
112
 
113
+ def project_name
114
+ service_config['backup']['project_name']
115
+ end
116
+
117
+ def base_install_dir
118
+ "/opt/#{project_name}"
119
+ end
120
+
121
+ def addon_install_dir(name)
122
+ # can use extra field in SERVER_ADD_ONS to extend if someone isn't following this pattern.
123
+ "/opt/#{name}"
124
+ end
125
+
126
+ def base_config_dir
127
+ "/etc/#{project_name}"
128
+ end
129
+
130
+ def chpst
131
+ "#{base_install_dir}/embedded/bin/chpst"
132
+ end
133
+
134
+ def pgsql
135
+ "#{base_install_dir}/embedded/bin/psql"
136
+ end
137
+
138
+ def pg_options
139
+ config['pg_options'] ||
140
+ (service_config && service_config['backup']['pg_options']) ||
141
+ DEFAULT_PG_OPTIONS
142
+ end
143
+
57
144
  def all_services
58
- Dir['/opt/opscode/sv/*'].map { |f| File.basename(f) }.sort
145
+ Dir["#{base_install_dir}/sv/*"].map { |f| File.basename(f) }.sort
59
146
  end
60
147
 
61
148
  def enabled_services
@@ -63,20 +150,20 @@ module Helpers
63
150
  end
64
151
 
65
152
  def disabled_services
66
- all_services.select { |sv| !service_enabled?(sv) }
153
+ all_services.reject { |sv| service_enabled?(sv) }
67
154
  end
68
155
 
69
156
  def service_enabled?(service)
70
- File.symlink?("/opt/opscode/service/#{service}")
157
+ File.symlink?("#{base_install_dir}/service/#{service}")
71
158
  end
72
159
 
73
160
  def stop_service(service)
74
- res = shell_out("chef-server-ctl stop #{service}")
161
+ res = shell_out("#{ctl_command} stop #{service}")
75
162
  res
76
163
  end
77
164
 
78
165
  def start_service(service)
79
- res = shell_out("chef-server-ctl start #{service}")
166
+ res = shell_out("#{ctl_command} start #{service}")
80
167
  res
81
168
  end
82
169
 
@@ -92,32 +179,53 @@ module Helpers
92
179
  enabled_services.each { |sv| start_service(sv) }
93
180
  end
94
181
 
95
- def enabled_addons
96
- SERVER_ADD_ONS.select { |service| addon?(service) }
182
+ def restart_chef_server
183
+ shell_out("#{ctl_command} restart #{service}")
184
+ end
185
+
186
+ def reconfigure_add_ons
187
+ enabled_addons.each do |_name, config|
188
+ shell_out("#{config['ctl_command']} reconfigure") if config.key?('ctl_command')
189
+ end
190
+ end
191
+
192
+ def restart_add_ons
193
+ enabled_addons.each do |_name, config|
194
+ shell_out("#{config['ctl_command']} restart") if config.key?('ctl_command')
195
+ end
196
+ end
197
+
198
+ def reconfigure_marketplace
199
+ log 'Setting up Chef Marketplace'
200
+ shell_out('chef-marketplace-ctl reconfigure')
97
201
  end
98
202
 
99
- def addon?(service)
100
- File.directory?("/etc/#{service}")
203
+ def enabled_addons
204
+ SERVER_ADD_ONS.select do |name, config|
205
+ !config['config_file'].nil? &&
206
+ File.directory?(File.dirname(config['config_file'])) &&
207
+ File.directory?(addon_install_dir(name))
208
+ end
101
209
  end
102
210
 
103
211
  def strategy
104
- private_chef['backup']['strategy']
212
+ service_config['backup']['strategy']
105
213
  end
106
214
 
107
215
  def topology
108
- private_chef['topology']
216
+ service_config['topology']
109
217
  end
110
218
 
111
219
  def frontend?
112
- private_chef['role'] == 'frontend'
220
+ service_config['role'] == 'frontend'
113
221
  end
114
222
 
115
223
  def backend?
116
- private_chef['role'] =~ /backend|standalone/
224
+ service_config['role'] =~ /backend|standalone/
117
225
  end
118
226
 
119
227
  def online?
120
- private_chef['backup']['mode'] == 'online'
228
+ service_config['backup']['mode'] == 'online'
121
229
  end
122
230
 
123
231
  def ha?
@@ -132,10 +240,14 @@ module Helpers
132
240
  topology == 'standalone'
133
241
  end
134
242
 
243
+ def marketplace?
244
+ shell_out('which chef-marketplace-ctl').exitstatus == 0
245
+ end
246
+
135
247
  def tmp_dir
136
248
  @tmp_dir ||= begin
137
249
  dir = safe_key { config['tmp_dir'] } ||
138
- safe_key { private_chef['backup']['tmp_dir'] }
250
+ safe_key { service_config['backup']['tmp_dir'] }
139
251
  if dir
140
252
  FileUtils.mkdir_p(dir) unless File.directory?(dir)
141
253
  dir
@@ -152,6 +264,20 @@ module Helpers
152
264
  true
153
265
  end
154
266
 
267
+ def version_from_manifest_file(file)
268
+ return :no_version if file.nil?
269
+
270
+ path = File.expand_path(file)
271
+ if File.exist?(path)
272
+ config = JSON.parse(File.read(path))
273
+ { 'version' => config['build_version'],
274
+ 'revision' => config['build_git_revision'],
275
+ 'path' => path }
276
+ else
277
+ :no_version
278
+ end
279
+ end
280
+
155
281
  private
156
282
 
157
283
  def safe_key
@@ -1,5 +1,3 @@
1
- require 'highline'
2
-
3
1
  module ChefBackup
4
2
  # Basic Logging Class
5
3
  class Logger
@@ -15,18 +13,25 @@ module ChefBackup
15
13
  attr_accessor :stdout
16
14
 
17
15
  def initialize(logfile = nil)
18
- $stdout = logfile ? File.open(logfile, 'ab') : $stdout
19
- @highline = HighLine.new($stdin, $stdout)
16
+ $stdout = logfile ? File.open(logfile, "ab") : $stdout
17
+ end
18
+
19
+ # pastel.decorate is a lightweight replacement for highline.color
20
+ def pastel
21
+ @pastel ||= begin
22
+ require "pastel" unless defined?(Pastel)
23
+ Pastel.new
24
+ end
20
25
  end
21
26
 
22
27
  def log(msg, level = :info)
23
28
  case level
24
29
  when :warn
25
30
  msg = "WARNING: #{msg}"
26
- $stdout.puts(color? ? @highline.color(msg, :yellow) : msg)
31
+ $stdout.puts(color? ? pastel.decorate(msg, :yellow) : msg)
27
32
  when :error
28
33
  msg = "ERROR: #{msg}"
29
- $stdout.puts(color? ? @highline.color(msg, :red) : msg)
34
+ $stdout.puts(color? ? pastel.decorate(msg, :red) : msg)
30
35
  else
31
36
  $stdout.puts(msg)
32
37
  end