Class: Puppet::Application::Debugger

Inherits:
Puppet::Application
  • Object
show all
Defined in:
lib/puppet/application/debugger.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command_line = Puppet::Util::CommandLine.new) ⇒ Debugger

Returns a new instance of Debugger.



176
177
178
179
180
181
182
183
184
185
# File 'lib/puppet/application/debugger.rb', line 176

def initialize(command_line = Puppet::Util::CommandLine.new)
  @command_line = CommandLineArgs.new(command_line.subcommand_name, command_line.args.dup)
  @options = { use_facterdb: true, play: nil, run_once: false, node_name: nil, quiet: false, help: false, scope: nil }
  @use_stdin = false
  begin
    require "puppet-debugger"
  rescue LoadError => e
    Puppet.err("You must install the puppet-debugger: gem install puppet-debugger")
  end
end

Instance Attribute Details

#use_stdinObject (readonly)

Returns the value of attribute use_stdin.



8
9
10
# File 'lib/puppet/application/debugger.rb', line 8

def use_stdin
  @use_stdin
end

Instance Method Details

#create_environment(manifest) ⇒ Object



228
229
230
231
232
233
# File 'lib/puppet/application/debugger.rb', line 228

def create_environment(manifest)
  configured_environment = Puppet.lookup(:current_environment)
  manifest ?
    configured_environment.override_with(manifest: manifest) :
    configured_environment
end

#create_node(environment) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/puppet/application/debugger.rb', line 235

def create_node(environment)
  node = nil
  unless Puppet[:node_name_fact].empty?
    # Collect our facts.
    unless facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value])
      raise "Could not find facts for #{Puppet[:node_name_value]}"
    end
    Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]
    facts.name = Puppet[:node_name_value]
  end
  Puppet.override({ current_environment: environment }, "For puppet debugger") do
    # Find our Node
    unless node = Puppet::Node.indirection.find(Puppet[:node_name_value])
      raise "Could not find node #{Puppet[:node_name_value]}"
    end
    # Merge in the facts.
    node.merge(facts.values) if facts
  end
  node
end

#create_scope(node) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
# File 'lib/puppet/application/debugger.rb', line 256

def create_scope(node)
  compiler = Puppet::Parser::Compiler.new(node) # creates a new compiler for each scope
  scope = Puppet::Parser::Scope.new(compiler)
  # creates a node class
  scope.source = Puppet::Resource::Type.new(:node, node.name)
  scope.parent = compiler.topscope
  # compiling will load all the facts into the scope
  # without this step facts will not get resolved
  scope.compiler.compile # this will load everything into the scope
  scope
end

#helpObject



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
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/puppet/application/debugger.rb', line 46

def help
  <<-HELP

puppet-debugger(8) -- Starts a debugger session using the puppet-debugger tool
========

SYNOPSIS
--------
A interactive command line tool for evaluating the puppet language and debugging
puppet code.

USAGE
-----
puppet debugger [--help] [--version] [-e|--execute CODE] [--facterdb-filter FILTER]
              [--test] [--no-facterdb] [-q|--quiet] [-p|--play URL] [-s|--stdin]
              [-r|--run-once] [-n|--node-name CERTNAME]


DESCRIPTION
-----------
A interactive command line tool for evaluating the puppet language and debugging
puppet code.

USAGE WITH DEBUG MODULE
-----------------------
Use the puppet debugger in conjunction with the debug::break() puppet function
to pry into your code during compilation.  Get immediate insight in how the puppet4
languge works during the execution of your code.

To use the break function install the module via: puppet module install nwops/debug

Now place the debug::break() function anywhere in your code to

Example:
puppet debugger -e '$abs_vars = [-11,-22,-33].map | Integer $num | { debug::break() ; notice($num) }'

See: https://github.com/nwops/puppet-debug
OPTIONS
-------
Note that any setting that's valid in the configuration
file is also a valid long argument. For example, 'server' is a valid
setting, so you can specify '--server <servername>' as
an argument.

See the configuration file documentation at
http://docs.puppetlabs.com/references/stable/configuration.html for the
full list of acceptable parameters. A commented list of all
configuration options can also be generated by running puppet debugger with
'--genconfig'.

* --help:
Print this help message

* --version:
Print the puppet version number and exit.

* --execute:
Execute a specific piece of Puppet code

* --facterdb-filter
Disables the usage of the current node level facts and uses cached facts
from facterdb.  Specifying a filter will override the default facterdb filter.
Not specifiying a filter will use the default CentOS based filter.
This will greatly speed up the start time of the debugger since
you are using cached facts.  Additionally, using facterdb also allows you
to play with many other operating system facts that you might not have access
to.  For example filters please see the facterdb docs.

See https://github.com/camptocamp/facterdb for more info

