Skip to content

Commit

Permalink
Target Mode Ohai
Browse files Browse the repository at this point in the history
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
  • Loading branch information
lamont-granquist committed Oct 2, 2020
1 parent 6d6e6f4 commit 782ea2a
Show file tree
Hide file tree
Showing 63 changed files with 704 additions and 483 deletions.
3 changes: 3 additions & 0 deletions .expeditor/run_linux_tests.sh
Expand Up @@ -9,6 +9,9 @@ export LANG=C.UTF-8 LANGUAGE=C.UTF-8

echo "--- bundle install"

# we need the ruby 2.7 version of bundler, the 2.5/2.6 versions cannot pull our Gemfile correctly
gem update --system
gem install bundler
bundle config --local path vendor/bundle
bundle install --jobs=7 --retry=3

Expand Down
11 changes: 3 additions & 8 deletions .expeditor/verify.pipeline.yml
Expand Up @@ -26,14 +26,6 @@ steps:
docker:
image: ruby:2.7-buster

- label: run-specs-ruby-2.5
command:
- .expeditor/run_linux_tests.sh rspec
expeditor:
executor:
docker:
image: ruby:2.5-buster

- label: run-specs-ruby-2.6
command:
- .expeditor/run_linux_tests.sh rspec
Expand All @@ -52,6 +44,9 @@ steps:

- label: run-specs-windows
command:
# we need the ruby 2.7 version of bundler, the 2.5/2.6 versions cannot pull our Gemfile correctly
- gem update --system
- gem install bundler
- bundle config --local path vendor/bundle
- bundle install --jobs=7 --retry=3 --without=profile
- bundle exec rake spec
Expand Down
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -2,6 +2,10 @@ source "https://rubygems.org"

gemspec

# pull these gems from master of chef/chef so that we're testing against what we will release
gem "chef-config", git: "https://github.com/chef/chef", glob: "chef-config/chef-config.gemspec"
gem "chef-utils", git: "https://github.com/chef/chef", glob: "chef-utils/chef-utils.gemspec"

# NOTE: do not submit PRs to add pry as a dep, add to your Gemfile.local
group :development do
gem "chefstyle", git: "https://github.com/chef/chefstyle.git", branch: "master"
Expand Down
39 changes: 39 additions & 0 deletions lib/ohai/application.rb
Expand Up @@ -56,6 +56,15 @@ class Ohai::Application
description: "Set the log file location, defaults to STDOUT - recommended for daemonizing",
proc: nil

option :target,
short: "-t TARGET",
long: "--target TARGET",
description: "Target Ohai against a remote system or device",
proc: lambda { |target|
Ohai::Log.warn "-- EXPERIMENTAL -- Target mode activated -- EXPERIMENTAL --"
target
}

option :help,
short: "-h",
long: "--help",
Expand Down Expand Up @@ -93,9 +102,39 @@ def configure_ohai

load_workstation_config

merge_configs

if config[:target]
Ohai::Config.target_mode.host = config[:target]
if URI.parse(Ohai::Config.target_mode.host).scheme
train_config = Train.unpack_target_from_uri(Ohai::Config.target_mode.host)
Ohai::Config.target_mode = train_config
end
Ohai::Config.target_mode.enabled = true
Ohai::Config.node_name = Ohai::Config.target_mode.host unless Ohai::Config.node_name
end

Ohai::Log.init(Ohai.config[:log_location])
end

# @api private
def config_file_defaults
Ohai::Config.save(true)
end

# @api private
def config_file_settings
Ohai::Config.save(false)
end

# See lib/chef/knife.rb in the chef/chef github repo
#
# @api private
def merge_configs
config.replace(config_file_defaults.merge(default_config).merge(config_file_settings).merge(config))
Ohai::Config.merge!(config) # make them both the same
end

# Passes config and attributes arguments to Ohai::System then prints the results.
# Called by the run method after config / logging have been initialized
#
Expand Down
7 changes: 7 additions & 0 deletions lib/ohai/dsl/plugin.rb
Expand Up @@ -22,6 +22,7 @@
require_relative "../mixin/shell_out"
require_relative "../mixin/seconds_to_human"
require_relative "../mixin/which"
require_relative "../mixin/train_helpers"
require_relative "../hints"

module Ohai
Expand Down Expand Up @@ -86,10 +87,12 @@ class Plugin
include Ohai::Mixin::ShellOut
include Ohai::Mixin::SecondsToHuman
include Ohai::Mixin::Which
include Ohai::Mixin::TrainHelpers

attr_reader :data
attr_reader :failed
attr_reader :logger
attr_accessor :transport_connection

