Moving all files into a consolidated trunk. All tests pass except the known-failing certificate test, but there appear to be some errors that are incorrectly not resulting in failurs. I will track those down ASAP.

git-svn-id: https://reductivelabs.com/svn/puppet/trunk@576 980ebf18-57e1-0310-9a29-db15c13687c0
This commit is contained in:
Luke Kanies 2005-08-23 16:09:14 +00:00
Родитель e87eb58ce8
Коммит 6029ef7812
81 изменённых файлов: 9419 добавлений и 215 удалений

2
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,2 @@
This entire repository is under the GNU Public License. See www.gnu.org
for a copy.

22
README Normal file
Просмотреть файл

@ -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.

4
TODO Normal file
Просмотреть файл

@ -0,0 +1,4 @@
$Id$
* Syntax for configuration of types (e.g., search path for Services)
* Get file parsing working in the language

44
bin/certmgr.rb Executable file
Просмотреть файл

@ -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))

40
bin/filebucketd Executable file
Просмотреть файл

@ -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

129
bin/puppet Executable file
Просмотреть файл

@ -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

127
bin/puppetca Executable file
Просмотреть файл

@ -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

102
bin/puppetd Executable file
Просмотреть файл

@ -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

129
bin/puppetdoc Executable file
Просмотреть файл

@ -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

89
bin/puppetmasterd Executable file
Просмотреть файл

@ -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

13
examples/code/allatonce Normal file
Просмотреть файл

@ -0,0 +1,13 @@
# $Id$
define thingie {
file { "/tmp/classtest": create => true, mode => 755 }
#testing {}
}
class testing {
thingie { "componentname": }
}
#component {}
testing { "testingname": }

11
examples/code/assignments Normal file
Просмотреть файл

@ -0,0 +1,11 @@
# $Id$
$goodness = sunos
$subvariable = $goodness
$yayness = "this is a string of text"
#$sleeper = service { sleeper:
# running => "1"
#}

35
examples/code/classing Normal file
Просмотреть файл

@ -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

73
examples/code/components Normal file
Просмотреть файл

@ -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":}

16
examples/code/execs Normal file
Просмотреть файл

@ -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
}

22
examples/code/facts Normal file
Просмотреть файл

@ -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")

Просмотреть файл

@ -0,0 +1,10 @@
class comp() {
file { "/etc/passwd":
mode => 644
}
}
# this argument is invalid, thus we should get a falure
comp {
fakearg => "yay"
}

Просмотреть файл

@ -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"
}

Просмотреть файл

@ -0,0 +1,9 @@
define comp() {
file { "/etc/passwd":
mode => 644
}
}
comp {
fakearg => "yay"
}

Просмотреть файл

@ -0,0 +1,9 @@
define comp(arg1) {
file { "/etc/passwd":
mode => 644
}
}
comp {
fakearg => "yay"
}

Просмотреть файл

@ -0,0 +1,3 @@
file { "/etc/passwd":
fakeparam => 644
}

11
examples/code/file.bl Normal file
Просмотреть файл

@ -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;
}

Просмотреть файл

@ -0,0 +1,10 @@
# $Id$
File {
mode => 755,
recurse => true
}
file { "/tmp/filedefaultstest":
create => true
}

116
examples/code/fileparsing Normal file
Просмотреть файл

@ -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}":
}
}

Просмотреть файл

@ -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,
}

3
examples/code/functions Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# $Id$
$yaytest = fact("operatingsystem")

7
examples/code/groups Normal file
Просмотреть файл

@ -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"

30
examples/code/head Normal file
Просмотреть файл

@ -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

21
examples/code/iftest Normal file
Просмотреть файл

@ -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"
}

8
examples/code/importing Normal file
Просмотреть файл

@ -0,0 +1,8 @@
# $Id$
#import "groups"
# testing import loops
import "importing"
$name = "value"
$system = $operatingsystem

20
examples/code/nodes Normal file
Просмотреть файл

@ -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
}

8
examples/code/one Normal file
Просмотреть файл

@ -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
}

Просмотреть файл

@ -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
}

28
examples/code/selectors Normal file
Просмотреть файл

@ -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
}

11
examples/code/simpletests Normal file
Просмотреть файл

@ -0,0 +1,11 @@
# $Id$
file {
"/tmp/atest": create => true;
"/tmp/btest": create => true
}
file {
"/tmp/ctest": create => true;
"/tmp/dtest": create => true;
}

Просмотреть файл

@ -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
}

Просмотреть файл

