Class: Puppet::Application::FaceBase

Inherits:
Puppet::Application show all
Defined in:
lib/puppet/application/face_base.rb

Direct Known Subclasses

Config, Epp, Generate, Help, IndirectionBase, Module, Parser, Plugin

Constant Summary

Constants inherited from Puppet::Application

DOCPATTERN

Constants included from Util

Util::ALNUM, Util::ALPHA, Util::AbsolutePathPosix, Util::AbsolutePathWindows, Util::DEFAULT_POSIX_MODE, Util::DEFAULT_WINDOWS_MODE, Util::ESCAPED, Util::HEX, Util::HttpProxy, Util::PUPPET_STACK_INSERTION_FRAME, Util::RESERVED, Util::RFC_3986_URI_REGEX, Util::UNRESERVED, Util::UNSAFE

Constants included from Util::POSIX

Util::POSIX::LOCALE_ENV_VARS, Util::POSIX::USER_ENV_VARS

Constants included from Util::SymbolicFileMode

Util::SymbolicFileMode::SetGIDBit, Util::SymbolicFileMode::SetUIDBit, Util::SymbolicFileMode::StickyBit, Util::SymbolicFileMode::SymbolicMode, Util::SymbolicFileMode::SymbolicSpecialToBit

Instance Attribute Summary collapse

Attributes inherited from Puppet::Application

#command_line, #options

Instance Method Summary collapse

Methods inherited from Puppet::Application

[], #app_defaults, available_application_names, banner, clear!, clear?, clear_everything_for_tests, #configure_indirector_routes, controlled_run, #deprecate, #deprecated?, environment_mode, exit, find, get_environment_mode, #handle_logdest_arg, #handlearg, #help, #initialize, #initialize_app_defaults, interrupted?, #log_runtime_environment, #name, option, option_parser_commands, restart!, restart_requested?, #run, #run_command, run_mode, #set_log_level, #setup_logs, stop!, stop_requested?, #summary, try_load_class

Methods included from Util

absolute_path?, benchmark, chuser, clear_environment, create_erb, default_env, deterministic_rand, deterministic_rand_int, exit_on_fail, format_backtrace_array, format_puppetstack_frame, get_env, get_environment, logmethods, merge_environment, path_to_uri, pretty_backtrace, replace_file, resolve_stackframe, rfc2396_escape, safe_posix_fork, set_env, skip_external_facts, symbolizehash, thinmark, uri_encode, uri_query_encode, uri_to_path, uri_unescape, which, withenv, withumask

Methods included from Util::POSIX

#get_posix_field, #gid, groups_of, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Methods included from Util::SymbolicFileMode

#display_mode, #normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Constructor Details

This class inherits a constructor from Puppet::Application

Instance Attribute Details

#actionObject



32
33
34
# File 'lib/puppet/application/face_base.rb', line 32

def action
  @action
end

#argumentsObject



32
33
34
# File 'lib/puppet/application/face_base.rb', line 32

def arguments
  @arguments
end

#faceObject



32
33
34
# File 'lib/puppet/application/face_base.rb', line 32

def face
  @face
end

#render_asObject



31
32
33
# File 'lib/puppet/application/face_base.rb', line 31

def render_as
  @render_as
end

#typeObject



32
33
34
# File 'lib/puppet/application/face_base.rb', line 32

def type
  @type
end

Instance Method Details

#find_application_argument(item) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/puppet/application/face_base.rb', line 170

