puppet-debugger 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +54 -0
- data/.gitlab-ci.yml +129 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +61 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +67 -0
- data/LICENSE.txt +20 -0
- data/README.md +276 -0
- data/Rakefile +32 -0
- data/bin/pdb +4 -0
- data/lib/awesome_print/ext/awesome_puppet.rb +40 -0
- data/lib/puppet-debugger/cli.rb +247 -0
- data/lib/puppet-debugger/code/code_file.rb +98 -0
- data/lib/puppet-debugger/code/code_range.rb +69 -0
- data/lib/puppet-debugger/code/loc.rb +80 -0
- data/lib/puppet-debugger/debugger_code.rb +318 -0
- data/lib/puppet-debugger/support/compiler.rb +20 -0
- data/lib/puppet-debugger/support/environment.rb +38 -0
- data/lib/puppet-debugger/support/errors.rb +75 -0
- data/lib/puppet-debugger/support/facts.rb +78 -0
- data/lib/puppet-debugger/support/functions.rb +72 -0
- data/lib/puppet-debugger/support/input_responders.rb +136 -0
- data/lib/puppet-debugger/support/node.rb +90 -0
- data/lib/puppet-debugger/support/play.rb +91 -0
- data/lib/puppet-debugger/support/scope.rb +42 -0
- data/lib/puppet-debugger/support.rb +176 -0
- data/lib/puppet-debugger.rb +55 -0
- data/lib/trollop.rb +861 -0
- data/lib/version.rb +3 -0
- data/puppet-debugger.gemspec +36 -0
- data/run_container_test.sh +12 -0
- data/spec/facts_spec.rb +86 -0
- data/spec/fixtures/environments/production/manifests/site.pp +1 -0
- data/spec/fixtures/invalid_node_obj.yaml +8 -0
- data/spec/fixtures/node_obj.yaml +298 -0
- data/spec/fixtures/sample_manifest.pp +2 -0
- data/spec/fixtures/sample_start_debugger.pp +13 -0
- data/spec/pdb_spec.rb +50 -0
- data/spec/puppet-debugger_spec.rb +492 -0
- data/spec/remote_node_spec.rb +170 -0
- data/spec/spec_helper.rb +57 -0
- data/spec/support_spec.rb +190 -0
- data/test_matrix.rb +42 -0
- metadata +148 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
module PuppetDebugger
|
2
|
+
module Support
|
3
|
+
module InputResponders
|
4
|
+
|
5
|
+
def static_responder_list
|
6
|
+
["exit", "functions", "classification","vars", 'facterdb_filter', "krt", "facts",
|
7
|
+
"resources", "classes", "whereami", "play","reset", "help"
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
# @source_file and @source_line_num instance variables must be set for this
|
12
|
+
# method to show the surrounding code
|
13
|
+
# @return [String] - string output of the code surrounded by the breakpoint or nil if file or line_num do not exist
|
14
|
+
def whereami(command=nil, args=nil)
|
15
|
+
file=@source_file
|
16
|
+
line_num=@source_line_num
|
17
|
+
if file and line_num
|
18
|
+
if file == :code
|
19
|
+
source_code = Puppet[:code]
|
20
|
+
code = DebuggerCode.from_string(source_code, :puppet)
|
21
|
+
else
|
22
|
+
code = DebuggerCode.from_file(file, :puppet)
|
23
|
+
end
|
24
|
+
return code.with_marker(line_num).around(line_num, 5).with_line_numbers.with_indentation(5).to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# displays the facterdb filter
|
29
|
+
# @param [Array] - args is not used
|
30
|
+
def facterdb_filter(args=[])
|
31
|
+
dynamic_facterdb_filter.ai
|
32
|
+
end
|
33
|
+
|
34
|
+
def help(args=[])
|
35
|
+
PuppetDebugger::Cli.print_repl_desc
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_set(input)
|
39
|
+
output = ''
|
40
|
+
args = input.split(' ')
|
41
|
+
args.shift # throw away the set
|
42
|
+
case args.shift
|
43
|
+
when /node/
|
44
|
+
if name = args.shift
|
45
|
+
output = "Resetting to use node #{name}"
|
46
|
+
reset
|
47
|
+
set_remote_node_name(name)
|
48
|
+
else
|
49
|
+
out_buffer.puts "Must supply a valid node name"
|
50
|
+
end
|
51
|
+
when /loglevel/
|
52
|
+
if level = args.shift
|
53
|
+
@log_level = level
|
54
|
+
set_log_level(level)
|
55
|
+
output = "loglevel #{Puppet::Util::Log.level} is set"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
output
|
59
|
+
end
|
60
|
+
|
61
|
+
def facts(args=[])
|
62
|
+
variables = node.facts.values
|
63
|
+
variables.ai({:sort_keys => true, :indent => -1})
|
64
|
+
end
|
65
|
+
|
66
|
+
def functions(args=[])
|
67
|
+
filter = args.first || ''
|
68
|
+
function_map.keys.sort.grep(/^#{Regexp.escape(filter)}/)
|
69
|
+
end
|
70
|
+
|
71
|
+
def vars(args=[])
|
72
|
+
# remove duplicate variables that are also in the facts hash
|
73
|
+
variables = scope.to_hash.delete_if {| key, value | node.facts.values.key?(key) }
|
74
|
+
variables['facts'] = 'removed by the puppet-debugger' if variables.key?('facts')
|
75
|
+
output = "Facts were removed for easier viewing".ai + "\n"
|
76
|
+
output += variables.ai({:sort_keys => true, :indent => -1})
|
77
|
+
end
|
78
|
+
|
79
|
+
def environment(args=[])
|
80
|
+
"Puppet Environment: #{puppet_env_name}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset(args=[])
|
84
|
+
set_scope(nil)
|
85
|
+
set_remote_node_name(nil)
|
86
|
+
set_node(nil)
|
87
|
+
set_facts(nil)
|
88
|
+
set_environment(nil)
|
89
|
+
set_log_level(log_level)
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_log_level(level)
|
93
|
+
Puppet::Util::Log.level = level.to_sym
|
94
|
+
buffer_log = Puppet::Util::Log.newdestination(:buffer)
|
95
|
+
if buffer_log
|
96
|
+
# if this is already set the buffer_log is nil
|
97
|
+
buffer_log.out_buffer = out_buffer
|
98
|
+
buffer_log.err_buffer = out_buffer
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def krt(args=[])
|
104
|
+
known_resource_types.ai({:sort_keys => true, :indent => -1})
|
105
|
+
end
|
106
|
+
|
107
|
+
def play(args=[])
|
108
|
+
config = {}
|
109
|
+
config[:play] = args.first
|
110
|
+
play_back(config)
|
111
|
+
return nil # we don't want to return anything
|
112
|
+
end
|
113
|
+
|
114
|
+
def classification(args=[])
|
115
|
+
node.classes.ai
|
116
|
+
end
|
117
|
+
|
118
|
+
def resources(args=[])
|
119
|
+
res = scope.compiler.catalog.resources.map do |res|
|
120
|
+
res.to_s.gsub(/\[/, "['").gsub(/\]/, "']") # ensure the title has quotes
|
121
|
+
end
|
122
|
+
if !args.first.nil?
|
123
|
+
res[args.first.to_i].ai
|
124
|
+
else
|
125
|
+
output = "Resources not shown in any specific order\n".warning
|
126
|
+
output += res.ai
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def classes(args=[])
|
131
|
+
scope.compiler.catalog.classes.ai
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'puppet/indirector/node/rest'
|
2
|
+
|
3
|
+
module PuppetDebugger
|
4
|
+
module Support
|
5
|
+
module Node
|
6
|
+
# creates a node object using defaults or gets the remote node
|
7
|
+
# object if the remote_node_name is defined
|
8
|
+
def create_node
|
9
|
+
Puppet[:trusted_server_facts] = true if Puppet.version.to_f >= 4.1
|
10
|
+
|
11
|
+
if remote_node_name
|
12
|
+
# refetch
|
13
|
+
node_obj = set_node_from_name(remote_node_name)
|
14
|
+
end
|
15
|
+
unless node_obj
|
16
|
+
options = {}
|
17
|
+
options[:parameters] = default_facts.values
|
18
|
+
options[:facts] = default_facts
|
19
|
+
options[:classes] = []
|
20
|
+
options[:environment] = puppet_environment
|
21
|
+
name = default_facts.values['fqdn']
|
22
|
+
node_obj = Puppet::Node.new(name, options)
|
23
|
+
node_obj.add_server_facts(server_facts) if node_obj.respond_to?(:add_server_facts)
|
24
|
+
node_obj
|
25
|
+
end
|
26
|
+
node_obj
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_remote_node_name(name)
|
30
|
+
@remote_node_name = name
|
31
|
+
end
|
32
|
+
|
33
|
+
def remote_node_name=(name)
|
34
|
+
@remote_node_name = name
|
35
|
+
end
|
36
|
+
|
37
|
+
def remote_node_name
|
38
|
+
@remote_node_name
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [node] puppet node object
|
42
|
+
def node
|
43
|
+
@node ||= create_node
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_remote_node(name)
|
47
|
+
indirection = Puppet::Indirector::Indirection.instance(:node)
|
48
|
+
indirection.terminus_class = 'rest'
|
49
|
+
remote_node = indirection.find(name, :environment => puppet_environment)
|
50
|
+
remote_node
|
51
|
+
end
|
52
|
+
|
53
|
+
# this is a hack to get around that the puppet node fact face does not return
|
54
|
+
# a proper node object with the facts hash populated
|
55
|
+
# returns a node object with a proper facts hash
|
56
|
+
def convert_remote_node(remote_node)
|
57
|
+
options = {}
|
58
|
+
# remove trusted data as it will later get populated during compilation
|
59
|
+
parameters = remote_node.parameters.dup
|
60
|
+
trusted_data = parameters.delete('trusted')
|
61
|
+
options[:parameters] = parameters || {}
|
62
|
+
options[:facts] = Puppet::Node::Facts.new(remote_node.name,remote_node.parameters)
|
63
|
+
options[:classes] = remote_node.classes
|
64
|
+
options[:environment] = puppet_environment
|
65
|
+
node_object = Puppet::Node.new(remote_node.name, options)
|
66
|
+
node_object.add_server_facts(server_facts) if node_object.respond_to?(:add_server_facts)
|
67
|
+
node_object.trusted_data = trusted_data
|
68
|
+
node_object
|
69
|
+
end
|
70
|
+
|
71
|
+
# query the remote puppet server and retrieve the node object
|
72
|
+
#
|
73
|
+
def set_node_from_name(name)
|
74
|
+
out_buffer.puts ("Fetching node #{name}")
|
75
|
+
remote_node = get_remote_node(name)
|
76
|
+
if remote_node and remote_node.parameters.empty?
|
77
|
+
remote_node_name = nil # clear out the remote name
|
78
|
+
raise PuppetDebugger::Exception::UndefinedNode.new(:name => remote_node.name)
|
79
|
+
end
|
80
|
+
remote_node_name = remote_node.name
|
81
|
+
node_object = convert_remote_node(remote_node)
|
82
|
+
set_node(node_object)
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_node(value)
|
86
|
+
@node = value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module PuppetDebugger
|
2
|
+
module Support
|
3
|
+
module Play
|
4
|
+
|
5
|
+
def play_back(config={})
|
6
|
+
if config[:play]
|
7
|
+
if config[:play] =~ /^http/
|
8
|
+
play_back_url(config[:play])
|
9
|
+
elsif File.exists? config[:play]
|
10
|
+
play_back_string(File.read(config[:play]))
|
11
|
+
else config[:play]
|
12
|
+
out_buffer.puts "puppet-debugger can't play #{config[:play]}'"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert_to_text(url)
|
18
|
+
require 'uri'
|
19
|
+
url_data = URI(url)
|
20
|
+
case url_data.host
|
21
|
+
when /^gist\.github*/
|
22
|
+
unless url_data.path =~ /raw/
|
23
|
+
url = url += '.txt'
|
24
|
+
end
|
25
|
+
url
|
26
|
+
when /^github.com/
|
27
|
+
if url_data.path =~ /blob/
|
28
|
+
url.gsub('blob', 'raw')
|
29
|
+
end
|
30
|
+
when /^gist.github.com/
|
31
|
+
unless url_data.path =~ /raw/
|
32
|
+
url = url += '.txt'
|
33
|
+
end
|
34
|
+
url
|
35
|
+
when /^gitlab.com/
|
36
|
+
if url_data.path =~ /snippets/
|
37
|
+
url += '/raw' unless url_data.path =~ /raw/
|
38
|
+
url
|
39
|
+
else
|
40
|
+
url.gsub('blob', 'raw')
|
41
|
+
end
|
42
|
+
else
|
43
|
+
url
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# opens the url and reads the data
|
48
|
+
def fetch_url_data(url)
|
49
|
+
open(url).read
|
50
|
+
end
|
51
|
+
|
52
|
+
def play_back_url(url)
|
53
|
+
begin
|
54
|
+
require 'open-uri'
|
55
|
+
require 'net/http'
|
56
|
+
converted_url = convert_to_text(url)
|
57
|
+
str = fetch_url_data(converted_url)
|
58
|
+
play_back_string(str)
|
59
|
+
rescue SocketError
|
60
|
+
abort "puppet-debugger can't play `#{converted_url}'"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# plays back the string to the output stream
|
65
|
+
# puts the input to the output as well as the produced output
|
66
|
+
def play_back_string(str)
|
67
|
+
full_buffer = ''
|
68
|
+
str.split("\n").each do |buf|
|
69
|
+
begin
|
70
|
+
full_buffer += buf
|
71
|
+
# unless this is puppet code, otherwise skip repl keywords
|
72
|
+
if keyword_expression.match(buf)
|
73
|
+
out_buffer.write(">> ")
|
74
|
+
else
|
75
|
+
parser.parse_string(full_buffer)
|
76
|
+
out_buffer.write(">> ")
|
77
|
+
end
|
78
|
+
rescue Puppet::ParseErrorWithIssue => e
|
79
|
+
if multiline_input?(e)
|
80
|
+
full_buffer += "\n"
|
81
|
+
next
|
82
|
+
end
|
83
|
+
end
|
84
|
+
out_buffer.puts(full_buffer)
|
85
|
+
handle_input(full_buffer)
|
86
|
+
full_buffer = ''
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module PuppetDebugger
|
2
|
+
module Support
|
3
|
+
module Scope
|
4
|
+
def set_scope(value)
|
5
|
+
@scope = value
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Scope] puppet scope object
|
9
|
+
def scope
|
10
|
+
unless @scope
|
11
|
+
@scope ||= create_scope
|
12
|
+
end
|
13
|
+
@scope
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_scope
|
17
|
+
do_initialize
|
18
|
+
begin
|
19
|
+
@compiler = create_compiler(node) # creates a new compiler for each scope
|
20
|
+
scope = Puppet::Parser::Scope.new(@compiler)
|
21
|
+
# creates a node class
|
22
|
+
scope.source = Puppet::Resource::Type.new(:node, node.name)
|
23
|
+
scope.parent = @compiler.topscope
|
24
|
+
load_lib_dirs
|
25
|
+
# compiling will load all the facts into the scope
|
26
|
+
# without this step facts will not get resolved
|
27
|
+
scope.compiler.compile # this will load everything into the scope
|
28
|
+
rescue StandardError => e
|
29
|
+
err = parse_error(e)
|
30
|
+
raise err
|
31
|
+
end
|
32
|
+
scope
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns a hash of varaibles that are currently in scope
|
36
|
+
def scope_vars
|
37
|
+
vars = scope.to_hash.delete_if {| key, value | node.facts.values.key?(key.to_sym) }
|
38
|
+
vars['facts'] = 'removed by the puppet-debugger'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'puppet/pops'
|
2
|
+
require 'facterdb'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
# load all the generators found in the generators directory
|
6
|
+
Dir.glob(File.join(File.dirname(__FILE__),'support', '*.rb')).each do |file|
|
7
|
+
require_relative File.join('support', File.basename(file, '.rb'))
|
8
|
+
end
|
9
|
+
|
10
|
+
module PuppetDebugger
|
11
|
+
module Support
|
12
|
+
include PuppetDebugger::Support::Compilier
|
13
|
+
include PuppetDebugger::Support::Environment
|
14
|
+
include PuppetDebugger::Support::Facts
|
15
|
+
include PuppetDebugger::Support::Scope
|
16
|
+
include PuppetDebugger::Support::Functions
|
17
|
+
include PuppetDebugger::Support::Node
|
18
|
+
include PuppetDebugger::Support::InputResponders
|
19
|
+
include PuppetDebugger::Support::Play
|
20
|
+
|
21
|
+
# parses the error type into a more useful error message defined in errors.rb
|
22
|
+
# returns new error object or the original if error cannot be parsed
|
23
|
+
def parse_error(error)
|
24
|
+
case error
|
25
|
+
when SocketError
|
26
|
+
PuppetDebugger::Exception::ConnectError.new(:message => "Unknown host: #{Puppet[:server]}")
|
27
|
+
when Net::HTTPError
|
28
|
+
PuppetDebugger::Exception::AuthError.new(:message => error.message)
|
29
|
+
when Errno::ECONNREFUSED
|
30
|
+
PuppetDebugger::Exception::ConnectError.new(:message => error.message)
|
31
|
+
when Puppet::Error
|
32
|
+
if error.message =~ /could\ not\ find\ class/i
|
33
|
+
PuppetDebugger::Exception::NoClassError.new(:default_modules_paths => default_modules_paths,
|
34
|
+
:message => error.message)
|
35
|
+
elsif error.message =~ /default\ node/i
|
36
|
+
PuppetDebugger::Exception::NodeDefinitionError.new(:default_site_manifest => default_site_manifest,
|
37
|
+
:message => error.message)
|
38
|
+
else
|
39
|
+
error
|
40
|
+
end
|
41
|
+
else
|
42
|
+
error
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# returns an array of module directories, generally this is the only place
|
47
|
+
# to look for puppet code by default. This is read from the puppet configuration
|
48
|
+
def default_modules_paths
|
49
|
+
dirs = []
|
50
|
+
do_initialize if Puppet[:codedir].nil?
|
51
|
+
# add the puppet-debugger directory so we can load any defined functions
|
52
|
+
dirs << File.join(Puppet[:environmentpath],default_puppet_env_name,'modules') unless Puppet[:environmentpath].empty?
|
53
|
+
dirs << Puppet.settings[:basemodulepath].split(':')
|
54
|
+
dirs.flatten
|
55
|
+
end
|
56
|
+
|
57
|
+
# this is the lib directory of this gem
|
58
|
+
# in order to load any puppet functions from this gem we need to add the lib path
|
59
|
+
# of this gem
|
60
|
+
def puppet_repl_lib_dir
|
61
|
+
File.expand_path(File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'lib'))
|
62
|
+
end
|
63
|
+
|
64
|
+
# returns all the modules paths defined in the environment
|
65
|
+
def modules_paths
|
66
|
+
puppet_environment.full_modulepath
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize_from_scope(value)
|
70
|
+
set_scope(value)
|
71
|
+
unless value.nil?
|
72
|
+
set_environment(value.environment)
|
73
|
+
set_node(value.compiler.node)
|
74
|
+
set_compiler(value.compiler)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def keyword_expression
|
79
|
+
@keyword_expression ||= Regexp.new(/^exit|^:set|^play|^classification|^facts|^vars|^functions|^classes|^resources|^krt|^environment|^reset|^help/)
|
80
|
+
end
|
81
|
+
|
82
|
+
def known_resource_types
|
83
|
+
res = {
|
84
|
+
:hostclasses => scope.environment.known_resource_types.hostclasses.keys,
|
85
|
+
:definitions => scope.environment.known_resource_types.definitions.keys,
|
86
|
+
:nodes => scope.environment.known_resource_types.nodes.keys,
|
87
|
+
}
|
88
|
+
if sites = scope.environment.known_resource_types.instance_variable_get(:@sites)
|
89
|
+
res.merge!(:sites => scope.environment.known_resource_types.instance_variable_get(:@sites).first)
|
90
|
+
end
|
91
|
+
if scope.environment.known_resource_types.respond_to?(:applications)
|
92
|
+
res.merge!(:applications => scope.environment.known_resource_types.applications.keys)
|
93
|
+
end
|
94
|
+
# some versions of puppet do not support capabilities
|
95
|
+
if scope.environment.known_resource_types.respond_to?(:capability_mappings)
|
96
|
+
res.merge!(:capability_mappings => scope.environment.known_resource_types.capability_mappings.keys)
|
97
|
+
end
|
98
|
+
res
|
99
|
+
end
|
100
|
+
|
101
|
+
# this is required in order to load things only when we need them
|
102
|
+
def do_initialize
|
103
|
+
begin
|
104
|
+
Puppet.initialize_settings
|
105
|
+
Puppet[:parser] = 'future' # this is required in order to work with puppet 3.8
|
106
|
+
Puppet[:trusted_node_data] = true
|
107
|
+
rescue ArgumentError => e
|
108
|
+
|
109
|
+
rescue Puppet::DevError => e
|
110
|
+
# do nothing otherwise calling init twice raises an error
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @param String - any valid puppet language code
|
115
|
+
# @return Hostclass - a puppet Program object which is considered the main class
|
116
|
+
def generate_ast(string = nil)
|
117
|
+
parse_result = parser.parse_string(string, '')
|
118
|
+
# the parse_result may be
|
119
|
+
# * empty / nil (no input)
|
120
|
+
# * a Model::Program
|
121
|
+
# * a Model::Expression
|
122
|
+
#
|
123
|
+
model = parse_result.nil? ? nil : parse_result.current
|
124
|
+
args = {}
|
125
|
+
::Puppet::Pops::Model::AstTransformer.new('').merge_location(args, model)
|
126
|
+
|
127
|
+
ast_code =
|
128
|
+
if model.is_a? ::Puppet::Pops::Model::Program
|
129
|
+
::Puppet::Parser::AST::PopsBridge::Program.new(model, args)
|
130
|
+
else
|
131
|
+
args[:value] = model
|
132
|
+
::Puppet::Parser::AST::PopsBridge::Expression.new(args)
|
133
|
+
end
|
134
|
+
# Create the "main" class for the content - this content will get merged with all other "main" content
|
135
|
+
::Puppet::Parser::AST::Hostclass.new('', :code => ast_code)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param String - any valid puppet language code
|
139
|
+
# @return Object - returns either a string of the result or object from puppet evaulation
|
140
|
+
def puppet_eval(input)
|
141
|
+
# in order to add functions to the scope the loaders must be created
|
142
|
+
# in order to call native functions we need to set the global_scope
|
143
|
+
ast = generate_ast(input)
|
144
|
+
# record the input for puppet to retrieve and reference later
|
145
|
+
file = Tempfile.new(['puppet_repl_input', '.pp'])
|
146
|
+
File.open(file, 'w') do |f|
|
147
|
+
f.write(input)
|
148
|
+
end
|
149
|
+
Puppet.override( {:code => input, :global_scope => scope, :loaders => scope.compiler.loaders } , 'For puppet-debugger') do
|
150
|
+
# because the repl is not a module we leave the modname blank
|
151
|
+
scope.environment.known_resource_types.import_ast(ast, '')
|
152
|
+
parser.evaluate_string(scope, input, File.expand_path(file))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def puppet_lib_dir
|
157
|
+
# returns something like "/Library/Ruby/Gems/2.0.0/gems/puppet-4.2.2/lib/puppet.rb"
|
158
|
+
# this is only useful when returning a namespace with the functions
|
159
|
+
@puppet_lib_dir ||= File.dirname(Puppet.method(:[]).source_location.first)
|
160
|
+
end
|
161
|
+
|
162
|
+
# returns a future parser for evaluating code
|
163
|
+
def parser
|
164
|
+
@parser ||= ::Puppet::Pops::Parser::EvaluatingParser.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def default_manifests_dir
|
168
|
+
File.join(Puppet[:environmentpath],default_puppet_env_name,'manifests')
|
169
|
+
end
|
170
|
+
|
171
|
+
def default_site_manifest
|
172
|
+
File.join(default_manifests_dir, 'site.pp')
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'puppet-debugger/cli'
|
2
|
+
require_relative 'version'
|
3
|
+
require 'awesome_print'
|
4
|
+
require_relative 'awesome_print/ext/awesome_puppet'
|
5
|
+
require_relative 'trollop'
|
6
|
+
require 'puppet/util/log'
|
7
|
+
require_relative 'puppet-debugger/debugger_code'
|
8
|
+
|
9
|
+
require_relative 'puppet-debugger/support/errors'
|
10
|
+
# monkey patch in some color effects string methods
|
11
|
+
class String
|
12
|
+
def red; "\033[31m#{self}\033[0m" end
|
13
|
+
def green; "\033[32m#{self}\033[0m" end
|
14
|
+
def cyan; "\033[36m#{self}\033[0m" end
|
15
|
+
def yellow; "\033[33m#{self}\033[0m" end
|
16
|
+
def warning; yellow end
|
17
|
+
def fatal; red end
|
18
|
+
def info; green end
|
19
|
+
|
20
|
+
def camel_case
|
21
|
+
return self if self !~ /_/ && self =~ /[A-Z]+.*/
|
22
|
+
split('_').map(&:capitalize).join
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Puppet::Util::Log.newdesttype :buffer do
|
27
|
+
require 'puppet/util/colors'
|
28
|
+
include Puppet::Util::Colors
|
29
|
+
|
30
|
+
attr_accessor :err_buffer, :out_buffer
|
31
|
+
|
32
|
+
def initialize(err=$stderr, out=$stdout)
|
33
|
+
@err_buffer = err
|
34
|
+
@out_buffer = out
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle(msg)
|
38
|
+
levels = {
|
39
|
+
:emerg => { :name => 'Emergency', :color => :hred, :stream => err_buffer },
|
40
|
+
:alert => { :name => 'Alert', :color => :hred, :stream => err_buffer },
|
41
|
+
:crit => { :name => 'Critical', :color => :hred, :stream => err_buffer },
|
42
|
+
:err => { :name => 'Error', :color => :hred, :stream => err_buffer },
|
43
|
+
:warning => { :name => 'Warning', :color => :hred, :stream => err_buffer },
|
44
|
+
:notice => { :name => 'Notice', :color => :reset, :stream => out_buffer },
|
45
|
+
:info => { :name => 'Info', :color => :green, :stream => out_buffer },
|
46
|
+
:debug => { :name => 'Debug', :color => :cyan, :stream => out_buffer },
|
47
|
+
}
|
48
|
+
|
49
|
+
str = msg.respond_to?(:multiline) ? msg.multiline : msg.to_s
|
50
|
+
str = msg.source == "Puppet" ? str : "#{msg.source}: #{str}"
|
51
|
+
|
52
|
+
level = levels[msg.level]
|
53
|
+
level[:stream].puts colorize(level[:color], "#{level[:name]}: #{str}")
|
54
|
+
end
|
55
|
+
end
|