summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--README22
-rw-r--r--TODO4
-rwxr-xr-xbin/certmgr.rb44
-rwxr-xr-xbin/filebucketd40
-rwxr-xr-xbin/puppet129
-rwxr-xr-xbin/puppetca127
-rwxr-xr-xbin/puppetd102
-rwxr-xr-xbin/puppetdoc129
-rwxr-xr-xbin/puppetmasterd89
-rw-r--r--examples/code/allatonce13
-rw-r--r--examples/code/assignments11
-rw-r--r--examples/code/classing35
-rw-r--r--examples/code/components73
-rw-r--r--examples/code/execs16
-rw-r--r--examples/code/facts22
-rw-r--r--examples/code/failers/badclassnoparam10
-rw-r--r--examples/code/failers/badclassparam10
-rw-r--r--examples/code/failers/badcompnoparam9
-rw-r--r--examples/code/failers/badcompparam9
-rw-r--r--examples/code/failers/badtypeparam3
-rw-r--r--examples/code/file.bl11
-rw-r--r--examples/code/filedefaults10
-rw-r--r--examples/code/fileparsing116
-rw-r--r--examples/code/filerecursion15
-rw-r--r--examples/code/functions3
-rw-r--r--examples/code/groups7
-rw-r--r--examples/code/head30
-rw-r--r--examples/code/iftest21
-rw-r--r--examples/code/importing8
-rw-r--r--examples/code/nodes20
-rw-r--r--examples/code/one8
-rw-r--r--examples/code/relationships34
-rw-r--r--examples/code/selectors28
-rw-r--r--examples/code/simpletests11
-rw-r--r--examples/code/snippets/argumentdefaults14
-rw-r--r--examples/code/snippets/classpathtest13
-rw-r--r--examples/code/snippets/dirchmod19
-rw-r--r--examples/code/snippets/filecreate11
-rw-r--r--examples/code/snippets/simpledefaults5
-rw-r--r--examples/code/snippets/simpleselector38
-rw-r--r--examples/code/svncommit13
-rwxr-xr-xexamples/root/bin/sleeper69
-rw-r--r--examples/root/etc/configfile0
-rw-r--r--examples/root/etc/debian-passwd29
-rw-r--r--examples/root/etc/debian-syslog.conf71
-rwxr-xr-xexamples/root/etc/init.d/sleeper65
-rw-r--r--examples/root/etc/otherfile0
-rw-r--r--lib/puppet/ca.rb155
-rw-r--r--lib/puppet/client.rb18
-rwxr-xr-xlib/puppet/fileserver.rb260
-rw-r--r--lib/puppet/master.rb88
-rw-r--r--lib/puppet/metric.rb4
-rw-r--r--lib/puppet/parser/ast.rb1238
-rw-r--r--lib/puppet/parser/grammar.ra672
-rw-r--r--lib/puppet/parser/interpreter.rb135
-rw-r--r--lib/puppet/parser/lexer.rb225
-rw-r--r--lib/puppet/parser/makefile5
-rw-r--r--lib/puppet/parser/parser.rb1264
-rw-r--r--lib/puppet/parser/scope.rb427
-rw-r--r--lib/puppet/server.rb129
-rw-r--r--lib/puppet/servlet.rb110
-rw-r--r--lib/puppet/type.rb9
-rw-r--r--lib/puppet/type/pfile.rb660
-rwxr-xr-xtest/certmgr/tc_certmgr.rb275
-rwxr-xr-xtest/executables/tc_puppetbin.rb39
-rwxr-xr-xtest/executables/tc_puppetca.rb90
-rwxr-xr-xtest/executables/tc_puppetd.rb88
-rwxr-xr-xtest/executables/tc_puppetmasterd.rb171
-rwxr-xr-xtest/language/tc_scope.rb264
-rwxr-xr-xtest/language/tc_snippets.rb317
-rw-r--r--test/other/tc_metrics.rb2
-rw-r--r--test/parser/tc_lexer.rb112
-rw-r--r--test/parser/tc_parser.rb55
-rwxr-xr-xtest/puppet/tc_defaults.rb27
-rw-r--r--test/server/tc_ca.rb225
-rwxr-xr-xtest/server/tc_fileserver.rb386
-rw-r--r--test/server/tc_master.rb135
-rw-r--r--test/server/tc_server.rb364
-rwxr-xr-xtest/test13
-rw-r--r--test/types/tc_file.rb96
81 files changed, 9415 insertions, 211 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..b3701c91b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+This entire repository is under the GNU Public License. See www.gnu.org
+for a copy.
diff --git a/README b/README
new file mode 100644
index 000000000..b25a28b5d
--- /dev/null
+++ b/README
@@ -0,0 +1,22 @@
+$Id$
+
+This is what you need to get puppet running:
+
+-- Check you have Ruby version 1.8.2 or later
+ You can download it from
+ ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.2.tar.gz
+ Un tar it, then: ./configure; make; make install
+
+-- http://reductivelabs.com/downloads/facter/facter-1.0.tgz
+ A library that puppet needs to run. Un tar it, and calling
+ 'ruby install.rb' file should successfully install it; let
+ me know if it doesn't. Otherwise, you can just set RUBYLIB
+ to contain its lib directory.
+
+-- Install puppet
+ Run 'ruby install.rb' or add the 'lib/' directory to your RUBYLIB path.
+ Documentation can be found online at
+ http://reductivelabs.com/projects/puppet/documentation/
+
+-- After that, you should be able to go into test/ and run ./test,
+ or run 'bin/puppet' on whichever puppet config files you want.
diff --git a/TODO b/TODO
new file mode 100644
index 000000000..aec26f8c5
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+$Id$
+
+* Syntax for configuration of types (e.g., search path for Services)
+* Get file parsing working in the language
diff --git a/bin/certmgr.rb b/bin/certmgr.rb
new file mode 100755
index 000000000..37f4a406c
--- /dev/null
+++ b/bin/certmgr.rb
@@ -0,0 +1,44 @@
+#!/usr/bin/ruby -w
+
+#--------------------
+# the puppet client
+#
+# $Id$
+
+
+require 'puppet'
+require 'puppet/openssl'
+require 'getoptlong'
+
+result = GetoptLong.new(
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+check = false
+
+result.each { |opt,arg|
+ case opt
+ when "--debug":
+ when "--check":
+ when "--help":
+ puts "There is no help yet"
+ exit
+ else
+ puts "Invalid option '#{opt}'"
+ exit(10)
+ end
+}
+
+Puppet[:logdest] = :console
+Puppet[:loglevel] = :info
+
+rootcert = Puppet[:rootcert]
+rootkey = Puppet[:rootkey]
+rootkey = Puppet[:rootkey]
+
+unless rootcert
+ raise "config unset"
+end
+
+#mkcertsdir(File.basename(rootcert))
diff --git a/bin/filebucketd b/bin/filebucketd
new file mode 100755
index 000000000..2e7177d6a
--- /dev/null
+++ b/bin/filebucketd
@@ -0,0 +1,40 @@
+#!/usr/bin/ruby -w
+
+#--------------------
+# accept and serve files
+#
+# $Id$
+
+require 'getoptlong'
+require 'puppet/filebucket'
+
+ #[ "--size", "-s", GetoptLong::REQUIRED_ARGUMENT ],
+result = GetoptLong.new(
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ else
+ raise "Invalid option '#{opt}'"
+ end
+}
+
+begin
+ server = FileBucket::BucketWebserver.new(
+ :Bucket => File.expand_path("~/.puppet/bucket"),
+ :Debug => true,
+ :Port => 8080
+ )
+ trap(:INT) { server.shutdown }
+ server.start
+ #server = FileBucket::BucketServer.new(
+ # :Bucket => File.expand_path("~/.puppet/bucket")
+ #)
+rescue => detail
+ $stderr.puts detail
+ exit(1)
+end
diff --git a/bin/puppet b/bin/puppet
new file mode 100755
index 000000000..094cefd0a
--- /dev/null
+++ b/bin/puppet
@@ -0,0 +1,129 @@
+#!/usr/bin/ruby -w
+
+#
+# = Synopsis
+#
+# Run a stand-alone +puppet+ script.
+#
+# = Usage
+#
+# puppet [-V|--version] [-d|--debug] [-v|--verbose] [-l|--logfile <file>] [-h|--help] <file>
+#
+# = Description
+#
+# This is the standalone +puppet+ execution script; use it to execute
+# individual scripts that you write. If you need to execute site-wide
+# scripts, use +puppetd+ and +puppetmasterd+.
+#
+# = Options
+#
+# debug::
+# Enable full debugging.
+#
+# help::
+# Print this help message
+#
+# logfile::
+# Where to send messages. Defaults to sending messages to the console.
+#
+# = Example
+#
+# puppet -l /tmp/script.log script.pp
+#
+# = Author
+#
+# Luke Kanies
+#
+# = Copyright
+#
+# Copyright (c) 2005 Reductive Labs, LLC
+# Licensed under the Gnu Public License
+
+require 'puppet'
+require 'puppet/server'
+require 'puppet/client'
+require 'getoptlong'
+
+$haveusage = true
+
+begin
+ require 'rdoc/usage'
+rescue
+ $haveusage = false
+end
+
+ result = GetoptLong.new(
+ [ "--version", "-V", GetoptLong::NO_ARGUMENT ],
+ [ "--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+ )
+
+debug = false
+verbose = false
+logfile = false
+
+begin
+ result.each { |opt,arg|
+ case opt
+ when "--version"
+ puts "%s" % Puppet.version
+ exit
+ when "--help"
+ if $haveusage
+ RDoc::usage && exit
+ else
+ puts "No help available unless you have RDoc::usage installed"
+ exit
+ end
+ when "--verbose"
+ verbose = true
+ when "--debug"
+ debug = true
+ when "--logfile"
+ logfile = arg
+ else
+ $stderr.puts "Invalid option '#{opt}'"
+ Rdoc::usage(1,'usage')
+ end
+ }
+rescue GetoptLong::InvalidOption => detail
+ RDoc::usage(1,'usage')
+end
+
+if debug
+ Puppet[:loglevel] = :debug
+elsif verbose
+ Puppet[:loglevel] = :info
+end
+
+if logfile
+ Puppet[:logdest] = logfile
+end
+
+begin
+ server = Puppet::Master.new(
+ :File => ARGV.shift,
+ :Local => true
+ )
+rescue => detail
+ $stderr.puts detail
+ exit(1)
+end
+
+begin
+ client = Puppet::Client.new(
+ :Server => server
+ )
+rescue => detail
+ $stderr.puts detail
+ exit(1)
+end
+
+begin
+ client.getconfig
+rescue => detail
+ $stderr.puts detail
+ exit(1)
+end
diff --git a/bin/puppetca b/bin/puppetca
new file mode 100755
index 000000000..358f721b1
--- /dev/null
+++ b/bin/puppetca
@@ -0,0 +1,127 @@
+#!/usr/bin/ruby -w
+
+#--------------------
+# the puppet client
+#
+# $Id$
+
+
+require 'puppet'
+require 'puppet/sslcertificates'
+require 'getoptlong'
+
+result = GetoptLong.new(
+ [ "--ssldir", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--list", "-l", GetoptLong::NO_ARGUMENT ],
+ [ "--sign", "-s", GetoptLong::NO_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
+ [ "--all", "-a", GetoptLong::NO_ARGUMENT ],
+ [ "--cadir", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--generate", "-g", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+mode = nil
+all = false
+generate = nil
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ when "--list"
+ mode = :list
+ when "--sign"
+ mode = :sign
+ when "--all"
+ all = true
+ when "--verbose"
+ Puppet[:loglevel] = :info
+ when "--debug"
+ Puppet[:loglevel] = :debug
+ when "--generate"
+ generate = arg
+ mode = :generate
+ when "--cadir"
+ Puppet[:cadir] = arg
+ when "--ssldir"
+ Puppet[:ssldir] = arg
+ else
+ puts "Invalid option '#{opt}'"
+ exit(10)
+ end
+}
+
+ca = Puppet::SSLCertificates::CA.new()
+
+unless mode
+ $stderr.puts "You must specify --list or --sign"
+ exit(12)
+end
+
+hosts = ca.list
+unless hosts.length > 0 or mode == :generate
+ Puppet.info "No waiting requests"
+ exit(0)
+end
+
+case mode
+when :list
+ puts hosts.join("\n")
+when :sign
+ unless ARGV.length > 0 or all
+ $stderr.puts(
+ "You must specify to sign all certificates or you must specify hostnames"
+ )
+ exit(24)
+ end
+
+ unless all
+ hosts = hosts.find_all { |host|
+ ARGV.include?(host)
+ }
+ end
+
+ hosts.each { |host|
+ begin
+ csr = ca.getclientcsr(host)
+ rescue => detail
+ $stderr.puts "Could not retrieve request for %s: %s" % [host, detail]
+ end
+
+ begin
+ ca.sign(csr)
+ rescue => detail
+ $stderr.puts "Could not sign request for %s: %s" % [host, detail]
+ end
+
+ begin
+ ca.removeclientcsr(host)
+ rescue => detail
+ $stderr.puts "Could not remove request for %s: %s" % [host, detail]
+ end
+ }
+when :generate
+ # we need to generate a certificate for a host
+ unless ARGV.length > 0
+ $stderr.puts "You must specify hosts to generate certs for"
+ exit(84)
+ end
+ ARGV.each { |host|
+ puts "Generating certificate for %s" % host
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => host
+ )
+ cert.mkcsr
+ signedcert, cacert = ca.sign(cert.csr)
+
+ cert.cert = signedcert
+ cert.cacert = cacert
+ cert.write
+ }
+else
+ $stderr.puts "Invalid mode %s" % mode
+ exit(42)
+end
diff --git a/bin/puppetd b/bin/puppetd
new file mode 100755
index 000000000..7b5184948
--- /dev/null
+++ b/bin/puppetd
@@ -0,0 +1,102 @@
+#!/usr/bin/ruby
+
+#--------------------
+# the puppet client
+#
+# $Id$
+
+
+$:.unshift '../lib'
+
+require 'puppet'
+require 'puppet/server'
+require 'puppet/client'
+require 'getoptlong'
+
+result = GetoptLong.new(
+ [ "--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--ssldir", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--fqdn", "-f", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--server", "-s", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--secure", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--port", "-p", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--noinit", "-n", GetoptLong::NO_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+noinit = false
+server = "localhost"
+proto = "http"
+fqdn = nil
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ when "--verbose"
+ Puppet[:loglevel] = :info
+ when "--debug"
+ Puppet[:loglevel] = :debug
+ when "--ssldir"
+ Puppet[:ssldir] = arg
+ when "--secure"
+ proto = "https"
+ when "--noinit"
+ noinit = true
+ when "--fqdn"
+ fqdn = arg
+ when "--server"
+ server = arg
+ when "--port"
+ Puppet[:masterport] = arg
+ when "--logfile"
+ Puppet[:logfile] = arg
+ else
+ puts "Invalid option '#{opt}'"
+ exit(10)
+ end
+}
+
+bg = false
+
+unless Puppet[:loglevel] == :debug or Puppet[:loglevel] == :info
+ bg = true
+end
+
+args = {:Server => server}
+if fqdn
+ args[:FQDN] = fqdn
+end
+client = Puppet::Client.new(args)
+
+unless client.readcert
+ begin
+ while ! client.requestcert do
+ Puppet.notice "Could not request certificate"
+ sleep 5
+ end
+ rescue => detail
+ Puppet.err "Could not request certificate: %s" % detail.to_s
+ exit(23)
+ end
+end
+
+if bg
+ Puppet[:logdest] = Puppet[:logfile]
+ client.daemonize
+end
+#client.start
+client.getconfig
+
+#threads = []
+#threads << Thread.new {
+# trap(:INT) {
+# client.shutdown
+# }
+# client.start
+#}
+#
+#client.getconfig
diff --git a/bin/puppetdoc b/bin/puppetdoc
new file mode 100755
index 000000000..e64ca0624
--- /dev/null
+++ b/bin/puppetdoc
@@ -0,0 +1,129 @@
+#!/usr/local/bin/ruby
+#!/usr/bin/ruby -w
+
+#--------------------
+# produce documentation on all of the puppet types
+#
+# $Id$
+
+
+$:.unshift '../lib'
+
+require 'puppet'
+require 'getoptlong'
+
+def tab(num)
+ return $tab * num
+end
+
+result = GetoptLong.new(
+ [ "--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+debug = false
+logfile = false
+
+$tab = " "
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ when "--debug"
+ debug = true
+ when "--logfile"
+ logfile = arg
+ else
+ raise "Invalid option '#{opt}'"
+ end
+}
+
+puts %{
+==============
+Type Reference
+==============
+
+}
+
+types = {}
+Puppet::Type.eachtype { |type|
+ types[type.name] = type
+}
+puts %{
+---------------
+Meta-Parameters
+---------------
+
+}
+Puppet::Type.eachmetaparam { |param|
+ puts "- **" + param.to_s + "**"
+ puts tab(1) + Puppet::Type.metaparamdoc(param).gsub(/\n\s*/,' ')
+}
+
+puts %{
+-----
+Types
+-----
+
+- *namevar* is the parameter used to uniquely identify a type instance.
+ This is the parameter that gets assigned when a string is provided before
+ the colon in a type declaration.
+- *states* are the aspects of a type that can be changed.
+- *params* control how a type implements the state changes.
+
+
+}
+
+types.sort { |a,b|
+ a.to_s <=> b.to_s
+}.each { |name,type|
+ next if name == :puppet
+ next if name == :component
+
+ puts "
+
+----------------
+
+"
+
+ puts "
+%s
+%s" % [name, "=" * (name.to_s.length + 4)]
+ #String.new('n%s\n') % name.to_s
+ #puts "**" + type.doc.gsub(/\n\s*/, ' ') + "**\n\n"
+ puts type.doc.gsub(/\n\s*/, ' ') + "\n\n"
+ type.buildstatehash
+ #puts tab(1) + "* namevar: %s" % type.namevar
+ puts "%s States\n'''''''''''''''''''''''''''''''" % name.to_s.capitalize
+ type.validstates.sort { |a,b|
+ a.to_s <=> b.to_s
+ }.each { |sname,state|
+ puts "- **%s**" % sname
+ puts tab(1) + state.doc.gsub(/\n\s*/,' ')
+ }
+
+ puts "\n%s Parameters\n''''''''''''''''''''''''''''''" % name.to_s.capitalize
+ type.parameters.sort { |a,b|
+ a.to_s <=> b.to_s
+ }.each { |name,param|
+ print "- **%s**" % name
+ if type.namevar == name and name != :name
+ puts " (*namevar*)"
+ else
+ puts ""
+ end
+ puts tab(1) + type.paramdoc(name).gsub(/\n\s*/,' ')
+ }
+ puts "\n"
+}
+
+puts "
+
+----------------
+
+"
+
+puts "\n*This page autogenerated on %s*" % Time.now
diff --git a/bin/puppetmasterd b/bin/puppetmasterd
new file mode 100755
index 000000000..af5da3ee5
--- /dev/null
+++ b/bin/puppetmasterd
@@ -0,0 +1,89 @@
+#!/usr/bin/ruby -w
+
+#--------------------
+# the central puppet server
+#
+# $Id$
+
+require 'getoptlong'
+require 'puppet'
+require 'puppet/server'
+
+result = GetoptLong.new(
+ [ "--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--manifest", "-m", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--ssldir", "-s", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--noinit", "-n", GetoptLong::NO_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
+ [ "--noca", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+noinit = false
+ca = true
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ when "--verbose"
+ Puppet[:loglevel] = :info
+ when "--debug"
+ Puppet[:debug] = true
+ when "--noca"
+ ca = false
+ when "--ssldir"
+ Puppet[:ssldir] = arg
+ when "--manifest"
+ Puppet[:manifest] = arg
+ when "--noinit"
+ noinit = true
+ when "--logfile"
+ Puppet[:masterlog] = arg
+ else
+ raise "Invalid option '#{opt}'"
+ end
+}
+
+bg = false
+
+Puppet[:autosign] = true
+
+unless Puppet[:loglevel] == :debug or Puppet[:loglevel] == :info
+ bg = true
+end
+
+if bg
+ Puppet[:logdest] = Puppet[:masterlog]
+end
+
+begin
+ # use the default, um, everything
+ #server = Puppet::Server.new(:CA => ca)
+ server = Puppet::Server.new(
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Master => {},
+ :Status => {}
+ }
+ )
+rescue => detail
+ $stderr.puts detail
+ exit(1)
+end
+
+if bg
+ server.daemonize
+end
+
+trap(:INT) {
+ server.shutdown
+}
+begin
+ server.start
+rescue => detail
+ Puppet.err "Could not start puppetmaster: %s" % detail
+ exit(1)
+end
diff --git a/examples/code/allatonce b/examples/code/allatonce
new file mode 100644
index 000000000..2857b9b62
--- /dev/null
+++ b/examples/code/allatonce
@@ -0,0 +1,13 @@
+# $Id$
+
+define thingie {
+ file { "/tmp/classtest": create => true, mode => 755 }
+ #testing {}
+}
+
+class testing {
+ thingie { "componentname": }
+}
+
+#component {}
+testing { "testingname": }
diff --git a/examples/code/assignments b/examples/code/assignments
new file mode 100644
index 000000000..3edcef84e
--- /dev/null
+++ b/examples/code/assignments
@@ -0,0 +1,11 @@
+# $Id$
+
+$goodness = sunos
+
+$subvariable = $goodness
+
+$yayness = "this is a string of text"
+
+#$sleeper = service { sleeper:
+# running => "1"
+#}
diff --git a/examples/code/classing b/examples/code/classing
new file mode 100644
index 000000000..8f9477721
--- /dev/null
+++ b/examples/code/classing
@@ -0,0 +1,35 @@
+# $Id$
+
+# define the server as a class
+
+import "components"
+
+class base() {
+ # how do i handle components that don't take arguments? do they still
+ # require a name?
+ sudo { }
+}
+
+class server inherits base {
+ file { "/tmp/puppetfiletest":
+ create => true
+ }
+}
+
+class webserver(docroot) inherits server {
+ apache {
+ php => false,
+ docroot => $docroot,
+ user => http,
+ group => http
+ }
+}
+
+class sleepserver(path) inherits server {
+ sleeper {
+ path => $path,
+ mode => 644
+ }
+}
+
+# see 'nodes' for how to handle nodes
diff --git a/examples/code/components b/examples/code/components
new file mode 100644
index 000000000..efedf75e3
--- /dev/null
+++ b/examples/code/components
@@ -0,0 +1,73 @@
+# $Id$
+
+# i still have no 'require'-like functionality, and i should also
+# have 'recommend'-like functionality...
+define apache(php,docroot,user,group) {
+ package { apache:
+ version => "2.0.53"
+ }
+ service { apache:
+ running => true
+ }
+
+
+ # this definitely won't parse
+ if $php == "true" {
+ # this needs to do two things:
+ # - mark a dependency
+ # - cause this apache component to receive refresh events generated by php
+ #require("php")
+ $var = value
+ }
+
+ #file { "../examples/root/etc/configfile":
+ # owner => $user
+ #}
+}
+
+define sudo() {
+ package { sudo:
+ version => "1.6.8p7"
+ }
+ file { "/etc/sudoers":
+ owner => root,
+ group => root,
+ mode => "440"
+ }
+}
+
+define ssh {
+ package { ssh:
+ version => "3.4.4.4"
+ }
+ service { "sshd":
+ running => true
+ }
+}
+
+define sleeper(path,mode) {
+ Service {
+ path => "../examples/root/etc/init.d"
+ }
+
+ service { sleeper:
+ running => true,
+ path => "../examples/root/etc/init.d"
+ }
+ file { $path:
+ mode => $mode
+ }
+ $files = ["/tmp/testness","/tmp/funtest"]
+ file { $files:
+ create => true
+ }
+}
+
+#apache { "test":
+# php => false,
+# docroot => "/export/html",
+# user => "www-data",
+# group => "www-data"
+#}
+
+#ssh { "yucko":}
diff --git a/examples/code/execs b/examples/code/execs
new file mode 100644
index 000000000..44f133098
--- /dev/null
+++ b/examples/code/execs
@@ -0,0 +1,16 @@
+$path = "/usr/bin:/bin"
+
+exec { "mkdir -p /tmp/fakedir":
+ path => $path
+}
+
+exec { "rm -rf /tmp/fakedir":
+ path => $path
+}
+
+exec { "touch /this/directory/does/not/exist":
+ path => $path,
+ returns => 1
+}
+
+
diff --git a/examples/code/facts b/examples/code/facts
new file mode 100644
index 000000000..fcfec2c0d
--- /dev/null
+++ b/examples/code/facts
@@ -0,0 +1,22 @@
+# $Id$
+
+# DISABLED
+
+# facts are now added to the top scope (e.g., operatingsystem and macaddress)
+
+# these facts have to get defined by the server onto the client before
+# we can retrieve their values
+#fact { "videocard":
+# interpreter => "/bin/sh",
+# code => "lspci | grep VGA",
+# os => "Linux"
+#}
+$testing = "value"
+$operatingsystem = fact("operatingsystem")
+$fact = addfact(
+ name => "videocard",
+ interpreter => "/bin/sh",
+ code => "lspci | grep VGA",
+ os => "Linux"
+)
+$card = fact("videocard")
diff --git a/examples/code/failers/badclassnoparam b/examples/code/failers/badclassnoparam
new file mode 100644
index 000000000..a0397aacc
--- /dev/null
+++ b/examples/code/failers/badclassnoparam
@@ -0,0 +1,10 @@
+class comp() {
+ file { "/etc/passwd":
+ mode => 644
+ }
+}
+
+# this argument is invalid, thus we should get a falure
+comp {
+ fakearg => "yay"
+}
diff --git a/examples/code/failers/badclassparam b/examples/code/failers/badclassparam
new file mode 100644
index 000000000..4c9ff6199
--- /dev/null
+++ b/examples/code/failers/badclassparam
@@ -0,0 +1,10 @@
+class comp(arg1) {
+ file { "/etc/passwd":
+ mode => 644
+ }
+}
+
+# i've specified an arg but it's an invalid one
+comp {
+ fakearg => "yay"
+}
diff --git a/examples/code/failers/badcompnoparam b/examples/code/failers/badcompnoparam
new file mode 100644
index 000000000..fd25c9445
--- /dev/null
+++ b/examples/code/failers/badcompnoparam
@@ -0,0 +1,9 @@
+define comp() {
+ file { "/etc/passwd":
+ mode => 644
+ }
+}
+
+comp {
+ fakearg => "yay"
+}
diff --git a/examples/code/failers/badcompparam b/examples/code/failers/badcompparam
new file mode 100644
index 000000000..283d54014
--- /dev/null
+++ b/examples/code/failers/badcompparam
@@ -0,0 +1,9 @@
+define comp(arg1) {
+ file { "/etc/passwd":
+ mode => 644
+ }
+}
+
+comp {
+ fakearg => "yay"
+}
diff --git a/examples/code/failers/badtypeparam b/examples/code/failers/badtypeparam
new file mode 100644
index 000000000..4634f2052
--- /dev/null
+++ b/examples/code/failers/badtypeparam
@@ -0,0 +1,3 @@
+file { "/etc/passwd":
+ fakeparam => 644
+}
diff --git a/examples/code/file.bl b/examples/code/file.bl
new file mode 100644
index 000000000..41d80b53d
--- /dev/null
+++ b/examples/code/file.bl
@@ -0,0 +1,11 @@
+# $Id$
+
+file {
+ "/tmp/atest": create => true, mode => 755;
+ "/tmp/btest": create => true, mode => 755
+}
+
+file {
+ "/tmp/ctest": create => true;
+ "/tmp/dtest": create => true;
+}
diff --git a/examples/code/filedefaults b/examples/code/filedefaults
new file mode 100644
index 000000000..cb005c093
--- /dev/null
+++ b/examples/code/filedefaults
@@ -0,0 +1,10 @@
+# $Id$
+
+File {
+ mode => 755,
+ recurse => true
+}
+
+file { "/tmp/filedefaultstest":
+ create => true
+}
diff --git a/examples/code/fileparsing b/examples/code/fileparsing
new file mode 100644
index 000000000..f9766b9f6
--- /dev/null
+++ b/examples/code/fileparsing
@@ -0,0 +1,116 @@
+# $Id$
+
+# this will eventually parse different config files
+
+# this creates the 'passwd' type, but it does not create any instances
+filetype { "passwd":
+ linesplit => "\n",
+ escapednewlines => false
+}
+
+
+# this creates the 'PasswdUser' type, but again, no instances
+filerecord { "user":
+ filetype => passwd,
+ fields => [name, password, uid, gid, gcos, home, shell],
+ namevar => name,
+ splitchar => ":"
+
+}
+
+filetype { ini:
+ linesplit => "\n\n"
+}
+
+# ini files are different because we don't really care about validating fields
+# or at least, we can't do it for most files...
+filerecord { "initrecord":
+ filetype => ini,
+ fields => [name, password, uid, gid, gcos, home, shell],
+ namevar => name,
+ splitchar => ":"
+
+}
+
+# this won't work for multiple record types, will it?
+# or at least, it requires that we specify multiple times
+# ah, and it doesn't specify which of the available record types
+# it works for...
+passwd { user:
+ complete => true, # manage the whole file
+ path => "/etc/passwd"
+}
+
+user { yaytest:
+ password => x,
+ uid => 10000,
+ gid => 10000,
+ home => "/home/yaytest",
+ gcos => "The Yaytest",
+ shell => "/bin/sh"
+}
+ # there seems to be an intrinsic problem here -- i've got subtypes that only
+ # make sense when an instance of the super type already exists, and i need
+ # to associate the instances of the subtype with the instances of the supertype
+ # even if i created the parsers manually, I'd have the same problem
+
+# this is the crux of it -- i want to be able to say 'user' here without having
+# to specify the file, which leaves two options:
+# 1) associate the record type with a filetype instance (BAD)
+# 2) once the filetype and record type are created, have another command
+# that specifically creates a filetype instance and gives names for instances
+# of its record types
+
+define syslog {
+
+ # create a new type, with all defaults
+ filetype { "syslog":
+ escapednewlines => true
+ }
+
+ filerecord { "log":
+ filetype => syslog,
+ regex => "^([^#\s]+)\s+(\S+)$",
+ joinchar => "\t",
+ fields => [logs, dest]
+ }
+
+ # these two should just be supported within the filetypes
+ filerecord { "comment":
+ filetype => syslog,
+ regex => "^(#.*)$",
+ joinchar => "s",
+ fields => [comment]
+ }
+
+ filerecord { "blank":
+ filetype => syslog,
+ regex => "^(\s*)$",
+ joinchar => "s",
+ fields => blank
+ }
+}
+
+define cron {
+ filetype { "usercrontab":
+ }
+
+ # this won't actually work, of course
+ filerecord { "cronjob":
+ filetype => crontab,
+ regex => "^([^#\s]+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$",
+ joinchar => " ",
+ fields => [minute, hour, day, month, weekday, command],
+ defaults => ["*", "*", "*", "*", "*", nil],
+ optional => [minute, hour, day, month, weekday]
+ }
+
+ crontab { "luke":
+ }
+}
+
+# XXX this doesn't work in the slightest
+define crontab(name,path) {
+ usercrontab { "${path}/${name}":
+ }
+}
diff --git a/examples/code/filerecursion b/examples/code/filerecursion
new file mode 100644
index 000000000..b7d8278c2
--- /dev/null
+++ b/examples/code/filerecursion
@@ -0,0 +1,15 @@
+# $Id$
+
+file { "/tmp/dirtest/b/a":
+ mode => 755,
+}
+
+file { "/tmp/dirtest":
+ mode => 755,
+ recurse => true,
+}
+
+file { "/tmp/dirtest/b/b":
+ mode => 644,
+}
+
diff --git a/examples/code/functions b/examples/code/functions
new file mode 100644
index 000000000..8e95c3a72
--- /dev/null
+++ b/examples/code/functions
@@ -0,0 +1,3 @@
+# $Id$
+
+$yaytest = fact("operatingsystem")
diff --git a/examples/code/groups b/examples/code/groups
new file mode 100644
index 000000000..35505a2eb
--- /dev/null
+++ b/examples/code/groups
@@ -0,0 +1,7 @@
+# $Id$
+
+# there need to be two forms of adding to groups:
+# add the current host to a group, and add a list of hosts to a
+# group by name
+
+$group = "crap"
diff --git a/examples/code/head b/examples/code/head
new file mode 100644
index 000000000..0b7a6c288
--- /dev/null
+++ b/examples/code/head
@@ -0,0 +1,30 @@
+# $Id$
+
+# this file is responsible for importing all of the files we want to actually test
+
+# these are all of the simple tests
+import "simpletests"
+import "assignments"
+import "selectors"
+import "iftest"
+import "importing"
+import "execs"
+import "filedefaults"
+
+# facts are now imported into the top of the namespace
+#import "facts"
+
+# obsoleted
+#import "functions"
+
+# files we no longer need to import directly, or at all in some cases
+#import "one"
+#import "classing"
+#import "components"
+#import "file.bl"
+#import "fileparsing.disabled"
+#import "groups"
+
+# this imports the more complex files
+import "allatonce" # imports classing and components
+import "nodes" # imports classing and components
diff --git a/examples/code/iftest b/examples/code/iftest
new file mode 100644
index 000000000..bc9d9cff1
--- /dev/null
+++ b/examples/code/iftest
@@ -0,0 +1,21 @@
+# $Id$
+$variable = "value"
+
+$cooltest = $variable ? {
+ other => yayness,
+ value => goodtest
+}
+
+if $variable == "other" {
+ $cooltest = "yayness"
+ $platform = $operatingsystem
+} elsif $variable == "value" {
+ $cooltest = "goodtest"
+ $platform = $operatingsystem
+} else {
+ $goodness = $operatingsystemrelease
+}
+
+if "value" == "value" {
+ $booyah = "value"
+}
diff --git a/examples/code/importing b/examples/code/importing
new file mode 100644
index 000000000..f02604109
--- /dev/null
+++ b/examples/code/importing
@@ -0,0 +1,8 @@
+# $Id$
+
+#import "groups"
+# testing import loops
+import "importing"
+
+$name = "value"
+$system = $operatingsystem
diff --git a/examples/code/nodes b/examples/code/nodes
new file mode 100644
index 000000000..42488e689
--- /dev/null
+++ b/examples/code/nodes
@@ -0,0 +1,20 @@
+# $Id$
+
+# define nodes
+
+#service.setpath("../examples/root/etc/init.d")
+
+Service {
+ path => "../examples/root/etc/init.d"
+}
+
+import "classing"
+
+sleepserver {
+ path => $operatingsystem ? {
+ sunos => "../examples/root/etc/configfile",
+ hpux => "../examples/other/etc/configfile",
+ default => "../examples/root/etc/configfile"
+ },
+ schedule => true
+}
diff --git a/examples/code/one b/examples/code/one
new file mode 100644
index 000000000..452d32f3e
--- /dev/null
+++ b/examples/code/one
@@ -0,0 +1,8 @@
+# $Id$
+
+# this service doesn't actually exist, so we noop it
+# and this way, we can test noop :)
+service { "funtest":
+ running => "0",
+ noop => true
+}
diff --git a/examples/code/relationships b/examples/code/relationships
new file mode 100644
index 000000000..f2319d5e4
--- /dev/null
+++ b/examples/code/relationships
@@ -0,0 +1,34 @@
+# $Id$
+
+#service.setpath("../examples/root/etc/init.d")
+#puppet.statefile("/tmp/puppetstate")
+$path = "../examples/root/etc/configfile"
+ path => "../examples/root/etc/init.d"
+
+
+define files {
+ file { "/tmp/yaytest":
+ create => true,
+ mode => 755
+ }
+ file { "/tmp/exists":
+ checksum => md5
+ }
+}
+
+define sleeper {
+ file { $path:
+ mode => 755
+ }
+ service { sleeper:
+ path => "../examples/root/etc/init.d",
+ running => 1
+ }
+}
+
+files { }
+
+sleeper {
+ require => files["yay"],
+ schedule => true
+}
diff --git a/examples/code/selectors b/examples/code/selectors
new file mode 100644
index 000000000..b1f1c63ec
--- /dev/null
+++ b/examples/code/selectors
@@ -0,0 +1,28 @@
+# $Id$
+#
+
+$platform = sunos
+
+$funtest = $platform ? {
+ sunos => yayness,
+ aix => goodness,
+ default => badness
+}
+
+# this is a comment
+
+$filename = "/tmp/yayness"
+
+$sleeper = file { $filename:
+ mode => $platform ? {
+ sunos => 644,
+ default => 755
+ },
+ create => $platform ? sunos => true
+}
+
+# i guess it has to be solved this way...
+
+$platform ? sunos => file { $filename:
+ mode => 644
+}
diff --git a/examples/code/simpletests b/examples/code/simpletests
new file mode 100644
index 000000000..6a2d5c5ff
--- /dev/null
+++ b/examples/code/simpletests
@@ -0,0 +1,11 @@
+# $Id$
+
+file {
+ "/tmp/atest": create => true;
+ "/tmp/btest": create => true
+}
+
+file {
+ "/tmp/ctest": create => true;
+ "/tmp/dtest": create => true;
+}
diff --git a/examples/code/snippets/argumentdefaults b/examples/code/snippets/argumentdefaults
new file mode 100644
index 000000000..b4081e9b0
--- /dev/null
+++ b/examples/code/snippets/argumentdefaults
@@ -0,0 +1,14 @@
+# $Id$
+
+define testargs(file, mode = 755) {
+ file { $file: create => true, mode => $mode }
+}
+
+testargs { "testingname":
+ file => "/tmp/argumenttest1"
+}
+
+testargs { "testingother":
+ file => "/tmp/argumenttest2",
+ mode => 644
+}
diff --git a/examples/code/snippets/classpathtest b/examples/code/snippets/classpathtest
new file mode 100644
index 000000000..609b3bef5
--- /dev/null
+++ b/examples/code/snippets/classpathtest
@@ -0,0 +1,13 @@
+# $Id$
+
+define component {
+ file { "/tmp/classtest": create => true, mode => 755 }
+ #testing {}
+}
+
+class testing {
+ component { "componentname": }
+}
+
+#component {}
+testing { "testingname": }
diff --git a/examples/code/snippets/dirchmod b/examples/code/snippets/dirchmod
new file mode 100644
index 000000000..0a8268fa8
--- /dev/null
+++ b/examples/code/snippets/dirchmod
@@ -0,0 +1,19 @@
+# $Id$
+
+file {
+ "/tmp/dirchmodtesta": create => directory;
+ "/tmp/dirchmodtesta/testing": create => true
+}
+
+file { "/tmp/dirchmodtesta":
+ mode => 644, recurse => true
+}
+
+file {
+ "/tmp/dirchmodtestb": create => directory;
+ "/tmp/dirchmodtestb/testing": create => true
+}
+
+file { "/tmp/dirchmodtestb":
+ mode => 600, recurse => true
+}
diff --git a/examples/code/snippets/filecreate b/examples/code/snippets/filecreate
new file mode 100644
index 000000000..7a4cdbb1e
--- /dev/null
+++ b/examples/code/snippets/filecreate
@@ -0,0 +1,11 @@
+# $Id$
+
+file {
+ "/tmp/createatest": create => true, mode => 755;
+ "/tmp/createbtest": create => true, mode => 755
+}
+
+file {
+ "/tmp/createctest": create => true;
+ "/tmp/createdtest": create => true;
+}
diff --git a/examples/code/snippets/simpledefaults b/examples/code/snippets/simpledefaults
new file mode 100644
index 000000000..a8f6190b1
--- /dev/null
+++ b/examples/code/snippets/simpledefaults
@@ -0,0 +1,5 @@
+# $Id$
+
+File { mode => 755 }
+
+file { "/tmp/defaulttest": create => true }
diff --git a/examples/code/snippets/simpleselector b/examples/code/snippets/simpleselector
new file mode 100644
index 000000000..52a06c773
--- /dev/null
+++ b/examples/code/snippets/simpleselector
@@ -0,0 +1,38 @@
+# $Id$
+
+$var = "value"
+
+file { "/tmp/snippetselectatest":
+ create => true,
+ mode => $var ? {
+ nottrue => 641,
+ value => 755
+ }
+}
+
+file { "/tmp/snippetselectbtest":
+ create => true,
+ mode => $var ? {
+ nottrue => 644,
+ default => 755
+ }
+}
+
+$othervar = "complex value"
+
+file { "/tmp/snippetselectctest":
+ create => true,
+ mode => $othervar ? {
+ "complex value" => 755,
+ default => 644
+ }
+}
+$anothervar = Yayness
+
+file { "/tmp/snippetselectdtest":
+ create => true,
+ mode => $anothervar ? {
+ Yayness => 755,
+ default => 644
+ }
+}
diff --git a/examples/code/svncommit b/examples/code/svncommit
new file mode 100644
index 000000000..350cd8580
--- /dev/null
+++ b/examples/code/svncommit
@@ -0,0 +1,13 @@
+$path = "/usr/bin:/bin"
+
+file { "/tmp/svntests":
+ recurse => true,
+ checksum => md5
+}
+
+exec { "echo 'files have been updated'":
+ cwd => "/tmp/svntests",
+ refreshonly => true,
+ require => file["/tmp/svntests"],
+ path => $path
+}
diff --git a/examples/root/bin/sleeper b/examples/root/bin/sleeper
new file mode 100755
index 000000000..ca38c3e53
--- /dev/null
+++ b/examples/root/bin/sleeper
@@ -0,0 +1,69 @@
+#!/usr/bin/ruby -w
+
+###
+# sleep indefinitely as a debug
+
+require 'getoptlong'
+
+#-----------------------------------------------------------------
+def daemonize
+ outfile = "/tmp/sleeperout"
+ if pid = fork()
+ Process.detach(pid)
+ sleep 1
+ # verify that we didn't have any problems starting the daemon
+ if FileTest.exists?(outfile)
+ $stderr.puts "Sleeper failed: %s" % File.read(outfile)
+ File.unlink(outfile)
+ exit(14)
+ else
+ exit(0)
+ end
+ end
+ Process.setsid
+ Dir.chdir("/")
+ begin
+ $stdin.reopen "/dev/null"
+ $stdout.reopen "/dev/null", "a"
+ $stderr.reopen $stdin
+ rescue => detail
+ File.open(outfile, "w") { |f|
+ f.puts detail
+ }
+ exit(12)
+ end
+end
+#-----------------------------------------------------------------
+
+debug = false
+
+result = GetoptLong.new(
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ]
+)
+
+result.each { |opt,arg|
+ case opt
+ when "--help"
+ puts "There is no help yet"
+ exit
+ when "--debug"
+ debug = true
+ else
+ raise "Invalid option '#{opt}'"
+ end
+}
+
+trap(:INT) {
+ exit
+}
+
+unless debug
+ daemonize()
+end
+
+var = true
+
+while var do
+ sleep 600
+end
diff --git a/examples/root/etc/configfile b/examples/root/etc/configfile
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/root/etc/configfile
diff --git a/examples/root/etc/debian-passwd b/examples/root/etc/debian-passwd
new file mode 100644
index 000000000..59cdf4acf
--- /dev/null
+++ b/examples/root/etc/debian-passwd
@@ -0,0 +1,29 @@
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/bin/sh
+bin:x:2:2:bin:/bin:/bin/sh
+sys:x:3:3:sys:/dev:/bin/sh
+sync:x:4:65534:sync:/bin:/bin/sync
+games:x:5:60:games:/usr/games:/bin/sh
+man:x:6:12:man:/var/cache/man:/bin/sh
+lp:x:7:7:lp:/var/spool/lpd:/bin/sh
+mail:x:8:8:mail:/var/mail:/bin/sh
+news:x:9:9:news:/var/spool/news:/bin/sh
+uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
+proxy:x:13:13:proxy:/bin:/bin/sh
+postgres:x:31:32:postgres:/var/lib/postgres:/bin/sh
+www-data:x:33:33:www-data:/var/www:/bin/sh
+backup:x:34:34:backup:/var/backups:/bin/sh
+operator:x:37:37:Operator:/var:/bin/sh
+list:x:38:38:Mailing List Manager:/var/list:/bin/sh
+irc:x:39:39:ircd:/var/run/ircd:/bin/sh
+gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
+nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
+sshd:x:102:65534::/var/run/sshd:/bin/false
+gdm:x:101:101:Gnome Display Manager:/var/lib/gdm:/bin/false
+telnetd:x:103:103::/usr/lib/telnetd:/bin/false
+nagios:x:1000:1001::/home/nagios:
+messagebus:x:104:107::/var/run/dbus:/bin/false
+saned:x:109:109::/home/saned:/bin/false
+ganglia:x:105:110:Ganglia Monitor:/var/lib/ganglia:/bin/false
+zope:x:106:111::/var/lib/zope2.7/var:/bin/false
+fbgetty:x:112:112::/home/fbgetty:/bin/false
diff --git a/examples/root/etc/debian-syslog.conf b/examples/root/etc/debian-syslog.conf
new file mode 100644
index 000000000..8f2925960
--- /dev/null
+++ b/examples/root/etc/debian-syslog.conf
@@ -0,0 +1,71 @@
+# /etc/syslog.conf Configuration file for syslogd.
+#
+# For more information see syslog.conf(5)
+# manpage.
+
+#
+# First some standard logfiles. Log by facility.
+#
+
+auth,authpriv.* /var/log/auth.log
+*.*;auth,authpriv.none -/var/log/syslog
+#cron.* /var/log/cron.log
+daemon.* -/var/log/daemon.log
+kern.* -/var/log/kern.log
+lpr.* -/var/log/lpr.log
+mail.* -/var/log/mail.log
+user.* -/var/log/user.log
+uucp.* /var/log/uucp.log
+
+#
+# Logging for the mail system. Split it up so that
+# it is easy to write scripts to parse these files.
+#
+mail.info -/var/log/mail.info
+mail.warn -/var/log/mail.warn
+mail.err /var/log/mail.err
+
+# Logging for INN news system
+#
+news.crit /var/log/news/news.crit
+news.err /var/log/news/news.err
+news.notice -/var/log/news/news.notice
+
+#
+# Some `catch-all' logfiles.
+#
+*.=debug;\
+ auth,authpriv.none;\
+ news.none;mail.none -/var/log/debug
+*.=info;*.=notice;*.=warn;\
+ auth,authpriv.none;\
+ cron,daemon.none;\
+ mail,news.none -/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg *
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+# news.=crit;news.=err;news.=notice;\
+# *.=debug;*.=info;\
+# *.=notice;*.=warn /dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
+# you must invoke `xconsole' with the `-file' option:
+#
+# $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+# busy site..
+#
+daemon.*;mail.*;\
+ news.crit;news.err;news.notice;\
+ *.=debug;*.=info;\
+ *.=notice;*.=warn |/dev/xconsole
+
diff --git a/examples/root/etc/init.d/sleeper b/examples/root/etc/init.d/sleeper
new file mode 100755
index 000000000..e7f5d3e30
--- /dev/null
+++ b/examples/root/etc/init.d/sleeper
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+# $Id$
+
+script=$0
+path=`echo $script | sed 's/etc..*/bin/'`
+
+PATH=$PATH:$path
+
+ps=`facter ps | cut -d ' ' -f3-`
+
+if [ -z "$ps" ]; then
+ ps="ps -ef"
+fi
+
+function start
+{
+ sleeper
+}
+
+function stop
+{
+ #if [ -n `which pgrep` ]; then
+ # pid=`pgrep sleeper`
+ #else
+ pid=`$ps | grep -v grep | grep sleeper | grep ruby | awk '{print $2}'`
+ #fi
+ if [ -n "$pid" ]; then
+ kill $pid
+ fi
+}
+
+function status
+{
+ #if [ -n `which pgrep` ]; then
+ # cmd="pgrep sleeper"
+ #else
+ #cmd="$ps | grep -v grep | grep sleeper | grep ruby | awk '{print $2}'"
+ #fi
+ #$cmd
+ $ps | grep -v grep | grep sleeper | grep ruby
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop; start
+ ;;
+ status)
+ output=`status`
+ #status
+ exit $?
+ ;;
+ *)
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/examples/root/etc/otherfile b/examples/root/etc/otherfile
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/root/etc/otherfile
diff --git a/lib/puppet/ca.rb b/lib/puppet/ca.rb
new file mode 100644
index 000000000..9a6f8aeda
--- /dev/null
+++ b/lib/puppet/ca.rb
@@ -0,0 +1,155 @@
+require 'openssl'
+require 'puppet'
+require 'puppet/sslcertificates'
+require 'xmlrpc/server'
+
+# Much of this was taken from QuickCert:
+# http://segment7.net/projects/ruby/QuickCert/
+
+module Puppet
+ class CAError < Puppet::Error; end
+ class CA
+ attr_reader :ca
+
+ def self.interface
+ XMLRPC::Service::Interface.new("puppetca") { |iface|
+ iface.add_method("array getcert(csr)")
+ }
+ end
+
+ def autosign?(hostname)
+ # simple values are easy
+ asign = Puppet[:autosign]
+ if asign == true or asign == false
+ return asign
+ end
+
+ # we only otherwise know how to handle files
+ unless asign =~ /^\//
+ raise Puppet::Error, "Invalid autosign value %s" %
+ asign
+ end
+
+ unless FileTest.exists?(asign)
+ Puppet.warning "Autosign is enabled but %s is missing" % asign
+ return false
+ end
+ File.open(asign) { |f|
+ f.each { |line|
+ line.chomp!
+ if line =~ /^[.\w-]+$/ and line == hostname
+ Puppet.info "%s exactly matched %s" % [hostname, line]
+ return true
+ else
+ begin
+ rx = Regexp.new(line)
+ rescue => detail
+ Puppet.err(
+ "Could not create regexp out of autosign line %s: %s" %
+ [line, detail]
+ )
+ next
+ end
+
+ if hostname =~ rx
+ Puppet.info "%s matched %s" % [hostname, line]
+ return true
+ end
+ end
+ }
+ }
+
+ return false
+ end
+
+ def initialize(hash = {})
+ @ca = Puppet::SSLCertificates::CA.new()
+ end
+
+ # our client sends us a csr, and we either store it for later signing,
+ # or we sign it right away
+ def getcert(csrtext, request = nil)
+ # okay, i need to retrieve the hostname from the csr, and then
+ # verify that i get the same hostname through reverse lookup or
+ # something
+
+ Puppet.info "Someone's trying for a cert"
+ csr = OpenSSL::X509::Request.new(csrtext)
+
+ subject = csr.subject
+
+ nameary = subject.to_a.find { |ary|
+ ary[0] == "CN"
+ }
+
+ if nameary.nil?
+ Puppet.err "Invalid certificate request"
+ return "invalid"
+ end
+
+ hostname = nameary[1]
+
+ unless @ca
+ Puppet.notice "Host %s asked for signing from non-CA master" % hostname
+ return ""
+ end
+
+ # okay, we're now going to store the public key if we don't already
+ # have it
+ public_key = csr.public_key
+ unless FileTest.directory?(Puppet[:publickeydir])
+ Puppet.recmkdir(Puppet[:publickeydir])
+ end
+ pkeyfile = File.join(Puppet[:publickeydir], [hostname, "pem"].join('.'))
+
+ if FileTest.exists?(pkeyfile)
+ currentkey = File.open(pkeyfile) { |k| k.read }
+ unless currentkey == public_key.to_s
+ raise Puppet::Error, "public keys for %s differ" % hostname
+ end
+ else
+ File.open(pkeyfile, "w", 0644) { |f|
+ f.print public_key.to_s
+ }
+ end
+ unless FileTest.directory?(Puppet[:certdir])
+ Puppet.recmkdir(Puppet[:certdir], 0770)
+ end
+ certfile = File.join(Puppet[:certdir], [hostname, "pem"].join("."))
+
+ #puts hostname
+ #puts certfile
+
+ unless FileTest.directory?(Puppet[:csrdir])
+ Puppet.recmkdir(Puppet[:csrdir], 0770)
+ end
+ # first check to see if we already have a signed cert for the host
+ cert, cacert = ca.getclientcert(hostname)
+ if cert and cacert
+ Puppet.info "Retrieving existing certificate for %s" % hostname
+ Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class]
+ return [cert.to_pem, cacert.to_pem]
+ elsif @ca
+ if self.autosign?(hostname)
+ # okay, we don't have a signed cert
+ # if we're a CA and autosign is turned on, then go ahead and sign
+ # the csr and return the results
+ Puppet.info "Signing certificate for %s" % hostname
+ cert, cacert = @ca.sign(csr)
+ Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class]
+ return [cert.to_pem, cacert.to_pem]
+ else # just write out the csr for later signing
+ if @ca.getclientcsr(hostname)
+ Puppet.info "Not replacing existing request from %s" % hostname
+ else
+ Puppet.info "Storing certificate request for %s" % hostname
+ @ca.storeclientcsr(csr)
+ end
+ return ["", ""]
+ end
+ else
+ raise "huh?"
+ end
+ end
+ end
+end
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb
index 6f9b21cbf..1282a5900 100644
--- a/lib/puppet/client.rb
+++ b/lib/puppet/client.rb
@@ -5,6 +5,7 @@
require 'puppet'
require 'puppet/sslcertificates'
require 'puppet/type'
+require 'puppet/server'
require 'facter'
require 'openssl'
require 'puppet/transaction'
@@ -32,22 +33,23 @@ module Puppet
class NetworkClient < XMLRPC::Client
#include Puppet::Daemon
- @@methods = {
- "puppetmaster" => [ :getconfig ],
- "puppetca" => [ :getcert ]
- }
+ @@handlers = [Puppet::FileServer, Puppet::CA, Puppet::Master]
+
+ @@handlers.each { |handler|
+ interface = handler.interface
+ namespace = interface.prefix
- @@methods.each { |namespace, methods|
- methods.each { |method|
+ interface.methods.each { |ary|
+ method = ary[0]
self.send(:define_method,method) { |*args|
- Puppet.info "peer cert is %s" % @http.peer_cert
+ #Puppet.info "peer cert is %s" % @http.peer_cert
#Puppet.info "cert is %s" % @http.cert
begin
call("%s.%s" % [namespace, method.to_s],*args)
rescue XMLRPC::FaultException => detail
Puppet.err "XML Could not call %s.%s: %s" %
[namespace, method, detail.faultString]
- raise NetworkClientError.new,
+ raise NetworkClientError,
"XMLRPC Error: %s" % detail.faultString
rescue => detail
Puppet.err "Could not call %s.%s: %s" %
diff --git a/lib/puppet/fileserver.rb b/lib/puppet/fileserver.rb
new file mode 100755
index 000000000..f04c337af
--- /dev/null
+++ b/lib/puppet/fileserver.rb
@@ -0,0 +1,260 @@
+require 'puppet'
+require 'cgi'
+
+module Puppet
+ class FileServerError < Puppet::Error; end
+ class FileServer
+ attr_accessor :local
+
+ #CHECKPARAMS = %w{checksum type mode owner group}
+ CHECKPARAMS = [:mode, :type, :owner, :group, :checksum]
+
+ def self.interface
+ XMLRPC::Service::Interface.new("fileserver") { |iface|
+ iface.add_method("string describe(string)")
+ iface.add_method("string list(string, boolean)")
+ iface.add_method("string retrieve(string)")
+ }
+ end
+
+ def check(dir)
+ unless FileTest.exists?(dir)
+ Puppet.notice "File source %s does not exist" % dir
+ return nil
+ end
+
+ obj = nil
+ unless obj = Puppet::Type::PFile[dir]
+ obj = Puppet::Type::PFile.new(
+ :name => dir,
+ :check => CHECKPARAMS
+ )
+ end
+ # we should really have a timeout here -- we don't
+ # want to actually check on every connection, maybe no more
+ # than every 60 seconds or something
+ #@files[mount].evaluate
+ obj.evaluate
+
+ return obj
+ end
+
+ def describe(file)
+ mount, path = splitpath(file)
+
+ subdir = nil
+ unless subdir = subdir(mount, path)
+ Puppet.notice "Could not find subdirectory %s" %
+ "//%s/%s" % [mount, path]
+ return ""
+ end
+
+ obj = nil
+ unless obj = self.check(subdir)
+ return ""
+ end
+
+ desc = []
+ CHECKPARAMS.each { |check|
+ if state = obj.state(check)
+ unless state.is
+ Puppet.notice "Manually retrieving info for %s" % check
+ state.retrieve
+ end
+ desc << state.is
+ else
+ if check == "checksum" and obj.state(:type).is == "file"
+ Puppet.notice "File %s does not have data for %s" %
+ [obj.name, check]
+ end
+ desc << nil
+ end
+ }
+
+ return desc.join("\t")
+ end
+
+ def initialize(hash = {})
+ @mounts = {}
+ @files = {}
+
+ if hash[:Local]
+ @local = hash[:Local]
+ else
+ @local = false
+ end
+ end
+
+ def list(dir, recurse = false, sum = "md5")
+ mount, path = splitpath(dir)
+
+ subdir = nil
+ unless subdir = subdir(mount, path)
+ Puppet.notice "Could not find subdirectory %s" %
+ "//%s/%s" % [mount, path]
+ return ""
+ end
+
+ obj = nil
+ unless FileTest.exists?(subdir)
+ return ""
+ end
+
+ #rmdir = File.dirname(File.join(@mounts[mount], path))
+ rmdir = nameswap(dir, mount)
+ desc = self.reclist(rmdir, subdir, recurse)
+
+ if desc.length == 0
+ Puppet.notice "Got no information on //%s/%s" %
+ [mount, path]
+ return ""
+ end
+
+ desc.collect { |sub|
+ sub.join("\t")
+ }.join("\n")
+ end
+
+ def mount(dir, name)
+ if @mounts.include?(name)
+ if @mounts[name] != dir
+ raise FileServerError, "%s is already mounted at %s" %
+ [@mounts[name], name]
+ else
+ # it's already mounted; no problem
+ return
+ end
+ end
+
+ unless name =~ %r{^\w+$}
+ raise FileServerError, "Invalid name format '%s'" % name
+ end
+
+ unless FileTest.exists?(dir)
+ raise FileServerError, "%s does not exist" % dir
+ end
+
+ if FileTest.directory?(dir)
+ if FileTest.readable?(dir)
+ Puppet.info "Mounting %s at %s" % [dir, name]
+ @mounts[name] = dir
+ else
+ raise FileServerError, "%s is not readable" % dir
+ end
+ else
+ raise FileServerError, "%s is not a directory" % dir
+ end
+ end
+
+ # recursive listing function
+ def reclist(root, path, recurse)
+ #desc = [obj.name.sub(%r{#{root}/?}, '')]
+ name = path.sub(root, '')
+ if name == ""
+ name = "/"
+ end
+
+ if name == path
+ raise Puppet::FileServerError, "Could not match %s in %s" %
+ [root, path]
+ end
+
+ desc = [name]
+ ftype = File.stat(path).ftype
+
+ desc << ftype
+ if recurse.is_a?(Integer)
+ recurse -= 1
+ end
+
+ ary = [desc]
+ if recurse == true or (recurse.is_a?(Integer) and recurse > -1)
+ if ftype == "directory"
+ Dir.entries(path).each { |child|
+ next if child =~ /^\.\.?$/
+ self.reclist(root, File.join(path, child), recurse).each { |cobj|
+ ary << cobj
+ }
+ }
+ end
+ end
+
+ return ary.reject { |c| c.nil? }
+ end
+
+ def retrieve(file)
+ mount, path = splitpath(file)
+
+ unless (@mounts.include?(mount))
+ # FIXME I really need some better way to pass and handle xmlrpc errors
+ raise FileServerError, "%s not mounted" % mount
+ end
+
+ fpath = nil
+ if path
+ fpath = File.join(@mounts[mount], path)
+ else
+ fpath = @mounts[mount]
+ end
+
+ unless FileTest.exists?(fpath)
+ return ""
+ end
+
+ str = File.read(fpath)
+
+ if @local
+ return str
+ else
+ return CGI.escape(str)
+ end
+ end
+
+ private
+
+ def nameswap(name, mount)
+ name.sub(/\/#{mount}/, @mounts[mount]).gsub(%r{//}, '/').sub(
+ %r{/$}, ''
+ )
+ #Puppet.info "Swapped %s to %s" % [name, newname]
+ #newname
+ end
+
+ def splitpath(dir)
+ # the dir is based on one of the mounts
+ # so first retrieve the mount path
+ mount = nil
+ path = nil
+ if dir =~ %r{/(\w+)/?}
+ mount = $1
+ path = dir.sub(%r{/#{mount}/?}, '')
+
+ unless @mounts.include?(mount)
+ raise FileServerError, "%s not mounted" % mount
+ end
+ else
+ raise FileServerError, "Invalid path '%s'" % dir
+ end
+
+ if path == ""
+ path = nil
+ end
+ return mount, path
+ end
+
+ def subdir(mount, dir)
+ basedir = @mounts[mount]
+
+ dirname = nil
+ if dir
+ dirname = File.join(basedir, dir.split("/").join(File::SEPARATOR))
+ else
+ dirname = basedir
+ end
+
+ return dirname
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/master.rb b/lib/puppet/master.rb
new file mode 100644
index 000000000..bbfeaf557
--- /dev/null
+++ b/lib/puppet/master.rb
@@ -0,0 +1,88 @@
+require 'openssl'
+require 'puppet'
+require 'puppet/parser/interpreter'
+require 'puppet/sslcertificates'
+require 'xmlrpc/server'
+
+module Puppet
+ class MasterError < Puppet::Error; end
+ class Master
+ attr_accessor :ast, :local
+ attr_reader :ca
+
+ def self.interface
+ XMLRPC::Service::Interface.new("puppetmaster") { |iface|
+ iface.add_method("string getconfig(string)")
+ }
+ end
+
+ def initialize(hash = {})
+
+ # build our AST
+ @file = hash[:File] || Puppet[:manifest]
+ @parser = Puppet::Parser::Parser.new()
+ @parser.file = @file
+ @ast = @parser.parse
+ hash.delete(:File)
+
+ if hash[:Local]
+ @local = hash[:Local]
+ else
+ @local = false
+ end
+
+ if hash.include?(:CA) and hash[:CA]
+ @ca = Puppet::SSLCertificates::CA.new()
+ else
+ @ca = nil
+ end
+ end
+
+ def getconfig(facts, request = nil)
+ if request
+ #Puppet.warning request.inspect
+ end
+ if @local
+ # we don't need to do anything, since we should already
+ # have raw objects
+ Puppet.debug "Our client is local"
+ else
+ Puppet.debug "Our client is remote"
+
+ # XXX this should definitely be done in the protocol, somehow
+ begin
+ facts = Marshal::load(CGI.unescape(facts))
+ rescue => detail
+ puts "AAAAA"
+ puts detail
+ exit
+ end
+ end
+
+ Puppet.debug("Creating interpreter")
+
+ begin
+ interpreter = Puppet::Parser::Interpreter.new(
+ :ast => @ast,
+ :facts => facts
+ )
+ rescue => detail
+ return detail.to_s
+ end
+
+ Puppet.debug("Running interpreter")
+ begin
+ retobjects = interpreter.run()
+ rescue => detail
+ Puppet.err detail.to_s
+ return ""
+ end
+
+ if @local
+ return retobjects
+ else
+ return CGI.escape(Marshal::dump(retobjects))
+ end
+ end
+ end
+end
diff --git a/lib/puppet/metric.rb b/lib/puppet/metric.rb
index 9a4fb46da..f2d754a57 100644
--- a/lib/puppet/metric.rb
+++ b/lib/puppet/metric.rb
@@ -223,7 +223,7 @@ module Puppet
begin
RRD.graph(*args)
rescue => detail
- err "Failed to graph %s: %s" % [self.name,detail]
+ Puppet.err "Failed to graph %s: %s" % [self.name,detail]
end
end
@@ -241,7 +241,7 @@ module Puppet
begin
RRD.update(self.path,args.join(":"))
rescue => detail
- err "Failed to update %s: %s" % [self.name,detail]
+ Puppet.err "Failed to update %s: %s" % [self.name,detail]
end
end
end
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
new file mode 100644
index 000000000..ff237f62e
--- /dev/null
+++ b/lib/puppet/parser/ast.rb
@@ -0,0 +1,1238 @@
+#/usr/bin/ruby
+
+# $Id$
+# vim: syntax=ruby
+
+# the AST tree
+
+# the parent class for all of our syntactical objects
+module Puppet
+ module Parser
+ class ASTError < RuntimeError; end
+ #---------------------------------------------------------------
+ class AST
+ attr_accessor :line, :file, :parent
+
+ @@pink = ""
+ @@green = ""
+ @@yellow = ""
+ @@slate = ""
+ @@reset = ""
+
+ @@indent = " " * 4
+ @@indline = @@pink + ("-" * 4) + @@reset
+ @@midline = @@slate + ("-" * 4) + @@reset
+
+ @@settypes = Hash.new { |hash,key|
+ hash[key] = Hash.new(0)
+ }
+
+ def AST.indention
+ return @@indent * @@indention
+ end
+
+ def AST.midline
+ return @@midline
+ end
+
+ def evaluate(scope)
+ #Puppet.debug("Evaluating ast %s" % @name)
+ value = self.collect { |obj|
+ obj.evaluate(scope)
+ }.reject { |obj|
+ obj.nil?
+ }
+ end
+
+ def typewrap(string)
+ #return self.class.to_s.sub(/.+::/,'') +
+ #"(" + @@green + string.to_s + @@reset + ")"
+ return @@green + string.to_s + @@reset +
+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
+ end
+
+ def initialize(args)
+ # this has to wait until all of the objects are defined
+ unless defined? @@klassorder
+ @@klassorder = [
+ AST::VarDef, AST::TypeDefaults,
+ AST::ObjectDef, AST::StatementArray
+ ]
+ end
+
+ args.each { |param,value|
+ method = param.to_s + "="
+ unless self.respond_to?(method)
+ error = Puppet::ParseError.new(
+ "Invalid parameter %s to object class %s" %
+ [method,self.class.to_s]
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+
+ begin
+ #Puppet.debug("sending %s to %s" % [method, self.class])
+ self.send(method,value)
+ rescue => detail
+ error = Puppet::DevError.new(
+ "Could not set parameter %s on class %s: %s" %
+ [method,self.class.to_s,detail]
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ end
+
+ #---------------------------------------------------------------
+ # this differentiation is used by the interpreter
+ # these objects have children
+ class Branch < AST
+ include Enumerable
+ attr_accessor :pin, :children
+
+ def each
+ @children.each { |child|
+ yield child
+ }
+ end
+
+ def evaluate(scope)
+ #Puppet.debug("Evaluating branch")
+# rets = nil
+# if scope.declarative
+# # if we're operating declaratively, then we want to get
+# # all of our 'setting' operations done first
+# rets = @children.sort { |a,b|
+# [a,b].each { |i|
+# unless @@klassorder.include?(i.class)
+# raise "Order not defined for %s" % i.class
+# end
+# }
+# @@klassorder.index(a.class) <=> @@klassorder.index(b.class)
+# }.collect { |item|
+# Puppet.debug "Decl evaluating %s" % item.class
+# item.evaluate(scope)
+# }
+# else
+# rets = @children.collect { |item|
+# item.evaluate(scope)
+# }
+# end
+ self.collect { |item|
+ #Puppet.debug "Evaluating %s" % item.class
+ item.evaluate(scope)
+ }.reject { |obj|
+ obj.nil
+ }
+ end
+
+ def initialize(arghash)
+ super(arghash)
+
+ unless defined? @children
+ @children = []
+ end
+
+ #puts "children is '%s'" % [@children]
+
+ self.each { |child|
+ if child.class == Array
+ error = Puppet::DevError.new(
+ "child for %s(%s) is array" % [self.class,self.parent]
+ )
+ error.stack = caller
+ raise error
+ end
+ unless child.nil?
+ child.parent = self
+ end
+ }
+ end
+
+ def tree(indent = 0)
+ return ((@@indline * indent) +
+ self.typewrap(self.pin)) + "\n" + self.collect { |child|
+ child.tree(indent + 1)
+ }.join("\n")
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ASTArray < AST::Branch
+ include Enumerable
+
+ def [](index)
+ @children[index]
+ end
+
+ def evaluate(scope)
+ rets = nil
+ if scope.declarative
+ test = [
+ AST::VarDef, AST::TypeDefaults
+ ]
+
+ # if we're operating declaratively, then we want to get
+ # all of our 'setting' operations done first
+ settors = []
+ others = []
+ @children.each { |child|
+ if test.include?(child.class)
+ settors.push child
+ else
+ others.push child
+ end
+ }
+ rets = [settors,others].flatten.collect { |child|
+ child.evaluate(scope)
+ }
+ else
+ rets = @children.collect { |item|
+ item.evaluate(scope)
+ }
+ end
+ rets = rets.reject { |obj| obj.nil? }
+ end
+
+ def initialize(hash)
+ super(hash)
+
+ @children.each { |child|
+ unless child.is_a?(AST)
+ Puppet.err("child %s is not an ast" % child)
+ exit
+ end
+ }
+ return self
+ end
+
+ def push(*ary)
+ ary.each { |child|
+ #Puppet.debug "adding %s(%s) of type %s to %s" %
+ # [child, child.object_id, child.class.to_s.sub(/.+::/,''),
+ # self.object_id]
+ @children.push(child)
+ }
+
+ return self
+ end
+
+ def to_s
+ return "[" + @children.collect { |child|
+ child.to_s
+ }.join(", ") + "]"
+ end
+
+ def tree(indent = 0)
+ #puts((AST.indent * indent) + self.pin)
+ self.collect { |child|
+ if child.class == Array
+ Puppet.debug "child is array for %s" % self.class
+ end
+ child.tree(indent)
+ }.join("\n" + (AST.midline * (indent+1)) + "\n")
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class StatementArray < ASTArray
+ def evaluate(scope)
+ rets = nil
+ if scope.declarative
+ # if we're operating declaratively, then we want to get
+ # all of our 'setting' operations done first
+ rets = @children.sort { |a,b|
+ [a,b].each { |i|
+ unless @@klassorder.include?(i.class)
+ error = Puppet::DevError.new(
+ "Order not defined for %s" % i.class
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ @@klassorder.index(a.class) <=> @@klassorder.index(b.class)
+ }.collect { |item|
+ Puppet.debug "Decl evaluating %s" % item.class
+ item.evaluate(scope)
+ }.reject { |obj| obj.nil? }
+ else
+ rets = @children.collect { |item|
+ item.evaluate(scope)
+ }.reject { |obj| obj.nil? }
+ end
+
+ return rets
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # and these ones don't
+ class Leaf < AST
+ attr_accessor :value, :type
+
+ # this only works if @value has already been evaluated
+ # otherwise you get AST objects, which you don't likely want...
+ def evaluate(scope)
+ return @value
+ end
+
+ def tree(indent = 0)
+ return ((@@indent * indent) + self.typewrap(self.value))
+ end
+
+ def to_s
+ return @value
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Boolean < AST::Leaf
+ def initialize(hash)
+ super
+
+ unless @value == 'true' or @value == 'false'
+ error = Puppet::DevError.new(
+ "'%s' is not a boolean" % @value
+ )
+ error.stack = caller
+ raise error
+ end
+ if @value == 'true'
+ @value = true
+ else
+ @value = false
+ end
+ end
+
+ def evaluate(scope)
+ return @value
+ end
+
+ def to_s
+ return @value
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class String < AST::Leaf
+ def evaluate(scope)
+ return scope.strinterp(@value)
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Word < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Type < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Name < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Variable < Word
+ def evaluate(scope)
+ # look up the variable value in the symbol table
+ begin
+ return scope.lookupvar(@value)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::DevError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectDef < AST::Branch
+ attr_accessor :name, :type
+ attr_reader :params
+
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ def [](index)
+ return @params[index]
+ end
+
+ def each
+ #Puppet.debug("each called on %s" % self)
+ [@type,@name,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def evaluate(scope)
+ hash = {}
+
+ objtype = @type.evaluate(scope)
+ objnames = @name.evaluate(scope)
+
+ # first, retrieve the defaults
+ defaults = scope.lookupdefaults(objtype)
+ defaults.each { |var,value|
+ Puppet.debug "Found default %s for %s" %
+ [var,objtype]
+
+ hash[var] = value
+ }
+
+ # then set all of the specified params
+ @params.each { |param|
+ ary = param.evaluate(scope)
+ hash[ary[0]] = ary[1]
+ }
+
+ # it's easier to always use an array, even for only one name
+ unless objnames.is_a?(Array)
+ objnames = [objnames]
+ end
+
+ begin
+ object = scope.lookuptype(objtype)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+
+ # this is where our implicit iteration takes place;
+ # if someone passed an array as the name, then we act
+ # just like the called us many times
+ objnames.collect { |objname|
+ # if the type is not defined in our scope, we assume
+ # that it's a type that the client will understand, so we
+ # just store it in our objectable
+ if object.nil?
+ begin
+ Puppet.debug("Setting object '%s' with arguments %s" %
+ [objname, hash.inspect])
+ obj = scope.setobject(
+ objtype,
+ objname,
+ hash,
+ @file,
+ @line
+ )
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ else
+ # but things like components create a new type; if we find
+ # one of those, evaluate that with our arguments
+ Puppet.debug("Calling object '%s' with arguments %s" %
+ [object.name, hash.inspect])
+ object.evaluate(scope,hash,objtype,objname)
+ end
+ }.reject { |obj| obj.nil? }
+ end
+
+ def initialize(hash)
+ super
+
+ Puppet.debug "%s id is %s" % [@name, object_id]
+
+ # we don't have to evaluate because we require bare words
+ # for types
+ objtype = @type.value
+
+ if Puppet[:typecheck]
+ builtin = false
+ begin
+ builtin = Puppet::Type.type(objtype)
+ rescue TypeError
+ # nothing; we've already set builtin to false
+ end
+ if builtin
+ # we're a builtin type
+ #Puppet.debug "%s is a builtin type" % objtype
+ if Puppet[:paramcheck]
+ @params.each { |param|
+ pname = param.param.value
+ next if pname == "name" # always allow these
+ unless builtin.validarg?(pname)
+ error = Puppet::ParseError.new(
+ "Invalid parameter '%s' for type '%s'" %
+ [pname,objtype]
+ )
+ error.stack = caller
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ }
+ end
+ # FIXME this should use scoping rules to find the set type,
+ # not a global list
+ elsif @@settypes.include?(objtype)
+ # we've defined it locally
+ Puppet.debug "%s is a defined type" % objtype
+ hash = @@settypes[objtype]
+ @params.each { |param|
+ # FIXME we might need to do more here eventually...
+ if Puppet::Type.metaparam?(param.param.value.intern)
+ next
+ end
+
+ pname = param.param.value
+ unless hash.include?(pname)
+ error = Puppet::ParseError.new(
+ "Invalid parameter '%s' for type '%s'" %
+ [pname,objtype]
+ )
+ error.stack = caller
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ }
+ else
+ # we don't know anything about it
+ error = Puppet::ParseError.new(
+ "Unknown type '%s'" % objtype
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+ end
+
+ def params=(params)
+ if params.is_a?(AST::ASTArray)
+ @params = params
+ else
+ @params = AST::ASTArray.new(
+ :children => [params]
+ )
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @name.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @params.collect { |param|
+ begin
+ param.tree(indent + 1)
+ rescue NoMethodError => detail
+ Puppet.err @params.inspect
+ error = Puppet::DevError.new(
+ "failed to tree a %s" % self.class
+ )
+ error.stack = caller
+ raise error
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@name,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectRef < AST::Branch
+ attr_accessor :name, :type
+
+ def each
+ #Puppet.debug("each called on %s" % self)
+ [@type,@name].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def evaluate(scope)
+ objtype = @type.evaluate(scope)
+ objnames = @name.evaluate(scope)
+
+ # it's easier to always use an array, even for only one name
+ unless objnames.is_a?(Array)
+ objnames = [objnames]
+ end
+
+ begin
+ object = scope.lookuptype(objtype)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ Puppet.debug "ObjectRef returned type %s" % object
+
+ # should we implicitly iterate here?
+ # yes, i believe that we essentially have to...
+ objnames.collect { |objname|
+ if object.is_a?(Component)
+ objname = "%s[%s]" % [objtype,objname]
+ objtype = "component"
+ end
+ [objtype,objname]
+ }.reject { |obj| obj.nil? }
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @name.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin))
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s[%s]" % [@name,@type]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectParam < AST::Branch
+ attr_accessor :value, :param
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ return [@param.evaluate(scope),@value.evaluate(scope)]
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@param,@value]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Test < AST::Branch
+ attr_accessor :lhs, :rhs
+
+ # is our test true or false?
+ def evaluate(scope)
+ # retrieve our values and make them a touch easier to manage
+ lvalue = @lhs.evaluate(scope)
+ rvalue = @rhs.evaluate(scope)
+
+ # FIXME this probably won't work except on strings right now...
+ retvalue = lvalue.send(@pin, rvalue)
+
+ #Puppet.debug "test '%s' returned %s" % [self.to_s,retvalue]
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ return [
+ @lhs.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @rhs.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@lhs,@rhs].each { |child| yield child }
+ end
+
+ def to_s
+ return "%s %s %s" % [@lhs,@pin,@rhs]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class If < AST::Branch
+ attr_accessor :test, :statements, :else, :elsif
+
+ # 'if' is a bit special, since we don't want to continue
+ # evaluating if a test turns up true
+ def evaluate(scope)
+ scope = scope.newscope
+ retvalue = nil
+ if @test.evaluate(scope)
+ Puppet.debug "%s is true" % @test
+ retvalue = @statements.evaluate(scope)
+ elsif defined? @elsif
+ Puppet.debug "%s is false" % @test
+ elsereturn = nil
+ @elsif.each { |elsetest|
+ if elsereturn = @elsif.evaluate(scope)
+ retvalue = elsereturn
+ end
+ }
+ elsif defined? @else
+ retvalue = @else.evaluate(scope)
+ else
+ Puppet.debug "None of the ifs are true"
+ end
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ rettree = [
+ @test.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @statements.tree(indent + 1)
+ ]
+ if defined? @elsif
+ @elsif.each { |elsetest|
+ rettree.push(elsetest.tree(indent + 1))
+ }
+ end
+
+ if defined? @else
+ rettree.push(@else.tree(indent+1))
+ end
+
+ return rettree.flatten.join("\n")
+ end
+
+ def each
+ list = [@test,@statements]
+
+ if defined? @elsif
+ @elsif.each { |tmp|
+ list.push(tmp)
+ }
+ end
+
+ if defined? @else
+ list.push(@else)
+ end
+
+ list.each { |child| yield child }
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Selector < AST::Branch
+ attr_accessor :param, :value
+
+ # okay, here's a decision point...
+ def evaluate(scope)
+ # retrieve our values and make them a touch easier to manage
+ hash = Hash[*(@value.evaluate(scope).flatten)]
+
+ retvalue = nil
+
+ paramvalue = @param.evaluate(scope)
+
+ retvalue = hash.detect { |test,value|
+ # FIXME this will return variables named 'default'...
+ if paramvalue == test
+ break value
+ end
+ }
+ if retvalue.nil?
+ if hash.include?("default")
+ return hash["default"]
+ else
+ error = Puppet::ParseError.new(
+ "No value for selector param '%s'" % paramvalue
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = self.stack
+ raise error
+ end
+ end
+
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class VarDef < AST::Branch
+ attr_accessor :name, :value
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+ value = @value.evaluate(scope)
+
+ Puppet.debug "setting %s to %s" % [name,value]
+ begin
+ scope.setvar(name,value)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+
+ def each
+ [@name,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@value]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class TypeDefaults < AST::Branch
+ attr_accessor :type, :params
+
+ def each
+ [@type,@params].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ type = @type.evaluate(scope)
+ params = @params.evaluate(scope)
+
+ #Puppet.info "Params are %s" % params.inspect
+ #Puppet.debug("evaluating '%s.%s' with values [%s]" %
+ # [type,name,values])
+ # okay, now i need the interpreter's client object thing...
+ begin
+ scope.setdefaults(type.downcase,params)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @params.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s { %s }" % [@type,@params]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # these are analogous to defining new object types
+ class CompDef < AST::Branch
+ attr_accessor :name, :args, :code
+
+ def each
+ [@name,@args,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+
+ args = @args.evaluate(scope)
+
+ #Puppet.debug("defining '%s' with arguments [%s]" %
+ # [name,args])
+ #p @args
+ #p args
+ # okay, now i need to evaluate all of the statements
+ # within a component and a new lexical scope...
+
+ begin
+ scope.settype(name,
+ Component.new(
+ :name => name,
+ :args => args,
+ :code => @code
+ )
+ )
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+
+ def initialize(hash)
+ super
+
+ Puppet.debug "Defining type %s" % @name.value
+
+ # we need to both mark that a given argument is valid,
+ # and we need to also store any provided default arguments
+ hash = @@settypes[@name.value]
+ if @args.is_a?(AST::ASTArray)
+ @args.each { |ary|
+ if ary.is_a?(AST::ASTArray)
+ arg = ary[0]
+ hash[arg.value] += 1
+ else
+ hash[ary.value] += 1
+ end
+ }
+ else
+ Puppet.warning "got arg %s" % @args.inspect
+ hash[@args.value] += 1
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap("define")),
+ @args.tree(indent + 1),
+ @code.tree(indent + 1),
+ ].join("\n")
+ end
+
+ def to_s
+ return "define %s(%s) {\n%s }" % [@name, @args, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # these are analogous to defining new object types
+ class ClassDef < AST::CompDef
+ attr_accessor :parentclass
+
+ def each
+ [@name,@args,@parentclass,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+ args = @args.evaluate(scope)
+
+ #Puppet.debug "evaluating parent %s of type %s" %
+ # [@parent.name, @parent.class]
+ parent = @parentclass.evaluate(scope)
+
+ Puppet.debug("defining hostclass '%s' with arguments [%s]" %
+ [name,args])
+
+ begin
+ scope.settype(name,
+ HostClass.new(
+ :name => name,
+ :args => args,
+ :parent => parent,
+ :code => @code
+ )
+ )
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap("class")),
+ @args.tree(indent + 1),
+ @parentclass.tree(indent + 1),
+ @code.tree(indent + 1),
+ ].join("\n")
+ end
+
+ def to_s
+ return "class %s(%s) inherits %s {\n%s }" %
+ [@name, @args, @parentclass, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # host definitions are special, because they get called when a host
+ # whose name matches connects
+ class NodeDef < AST::Branch
+ attr_accessor :names, :code
+
+ def each
+ [@names,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ names = @names.evaluate(scope)
+
+ unless names.is_a?(Array)
+ names = [names]
+ end
+ Puppet.debug("defining hosts '%s'" % [names.join(", ")])
+
+ names.each { |name|
+ begin
+ scope.sethost(name,
+ Host.new(
+ :name => name,
+ :code => @code
+ )
+ )
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ }
+ end
+
+ def tree(indent = 0)
+ return [
+ @names.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap("host")),
+ @code.tree(indent + 1),
+ ].join("\n")
+ end
+
+ def to_s
+ return "host %s {\n%s }" % [@name, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class Component < AST::Branch
+ attr_accessor :name, :args, :code
+
+ def evaluate(scope,hash,objtype,objname)
+ scope = scope.newscope
+ scope.type = objtype
+ scope.name = objname
+
+ # define all of the arguments in our local scope
+ if self.args
+ Puppet.debug "args are %s" % self.args.inspect
+ self.args.each { |arg, default|
+ unless hash.include?(arg)
+ if default
+ hash[arg] = default
+ else
+ error = Puppet.ParseError.new(
+ "Must pass %s to %s of type %s" %
+ [arg.inspect,name,objtype]
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+ }
+
+ hash.each { |arg,value|
+ begin
+ scope.setvar(arg,hash[arg])
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => except
+ error = Puppet::ParseError.new(except.message)
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ }
+ end
+
+ # now just evaluate the code with our new bindings
+ self.code.evaluate(scope)
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class HostClass < AST::Component
+ attr_accessor :parentclass
+
+ def evaluate(scope,hash,objtype,objname)
+ if @parentclass
+ begin
+ parentobj = scope.lookuptype(@parentclass)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ unless parentobj
+ error = Puppet::ParseError.new(
+ "Could not find parent '%s' of '%s'" % [@parentclass,@name])
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ parentobj.evaluate(scope,hash,objtype,objname)
+ end
+
+ # just use the Component evaluate method, but change the type
+ # to our own type
+ super(scope,hash,@name,objname)
+ end
+
+ def initialize(hash)
+ @parentclass = nil
+ super
+ if self.parent.is_a?(Array)
+ self.parent = nil
+ end
+ end
+
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class Host < AST::Component
+ attr_accessor :name, :args, :code, :parentclass
+
+ def evaluate(scope,hash,objtype,objname)
+ if @parentclass
+ begin
+ parentobj = scope.lookuptype(@parentclass)
+ rescue Puppet::ParseError => except
+ except.line = self.line
+ except.file = self.file
+ raise except
+ rescue => detail
+ error = Puppet::ParseError.new(detail)
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ unless parentobj
+ error = Puppet::ParseError.new(
+ "Could not find parent '%s' of '%s'" % [@parentclass,@name])
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ parentobj.evaluate(scope,hash,objtype,objname)
+ end
+
+ # just use the Component evaluate method, but change the type
+ # to our own type
+ super(scope,hash,@name,objname)
+ end
+ end
+ #---------------------------------------------------------------
+ end
+ end
+end
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
new file mode 100644
index 000000000..7386a0e15
--- /dev/null
+++ b/lib/puppet/parser/grammar.ra
@@ -0,0 +1,672 @@
+#/usr/bin/ruby
+
+# $Id$
+# vim: syntax=ruby
+
+# the parser
+
+class Puppet::Parser::Parser
+
+token LBRACK QTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE FALSE EQUALS
+token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL NOTEQUAL
+token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN DOT COLON TYPE
+token NAME SEMIC
+
+rule
+program: statements {
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :children => [val[0]]
+ )
+ end
+
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Puppet[:parseonly]
+ begin
+ if Puppet[:debug]
+ puts result.tree(0)
+ end
+ rescue NoMethodError => detail
+ Puppet.err detail
+ #exit(78)
+ end
+ #require 'puppet/parser/interpreter'
+ #result = Puppet::Server.new(result)
+ end
+}
+
+statements: statement
+ | statements statement {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :children => [val[0],val[1]]
+ )
+ end
+}
+
+statement: object
+ | assignment
+ | selector
+ | iftest
+ | import
+ | definition
+ | hostclass
+
+#object: name LBRACE objectname COLON params endcomma RBRACE {
+object: name LBRACE objectinstances endsemi RBRACE {
+ ary = val[2]
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ if ary[0].is_a?(AST::Leaf)
+ ary = [ary]
+ end
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file
+ )
+ ary.each { |instance|
+ Puppet.debug "Adding %s with name %s" % [val[0].value, instance[0].value]
+ result.push AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => instance[0],
+ :params => instance[1]
+ )
+ }
+} | name LBRACE params endcomma RBRACE {
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ # an object but without a name
+ # this cannot be an instance of a library type
+
+ Puppet.debug "Adding %s" % val[0].value
+ # make a unique name for bookkeeping purposes
+ name = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => [val[0].value, "-", val[0].object_id].join('')
+ )
+
+ result = AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => name,
+ :params => val[2]
+ )
+} | type LBRACE params endcomma RBRACE {
+ # a template setting for a type
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid type"
+ raise Puppet::ParseError, "Invalid type"
+ end
+ result = AST::TypeDefaults.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :params => val[2]
+ )
+}
+
+objectinst: objectname COLON params {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+}
+
+objectinstances: objectinst
+ | objectinstances SEMIC objectinst {
+ if val[0][0].is_a?(AST::ASTArray)
+ val[0].push val[2]
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+endsemi: # nothing
+ | SEMIC
+
+name: NAME {
+ result = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+type: TYPE {
+ result = AST::Type.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+objectname: quotedtext
+ | name
+ | selector
+ | variable
+ | array
+
+assignment: VARIABLE EQUALS rvalue {
+ # this is distinct from referencing a variable
+ variable = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0].sub(/^\$/,'')
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :name => variable,
+ :value => val[2]
+ )
+}
+
+params: # nothing
+{
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+}
+ | param { result = val[0] }
+ | params COMMA param {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+param: NAME FARROW rvalue {
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+rvalues: rvalue
+ | rvalues comma rvalue {
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0].push(val[2])
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+rvalue: quotedtext
+ | name
+ | type
+ | boolean
+ | selector
+ | object
+ | variable
+ | array
+ | objectref
+
+quotedtext: QTEXT {
+ result = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+boolean: BOOLEAN {
+ result = AST::Boolean.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+objectref: name LBRACK rvalue RBRACK {
+ result = AST::ObjectRef.new(
+ :pin => '[]',
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => val[2]
+ )
+}
+
+iftest: IF test LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :statements => val[3]
+ )
+}
+ | IF test LBRACE statements RBRACE elsifs ELSE LBRACE statements RBRACE {
+ # make sure our elsifs are an array, as it will save lots of
+ # effort later
+ unless val[5].is_a?(AST::ASTArray)
+ val[5] = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[5]]
+ )
+ end
+
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[8],
+ :elsif => val[5]
+ )
+
+}
+ | IF test LBRACE statements RBRACE ELSE LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[7]
+ )
+
+}
+
+elsifs: ELSIF test LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "elseif",
+ :test => val[1],
+ :file => @lexer.file,
+ :statements => val[3],
+ :line => @lexer.line
+ )
+}
+ | elsifs ELSIF test LBRACE statements RBRACE {
+ second = AST::If.new(
+ :pin => "elsif",
+ :test => val[2],
+ :statements => val[4],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(second)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],second]
+ )
+ end
+}
+
+test: rvalue
+ | rvalue testop rvalue {
+ result = AST::Test.new(
+ :pin => val[1],
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :lhs => val[0],
+ :rhs => val[2]
+ )
+}
+
+testop: ISEQUAL
+ | GREATEREQUAL
+ | GREATERTHAN
+ | LESSTHAN
+ | LESSEQUAL
+ | NOTEQUAL
+
+selector: variable QMARK svalues {
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => val[0],
+ :value => val[2]
+ )
+}
+
+svalues: selectval
+ | LBRACE sintvalues RBRACE { result = val[1] }
+
+sintvalues: selectval
+ | sintvalues comma selectval {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+selectval: selectlhand FARROW rvalue {
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+selectlhand: NAME
+ | TYPE
+ | QTEXT
+
+import: IMPORT QTEXT {
+ # importing files
+ # yuk, i hate keywords
+ # we'll probably have to have some kind of search path eventually
+ # but for now, just use a path relative to the file doing the importing
+ path = @lexer.file.sub(%r{[^/]+$},val[1])
+ parser = Puppet::Parser::Parser.new()
+ parser.stack = self.stack
+ Puppet.debug("importing %s" % path)
+ noimport = false
+ begin
+ parser.file = path
+ rescue Puppet::ImportError
+ Puppet.warning("Importing %s would result in an import loop" % path)
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ noimport = true
+ end
+ unless noimport
+ result = parser.parse
+ end
+}
+
+definition: DEFINE NAME argumentlist LBRACE statements RBRACE {
+ result = AST::CompDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[4]
+ )
+}
+
+hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
+ result = AST::ClassDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :parentclass => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[5]
+ )
+}
+
+#nodedef: NODE words LBRACE statements RBRACE {
+# result = AST::NodeDef.new(
+# :names => val[1],
+# :code => val[3]
+# )
+#}
+
+nothing: {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+}
+
+argumentlist: nothing
+ | LPAREN nothing RPAREN {
+ result = val[1]
+}
+ | LPAREN arguments RPAREN {
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ end
+}
+
+arguments: argument
+ | arguments COMMA argument {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+argument: name EQUALS rvalue {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+}
+ | name {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+}
+
+parent: nothing
+ | INHERITS NAME {
+ result = AST::Name.new(
+ :value => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+}
+
+variable: VARIABLE {
+ name = val[0].sub(/^\$/,'')
+ result = AST::Variable.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => name
+ )
+}
+
+array: LBRACK rvalues RBRACK {
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new
+ result.push val[1]
+ end
+}
+
+comma: FARROW
+ | COMMA
+
+endcomma: # nothing
+ | COMMA { result = nil }
+
+end
+---- header ----
+require 'puppet'
+require 'puppet/parser/lexer'
+require 'puppet/parser/ast'
+#require 'puppet/parser/interpreter'
+
+module Puppet
+ # this exception class already has a :stack accessor
+ class ParseError < Puppet::Error
+ attr_accessor :line, :file
+ end
+
+ class ImportError < Racc::ParseError; end
+end
+
+Puppet[:typecheck] = true
+Puppet[:paramcheck] = true
+
+---- inner ----
+attr_writer :stack
+attr_reader :file
+
+def file=(file)
+ if self.stack.include?(file)
+ raise Puppet::ImportError.new("Import loop detected")
+ else
+ @lexer.file = file
+ end
+end
+
+def initialize
+ @lexer = Puppet::Parser::Lexer.new()
+ if Puppet[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ #error = "line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last]
+ error = "an error was found"
+
+ if Puppet[:debug]
+ puts stack.inspect
+ puts stack
+ end
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ except = Puppet::ParseError.new(error)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
+# how should I do error handling here?
+def parse
+ begin
+ yyparse(@lexer,:scan)
+ rescue Racc::ParseError => except
+ error = Puppet::ParseError.new(except)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ raise error
+ rescue Puppet::ParseError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::Error => except
+ # and this is a framework error
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= except.stack
+ raise except
+ rescue Puppet::DevError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= caller
+ if Puppet[:debug]
+ puts except.stack
+ end
+ raise except
+ rescue => except
+ error = Puppet::DevError.new(except.message)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ if Puppet[:debug]
+ puts caller
+ end
+ raise error
+ end
+end
+
+def stack
+ if defined? @stack and ! @stack.nil?
+ if @lexer.file
+ return [@stack,@lexer.file].flatten
+ else
+ return @stack
+ end
+ else
+ if @lexer.file
+ return [@lexer.file]
+ else
+ return []
+ end
+ end
+end
+
+def string=(string)
+ @lexer.string = string
+end
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
new file mode 100644
index 000000000..88ab5854e
--- /dev/null
+++ b/lib/puppet/parser/interpreter.rb
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the interpreter
+#
+# this builds our virtual pinball machine, into which we'll place our host-specific
+# information and out of which we'll receive our host-specific configuration
+
+require 'puppet'
+require 'puppet/parser/parser'
+require 'puppet/parser/scope'
+
+
+module Puppet
+ module Parser
+ #---------------------------------------------------------------
+ class Interpreter
+ attr_accessor :ast, :topscope
+ # just shorten the constant path a bit, using what amounts to an alias
+ AST = Puppet::Parser::AST
+
+ #------------------------------------------------------------
+ def clear
+ TransObject.clear
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ #def callfunc(function,*args)
+ # #Puppet.debug("Calling %s on %s" % [function,@client])
+ # @client.callfunc(function,*args)
+ # #Puppet.debug("Finished %s" % function)
+ #end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # create our interpreter
+ def initialize(hash)
+ unless hash.include?(:ast)
+ raise ArgumentError.new("Must pass tree and client to Interpreter")
+ end
+ @ast = hash[:ast]
+ #@client = hash[:client]
+
+ @scope = Puppet::Parser::Scope.new() # no parent scope
+ @topscope = @scope
+ @scope.interp = self
+
+ if hash.include?(:facts)
+ facts = hash[:facts]
+ unless facts.is_a?(Hash)
+ raise ArgumentError.new("Facts must be a hash")
+ end
+
+ facts.each { |fact,value|
+ @scope.setvar(fact,value)
+ }
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # evaluate our whole tree
+ def run
+ # evaluate returns a value, but at the top level we only
+ # care about its side effects
+ # i think
+ unless @ast.is_a?(AST) or @ast.is_a?(AST::ASTArray)
+ Puppet.err "Received top-level non-ast '%s' of type %s" %
+ [@ast,@ast.class]
+ raise TypeError.new("Received non-ast '%s' of type %s" %
+ [@ast,@ast.class])
+ end
+
+ begin
+ @ast.evaluate(@scope)
+ rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except
+ #Puppet.err "File %s, line %s: %s" %
+ # [except.file, except.line, except.message]
+ if Puppet[:debug]
+ puts except.stack
+ end
+ #exit(1)
+ raise
+ rescue => except
+ error = Puppet::DevError.new("%s: %s" %
+ [except.class, except.message])
+ error.stack = caller
+ if Puppet[:debug]
+ puts error.stack
+ end
+ raise error
+ end
+
+ # okay, at this point we have a tree of scopes, and we want to
+ # unzip along that tree, building our structure of objects
+ # to pass to the client
+ # this will be heirarchical, and will (at this point) contain
+ # only TransObjects and TransSettings
+ @topscope.name = "top"
+ @topscope.type = "puppet"
+ begin
+ topbucket = @topscope.to_trans
+ rescue => detail
+ Puppet.warning detail
+ raise
+ end
+
+ # add our settings to the front of the array
+ # at least, for now
+ #@topscope.typesets.each { |setting|
+ # topbucket.unshift setting
+ #}
+
+ # guarantee that settings are at the very top
+ #topbucket.push settingbucket
+ #topbucket.push @scope.to_trans
+
+ #retlist = TransObject.list
+ #Puppet.debug "retobject length is %s" % retlist.length
+ #TransObject.clear
+ return topbucket
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def scope
+ return @scope
+ end
+ #------------------------------------------------------------
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
new file mode 100644
index 000000000..d9d9d5f5c
--- /dev/null
+++ b/lib/puppet/parser/lexer.rb
@@ -0,0 +1,225 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the scanner/lexer
+
+require 'strscan'
+require 'puppet'
+
+
+module Puppet
+ class LexError < RuntimeError; end
+ module Parser
+ #---------------------------------------------------------------
+ class Lexer
+ attr_reader :line, :last, :file
+
+ #%r{\w+} => :WORD,
+ @@tokens = {
+ %r{#.*} => :COMMENT,
+ %r{\[} => :LBRACK,
+ %r{\]} => :RBRACK,
+ %r{\{} => :LBRACE,
+ %r{\}} => :RBRACE,
+ %r{\(} => :LPAREN,
+ %r{\)} => :RPAREN,
+ %r{"} => :DQUOTE,
+ %r{\n} => :RETURN,
+ %r{'} => :SQUOTE,
+ %r{=} => :EQUALS,
+ %r{==} => :ISEQUAL,
+ %r{>=} => :GREATEREQUAL,
+ %r{>} => :GREATERTHAN,
+ %r{<} => :LESSTHAN,
+ %r{<=} => :LESSEQUAL,
+ %r{!=} => :NOTEQUAL,
+ %r{,} => :COMMA,
+ %r{\.} => :DOT,
+ %r{:} => :COLON,
+ %r{;} => :SEMIC,
+ %r{\?} => :QMARK,
+ %r{\\} => :BACKSLASH,
+ %r{=>} => :FARROW,
+ %r{[a-z]\w*} => :NAME,
+ %r{[A-Z]\w*} => :TYPE,
+ %r{[0-9]+} => :NUMBER,
+ %r{\$\w+} => :VARIABLE
+ }
+
+ @@keywords = {
+ "if" => :IF,
+ "elsif" => :ELSIF,
+ "else" => :ELSE,
+ "import" => :IMPORT,
+ "class" => :CLASS,
+ "node" => :NODE,
+ "host" => :NODE,
+ "true" => :BOOLEAN,
+ "false" => :BOOLEAN,
+ "inherits" => :INHERITS,
+ "define" => :DEFINE
+ }
+
+ # scan the whole file
+ # basically just used for testing
+ def fullscan
+ array = []
+
+ self.scan { |token,str|
+ #Puppet.debug("got token '%s' => '%s'" % [token,str])
+ if token.nil?
+ return array
+ else
+ array.push([token,str])
+ end
+ }
+ return array
+ end
+
+ # this is probably pretty damned inefficient...
+ # it'd be nice not to have to load the whole file first...
+ def file=(file)
+ @file = file
+ @line = 1
+ File.open(file) { |of|
+ str = ""
+ of.each { |line| str += line }
+ @scanner = StringScanner.new(str)
+ }
+ end
+
+ def initialize
+ @line = 1
+ @last = ""
+ @scanner = nil
+ @file = nil
+ # AAARRGGGG! okay, regexes in ruby are bloody annoying
+ # no one else has "\n" =~ /\s/
+ @skip = %r{[ \t]+}
+ end
+
+ def rest
+ @scanner.rest
+ end
+
+ # this is the heart of the lexer
+ def scan
+ #Puppet.debug("entering scan")
+ if @scanner.nil?
+ raise TypeError.new("Invalid or empty string")
+ end
+
+ @scanner.skip(@skip)
+ until @scanner.eos? do
+ yielded = false
+ sendbreak = false # gah, this is a nasty hack
+ stoken = nil
+ sregex = nil
+ value = ""
+
+ # first find out which type of token we've got
+ @@tokens.each { |regex,token|
+ # we're just checking, which doesn't advance the scan
+ # pointer
+ tmp = @scanner.check(regex)
+ if tmp.nil?
+ #puppet.debug("did not match %s to '%s'" %
+ # [regex,@scanner.rest])
+ next
+ end
+
+ # find the longest match
+ if tmp.length > value.length
+ value = tmp
+ stoken = token
+ sregex = regex
+ else
+ # we've already got a longer match
+ next
+ end
+ }
+
+ # error out if we didn't match anything at all
+ if stoken.nil?
+ nword = nil
+ if @scanner.rest =~ /^(\S+)/
+ nword = $1
+ elsif@scanner.rest =~ /^(\s+)/
+ nword = $1
+ else
+ nword = @scanner.rest
+ end
+ raise "Could not match '%s'" % nword
+ end
+
+ value = @scanner.scan(sregex)
+
+ if value == ""
+ raise "Didn't match regex on token %s" % stoken
+ end
+
+ # token-specific operations
+ # if this gets much more complicated, it should
+ # be moved up to where the tokens themselves are defined
+ # which will get me about 75% of the way to a lexer generator
+ case stoken
+ when :NAME then
+ wtoken = stoken
+ # we're looking for keywords here
+ if @@keywords.include?(value)
+ wtoken = @@keywords[value]
+ #Puppet.debug("token '%s'" % wtoken)
+ end
+ yield [wtoken,value]
+ @last = value
+ when :NUMBER then
+ yield [:NAME,value]
+ # just throw comments away
+ when :COMMENT then
+ # just throw comments away
+ when :RETURN then
+ @line += 1
+ @scanner.skip(@skip)
+ when :DQUOTE then
+ #Puppet.debug("searching '%s' after '%s'" % [self.rest,value])
+ value = self.slurpstring(value)
+ yield [:QTEXT,value]
+ @last = value
+ #stoken = :QTEXT
+ #Puppet.debug("got string '%s' => '%s'" % [:QTEXT,value])
+ else
+ yield [stoken,value]
+ @last = value
+ #Puppet.debug("got token '%s' => '%s'" % [stoken,value])
+ end
+ @scanner.skip(@skip)
+ end
+ @scanner = nil
+ yield [false,false]
+ end
+
+ # we've encountered an opening quote...
+ # slurp in the rest of the string and return it
+ def slurpstring(quote)
+ #Puppet.debug("searching '%s'" % self.rest)
+ str = @scanner.scan_until(/[^\\]#{quote}/)
+ #str = @scanner.scan_until(/"/)
+ if str.nil?
+ raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" %
+ [self.last,self.rest])
+ else
+ str.sub!(/#{quote}$/,"")
+ str.gsub!(/\\#{quote}/,quote)
+ end
+
+ return str
+ end
+
+ def string=(string)
+ @scanner = StringScanner.new(string)
+ end
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/puppet/parser/makefile b/lib/puppet/parser/makefile
new file mode 100644
index 000000000..9ec6265de
--- /dev/null
+++ b/lib/puppet/parser/makefile
@@ -0,0 +1,5 @@
+#parser.rb: grammar.ry
+# ryacc --output parser grammar
+
+parser.rb: grammar.ra
+ racc -v -o$@ grammar.ra
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
new file mode 100644
index 000000000..3b42bd8d7
--- /dev/null
+++ b/lib/puppet/parser/parser.rb
@@ -0,0 +1,1264 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by racc 1.4.4
+# from racc grammer file "grammar.ra".
+#
+
+require 'racc/parser'
+
+
+require 'puppet'
+require 'puppet/parser/lexer'
+require 'puppet/parser/ast'
+#require 'puppet/parser/interpreter'
+
+module Puppet
+ # this exception class already has a :stack accessor
+ class ParseError < Puppet::Error
+ attr_accessor :line, :file
+ end
+
+ class ImportError < Racc::ParseError; end
+end
+
+Puppet[:typecheck] = true
+Puppet[:paramcheck] = true
+
+
+module Puppet
+
+ module Parser
+
+ class Parser < Racc::Parser
+
+module_eval <<'..end grammar.ra modeval..ida89b5273aa', 'grammar.ra', 573
+attr_writer :stack
+attr_reader :file
+
+def file=(file)
+ if self.stack.include?(file)
+ raise Puppet::ImportError.new("Import loop detected")
+ else
+ @lexer.file = file
+ end
+end
+
+def initialize
+ @lexer = Puppet::Parser::Lexer.new()
+ if Puppet[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ #error = "line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last]
+ error = "an error was found"
+
+ if Puppet[:debug]
+ puts stack.inspect
+ puts stack
+ end
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ except = Puppet::ParseError.new(error)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
+# how should I do error handling here?
+def parse
+ begin
+ yyparse(@lexer,:scan)
+ rescue Racc::ParseError => except
+ error = Puppet::ParseError.new(except)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ raise error
+ rescue Puppet::ParseError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::Error => except
+ # and this is a framework error
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= except.stack
+ raise except
+ rescue Puppet::DevError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= caller
+ if Puppet[:debug]
+ puts except.stack
+ end
+ raise except
+ rescue => except
+ error = Puppet::DevError.new(except.message)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ if Puppet[:debug]
+ puts caller
+ end
+ raise error
+ end
+end
+
+def stack
+ if defined? @stack and ! @stack.nil?
+ if @lexer.file
+ return [@stack,@lexer.file].flatten
+ else
+ return @stack
+ end
+ else
+ if @lexer.file
+ return [@lexer.file]
+ else
+ return []
+ end
+ end
+end
+
+def string=(string)
+ @lexer.string = string
+end
+..end grammar.ra modeval..ida89b5273aa
+
+##### racc 1.4.4 generates ###
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 38, :_reduce_1,
+ 1, 39, :_reduce_none,
+ 2, 39, :_reduce_3,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 5, 41, :_reduce_11,
+ 5, 41, :_reduce_12,
+ 5, 41, :_reduce_13,
+ 3, 54, :_reduce_14,
+ 1, 49, :_reduce_none,
+ 3, 49, :_reduce_16,
+ 0, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 48, :_reduce_19,
+ 1, 53, :_reduce_20,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 3, 42, :_reduce_26,
+ 0, 51, :_reduce_27,
+ 1, 51, :_reduce_28,
+ 3, 51, :_reduce_29,
+ 3, 60, :_reduce_30,
+ 1, 61, :_reduce_none,
+ 3, 61, :_reduce_32,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 56, :_reduce_42,
+ 1, 63, :_reduce_43,
+ 4, 64, :_reduce_44,
+ 5, 44, :_reduce_45,
+ 10, 44, :_reduce_46,
+ 9, 44, :_reduce_47,
+ 5, 66, :_reduce_48,
+ 6, 66, :_reduce_49,
+ 1, 65, :_reduce_none,
+ 3, 65, :_reduce_51,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 3, 43, :_reduce_58,
+ 1, 68, :_reduce_none,
+ 3, 68, :_reduce_60,
+ 1, 70, :_reduce_none,
+ 3, 70, :_reduce_62,
+ 3, 69, :_reduce_63,
+ 1, 71, :_reduce_none,
+ 1, 71, :_reduce_none,
+ 1, 71, :_reduce_none,
+ 2, 45, :_reduce_67,
+ 6, 46, :_reduce_68,
+ 7, 47, :_reduce_69,
+ 0, 74, :_reduce_70,
+ 1, 72, :_reduce_none,
+ 3, 72, :_reduce_72,
+ 3, 72, :_reduce_73,
+ 1, 75, :_reduce_none,
+ 3, 75, :_reduce_75,
+ 3, 76, :_reduce_76,
+ 1, 76, :_reduce_77,
+ 1, 73, :_reduce_none,
+ 2, 73, :_reduce_79,
+ 1, 57, :_reduce_80,
+ 3, 58, :_reduce_81,
+ 1, 62, :_reduce_none,
+ 1, 62, :_reduce_none,
+ 0, 52, :_reduce_none,
+ 1, 52, :_reduce_85 ]
+
+racc_reduce_n = 86
+
+racc_shift_n = 153
+
+racc_action_table = [
+ 22, 24, 22, 24, 1, 54, 5, 9, 39, 12,
+ 14, 22, 24, 22, 24, 82, 3, 7, 116, 84,
+ 85, 59, 22, 24, 115, 28, 126, 28, 96, 31,
+ 108, 31, 3, 7, 3, 7, 28, 59, 28, 62,
+ 31, 7, 31, 3, 7, 3, 7, 28, 22, 24,
+ 89, 31, 56, 57, 3, 7, 97, 22, 24, 22,
+ 24, 22, 24, 128, 22, 24, 129, 113, 56, 57,
+ 22, 24, 114, 28, 98, 22, 24, 31, 59, 103,
+ 3, 7, 28, 80, 28, 80, 28, 63, 31, 28,
+ 7, 3, 7, 31, 69, 28, 3, 7, 133, 31,
+ 28, 121, 3, 7, 31, 22, 24, 3, 7, 56,
+ 57, 122, 136, 80, 1, 137, 5, 9, 124, 12,
+ 14, 125, 111, 109, 84, 85, 3, 7, 102, 102,
+ 28, 78, 63, 55, 31, 44, 134, 3, 7, 1,
+ 140, 5, 9, 99, 12, 14, 98, 138, 37, 80,
+ 37, 3, 7, 141, 152, 44, 1, 42, 5, 9,
+ 144, 12, 14, 41, 146, 40, 39, 38, 3, 7,
+ 1, 151, 5, 9, 37, 12, 14, 36, 7, nil,
+ nil, nil, 3, 7, nil, 149, nil, 1, nil, 5,
+ 9, nil, 12, 14, nil, nil, nil, nil, nil, 3,
+ 7, 1, 147, 5, 9, nil, 12, 14, nil, nil,
+ nil, nil, nil, 3, 7, nil, nil, nil, 1, nil,
+ 5, 9, nil, 12, 14, nil, nil, nil, nil, nil,
+ 3, 7, 1, nil, 5, 9, nil, 12, 14, nil,
+ nil, nil, nil, nil, 3, 7, 1, nil, 5, 9,
+ nil, 12, 14, nil, nil, nil, nil, nil, 3, 7,
+ 1, nil, 5, 9, nil, 12, 14, nil, nil, nil,
+ nil, nil, 3, 7, 1, nil, 5, 9, nil, 12,
+ 14, nil, nil, nil, nil, nil, 3, 7, 1, nil,
+ 5, 9, nil, 12, 14, nil, nil, nil, nil, nil,
+ 3, 7, 1, nil, 5, 9, nil, 12, 14, nil,
+ nil, nil, nil, nil, 3, 7, 1, nil, 5, 9,
+ nil, 12, 14, nil, nil, nil, nil, nil, 3, 7,
+ 1, nil, 5, 9, nil, 12, 14, nil, nil, nil,
+ nil, nil, 3, 7, 48, 49, 50, 51, 52, 53 ]
+
+racc_action_check = [
+ 1, 1, 83, 83, 96, 27, 96, 96, 27, 96,
+ 96, 89, 89, 54, 54, 46, 96, 96, 94, 46,
+ 46, 112, 47, 47, 94, 1, 106, 83, 64, 1,
+ 87, 83, 1, 1, 83, 83, 89, 37, 54, 37,
+ 89, 63, 54, 89, 89, 54, 54, 47, 98, 98,
+ 61, 47, 112, 112, 47, 47, 66, 99, 99, 129,
+ 129, 39, 39, 109, 113, 113, 109, 92, 37, 37,
+ 137, 137, 93, 98, 80, 22, 22, 98, 62, 79,
+ 98, 98, 99, 97, 129, 44, 39, 42, 129, 113,
+ 99, 129, 129, 113, 39, 137, 113, 113, 117, 137,
+ 22, 100, 137, 137, 22, 40, 40, 22, 22, 62,
+ 62, 101, 127, 102, 117, 127, 117, 117, 103, 117,
+ 117, 104, 91, 88, 91, 91, 117, 117, 81, 74,
+ 40, 41, 38, 34, 40, 33, 118, 40, 40, 88,
+ 135, 88, 88, 73, 88, 88, 69, 128, 68, 134,
+ 23, 88, 88, 136, 150, 18, 135, 14, 135, 135,
+ 139, 135, 135, 13, 142, 12, 11, 9, 135, 135,
+ 150, 148, 150, 150, 6, 150, 150, 5, 116, nil,
+ nil, nil, 150, 150, nil, 145, nil, 148, nil, 148,
+ 148, nil, 148, 148, nil, nil, nil, nil, nil, 148,
+ 148, 145, 143, 145, 145, nil, 145, 145, nil, nil,
+ nil, nil, nil, 145, 145, nil, nil, nil, 143, nil,
+ 143, 143, nil, 143, 143, nil, nil, nil, nil, nil,
+ 143, 143, 144, nil, 144, 144, nil, 144, 144, nil,
+ nil, nil, nil, nil, 144, 144, 141, nil, 141, 141,
+ nil, 141, 141, nil, nil, nil, nil, nil, 141, 141,
+ 146, nil, 146, 146, nil, 146, 146, nil, nil, nil,
+ nil, nil, 146, 146, 55, nil, 55, 55, nil, 55,
+ 55, nil, nil, nil, nil, nil, 55, 55, 138, nil,
+ 138, 138, nil, 138, 138, nil, nil, nil, nil, nil,
+ 138, 138, 15, nil, 15, 15, nil, 15, 15, nil,
+ nil, nil, nil, nil, 15, 15, 125, nil, 125, 125,
+ nil, 125, 125, nil, nil, nil, nil, nil, 125, 125,
+ 0, nil, 0, 0, nil, 0, 0, nil, nil, nil,
+ nil, nil, 0, 0, 26, 26, 26, 26, 26, 26 ]
+
+racc_action_pointer = [
+ 308, -2, nil, nil, nil, 174, 161, nil, nil, 132,
+ nil, 161, 153, 163, 122, 280, nil, nil, 150, nil,
+ nil, nil, 73, 137, nil, nil, 328, 3, nil, nil,
+ nil, nil, nil, 130, 128, nil, nil, 34, 118, 59,
+ 103, 131, 73, nil, 50, nil, 11, 20, nil, nil,
+ nil, nil, nil, nil, 11, 252, nil, nil, nil, nil,
+ nil, 42, 75, 6, 23, nil, 23, nil, 135, 138,
+ nil, nil, nil, 107, 120, nil, nil, nil, nil, 50,
+ 66, 119, nil, 0, nil, nil, nil, 26, 117, 9,
+ nil, 116, 55, 57, 9, nil, -18, 48, 46, 55,
+ 95, 105, 78, 83, 116, nil, 20, nil, nil, 40,
+ nil, nil, 18, 62, nil, nil, 143, 92, 127, nil,
+ nil, nil, nil, nil, nil, 294, nil, 89, 142, 57,
+ nil, nil, nil, nil, 114, 134, 148, 68, 266, 155,
+ nil, 224, 159, 196, 210, 179, 238, nil, 165, nil,
+ 148, nil, nil ]
+
+racc_action_default = [
+ -86, -86, -7, -20, -8, -86, -86, -19, -9, -86,
+ -10, -86, -80, -86, -86, -1, -2, -4, -86, -5,
+ -6, -33, -86, -39, -42, -40, -50, -34, -80, -38,
+ -36, -43, -41, -35, -86, -37, -67, -86, -70, -27,
+ -86, -86, -70, -3, -27, -31, -86, -86, -52, -53,
+ -54, -55, -56, -57, -86, -86, -65, -64, -58, -66,
+ -59, -86, -86, -70, -86, -71, -86, -21, -24, -19,
+ -25, -22, -28, -17, -84, -23, -15, -26, 153, -70,
+ -86, -84, -81, -86, -82, -83, -51, -86, -86, -86,
+ -61, -86, -77, -86, -86, -74, -86, -27, -86, -18,
+ -86, -86, -85, -86, -86, -78, -86, -32, -44, -45,
+ -63, -60, -86, -86, -72, -73, -86, -86, -14, -30,
+ -16, -11, -12, -29, -79, -86, -13, -86, -86, -86,
+ -62, -76, -75, -68, -86, -86, -86, -86, -86, -86,
+ -69, -86, -86, -86, -86, -86, -86, -47, -86, -46,
+ -86, -48, -49 ]
+
+racc_goto_table = [
+ 15, 29, 76, 33, 60, 70, 23, 34, 67, 123,
+ 93, 74, 83, 95, 45, 64, 81, 58, 101, 79,
+ 47, 127, 29, 73, 33, 106, 105, 23, 91, 90,
+ 100, 104, 77, 46, 43, 94, 13, nil, nil, 86,
+ 29, 123, 33, nil, 68, 23, 87, 29, nil, 33,
+ nil, nil, 23, nil, 29, 88, 33, 112, nil, 23,
+ nil, nil, 120, 35, nil, 70, 132, nil, 67, 118,
+ nil, nil, nil, nil, nil, 107, nil, nil, nil, 130,
+ nil, 110, nil, 29, 35, 33, nil, nil, 23, 29,
+ 119, 33, nil, nil, 23, nil, 117, nil, 29, nil,
+ 33, 75, 35, 23, 68, 131, nil, 43, nil, 35,
+ nil, nil, nil, 29, nil, 33, 35, nil, 23, 27,
+ nil, nil, nil, nil, nil, 135, nil, nil, nil, 29,
+ nil, 33, nil, nil, 23, 139, 43, 29, 143, 33,
+ 27, 145, 23, 142, 148, 35, 150, nil, nil, nil,
+ nil, 35, nil, nil, 43, nil, nil, 71, 27, nil,
+ 35, 75, 43, nil, 43, 27, nil, 43, nil, 43,
+ nil, nil, 27, nil, nil, 35, nil, nil, nil, nil,
+ nil, 92, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 35, nil, nil, nil, nil, nil, nil, nil, 35,
+ nil, 27, nil, nil, nil, nil, nil, 27, nil, nil,
+ nil, nil, nil, nil, nil, nil, 27, 71, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 27, nil, nil, 92, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 27, nil, nil,
+ nil, nil, nil, nil, nil, 27 ]
+
+racc_goto_check = [
+ 2, 4, 17, 16, 32, 21, 20, 28, 19, 23,
+ 37, 14, 25, 39, 22, 35, 14, 31, 15, 35,
+ 30, 29, 4, 12, 16, 15, 37, 20, 33, 32,
+ 13, 36, 22, 24, 3, 38, 1, nil, nil, 22,
+ 4, 23, 16, nil, 20, 20, 22, 4, nil, 16,
+ nil, nil, 20, nil, 4, 2, 16, 25, nil, 20,
+ nil, nil, 17, 6, nil, 21, 39, nil, 19, 14,
+ nil, nil, nil, nil, nil, 22, nil, nil, nil, 32,
+ nil, 22, nil, 4, 6, 16, nil, nil, 20, 4,
+ 22, 16, nil, nil, 20, nil, 2, nil, 4, nil,
+ 16, 6, 6, 20, 20, 22, nil, 3, nil, 6,
+ nil, nil, nil, 4, nil, 16, 6, nil, 20, 11,
+ nil, nil, nil, nil, nil, 2, nil, nil, nil, 4,
+ nil, 16, nil, nil, 20, 28, 3, 4, 2, 16,
+ 11, 2, 20, 28, 2, 6, 2, nil, nil, nil,
+ nil, 6, nil, nil, 3, nil, nil, 11, 11, nil,
+ 6, 6, 3, nil, 3, 11, nil, 3, nil, 3,
+ nil, nil, 11, nil, nil, 6, nil, nil, nil, nil,
+ nil, 11, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 6, nil, nil, nil, nil, nil, nil, nil, 6,
+ nil, 11, nil, nil, nil, nil, nil, 11, nil, nil,
+ nil, nil, nil, nil, nil, nil, 11, 11, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 11, nil, nil, 11, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 11, nil, nil,
+ nil, nil, nil, nil, nil, 11 ]
+
+racc_goto_pointer = [
+ nil, 36, 0, 19, 0, nil, 62, nil, nil, nil,
+ nil, 118, -16, -43, -28, -56, 2, -37, nil, -31,
+ 5, -34, -8, -93, 11, -34, nil, nil, 6, -88,
+ -6, -20, -33, -34, nil, -23, -48, -53, -28, -50 ]
+
+racc_goto_default = [
+ nil, nil, nil, 16, 17, 19, 20, 2, 4, 8,
+ 10, 11, nil, nil, nil, nil, 18, nil, 66, 21,
+ 6, 25, 26, 72, nil, nil, 30, 32, nil, nil,
+ nil, nil, nil, nil, 61, nil, nil, 65, nil, nil ]
+
+racc_token_table = {
+ false => 0,
+ Object.new => 1,
+ :LBRACK => 2,
+ :QTEXT => 3,
+ :RBRACK => 4,
+ :LBRACE => 5,
+ :RBRACE => 6,
+ :SYMBOL => 7,
+ :FARROW => 8,
+ :COMMA => 9,
+ :TRUE => 10,
+ :FALSE => 11,
+ :EQUALS => 12,
+ :QMARK => 13,
+ :LPAREN => 14,
+ :RPAREN => 15,
+ :ISEQUAL => 16,
+ :GREATEREQUAL => 17,
+ :GREATERTHAN => 18,
+ :LESSTHAN => 19,
+ :LESSEQUAL => 20,
+ :NOTEQUAL => 21,
+ :IF => 22,
+ :ELSE => 23,
+ :IMPORT => 24,
+ :DEFINE => 25,
+ :ELSIF => 26,
+ :VARIABLE => 27,
+ :CLASS => 28,
+ :INHERITS => 29,
+ :NODE => 30,
+ :BOOLEAN => 31,
+ :DOT => 32,
+ :COLON => 33,
+ :TYPE => 34,
+ :NAME => 35,
+ :SEMIC => 36 }
+
+racc_use_result_var = true
+
+racc_nt_base = 37
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+'$end',
+'error',
+'LBRACK',
+'QTEXT',
+'RBRACK',
+'LBRACE',
+'RBRACE',
+'SYMBOL',
+'FARROW',
+'COMMA',
+'TRUE',
+'FALSE',
+'EQUALS',
+'QMARK',
+'LPAREN',
+'RPAREN',
+'ISEQUAL',
+'GREATEREQUAL',
+'GREATERTHAN',
+'LESSTHAN',
+'LESSEQUAL',
+'NOTEQUAL',
+'IF',
+'ELSE',
+'IMPORT',
+'DEFINE',
+'ELSIF',
+'VARIABLE',
+'CLASS',
+'INHERITS',
+'NODE',
+'BOOLEAN',
+'DOT',
+'COLON',
+'TYPE',
+'NAME',
+'SEMIC',
+'$start',
+'program',
+'statements',
+'statement',
+'object',
+'assignment',
+'selector',
+'iftest',
+'import',
+'definition',
+'hostclass',
+'name',
+'objectinstances',
+'endsemi',
+'params',
+'endcomma',
+'type',
+'objectinst',
+'objectname',
+'quotedtext',
+'variable',
+'array',
+'rvalue',
+'param',
+'rvalues',
+'comma',
+'boolean',
+'objectref',
+'test',
+'elsifs',
+'testop',
+'svalues',
+'selectval',
+'sintvalues',
+'selectlhand',
+'argumentlist',
+'parent',
+'nothing',
+'arguments',
+'argument']
+
+Racc_debug_parser = false
+
+##### racc system variables end #####
+
+ # reduce 0 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 40
+ def _reduce_1( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :children => [val[0]]
+ )
+ end
+
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Puppet[:parseonly]
+ begin
+ if Puppet[:debug]
+ puts result.tree(0)
+ end
+ rescue NoMethodError => detail
+ Puppet.err detail
+ #exit(78)
+ end
+ #require 'puppet/parser/interpreter'
+ #result = Puppet::Server.new(result)
+ end
+ result
+ end
+.,.,
+
+ # reduce 2 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 54
+ def _reduce_3( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :children => [val[0],val[1]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 4 omitted
+
+ # reduce 5 omitted
+
+ # reduce 6 omitted
+
+ # reduce 7 omitted
+
+ # reduce 8 omitted
+
+ # reduce 9 omitted
+
+ # reduce 10 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 89
+ def _reduce_11( val, _values, result )
+ ary = val[2]
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ if ary[0].is_a?(AST::Leaf)
+ ary = [ary]
+ end
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file
+ )
+ ary.each { |instance|
+ Puppet.debug "Adding %s with name %s" % [val[0].value, instance[0].value]
+ result.push AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => instance[0],
+ :params => instance[1]
+ )
+ }
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 113
+ def _reduce_12( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ # an object but without a name
+ # this cannot be an instance of a library type
+
+ Puppet.debug "Adding %s" % val[0].value
+ # make a unique name for bookkeeping purposes
+ name = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => [val[0].value, "-", val[0].object_id].join('')
+ )
+
+ result = AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => name,
+ :params => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 126
+ def _reduce_13( val, _values, result )
+ # a template setting for a type
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid type"
+ raise Puppet::ParseError, "Invalid type"
+ end
+ result = AST::TypeDefaults.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :params => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 134
+ def _reduce_14( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ result
+ end
+.,.,
+
+ # reduce 15 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 148
+ def _reduce_16( val, _values, result )
+ if val[0][0].is_a?(AST::ASTArray)
+ val[0].push val[2]
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 17 omitted
+
+ # reduce 18 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 159
+ def _reduce_19( val, _values, result )
+ result = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 167
+ def _reduce_20( val, _values, result )
+ result = AST::Type.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+ # reduce 21 omitted
+
+ # reduce 22 omitted
+
+ # reduce 23 omitted
+
+ # reduce 24 omitted
+
+ # reduce 25 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 189
+ def _reduce_26( val, _values, result )
+ # this is distinct from referencing a variable
+ variable = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0].sub(/^\$/,'')
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :name => variable,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 198
+ def _reduce_27( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 198
+ def _reduce_28( val, _values, result )
+ result = val[0]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 211
+ def _reduce_29( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 226
+ def _reduce_30( val, _values, result )
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 31 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 239
+ def _reduce_32( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0].push(val[2])
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 33 omitted
+
+ # reduce 34 omitted
+
+ # reduce 35 omitted
+
+ # reduce 36 omitted
+
+ # reduce 37 omitted
+
+ # reduce 38 omitted
+
+ # reduce 39 omitted
+
+ # reduce 40 omitted
+
+ # reduce 41 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 257
+ def _reduce_42( val, _values, result )
+ result = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 265
+ def _reduce_43( val, _values, result )
+ result = AST::Boolean.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 275
+ def _reduce_44( val, _values, result )
+ result = AST::ObjectRef.new(
+ :pin => '[]',
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 285
+ def _reduce_45( val, _values, result )
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :statements => val[3]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 307
+ def _reduce_46( val, _values, result )
+ # make sure our elsifs are an array, as it will save lots of
+ # effort later
+ unless val[5].is_a?(AST::ASTArray)
+ val[5] = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[5]]
+ )
+ end
+
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[8],
+ :elsif => val[5]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 318
+ def _reduce_47( val, _values, result )
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[7]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 328
+ def _reduce_48( val, _values, result )
+ result = AST::If.new(
+ :pin => "elseif",
+ :test => val[1],
+ :file => @lexer.file,
+ :statements => val[3],
+ :line => @lexer.line
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 348
+ def _reduce_49( val, _values, result )
+ second = AST::If.new(
+ :pin => "elsif",
+ :test => val[2],
+ :statements => val[4],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(second)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],second]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 50 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 359
+ def _reduce_51( val, _values, result )
+ result = AST::Test.new(
+ :pin => val[1],
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :lhs => val[0],
+ :rhs => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 52 omitted
+
+ # reduce 53 omitted
+
+ # reduce 54 omitted
+
+ # reduce 55 omitted
+
+ # reduce 56 omitted
+
+ # reduce 57 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 376
+ def _reduce_58( val, _values, result )
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => val[0],
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 59 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 378
+ def _reduce_60( val, _values, result )
+ result = val[1]
+ result
+ end
+.,.,
+
+ # reduce 61 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 393
+ def _reduce_62( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 408
+ def _reduce_63( val, _values, result )
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 64 omitted
+
+ # reduce 65 omitted
+
+ # reduce 66 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 437
+ def _reduce_67( val, _values, result )
+ # importing files
+ # yuk, i hate keywords
+ # we'll probably have to have some kind of search path eventually
+ # but for now, just use a path relative to the file doing the importing
+ path = @lexer.file.sub(%r{[^/]+$},val[1])
+ parser = Puppet::Parser::Parser.new()
+ parser.stack = self.stack
+ Puppet.debug("importing %s" % path)
+ noimport = false
+ begin
+ parser.file = path
+ rescue Puppet::ImportError
+ Puppet.warning("Importing %s would result in an import loop" % path)
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ noimport = true
+ end
+ unless noimport
+ result = parser.parse
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 447
+ def _reduce_68( val, _values, result )
+ result = AST::CompDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[4]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 458
+ def _reduce_69( val, _values, result )
+ result = AST::ClassDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :parentclass => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[5]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 473
+ def _reduce_70( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+ result
+ end
+.,.,
+
+ # reduce 71 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 478
+ def _reduce_72( val, _values, result )
+ result = val[1]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 489
+ def _reduce_73( val, _values, result )
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 74 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 503
+ def _reduce_75( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 511
+ def _reduce_76( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 518
+ def _reduce_77( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ result
+ end
+.,.,
+
+ # reduce 78 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 527
+ def _reduce_79( val, _values, result )
+ result = AST::Name.new(
+ :value => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 536
+ def _reduce_80( val, _values, result )
+ name = val[0].sub(/^\$/,'')
+ result = AST::Variable.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => name
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 545
+ def _reduce_81( val, _values, result )
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new
+ result.push val[1]
+ end
+ result
+ end
+.,.,
+
+ # reduce 82 omitted
+
+ # reduce 83 omitted
+
+ # reduce 84 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 550
+ def _reduce_85( val, _values, result )
+ result = nil
+ result
+ end
+.,.,
+
+ def _reduce_none( val, _values, result )
+ result
+ end
+
+ end # class Parser
+
+ end # module Parser
+
+end # module Puppet
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
new file mode 100644
index 000000000..dcefdbcae
--- /dev/null
+++ b/lib/puppet/parser/scope.rb
@@ -0,0 +1,427 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the interpreter
+#
+# this builds our virtual pinball machine, into which we'll place our host-specific
+# information and out of which we'll receive our host-specific configuration
+
+require 'puppet/transportable'
+
+module Puppet
+ module Parser
+ class ScopeError < RuntimeError
+ attr_accessor :line, :file
+ end
+ #---------------------------------------------------------------
+ class Scope
+
+ attr_accessor :symtable, :objectable, :parent, :level, :interp
+ attr_accessor :name, :type
+
+ # i don't really know how to deal with a global scope yet, so
+ # i'm leaving it disabled
+ @@global = nil
+
+ @@hosttable = {}
+ @@settingtable = []
+ @@declarative = true
+
+ #------------------------------------------------------------
+ def Scope.declarative
+ return @@declarative
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def Scope.declarative=(val)
+ @@declarative = val
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def Scope.global
+ return @@global
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def child=(scope)
+ @children.push(scope)
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def declarative
+ return @@declarative
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def initialize(parent = nil, declarative = true)
+ @parent = parent
+ if @parent.nil?
+ @level = 1
+ @@declarative = declarative
+ else
+ @parent.child = self
+ @level = @parent.level + 1
+ @interp = @parent.interp
+ end
+
+ @children = []
+
+ @symtable = Hash.new(nil)
+ @typetable = Hash.new(nil)
+
+ # the defaultstable is a hash of hashes
+ @defaultstable = Hash.new { |dhash,type|
+ dhash[type] = Hash.new(nil)
+ }
+
+ @objectable = Hash.new { |typehash,typekey|
+ #hash[key] = TransObject.new(key)
+ typehash[typekey] = Hash.new { |namehash, namekey|
+ #Puppet.debug("Creating iobject with name %s and type %s" %
+ # [namekey,typekey])
+ namehash[namekey] = TransObject.new(namekey,typekey)
+ @children.push namehash[namekey]
+
+ # this has to be last, because the return value of the
+ # block is the actual hash
+ namehash[namekey]
+ }
+ }
+ @map = {
+ "variable" => @symtable,
+ "type" => @typetable,
+ "object" => @objectable,
+ "defaults" => @defaultstable
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # this method just abstracts the upwards-recursive nature of
+ # name resolution
+ # because different tables are different depths (e.g., flat, or
+ # hash of hashes), we pass in a code snippet that gets passed
+ # the table. It is assumed that the code snippet already has
+ # the name in it
+ def lookup(type,sub)
+ table = @map[type]
+ if table.nil?
+ error = Puppet::ParseError.new(
+ "Could not retrieve %s table at level %s" % [type,self.level]
+ )
+ error.stack = caller
+ raise error
+ end
+
+ if sub.is_a?(Proc) and obj = sub.call(table)
+ return obj
+ elsif table.include?(sub)
+ return table[sub]
+ elsif ! @parent.nil?
+ return @parent.lookup(type,sub)
+ else
+ return :undefined
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookuphost(name)
+ if @@hosttable.include?(name)
+ return @@hosttable[name]
+ else
+ return nil
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # collect all of the defaults set at any higher scopes
+ # this is a different type of lookup because it's additive --
+ # it collects all of the defaults, with defaults in closer scopes
+ # overriding those in later scopes
+ def lookupdefaults(type)
+ values = {}
+ # first collect the values from the parents
+ unless @parent.nil?
+ @parent.lookupdefaults(type).each { |var,value|
+ values[var] = value
+ }
+ end
+
+ # then override them with any current values
+ # this should probably be done differently
+ if @defaultstable.include?(type)
+ @defaultstable[type].each { |var,value|
+ values[var] = value
+ }
+ end
+ Puppet.debug "Got defaults for %s: %s" %
+ [type,values.inspect]
+ return values
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookuptype(name)
+ Puppet.debug "Looking up type %s" % name
+ value = self.lookup("type",name)
+ if value == :undefined
+ return nil
+ else
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # slightly different, because we're looking through a hash of hashes
+ def lookupobject(name,type)
+ Puppet.debug "Looking up object %s of type %s" % [name, type]
+ sub = proc { |table|
+ if table.include?(type)
+ if type[type].include?(name)
+ type[type][name]
+ end
+ else
+ nil
+ end
+ }
+ value = self.lookup("object",sub)
+ if value == :undefined
+ return nil
+ else
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookupvar(name)
+ Puppet.debug "Looking up variable %s" % name
+ value = self.lookup("variable", name)
+ if value == :undefined
+ error = Puppet::ParseError.new(
+ "Undefined variable '%s'" % name
+ )
+ error.stack = caller
+ raise error
+ else
+ #Puppet.debug "Value of '%s' is '%s'" % [name,value]
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def newscope
+ Puppet.debug "Creating new scope, level %s" % [self.level + 1]
+ return Puppet::Parser::Scope.new(self)
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def setdefaults(type,params)
+ table = @defaultstable[type]
+
+ # if we got a single param, it'll be in its own array
+ unless params[0].is_a?(Array)
+ params = [params]
+ end
+
+ params.each { |ary|
+ Puppet.debug "Default for %s is %s => %s" %
+ [type,ary[0].inspect,ary[1].inspect]
+ if @@declarative
+ if table.include?(ary[0])
+ error = Puppet::ParseError.new(
+ "Default already defined for %s { %s }" %
+ [type,ary[0]]
+ )
+ error.stack = caller
+ raise error
+ end
+ else
+ if table.include?(ary[0])
+ # we should maybe allow this warning to be turned off...
+ Puppet.warning "Replacing default for %s { %s }" %
+ [type,ary[0]]
+ end
+ end
+ table[ary[0]] = ary[1]
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def sethost(name,host)
+ @@hosttable[name] = host
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def settype(name,ltype)
+ @typetable[name] = ltype
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # when we have an 'eval' function, we should do that instead
+ # for now, we only support variables in strings
+ def strinterp(string)
+ newstring = string.dup
+ regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
+ #Puppet.debug("interpreting '%s'" % string)
+ while match = regex.match(newstring) do
+ if match[1]
+ newstring.sub!(regex,self.lookupvar(match[1]))
+ elsif match[2]
+ newstring.sub!(regex,self.lookupvar(match[2]))
+ else
+ raise 'fuh?'
+ end
+ end
+ #Puppet.debug("result is '%s'" % newstring)
+ return newstring
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # this is kind of quirky, because it doesn't differentiate between
+ # creating a new object and adding params to an existing object
+ # it doesn't solve the real problem, though: cases like file recursion,
+ # where one statement explicitly modifies an object, and another
+ # statement modifies it because of recursion
+ def setobject(type, name, params, file, line)
+ obj = self.lookupobject(name,type)
+ if obj == :undefined or obj.nil?
+ obj = @objectable[type][name]
+
+ # only set these if we've created the object, which is the
+ # most common case
+ obj.file = file
+ obj.line = line
+ end
+
+ # now add the params to whatever object we've found, whether
+ # it was in a higher scope or we just created it
+ # it will not be obvious where these parameters are from, that is,
+ # which file they're in or whatever
+ params.each { |var,value|
+ obj[var] = value
+ }
+ return obj
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def setvar(name,value)
+ Puppet.debug "Setting '%s' to '%s' at level %s" %
+ [name.inspect,value,self.level]
+ if @@declarative and @symtable.include?(name)
+ error = Puppet::ParseError.new(
+ "Cannot reassign variable %s" % name
+ )
+ error.stack = caller
+ raise error
+ else
+ if @symtable.include?(name)
+ Puppet.warning "Reassigning %s to %s" % [name,value]
+ end
+ @symtable[name] = value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # I'm pretty sure this method could be obviated, but it doesn't
+ # really seem worth it
+ def to_trans
+ Puppet.debug "Translating scope %s at level %s" %
+ [self.object_id,self.level]
+
+ results = []
+
+ @children.each { |child|
+ if child.is_a?(Scope)
+ cresult = child.to_trans
+ Puppet.debug "Got %s from scope %s" %
+ [cresult.class,child.object_id]
+
+ # get rid of the arrayness
+ unless cresult.is_a?(TransBucket)
+ cresult.each { |result|
+ results.push(result)
+ }
+ else
+ results.push(cresult)
+ end
+ elsif child.is_a?(TransObject)
+ results.push(child)
+ else
+ error = Puppet::DevError.new(
+ "Puppet::Parse::Scope cannot handle objects of type %s" %
+ child.class
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ results = results.reject { |child|
+ # if a scope didn't result in any objects, we get some nils
+ # just get rid of them
+ child.nil?
+ }
+
+ # if we have a name and type, then make a TransBucket, which
+ # becomes a component
+ # else, just stack all of the objects into the current bucket
+ if defined? @name
+ bucket = TransBucket.new
+ bucket.name = @name
+
+ # it'd be nice not to have to do this...
+ results.each { |result|
+ #Puppet.debug "Result type is %s" % result.class
+ bucket.push(result)
+ }
+ if defined? @type
+ bucket.type = @type
+ else
+ error = Puppet::ParseError.new(
+ "No type for scope %s" % @name
+ )
+ error.stack = caller
+ raise error
+ end
+ Puppet.debug "TransBucket with name %s and type %s in scope %s" %
+ [@name,@type,self.object_id]
+
+ # now find metaparams
+ @symtable.each { |var,value|
+ if Puppet::Type.metaparam?(var.intern)
+ #Puppet.debug("Adding metaparam %s" % var)
+ bucket.param(var,value)
+ else
+ #Puppet.debug("%s is not a metaparam" % var)
+ end
+ }
+ #Puppet.debug "Returning bucket %s from scope %s" %
+ # [bucket.name,self.object_id]
+ return bucket
+ else
+ #Puppet.debug "nameless scope; just returning a list"
+ return results
+ end
+ end
+ #------------------------------------------------------------
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/puppet/server.rb b/lib/puppet/server.rb
new file mode 100644
index 000000000..28bb1bd46
--- /dev/null
+++ b/lib/puppet/server.rb
@@ -0,0 +1,129 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the server
+#
+# allow things to connect to us and communicate, and stuff
+
+require 'puppet'
+require 'puppet/daemon'
+require 'puppet/servlet'
+require 'puppet/master'
+require 'puppet/ca'
+
+$noservernetworking = false
+
+begin
+ require 'webrick'
+ require 'webrick/https'
+ require 'cgi'
+ require 'xmlrpc/server'
+ require 'xmlrpc/client'
+rescue LoadError => detail
+ $noservernetworking = detail
+end
+
+module Puppet
+ class ServerError < RuntimeError; end
+ #---------------------------------------------------------------
+ if $noservernetworking
+ Puppet.err "Could not create server: %s" % $noservernetworking
+ else
+ class ServerStatus
+ attr_reader :ca
+
+ def self.interface
+ XMLRPC::Service::Interface.new("status") { |iface|
+ iface.add_method("int status()")
+ }
+ end
+
+ def initialize(hash = {})
+ end
+
+ def status(status = nil, request = nil)
+ Puppet.warning "Returning status"
+ return 1
+ end
+ end
+
+ class Server < WEBrick::HTTPServer
+ include Puppet::Daemon
+
+ # a bit of a hack for now, but eh, wadda ya gonna do?
+ @@handlers = {
+ :Master => Puppet::Master,
+ :CA => Puppet::CA,
+ :Status => Puppet::ServerStatus
+ }
+
+ def self.inithandler(handler,args)
+ unless @@handlers.include?(handler)
+ raise ServerError, "Invalid handler %s" % handler
+ end
+
+ hclass = @@handlers[handler]
+
+ obj = hclass.new(args)
+ return obj
+ end
+
+ def initialize(hash = {})
+ hash[:Port] ||= Puppet[:masterport]
+ hash[:Logger] ||= self.httplog
+ hash[:AccessLog] ||= [
+ [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
+ [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
+ ]
+
+ if hash.include?(:Handlers)
+ unless hash[:Handlers].is_a?(Hash)
+ raise ServerError, "Handlers must have arguments"
+ end
+
+ @handlers = hash[:Handlers].collect { |handler, args|
+ self.class.inithandler(handler, args)
+ }
+ else
+ raise ServerError, "A server must have handlers"
+ end
+
+ # okay, i need to retrieve my cert and set it up, somehow
+ # the default case will be that i'm also the ca
+ if ca = @handlers.find { |handler| handler.is_a?(Puppet::CA) }
+ @driver = ca
+ @secureinit = true
+ self.fqdn
+ end
+
+ unless self.readcert
+ unless self.requestcert
+ raise Puppet::Error, "Cannot start without certificates"
+ end
+ end
+
+ hash[:SSLCertificate] = @cert
+ hash[:SSLPrivateKey] = @key
+ hash[:SSLStartImmediately] = true
+ hash[:SSLEnable] = true
+ hash[:SSLCACertificateFile] = @cacertfile
+ hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_NONE
+ hash[:SSLCertName] = nil
+
+ super(hash)
+
+ # this creates a new servlet for every connection,
+ # but all servlets have the same list of handlers
+ # thus, the servlets can have their own state -- passing
+ # around the requests and such -- but the handlers
+ # have a global state
+
+ # mount has to be called after the server is initialized
+ self.mount("/RPC2", Puppet::Servlet, @handlers)
+ end
+ end
+ end
+
+ #---------------------------------------------------------------
+end
diff --git a/lib/puppet/servlet.rb b/lib/puppet/servlet.rb
new file mode 100644
index 000000000..3fa965fa3
--- /dev/null
+++ b/lib/puppet/servlet.rb
@@ -0,0 +1,110 @@
+require 'xmlrpc/server'
+
+module Puppet
+ class ServletError < RuntimeError; end
+ class Servlet < XMLRPC::WEBrickServlet
+ attr_accessor :request
+
+ # this is just a duplicate of the normal method; it's here for
+ # debugging when i need it
+ def self.get_instance(server, *options)
+ self.new(server, *options)
+ end
+
+ def initialize(server, handlers)
+ #Puppet.info server.inspect
+
+ # the servlet base class does not consume any arguments
+ # and its BasicServer base class only accepts a 'class_delim'
+ # option which won't change in Puppet at all
+ # thus, we don't need to pass any args to our base class,
+ # and we can consume them all ourselves
+ super()
+
+ handlers.each { |handler|
+ Puppet.debug "adding handler for %s" % handler.class
+ self.add_handler(handler.class.interface, handler)
+ }
+
+ @request = nil
+ self.set_service_hook { |obj, *args|
+ #raise "crap!"
+ if @request
+ args.push @request
+ #obj.call(args, @request)
+ end
+ begin
+ obj.call(*args)
+ rescue => detail
+ Puppet.warning obj.inspect
+ Puppet.err "Could not call: %s" % detail.to_s
+ end
+ }
+ end
+
+ def service(request, response)
+ @request = request
+ if @request.client_cert
+ Puppet.info "client cert is %s" % @request.client_cert
+ end
+ if @request.server_cert
+ Puppet.info "server cert is %s" % @request.server_cert
+ end
+ #p @request
+ begin
+ super
+ rescue => detail
+ Puppet.err "Could not service request: %s: %s" %
+ [detail.class, detail]
+ end
+ @request = nil
+ end
+
+ private
+
+ # this is pretty much just a copy of the original method but with more
+ # feedback
+ def dispatch(methodname, *args)
+ #Puppet.warning "dispatch on %s called with %s" %
+ # [methodname, args.inspect]
+ for name, obj in @handler
+ if obj.kind_of? Proc
+ unless methodname == name
+ Puppet.debug "obj is proc but %s != %s" %
+ [methodname, name]
+ next
+ end
+ else
+ unless methodname =~ /^#{name}(.+)$/
+ Puppet.debug "methodname did not match"
+ next
+ end
+ unless obj.respond_to? $1
+ Puppet.debug "methodname does not respond to %s" % $1
+ next
+ end
+ obj = obj.method($1)
+ end
+
+ if check_arity(obj, args.size)
+ if @service_hook.nil?
+ return obj.call(*args)
+ else
+ return @service_hook.call(obj, *args)
+ end
+ else
+ Puppet.debug "arity is incorrect"
+ end
+ end
+
+ if @default_handler.nil?
+ raise XMLRPC::FaultException.new(
+ ERR_METHOD_MISSING,
+ "Method #{methodname} missing or wrong number of parameters!"
+ )
+ else
+ @default_handler.call(methodname, *args)
+ end
+ end
+ end
+end
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index d77e9017b..107e9e906 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -409,9 +409,13 @@ class Type < Puppet::Element
# does the name reflect a valid parameter?
def self.validparameter?(name)
unless defined? @parameters
- raise "Class %s has not defined parameters" % self
+ raise Puppet::DevError, "Class %s has not defined parameters" % self
+ end
+ if @parameters.include?(name) or @@metaparams.include?(name)
+ return true
+ else
+ return false
end
- return @parameters.include?(name)
end
#---------------------------------------------------------------
@@ -474,6 +478,7 @@ class Type < Puppet::Element
raise Puppet::Error.new("Got nil value for %s" % name)
end
if Puppet::Type.metaparam?(name)
+ @parameters[name] = value
# call the metaparam method
self.send(("meta" + name.id2name + "="),value)
elsif stateklass = self.class.validstate?(name)
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index c0e05f5a8..4eab8ca5b 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -4,14 +4,42 @@
require 'digest/md5'
require 'etc'
+require 'uri'
require 'fileutils'
require 'puppet/type/state'
+require 'puppet/fileserver'
module Puppet
# we first define all of the state that our file will use
# because the objects must be defined for us to use them in our
# definition of the file object
class State
+ class PFileType < Puppet::State
+ require 'etc'
+ @doc = "A read-only state to check the file type."
+ @name = :type
+
+ def should=(value)
+ raise Puppet::Error, ":type is read-only"
+ end
+
+ def retrieve
+ if stat = @parent.stat(true)
+ @is = stat.ftype
+ else
+ @is = -1
+ end
+
+ # so this state is never marked out of sync
+ @should = @is
+ end
+
+
+ def sync
+ raise Puppet::Error, ":type is read-only"
+ end
+ end
+
class PFileCreate < Puppet::State
require 'etc'
@doc = "Whether to create files that don't currently exist.
@@ -209,7 +237,7 @@ module Puppet
if @is == -1
# if they're copying, then we won't worry about the file
# not existing yet
- unless @parent.state(:copy) or @parent.state(:create)
+ unless @parent.state(:source) or @parent.state(:create)
Puppet.warning "File %s does not exist -- cannot checksum" %
@parent.name
end
@@ -280,16 +308,16 @@ module Puppet
def retrieve
# if we're not root, then we can't chown anyway
- unless Process.uid == 0
- @parent.delete(self.name)
- @should = nil
- @is = nil
- unless defined? @@notified
- Puppet.notice "Cannot manage ownership unless running as root"
- @@notified = true
- return
- end
- end
+# unless Process.uid == 0
+# @parent.delete(self.name)
+# @should = nil
+# @is = nil
+# unless defined? @@notified
+# Puppet.notice "Cannot manage ownership unless running as root"
+# @@notified = true
+# return
+# end
+# end
unless stat = @parent.stat(true)
@is = -1
@@ -307,7 +335,9 @@ module Puppet
Puppet.notice "Cannot manage ownership unless running as root"
#@parent.delete(self.name)
@@notified = true
- return
+ end
+ if @parent.state(:owner)
+ @parent.delete(:owner)
end
raise Puppet::Error.new(
"Cannot manage ownership unless running as root"
@@ -588,54 +618,172 @@ module Puppet
end
end
- class PFileCopy < Puppet::State
+ class PFileSource < Puppet::State
attr_accessor :source, :local
@doc = "Copy a file over the current file. Uses `checksum` to
determine when a file should be copied. This is largely a support
state for the `source` parameter, which is what should generally
be used instead of `copy`."
- @name = :copy
+ @name = :source
+
+ def desc2hash(line, params)
+ args = {}
+
+ values = line.split("\t").collect { |value|
+ if value =~ /^[0-9]+$/
+ value.to_i
+ else
+ value
+ end
+ }
- def retrieve
- sum = nil
- if sum = @parent.state(:checksum)
- if sum.is
- if sum.is == -1
- sum.retrieve
+ params.zip(values).each { |param, value|
+ args[param] = value
+ }
+
+ if args[:type] == "directory"
+ args.delete(:checksum)
+ end
+ args
+ end
+
+ def hash2child(hash, source, recurse)
+ # "/" == self
+ if hash[:name] == "/"
+ if hash[:type] == "directory"
+ else
+ self[:source] = source
+ end
+ hash.each { |param, value|
+ next if param == :name
+ next if param == :type
+
+ unless self.state(param)
+ self[param] = value
end
- @is = sum.is
+ }
+ if source =~ /Filesourcetest/
+ p self
+ end
+ # we can now skip this object, and the rest is
+ # pretty much related to children
+ return
+ end
+
+ name = File.join(self.name, hash[:name])
+ if child = @children.find { |child|
+ child.name == name }
+ else # the child does not yet exist
+ #hash.delete(:name)
+ sum = nil
+ if hash[:type] == "directory"
+ hash[:create] = "directory"
+ hash[:source] = @parameters[:source] + hash[:name]
+ hash[:recurse] = recurse
else
- @is = -1
+ hash[:source] = source + hash[:name]
+ #sum = hash[:checksum]
+ #hash.delete(:checksum)
end
- else
- @is = -1
+ hash.delete(:type)
+
+ name = hash[:name].sub(/^\//, '') # rm leading /
+ hash.delete(:name)
+
+ Puppet.warning "Found new file %s under %s" %
+ [name.inspect, self.name]
+ self.newchild(name, hash)
end
end
- def should=(source)
- @local = true # redundant for now
- @source = source
- type = Puppet::Type.type(:file)
+ def describe
+ source = @source
+
+ sourceobj, path = @parent.uri2obj(source)
+
+ server = sourceobj.server
+
+ desc = server.describe(path)
+
+ args = {}
+ Puppet::Type::PFile::PINPARAMS.zip(desc.split("\t")).each { |param, value|
+ if value =~ /^[0-9]+$/
+ value = value.to_i
+ end
+ args[param] = value
+ }
+
+ return args
+ end
+
+ def retrieve
+ sum = nil
+
+ @stats = self.describe
+
+ @stats.each { |stat, value|
+ next if stat == :checksum
+ next if stat == :type
- sourcesum = nil
- stat = File.stat(@source)
- case stat.ftype
+ unless @parent.argument?(stat)
+ if state = @parent.state(stat)
+ state.should = value
+ else
+ @parent[stat] = value
+ end
+ end
+ }
+ case @stats[:type]
when "file":
- unless sourcesum = type[@source].state(:checksum).is
- raise Puppet::Error.new(
- "Could not retrieve checksum of source %s" %
- @source
- )
+ if sum = @parent.state(:checksum)
+ if sum.is
+ if sum.is == -1
+ sum.retrieve
+ end
+ @is = sum.is
+ else
+ @is = -1
+ end
+ else
+ @is = -1
+ end
+
+ @should = @stats[:checksum]
+
+ if state = @parent.state(:create)
+ unless state.should == "file"
+ Puppet.notice(
+ "File %s had both create and source enabled" %
+ @parent.name
+ )
+ @parent.delete(:create)
+ end
end
when "directory":
- raise Puppet::Error.new(
- "Somehow got told to copy dir %s" % @parent.name)
+ if state = @parent.state(:create)
+ unless state.should == "directory"
+ state.should = "directory"
+ end
+ else
+ @parent[:create] = "directory"
+ @parent.state(:create).retrieve
+ end
+ # we'll let the :create state do our work
+ @should = true
+ @is = true
else
- raise Puppet::Error.new(
- "Cannot use files of type %s as source" % stat.ftype)
+ Puppet.err "Cannot use files of type %s as sources" %
+ @stats[:type]
+ @should = true
+ @is = true
end
+ end
- @should = sourcesum
+ def should=(source)
+ @source = source
+
+ # stupid hack for now; it'll get overriden
+ @should = source
end
def sync
@@ -673,64 +821,140 @@ module Puppet
@backed = true
end
+ unless @stats[:type] == "file"
+ raise Puppet::DevError, "Got told to copy non-file %s" %
+ @parent.name
+ end
+
+ sourceobj, path = @parent.uri2obj(@source)
+
+ contents = sourceobj.server.retrieve(path)
+
+ unless sourceobj.server.local
+ contents = CGI.unescape(contents)
+ end
+
+ if contents == ""
+ Puppet.notice "Could not retrieve contents for %s" %
+ @source
+ end
+
+ begin
+ if FileTest.exists?(@parent.name)
+ if FileTest.exists?(@parent.name + bak)
+ Puppet.warning "Deleting backup of %s" %
+ @parent.name
+ File.unlink(@parent.name + bak)
+ end
+
+ # rename the existing one
+ File.rename(
+ @parent.name,
+ @parent.name + ".puppet-bak"
+ )
+ # move the new file into place
+ args = [@parent.name,
+ File::CREAT | File::WRONLY | File::TRUNC]
+
+ # try to create it with the correct modes to start
+ # we should also be changing our effective uid/gid, but...
+ if @parent[:mode]
+ args.push @parent[:mode]
+ end
+ File.open(*args) { |f|
+ f.print contents
+ }
+ # if we've made a backup, then delete the old file
+ if @backed
+ #Puppet.err "Unlinking backup"
+ File.unlink(@parent.name + bak)
+ #else
+ #Puppet.err "Not unlinking backup"
+ end
+ else
+ # the easy case
+ args = [@parent.name,
+ File::CREAT | File::WRONLY | File::TRUNC]
+
+ # try to create it with the correct modes to start
+ # we should also be changing our effective uid/gid, but...
+ if @parent[:mode]
+ args.push @parent[:mode]
+ end
+ File.open(*args) { |f|
+ f.print contents
+ }
+ end
+ rescue => detail
+ # since they said they want a backup, let's error out
+ # if we couldn't make one
+ error = Puppet::Error.new("Could not copy %s to %s: %s" %
+ [@source, @parent.name, detail.message])
+ raise error
+ end
+
#Puppet.notice "@is: %s; @should: %s" % [@is,@should]
#Puppet.err "@is: %s; @should: %s" % [@is,@should]
# okay, we've now got whatever backing up done we might need
# so just copy the files over
- if @local
- stat = File.stat(@source)
- case stat.ftype
- when "file":
- begin
- if FileTest.exists?(@parent.name)
- # get the file here
- FileUtils.cp(@source, @parent.name + ".tmp")
- if FileTest.exists?(@parent.name + bak)
- Puppet.warning "Deleting backup of %s" %
+
+ f = false
+ if f
+ if @local
+ stat = File.stat(@source)
+ case stat.ftype
+ when "file":
+ begin
+ if FileTest.exists?(@parent.name)
+ # get the file here
+ FileUtils.cp(@source, @parent.name + ".tmp")
+ if FileTest.exists?(@parent.name + bak)
+ Puppet.warning "Deleting backup of %s" %
+ @parent.name
+ File.unlink(@parent.name + bak)
+ end
+ # rename the existing one
+ File.rename(
+ @parent.name,
+ @parent.name + ".puppet-bak"
+ )
+ # move the new file into place
+ File.rename(
+ @parent.name + ".tmp",
@parent.name
- File.unlink(@parent.name + bak)
- end
- # rename the existing one
- File.rename(
- @parent.name,
- @parent.name + ".puppet-bak"
- )
- # move the new file into place
- File.rename(
- @parent.name + ".tmp",
- @parent.name
- )
- # if we've made a backup, then delete the old file
- if @backed
- #Puppet.err "Unlinking backup"
- File.unlink(@parent.name + bak)
- #else
- #Puppet.err "Not unlinking backup"
+ )
+ # if we've made a backup, then delete the old file
+ if @backed
+ #Puppet.err "Unlinking backup"
+ File.unlink(@parent.name + bak)
+ #else
+ #Puppet.err "Not unlinking backup"
+ end
+ else
+ # the easy case
+ FileUtils.cp(@source, @parent.name)
end
- else
- # the easy case
- FileUtils.cp(@source, @parent.name)
+ rescue => detail
+ # since they said they want a backup, let's error out
+ # if we couldn't make one
+ error = Puppet::Error.new("Could not copy %s to %s: %s" %
+ [@source, @parent.name, detail.message])
+ raise error
end
- rescue => detail
- # since they said they want a backup, let's error out
- # if we couldn't make one
- error = Puppet::Error.new("Could not copy %s to %s: %s" %
- [@source, @parent.name, detail.message])
- raise error
+ when "directory":
+ raise Puppet::Error.new(
+ "Somehow got told to copy directory %s" %
+ @parent.name)
+ when "link":
+ dest = File.readlink(@source)
+ Puppet::State::PFileLink.create(@dest,@parent.path)
+ else
+ raise Puppet::Error.new(
+ "Cannot use files of type %s as source" % stat.ftype)
end
- when "directory":
- raise Puppet::Error.new(
- "Somehow got told to copy directory %s" %
- @parent.name)
- when "link":
- dest = File.readlink(@source)
- Puppet::State::PFileLink.create(@dest,@parent.path)
else
- raise Puppet::Error.new(
- "Cannot use files of type %s as source" % stat.ftype)
+ raise Puppet::Error.new("Somehow got a non-local source")
end
- else
- raise Puppet::Error.new("Somehow got a non-local source")
end
return :file_changed
end
@@ -746,18 +970,19 @@ module Puppet
@states = [
Puppet::State::PFileCreate,
Puppet::State::PFileChecksum,
- Puppet::State::PFileCopy,
+ Puppet::State::PFileSource,
Puppet::State::PFileUID,
Puppet::State::PFileGroup,
- Puppet::State::PFileMode
+ Puppet::State::PFileMode,
+ Puppet::State::PFileType
]
@parameters = [
:path,
:backup,
:linkmaker,
- :recurse,
:source,
+ :recurse,
:filebucket
]
@@ -782,7 +1007,7 @@ module Puppet
*file* is supported as a protocol)."
@paramdoc[:filebucket] = "A repository for backing up files, including
- over the network. Argument must the name of an existing
+ over the network. Argument must be the name of an existing
filebucket."
@name = :file
@@ -790,11 +1015,12 @@ module Puppet
@depthfirst = false
- if Process.uid == 0
- @@pinparams = [:mode, :owner, :group, :checksum]
- else
- @@pinparams = [:mode, :group, :checksum]
- end
+ #if Process.uid == 0
+ @@pinparams = [:mode, :type, :owner, :group, :checksum]
+ PINPARAMS = [:mode, :type, :owner, :group, :checksum]
+ #else
+ # @@pinparams = [:mode, :type, :group, :checksum]
+ #end
def self.recursecompare(source,dest)
@@ -825,6 +1051,10 @@ module Puppet
end
end
+ def argument?(arg)
+ @arghash.include?(arg)
+ end
+
def initialize(hash)
@arghash = self.argclean(hash)
@arghash.delete(self.class.namevar)
@@ -882,13 +1112,13 @@ module Puppet
end
args = @arghash.dup
+ #args = {}
args[:path] = path
unless hash.include?(:source) # it's being manually overridden
-
if args.include?(:source)
- #Puppet.notice "Rewriting source for %s" % path
+ Puppet.err "Rewriting source for %s" % path
name = File.basename(path)
dirname = args[:source]
#Puppet.notice "Old: %s" % args[:source]
@@ -937,16 +1167,26 @@ module Puppet
klass = Puppet::Type::PFile
end
if child = klass[path]
- #raise "Ruh-roh"
+ unless @children.include?(child)
+ raise Puppet::Error,
+ "Planned child file %s already exists with parent %s" %
+ [path, child.parent]
+ end
args.each { |var,value|
next if var == :path
next if var == :name
- child[var] = value
+ # behave idempotently
+ unless child[var] == value
+ #Puppet.warning "%s is %s, not %s" % [var, child[var], value]
+ child[var] = value
+ end
}
else # create it anew
#notice "Creating new file with args %s" % args.inspect
begin
child = klass.new(args)
+ child.parent = self
+ @children << child
rescue Puppet::Error => detail
Puppet.notice(
"Cannot manage %s: %s" %
@@ -963,9 +1203,6 @@ module Puppet
child = nil
end
end
- if child
- child.parent = self
- end
return child
end
@@ -984,8 +1221,8 @@ module Puppet
#Puppet.info "%s is already in memory" % @source
if obj.managed?
raise Puppet::Error.new(
- "You cannot currently use managed files as sources;" +
- "%s is managed" % path
+ "You cannot currently use managed file %s as a source" %
+ path.inspect
)
else
# verify they're looking up the correct info
@@ -1031,18 +1268,29 @@ module Puppet
return obj
end
- # pinning is like recursion, except that it's recursion across
- # the pinned file's tree, instead of our own
- # if recursion is turned off, then this whole thing is pretty easy
- def paramsource=(source)
+ def disabledparamsource=(source)
@parameters[:source] = source
@arghash.delete(:source)
@source = source
# verify we support the proto
- if @source =~ /^file:\/\/(\/.+)/
- @source = $1
- elsif @source =~ /(\w+):\/\/(\/.+)/
+ if @source =~ /^\/(\/.+)/
+ @sourcetype = "file"
+ elsif @source =~ /^(\w):\/\/(\/.+)/
+ @sourcetype = $1
+ @source = $2
+ else
+ raise Puppet::Error, "Invalid source %s" % @source
+ end
+
+
+ case @sourcetype
+ when "file":
+ when "http", "https":
+ if @parameters[:recurse]
+ raise Puppet::Error.new("HTTP is not supported recursively")
+ end
+ else
raise Puppet::Error.new("Protocol %s not supported" % $1)
end
@@ -1158,9 +1406,7 @@ module Puppet
else # they have it but we don't
fullname = File.join(self.name, name)
if kid = self.newchild(name,:source => child.name)
- if @children.include?(kid)
- Puppet.notice "Child already included"
- else
+ unless @children.include?(kid)
self.push kid
end
end
@@ -1169,19 +1415,12 @@ module Puppet
}
else
- self[:copy] = @sourceobj.name
+ self[:source] = @sourceobj.name
end
end
- def paramrecurse=(value)
- @parameters[:recurse] = value
- unless FileTest.exist?(self.name) and self.stat.directory?
- #Puppet.info "%s is not a directory; not recursing" %
- # self.name
- return
- end
-
- recurse = value
+ def recurse
+ recurse = @parameters[:recurse]
# we might have a string, rather than a number
if recurse.is_a?(String)
if recurse =~ /^[0-9]+$/
@@ -1194,6 +1433,24 @@ module Puppet
# are we at the end of the recursion?
if recurse == 0
+ Puppet.info "finished recursing"
+ return
+ end
+
+ if recurse.is_a?(Integer)
+ recurse -= 1
+ end
+
+ self.localrecurse(recurse)
+ if @states.include?(:source)
+ self.sourcerecurse(recurse)
+ end
+ end
+
+ def localrecurse(recurse)
+ unless FileTest.exist?(self.name) and self.stat.directory?
+ #Puppet.info "%s is not a directory; not recursing" %
+ # self.name
return
end
@@ -1211,51 +1468,79 @@ module Puppet
Dir.foreach(self.name) { |file|
next if file =~ /^\.\.?/ # skip . and ..
if child = self.newchild(file, :recurse => recurse)
- if @children.include?(child)
- Puppet.notice "Child already included"
- else
+ unless @children.include?(child)
self.push child
added.push file
end
end
}
+ end
+
+ def sourcerecurse(recurse)
+ source = @states[:source].source
+
+ sourceobj, path = uri2obj(source)
- # here's where we handle sources; it's special, because it can
- # require us to build a structure of files that don't yet exist
- if @parameters.include?(:source)
- unless FileTest.directory?(@parameters[:source])
- raise Puppet::Error("Cannot use file %s as a source for a dir" %
- @parameters[:source])
+ # we'll set this manually as necessary
+ if @arghash.include?(:create)
+ @arghash.delete(:create)
+ end
+
+ # okay, we've got our source object; now we need to
+ # build up a local file structure to match the remote
+ # one
+
+ server = sourceobj.server
+ sum = "md5"
+ if state = self.state(:checksum)
+ sum = state.checktype
+ end
+ r = false
+ if recurse
+ unless recurse == 0
+ r = 1
end
- Dir.foreach(@parameters[:source]) { |file|
- next if file =~ /^\.\.?$/ # skip . and ..
- unless added.include?(file)
- Puppet.notice "Adding absent source-file %s" % file
- if child = self.newchild(file,
- :recurse => recurse,
- :source => File.join(self.name, file)
- )
- if @children.include?(child)
- Puppet.notice "Child already included"
- else
- self.push child
- added.push file
- end
- end
- end
- }
end
+
+ #Puppet.warning "Listing path %s" % path.inspect
+ desc = server.list(path, r)
+
+ #params = @@pinparams.dup
+ #params.unshift(:name)
+ desc.split("\n").each { |line|
+ file, type = line.split("\t")
+ next if file == "/"
+ name = file.sub(/^\//, '')
+ #Puppet.warning "child name is %s" % name
+ args = {:source => source + file}
+ if type == file
+ args[:recurse] = nil
+ end
+ self.newchild(name, args)
+ #self.newchild(hash, source, recurse)
+ #hash2child(hash, source, recurse)
+ }
end
# a wrapper method to make sure the file exists before doing anything
def retrieve
+ if @states.include?(:source)
+ @states[:source].retrieve
+ end
+
+ if @parameters.include?(:recurse)
+ #self[:recurse] = @parameters[:recurse]
+ self.recurse
+ end
+
unless stat = self.stat(true)
- Puppet.debug "File %s does not exist" % self.name
+ Puppet.debug "rFile %s does not exist" % self.name
@states.each { |name,state|
state.is = -1
}
return
end
+
super
end
@@ -1264,8 +1549,6 @@ module Puppet
begin
@stat = File.stat(self.name)
rescue Errno::ENOENT => error
- #Puppet.debug "Failed to stat %s: No such file or directory" %
- # [self.name]
@stat = nil
rescue => error
Puppet.debug "Failed to stat %s: %s" %
@@ -1276,30 +1559,57 @@ module Puppet
return @stat
end
- end # Puppet::Type::PFile
- end # Puppet::Type
-
- class PFileSource
- attr_accessor :name
- @sources = Hash.new(nil)
-
- def PFileSource.[]=(name,sub)
- @sources[name] = sub
- end
+ def uri2obj(source)
+ sourceobj = FileSource.new
+ path = nil
+ if source =~ /^\//
+ source = "file://localhost/%s" % source
+ sourceobj.mount = "localhost"
+ sourceobj.local = true
+ end
+ begin
+ uri = URI.parse(source)
+ rescue => detail
+ raise Puppet::Error, "Could not understand source %s: %s" %
+ [source, detail.to_s]
+ end
- def PFileSource.[](name)
- return @sources[name]
- end
+ case uri.scheme
+ when "file":
+ sourceobj.server = Puppet::FileServer.new(
+ :Local => true
+ )
+ sourceobj.server.mount("/", "localhost")
+ path = "/localhost" + uri.path
+ when "puppet":
+ args = { :Server => uri.host }
+ if uri.port
+ args[:Port] = uri.port
+ end
+ sourceobj.server = Puppet::NetworkClient.new(args)
- def initialize(name)
- @name = name
+ tmp = uri.path
+ if tmp =~ %r{^/(\w+)}
+ sourceobj.mount = $1
+ path = tmp.sub(%r{^/\w+},'') || "/"
+ else
+ raise Puppet::Error, "Invalid source path %s" % tmp
+ end
+ else
+ raise Puppet::Error,
+ "Got other recursive file proto %s" % uri.scheme
+ return
+ end
- if block_given?
- yield self
+ return [sourceobj, path.sub(/\/\//, '/')]
end
+ end # Puppet::Type::PFile
+ end # Puppet::Type
- PFileSource[name] = self
- end
+ # the filesource class can't include the path, because the path
+ # changes for every file instance
+ class FileSource
+ attr_accessor :mount, :root, :server, :local
end
end
diff --git a/test/certmgr/tc_certmgr.rb b/test/certmgr/tc_certmgr.rb
new file mode 100755
index 000000000..e00acd067
--- /dev/null
+++ b/test/certmgr/tc_certmgr.rb
@@ -0,0 +1,275 @@
+#!/usr/bin/ruby
+
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/sslcertificates.rb'
+require 'test/unit'
+require 'puppettest'
+
+# so, what kind of things do we want to test?
+
+# we don't need to test function, since we're confident in the
+# library tests. We do, however, need to test how things are actually
+# working in the language.
+
+# so really, we want to do things like test that our ast is correct
+# and test whether we've got things in the right scopes
+
+class TestCertMgr < Test::Unit::TestCase
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ #@dir = File.join(Puppet[:certdir], "testing")
+ @dir = "/tmp/puppetcertestingdir"
+ Puppet[:ssldir] = @dir
+ system("mkdir -p %s" % @dir)
+ @@tmpfiles = [@dir]
+ end
+
+ def mkPassFile()
+ keyfile = File.join(@dir, "tmpkeyfile")
+ @@tmpfiles << keyfile
+ unless FileTest.exists?(@dir)
+ system("mkdir -p %s" % @dir)
+ end
+ File.open(keyfile, "w", 0600) { |f|
+ f.print "as;dklj23rlkjzdflij23wr"
+ }
+
+ return keyfile
+ end
+
+ def mkCA
+ ca = nil
+ Puppet[:ssldir] = @dir
+ assert_nothing_raised {
+ ca = Puppet::SSLCertificates::CA.new()
+ }
+
+ return ca
+ end
+
+ def teardown
+ @@tmpfiles.each { |f|
+ if FileTest.exists?(f)
+ system("rm -rf %s" % f)
+ end
+ }
+ end
+
+ def testCreateSelfSignedCertificate
+ cert = nil
+ name = "testing"
+ newcert = proc {
+ Puppet::SSLCertificates::Certificate.new(
+ :name => name,
+ :selfsign => true
+ )
+ }
+ assert_nothing_raised {
+ cert = newcert.call()
+ }
+ assert_nothing_raised {
+ cert.mkselfsigned
+ }
+
+ assert_raise(Puppet::Error) {
+ cert.mkselfsigned
+ }
+
+ assert_nothing_raised {
+ cert.write
+ }
+
+ assert(FileTest.exists?(cert.certfile))
+
+ assert_nothing_raised {
+ cert.delete
+ }
+
+ assert_nothing_raised {
+ cert = newcert.call()
+ }
+ assert_nothing_raised {
+ cert.mkselfsigned
+ }
+
+ assert_nothing_raised {
+ cert.delete
+ }
+
+ end
+
+ def disabled_testCreateEncryptedSelfSignedCertificate
+ cert = nil
+ name = "testing"
+ keyfile = mkPassFile
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => name,
+ :selfsign => true,
+ :capass => keyfile
+ )
+ }
+ assert_nothing_raised {
+ cert.mkselfsigned
+ }
+ assert_nothing_raised {
+ cert.mkhash
+ }
+
+ assert_raise(Puppet::Error) {
+ cert.mkselfsigned
+ }
+
+ assert(FileTest.exists?(cert.certfile))
+ assert(FileTest.exists?(cert.hash))
+
+ assert_nothing_raised {
+ cert.delete
+ }
+
+ assert_nothing_raised {
+ cert.mkselfsigned
+ }
+
+ assert_nothing_raised {
+ cert.delete
+ }
+
+ end
+
+ def testCreateCA
+ ca = nil
+ assert_nothing_raised {
+ ca = Puppet::SSLCertificates::CA.new()
+ }
+
+ # make the CA again and verify it doesn't fail because everything
+ # still exists
+ assert_nothing_raised {
+ ca = Puppet::SSLCertificates::CA.new()
+ }
+
+ end
+
+ def testSignCert
+ ca = mkCA()
+
+ cert = nil
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => "signedcertest",
+ :state => "TN",
+ :city => "Nashville",
+ :country => "US",
+ :email => "luke@madstop.com",
+ :org => "Reductive",
+ :ou => "Development",
+ :encrypt => mkPassFile()
+ )
+
+ }
+
+ assert_nothing_raised {
+ cert.mkcsr
+ }
+
+ signedcert = nil
+ cacert = nil
+
+ assert_nothing_raised {
+ signedcert, cacert = ca.sign(cert.csr)
+ }
+
+ assert_instance_of(OpenSSL::X509::Certificate, signedcert)
+ assert_instance_of(OpenSSL::X509::Certificate, cacert)
+
+ assert_nothing_raised {
+ cert.cert = signedcert
+ cert.cacert = cacert
+ cert.write
+ }
+ #system("find %s" % Puppet[:ssldir])
+
+ output = nil
+ assert_nothing_raised {
+ output = %x{openssl verify -CApath #{Puppet[:certdir]} -purpose sslserver #{cert.certfile}}
+ }
+
+ assert_equal($?,0)
+ assert_equal("\n", output)
+ end
+
+ def mkcert(hostname)
+ cert = nil
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(:name => hostname)
+ cert.mkcsr
+ }
+
+ return cert
+ end
+
+
+ def test_interactiveca
+ ca = nil
+ Puppet[:ssldir] = "/tmp/puppetinteractivecatest"
+ @@tmpfiles.push Puppet[:ssldir]
+
+ assert_nothing_raised {
+ ca = Puppet::SSLCertificates::CA.new
+ }
+
+ # basic initialization
+ hostname = "test.hostname.com"
+ cert = mkcert(hostname)
+
+ # create the csr
+ csr = nil
+ assert_nothing_raised {
+ csr = cert.mkcsr
+ }
+
+ assert_nothing_raised {
+ ca.storeclientcsr(csr)
+ }
+
+ # store it
+ pulledcsr = nil
+ assert_nothing_raised {
+ pulledcsr = ca.getclientcsr(hostname)
+ }
+
+ assert_equal(csr.to_pem, pulledcsr.to_pem)
+
+ signedcert = nil
+ assert_nothing_raised {
+ signedcert, cacert = ca.sign(csr)
+ }
+
+ assert_instance_of(OpenSSL::X509::Certificate, signedcert)
+ newsignedcert = nil
+ assert_nothing_raised {
+ newsignedcert, cacert = ca.getclientcert(hostname)
+ }
+
+ assert(newsignedcert)
+
+ assert_equal(signedcert.to_pem, newsignedcert.to_pem)
+ end
+
+ def test_cafailures
+ ca = mkCA()
+ cert = cacert = nil
+ assert_nothing_raised {
+ cert, cacert = ca.getclientcert("nohost")
+ }
+ assert_nil(cert)
+ end
+end
diff --git a/test/executables/tc_puppetbin.rb b/test/executables/tc_puppetbin.rb
new file mode 100755
index 000000000..b42f5aa6a
--- /dev/null
+++ b/test/executables/tc_puppetbin.rb
@@ -0,0 +1,39 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+# $ID: $
+
+require 'puppet'
+require 'puppet/server'
+require 'puppet/sslcertificates'
+require 'test/unit'
+require 'puppettest.rb'
+
+# add the bin directory to our search path
+ENV["PATH"] += ":" + File.join($puppetbase, "bin")
+
+# and then the library directories
+libdirs = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+}
+ENV["RUBYLIB"] = libdirs.join(":")
+
+class TestPuppetBin < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ def test_version
+ output = nil
+ assert_nothing_raised {
+ output = %x{puppet --version}.chomp
+ }
+ assert(output == Puppet.version)
+ end
+end
diff --git a/test/executables/tc_puppetca.rb b/test/executables/tc_puppetca.rb
new file mode 100755
index 000000000..e657de483
--- /dev/null
+++ b/test/executables/tc_puppetca.rb
@@ -0,0 +1,90 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/server'
+require 'puppet/sslcertificates'
+require 'test/unit'
+require 'puppettest.rb'
+
+# $Id$
+
+# ok, we have to add the bin directory to our search path
+ENV["PATH"] += ":" + File.join($puppetbase, "bin")
+
+# and then the library directories
+libdirs = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+}
+ENV["RUBYLIB"] = libdirs.join(":")
+
+class TestPuppetCA < Test::Unit::TestCase
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ @@tmpfiles = []
+ end
+
+ def teardown
+ @@tmpfiles.flatten.each { |file|
+ if File.exists? file
+ system("rm -rf %s" % file)
+ end
+ }
+ end
+
+ def mkcert(hostname)
+ cert = nil
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => hostname
+ )
+ cert.mkcsr
+ }
+
+ return cert
+ end
+
+ def test_signing
+ ca = nil
+ Puppet[:ssldir] = "/tmp/puppetcatest"
+ @@tmpfiles << Puppet[:ssldir]
+ Puppet[:autosign] = false
+ assert_nothing_raised {
+ ca = Puppet::CA.new()
+ }
+ #Puppet.warning "SSLDir is %s" % Puppet[:ssldir]
+ #system("find %s" % Puppet[:ssldir])
+
+ cert = mkcert("host.test.com")
+ resp = nil
+ assert_nothing_raised {
+ resp = ca.getcert(cert.csr.to_pem)
+ }
+ assert_equal(["",""], resp)
+ #Puppet.warning "SSLDir is %s" % Puppet[:ssldir]
+ #system("find %s" % Puppet[:ssldir])
+
+ output = nil
+ assert_nothing_raised {
+ output = %x{puppetca --list --ssldir=#{Puppet[:ssldir]} 2>&1}.chomp.split("\n")
+ }
+ #Puppet.warning "SSLDir is %s" % Puppet[:ssldir]
+ #system("find %s" % Puppet[:ssldir])
+ assert_equal($?,0)
+ assert_equal(%w{host.test.com}, output)
+ assert_nothing_raised {
+ output = %x{puppetca --sign -a --ssldir=#{Puppet[:ssldir]}}.chomp.split("\n")
+ }
+ assert_equal($?,0)
+ assert_equal([], output)
+ assert_nothing_raised {
+ output = %x{puppetca --list --ssldir=#{Puppet[:ssldir]}}.chomp.split("\n")
+ }
+ assert_equal($?,0)
+ assert_equal([], output)
+ end
+end
diff --git a/test/executables/tc_puppetd.rb b/test/executables/tc_puppetd.rb
new file mode 100755
index 000000000..38467332d
--- /dev/null
+++ b/test/executables/tc_puppetd.rb
@@ -0,0 +1,88 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/server'
+require 'test/unit'
+require 'puppettest.rb'
+require 'socket'
+require 'facter'
+
+# $Id$
+
+# ok, we have to add the bin directory to our search path
+ENV["PATH"] += ":" + File.join($puppetbase, "bin")
+
+# and then the library directories
+libdirs = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+}
+ENV["RUBYLIB"] = libdirs.join(":")
+
+class TestPuppetDExe < Test::Unit::TestCase
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ @@tmpfiles = []
+ @@tmppids = []
+ end
+
+ def teardown
+ @@tmpfiles.flatten.each { |file|
+ if File.exists? file
+ system("rm -rf %s" % file)
+ end
+ }
+
+ @@tmppids.each { |pid|
+ %x{kill -INT #{pid} 2>/dev/null}
+ }
+ end
+
+ def startmaster
+ file = File.join($puppetbase, "examples", "code", "head")
+ output = nil
+ assert_nothing_raised {
+ output = %x{puppetmasterd --manifest #{file}}.chomp
+ }
+ assert($? == 0)
+ @@tmppids << $?.pid
+ assert_equal("", output)
+ end
+
+ def stopmaster
+ ps = Facter["ps"].value || "ps -ef"
+
+ pid = nil
+ %x{#{ps}}.chomp.split(/\n/).each { |line|
+ if line =~ /puppetmasterd/
+ ary = line.split(" ")
+ pid = ary[1].to_i
+ end
+ }
+ assert(pid)
+
+ assert_nothing_raised {
+ Process.kill("-INT", pid)
+ }
+ end
+
+ def test_normalstart
+ startmaster
+ output = nil
+ assert_nothing_raised {
+ output = %x{puppetd --server localhost}.chomp
+ }
+ assert($? == 0)
+ assert_equal("", output)
+
+ assert_nothing_raised {
+ socket = TCPSocket.new("127.0.0.1", Puppet[:masterport])
+ socket.close
+ }
+ stopmaster
+ end
+end
diff --git a/test/executables/tc_puppetmasterd.rb b/test/executables/tc_puppetmasterd.rb
new file mode 100755
index 000000000..5ef802151
--- /dev/null
+++ b/test/executables/tc_puppetmasterd.rb
@@ -0,0 +1,171 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/server'
+require 'puppet/daemon'
+require 'test/unit'
+require 'puppettest.rb'
+require 'socket'
+require 'facter'
+
+# $Id$
+
+# ok, we have to add the bin directory to our search path
+ENV["PATH"] += ":" + File.join($puppetbase, "bin")
+
+# and then the library directories
+libdirs = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+}
+ENV["RUBYLIB"] = libdirs.join(":")
+
+class TestPuppetMasterD < Test::Unit::TestCase
+ def getcerts
+ include Puppet::Daemon
+ if self.readcerts
+ return [@cert, @key, @cacert, @cacertfile]
+ else
+ raise "Couldn't read certs"
+ end
+ end
+
+ def setup
+ if __FILE__ == $0
+ Puppet[:loglevel] = :debug
+ end
+ @@tmpfiles = []
+ end
+
+ def startmasterd(args)
+ output = nil
+ cmd = "puppetmasterd %s" % args
+ #if Puppet[:debug]
+ # Puppet.debug "turning daemon debugging on"
+ # cmd += " --debug"
+ #end
+ assert_nothing_raised {
+ output = %x{puppetmasterd #{args}}.chomp
+ }
+ assert($? == 0)
+ assert_equal("", output)
+ end
+
+ def stopmasterd(running = true)
+ ps = Facter["ps"].value || "ps -ef"
+
+ pid = nil
+ %x{#{ps}}.chomp.split(/\n/).each { |line|
+ if line =~ /puppetmasterd --manifest/
+ ary = line.split(" ")
+ pid = ary[1].to_i
+ end
+ }
+
+ # we default to mandating that it's running, but teardown
+ # doesn't require that
+ if running or pid
+ assert(pid)
+
+ assert_nothing_raised {
+ Process.kill("-INT", pid)
+ }
+ end
+ end
+
+ def teardown
+ @@tmpfiles.flatten.each { |file|
+ if File.exists?(file)
+ system("rm -rf %s" % file)
+ end
+ }
+
+ stopmasterd(false)
+ end
+
+ def test_normalstart
+ file = File.join($puppetbase, "examples", "code", "head")
+ startmasterd("--manifest #{file}")
+
+ assert_nothing_raised {
+ socket = TCPSocket.new("127.0.0.1", Puppet[:masterport])
+ socket.close
+ }
+
+ client = nil
+ assert_nothing_raised() {
+ client = XMLRPC::Client.new("localhost", "/RPC2", Puppet[:masterport],
+ nil, nil, nil, nil, true, 5)
+ }
+ retval = nil
+
+ assert_nothing_raised() {
+ retval = client.call("status.status", "")
+ }
+ assert_equal(1, retval)
+ facts = {}
+ Facter.each { |p,v|
+ facts[p] = v
+ }
+ textfacts = CGI.escape(Marshal::dump(facts))
+ assert_nothing_raised() {
+ #Puppet.notice "calling status"
+ #retval = client.call("status.status", "")
+ retval = client.call("puppetmaster.getconfig", textfacts)
+ }
+
+ objects = nil
+ assert_nothing_raised {
+ Marshal::load(CGI.unescape(retval))
+ }
+ #stopmasterd
+ end
+
+ def disabled_test_sslconnection
+ #file = File.join($puppetbase, "examples", "code", "head")
+ #startmasterd("--manifest #{file}")
+
+ #assert_nothing_raised {
+ # socket = TCPSocket.new("127.0.0.1", Puppet[:masterport])
+ # socket.close
+ #}
+
+ client = nil
+ cert, key, cacert, cacertfile = getcerts()
+
+ assert_nothing_raised() {
+ client = Net::HTTP.new("localhost", Puppet[:masterport])
+ client.cert = cert
+ client.key = key
+ client.ca_file = cacertfile
+ client.use_ssl = true
+ client.start_immediately = true
+ }
+ retval = nil
+
+ assert_nothing_raised() {
+ retval = client.nothing
+ }
+ assert_equal(1, retval)
+ facts = {}
+ Facter.each { |p,v|
+ facts[p] = v
+ }
+ textfacts = CGI.escape(Marshal::dump(facts))
+ assert_nothing_raised() {
+ #Puppet.notice "calling status"
+ #retval = client.call("status.status", "")
+ retval = client.call("puppetmaster.getconfig", textfacts)
+ }
+
+ objects = nil
+ assert_nothing_raised {
+ Marshal::load(CGI.unescape(retval))
+ }
+ #stopmasterd
+ end
+end
diff --git a/test/language/tc_scope.rb b/test/language/tc_scope.rb
new file mode 100755
index 000000000..ece621629
--- /dev/null
+++ b/test/language/tc_scope.rb
@@ -0,0 +1,264 @@
+#!/usr/bin/ruby
+
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/parser/interpreter'
+require 'puppet/parser/parser'
+require 'puppet/client'
+require 'test/unit'
+require 'puppettest'
+
+# so, what kind of things do we want to test?
+
+# we don't need to test function, since we're confident in the
+# library tests. We do, however, need to test how things are actually
+# working in the language.
+
+# so really, we want to do things like test that our ast is correct
+# and test whether we've got things in the right scopes
+
+class TestScope < Test::Unit::TestCase
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ end
+
+ def to_ary(hash)
+ hash.collect { |key,value|
+ [key,value]
+ }
+ end
+
+ def test_variables
+ scope = nil
+ over = "over"
+
+ scopes = []
+ vars = []
+ values = {}
+ ovalues = []
+
+ 10.times { |index|
+ # slap some recursion in there
+ scope = Puppet::Parser::Scope.new(scope)
+ scopes.push scope
+
+ var = "var%s" % index
+ value = rand(1000)
+ ovalue = rand(1000)
+
+ ovalues.push ovalue
+
+ vars.push var
+ values[var] = value
+
+ # set the variable in the current scope
+ assert_nothing_raised {
+ scope.setvar(var,value)
+ }
+
+ # this should override previous values
+ assert_nothing_raised {
+ scope.setvar(over,ovalue)
+ }
+
+ assert_equal(value,scope.lookupvar(var))
+
+ #puts "%s vars, %s scopes" % [vars.length,scopes.length]
+ i = 0
+ vars.zip(scopes) { |v,s|
+ # this recurses all the way up the tree as necessary
+ val = nil
+ oval = nil
+
+ # look up the values using the bottom scope
+ assert_nothing_raised {
+ val = scope.lookupvar(v)
+ oval = scope.lookupvar(over)
+ }
+
+ # verify they're correct
+ assert_equal(values[v],val)
+ assert_equal(ovalue,oval)
+
+ # verify that we get the most recent value
+ assert_equal(ovalue,scope.lookupvar(over))
+
+ # verify that they aren't available in upper scopes
+ if parent = s.parent
+ assert_raise(Puppet::ParseError) {
+ parent.lookupvar(v)
+ }
+
+ # and verify that the parent sees its correct value
+ assert_equal(ovalues[i - 1],parent.lookupvar(over))
+ end
+ i += 1
+ }
+ }
+ end
+
+ def test_declarative
+ # set to declarative
+ top = Puppet::Parser::Scope.new(nil,true)
+ sub = Puppet::Parser::Scope.new(top)
+
+ assert_nothing_raised {
+ top.setvar("test","value")
+ }
+ assert_raise(Puppet::ParseError) {
+ top.setvar("test","other")
+ }
+ assert_nothing_raised {
+ sub.setvar("test","later")
+ }
+ assert_raise(Puppet::ParseError) {
+ top.setvar("test","yeehaw")
+ }
+ end
+
+ def test_notdeclarative
+ # set to not declarative
+ top = Puppet::Parser::Scope.new(nil,false)
+ sub = Puppet::Parser::Scope.new(top)
+
+ assert_nothing_raised {
+ top.setvar("test","value")
+ }
+ assert_nothing_raised {
+ top.setvar("test","other")
+ }
+ assert_nothing_raised {
+ sub.setvar("test","later")
+ }
+ assert_nothing_raised {
+ sub.setvar("test","yayness")
+ }
+ end
+
+ def test_defaults
+ scope = nil
+ over = "over"
+
+ scopes = []
+ vars = []
+ values = {}
+ ovalues = []
+
+ defs = Hash.new { |hash,key|
+ hash[key] = Hash.new(nil)
+ }
+
+ prevdefs = Hash.new { |hash,key|
+ hash[key] = Hash.new(nil)
+ }
+
+ params = %w{a list of parameters that could be used for defaults}
+
+ types = %w{a set of types that could be used to set defaults}
+
+ 10.times { |index|
+ scope = Puppet::Parser::Scope.new(scope)
+ scopes.push scope
+
+ tmptypes = []
+
+ # randomly create defaults for a random set of types
+ tnum = rand(5)
+ tnum.times { |t|
+ # pick a type
+ #Puppet.debug "Type length is %s" % types.length
+ #s = rand(types.length)
+ #Puppet.debug "Type num is %s" % s
+ #type = types[s]
+ #Puppet.debug "Type is %s" % s
+ type = types[rand(types.length)]
+ if tmptypes.include?(type)
+ Puppet.debug "Duplicate type %s" % type
+ redo
+ else
+ tmptypes.push type
+ end
+
+ Puppet.debug "type is %s" % type
+
+ d = {}
+
+ # randomly assign some parameters
+ num = rand(4)
+ num.times { |n|
+ param = params[rand(params.length)]
+ if d.include?(param)
+ Puppet.debug "Duplicate param %s" % param
+ redo
+ else
+ d[param] = rand(1000)
+ end
+ }
+
+ # and then add a consistent type
+ d["always"] = rand(1000)
+
+ d.each { |var,val|
+ defs[type][var] = val
+ }
+
+ assert_nothing_raised {
+ scope.setdefaults(type,to_ary(d))
+ }
+ fdefs = nil
+ assert_nothing_raised {
+ fdefs = scope.lookupdefaults(type)
+ }
+
+ # now, make sure that reassignment fails if we're
+ # in declarative mode
+ assert_raise(Puppet::ParseError) {
+ scope.setdefaults(type,[%w{always funtest}])
+ }
+
+ # assert that we have collected the same values
+ assert_equal(defs[type],fdefs)
+
+ # now assert that our parent still finds the same defaults
+ # it got last time
+ if parent = scope.parent
+ unless prevdefs[type].nil?
+ assert_equal(prevdefs[type],parent.lookupdefaults(type))
+ end
+ end
+ d.each { |var,val|
+ prevdefs[type][var] = val
+ }
+ }
+ }
+ end
+
+ def test_strinterp
+ scope = Puppet::Parser::Scope.new(nil)
+
+ assert_nothing_raised {
+ scope.setvar("test","value")
+ }
+ val = nil
+ assert_nothing_raised {
+ val = scope.strinterp("string ${test}")
+ }
+ assert_equal("string value", val)
+
+ assert_nothing_raised {
+ val = scope.strinterp("string ${test} ${test} ${test}")
+ }
+ assert_equal("string value value value", val)
+
+ assert_nothing_raised {
+ val = scope.strinterp("string $test ${test} $test")
+ }
+ assert_equal("string value value value", val)
+ end
+end
diff --git a/test/language/tc_snippets.rb b/test/language/tc_snippets.rb
new file mode 100755
index 000000000..da1172f63
--- /dev/null
+++ b/test/language/tc_snippets.rb
@@ -0,0 +1,317 @@
+#!/usr/bin/ruby -w
+
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '..'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/parser/interpreter'
+require 'puppet/parser/parser'
+require 'puppet/client'
+require 'puppet/server'
+require 'test/unit'
+require 'puppettest'
+
+# so, what kind of things do we want to test?
+
+# we don't need to test function, since we're confident in the
+# library tests. We do, however, need to test how things are actually
+# working in the language.
+
+# so really, we want to do things like test that our ast is correct
+# and test whether we've got things in the right scopes
+
+class TestSnippets < Test::Unit::TestCase
+ $snippetbase = File.join($puppetbase, "examples", "code", "snippets")
+
+ def file2ast(file)
+ parser = Puppet::Parser::Parser.new()
+ parser.file = file
+ ast = parser.parse
+
+ return ast
+ end
+
+ def snippet2ast(text)
+ parser = Puppet::Parser::Parser.new()
+ parser.string = text
+ ast = parser.parse
+
+ return ast
+ end
+
+ def client
+ args = {
+ :Listen => false
+ }
+ Puppet::Client.new(args)
+ end
+
+ def ast2scope(ast)
+ interp = Puppet::Parser::Interpreter.new(
+ :ast => ast,
+ :client => client()
+ )
+ scope = Puppet::Parser::Scope.new()
+ ast.evaluate(scope)
+
+ return scope
+ end
+
+ def scope2objs(scope)
+ objs = scope.to_trans
+ end
+
+ def snippet2scope(snippet)
+ ast = snippet2ast(snippet)
+ scope = ast2scope(ast)
+ end
+
+ def snippet2objs(snippet)
+ ast = snippet2ast(snippet)
+ scope = ast2scope(ast)
+ objs = scope2objs(scope)
+ end
+
+ def states(type)
+ states = []
+
+ type.buildstatehash
+ type.validstates.each { |name,state|
+ states.push name
+ }
+
+ #if states.length == 0
+ # raise "%s has no states" % type
+ #end
+
+ states
+ end
+
+ def metaparams(type)
+ mparams = []
+ Puppet::Type.eachmetaparam { |param|
+ mparams.push param
+ }
+
+ mparams
+ end
+
+ def params(type)
+ params = []
+ type.parameters.each { |name,state|
+ params.push name
+ }
+
+ params
+ end
+
+ def randthing(thing,type)
+ list = self.send(thing,type)
+ list[rand(list.length)]
+ end
+
+ def randeach(type)
+ [:states, :metaparams, :params].collect { |thing|
+ randthing(thing,type)
+ }
+ end
+
+ @@snippets = {
+ true => [
+ %{File { mode => 755 }}
+ ],
+ }
+
+ def disabled_test_defaults
+ Puppet::Type.eachtype { |type|
+ next if type.name == :puppet or type.name == :component
+
+ rands = randeach(type)
+
+ name = type.name.to_s.capitalize
+
+ [0..1, 0..2].each { |range|
+ params = rands[range]
+ paramstr = params.collect { |param|
+ "%s => fake" % param
+ }.join(", ")
+
+ str = "%s { %s }" % [name, paramstr]
+
+ scope = nil
+ assert_nothing_raised {
+ scope = snippet2scope(str)
+ }
+
+ defaults = nil
+ assert_nothing_raised {
+ defaults = scope.lookupdefaults(name)
+ }
+
+ p defaults
+
+ params.each { |param|
+ puts "%s => '%s'" % [name,param]
+ assert(defaults.include?(param))
+ }
+ }
+ }
+ end
+
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ @@tmpfiles = []
+ end
+
+ def teardown
+ @@tmpfiles.flatten.each { |file|
+ if FileTest.exists?(file)
+ File.unlink(file)
+ end
+ }
+ Puppet::Type.allclear
+ end
+
+ # this is here in case no tests get defined; otherwise we get a warning
+ def test_nothing
+ end
+
+ def snippet_filecreate(trans)
+ %w{a b c d}.each { |letter|
+ file = "/tmp/create%stest" % letter
+ Puppet.info "testing %s" % file
+ assert(Puppet::Type::PFile[file])
+ assert(FileTest.exists?(file))
+ @@tmpfiles << file
+ }
+ %w{a b}.each { |letter|
+ file = "/tmp/create%stest" % letter
+ assert(File.stat(file).mode & 007777 == 0755)
+ }
+
+ assert_nothing_raised {
+ trans.rollback
+ }
+ %w{a b c d}.each { |letter|
+ file = "/tmp/create%stest" % letter
+ assert(! FileTest.exists?(file))
+ }
+ end
+
+ def snippet_simpledefaults(trans)
+ file = "/tmp/defaulttest"
+ @@tmpfiles << file
+ assert(FileTest.exists?(file))
+ assert(File.stat(file).mode & 007777 == 0755)
+
+ assert_nothing_raised {
+ trans.rollback
+ }
+ assert(! FileTest.exists?(file))
+ end
+
+ def snippet_simpleselector(trans)
+ files = %w{a b c d}.collect { |letter|
+ "/tmp/snippetselect%stest" % letter
+ }
+ @@tmpfiles << files
+
+ files.each { |file|
+ assert(FileTest.exists?(file))
+ assert(File.stat(file).mode & 007777 == 0755)
+ @@tmpfiles << file
+ }
+
+ assert_nothing_raised {
+ trans.rollback
+ }
+ files.each { |file|
+ assert(! FileTest.exists?(file))
+ }
+ end
+
+ def snippet_classpathtest(trans)
+ file = "/tmp/classtest"
+ @@tmpfiles << file
+
+ assert(FileTest.exists?(file))
+
+ obj = nil
+ assert_nothing_raised {
+ obj = Puppet::Type::PFile[file]
+ }
+
+ assert_nothing_raised {
+ assert_equal(%w{puppet[top] testing[testingname] component[componentname] /tmp/classtest}, obj.path)
+ #Puppet.err obj.path
+ }
+
+ assert_nothing_raised {
+ trans.rollback
+ }
+ assert(! FileTest.exists?(file))
+ end
+
+ def snippet_argumentdefaults(trans)
+ file1 = "/tmp/argumenttest1"
+ file2 = "/tmp/argumenttest2"
+ #@@tmpfiles << file
+
+ assert(FileTest.exists?(file1))
+ assert(File.stat(file1).mode & 007777 == 0755)
+
+ assert(FileTest.exists?(file2))
+ assert(File.stat(file2).mode & 007777 == 0644)
+ end
+
+ def disabled_snippet_dirchmod(trans)
+ dirs = %w{a b}.collect { |letter|
+ "/tmp/dirchmodtest%s" % letter
+ }
+
+ @@tmpfiles << dirs
+
+ dirs.each { |dir|
+ assert(FileTest.directory?(dir))
+ }
+
+ assert(File.stat("/tmp/dirchmodtesta").mode & 007777 == 0755)
+ assert(File.stat("/tmp/dirchmodtestb").mode & 007777 == 0700)
+
+ assert_nothing_raised {
+ trans.rollback
+ }
+ end
+
+ # XXX this is the answer
+ Dir.entries($snippetbase).sort.each { |file|
+ next if file =~ /^\./
+
+ mname = "snippet_" + file
+ if self.method_defined?(mname)
+ #eval("alias %s %s" % [testname, mname])
+ testname = ("test_" + mname).intern
+ self.send(:define_method, testname) {
+ # first parse the file
+ server = Puppet::Master.new(
+ :File => File.join($snippetbase, file),
+ :Local => true
+ )
+ client = Puppet::Client.new(
+ :Server => server,
+ :Cache => false
+ )
+
+ assert(client.local)
+ client.getconfig()
+ trans = client.config()
+ self.send(mname, trans)
+ }
+ mname = mname.intern
+ #eval("alias %s %s" % [testname, mname])
+ end
+ }
+end
diff --git a/test/other/tc_metrics.rb b/test/other/tc_metrics.rb
index 7ba03d554..9db8683e9 100644
--- a/test/other/tc_metrics.rb
+++ b/test/other/tc_metrics.rb
@@ -1,7 +1,7 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
- $puppetbase = "../../../../language/trunk/"
+ $puppetbase = "../.."
end
require 'puppet/metric'
diff --git a/test/parser/tc_lexer.rb b/test/parser/tc_lexer.rb
new file mode 100644
index 000000000..b8fd3e23b
--- /dev/null
+++ b/test/parser/tc_lexer.rb
@@ -0,0 +1,112 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/parser/lexer'
+require 'test/unit'
+require 'puppettest.rb'
+
+# $Id$
+
+#%q{service("telnet") = \{
+# port => "23",
+# protocol => "tcp",
+# name => "telnet",
+#\}
+#} => [[:NAME, "service"], [:LPAREN, "("], [:DQUOTE, "\""], [:NAME, "telnet"], [:DQUOTE, "\""], [:RPAREN, ")"], [:EQUALS, "="], [:lbrace, "{"], [:NAME, "port"], [:FARROW, "=>"], [:DQUOTE, "\""], [:NAME, "23"], [:DQUOTE, "\""], [:COMMA, ","], [:NAME, "protocol"], [:FARROW, "=>"], [:DQUOTE, "\""], [:NAME, "tcp"], [:DQUOTE, "\""], [:COMMA, ","], [:NAME, "name"], [:FARROW, "=>"], [:DQUOTE, "\""], [:NAME, "telnet"], [:DQUOTE, "\""], [:COMMA, ","], [:RBRACE, "}"]]
+
+class TestLexer < Test::Unit::TestCase
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ @lexer = Puppet::Parser::Lexer.new()
+ end
+
+ def test_simple_lex
+ strings = {
+%q{\\} => [[:BACKSLASH,"\\"],[false,false]],
+%q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"],[false,false]],
+%q{returned scanner test
+} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"],[false,false]]
+ }
+ strings.each { |str,ary|
+ @lexer.string = str
+ assert_equal(
+ ary,
+ @lexer.fullscan()
+ )
+ }
+ end
+
+ def test_quoted_strings
+ strings = {
+%q{a simple "scanner" test
+} => [[:NAME,"a"],[:NAME,"simple"],[:QTEXT,"scanner"],[:NAME,"test"],[false,false]],
+%q{a harder "scanner test"
+} => [[:NAME,"a"],[:NAME,"harder"],[:QTEXT,"scanner test"],[false,false]],
+%q{a hardest "scanner \"test\""
+} => [[:NAME,"a"],[:NAME,"hardest"],[:QTEXT,'scanner "test"'],[false,false]],
+%q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:QTEXT,'call'],[:RPAREN,")"],[false,false]]
+}
+ strings.each { |str,array|
+ @lexer.string = str
+ assert_equal(
+ array,
+ @lexer.fullscan()
+ )
+ }
+ end
+
+ def test_errors
+ strings = %w{
+ ^
+ @
+ }
+ strings.each { |str|
+ @lexer.string = str
+ assert_raise(RuntimeError) {
+ @lexer.fullscan()
+ }
+ }
+ end
+
+ def test_more_error
+ assert_raise(TypeError) {
+ @lexer.fullscan()
+ }
+ end
+
+ def test_files
+ textfiles() { |file|
+ @lexer.file = file
+ assert_nothing_raised() {
+ @lexer.fullscan()
+ }
+ Puppet::Type.allclear
+ }
+ end
+
+ def test_strings
+ names = %w{this is a bunch of names}
+ types = %w{Many Different Words A Word}
+ words = %w{differently Cased words A a}
+
+ names.each { |t|
+ @lexer.string = t
+ assert_equal(
+ [[:NAME,t],[false,false]],
+ @lexer.fullscan
+ )
+ }
+ types.each { |t|
+ @lexer.string = t
+ assert_equal(
+ [[:TYPE,t],[false,false]],
+ @lexer.fullscan
+ )
+ }
+ end
+end
diff --git a/test/parser/tc_parser.rb b/test/parser/tc_parser.rb
new file mode 100644
index 000000000..b88bd8116
--- /dev/null
+++ b/test/parser/tc_parser.rb
@@ -0,0 +1,55 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/parser/parser'
+require 'test/unit'
+require 'puppettest'
+
+# $Id$
+
+class TestParser < Test::Unit::TestCase
+ # hmmm
+ # this is complicated, because we store references to the created
+ # objects in a central store
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ Puppet[:parseonly] = true
+ #@lexer = Puppet::Parser::Lexer.new()
+ @parser = Puppet::Parser::Parser.new()
+ end
+
+ def test_each_file
+ textfiles { |file|
+ Puppet.debug("parsing %s" % file) if __FILE__ == $0
+ assert_nothing_raised() {
+ @parser.file = file
+ @parser.parse
+ }
+
+ Puppet::Type.eachtype { |type|
+ type.each { |obj|
+ assert(obj.file)
+ assert(obj.name)
+ assert(obj.line)
+ }
+ }
+ Puppet::Type.allclear
+ }
+ end
+
+ def test_failers
+ failers { |file|
+ Puppet.debug("parsing failer %s" % file) if __FILE__ == $0
+ assert_raise(Puppet::ParseError) {
+ @parser.file = file
+ @parser.parse
+ }
+ Puppet::Type.allclear
+ }
+ end
+end
diff --git a/test/puppet/tc_defaults.rb b/test/puppet/tc_defaults.rb
index 1bb7e9afa..e1d0124d2 100755
--- a/test/puppet/tc_defaults.rb
+++ b/test/puppet/tc_defaults.rb
@@ -1,7 +1,7 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
- $puppetbase = "../../../../language/trunk/"
+ $puppetbase = ".."
end
require 'puppet'
@@ -11,8 +11,7 @@ require 'test/unit'
class TestPuppetDefaults < Test::Unit::TestCase
@@dirs = %w{rrddir puppetconf puppetvar logdir statedir}
- @@files = %w{logfile checksumfile
- manifest masterlog}
+ @@files = %w{logfile checksumfile manifest masterlog}
@@normals = %w{puppetport masterport server}
@@booleans = %w{rrdgraph noop}
@@ -40,17 +39,19 @@ class TestPuppetDefaults < Test::Unit::TestCase
}
end
- def testContained
- confdir = Regexp.new(Puppet[:puppetconf])
- vardir = Regexp.new(Puppet[:puppetvar])
- [@@dirs,@@files].flatten.each { |param|
- value = Puppet[param]
+ if __FILE__ == $0
+ def disabled_testContained
+ confdir = Regexp.new(Puppet[:puppetconf])
+ vardir = Regexp.new(Puppet[:puppetvar])
+ [@@dirs,@@files].flatten.each { |param|
+ value = Puppet[param]
- unless value =~ confdir or value =~ vardir
- assert_nothing_raised { raise "%s is in wrong dir: %s" %
- [param,value] }
- end
- }
+ unless value =~ confdir or value =~ vardir
+ assert_nothing_raised { raise "%s is in wrong dir: %s" %
+ [param,value] }
+ end
+ }
+ end
end
def testArgumentTypes
diff --git a/test/server/tc_ca.rb b/test/server/tc_ca.rb
new file mode 100644
index 000000000..9fb579638
--- /dev/null
+++ b/test/server/tc_ca.rb
@@ -0,0 +1,225 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/ca'
+require 'puppet/sslcertificates'
+require 'openssl'
+require 'test/unit'
+require 'puppettest.rb'
+
+# $Id$
+
+if ARGV.length > 0 and ARGV[0] == "short"
+ $short = true
+else
+ $short = false
+end
+
+class TestCA < Test::Unit::TestCase
+ def setup
+ if __FILE__ == $0
+ Puppet[:loglevel] = :debug
+ #paths = Puppet::Type.type(:service).searchpath
+ #paths.push "%s/examples/root/etc/init.d" % $puppetbase
+ #Puppet::Type.type(:service).setpath(paths)
+ end
+
+ @@tmpfiles = []
+ end
+
+ def teardown
+ Puppet::Type.allclear
+ print "\n\n" if Puppet[:debug]
+
+ @@tmpfiles.each { |file|
+ if FileTest.exists?(file)
+ system("rm -rf %s" % file)
+ end
+ }
+ end
+
+ def test_autocertgeneration
+ ssldir = "/tmp/testcertdir"
+ @@tmpfiles.push ssldir
+ assert_nothing_raised {
+ Puppet[:autosign] = true
+ Puppet[:ssldir] = ssldir
+ }
+ file = File.join($puppetbase, "examples", "code", "head")
+ ca = nil
+
+ assert_nothing_raised {
+ ca = Puppet::CA.new()
+ }
+
+ key = nil
+ csr = nil
+ cert = nil
+ hostname = "test.domain.com"
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => "test.domain.com"
+ )
+ }
+ assert_nothing_raised {
+ cert.mkcsr
+ }
+
+ certtext = nil
+ cacerttext = nil
+ assert_nothing_raised {
+ certtext, cacerttext = ca.getcert(cert.csr.to_s)
+ }
+
+ assert_instance_of(String, certtext)
+ assert_instance_of(String, cacerttext)
+ x509 = nil
+ assert_nothing_raised {
+ x509 = OpenSSL::X509::Certificate.new(certtext)
+ }
+ assert_nothing_raised {
+ OpenSSL::X509::Certificate.new(cacerttext)
+ }
+
+ # and pull it again, just to make sure we're getting the same thing
+ newtext = nil
+ assert_nothing_raised {
+ newtext, cacerttext = ca.getcert(cert.csr.to_s)
+ }
+
+ assert_equal(certtext,newtext)
+ end
+
+ def test_storeAndSign
+ ssldir = "/tmp/testcertdir"
+ @@tmpfiles.push ssldir
+ assert_nothing_raised {
+ Puppet[:ssldir] = ssldir
+ Puppet[:autosign] = false
+ }
+ file = File.join($puppetbase, "examples", "code", "head")
+ ca = nil
+ caserv = nil
+ assert_nothing_raised {
+ caserv = Puppet::CA.new()
+ }
+ assert_nothing_raised {
+ ca = caserv.ca
+ }
+
+ key = nil
+ csr = nil
+ cert = nil
+ hostname = "test.domain.com"
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => "anothertest.domain.com"
+ )
+ }
+ assert_nothing_raised {
+ cert.mkcsr
+ }
+
+ certtext = nil
+ assert_nothing_raised {
+ certtext, cacerttext = caserv.getcert(cert.csr.to_s)
+ }
+
+ assert_equal("", certtext)
+
+ x509 = nil
+ assert_nothing_raised {
+ x509, cacert = ca.sign(cert.csr)
+ }
+ cert.cert = x509
+ assert_nothing_raised {
+ cert.write
+ }
+
+ assert(File.exists?(cert.certfile))
+
+ newtext = nil
+ assert_nothing_raised {
+ newtext, cacerttext = caserv.getcert(cert.csr.to_s)
+ }
+
+ assert(newtext)
+ end
+
+ def cycleautosign
+ ssldir = "/tmp/testcertdir"
+ autosign = "/tmp/autosign"
+ @@tmpfiles.push ssldir
+ @@tmpfiles.push autosign
+ assert_nothing_raised {
+ Puppet[:ssldir] = ssldir
+ }
+ file = File.join($puppetbase, "examples", "code", "head")
+ caserv = nil
+
+ assert_nothing_raised {
+ caserv = Puppet::CA.new()
+ }
+
+ key = nil
+ csr = nil
+ cert = nil
+ hostname = "test.domain.com"
+ assert_nothing_raised {
+ cert = Puppet::SSLCertificates::Certificate.new(
+ :name => "test.domain.com"
+ )
+ }
+ assert_nothing_raised {
+ cert.mkcsr
+ }
+
+ certtext = nil
+ assert_nothing_raised {
+ certtext = caserv.getcert(cert.csr.to_s)
+ }
+
+ x509 = nil
+ assert_nothing_raised {
+ x509 = OpenSSL::X509::Certificate.new(certtext)
+ }
+
+ assert(File.exists?(cert.certfile))
+
+ newtext = nil
+ assert_nothing_raised {
+ newtext = caserv.getcert(cert.csr.to_s)
+ }
+
+ assert_equal(certtext,newtext)
+ end
+
+ def test_autosign
+ autosign = "/tmp/autosign"
+ Puppet[:autosign] = "/tmp/autosign"
+ @@tmpfiles << autosign
+ File.open(autosign, "w") { |f|
+ f.puts "hostmatch.domain.com"
+ f.puts ".+.other.com"
+ f.puts "hostname.+"
+ }
+
+ caserv = nil
+ file = File.join($puppetbase, "examples", "code", "head")
+ assert_nothing_raised {
+ caserv = Puppet::CA.new()
+ }
+
+ assert(caserv.autosign?("hostmatch.domain.com"))
+ assert(caserv.autosign?("fakehost.other.com"))
+ assert(caserv.autosign?("hostname.rahtest.boo"))
+ assert(caserv.autosign?("hostname.com")) # a tricky one
+ assert(!caserv.autosign?("kirby.reductivelabs.com"))
+ assert(!caserv.autosign?("culain.domain.com"))
+ end
+end
diff --git a/test/server/tc_fileserver.rb b/test/server/tc_fileserver.rb
new file mode 100755
index 000000000..ad9b6298c
--- /dev/null
+++ b/test/server/tc_fileserver.rb
@@ -0,0 +1,386 @@
+if __FILE__ == $0
+ if Dir.getwd =~ /test\/server$/
+ Dir.chdir("..")
+ end
+
+ $:.unshift '../lib'
+ $:.unshift '../../../library/trunk/lib/'
+ $:.unshift '../../../library/trunk/test/'
+ $puppetbase = ".."
+
+end
+
+require 'puppet'
+require 'puppet/fileserver'
+require 'test/unit'
+require 'puppettest.rb'
+
+class TestFileServer < TestPuppet
+ def setup
+ if __FILE__ == $0
+ Puppet[:loglevel] = :debug
+ end
+
+ super
+ end
+
+ def mkrandomdirs(dir, depth, width)
+ assert_nothing_raised {
+ Dir.mkdir(dir)
+ }
+
+ end
+
+ def test_namefailures
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_raise(Puppet::FileServerError) {
+ server.mount("/tmp", "invalid+name")
+ }
+
+ assert_raise(Puppet::FileServerError) {
+ server.mount("/tmp", "invalid-name")
+ }
+
+ assert_raise(Puppet::FileServerError) {
+ server.mount("/tmp", "invalid name")
+ }
+
+ assert_raise(Puppet::FileServerError) {
+ server.mount("/tmp", "")
+ }
+ end
+
+ def test_listroot
+ server = nil
+ testdir = "/tmp/remotefilecopying"
+ tmpfile = File.join(testdir, "tmpfile")
+ assert_nothing_raised {
+ Dir.mkdir(testdir)
+ File.open(tmpfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << testdir
+ }
+
+ file = nil
+ checks = Puppet::FileServer::CHECKPARAMS
+
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_nothing_raised {
+ server.mount(testdir, "test")
+ }
+
+ list = nil
+ assert_nothing_raised {
+ list = server.list("/test/", true)
+ }
+
+ assert(list =~ /tmpfile/)
+
+ assert_nothing_raised {
+ list = server.list("/test", true)
+ }
+ assert(list =~ /tmpfile/)
+
+ end
+
+ def test_getfilelist
+ server = nil
+ testdir = "/tmp/remotefilecopying"
+ #subdir = "testingyo"
+ #subpath = File.join(testdir, "testingyo")
+ #dir = File.join(testdir, subdir)
+ tmpfile = File.join(testdir, "tmpfile")
+ assert_nothing_raised {
+ Dir.mkdir(testdir)
+ #Dir.mkdir(subpath)
+ File.open(tmpfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << testdir
+ }
+
+ file = nil
+
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_nothing_raised {
+ server.mount(testdir, "test")
+ }
+
+ list = nil
+ sfile = "/test/tmpfile"
+ assert_nothing_raised {
+ list = server.list(sfile, true)
+ }
+
+ assert_nothing_raised {
+ file = Puppet::Type::PFile[tmpfile]
+ }
+
+ output = "/\tfile"
+
+ assert_equal(output, list)
+ assert(list !~ /\t\t/)
+
+ list.split("\n").each { |line|
+ assert(line !~ %r{remotefile})
+ }
+ contents = File.read(tmpfile)
+
+ ret = nil
+ assert_nothing_raised {
+ ret = server.retrieve(sfile)
+ }
+
+ assert_equal(contents, ret)
+ end
+
+ def test_seenewfiles
+ server = nil
+ testdir = "/tmp/remotefilecopying"
+ oldfile = File.join(testdir, "oldfile")
+ newfile = File.join(testdir, "newfile")
+ assert_nothing_raised {
+ Dir.mkdir(testdir)
+ File.open(oldfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << testdir
+ }
+
+ file = nil
+ checks = Puppet::FileServer::CHECKPARAMS
+
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_nothing_raised {
+ server.mount(testdir, "test")
+ }
+
+ list = nil
+ sfile = "/test/"
+ assert_nothing_raised {
+ list = server.list(sfile, true)
+ }
+
+ File.open(newfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+
+ newlist = nil
+ assert_nothing_raised {
+ newlist = server.list(sfile, true)
+ }
+
+ assert(list != newlist)
+
+ assert(newlist =~ /newfile/)
+ end
+
+ def test_mountroot
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_nothing_raised {
+ server.mount("/", "root")
+ }
+
+ testdir = "/tmp/remotefilecopying"
+ oldfile = File.join(testdir, "oldfile")
+ assert_nothing_raised {
+ Dir.mkdir(testdir)
+ File.open(oldfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << testdir
+ }
+
+ list = nil
+ assert_nothing_raised {
+ list = server.list("/root/" + testdir, true)
+ }
+
+ assert(list =~ /oldfile/)
+ end
+
+ def test_recursionlevels
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ basedir = "/tmp/remotefilecopying"
+ testdir = "%s/with/some/sub/directories/for/the/purposes/of/testing" % basedir
+ oldfile = File.join(testdir, "oldfile")
+ assert_nothing_raised {
+ system("mkdir -p %s" % testdir)
+ File.open(oldfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << basedir
+ }
+
+ assert_nothing_raised {
+ server.mount(basedir, "test")
+ }
+
+ list = nil
+ assert_nothing_raised {
+ list = server.list("/test/with", false)
+ }
+
+ assert(list !~ /\n/)
+
+ [0, 1, 2].each { |num|
+ assert_nothing_raised {
+ list = server.list("/test/with", num)
+ }
+
+ count = 0
+ #p list
+ while list =~ /\n/
+ list.sub!(/\n/, '')
+ count += 1
+ end
+ assert_equal(num, count)
+ }
+ end
+
+ def test_listedpath
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ basedir = "/tmp/remotefilecopying"
+ testdir = "%s/with/some/sub/directories/for/testing" % basedir
+ oldfile = File.join(testdir, "oldfile")
+ assert_nothing_raised {
+ system("mkdir -p %s" % testdir)
+ File.open(oldfile, "w") { |f|
+ 3.times { f.puts rand(100) }
+ }
+ @@tmpfiles << basedir
+ }
+
+ assert_nothing_raised {
+ server.mount(basedir, "localhost")
+ }
+
+ list = nil
+ assert_nothing_raised {
+ list = server.list("/localhost/with", false)
+ }
+
+ assert(list !~ /with/)
+
+ assert_nothing_raised {
+ list = server.list("/localhost/with/some/sub", true)
+ }
+
+ assert(list !~ /sub/)
+ end
+
+ def test_widelists
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ basedir = "/tmp/remotefilecopying"
+ dirs = %w{a set of directories}
+ assert_nothing_raised {
+ Dir.mkdir(basedir)
+ dirs.each { |dir|
+ Dir.mkdir(File.join(basedir, dir))
+ }
+ @@tmpfiles << basedir
+ }
+
+ assert_nothing_raised {
+ server.mount(basedir, "localhost")
+ }
+
+ list = nil
+ assert_nothing_raised {
+ list = server.list("/localhost/", 1).split("\n")
+ }
+
+ assert_equal(dirs.length + 1, list.length)
+ end
+
+ def test_describe
+ server = nil
+ testdir = "/tmp/remotefilecopying"
+ assert_nothing_raised {
+ Dir.mkdir(testdir)
+ @@tmpfiles << testdir
+ %w{a b c d e}.each { |l|
+ name = File.join(testdir, "file%s" % name)
+ File.open(name, "w") { |f|
+ f.puts rand(100)
+ }
+ }
+ }
+
+ file = nil
+ checks = Puppet::FileServer::CHECKPARAMS
+
+ assert_nothing_raised {
+ server = Puppet::FileServer.new(
+ :Local => true
+ )
+ }
+
+ assert_nothing_raised {
+ server.mount(testdir, "test")
+ }
+
+ list = nil
+ sfile = "/test/"
+ assert_nothing_raised {
+ list = server.list(sfile, true)
+ }
+
+ assert_nothing_raised {
+ list.split("\n").each { |line|
+ file, type = line.split("\t")
+
+ desc = server.describe(sfile + file)
+ }
+ }
+ end
+end
+
+# $Id$
+
diff --git a/test/server/tc_master.rb b/test/server/tc_master.rb
new file mode 100644
index 000000000..be61b232b
--- /dev/null
+++ b/test/server/tc_master.rb
@@ -0,0 +1,135 @@
+if __FILE__ == $0
+ if Dir.getwd =~ /test\/server$/
+ Dir.chdir("..")
+ #puts "Unfortunately, you must be in the test dir to run this test."
+ #puts "Yes, I know it's different than all of the others."
+ #exit
+ end
+
+ $:.unshift '../lib'
+ $:.unshift '../../../library/trunk/lib/'
+ $:.unshift '../../../library/trunk/test/'
+ $puppetbase = ".."
+
+end
+
+#if __FILE__ == $0
+# $:.unshift '../../lib'
+# $:.unshift '../../../../library/trunk/lib/'
+# $:.unshift '../../../../library/trunk/test/'
+# $puppetbase = "../.."
+#end
+
+require 'puppet'
+require 'puppet/master'
+require 'puppet/client'
+require 'test/unit'
+require 'puppettest.rb'
+
+class TestMaster < Test::Unit::TestCase
+ def setup
+ if __FILE__ == $0
+ Puppet[:loglevel] = :debug
+ end
+
+ @@tmpfiles = []
+ end
+
+ def stopservices
+ if stype = Puppet::Type.type(:service)
+ stype.each { |service|
+ service[:running] = false
+ service.sync
+ }
+ end
+ end
+
+ def teardown
+ Puppet::Type.allclear
+ print "\n\n\n\n" if Puppet[:debug]
+
+ @@tmpfiles.each { |file|
+ if FileTest.exists?(file)
+ system("rm -rf %s" % file)
+ end
+ }
+ end
+
+ def test_files
+ Puppet[:debug] = true if __FILE__ == $0
+ Puppet[:puppetconf] = "/tmp/servertestingdir"
+ @@tmpfiles << Puppet[:puppetconf]
+ textfiles { |file|
+ Puppet.debug("parsing %s" % file)
+ server = nil
+ client = nil
+ threads = []
+ port = 8080
+ master = nil
+ assert_nothing_raised() {
+ # this is the default server setup
+ master = Puppet::Master.new(
+ :File => file,
+ :Local => true
+ )
+ }
+ assert_nothing_raised() {
+ client = Puppet::Client.new(
+ :Server => master
+ )
+ }
+
+ # pull our configuration
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+ }
+ end
+
+ def test_defaultmanifest
+ Puppet[:debug] = true if __FILE__ == $0
+ Puppet[:puppetconf] = "/tmp/servertestingdir"
+ @@tmpfiles << Puppet[:puppetconf]
+ textfiles { |file|
+ Puppet[:manifest] = file
+ client = nil
+ master = nil
+ assert_nothing_raised() {
+ # this is the default server setup
+ master = Puppet::Master.new(
+ :File => file,
+ :Local => true
+ )
+ }
+ assert_nothing_raised() {
+ client = Puppet::Client.new(
+ :Server => master
+ )
+ }
+
+ # pull our configuration
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+
+ break
+ }
+ end
+end
+
+# $Id$
+
diff --git a/test/server/tc_server.rb b/test/server/tc_server.rb
new file mode 100644
index 000000000..d3a4ea305
--- /dev/null
+++ b/test/server/tc_server.rb
@@ -0,0 +1,364 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '../../../../library/trunk/lib/'
+ $:.unshift '../../../../library/trunk/test/'
+ $puppetbase = "../.."
+end
+#if __FILE__ == $0
+# $:.unshift '../lib'
+# $:.unshift '../../../library/trunk/lib/'
+# $:.unshift '../../../library/trunk/test/'
+# $puppetbase = ".."
+#
+#
+# if Dir.getwd !~ /test$/
+# puts "Unfortunately, you must be in the test dir to run this test."
+# puts "Yes, I know it's different than all of the others."
+# exit
+# end
+#end
+
+require 'puppet'
+require 'cgi'
+require 'puppet/server'
+require 'facter'
+require 'puppet/client'
+require 'xmlrpc/client'
+require 'test/unit'
+require 'puppettest.rb'
+
+# $Id$
+
+if ARGV.length > 0 and ARGV[0] == "short"
+ $short = true
+else
+ $short = false
+end
+
+class TestServer < Test::Unit::TestCase
+ def setup
+ if __FILE__ == $0
+ Puppet[:loglevel] = :debug
+ #paths = Puppet::Type.type(:service).searchpath
+ #paths.push "%s/examples/root/etc/init.d" % $puppetbase
+ #Puppet::Type.type(:service).setpath(paths)
+ end
+
+ @@tmpfiles = []
+ @@tmppids = []
+ end
+
+ def stopservices
+ if stype = Puppet::Type.type(:service)
+ stype.each { |service|
+ service[:running] = false
+ service.sync
+ }
+ end
+ end
+
+ def teardown
+ Puppet::Type.allclear
+ print "\n\n\n\n" if Puppet[:debug]
+
+ @@tmpfiles.each { |file|
+ if FileTest.exists?(file)
+ system("rm -rf %s" % file)
+ end
+ }
+ @@tmppids.each { |pid|
+ system("kill -INT %s" % pid)
+ }
+ end
+
+ def test_start
+ server = nil
+ Puppet[:ssldir] = "/tmp/serverstarttesting"
+ Puppet[:autosign] = true
+ @@tmpfiles << "/tmp/serverstarttesting"
+ port = 8081
+ file = File.join($puppetbase, "examples", "code", "head")
+ assert_nothing_raised() {
+ server = Puppet::Server.new(
+ :Port => port,
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Master => {
+ :File => file,
+ },
+ :Status => nil
+ }
+ )
+
+ }
+ sthread = nil
+ assert_nothing_raised() {
+ trap(:INT) { server.shutdown }
+ sthread = Thread.new {
+ server.start
+ }
+ }
+ sleep 1
+ assert_nothing_raised {
+ server.shutdown
+ }
+ assert_nothing_raised {
+ sthread.join
+ }
+ end
+
+ # disabled because i can't find a good way to test client connecting
+ # i'll have to test the external executables
+ def disabled_test_connect_with_threading
+ server = nil
+ Puppet[:ssldir] = "/tmp/serverconnecttesting"
+ Puppet[:autosign] = true
+ @@tmpfiles << "/tmp/serverconnecttesting"
+ threads = []
+ port = 8080
+ server = nil
+ Thread.abort_on_exception = true
+ assert_nothing_raised() {
+ server = Puppet::Server.new(
+ :Port => port,
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Status => nil
+ }
+ )
+
+ }
+ sthread = Thread.new {
+ assert_nothing_raised() {
+ #trap(:INT) { server.shutdown; Kernel.exit! }
+ trap(:INT) { server.shutdown }
+ server.start
+ }
+ }
+
+ sleep(3)
+ client = nil
+ assert_nothing_raised() {
+ client = XMLRPC::Client.new("localhost", "/RPC2", port, nil, nil,
+ nil, nil, true, 3)
+ }
+ retval = nil
+
+ clthread = Thread.new {
+ assert_nothing_raised() {
+ Puppet.notice "calling status"
+ retval = client.call("status.status", "")
+ }
+ }
+ assert_not_nil(clthread.join(5))
+
+ assert_equal(1, retval)
+ assert_nothing_raised {
+ #system("kill -INT %s" % serverpid)
+ server.shutdown
+ }
+
+ assert_not_nil(sthread.join(5))
+
+ #Process.wait
+ end
+
+ # disabled because i can't find a good way to test client connecting
+ # i'll have to test the external executables
+ def test_connect_with_fork
+ server = nil
+ Puppet[:ssldir] = "/tmp/serverconnecttesting"
+ Puppet[:autosign] = true
+ @@tmpfiles << "/tmp/serverconnecttesting"
+ serverpid = nil
+ port = 8080
+ assert_nothing_raised() {
+ server = Puppet::Server.new(
+ :Port => port,
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Status => nil
+ }
+ )
+
+ }
+ serverpid = fork {
+ assert_nothing_raised() {
+ #trap(:INT) { server.shutdown; Kernel.exit! }
+ trap(:INT) { server.shutdown }
+ server.start
+ }
+ }
+ @@tmppids << serverpid
+
+ sleep(3)
+ client = nil
+ assert_nothing_raised() {
+ client = XMLRPC::Client.new("localhost", "/RPC2", port, nil, nil,
+ nil, nil, true, 3)
+ }
+ retval = nil
+
+ assert_nothing_raised() {
+ Puppet.notice "calling status"
+ retval = client.call("status.status")
+ }
+
+ assert_equal(1, retval)
+ #assert_nothing_raised {
+ # system("kill -INT %s" % serverpid)
+ # #server.shutdown
+ #}
+
+ #Process.wait
+ end
+
+ # disabled because i can't find a good way to test client connecting
+ # i'll have to test the external executables
+ def test_zzgetconfig_with_fork
+ server = nil
+ Puppet[:ssldir] = "/tmp/serverconfigtesting"
+ Puppet[:autosign] = true
+ @@tmpfiles << "/tmp/serverconfigtesting"
+ serverpid = nil
+ port = 8082
+ file = File.join($puppetbase, "examples", "code", "head")
+ assert_nothing_raised() {
+ server = Puppet::Server.new(
+ :Port => port,
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Master => {
+ :File => file
+ },
+ :Status => nil
+ }
+ )
+
+ }
+ serverpid = fork {
+ assert_nothing_raised() {
+ #trap(:INT) { server.shutdown; Kernel.exit! }
+ trap(:INT) { server.shutdown }
+ server.start
+ }
+ }
+ @@tmppids << serverpid
+
+ sleep(3)
+ client = nil
+
+ # first use a puppet client object
+ assert_nothing_raised() {
+ client = Puppet::Client.new(
+ :Server => "localhost",
+ :Port => port
+ )
+ }
+ retval = nil
+
+ assert_nothing_raised() {
+ Puppet.notice "calling status"
+ retval = client.getconfig
+ }
+
+ # then use a raw rpc client
+ assert_nothing_raised() {
+ client = XMLRPC::Client.new("localhost", "/RPC2", port, nil, nil,
+ nil, nil, true, 3)
+ }
+ retval = nil
+
+ facts = CGI.escape(Marshal.dump(Puppet::Client.facts))
+ assert_nothing_raised() {
+ Puppet.notice "calling status"
+ retval = client.call("puppetmaster.getconfig", facts)
+ }
+
+ #assert_equal(1, retval)
+ end
+
+ # disabled because clients can't seem to connect from in the same process
+ def disabled_test_files
+ Puppet[:debug] = true if __FILE__ == $0
+ Puppet[:puppetconf] = "/tmp/servertestingdir"
+ Puppet[:autosign] = true
+ @@tmpfiles << Puppet[:puppetconf]
+ textfiles { |file|
+ Puppet.debug("parsing %s" % file)
+ server = nil
+ client = nil
+ threads = []
+ port = 8080
+ assert_nothing_raised() {
+ # this is the default server setup
+ server = Puppet::Server.new(
+ :Port => port,
+ :Handlers => {
+ :CA => {}, # so that certs autogenerate
+ :Master => {
+ :File => file,
+ },
+ }
+ )
+ }
+ assert_nothing_raised() {
+ client = Puppet::Client.new(
+ :Server => "localhost",
+ :Port => port
+ )
+ }
+
+ # start the server
+ assert_nothing_raised() {
+ trap(:INT) { server.shutdown }
+ threads << Thread.new {
+ server.start
+ }
+ }
+
+ # start the client
+ #assert_nothing_raised() {
+ # threads << Thread.new {
+ # client.start
+ # }
+ #}
+
+ sleep(1)
+ # pull our configuration
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+ assert_nothing_raised() {
+ client.getconfig
+ stopservices
+ Puppet::Type.allclear
+ }
+
+ # and shut them both down
+ assert_nothing_raised() {
+ [server].each { |thing|
+ thing.shutdown
+ }
+ }
+
+ # make sure everything's complete before we stop
+ assert_nothing_raised() {
+ threads.each { |thr|
+ thr.join
+ }
+ }
+ assert_nothing_raised() {
+ stopservices
+ }
+ Puppet::Type.allclear
+ }
+ end
+end
diff --git a/test/test b/test/test
index b10d78121..8b850a144 100755
--- a/test/test
+++ b/test/test
@@ -7,14 +7,13 @@
$:.unshift '.'
$:.unshift '../lib'
-$:.unshift '../../../language/trunk/lib/'
# if we're not in the library trunk, then add that to the lib search path
-if Dir.getwd !~ %r{puppet/library}
- $:.unshift '../../../library/trunk/lib/'
- $:.unshift '../../../language/trunk/lib/'
- $:.unshift '../../../library/trunk/test/'
-end
+#if Dir.getwd !~ %r{puppet/library}
+# $:.unshift '../../../library/trunk/lib/'
+# $:.unshift '../../../language/trunk/lib/'
+# $:.unshift '../../../library/trunk/test/'
+#end
require 'puppettest.rb'
@@ -37,7 +36,7 @@ result.each { |opt,arg|
end
}
-$puppetbase = "../../../language/trunk"
+$puppetbase = ".."
suites = nil
diff --git a/test/types/tc_file.rb b/test/types/tc_file.rb
index 8c7316403..730bcfc8f 100644
--- a/test/types/tc_file.rb
+++ b/test/types/tc_file.rb
@@ -1,6 +1,7 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
+ $:.unshift "../../../../language/trunk/lib"
$puppetbase = "../../../../language/trunk"
end
@@ -67,7 +68,7 @@ class TestFile < Test::Unit::TestCase
Puppet::Storage.clear
end
- def test_zzowner
+ def test_owner
file = mktestfile()
users = {}
@@ -138,6 +139,9 @@ class TestFile < Test::Unit::TestCase
users.each { |uid, name|
# just make sure we don't try to manage users
assert_nothing_raised() {
+ file.sync
+ }
+ assert_nothing_raised() {
file[:owner] = name
}
assert_nothing_raised() {
@@ -151,7 +155,7 @@ class TestFile < Test::Unit::TestCase
end
end
- def test_zzgroup
+ def test_group
file = mktestfile()
[%x{groups}.chomp.split(/ /), Process.groups].flatten.each { |group|
assert_nothing_raised() {
@@ -493,6 +497,8 @@ class TestFile < Test::Unit::TestCase
trans.evaluate
}
+ assert(FileTest.exists?(todir))
+
clearstorage
Puppet::Type.allclear
end
@@ -518,7 +524,7 @@ class TestFile < Test::Unit::TestCase
return [fromdir,todir]
end
- def test_complex_sources_twice
+ def test_zzzcomplex_sources_twice
fromdir, todir = run_complex_sources
assert_trees_equal(fromdir,todir)
recursive_source_test(fromdir, todir)
@@ -528,6 +534,7 @@ class TestFile < Test::Unit::TestCase
def test_sources_with_deleted_destfiles
fromdir, todir = run_complex_sources
# then delete some files
+ assert(FileTest.exists?(todir))
delete_random_files(todir)
# and run
@@ -539,6 +546,7 @@ class TestFile < Test::Unit::TestCase
def test_sources_with_readonly_destfiles
fromdir, todir = run_complex_sources
+ assert(FileTest.exists?(todir))
readonly_random_files(todir)
recursive_source_test(fromdir, todir)
@@ -549,6 +557,7 @@ class TestFile < Test::Unit::TestCase
def test_sources_with_modified_dest_files
fromdir, todir = run_complex_sources
+ assert(FileTest.exists?(todir))
# then modify some files
modify_random_files(todir)
@@ -560,6 +569,7 @@ class TestFile < Test::Unit::TestCase
def test_sources_with_added_destfiles
fromdir, todir = run_complex_sources
+ assert(FileTest.exists?(todir))
# and finally, add some new files
add_random_files(todir)
@@ -582,4 +592,84 @@ class TestFile < Test::Unit::TestCase
# and make sure they're still equal
assert_trees_equal(fromdir,todir)
end
+
+ def test_filetype_retrieval
+ file = nil
+ assert_nothing_raised {
+ file = Puppet::Type::PFile.new(
+ :name => "/tmp",
+ :check => :type
+ )
+ }
+
+ assert_nothing_raised {
+ file.evaluate
+ }
+
+ assert_equal("directory", file.state(:type).is)
+
+ assert_nothing_raised {
+ file = Puppet::Type::PFile.new(
+ :name => "/etc/passwd",
+ :check => :type
+ )
+ }
+
+ assert_nothing_raised {
+ file.evaluate
+ }
+
+ assert_equal("file", file.state(:type).is)
+
+ assert_raise(Puppet::Error) {
+ file[:type] = "directory"
+ }
+
+ assert(file.insync?)
+
+ assert_raise(Puppet::Error) {
+ file.sync
+ }
+ end
+
+ def test_RecursionWithAddedFiles
+ basedir = "/tmp/recursionplussaddedfiles"
+ Dir.mkdir(basedir)
+ @@tmpfiles << basedir
+ file1 = File.join(basedir, "file1")
+ file2 = File.join(basedir, "file2")
+ subdir1 = File.join(basedir, "subdir1")
+ file3 = File.join(subdir1, "file")
+ File.open(file1, "w") { |f| 3.times { f.print rand(100) } }
+ rootobj = nil
+ assert_nothing_raised {
+ rootobj = Puppet::Type::PFile.new(
+ :name => basedir,
+ :recurse => true,
+ :check => %w{type owner}
+ )
+
+ rootobj.evaluate
+ }
+
+ klass = Puppet::Type::PFile
+ assert(klass[basedir])
+ assert(klass[file1])
+ assert_nil(klass[file2])
+
+ File.open(file2, "w") { |f| 3.times { f.print rand(100) } }
+
+ assert_nothing_raised {
+ rootobj.evaluate
+ }
+ assert(klass[file2])
+
+ Dir.mkdir(subdir1)
+ File.open(file3, "w") { |f| 3.times { f.print rand(100) } }
+
+ assert_nothing_raised {
+ rootobj.evaluate
+ }
+ assert(klass[file3])
+ end
end