@ -0,0 +1,13 @@
# $Id$
define component {
file { "/tmp/classtest": create => true, mode => 755 }
#testing {}
}
class testing {
component { "componentname": }
}
#component {}
testing { "testingname": }

Просмотреть файл

@ -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
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -0,0 +1,5 @@
# $Id$
File { mode => 755 }
file { "/tmp/defaulttest": create => true }

Просмотреть файл

@ -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
}
}

13
examples/code/svncommit Normal file
Просмотреть файл

@ -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
}

69
examples/root/bin/sleeper Executable file
Просмотреть файл

@ -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

Просмотреть файл

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

155
lib/puppet/ca.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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]
@@methods.each { |namespace, methods|
methods.each { |method|
@@handlers.each { |handler|
interface = handler.interface
namespace = interface.prefix
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" %

260
lib/puppet/fileserver.rb Executable file
Просмотреть файл

@ -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$

88
lib/puppet/master.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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

1238
lib/puppet/parser/ast.rb Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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

Просмотреть файл

@ -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

225
lib/puppet/parser/lexer.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -0,0 +1,5 @@
#parser.rb: grammar.ry
# ryacc --output parser grammar
parser.rb: grammar.ra
racc -v -o$@ grammar.ra

1264
lib/puppet/parser/parser.rb Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

427
lib/puppet/parser/scope.rb Normal file
Просмотреть файл

@ -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

129
lib/puppet/server.rb Normal file
Просмотреть файл

@ -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

110
lib/puppet/servlet.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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)

Просмотреть файл

@ -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
}
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
}
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
hash[:source] = source + hash[:name]
#sum = hash[:checksum]
#hash.delete(:checksum)
end
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 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
if sum = @parent.state(:checksum)
if sum.is
if sum.is == -1
sum.retrieve
@stats = self.describe
@stats.each { |stat, value|
next if stat == :checksum
next if stat == :type
unless @parent.argument?(stat)
if state = @parent.state(stat)
state.should = value
else
@parent[stat] = value
end
end
}
case @stats[:type]
when "file":
if sum = @parent.state(:checksum)
if sum.is
if sum.is == -1
sum.retrieve
end
@is = sum.is
else
@is = -1
end
@is = sum.is
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":
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
@is = -1
Puppet.err "Cannot use files of type %s as sources" %
@stats[:type]
@should = true
@is = true
end
end
def should=(source)
@local = true # redundant for now
@source = source
type = Puppet::Type.type(:file)
sourcesum = nil
stat = File.stat(@source)
case stat.ftype
when "file":
unless sourcesum = type[@source].state(:checksum).is
raise Puppet::Error.new(
"Could not retrieve checksum of source %s" %
@source
)
end
when "directory":
raise Puppet::Error.new(
"Somehow got told to copy dir %s" % @parent.name)
else
raise Puppet::Error.new(
"Cannot use files of type %s as source" % stat.ftype)
end
@should = sourcesum
# 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)
)
# 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
# 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"
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
# 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])
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
}
def sourcerecurse(recurse)
source = @states[:source].source
sourceobj, path = uri2obj(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
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
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
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)
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
return [sourceobj, path.sub(/\/\//, '/')]
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 PFileSource.[](name)
return @sources[name]
end
def initialize(name)
@name = name
if block_given?
yield self
end
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

275
test/certmgr/tc_certmgr.rb Executable file
Просмотреть файл

@ -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

Просмотреть файл

@ -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

90
test/executables/tc_puppetca.rb Executable file
Просмотреть файл

@ -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

88
test/executables/tc_puppetd.rb Executable file
Просмотреть файл

@ -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

Просмотреть файл

@ -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

264
test/language/tc_scope.rb Executable file
Просмотреть файл

@ -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

317
test/language/tc_snippets.rb Executable file
Просмотреть файл

@ -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

Просмотреть файл

@ -1,7 +1,7 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
$puppetbase = "../../../../language/trunk/"
$puppetbase = "../.."
end
require 'puppet/metric'

112
test/parser/tc_lexer.rb Normal file
Просмотреть файл

@ -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

55
test/parser/tc_parser.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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

225
test/server/tc_ca.rb Normal file
Просмотреть файл

@ -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

386
test/server/tc_fileserver.rb Executable file
Просмотреть файл

@ -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$

135
test/server/tc_master.rb Normal file
Просмотреть файл

@ -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$

364
test/server/tc_server.rb Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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 = {}
@ -137,6 +138,9 @@ class TestFile < Test::Unit::TestCase
us[uid] = name
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
}
@ -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