class OctocatalogDiff::CatalogUtil::Command

Used to construct the command to run 'puppet' to construct the catalog.

Public Class Methods

new(options = {}, logger = nil) click to toggle source

Constructor

# File lib/octocatalog-diff/catalog-util/command.rb, line 12
def initialize(options = {}, logger = nil)
  @options = options
  @logger = logger

  # Required parameters
  @compilation_dir = options[:compilation_dir]
  raise ArgumentError, 'Compile dir (:compilation_dir) must be a string' unless @compilation_dir.is_a?(String)
  raise Errno::ENOENT, "Compile dir #{@compilation_dir} doesn't exist" unless File.exist?(@compilation_dir)
  raise ArgumentError, "Compile dir #{@compilation_dir} not a directory" unless File.directory?(@compilation_dir)

  @node = options[:node]
  raise ArgumentError, 'Node must be specified to compile catalog' if @node.nil? || !@node.is_a?(String)

  # To be initialized on-demand
  @puppet_argv = nil
  @puppet_binary = nil
end

Public Instance Methods

puppet_argv() click to toggle source

Retrieve puppet_command, puppet_binary, puppet_argv

# File lib/octocatalog-diff/catalog-util/command.rb, line 31
def puppet_argv
  setup
  @puppet_argv
end
puppet_binary() click to toggle source
# File lib/octocatalog-diff/catalog-util/command.rb, line 36
def puppet_binary
  setup
  @puppet_binary
end
puppet_command() click to toggle source
# File lib/octocatalog-diff/catalog-util/command.rb, line 41
def puppet_command
  setup
  [@puppet_binary, @puppet_argv].flatten.join(' ')
end

Private Instance Methods

key_position(cmdline, key) click to toggle source

Private: Determine if the key (given by –key) is already defined in the command line. Returns nil if it is not already defined, otherwise returns the index. @param cmdline [Array] Existing command line @param key [String] Key to look up @return [Integer] Index of where key is defined (nil if undefined)