* --no-facterdb
Use the facts found on this node instead of cached facts from facterdb.

* --log-level
Set the Puppet log level which can be very useful with using the debugger.

* --quiet
Do not display the debugger help script upon startup.

* --play
Plays back the code file supplied into the debugger.  Can also supply
any http based url.

* --run-once
Return the result from the debugger and exit

* --stdin
Read from stdin instead of starting the debugger right away.  Useful when piping code into the debugger.

* --node-name
Retrieves the node information remotely via the puppet server given the node name.
This is extremely useful when trying to debug classification issues, as this can show
classes and parameters retrieved from the ENC.  You can also play around with the real facts
of the remote node as well.

Note: this requires special permission in your puppet server's auth.conf file to allow
access to make remote calls from this node: #{Puppet[:certname]}

You must also have a signed cert and be able to connect to the server from this system.

Mutually exclusive with --facterdb-filter

* --test
Runs the code in the debugger and exit without showing the help screen ( --quiet --run-once, --stdin)

EXAMPLE
-------
  $ puppet debugger
  $ echo "notice('hello, can you hear me?')" | puppet debugger --test
  $ echo "notice('hello, can you hear me?')" | puppet debugger --stdin
  $ puppet debugger --execute "notice('hello')"
  $ puppet debugger --facterdb-filter 'facterversion=/^2.4\./ and operatingsystem=Debian'
  $ puppet debugger --play https://gist.github.com/logicminds/4f6bcfd723c92aad1f01f6a800319fa4
  $ puppet debugger --facterdb-filter 'facterversion=/^2.4\./ and operatingsystem=Debian' \\
                    --play https://gist.github.com/logicminds/4f6bcfd723c92aad1f01f6a800319fa4
  $ puppet debugger --node-name


AUTHOR
------
Corey Osman <[email protected]>


COPYRIGHT
---------
Copyright (c) 2019 NWOps

  HELP
end

#mainObject



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
225
226
# File 'lib/puppet/application/debugger.rb', line 187

def main
  # if this is a file we don't play back since its part of the environment
  # if just the code we put in a file and use the play feature of the debugger
  # we could do the same thing with the passed in manifest file but that might be too much code to show

  if options[:code]
    code_input = options.delete(:code)
    file = Tempfile.new(["puppet_repl_input", ".pp"])
    File.open(file, "w") do |f|
      f.write(code_input)
    end
    options[:play] = file
  elsif command_line.args.empty? && use_stdin
    code_input = STDIN.read
    file = Tempfile.new(["puppet_repl_input", ".pp"])
    File.open(file, "w") do |f|
      f.write(code_input)
    end
    options[:play] = file
  elsif !command_line.args.empty?
    manifest = command_line.args.shift
    raise "Could not find file #{manifest}" unless Puppet::FileSystem.exist?(manifest)
    Puppet.warning("Only one file can be used per run.  Skipping #{command_line.args.join(", ")}") unless command_line.args.empty?
    options[:play] = file
  end
  begin
    if !options[:use_facterdb] && options[:node_name].nil?
      debug_environment = create_environment(nil)
      Puppet.notice('Gathering node facts...')
      node = create_node(debug_environment)
      scope = create_scope(node)
      # start_debugger(scope)
      options[:scope] = scope
    end
    ::PuppetDebugger::Cli.start_without_stdin(options)
  rescue Exception => e
    puts e
    exit 1
  end
end

#stacktraceString, Integer

returns a stacktrace of called puppet code This method originally came from the puppet 4.6 codebase and was backported here for compatibility with older puppet versions The basics behind this are to find the ‘.pp` file in the list of loaded code

Returns:

  • (String)
    • file path to source code

  • (Integer)
    • line number of called function



286
287
288
289
290
291
292
293
294
295
296
# File 'lib/puppet/application/debugger.rb', line 286

def stacktrace
  result = caller.each_with_object([]) do |loc, memo|
    if loc =~ /\A(.*\.pp)?:([0-9]+):in\s(.*)/
      # if the file is not found we set to code
      # and read from Puppet[:code]
      # $3 is reserved for the stacktrace type
      memo << [Regexp.last_match(1).nil? ? :code : Regexp.last_match(1), Regexp.last_match(2).to_i]
    end
    memo
  end.reverse
end

#start_debugger(scope, options = {}) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
# File 'lib/puppet/application/debugger.rb', line 268

def start_debugger(scope, options = {})
  if $stdout.isatty
    options = options.merge(scope: scope)
    # required in order to use convert puppet hash into ruby hash with symbols
    options = options.each_with_object({}) { |(k, v), data| data[k.to_sym] = v; data }
    # options[:source_file], options[:source_line] = stacktrace.last
    ::PuppetRepl::Cli.start(options)
  else
    Puppet.info "puppet debug: refusing to start the debugger without a tty"
  end
end