Created
February 24, 2012 16:27
-
-
Save masterzen/1901871 to your computer and use it in GitHub Desktop.
Parse Puppet AST for global node variables
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'puppet' | |
class Parser | |
def scan(input_file_name) | |
@input_file_name = input_file_name | |
environment = Puppet::Node::Environment.new | |
@known_resource_types = environment.known_resource_types | |
unless environment.known_resource_types.watching_file?(@input_file_name) | |
Puppet.info "rdoc: scanning #{@input_file_name}" | |
if @input_file_name =~ /\.pp$/ | |
@parser = Puppet::Parser::Parser.new(environment) | |
@parser.file = input_file_name | |
@parser.parse.instantiate('').each do |type| | |
@known_resource_types.add type | |
end | |
end | |
end | |
scan_top_level | |
end | |
# split_module tries to find if +path+ belongs to the module path | |
# if it does, it returns the module name, otherwise if we are sure | |
# it is part of the global manifest path, "__site__" is returned. | |
# And finally if this path couldn't be mapped anywhere, nil is returned. | |
def split_module(path) | |
# find a module | |
fullpath = File.expand_path(path) | |
puts "rdoc: testing #{fullpath}" | |
if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins|lib)\/.+\.(pp|rb)$/ | |
modpath = $1 | |
name = $2 | |
puts "rdoc: module #{name} into #{modpath} ?" | |
Puppet::Node::Environment.new.modulepath.each do |mp| | |
if File.identical?(modpath,mp) | |
puts "rdoc: found module #{name}" | |
return name | |
end | |
end | |
end | |
if fullpath =~ /\.(pp|rb)$/ | |
# there can be paths we don't want to scan under modules | |
# imagine a ruby or manifest that would be distributed as part as a module | |
# but we don't want those to be hosted under <site> | |
Puppet::Node::Environment.new.modulepath.each do |mp| | |
# check that fullpath is a descendant of mp | |
dirname = fullpath | |
previous = dirname | |
while (dirname = File.dirname(previous)) != previous | |
previous = dirname | |
return nil if File.identical?(dirname,mp) | |
end | |
end | |
end | |
# we are under a global manifests | |
puts "rdoc: global manifests" | |
"__site__" | |
end | |
# create documentation for the top level +container+ | |
def scan_top_level | |
# infer module name from directory | |
name = split_module(@input_file_name) | |
if name.nil? | |
return | |
end | |
puts "rdoc: scanning for #{name}" | |
parse_elements | |
end | |
# create documentation for global variables assignements we can find in +code+ | |
# and associate it with +container+ | |
def scan_for_vardef(code) | |
code = [code] unless code.is_a?(Array) | |
code.each do |stmt| | |
scan_for_vardef(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray) | |
if stmt.is_a?(Puppet::Parser::AST::VarDef) | |
#puts "rdoc: found constant: #{stmt.name} = #{stmt.value}" | |
### EMIT AS YAML | |
case stmt.value | |
when Puppet::Parser::AST::String | |
puts "#{stmt.name}: #{stmt.value}" | |
when Puppet::Parser::AST::Boolean | |
puts "#{stmt.name}: #{stmt.value}" | |
when Puppet::Parser::AST::ASTArray | |
puts "#{stmt.name}:" | |
stmt.value.children.each do |m| | |
puts "\t- #{m}" | |
end | |
end | |
end | |
end | |
end | |
# create documentation for a node | |
def document_node(name, node) | |
puts "rdoc: found new node #{name}" | |
superclass = node.parent | |
code = node.code.children if node.code.is_a?(Puppet::Parser::AST::ASTArray) | |
code ||= node.code | |
unless code.nil? | |
scan_for_vardef(code) | |
end | |
rescue => detail | |
puts detail.backtrace | |
raise Puppet::ParseError, "impossible to parse node '#{name}' in #{node.file} at line #{node.line}: #{detail}" | |
end | |
# Traverse the AST tree and produce code-objects node | |
# that contains the documentation | |
def parse_elements | |
puts "rdoc: scanning manifest" | |
@known_resource_types.nodes.each do |name, node| | |
if node.file == @input_file_name | |
document_node(name.to_s,node) | |
end | |
end | |
end | |
end | |
Puppet[:modulepath] = "/home/brice/cvs/puppet/modules" | |
parser = Parser.new | |
parser.scan("/home/brice/cvs/puppet/manifests/site.pp") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment