rzo 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.gitignore +15 -0
- data/.rubocop.yml +62 -0
- data/.simplecov +5 -0
- data/.travis.yml +10 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -4
- data/Guardfile +41 -0
- data/LICENSE +13 -0
- data/README.md +308 -20
- data/Rakefile +28 -3
- data/bin/console +5 -6
- data/exe/rizzo +7 -0
- data/exe/rzo +7 -0
- data/ext/_rizzo.json +32 -0
- data/lib/rzo/app/config.rb +16 -0
- data/lib/rzo/app/generate.rb +137 -0
- data/lib/rzo/app/subcommand.rb +172 -0
- data/lib/rzo/app/templates/Vagrantfile.erb +55 -0
- data/lib/rzo/app.rb +92 -0
- data/lib/rzo/logging.rb +144 -0
- data/lib/rzo/option_parsing.rb +123 -0
- data/lib/rzo/trollop.rb +930 -0
- data/lib/rzo/version.rb +15 -1
- data/lib/rzo.rb +6 -2
- data/rzo.gemspec +35 -14
- metadata +196 -19
- data/CODE_OF_CONDUCT.md +0 -74
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'rzo/app/subcommand'
|
2
|
+
require 'pp'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Rzo
|
6
|
+
class App
|
7
|
+
##
|
8
|
+
# Produce a `Vagrantfile` in the top level puppet control repo.
|
9
|
+
#
|
10
|
+
# Load all rizzo config files, then produce the Vagrantfile from an ERB
|
11
|
+
# template.
|
12
|
+
class Generate < Subcommand
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
# The main run method for the subcommand.
|
16
|
+
def run
|
17
|
+
exit_status = 0
|
18
|
+
load_config!
|
19
|
+
# Vagrantfile
|
20
|
+
erbfile = File.expand_path('../templates/Vagrantfile.erb', __FILE__)
|
21
|
+
content = vagrantfile_content(erbfile, config)
|
22
|
+
write_file(opts[:vagrantfile]) { |fd| fd.write(content) }
|
23
|
+
say "Wrote vagrant config to #{opts[:vagrantfile]}"
|
24
|
+
exit_status
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Return a list of agent node definitions suitable for the Vagrantfile
|
29
|
+
# template.
|
30
|
+
#
|
31
|
+
# @param [Hash] config The configuration hash used to fill in the ERB
|
32
|
+
# template.
|
33
|
+
#
|
34
|
+
# @return [Array<Hash>] list of agent nodes to fill in the Vagrantfile
|
35
|
+
# template.
|
36
|
+
def vagrantfile_agents(config)
|
37
|
+
pm_settings = puppetmaster_settings(config)
|
38
|
+
agent_nodes = [*config['nodes']].reject do |n|
|
39
|
+
pm_settings['name'].include?(n['name'])
|
40
|
+
end
|
41
|
+
|
42
|
+
agent_nodes.each do |n|
|
43
|
+
n.deep_merge(config['defaults'])
|
44
|
+
log.debug "puppetagent #{n['name']} = \n" + n.pretty_inspect
|
45
|
+
end
|
46
|
+
|
47
|
+
agent_nodes
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Return a list of puppetmaster node definitions suitable for the
|
52
|
+
# Vagrantfile template.
|
53
|
+
#
|
54
|
+
# @param [Hash] config The configuration hash used to fill in the ERB
|
55
|
+
# template.
|
56
|
+
#
|
57
|
+
# @return [Array<Hash>] list of puppet master nodes to fill in the
|
58
|
+
# Vagrantfile template.
|
59
|
+
#
|
60
|
+
# rubocop:disable Metrics/AbcSize
|
61
|
+
def vagrantfile_puppet_masters(config)
|
62
|
+
pm_settings = puppetmaster_settings(config)
|
63
|
+
pm_names = pm_settings['name']
|
64
|
+
|
65
|
+
nodes = [*config['nodes']].find_all { |n| pm_names.include?(n['name']) }
|
66
|
+
nodes.each do |n|
|
67
|
+
n.deep_merge(config['defaults'])
|
68
|
+
n.deep_merge(pm_settings)
|
69
|
+
n[:puppetmaster] = true
|
70
|
+
log.debug "puppetmaster #{n['name']} = \n" + n.pretty_inspect
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# rubocop:enable Metrics/AbcSize
|
74
|
+
|
75
|
+
##
|
76
|
+
# Return the proxy configuration exception list as a string, or nil if not set.
|
77
|
+
#
|
78
|
+
# @param [Hash] config The configuration hash used to fill in the ERB
|
79
|
+
# template.
|
80
|
+
#
|
81
|
+
# @return [String,nil] proxy exclusion list or nil if not specified.
|
82
|
+
def proxy_config(config)
|
83
|
+
# Proxy Setting
|
84
|
+
return nil unless config['config']
|
85
|
+
config['config']['no_proxy'] || DEFAULT_NO_PROXY
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Return a timestamp to embed in the output Vagrantfile. This is a method
|
90
|
+
# so it may be stubbed out in the tests.
|
91
|
+
def timestamp
|
92
|
+
Time.now
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Return a string which is the Vagrantfile content of a filled in
|
97
|
+
# Vagrantfile erb template. The configuration data parsed by load_config!
|
98
|
+
# is expected as input, along with the template to fill in.
|
99
|
+
#
|
100
|
+
# The base templates directory is relative to the directory containing
|
101
|
+
# this file.
|
102
|
+
#
|
103
|
+
# @param [String] template The fully qualified path to the ERB template.
|
104
|
+
#
|
105
|
+
# @param [Hash] config The configuration hash used to fill in the ERB
|
106
|
+
# template.
|
107
|
+
#
|
108
|
+
# @return [String] the content of the filled in template.
|
109
|
+
def vagrantfile_content(template, config)
|
110
|
+
renderer = ERB.new(File.read(template), 0, '-')
|
111
|
+
|
112
|
+
no_proxy = proxy_config(config)
|
113
|
+
|
114
|
+
# Agent nodes [Array<Hash>]
|
115
|
+
agent_nodes = vagrantfile_agents(config)
|
116
|
+
# Puppet Master nodes [Array<Hash>]
|
117
|
+
puppet_master_nodes = vagrantfile_puppet_masters(config)
|
118
|
+
|
119
|
+
# nodes is used by the Vagrantfile.erb template
|
120
|
+
nodes = [*puppet_master_nodes, *agent_nodes]
|
121
|
+
content = renderer.result(binding)
|
122
|
+
content
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# dump out the puppetmaster settings from the config.
|
127
|
+
def puppetmaster_settings(config)
|
128
|
+
log.debug "config['puppetmaster'] = \n" + \
|
129
|
+
config['puppetmaster'].pretty_inspect
|
130
|
+
config['puppetmaster']
|
131
|
+
end
|
132
|
+
|
133
|
+
# Constants used by the Vagrantfile.erb template.
|
134
|
+
DEFAULT_NO_PROXY = 'localhost,127.0.0.1'.freeze
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'rzo/logging'
|
2
|
+
require 'deep_merge'
|
3
|
+
module Rzo
|
4
|
+
class App
|
5
|
+
# The base class for subcommands
|
6
|
+
class Subcommand
|
7
|
+
include Logging
|
8
|
+
extend Logging
|
9
|
+
# The options hash injected from the application controller via the
|
10
|
+
# initialize method.
|
11
|
+
attr_reader :opts
|
12
|
+
# The Rizzo configuration after loading ~/.rizzo.json (--config).
|
13
|
+
# See #load_config!
|
14
|
+
attr_reader :config
|
15
|
+
|
16
|
+
##
|
17
|
+
# Delegated method to mock with fixture data.
|
18
|
+
def self.load_rizzo_config(fpath)
|
19
|
+
config_file = Pathname.new(fpath).expand_path
|
20
|
+
raise ErrorAndExit, "Cannot read config file #{config_file}" unless config_file.readable?
|
21
|
+
config = JSON.parse(config_file.read)
|
22
|
+
log.debug "Loaded #{config_file}"
|
23
|
+
config
|
24
|
+
rescue JSON::ParserError => e
|
25
|
+
raise ErrorAndExit, "Could not parse rizzo config #{config_file} #{e.message}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initialize a subcommand with options injected by the application
|
29
|
+
# controller.
|
30
|
+
#
|
31
|
+
# @param [Hash] opts the Options hash initialized by the Application
|
32
|
+
# controller.
|
33
|
+
def initialize(opts = {}, stdout = $stdout, stderr = $stderr)
|
34
|
+
@opts = opts
|
35
|
+
@stdout = stdout
|
36
|
+
@stderr = stderr
|
37
|
+
reset_logging!(opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Default run method. Override this method in a subcommand sub-class
|
42
|
+
#
|
43
|
+
# @return [Fixnum] the exit status of the subcommand.
|
44
|
+
def run
|
45
|
+
error "Implement the run method in subclass #{self.class}"
|
46
|
+
1
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
##
|
52
|
+
# Load rizzo configuration. Populate @config.
|
53
|
+
#
|
54
|
+
# Read rizzo configuration by looping through control repos and stopping
|
55
|
+
# at first match and merge on top of local, defaults (~/.rizzo.json)
|
56
|
+
def load_config!
|
57
|
+
config = load_rizzo_config(opts[:config])
|
58
|
+
validate_config(config)
|
59
|
+
repos = config['control_repos']
|
60
|
+
@config = load_repo_configs(config, repos)
|
61
|
+
debug "Merged configuration: \n#{JSON.pretty_generate(@config)}"
|
62
|
+
validate_forwarded_ports(@config)
|
63
|
+
validate_ip_addresses(@config)
|
64
|
+
@config
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Given a list of repository paths, load .rizzo.json from the root of each
|
69
|
+
# repository and return the result merged onto config. The merging
|
70
|
+
# behavior is implemented by
|
71
|
+
# [deep_merge](http://www.rubydoc.info/gems/deep_merge/1.1.1)
|
72
|
+
#
|
73
|
+
# @param [Hash] config the starting config hash. Repo config maps will be
|
74
|
+
# merged on top of this starting map.
|
75
|
+
#
|
76
|
+
# @param [Array] repos the list of repositories to load .rizzo.json from.
|
77
|
+
#
|
78
|
+
# @return [Hash] the merged configuration hash.
|
79
|
+
def load_repo_configs(config = {}, repos = [])
|
80
|
+
repos.each_with_object(config.dup) do |repo, hsh|
|
81
|
+
fp = Pathname.new(repo).expand_path + '.rizzo.json'
|
82
|
+
if fp.readable?
|
83
|
+
hsh.deep_merge!(load_rizzo_config(fp))
|
84
|
+
else
|
85
|
+
log.debug "Skipped #{fp} (it is not readable)"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Basic validation of the configuration file content.
|
92
|
+
#
|
93
|
+
# @param [Hash] config the configuration map
|
94
|
+
def validate_config(config)
|
95
|
+
errors = []
|
96
|
+
errors.push 'control_repos key is not an Array' unless config['control_repos'].is_a?(Array)
|
97
|
+
errors.each { |l| log.error l }
|
98
|
+
raise ErrorAndExit, 'Errors found in config file. Cannot proceed.' unless errors.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Check for duplicate forwarded host ports across all hosts and exit
|
103
|
+
# non-zero with an error message if found.
|
104
|
+
def validate_forwarded_ports(config)
|
105
|
+
host_ports = []
|
106
|
+
[*config['nodes']].each do |node|
|
107
|
+
[*node['forwarded_ports']].each do |hsh|
|
108
|
+
port = hsh['host'].to_i
|
109
|
+
raise_port_err(port, node['name']) if host_ports.include?(port)
|
110
|
+
host_ports.push(port)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
log.debug "host_ports = #{host_ports}"
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Check for duplicate forwarded host ports across all hosts and exit
|
118
|
+
# non-zero with an error message if found.
|
119
|
+
def validate_ip_addresses(config)
|
120
|
+
ips = []
|
121
|
+
[*config['nodes']].each do |node|
|
122
|
+
if ip = node['ip']
|
123
|
+
raise_ip_err(ip, node['name']) if ips.include?(ip)
|
124
|
+
ips.push(ip)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
log.debug "ips = #{ips}"
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Helper to raise a duplicate port error
|
132
|
+
def raise_port_err(port, node)
|
133
|
+
raise ErrorAndExit, "host port #{port} on node #{node} " \
|
134
|
+
'is a duplicate. Ports must be unique. Check .rizzo.json ' \
|
135
|
+
'files in each control repository for duplicate forwarded_ports entries.'
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Helper to raise a duplicate port error
|
140
|
+
def raise_ip_err(ip, node)
|
141
|
+
raise ErrorAndExit, "host ip #{ip} on node #{node} " \
|
142
|
+
'is a duplicate. IP addresses must be unique. Check .rizzo.json ' \
|
143
|
+
'files in each control repository for duplicate ip entries'
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Load the base configuration and return it as a hash. This is necessary
|
148
|
+
# to get access to the `'control_repos'` top level key, which is expected
|
149
|
+
# to be an Array of fully qualified paths to control repo base
|
150
|
+
# directories.
|
151
|
+
#
|
152
|
+
# @param [String] fpath The fully qualified path to the configuration file
|
153
|
+
# to load.
|
154
|
+
#
|
155
|
+
# @return [Hash] The configuration map
|
156
|
+
def load_rizzo_config(fpath)
|
157
|
+
self.class.load_rizzo_config(fpath)
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Write a file by yielding a file descriptor to the passed block. In the
|
162
|
+
# case of opening a file, the FD will automatically be closed.
|
163
|
+
def write_file(filepath)
|
164
|
+
case filepath
|
165
|
+
when 'STDOUT' then yield @stdout
|
166
|
+
when 'STDERR' then yield @stderr
|
167
|
+
else File.open(filepath, 'w') { |fd| yield fd }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# This file generated with Rizzo <%= Rzo.version %> on <%= timestamp %>
|
2
|
+
# https://github.com/ghoneycutt/rizzo
|
3
|
+
Vagrant.configure(2) do |config|
|
4
|
+
# use 'vagrant plugin install vagrant-proxyconf' to install
|
5
|
+
if Vagrant.has_plugin?('vagrant-proxyconf')
|
6
|
+
config.proxy.http = ENV['HTTP_PROXY'] if ENV['HTTP_PROXY']
|
7
|
+
config.proxy.https = ENV['HTTPS_PROXY'] if ENV['HTTPS_PROXY']
|
8
|
+
<%- if no_proxy -%>
|
9
|
+
config.proxy.no_proxy = <%= no_proxy.inspect %>
|
10
|
+
<%- end -%>
|
11
|
+
end
|
12
|
+
<%- nodes.each do |nc| -%>
|
13
|
+
|
14
|
+
config.vm.define <%= nc['name'].inspect %>, autostart: false do |cfg|
|
15
|
+
cfg.vm.box = <%= nc['box'].inspect %>
|
16
|
+
cfg.vm.box_url = <%= nc['box_url'].inspect %>
|
17
|
+
cfg.vm.box_download_checksum = <%= nc['box_download_checksum'].inspect %>
|
18
|
+
cfg.vm.box_download_checksum_type = <%= nc['box_download_checksum_type'].inspect %>
|
19
|
+
cfg.vm.provider :virtualbox do |vb|
|
20
|
+
vb.customize ['modifyvm', :id, '--memory', <%= nc['memory'].inspect %>]
|
21
|
+
end
|
22
|
+
cfg.vm.hostname = <%= nc['hostname'].inspect %>
|
23
|
+
cfg.vm.network 'private_network',
|
24
|
+
ip: <%= nc['ip'].inspect %>,
|
25
|
+
netmask: <%= nc['netmask'].inspect %>
|
26
|
+
<%- [*nc['forwarded_ports']].each do |forwarded_port| -%>
|
27
|
+
cfg.vm.network 'forwarded_port',
|
28
|
+
guest: <%= forwarded_port['guest'].inspect %>,
|
29
|
+
host: <%= forwarded_port['host'].inspect %>
|
30
|
+
<%- end -%>
|
31
|
+
<%- synced_folders = nc['synced_folders'] || {} -%>
|
32
|
+
<%- synced_folders.each_pair do |k, v| -%>
|
33
|
+
cfg.vm.synced_folder <%= v['local'].inspect %>, <%= k.inspect %>,
|
34
|
+
owner: <%= v['owner'].inspect %>, group: <%= v['group'].inspect %>
|
35
|
+
<%- end -%>
|
36
|
+
<%- if nc['bootstrap_repo_path'] -%>
|
37
|
+
config.vm.synced_folder <%= nc['bootstrap_repo_path'].inspect %>,
|
38
|
+
<%= nc['bootstrap_guest_path'].inspect %>,
|
39
|
+
owner: 'vagrant', group: 'root'
|
40
|
+
<%- if nc[:puppetmaster] -%>
|
41
|
+
config.vm.provision 'shell', inline: <%= "echo 'modulepath = #{nc['modulepath'].join(':')}' > #{nc['bootstrap_guest_path']}/environment.conf".inspect %>
|
42
|
+
<%- end -%>
|
43
|
+
config.vm.provision 'shell', inline: <%= "/bin/bash #{nc['bootstrap_guest_path']}/#{nc['bootstrap_script_path']} #{nc['bootstrap_script_args']}".inspect %>
|
44
|
+
<%- if nc['update_packages'] -%>
|
45
|
+
config.vm.provision 'shell', inline: <%= nc['update_packages_command'].inspect %>
|
46
|
+
<%- end -%>
|
47
|
+
<%- if nc['shutdown'] -%>
|
48
|
+
config.vm.provision 'shell', inline: <%= nc['shutdown_command'].inspect %>
|
49
|
+
<%- end -%>
|
50
|
+
<%- end -%>
|
51
|
+
end
|
52
|
+
<%- end -%>
|
53
|
+
end
|
54
|
+
# -*- mode: ruby -*-
|
55
|
+
# vim:ft=ruby
|
data/lib/rzo/app.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rzo'
|
2
|
+
require 'rzo/logging'
|
3
|
+
require 'rzo/option_parsing'
|
4
|
+
require 'rzo/app/config'
|
5
|
+
require 'rzo/app/generate'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Rzo
|
9
|
+
##
|
10
|
+
# The application controller. An instance of this class models the
|
11
|
+
# application lifecycle. Input configuration follows the 12 factor model.
|
12
|
+
#
|
13
|
+
# The general lifecycle is:
|
14
|
+
#
|
15
|
+
# * app = App.new()
|
16
|
+
# * app.parse_options!
|
17
|
+
# * app.run
|
18
|
+
class App
|
19
|
+
include Rzo::Logging
|
20
|
+
include Rzo::OptionParsing
|
21
|
+
|
22
|
+
##
|
23
|
+
# Exception used to exit the app from a subcommand. Caught by the main run
|
24
|
+
# method in the app controller
|
25
|
+
class ErrorAndExit < StandardError
|
26
|
+
attr_accessor :exit_status
|
27
|
+
def initialize(message = nil, exit_status = 1)
|
28
|
+
super(message)
|
29
|
+
self.exit_status = exit_status
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# @param [Array] argv The argument vector, passed to the option parser.
|
35
|
+
#
|
36
|
+
# @param [Hash] env The environment hash, passed to the option parser to
|
37
|
+
# supply defaults not specified on the command line argument vector.
|
38
|
+
#
|
39
|
+
# @return [App] the application instance.
|
40
|
+
def initialize(argv = ARGV.dup, env = ENV.to_hash, stdout = $stdout, stderr = $stderr)
|
41
|
+
@argv = argv
|
42
|
+
@env = env
|
43
|
+
@stdout = stdout
|
44
|
+
@stderr = stderr
|
45
|
+
reset!
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Reset all state associated with this application instance.
|
50
|
+
def reset!
|
51
|
+
reset_options!
|
52
|
+
reset_logging!(opts)
|
53
|
+
@api = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Accessor to Subcommand::Generate
|
58
|
+
def generate
|
59
|
+
@generate ||= Generate.new(opts, @stdout, @stderr)
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Override this later to allow trollop to write to an intercepted file
|
64
|
+
# descriptor for testing. This will avoid trollop's behavior of calling
|
65
|
+
# exit()
|
66
|
+
def educate
|
67
|
+
Trollop.educate
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# The main application run method
|
72
|
+
#
|
73
|
+
# @return [Fixnum] the system exit code
|
74
|
+
#
|
75
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
76
|
+
def run
|
77
|
+
case opts[:subcommand]
|
78
|
+
when 'config'
|
79
|
+
Config.new(opts, @stdout, @stderr).run
|
80
|
+
when 'generate'
|
81
|
+
generate.run
|
82
|
+
else
|
83
|
+
educate
|
84
|
+
end
|
85
|
+
rescue ErrorAndExit => e
|
86
|
+
log.error e.message
|
87
|
+
e.backtrace.each { |l| log.debug(l) }
|
88
|
+
e.exit_status
|
89
|
+
end
|
90
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
91
|
+
end
|
92
|
+
end
|
data/lib/rzo/logging.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'stringio'
|
3
|
+
require 'syslog/logger'
|
4
|
+
module Rzo
|
5
|
+
##
|
6
|
+
# Support module to mix into a class for consistent logging behavior.
|
7
|
+
module Logging
|
8
|
+
##
|
9
|
+
# Reset the global logger instance and return it as an object.
|
10
|
+
#
|
11
|
+
# @return [Logger] initialized logging instance
|
12
|
+
def self.reset_logging!(opts)
|
13
|
+
logger = opts[:syslog] ? syslog_logger : stream_logger(opts)
|
14
|
+
@log = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Return a new Syslog::Logger instance configured for syslog output
|
19
|
+
def self.syslog_logger
|
20
|
+
# Use the daemon facility, matching Puppet behavior.
|
21
|
+
Syslog::Logger.new('rzo', Syslog::LOG_DAEMON)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Return a new Logger instance configured for file output
|
26
|
+
def self.stream_logger(opts)
|
27
|
+
out = map_file_option(opts[:logto])
|
28
|
+
logger = Logger.new(out)
|
29
|
+
logger.level = Logger::WARN
|
30
|
+
logger.level = Logger::INFO if opts[:verbose]
|
31
|
+
logger.level = Logger::DEBUG if opts[:debug]
|
32
|
+
logger
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Logging is handled centrally, the helper methods will delegate to the
|
37
|
+
# centrally configured logging instance.
|
38
|
+
def self.log
|
39
|
+
@log || reset_logging!(opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Map a file option to STDOUT, STDERR or a fully qualified file path.
|
44
|
+
#
|
45
|
+
# @param [String] filepath A relative or fully qualified file path, or the
|
46
|
+
# keyword strings 'STDOUT' or 'STDERR'
|
47
|
+
#
|
48
|
+
# @return [String] file path or $stdout or $sederr
|
49
|
+
def self.map_file_option(filepath)
|
50
|
+
case filepath
|
51
|
+
when 'STDOUT' then $stdout
|
52
|
+
when 'STDERR' then $stderr
|
53
|
+
when 'STDIN' then $stdin
|
54
|
+
when 'STRING' then StringIO.new
|
55
|
+
else File.expand_path(filepath)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def map_file_option(filepath)
|
60
|
+
::Rzo::Logging.map_file_option(filepath)
|
61
|
+
end
|
62
|
+
|
63
|
+
def log
|
64
|
+
::Rzo::Logging.log
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Reset the logging system, requires command line options to have been
|
69
|
+
# parsed.
|
70
|
+
#
|
71
|
+
# @param [Hash<Symbol, String>] opts Options hash, passed to the support module
|
72
|
+
def reset_logging!(opts)
|
73
|
+
::Rzo::Logging.reset_logging!(opts)
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Logs a message at the fatal (syslog err) log level
|
78
|
+
def fatal(msg)
|
79
|
+
log.fatal msg
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Logs a message at the error (syslog warning) log level.
|
84
|
+
# i.e. May indicate that an error will occur if action is not taken.
|
85
|
+
# e.g. A non-root file system has only 2GB remaining.
|
86
|
+
def error(msg)
|
87
|
+
log.error msg
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Logs a message at the warn (syslog notice) log level.
|
92
|
+
# e.g. Events that are unusual, but not error conditions.
|
93
|
+
def warn(msg)
|
94
|
+
log.warn msg
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Logs a message at the info (syslog info) log level
|
99
|
+
# i.e. Normal operational messages that require no action.
|
100
|
+
# e.g. An application has started, paused or ended successfully.
|
101
|
+
def info(msg)
|
102
|
+
log.info msg
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Logs a message at the debug (syslog debug) log level
|
107
|
+
# i.e. Information useful to developers for debugging the application.
|
108
|
+
def debug(msg)
|
109
|
+
log.debug msg
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Helper method to write output, used for stubbing out the tests.
|
114
|
+
#
|
115
|
+
# @param [String, IO] output the output path or a IO stream
|
116
|
+
def write_output(str, output)
|
117
|
+
if output.is_a?(IO)
|
118
|
+
output.puts(str)
|
119
|
+
else
|
120
|
+
File.open(output, 'w+') { |f| f.puts(str) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Helper method to read from STDIN, or a file and execute an arbitrary block
|
126
|
+
# of code. A block must be passed which will recieve an IO object in the
|
127
|
+
# event input is a readable file path.
|
128
|
+
def input_stream(input)
|
129
|
+
if input.is_a?(IO)
|
130
|
+
yield input
|
131
|
+
else
|
132
|
+
File.open(input, 'r') { |stream| yield stream }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Alternative to puts, writes output to STDERR by default and logs at level
|
138
|
+
# info.
|
139
|
+
def say(msg)
|
140
|
+
log.info(msg)
|
141
|
+
@stderr.puts(msg)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|