Class: Rzo::App::Subcommand
- Inherits:
-
Object
- Object
- Rzo::App::Subcommand
- Extended by:
- Logging
- Includes:
- ConfigValidation, Logging
- Defined in:
- lib/rzo/app/subcommand.rb
Overview
The base class for subcommands rubocop:disable Metrics/ClassLength
Constant Summary
Constants included from ConfigValidation
ConfigValidation::CHECKS_PERSONAL_CONFIG, ConfigValidation::CHECKS_REPO_CONFIG, ConfigValidation::RZO_PERSONAL_CONFIG_SCHEMA, ConfigValidation::RZO_REPO_CONFIG_SCHEMA
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
The Rizzo configuration after loading ~/.rizzo.yaml (--config).
-
#opts ⇒ Object
readonly
The options hash injected from the application controller via the initialize method.
-
#pwd ⇒ Object
readonly
The present working directory at startup.
Class Method Summary collapse
-
.load_rizzo_config(fpath) ⇒ Object
Delegated method to mock with fixture data.
Instance Method Summary collapse
-
#initialize(opts = {}, stdout = $stdout, stderr = $stderr) ⇒ Subcommand
constructor
Initialize a subcommand with options injected by the application controller.
-
#load_config! ⇒ Object
private
Load rizzo configuration.
-
#load_repo_configs(config = {}, repos = []) ⇒ Hash
private
Given a list of repository paths, load .rizzo.yaml from the root of each repository and return the result merged onto config.
-
#load_rizzo_config(fpath) ⇒ Hash
private
Load the base configuration and return it as a hash.
-
#project_dir(path) ⇒ Object
private
Memoized method to return the fully qualified path to the current rizzo project directory, based on the pwd.
-
#raise_ip_err(ip, node) ⇒ Object
private
Helper to raise a duplicate port error.
-
#raise_port_err(port, node) ⇒ Object
private
Helper to raise a duplicate port error.
-
#readable?(path) ⇒ Boolean
private
helper method to to stub in tests.
-
#reorder_repos(repos = []) ⇒ Object
private
Given a list of control repositories, determine if the user's runtime pwd is in a control repository.
-
#run ⇒ Fixnum
Default run method.
-
#validate_forwarded_ports(config) ⇒ Object
private
Check for duplicate forwarded host ports across all hosts and exit non-zero with an error message if found.
-
#validate_ip_addresses(config) ⇒ Object
private
Check for duplicate forwarded host ports across all hosts and exit non-zero with an error message if found.
-
#write_file(filepath) ⇒ Object
private
Write a file by yielding a file descriptor to the passed block.
Methods included from Logging
debug, error, fatal, info, input_stream, log, log, map_file_option, map_file_option, reset_logging!, reset_logging!, say, stream_logger, syslog_logger, warn, write_output
Methods included from ConfigValidation
#compute_issues, #validate_complete_config!, #validate_control_repos, #validate_defaults_key, #validate_existence, #validate_inform!, #validate_personal_config!, #validate_personal_schema, #validate_schema
Constructor Details
#initialize(opts = {}, stdout = $stdout, stderr = $stderr) ⇒ Subcommand
Initialize a subcommand with options injected by the application controller.
controller.
41 42 43 44 45 46 47 |
# File 'lib/rzo/app/subcommand.rb', line 41 def initialize(opts = {}, stdout = $stdout, stderr = $stderr) @opts = opts @stdout = stdout @stderr = stderr reset_logging!(opts) @pwd = Dir.pwd end |
Instance Attribute Details
#config ⇒ Object (readonly)
The Rizzo configuration after loading ~/.rizzo.yaml (--config). See #load_config!
19 20 21 |
# File 'lib/rzo/app/subcommand.rb', line 19 def config @config end |
#opts ⇒ Object (readonly)
The options hash injected from the application controller via the initialize method.
16 17 18 |
# File 'lib/rzo/app/subcommand.rb', line 16 def opts @opts end |
#pwd ⇒ Object (readonly)
The present working directory at startup
21 22 23 |
# File 'lib/rzo/app/subcommand.rb', line 21 def pwd @pwd end |
Class Method Details
.load_rizzo_config(fpath) ⇒ Object
Delegated method to mock with fixture data.
25 26 27 28 29 30 31 32 33 34 |
# File 'lib/rzo/app/subcommand.rb', line 25 def self.load_rizzo_config(fpath) config_file = Pathname.new(fpath). raise ErrorAndExit, "Cannot read config file #{config_file}" unless config_file.readable? config = YAML.safe_load(config_file.read) log.debug "Loaded #{config_file}" config rescue Psych::SyntaxError => e raise ErrorAndExit, "Could not parse rizzo config #{config_file} #{e.message}" end |
Instance Method Details
#load_config! ⇒ Object (private)
Load rizzo configuration. Populate @config.
Read rizzo configuration by looping through control repos and stopping at first match and merge on top of local, defaults (~/.rizzo.yaml)
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/rzo/app/subcommand.rb', line 65 def load_config! config = load_rizzo_config(opts[:config]) validate_personal_config!(config) repos = reorder_repos(config['control_repos']) config['control_repos'] = repos @config = load_repo_configs(config, repos) debug "Merged configuration: \n#{@config.to_yaml}" # TODO: Move these validations to an instance method? validate_complete_config!(@config) # validate_forwarded_ports(@config) # validate_ip_addresses(@config) @config end |
#load_repo_configs(config = {}, repos = []) ⇒ Hash (private)
Given a list of repository paths, load .rizzo.yaml from the root of each repository and return the result merged onto config. The merging behavior is implemented by deep_merge
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rzo/app/subcommand.rb', line 91 def load_repo_configs(config = {}, repos = []) repos.each_with_object(config.dup) do |repo, hsh| fp = Pathname.new(repo). + '.rizzo.yaml' if readable?(fp.to_s) hsh.deep_merge!(load_rizzo_config(fp.to_s)) else log.debug "Skipped #{fp} (it is not readable)" end end end |
#load_rizzo_config(fpath) ⇒ Hash (private)
Load the base configuration and return it as a hash. This is necessary
to get access to the 'control_repos' top level key, which is expected
to be an Array of fully qualified paths to control repo base
directories.
157 158 159 |
# File 'lib/rzo/app/subcommand.rb', line 157 def load_rizzo_config(fpath) self.class.load_rizzo_config(fpath) end |
#project_dir(path) ⇒ Object (private)
Memoized method to return the fully qualified path to the current rizzo
project directory, based on the pwd. The project directory is the
dirname of the full path to a .rizzo.yaml config file. Return false
if not a project directory. ~/.rizzo.yaml is considered a personal
configuration and not a project configuration.
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rzo/app/subcommand.rb', line 185 def project_dir(path) return @project_dir unless @project_dir.nil? rizzo_file = Pathname.new("#{path}/.rizzo.yaml") personal_config = Pathname.new(File.('~/.rizzo.yaml')) iterations = 0 while @project_dir.nil? && iterations < 100 iterations += 1 if readable?(rizzo_file.to_s) && rizzo_file != personal_config @project_dir = rizzo_file.dirname.to_s else rizzo_file = rizzo_file.dirname.dirname + '.rizzo.yaml' @project_dir = false if rizzo_file.dirname.root? end end @project_dir end |
#raise_ip_err(ip, node) ⇒ Object (private)
Helper to raise a duplicate port error
141 142 143 144 145 |
# File 'lib/rzo/app/subcommand.rb', line 141 def raise_ip_err(ip, node) raise ErrorAndExit, "host ip #{ip} on node #{node} " \ 'is a duplicate. IP addresses must be unique. Check .rizzo.yaml ' \ 'files in each control repository for duplicate ip entries' end |
#raise_port_err(port, node) ⇒ Object (private)
Helper to raise a duplicate port error
133 134 135 136 137 |
# File 'lib/rzo/app/subcommand.rb', line 133 def raise_port_err(port, node) raise ErrorAndExit, "host port #{port} on node #{node} " \ 'is a duplicate. Ports must be unique. Check .rizzo.yaml ' \ 'files in each control repository for duplicate forwarded_ports entries.' end |
#readable?(path) ⇒ Boolean (private)
helper method to to stub in tests
173 174 175 |
# File 'lib/rzo/app/subcommand.rb', line 173 def readable?(path) File.readable?(path) end |
#reorder_repos(repos = []) ⇒ Object (private)
Given a list of control repositories, determine if the user's runtime pwd is in a control repository. If it is, move that control repository to the top level. If the user is inside a control repository and
208 209 210 211 212 213 214 215 |
# File 'lib/rzo/app/subcommand.rb', line 208 def reorder_repos(repos = []) if path = project_dir(pwd) new_repos = repos - [path] new_repos.unshift(path) else repos end end |
#run ⇒ Fixnum
Default run method. Override this method in a subcommand sub-class
53 54 55 56 |
# File 'lib/rzo/app/subcommand.rb', line 53 def run error "Implement the run method in subclass #{self.class}" 1 end |
#validate_forwarded_ports(config) ⇒ Object (private)
Check for duplicate forwarded host ports across all hosts and exit non-zero with an error message if found.
105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/rzo/app/subcommand.rb', line 105 def validate_forwarded_ports(config) host_ports = [] [*config['nodes']].each do |node| [*node['forwarded_ports']].each do |hsh| port = hsh['host'].to_i raise_port_err(port, node['name']) if host_ports.include?(port) host_ports.push(port) end end log.debug "host_ports = #{host_ports}" end |
#validate_ip_addresses(config) ⇒ Object (private)
Check for duplicate forwarded host ports across all hosts and exit non-zero with an error message if found.
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/rzo/app/subcommand.rb', line 120 def validate_ip_addresses(config) ips = [] [*config['nodes']].each do |node| if ip = node['ip'] raise_ip_err(ip, node['name']) if ips.include?(ip) ips.push(ip) end end log.debug "ips = #{ips}" end |
#write_file(filepath) ⇒ Object (private)
Write a file by yielding a file descriptor to the passed block. In the case of opening a file, the FD will automatically be closed.
164 165 166 167 168 169 170 |
# File 'lib/rzo/app/subcommand.rb', line 164 def write_file(filepath) case filepath when 'STDOUT' then yield @stdout when 'STDERR' then yield @stderr else File.open(filepath, 'w') { |fd| yield fd } end end |