Class: Beaker::Options::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/beaker/options/parser.rb

Overview

An Object that parses, merges and normalizes all supported Beaker options and arguments

Constant Summary collapse

GITREPO =
'git://github.com/puppetlabs'
LONG_OPTS =

These options can have the form of arg1,arg2 or [arg] or just arg, should default to []

[:helper, :load_path, :tests, :pre_suite, :post_suite, :install, :modules]
RB_FILE_OPTS =

These options expand out into an array of .rb files

[:tests, :pre_suite, :post_suite]
PARSE_ERROR =
if RUBY_VERSION > '1.8.7'; then Psych::SyntaxError; else ArgumentError; end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeParser

Constructor for Parser



122
123
124
# File 'lib/beaker/options/parser.rb', line 122

def initialize
   @command_line_parser = Beaker::Options::CommandLineParser.new
end

Instance Attribute Details

#optionsObject

The OptionsHash of all parsed options



17
18
19
# File 'lib/beaker/options/parser.rb', line 17

def options
  @options
end

Instance Method Details

#check_yaml_file(f, msg = "") ⇒ Object

Determine is a given file exists and is a valid YAML file

Parameters:

  • f (String)

    The YAML file path to examine

  • msg (String) (defaults to: "")

    An options message to report in case of error

Raises:

  • (ArgumentError)

    Raise if file does not exist or is not valid YAML



175
176
177
178
179
180
181
182
183
184
# File 'lib/beaker/options/parser.rb', line 175

def check_yaml_file(f, msg = "")
  if not File.file?(f)
    parser_error "#{f} does not exist (#{msg})"
  end
  begin
    YAML.load_file(f)
  rescue PARSE_ERROR => e
    parser_error "#{f} is not a valid YAML file (#{msg})\n\t#{e}"
  end
end

#file_list(paths) ⇒ Array

Generates a list of files based upon a given path or list of paths.

Looks recursively for .rb files in paths.

Parameters:

  • paths (Array)

    Array of file paths to search for .rb files

Returns:

  • (Array)

    An Array of fully qualified paths to .rb files

Raises:

  • (ArgumentError)

    Raises if no .rb files are found in searched directory or if no .rb files are found overall



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/beaker/options/parser.rb', line 68

def file_list(paths)
  files = []
  if not paths.empty?
    paths.each do |root|
      if File.file? root then
        files << root
      else
        discover_files = Dir.glob(
          File.join(root, "**/*.rb")
        ).select { |f| File.file?(f) }
        if discover_files.empty?
          parser_error "empty directory used as an option (#{root})!"
        end
        files += discover_files
      end
    end
  end
  if files.empty?
    parser_error "no .rb files found in #{paths.to_s}"
  end
  files
end

#normalize_argsObject

Validate all merged options values for correctness

Currently checks:

- if a keyfile is provided then use it
- paths provided to --test, --pre-suite, --post-suite provided lists of .rb files for testing
- --type is one of 'pe' or 'git'
- --fail-mode is one of 'fast', 'stop' or nil
- if using blimpy hypervisor an EC2 YAML file exists
- if using the aix, solaris, or vcloud hypervisors a .fog file exists
- that one and only one master is defined per set of hosts
- that solaris/windows/aix hosts are agent only for PE tests OR
- that windows/aix host are agent only if type is not 'pe'

Raises:

  • (ArgumentError)

    Raise if argument/options values are invalid



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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/beaker/options/parser.rb', line 200

def normalize_args

  #use the keyfile if present
  if @options.has_key?(:keyfile)
    @options[:ssh][:keys] = [@options[:keyfile]]
  end

  #split out arguments - these arguments can have the form of arg1,arg2 or [arg] or just arg
  #will end up being normalized into an array
  LONG_OPTS.each do |opt|
    if @options.has_key?(opt)
      @options[opt] = split_arg(@options[opt])
      if RB_FILE_OPTS.include?(opt)
        @options[opt] = file_list(@options[opt])
      end
      if opt == :install
        @options[:install] = parse_git_repos(@options[:install])
      end
    else
      @options[opt] = []
    end
  end 
 
  #check for valid type
  if @options[:type] !~ /(pe)|(git)/
    parser_error "--type must be one of pe or git, not '#{@options[:type]}'"
  end

  #check for valid fail mode
  if not ["fast", "stop", nil].include?(@options[:fail_mode])
    parser_error "--fail-mode must be one of fast, stop" 
  end

  #check for config files necessary for different hypervisors
  hypervisors = [] 
  @options[:HOSTS].each_key do |name|  
    hypervisors << @options[:HOSTS][name][:hypervisor].to_s
  end
  hypervisors.uniq!
  hypervisors.each do |visor|
    if ['blimpy'].include?(visor)
      check_yaml_file(@options[:ec2_yaml], "required by #{visor}")
    end
    if ['aix', 'solaris', 'vcloud'].include?(visor)
      check_yaml_file(@options[:dot_fog], "required by #{visor}")
    end
  end

  #check that roles of hosts make sense
  # - must be one and only one master
  roles = []
  @options[:HOSTS].each_key do |name|  
    roles << @options[:HOSTS][name][:roles]
  end
  master = 0
  roles.each do |role_array|
    if role_array.include?('master')
      master += 1
    end
  end
  if master > 1 or master < 1
    parser_error "One and only one host/node may have the role 'master', fix #{@options[:hosts_file]}"
  end

  #check that solaris/windows/el-4 boxes are only agents
  @options[:HOSTS].each_key do |name|
    host = @options[:HOSTS][name]
    if (host[:platform] =~ /windows|el-4/) ||
       (@options.is_pe? && host[:platform] =~ /solaris/)

      test_host_roles(name, host)
    end
  end