# File lib/octocatalog-diff/catalog-util/command.rb, line 194
def key_position(cmdline, key)
  cmdline.index { |x| x == "--#{key}" || x =~ /\A--#{key}=/ }
end
override_and_append_commandline_with_user_supplied_arguments(cmdline) click to toggle source

Private: Mutate the command line with arguments that were passed directly from the user. This appends new arguments and overwrites existing arguments. @param cmdline [Array] Existing command line - mutated by this method

# File lib/octocatalog-diff/catalog-util/command.rb, line 153
def override_and_append_commandline_with_user_supplied_arguments(cmdline)
  return unless @options[:command_line].is_a?(Array)

  @options[:command_line].each do |opt|
    # Validate format: Accept '--key=value' or '--key' only.
    unless opt =~ /\A--([^=\s]+)(=.+)?\z/
      raise ArgumentError, "Command line option '#{opt}' does not match format '--SOME_OPTION=SOME_VALUE'"
    end
    key = Regexp.last_match(1)
    val = Regexp.last_match(2)

    # The key should not contain any shell metacharacters. Ensure that this is the case.
    unless key == Shellwords.escape(key)
      raise ArgumentError, "Command line option '#{key}' is invalid."
    end

    # If val is nil, then it's a '--key' argument. Else, it's a '--key=value' argument. Escape
    # the value to ensure it do not break the shell interpretation.
    new_setting = if val.nil?
      "--#{key}"
    else
      "--#{key}=#{Shellwords.escape(val.sub(/\A=/, ''))}"
    end

    # Determine if command line already contains this setting. If yes, the setting provided
    # here should override. If no, then append to the commandline.
    ind = key_position(cmdline, key)
    if ind.nil?
      cmdline << new_setting
    else
      cmdline[ind] = new_setting
    end
  end
end
setup() click to toggle source

Build up the command line to run Puppet

# File lib/octocatalog-diff/catalog-util/command.rb, line 49
def setup
  return if @puppet_binary && @puppet_argv

  # Where is the puppet binary?
  @puppet_binary = @options[:puppet_binary]
  raise ArgumentError, 'Puppet binary was not supplied' if @puppet_binary.nil?
  raise Errno::ENOENT, "Puppet binary #{@puppet_binary} doesn't exist" unless File.file?(@puppet_binary)

  puppet_version = Gem::Version.new(@options[:puppet_version])

  # Node to compile
  cmdline = []
  # The 'puppet master --compile' command was removed in Puppet 6.x and replaced in
  # Puppet 6.5 with an identically functioning 'puppet catalog compile' command.
  # From versions 6.0.0 until 6.5.0 there is no compatible invocation method.
  if puppet_version < Gem::Version.new('6.0.0')
    cmdline.concat ['master', '--compile', Shellwords.escape(@node)]
  elsif puppet_version < Gem::Version.new('6.5.0')
    raise OctocatalogDiff::Errors::PuppetVersionError,
          'Octocatalog-diff does not support Puppet versions >= 6.0.0 and < 6.5.0'
  else
    cmdline.concat ['catalog', 'compile', Shellwords.escape(@node)]
  end

  # storeconfigs?
  if @options[:storeconfigs]
    cmdline.concat %w(--storeconfigs --storeconfigs_backend=puppetdb)
  else
    cmdline << '--no-storeconfigs'
  end

  # enc?
  if @options[:enc]
    raise Errno::ENOENT, "Did not find ENC as expected at #{@options[:enc]}" unless File.file?(@options[:enc])
    cmdline << '--node_terminus=exec'
    cmdline << "--external_nodes=#{Shellwords.escape(@options[:enc])}"
  end

  # Future parser?
  cmdline << '--parser=future' if @options[:parser] == :future

  # Path to facts, or a specific fact file?
  facts_terminus = @options.fetch(:facts_terminus, 'yaml')
  if facts_terminus == 'yaml'
    cmdline << "--factpath=#{Shellwords.escape(File.join(@compilation_dir, 'var', 'yaml', 'facts'))}"
    if @options[:fact_file].is_a?(String) && @options[:fact_file] =~ /.*\.(\w+)$/
      fact_file = File.join(@compilation_dir, 'var', 'yaml', 'facts', "#{@node}.#{Regexp.last_match(1)}")
      FileUtils.cp @options[:fact_file], fact_file unless File.file?(fact_file) || @options[:fact_file] == fact_file
    end
    cmdline << '--facts_terminus=yaml'
  elsif facts_terminus == 'facter'
    cmdline << '--facts_terminus=facter'
  else
    raise ArgumentError, "Unrecognized facts_terminus setting: '#{facts_terminus}'"
  end

  # Some typical options for puppet
  cmdline.concat %w(
    --no-daemonize
    --color=false
  )

  if puppet_version < Gem::Version.new('6.0.0')
    # This config_version parameter causes an error when run with Puppet 6.x. Per
    # the Puppet configuration settings docs, the below config_version argument
    # may not actually be valid, but for backward compatibility's sake we'll keep it
    # for the versions it has always worked with:
    cmdline.concat ['--config_version="/bin/echo catalogscript"']

    # The 'ca' configuration option was removed in Puppet 6, but we'll keep it
    # for older versions:
    cmdline.concat ['--no-ca']
  end

  # Add environment - only make this variable if preserve_environments is used.
  # If preserve_environments is not used, the hard-coded 'production' here matches
  # up with the symlink created under the temporary directory structure.
  environ = @options.fetch(:environment, 'production')
  cmdline << "--environment=#{Shellwords.escape(environ)}"

  # For people who aren't running hiera, a hiera-config will not be generated when @options[:hiera_config]
  # is nil. For everyone else, the hiera config was generated/copied/munged in the 'builddir' class
  # and was installed into the compile directory and named hiera.yaml.
  unless @options[:hiera_config].nil?
    cmdline << "--hiera_config=#{Shellwords.escape(File.join(@compilation_dir, 'hiera.yaml'))}"
  end

  # Options with parameters
  cmdline << "--environmentpath=#{Shellwords.escape(File.join(@compilation_dir, 'environments'))}"
  cmdline << "--vardir=#{Shellwords.escape(File.join(@compilation_dir, 'var'))}"
  cmdline << "--logdir=#{Shellwords.escape(File.join(@compilation_dir, 'var'))}"
  cmdline << "--ssldir=#{Shellwords.escape(File.join(@compilation_dir, 'var', 'ssl'))}"
  cmdline << "--confdir=#{Shellwords.escape(@compilation_dir)}"

  # Other parameters provided by the user
  override_and_append_commandline_with_user_supplied_arguments(cmdline)

  # Return full command
  @puppet_argv = cmdline
end