Class: Inspec::Shell
- Inherits:
-
Object
- Object
- Inspec::Shell
- Defined in:
- lib/inspec/shell.rb
Overview
A pry based shell for inspec. Given a runner (with a configured backend and all that jazz), this shell will produce a pry shell from which you can run inspec/ruby commands that will be run within the context of the runner.
Instance Method Summary collapse
-
#configure_pry ⇒ Object
rubocop:disable Metrics/AbcSize.
- #help(topic = nil) ⇒ Object
-
#initialize(runner) ⇒ Shell
constructor
A new instance of Shell.
- #intro ⇒ Object
- #mark(x) ⇒ Object
- #print_matchers_help ⇒ Object
- #print_target_info ⇒ Object
- #readline_ignore(code) ⇒ Object
- #resources ⇒ Object
- #start ⇒ Object
Constructor Details
#initialize(runner) ⇒ Shell
Returns a new instance of Shell.
11 12 13 |
# File 'lib/inspec/shell.rb', line 11 def initialize(runner) @runner = runner end |
Instance Method Details
#configure_pry ⇒ Object
rubocop:disable Metrics/AbcSize
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/inspec/shell.rb', line 33 def configure_pry # rubocop:disable Metrics/AbcSize # Delete any before_session, before_eval, and after_eval hooks so we can # replace them with our own. Pry 0.10 used to have a single method to clear # all hooks, but this was removed in Pry 0.11. %i{before_session before_eval after_eval}.each do |event| Pry.hooks.get_hooks(event).keys.map { |hook| Pry.hooks.delete_hook(event, hook) } end that = self # Add the help command Pry::Commands.block_command "help", "Show examples" do |resource| that.help(resource) end # configure pry shell prompt Pry::Prompt.add( :inspec, "inspec custom prompt" ) do |_context, _nesting, _pry_instance, _sep| "#{readline_ignore("\e[1m\e[32m")}inspec> #{readline_ignore("\e[0m")}" end Pry.config.prompt = Pry::Prompt[:inspec] # Add a help menu as the default intro Pry.hooks.add_hook(:before_session, "inspec_intro") do intro print_target_info end # Track the rules currently registered and what their merge count is. Pry.hooks.add_hook(:before_eval, "inspec_before_eval") do @runner.reset end # After pry has evaluated a commanding within the binding context of a # test file, register all the rules it discovered. Pry.hooks.add_hook(:after_eval, "inspec_after_eval") do @runner.load @runner.run_tests unless @runner.all_rules.empty? end # Don't print out control class inspection when the user uses DSL methods. # Instead produce a result of evaluating their control. Pry.config.print = proc do |_output_, value, pry| next unless @runner.all_rules.empty? pry.pager.open do |pager| pager.print pry.config.output_prefix Pry::ColorPrinter.pp(value, pager, Pry::Output.new(pry).width - 1) end end end |
#help(topic = nil) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/inspec/shell.rb', line 110 def help(topic = nil) if topic.nil? puts <<~EOF Available commands: `[resource]` - run resource on target machine `help resources` - show all available resources that can be used as commands `help [resource]` - information about a specific resource `help matchers` - show information about common matchers `exit` - exit the InSpec shell You can use resources in this environment to test the target machine. For example: command('uname -a').stdout file('/proc/cpuinfo').content => "value" #{print_target_info} EOF elsif topic == "resources" require "inspec/resources" resources.sort.each do |resource| puts " - #{resource}" end elsif topic == "matchers" print_matchers_help elsif !Inspec::Resource.registry[topic].nil? # TODO: fix unnecessary logic topic_info = Inspec::Resource.registry[topic] info = "#{mark "Name:"} #{topic}\n\n" unless topic_info.desc.nil? info += "#{mark "Description:"}\n\n" info += "#{topic_info.desc}\n\n" end unless topic_info.example.nil? info += "#{mark "Example:"}\n\n" info += "#{topic_info.example}\n\n" end info += "#{mark "Web Reference:"}\n\n" info += "https://docs.chef.io/inspec/resources/#{topic}\n\n" puts info else begin require "inspec/resources/#{topic}" help topic rescue LoadError # TODO: stderr! puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources" end end end |
#intro ⇒ Object
95 96 97 98 99 |
# File 'lib/inspec/shell.rb', line 95 def intro puts "Welcome to the interactive InSpec Shell" puts "To find out how to use it, type: #{mark "help"}" puts end |
#mark(x) ⇒ Object
91 92 93 |
# File 'lib/inspec/shell.rb', line 91 def mark(x) "\e[1m\e[39m#{x}\e[0m" end |
#print_matchers_help ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/inspec/shell.rb', line 167 def print_matchers_help puts <<~EOL Matchers are used to compare resource values to expectations. While some resources implement their own custom matchers, the following matchers are common amongst all resources: #{mark "be"} The #{mark "be"} matcher can be used to compare numeric values. its('size') { should be >= 10 } #{mark "cmp"} The #{mark "cmp"} matcher is like #{mark "eq"} but less restrictive. It will try to fit the resource value to the expectation. "Protocol" likely returns a string, but cmp will ensure it's a number before comparing: its('Protocol') { should cmp 2 } its('Protocol') { should cmp '2' } "users" may return an array, but if it contains only one item, cmp will compare it as a string or number as needed: its('users') { should cmp 'root' } cmp is not case-sensitive: its('log_format') { should cmp 'raw' } its('log_format') { should cmp 'RAW' } #{mark "eq"} The #{mark "eq"} matcher tests for exact equality of two values. Value type (string, number, etc.) is important and must be the same. For a less-restrictive comparison matcher, use the #{mark "cmp"} matcher. its('RSAAuthentication') { should_not eq 'no' } #{mark "include"} The #{mark "include"} matcher tests to see if a value is included in a list. its('users') { should include 'my_user' } #{mark "match"} The #{mark "match"} matcher can be used to test a string for a match using a regular expression. its('content') { should_not match /^MyKey:\\s+some value/ } For more examples, see: https://docs.chef.io/inspec/matchers/ EOL end |
#print_target_info ⇒ Object
101 102 103 104 105 106 107 108 |
# File 'lib/inspec/shell.rb', line 101 def print_target_info ctx = @runner.backend puts <<~EOF You are currently running on: #{Inspec::BaseCLI.format_platform_info(params: ctx.platform.params, indent: 4, color: 39)} EOF end |
#readline_ignore(code) ⇒ Object
87 88 89 |
# File 'lib/inspec/shell.rb', line 87 def readline_ignore(code) "\001#{code}\002" end |
#resources ⇒ Object
163 164 165 |
# File 'lib/inspec/shell.rb', line 163 def resources Inspec::Resource.registry.keys end |
#start ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/inspec/shell.rb', line 15 def start ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec" # This will hold a single evaluation binding context as opened within # the instance_eval context of the anonymous class that the profile # context creates to evaluate each individual test file. We want to # pretend like we are constantly appending to the same file and want # to capture the local variable context from inside said class. @ctx_binding = @runner.eval_with_virtual_profile("binding") configure_pry @ctx_binding.pry rescue ChefLicensing::SoftwareNotEntitled Inspec::Log.error "License is not entitled to use InSpec." Inspec::UI.new.exit(:license_not_entitled) rescue ChefLicensing::Error => e Inspec::Log.error e. Inspec::UI.new.exit(:usage_error) end |