end

#parse_args(args = ARGV) ⇒ Object

Parses ARGV or provided arguments array, file options, hosts options and combines with environment variables and preset defaults to generate a Hash representing the Beaker options for a given test run

Order of priority is as follows:

1.  environment variables are given top priority
2.  host file options
3.  the 'CONFIG' section of the hosts file
4.  ARGV or provided arguments array
5.  options file values
6.  default or preset values are given the lowest priority

Parameters:

  • args (Array) (defaults to: ARGV)

    ARGV or a provided arguments array

Raises:

  • (ArgumentError)

    Raises error on bad input



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/beaker/options/parser.rb', line 139

def parse_args(args = ARGV)
  #NOTE on argument precedence:
  #
  # Will use env, then hosts/config file, then command line, then file options
  # 
  @options = Beaker::Options::Presets.presets
  cmd_line_options = @command_line_parser.parse!(args)
  file_options = Beaker::Options::OptionsFileParser.parse_options_file(cmd_line_options[:options_file])
  # merge together command line and file_options
  #   overwrite file options with command line options
  cmd_line_and_file_options = file_options.merge(cmd_line_options)
  # merge command line and file options with defaults
  #   overwrite defaults with command line and file options 
  @options = @options.merge(cmd_line_and_file_options)

  if not @options[:help] and not @options[:version]
    #read the hosts file that contains the node configuration and hypervisor info
    hosts_options = Beaker::Options::HostsFileParser.parse_hosts_file(@options[:hosts_file])
    # merge in host file vars
    #   overwrite options (default, file options, command line, env) with host file options
    @options = @options.merge(hosts_options)
    # merge in env vars
    #   overwrite options (default, file options, command line, hosts file) with env
    env_vars = Beaker::Options::Presets.env_vars
    @options = @options.merge(env_vars)

    normalize_args
  end

  @options
end

#parse_git_repos(git_opts) ⇒ Array

Converts array of paths into array of fully qualified git repo URLS with expanded keywords

Supports the following keywords

PUPPET 
FACTER
HIERA 
HIERA-PUPPET

Examples:

opts = ["PUPPET/3.1"]
parse_git_repos(opts) == ["#{GITREPO}/puppet.git#3.1"]

Parameters:

  • git_opts (Array)

    An array of paths

Returns:

  • (Array)

    An array of fully qualified git repo URLs with expanded keywords



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/beaker/options/parser.rb', line 103

def parse_git_repos(git_opts)
  git_opts.map! { |opt|
    case opt
      when /^PUPPET\//
        opt = "#{GITREPO}/puppet.git##{opt.split('/', 2)[1]}"
      when /^FACTER\//
        opt = "#{GITREPO}/facter.git##{opt.split('/', 2)[1]}"
      when /^HIERA\//
        opt = "#{GITREPO}/hiera.git##{opt.split('/', 2)[1]}"
      when /^HIERA-PUPPET\//
        opt = "#{GITREPO}/hiera-puppet.git##{opt.split('/', 2)[1]}"
    end
    opt
  }
  git_opts
end

#parser_error(msg = "") ⇒ Object

Raises an ArgumentError with associated message

Parameters:

  • msg (String) (defaults to: "")

    The error message to be reported

Raises:

  • (ArgumentError)

    Takes the supplied message and raises it as an ArgumentError



22
23
24
# File 'lib/beaker/options/parser.rb', line 22

def parser_error msg = ""
  raise ArgumentError, msg.to_s
end

#repoString

Returns the git repository used for git installations

Returns:

  • (String)

    The git repository



28
29
30
# File 'lib/beaker/options/parser.rb', line 28

def repo
  GITREPO
end

#split_arg(arg) ⇒ Array

Normalizes argument into an Array. Argument can either be converted into an array of a single value, or can become an array of multiple values by splitting arg over ‘,’. If argument is already an array that array is returned untouched.

Examples:

split_arg([1, 2, 3]) == [1, 2, 3] 
split_arg(1) == [1]
split_arg("1,2") == ["1", "2"]
split_arg(nil) == []

Parameters:

  • arg (Array, String)

    Either an array or a string to be split into an array

Returns:

  • (Array)

    An array of the form arg, [arg], or arg.split(‘,’)



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/beaker/options/parser.rb', line 48

def split_arg arg
  arry = []
  if arg.is_a?(Array)
    arry += arg
  elsif arg =~ /,/
    arry += arg.split(',')
  else
    arry << arg
  end
  arry
end

#usageString

Returns a description of Beaker’s supported arguments

Returns:

  • (String)

    The usage String



34
35
36
# File 'lib/beaker/options/parser.rb', line 34

def usage
 @command_line_parser.usage
end