def initialize(data, logger)
@data = data
Expand All @@ -98,6 +101,10 @@ def initialize(data, logger)
@failed = false
end

def target_mode?
!!@transport_connection
end

def run
@has_run = true

Expand Down
42 changes: 27 additions & 15 deletions lib/ohai/dsl/plugin/versionvii.rb
Expand Up @@ -46,7 +46,8 @@ def self.version
:version7
end

# the source of the plugin on disk. This is an array since a plugin may exist for multiple platforms and this would include each of those platform specific file paths
# The source of the plugin on disk. This is an array since a plugin may exist for multiple
# oses and this would include each of those os specific file paths
#
# @return [Array]
def self.sources
Expand All @@ -61,7 +62,7 @@ def self.depends_attrs
@depends_attrs ||= []
end

# A block per platform for actually performing data collection constructed
# A block per os for actually performing data collection constructed
# by the collect_data method
#
# @return [Mash]
Expand Down Expand Up @@ -101,15 +102,16 @@ def self.optional?
!!@optional
end

# define data collection methodology per platform
# define data collection methodology per os
#
# @param platform [Symbol] the platform to collect data for
# @param other_platforms [Array] additional platforms to collect data for
# @param block [block] the actual code to collect data for the specified platforms
def self.collect_data(platform = :default, *other_platforms, &block)
[platform, other_platforms].flatten.each do |plat|
Ohai::Log.warn("collect_data already defined on platform '#{plat}' for #{self}, last plugin seen will be used") if data_collector.key?(plat)
data_collector[plat] = block
# @param os [Array<Symbol>] the list of oses to collect data for
# @param block [block] the actual code to collect data for the specified os
#
def self.collect_data(*os_list, &block)
os_list = [ :default ] if os_list.empty?
os_list.flatten.each do |os|
Ohai::Log.warn("collect_data already defined on os '#{os}' for #{self}, last plugin seen will be used") if data_collector.key?(os)
data_collector[os] = block
end
end

Expand All @@ -120,12 +122,22 @@ def dependencies

def run_plugin
collector = self.class.data_collector
platform = collect_os

if collector.key?(platform)
instance_eval(&collector[platform])
elsif collector.key?(:default)
os = collect_os

# :default - means any remote or local unix or windows host
# :target - means any remote API which is not unix/windows or otherwise rubyable (cisco switches, IPMI console, HTTP API, etc)
#
# Do not be confused by the fact that collectors tagged :target do not run against e.g. target-mode ubuntu boxes, that is not
# what :target is intended for. Also, do not be confused by the fact that collectors tagged :default do not run by default against
# pure-target mode targets like switches. That is all intended behavior, the names are problematic. The :default nomenclature was
# invented 10 years before target mode and we are stuck with it.
#
if collector.key?(os)
instance_eval(&collector[os])
elsif collector.key?(:default) && !nonruby_target?
instance_eval(&collector[:default])
elsif collector.key?(:target) && nonruby_target?
instance_eval(&collector[:target])
else
logger.trace("Plugin #{name}: No data to collect. Skipping...")
end
Expand Down
14 changes: 13 additions & 1 deletion lib/ohai/mixin/chef_utils_wiring.rb
Expand Up @@ -16,6 +16,7 @@
#

require_relative "../config"
require "singleton" unless defined?(Singleton)

module Ohai
module Mixin
Expand All @@ -32,7 +33,18 @@ def __log
end

def __transport_connection
# Chef.run_context&.transport_connection
transport_connection
end

# beause of target mode we cache the PATH to avoid massive amounts of `echo $PATH` remote queries
#
def __env_path
PathCache.instance.path_cache ||= super
end

class PathCache
include Singleton
attr_accessor :path_cache
end
end
end
Expand Down
67 changes: 66 additions & 1 deletion lib/ohai/mixin/os.rb
Expand Up @@ -27,6 +27,71 @@ module OS
#
# @return [String] the OS
def collect_os
if target_mode?
collect_os_target
else
collect_os_local
end
end

