rzo 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|