def find_application_argument(item)
  self.class.option_parser_commands.each do |options, _function|
    options.each do |option|
      next unless option =~ /^-/

      pattern = /^#{option.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/
      next unless pattern.match(item)

      return {
        :argument => option =~ /[ =]/,
        :optional => option =~ /[ =]\[/
      }
    end
  end
  return nil                  # not found
end

#find_global_settings_argument(item) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/puppet/application/face_base.rb', line 157

def find_global_settings_argument(item)
  Puppet.settings.each do |_name, object|
    object.optparse_args.each do |arg|
      next unless arg =~ /^-/

      # sadly, we have to emulate some of optparse here...
      pattern = /^#{arg.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/
      pattern.match item and return object
    end
  end
  return nil                  # nothing found.
end

#mainObject



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
275
276
# File 'lib/puppet/application/face_base.rb', line 211

def main
  status = false

  # Call the method associated with the provided action (e.g., 'find').
  unless @action
    puts Puppet::Face[:help, :current].help(@face.name)
    raise _("%{face} does not respond to action %{arg}") % { face: face, arg: arguments.first }
  end

  # We need to do arity checking here because this is generic code
  # calling generic methods – that have argument defaulting.  We need to
  # make sure we don't accidentally pass the options as the first
  # argument to a method that takes one argument.  eg:
  #
  #   puppet facts find
  #   => options => {}
  #      @arguments => [{}]
  #   => @face.send :bar, {}
  #
  #   def face.bar(argument, options = {})
  #   => bar({}, {})  # oops!  we thought the options were the
  #                   # positional argument!!
  #
  # We could also fix this by making it mandatory to pass the options on
  # every call, but that would make the Ruby API much more annoying to
  # work with; having the defaulting is a much nicer convention to have.
  #
  # We could also pass the arguments implicitly, by having a magic
  # 'options' method that was visible in the scope of the action, which
  # returned the right stuff.
  #
  # That sounds attractive, but adds complications to all sorts of
  # things, especially when you think about how to pass options when you
  # are writing Ruby code that calls multiple faces.  Especially if
  # faces are involved in that. ;)
  #
  # --daniel 2011-04-27
  if (arity = @action.positional_arg_count) > 0
    unless (count = arguments.length) == arity then
      raise ArgumentError, n_("puppet %{face} %{action} takes %{arg_count} argument, but you gave %{given_count}", "puppet %{face} %{action} takes %{arg_count} arguments, but you gave %{given_count}", arity - 1) % { face: @face.name, action: @action.name, arg_count: arity - 1, given_count: count - 1 }
    end
  end

  if @face.deprecated?
    Puppet.deprecation_warning(_("'puppet %{face}' is deprecated and will be removed in a future release") % { face: @face.name })
  end

  result = @face.send(@action.name, *arguments)
  puts render(result, arguments) unless result.nil?
  status = true

# We need an easy way for the action to set a specific exit code, so we
# rescue SystemExit here; This allows each action to set the desired exit
# code by simply calling Kernel::exit.  eg:
#
#   exit(2)
#
# --kelsey 2012-02-14
rescue SystemExit => detail
  status = detail.status
rescue => detail
  Puppet.log_exception(detail)
  Puppet.err _("Try 'puppet help %{face} %{action}' for usage") % { face: @face.name, action: @action.name }
ensure
  exit status
end

#parse_optionsObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
# File 'lib/puppet/application/face_base.rb', line 63

def parse_options
  # We need to parse enough of the command line out early, to identify what
  # the action is, so that we can obtain the full set of options to parse.

  # REVISIT: These should be configurable versions, through a global
  # '--version' option, but we don't implement that yet... --daniel 2011-03-29
  @type = Puppet::Util::ConstantInflector.constant2file(self.class.name.to_s.sub(/.+:/, '')).to_sym
  @face = Puppet::Face[@type, :current]

  # Now, walk the command line and identify the action.  We skip over
  # arguments based on introspecting the action and all, and find the first
  # non-option word to use as the action.
  action_name = nil
  index       = -1
  until action_name or (index += 1) >= command_line.args.length do
    item = command_line.args[index]
    if item =~ /^-/
      option = @face.options.find do |name|
        item =~ /^-+#{name.to_s.gsub(/[-_]/, '[-_]')}(?:[ =].*)?$/
      end
      if option
        option = @face.get_option(option)
        # If we have an inline argument, just carry on.  We don't need to
        # care about optional vs mandatory in that case because we do a real
        # parse later, and that will totally take care of raising the error
        # when we get there. --daniel 2011-04-04
        if option.takes_argument? and !item.index('=')
          index += 1 unless
            (option.optional_argument? and command_line.args[index + 1] =~ /^-/)
        end
      else
        option = find_global_settings_argument(item)
        if option
          unless Puppet.settings.boolean? option.name
            # As far as I can tell, we treat non-bool options as always having
            # a mandatory argument. --daniel 2011-04-05
            # ... But, the mandatory argument will not be the next item if an = is
            # employed in the long form of the option. --jeffmccune 2012-09-18
            index += 1 unless item =~ /^--#{option.name}=/
          end
        else
          option = find_application_argument(item)
          if option
            index += 1 if (option[:argument] and not option[:optional])
          else
            raise OptionParser::InvalidOption.new(item.sub(/=.*$/, ''))
          end
        end
      end
    else
      # Stash away the requested action name for later, and try to fetch the
      # action object it represents; if this is an invalid action name that
      # will be nil, and handled later.
      action_name = item.to_sym
      @action = Puppet::Face.find_action(@face.name, action_name)
      @face   = @action.face if @action
    end
  end

  if @action.nil?
    @action = @face.get_default_action()
    if @action
      @is_default_action = true
    else
      # First try to handle global command line options
      # But ignoring invalid options as this is a invalid action, and
      # we want the error message for that instead.
      begin
        super
      rescue OptionParser::InvalidOption
      end

      face   = @face.name
      action = action_name.nil? ? 'default' : "'#{action_name}'"
      msg = _("'%{face}' has no %{action} action.  See `puppet help %{face}`.") % { face: face, action: action }

      Puppet.err(msg)
      Puppet::Util::Log.force_flushqueue()

      exit false
    end
  end

  # Now we can interact with the default option code to build behaviour
  # around the full set of options we now know we support.
  @action.options.each do |o|
    o = @action.get_option(o) # make it the object.
    self.class.option(*o.optparse) # ...and make the CLI parse it.
  end

  # ...and invoke our parent to parse all the command line options.
  super
end

#preinitObject



55
56
57
58
59
60
61
# File 'lib/puppet/application/face_base.rb', line 55

def preinit
  super
  Signal.trap(:INT) do
    $stderr.puts _("Cancelling Face")
    exit(0)
  end
end

#render(result, args_and_options) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/puppet/application/face_base.rb', line 39

def render(result, args_and_options)
  hook = action.when_rendering(render_as.name)

  if hook
    # when defining when_rendering on your action you can optionally
    # include arguments and options
    if hook.arity > 1
      result = hook.call(result, *args_and_options)
    else
      result = hook.call(result)
    end
  end

  render_as.render(result)
end

#setupObject



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/puppet/application/face_base.rb', line 187

def setup
  Puppet::Util::Log.newdestination :console

  @arguments = command_line.args

  # Note: because of our definition of where the action is set, we end up
  # with it *always* being the first word of the remaining set of command
  # line arguments.  So, strip that off when we construct the arguments to
  # pass down to the face action. --daniel 2011-04-04
  # Of course, now that we have default actions, we should leave the
  # "action" name on if we didn't actually consume it when we found our
  # action.
  @arguments.delete_at(0) unless @is_default_action

  # We copy all of the app options to the end of the call; This allows each
  # action to read in the options.  This replaces the older model where we
  # would invoke the action with options set as global state in the
  # interface object.  --daniel 2011-03-28
  @arguments << options

  # If we don't have a rendering format, set one early.
  self.render_as ||= (@action.render_as || :console)
end