# This should exactly preserve the semantics of collect_os_local below, which is authoritative
# for the API and must adhere to pre-existing ohai semantics and not follow inspec's notion of
# os/family/hierarchy.
#
# Right or wrong the ohai `os` variable has matched the ruby `host_os` definition for the past
# 10+ years, preceding inspec/train's definitions and this is the documented correct API of
# these methods. Mismatches between the ruby notion and the train version will be fixed as
# bugfixes in these methods and may not be considered semver violating even though they make
# break downstream consumers. Please ensure that both methods produce the same results if
# you are on a platform which supports running ruby (train is considered authoritative for
# any "OS" which cannot run ruby -- server consoles, REST APIs, etc...)
#
# @api private
def collect_os_target
case
when transport_connection.os.aix?
"aix"
when transport_connection.os.darwin?
"darwin"
when transport_connection.os.linux?
"linux"
when transport_connection.os.family == "freebsd"
"freebsd"
when transport_connection.os.family == "openbsd"
"openbsd"
when transport_connection.os.family == "netbsd"
"netbsd"
when transport_connection.os.family == "dragonflybsd"
"dragonflybsd"
when transport_connection.os.solaris?
"solaris2"
when transport_connection.os.windows?
"windows"

#
# The purpose of the next two lines is that anything which runs Unix is presumed to be able to run ruby, and
# if it was not caught above, we MUST translate whatever train uses as the 'os' into the proper ruby host_os
# string. If it is not unix and not caught above we assume it is something like a REST API which cannot run
# ruby. If these assumptions are incorrect then it is a bug, which should be submitted to fix it, and the
# values should not be relied upon until that bug is fixed. The train os is NEVER considered authoritataive
# for any target which can run ruby.
#
when transport_connection.os.unix?
raise "Target mode unsupported on this Unix-like host, please update the collect_os_target case statement with the correct ruby host_os value."
else
# now we have something like an IPMI console that isn't Unix-like or Windows, presumably cannot run ruby, and
# so we just trust the train O/S information.
transport_connection.os
end
end

# @api private
def nonruby_target?
transport_connection && !transport_connection.os.unix? && !transport_connection.os.windows
end

# @api private
def collect_os_local
case ::RbConfig::CONFIG["host_os"]
when /aix(.+)$/
"aix"
Expand Down Expand Up @@ -56,7 +121,7 @@ def collect_os
end
end

module_function :collect_os
extend self
end
end
end
35 changes: 35 additions & 0 deletions lib/ohai/mixin/train_helpers.rb
@@ -0,0 +1,35 @@
#
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "chef-utils/dsl/train_helpers" unless defined?(ChefUtils::DSL::TrainHelpers)
require_relative "chef_utils_wiring" unless defined?(Ohai::Mixin::ChefUtilsWiring)

module Ohai
module Mixin
module TrainHelpers
include ChefUtils::DSL::TrainHelpers
include ChefUtilsWiring

# anything added to this file temporarily should be pushed back up
# into ChefUtils::DSL::TrainHelpers

# XXX: this needs better support directly in train
def dir_glob(path)
shell_out!("ls -d #{path}").stdout.split
end
end
end
end
6 changes: 3 additions & 3 deletions lib/ohai/plugins/azure.rb
Expand Up @@ -47,16 +47,16 @@
# check for either the waagent or the unknown-245 DHCP option that Azure uses
# http://blog.mszcool.com/index.php/2015/04/detecting-if-a-virtual-machine-runs-in-microsoft-azure-linux-windows-to-protect-your-software-when-distributed-via-the-azure-marketplace/
def has_waagent?
if File.exist?("/usr/sbin/waagent") || Dir.exist?('C:\WindowsAzure')
if file_exist?("/usr/sbin/waagent") || dir_exist?('C:\WindowsAzure')
logger.trace("Plugin Azure: Found waagent used by Azure.")
true
end
end

def has_dhcp_option_245?
has_245 = false
if File.exist?("/var/lib/dhcp/dhclient.eth0.leases")
File.open("/var/lib/dhcp/dhclient.eth0.leases").each do |line|
if file_exist?("/var/lib/dhcp/dhclient.eth0.leases")
file_open("/var/lib/dhcp/dhclient.eth0.leases").each do |line|
if /unknown-245/.match?(line)
logger.trace("Plugin Azure: Found unknown-245 DHCP option used by Azure.")
has_245 = true
Expand Down
2 changes: 1 addition & 1 deletion lib/ohai/plugins/bsd/virtualization.rb
Expand Up @@ -66,7 +66,7 @@
end

# Detect bhyve by presence of /dev/vmm
if File.exist?("/dev/vmm")
if file_exist?("/dev/vmm")
virtualization[:system] = "bhyve"
virtualization[:role] = "host"
virtualization[:systems][:bhyve] = "host"
Expand Down
2 changes: 1 addition & 1 deletion lib/ohai/plugins/chef.rb
Expand Up @@ -19,7 +19,7 @@
Ohai.plugin(:Chef) do
provides "chef_packages/chef"

collect_data do
collect_data(:default, :target) do
begin
require "chef/version"
rescue Gem::LoadError
Expand Down

0 comments on commit 782ea2a

Please sign in to comment.