Consider the following statement in a puppet manifest (think of a manifest as a script).
node "subversion.math.ohio-state.edu" {
subversion::server::webrepository {
"support": path => "/var/svn/support";
"test": path => "/var/svn/test";
}
}
Without describing the problem this puppet snippet addresses, one might guess that I need to configure two subversion repositories, available via HTTP on the host "subversion.math.ohio-state.edu".
The reason I absolutely *love* Puppet is the above code is all there is to this entire problem. Think about all the work that actually needs to happen to setup a subversion repository on a SSL enabled web server:
- Install apache
- Setup SSL certificates
- Install subversion and dependencies
- Setup apache virtual host with mod_dav_svn
- Setup apache htaccess for access control to the repositry
- Punch holes in the firewall (80, 443)
- Create the blank repository with svnadmin
- Ensure the repository is owned by apache
- Ensure post-commit hooks are put in the right place and executable
Now, this is a lot of work, and I've already had the need to create new subversion repositories on other hosts. Because I've already modeled this problem in puppet, it's trivial for me to bring up subversion servers on arbitrary hosts. I just re-use the block you see above.
Now, for the tricky part... Here are the modules that actually model the subversion repository in question.
Note that I've left out the classes which model other aspects of the host in question. For example, web::baseserver::ssl, firewall::input-port, and site-files::certificates (SSL Certs).
# Subversion Module.
class subversion::server inherits subversion {
File {
mode => 0640,
owner => "apache",
group => 0,
require => [ User["apache"], Package["subversion"] ]
}
define webrepository ($path = false) {
File {
owner => "apache",
group => "0",
mode => 0660
}
$path_real = $path ? {
false => "$name",
default => "$path"
}
include subversion::server
repository {
"$name": path => "$path_real";
}
file {
"$path_real":
recurse => true,
require => [ User["apache"], Repository["$name"] ];
"$path_real/hooks":
ensure => directory;
"$path_real/hooks/bin":
ensure => directory;
"$path_real/hooks/bin/commit-email.pl":
content => template("subversion/hooks/bin/commit-email.pl"),
mode => 0770;
"$path_real/hooks/post-commit":
content => template("subversion/hooks/post-commit"),
mode => 0770;
}
}
include web::baseserver::ssl
file {
"/var/svn":
ensure => directory;
"/etc/httpd/htaccess/authz_svn.htaccess":
content => template("subversion/htaccess/authz_svn.htaccess.erb");
"/etc/httpd/htaccess/authz_svn.users":
content => template("subversion/htaccess/htpasswd.mathsvn.erb");
}
web::vhost {
"subversion":
template => "subversion.conf.erb";
}
package {
"subversion-perl":;
"mod_dav_svn":;
}
}
class subversion {
$authz_svn_access_file = "/etc/httpd/auth_SVNAccessFile.math"
$auth_svn_users_file = "/etc/httpd/auth_htpasswd.mathsvn"
$svn_base_parent_repo = "/var/svn"
Package {
ensure => present
}
package {
"subversion":;
}
define repository ($path = false) {
$path_real = $path ? {
false => "$name",
default => "$path"
}
include subversion
# Create a blank repository.
exec {
"svnadmin_create_$path_real":
command => "/usr/bin/svnadmin create '$path_real'",
require => [ Package["subversion"] ],
creates => "$path_real";
}
}
}