From f9f48873d20aaf35b753a9f626d4dedb7bdc9d3b Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Sat, 22 Feb 2014 08:26:23 -0500 Subject: [PATCH] massive import of the puppet modules --- puppet/manifests/cleanup.pp | 19 + puppet/manifests/default.pp | 5 + puppet/modules/base/manifests/init.pp | 10 + puppet/modules/devstack/files/compute.conf | 22 ++ puppet/modules/devstack/files/local.sh | 18 + puppet/modules/devstack/files/manager.conf | 21 + puppet/modules/devstack/manifests/init.pp | 45 +++ puppet/modules/devstack/templates/local.erb | 33 ++ puppet/modules/user/files/stack_bashrc | 72 ++++ puppet/modules/user/files/stack_sudoers | 1 + puppet/modules/user/manifests/create.pp | 58 +++ puppet/modules/user/manifests/stack.pp | 24 ++ puppet/modules/vcsrepo/CHANGELOG | 41 ++ puppet/modules/vcsrepo/Gemfile | 22 ++ puppet/modules/vcsrepo/Gemfile.lock | 91 +++++ puppet/modules/vcsrepo/LICENSE | 17 + puppet/modules/vcsrepo/Modulefile | 4 + puppet/modules/vcsrepo/README.BZR.markdown | 47 +++ puppet/modules/vcsrepo/README.CVS.markdown | 66 ++++ puppet/modules/vcsrepo/README.GIT.markdown | 95 +++++ puppet/modules/vcsrepo/README.HG.markdown | 73 ++++ puppet/modules/vcsrepo/README.SVN.markdown | 62 +++ puppet/modules/vcsrepo/README.markdown | 32 ++ puppet/modules/vcsrepo/Rakefile | 1 + puppet/modules/vcsrepo/examples/bzr/branch.pp | 6 + .../modules/vcsrepo/examples/bzr/init_repo.pp | 4 + puppet/modules/vcsrepo/examples/cvs/local.pp | 11 + puppet/modules/vcsrepo/examples/cvs/remote.pp | 5 + .../modules/vcsrepo/examples/git/bare_init.pp | 4 + puppet/modules/vcsrepo/examples/git/clone.pp | 5 + .../vcsrepo/examples/git/working_copy_init.pp | 4 + puppet/modules/vcsrepo/examples/hg/clone.pp | 6 + .../modules/vcsrepo/examples/hg/init_repo.pp | 4 + .../modules/vcsrepo/examples/svn/checkout.pp | 5 + puppet/modules/vcsrepo/examples/svn/server.pp | 4 + .../vcsrepo/lib/puppet/provider/vcsrepo.rb | 42 ++ .../lib/puppet/provider/vcsrepo/bzr.rb | 85 ++++ .../lib/puppet/provider/vcsrepo/cvs.rb | 137 +++++++ .../lib/puppet/provider/vcsrepo/dummy.rb | 12 + .../lib/puppet/provider/vcsrepo/git.rb | 323 +++++++++++++++ .../vcsrepo/lib/puppet/provider/vcsrepo/hg.rb | 115 ++++++ .../lib/puppet/provider/vcsrepo/svn.rb | 124 ++++++ .../vcsrepo/lib/puppet/type/vcsrepo.rb | 198 ++++++++++ puppet/modules/vcsrepo/metadata.json | 171 ++++++++ .../spec/fixtures/bzr_version_info.txt | 5 + .../vcsrepo/spec/fixtures/git_branch_a.txt | 14 + .../spec/fixtures/git_branch_feature_bar.txt | 14 + .../vcsrepo/spec/fixtures/git_branch_none.txt | 15 + .../vcsrepo/spec/fixtures/hg_parents.txt | 6 + .../modules/vcsrepo/spec/fixtures/hg_tags.txt | 18 + .../vcsrepo/spec/fixtures/svn_info.txt | 10 + puppet/modules/vcsrepo/spec/spec.opts | 6 + puppet/modules/vcsrepo/spec/spec_helper.rb | 13 + .../spec/support/filesystem_helpers.rb | 18 + .../vcsrepo/spec/support/fixture_helpers.rb | 7 + .../unit/puppet/provider/vcsrepo/bzr_spec.rb | 109 ++++++ .../unit/puppet/provider/vcsrepo/cvs_spec.rb | 115 ++++++ .../unit/puppet/provider/vcsrepo/git_spec.rb | 369 ++++++++++++++++++ .../unit/puppet/provider/vcsrepo/hg_spec.rb | 122 ++++++ .../unit/puppet/provider/vcsrepo/svn_spec.rb | 105 +++++ .../spec/unit/puppet/type/README.markdown | 4 + 61 files changed, 3094 insertions(+) create mode 100644 puppet/manifests/cleanup.pp create mode 100644 puppet/manifests/default.pp create mode 100644 puppet/modules/base/manifests/init.pp create mode 100644 puppet/modules/devstack/files/compute.conf create mode 100644 puppet/modules/devstack/files/local.sh create mode 100644 puppet/modules/devstack/files/manager.conf create mode 100644 puppet/modules/devstack/manifests/init.pp create mode 100644 puppet/modules/devstack/templates/local.erb create mode 100644 puppet/modules/user/files/stack_bashrc create mode 100644 puppet/modules/user/files/stack_sudoers create mode 100644 puppet/modules/user/manifests/create.pp create mode 100644 puppet/modules/user/manifests/stack.pp create mode 100644 puppet/modules/vcsrepo/CHANGELOG create mode 100644 puppet/modules/vcsrepo/Gemfile create mode 100644 puppet/modules/vcsrepo/Gemfile.lock create mode 100644 puppet/modules/vcsrepo/LICENSE create mode 100644 puppet/modules/vcsrepo/Modulefile create mode 100644 puppet/modules/vcsrepo/README.BZR.markdown create mode 100644 puppet/modules/vcsrepo/README.CVS.markdown create mode 100644 puppet/modules/vcsrepo/README.GIT.markdown create mode 100644 puppet/modules/vcsrepo/README.HG.markdown create mode 100644 puppet/modules/vcsrepo/README.SVN.markdown create mode 100644 puppet/modules/vcsrepo/README.markdown create mode 100644 puppet/modules/vcsrepo/Rakefile create mode 100644 puppet/modules/vcsrepo/examples/bzr/branch.pp create mode 100644 puppet/modules/vcsrepo/examples/bzr/init_repo.pp create mode 100644 puppet/modules/vcsrepo/examples/cvs/local.pp create mode 100644 puppet/modules/vcsrepo/examples/cvs/remote.pp create mode 100644 puppet/modules/vcsrepo/examples/git/bare_init.pp create mode 100644 puppet/modules/vcsrepo/examples/git/clone.pp create mode 100644 puppet/modules/vcsrepo/examples/git/working_copy_init.pp create mode 100644 puppet/modules/vcsrepo/examples/hg/clone.pp create mode 100644 puppet/modules/vcsrepo/examples/hg/init_repo.pp create mode 100644 puppet/modules/vcsrepo/examples/svn/checkout.pp create mode 100644 puppet/modules/vcsrepo/examples/svn/server.pp create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/git.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/hg.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/svn.rb create mode 100644 puppet/modules/vcsrepo/lib/puppet/type/vcsrepo.rb create mode 100644 puppet/modules/vcsrepo/metadata.json create mode 100644 puppet/modules/vcsrepo/spec/fixtures/bzr_version_info.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/git_branch_a.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/git_branch_feature_bar.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/git_branch_none.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/hg_parents.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/hg_tags.txt create mode 100644 puppet/modules/vcsrepo/spec/fixtures/svn_info.txt create mode 100644 puppet/modules/vcsrepo/spec/spec.opts create mode 100644 puppet/modules/vcsrepo/spec/spec_helper.rb create mode 100644 puppet/modules/vcsrepo/spec/support/filesystem_helpers.rb create mode 100644 puppet/modules/vcsrepo/spec/support/fixture_helpers.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/git_spec.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/hg_spec.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/svn_spec.rb create mode 100644 puppet/modules/vcsrepo/spec/unit/puppet/type/README.markdown diff --git a/puppet/manifests/cleanup.pp b/puppet/manifests/cleanup.pp new file mode 100644 index 0000000..391160c --- /dev/null +++ b/puppet/manifests/cleanup.pp @@ -0,0 +1,19 @@ +node default { + $dir = '/home/stack/devstack' + + file {"/etc/hostname": + ensure => absent + } + + file {"/etc/udev/rules.d/70-persistent-net.rules": + ensure => absent + } + + exec {"clean.sh": + cwd => $dir, + path => "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:.", + user => 'stack', + command => "$dir/clean.sh", + logoutput => "on_failure", + } +} diff --git a/puppet/manifests/default.pp b/puppet/manifests/default.pp new file mode 100644 index 0000000..94e0d19 --- /dev/null +++ b/puppet/manifests/default.pp @@ -0,0 +1,5 @@ +node default { + include base + include user::stack + include devstack +} diff --git a/puppet/modules/base/manifests/init.pp b/puppet/modules/base/manifests/init.pp new file mode 100644 index 0000000..3f505ad --- /dev/null +++ b/puppet/modules/base/manifests/init.pp @@ -0,0 +1,10 @@ +class base { + $editors = ["joe", "vim"] + $vcs = ["git"] + package {$editors: + ensure => latest + } + package {$vcs: + ensure => latest + } +} diff --git a/puppet/modules/devstack/files/compute.conf b/puppet/modules/devstack/files/compute.conf new file mode 100644 index 0000000..ea5709d --- /dev/null +++ b/puppet/modules/devstack/files/compute.conf @@ -0,0 +1,22 @@ +[[local|localrc]] +ENABLED_SERVICES=n-cpu,n-net,c-vol +MULTI_HOST="True" +GUEST_INTERFACE_DEFAULT=eth1 +HOST_IP_IFACE=eth1 +DATABASE_TYPE=mysql +SERVICE_HOST=api.dague.pvt +MYSQL_HOST=$SERVICE_HOST +RABBIT_HOST=$SERVICE_HOST +GLANCE_HOST=$SERVICE_HOST +DATABASE_PASSWORD=pass +RABBIT_PASSWORD=pass +SERVICE_TOKEN=pass +SERVICE_PASSWORD=pass +ADMIN_PASSWORD=pass +API_RATE_LIMIT="False" +RECLONE="True" + +[[post-config|$NOVA_CONF]] +[DEFAULT] +flat_interface = eth1 +vlan_interface = eth1 diff --git a/puppet/modules/devstack/files/local.sh b/puppet/modules/devstack/files/local.sh new file mode 100644 index 0000000..9678440 --- /dev/null +++ b/puppet/modules/devstack/files/local.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -o xtrace + +openrc=/home/stack/devstack/openrc +source $openrc admin + +if is_service_enabled n-api; then + for user in "admin demo"; do + source $openrc $user + nova keypair-add --pub-key /home/stack/.ssh/authorized_keys default + done + + source $openrc admin + + nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0 + nova secgroup-add-rule default tcp 22 22 0.0.0.0/0 +fi diff --git a/puppet/modules/devstack/files/manager.conf b/puppet/modules/devstack/files/manager.conf new file mode 100644 index 0000000..0f0ce45 --- /dev/null +++ b/puppet/modules/devstack/files/manager.conf @@ -0,0 +1,21 @@ +[[local|localrc]] +DATABASE_PASSWORD=pass +RABBIT_PASSWORD=pass +SERVICE_TOKEN=pass +SERVICE_PASSWORD=pass +ADMIN_PASSWORD=pass +MULTI_HOST="True" +API_RATE_LIMIT="False" +RECLONE="True" +GUEST_INTERFACE_DEFAULT=eth1 +HOST_IP_IFACE=eth1 +IMAGE_URLS="http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz" +IMAGE_URLS+=",https://download.fedoraproject.org/pub/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2" +IMAGE_URLS+=",https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img" + +enable_service dstat + +[[post-config|$NOVA_CONF]] +[DEFAULT] +flat_interface = eth1 +vlan_interface = eth1 diff --git a/puppet/modules/devstack/manifests/init.pp b/puppet/modules/devstack/manifests/init.pp new file mode 100644 index 0000000..3cedfae --- /dev/null +++ b/puppet/modules/devstack/manifests/init.pp @@ -0,0 +1,45 @@ +class devstack( + $dir = '/home/stack/devstack' +) +{ + vcsrepo { $dir: + ensure => latest, + provider => git, + source => 'http://github.com/openstack-dev/devstack', + require => Class["user::stack"], + user => 'stack', + revision => 'master' + } + + if $is_compute == 'true' { + $localrc = 'compute.conf' + } else { + $localrc = 'manager.conf' + } + + file { "$dir/local.sh": + owner => "stack", + group => "stack", + mode => 755, + source => "puppet:///modules/devstack/local.sh", + require => vcsrepo[ $dir ] + } + + file { "$dir/local.conf": + owner => "stack", + group => "stack", + mode => 644, + source => "puppet:///modules/devstack/$localrc", + require => [vcsrepo[ $dir ], file["$dir/local.sh"]] + } + + exec {"stack.sh": + require => [vcsrepo[ $dir ], file["$dir/local.conf"]], + cwd => $dir, + path => "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:.", + user => 'stack', + command => "$dir/stack.sh", + logoutput => true, + timeout => 1200 + } +} diff --git a/puppet/modules/devstack/templates/local.erb b/puppet/modules/devstack/templates/local.erb new file mode 100644 index 0000000..22580c4 --- /dev/null +++ b/puppet/modules/devstack/templates/local.erb @@ -0,0 +1,33 @@ +[[local|localrc]] +DATABASE_PASSWORD=pass +RABBIT_PASSWORD=pass +SERVICE_TOKEN=pass +SERVICE_PASSWORD=pass +ADMIN_PASSWORD=pass +MULTI_HOST="True" +API_RATE_LIMIT="False" +RECLONE="True" +GUEST_INTERFACE_DEFAULT=eth1 +HOST_IP_IFACE=eth1 +<% if @is_compute %> +ENABLED_SERVICES=n-cpu,n-net,c-vol + +DATABASE_TYPE=mysql +SERVICE_HOST=<%= @manager_hostname %> +MYSQL_HOST=$SERVICE_HOST +RABBIT_HOST=$SERVICE_HOST +GLANCE_HOST=$SERVICE_HOST +<% else %> +# The manager +IMAGE_URLS="http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz" +IMAGE_URLS+=",https://download.fedoraproject.org/pub/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2" +IMAGE_URLS+=",https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img" + +enable_service dstat + +<% end %> + +[[post-config|$NOVA_CONF]] +[DEFAULT] +flat_interface = eth1 +vlan_interface = eth1 diff --git a/puppet/modules/user/files/stack_bashrc b/puppet/modules/user/files/stack_bashrc new file mode 100644 index 0000000..4d93cbe --- /dev/null +++ b/puppet/modules/user/files/stack_bashrc @@ -0,0 +1,72 @@ +# -*- shell-script -*- +# .bashrc + +# This is a utility function to give you git branch in the prompt +# if you are in a git directory +function parse_git_branch { + git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/' +} + +alias ls='ls -F --color' + +export PROMPT_DIRTRIM=3 + +# setup variables for prompt color codes. Makes it much easier to +# read the prompt code below +function proml { + local BLUE="\[\033[0;34m\]" + local RED="\[\033[0;31m\]" + local LIGHT_RED="\[\033[1;31m\]" + local GREEN="\[\033[0;32m\]" + local YELLOW="\[\033[0;33m\]" + local LIGHT_GREEN="\[\033[1;32m\]" + local WHITE="\[\033[1;37m\]" + local LIGHT_GRAY="\[\033[0;37m\]" + local RESET="\[\e[0m\]" + +# These variables are used for prompt and title +MYUSER="\u@" +XMYUSER="" + +# if this terminal "is" an xterm (or compatible) build +# a titlebar variable which is the same as the prompt more or less + case $TERM in + xterm*) + TITLEBAR="\[\033]0;$MYUSER\h:\w\007\]" + ;; + *) + TITLEBAR="" + ;; + esac + +# PS1 - normal prompt +# set title bar +# set prompt to yellow user@host : red directory (green git branch) > +PS1="${TITLEBAR}\ +$YELLOW$MYUSER\h$RED:\w$GREEN\$(parse_git_branch)\ +$RED> $RESET" +PS2='> ' +PS4='+ ' +} +proml + +# Source global definitions +if [ -f /etc/bashrc ]; then + . /etc/bashrc +fi +# the variable is not defined, and after the /etc/inputrc +# include the ~/.inputrc +if [ -f /etc/bash_completion ]; then + . /etc/bash_completion +fi + +# because I'm not sure all platforms do this +export PAGER=less + +# allow core files, I do development, and I want to see them +ulimit -c unlimited + +export HISTIGNORE="&:ls:exit" + +# this massively speeds up pip install +export PIP_DOWNLOAD_CACHE=~/.pip/cache diff --git a/puppet/modules/user/files/stack_sudoers b/puppet/modules/user/files/stack_sudoers new file mode 100644 index 0000000..283f5e5 --- /dev/null +++ b/puppet/modules/user/files/stack_sudoers @@ -0,0 +1 @@ +stack ALL=(ALL) NOPASSWD:ALL \ No newline at end of file diff --git a/puppet/modules/user/manifests/create.pp b/puppet/modules/user/manifests/create.pp new file mode 100644 index 0000000..5181241 --- /dev/null +++ b/puppet/modules/user/manifests/create.pp @@ -0,0 +1,58 @@ +define user::create ( + $user = "", + $pass = "", + $key = "", + $key_type = "rsa", + $home = "/home/${user}", + $is_admin = false +) +{ + if $is_admin == true { + $extra_groups = ['sudo', 'dialout'] + } + else { + $extra_groups = ['dialout'] + } + + group {$user: + ensure => present, + } -> + + user {$user: + gid => $user, + password => $pass, + home => $home, + groups => $extra_groups, + ensure => present, + shell => "/bin/bash" + } -> + + file { $home: + owner => $user, + group => $user, + mode => 755, + ensure => directory, + } -> + + file { "${home}/bin": + owner => $user, + group => $user, + mode => 755, + ensure => directory, + } -> + + file { "${home}/.ssh": + owner => $user, + group => $user, + mode => 700, + ensure => directory, + } -> + + ssh_authorized_key { $user: + key => $key, + user => $user, + type => $key_type, + ensure => present + } + +} diff --git a/puppet/modules/user/manifests/stack.pp b/puppet/modules/user/manifests/stack.pp new file mode 100644 index 0000000..93f15b1 --- /dev/null +++ b/puppet/modules/user/manifests/stack.pp @@ -0,0 +1,24 @@ +class user::stack() { + + file {'/etc/sudoers.d/stack': + owner => "root", + group => "root", + mode => 440, + source => "puppet:///modules/user/stack_sudoers" + } -> + + user::create {'stack': + user => 'stack', + pass => $stack_pass, + key => $stack_sshkey, + is_admin => true, + } -> + + file {'/home/stack/.bashrc': + owner => "stack", + group => "stack", + mode => 644, + source => "puppet:///modules/user/stack_bashrc" + } + +} diff --git a/puppet/modules/vcsrepo/CHANGELOG b/puppet/modules/vcsrepo/CHANGELOG new file mode 100644 index 0000000..8142f5d --- /dev/null +++ b/puppet/modules/vcsrepo/CHANGELOG @@ -0,0 +1,41 @@ +2013-11-13 - Version 0.2.0 + +Summary: + +This release mainly focuses on a number of bugfixes, which should +significantly improve the reliability of Git and SVN. Thanks to +our many contributors for all of these fixes! + +Features: +- Git: + - Add autorequire for Package['git'] +- HG: + - Allow user and identity properties. +- Bzr: + - "ensure => latest" support. +- SVN: + - Added configuration parameter. + - Add support for master svn repositories. +- CVS: + - Allow for setting the CVS_RSH environment variable. + +Fixes: +- Handle Puppet::Util[::Execution].withenv for 2.x and 3.x properly. +- Change path_empty? to not do full directory listing. +- Overhaul spec tests to work with rspec2. +- Git: + - Improve Git SSH usage documentation. + - Add ssh session timeouts to prevent network issues from blocking runs. + - Fix git provider checkout of a remote ref on an existing repo. + - Allow unlimited submodules (thanks to --recursive). + - Use git checkout --force instead of short -f everywhere. + - Update git provider to handle checking out into an existing (empty) dir. +- SVN: + - Handle force property. for svn. + - Adds support for changing upstream repo url. + - Check that the URL of the WC matches the URL from the manifest. + - Changed from using "update" to "switch". + - Handle revision update without source switch. + - Fix svn provider to look for '^Revision:' instead of '^Last Changed Rev:'. +- CVS: + - Documented the "module" attribute. diff --git a/puppet/modules/vcsrepo/Gemfile b/puppet/modules/vcsrepo/Gemfile new file mode 100644 index 0000000..5def829 --- /dev/null +++ b/puppet/modules/vcsrepo/Gemfile @@ -0,0 +1,22 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system', :require => false + gem 'rspec-system-puppet', :require => false + gem 'rspec-system-serverspec', :require => false + gem 'serverspec', :require => false + gem 'puppet-lint', :require => false + gem 'pry', :require => false + gem 'simplecov', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/puppet/modules/vcsrepo/Gemfile.lock b/puppet/modules/vcsrepo/Gemfile.lock new file mode 100644 index 0000000..31f8284 --- /dev/null +++ b/puppet/modules/vcsrepo/Gemfile.lock @@ -0,0 +1,91 @@ +GEM + remote: https://rubygems.org/ + specs: + builder (3.2.2) + coderay (1.0.9) + diff-lcs (1.2.4) + facter (1.7.3) + hiera (1.2.1) + json_pure + highline (1.6.19) + json_pure (1.8.0) + kwalify (0.7.2) + metaclass (0.0.1) + method_source (0.8.2) + mocha (0.14.0) + metaclass (~> 0.0.1) + multi_json (1.8.0) + net-scp (1.1.2) + net-ssh (>= 2.6.5) + net-ssh (2.7.0) + nokogiri (1.5.10) + pry (0.9.12.2) + coderay (~> 1.0.5) + method_source (~> 0.8) + slop (~> 3.4) + puppet (3.3.0) + facter (~> 1.6) + hiera (~> 1.0) + rgen (~> 0.6.5) + puppet-lint (0.3.2) + puppetlabs_spec_helper (0.4.1) + mocha (>= 0.10.5) + rake + rspec (>= 2.9.0) + rspec-puppet (>= 0.1.1) + rake (10.1.0) + rbvmomi (1.6.0) + builder + nokogiri (>= 1.4.1) + trollop + rgen (0.6.6) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.5) + rspec-expectations (2.14.2) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.3) + rspec-puppet (0.1.6) + rspec + rspec-system (2.2.1) + kwalify (~> 0.7.2) + net-scp (~> 1.1) + net-ssh (~> 2.6) + nokogiri (~> 1.5.9) + rbvmomi (~> 1.6) + rspec (~> 2.13) + systemu (~> 2.5) + rspec-system-puppet (2.2.0) + rspec-system (~> 2.0) + rspec-system-serverspec (1.0.0) + rspec-system (~> 2.0) + serverspec (~> 0.6.0) + serverspec (0.6.3) + highline + net-ssh + rspec (~> 2.0) + simplecov (0.7.1) + multi_json (~> 1.0) + simplecov-html (~> 0.7.1) + simplecov-html (0.7.1) + slop (3.4.6) + systemu (2.5.2) + trollop (2.0) + +PLATFORMS + ruby + +DEPENDENCIES + pry + puppet + puppet-lint + puppetlabs_spec_helper + rake + rspec-puppet + rspec-system + rspec-system-puppet + rspec-system-serverspec + serverspec + simplecov diff --git a/puppet/modules/vcsrepo/LICENSE b/puppet/modules/vcsrepo/LICENSE new file mode 100644 index 0000000..2ee80c8 --- /dev/null +++ b/puppet/modules/vcsrepo/LICENSE @@ -0,0 +1,17 @@ +Copyright (C) 2010-2012 Puppet Labs Inc. + +Puppet Labs can be contacted at: info@puppetlabs.com + +This program and entire repository is free software; you can +redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software +Foundation; either version 2 of the License, or any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/puppet/modules/vcsrepo/Modulefile b/puppet/modules/vcsrepo/Modulefile new file mode 100644 index 0000000..d2bbe92 --- /dev/null +++ b/puppet/modules/vcsrepo/Modulefile @@ -0,0 +1,4 @@ +name 'puppetlabs/vcsrepo' +version '0.2.0' +summary 'Manage repositories from various version control systems' +description 'Manage repositories from various version control systems' diff --git a/puppet/modules/vcsrepo/README.BZR.markdown b/puppet/modules/vcsrepo/README.BZR.markdown new file mode 100644 index 0000000..cc257e9 --- /dev/null +++ b/puppet/modules/vcsrepo/README.BZR.markdown @@ -0,0 +1,47 @@ +Using vcsrepo with Bazaar +========================= + +To create a blank repository +---------------------------- + +Define a `vcsrepo` without a `source` or `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => bzr + } + +To branch from an existing repository +------------------------------------- + +Provide the `source` location: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => bzr, + source => 'lp:myproj' + } + +For a specific revision, use `revision` with a valid revisionspec +(see `bzr help revisionspec` for more information on formatting a revision): + + vcsrepo { "/path/to/repo": + ensure => present, + provider => bzr, + source => 'lp:myproj', + revision => 'menesis@pov.lt-20100309191856-4wmfqzc803fj300x' + } + +For sources that use SSH (eg, `bzr+ssh://...`, `sftp://...`) +------------------------------------------------------------ + +Manage your SSH keys with Puppet and use `require` in your `vcsrepo` +to ensure they are present. For more information, see the `require` +metaparameter documentation[1]. + +More Examples +------------- + +For examples you can run, see `examples/bzr/` + +[1]: http://docs.puppetlabs.com/references/stable/metaparameter.html#require diff --git a/puppet/modules/vcsrepo/README.CVS.markdown b/puppet/modules/vcsrepo/README.CVS.markdown new file mode 100644 index 0000000..3bdd59d --- /dev/null +++ b/puppet/modules/vcsrepo/README.CVS.markdown @@ -0,0 +1,66 @@ +Using vcsrepo with CVS +====================== + +To create a blank repository +---------------------------- + +Define a `vcsrepo` without a `source` or `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => cvs + } + +To checkout/update from a repository +------------------------------------ + +To get the current mainline: + + vcsrepo { "/path/to/workspace": + ensure => present, + provider => cvs, + source => ":pserver:anonymous@example.com:/sources/myproj" + } + +To get a specific module on the current mainline: + + vcsrepo {"/vagrant/lockss-daemon-source": + ensure => present, + provider => cvs, + source => ":pserver:anonymous@lockss.cvs.sourceforge.net:/cvsroot/lockss", + module => "lockss-daemon", + } + + +You can use the `compression` parameter (it works like CVS `-z`): + + vcsrepo { "/path/to/workspace": + ensure => present, + provider => cvs, + compression => 3, + source => ":pserver:anonymous@example.com:/sources/myproj" + } + +For a specific tag, use `revision`: + + vcsrepo { "/path/to/workspace": + ensure => present, + provider => cvs, + compression => 3, + source => ":pserver:anonymous@example.com:/sources/myproj", + revision => "SOMETAG" + } + +For sources that use SSH +------------------------ + +Manage your SSH keys with Puppet and use `require` in your `vcsrepo` +to ensure they are present. For more information, see the `require` +metaparameter documentation[1]. + +More Examples +------------- + +For examples you can run, see `examples/cvs/` + +[1]: http://docs.puppetlabs.com/references/stable/metaparameter.html#require diff --git a/puppet/modules/vcsrepo/README.GIT.markdown b/puppet/modules/vcsrepo/README.GIT.markdown new file mode 100644 index 0000000..846bdcc --- /dev/null +++ b/puppet/modules/vcsrepo/README.GIT.markdown @@ -0,0 +1,95 @@ +Using vcsrepo with Git +====================== + +To create a blank repository +---------------------------- + +Define a `vcsrepo` without a `source` or `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git + } + +If you're defining this for a central/"official" repository, you'll +probably want to make it a "bare" repository. Do this by setting +`ensure` to `bare` instead of `present`: + + vcsrepo { "/path/to/repo": + ensure => bare, + provider => git + } + +To clone/pull a repository +---------------------------- + +To get the current [master] HEAD: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git, + source => "git://example.com/repo.git" + } + +For a specific revision or branch (can be a commit SHA, tag or branch name): + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git, + source => 'git://example.com/repo.git', + revision => '0c466b8a5a45f6cd7de82c08df2fb4ce1e920a31' + } + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git, + source => 'git://example.com/repo.git', + revision => '1.1.2rc1' + } + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git, + source => 'git://example.com/repo.git', + revision => 'development' + } + +Check out as a user: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => git, + source => 'git://example.com/repo.git', + revision => '0c466b8a5a45f6cd7de82c08df2fb4ce1e920a31', + user => 'someUser' + } + +Keep the repository at the latest revision (note: this will always overwrite local changes to the repository): + + vcsrepo { "/path/to/repo": + ensure => latest, + provider => git, + source => 'git://example.com/repo.git', + revision => 'master', + } + +For sources that use SSH (eg, `username@server:...`) +---------------------------------------------------- + +If your SSH key is associated with a user, simply fill the `user` parameter to use his keys. + +Example: + + user => 'toto' # will use toto's $HOME/.ssh setup + + +Otherwise, manage your SSH keys with Puppet and use `require` in your `vcsrepo` to ensure they are present. +For more information, see the `require` metaparameter documentation[1]. + +More Examples +------------- + +For examples you can run, see `examples/git/` + +[1]: http://docs.puppetlabs.com/references/stable/metaparameter.html#require + diff --git a/puppet/modules/vcsrepo/README.HG.markdown b/puppet/modules/vcsrepo/README.HG.markdown new file mode 100644 index 0000000..55ceef4 --- /dev/null +++ b/puppet/modules/vcsrepo/README.HG.markdown @@ -0,0 +1,73 @@ +Using vcsrepo with Mercurial +============================ + +To create a blank repository +---------------------------- + +Define a `vcsrepo` without a `source` or `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg + } + +To clone/pull & update a repository +----------------------------------- + +To get the default branch tip: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "http://hg.example.com/myrepo" + } + +For a specific changeset, use `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "http://hg.example.com/myrepo", + revision => '21ea4598c962' + } + +You can also set `revision` to a tag: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "http://hg.example.com/myrepo", + revision => '1.1.2' + } + +Check out as a user: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "http://hg.example.com/myrepo", + user => 'user' + } + +Specify an SSH identity key: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "ssh://hg@hg.example.com/myrepo", + identity => "/home/user/.ssh/id_dsa, + } + +For sources that use SSH (eg, `ssh://...`) +------------------------------------------ + +Manage your SSH keys with Puppet and use `require` in your `vcsrepo` +to ensure they are present. For more information, see the `require` +metaparameter documentation[1]. + +More Examples +------------- + +For examples you can run, see `examples/hg/` + +[1]: http://docs.puppetlabs.com/references/stable/metaparameter.html#require diff --git a/puppet/modules/vcsrepo/README.SVN.markdown b/puppet/modules/vcsrepo/README.SVN.markdown new file mode 100644 index 0000000..f374094 --- /dev/null +++ b/puppet/modules/vcsrepo/README.SVN.markdown @@ -0,0 +1,62 @@ +Using vcsrepo with Subversion +============================= + +To create a blank repository +---------------------------- + +To create a blank repository suitable for use as a central repository, +define a `vcsrepo` without a `source` or `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => svn + } + +To checkout from a repository +----------------------------- + +Provide a `source` qualified to the branch/tag you want: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => svn, + source => "svn://svnrepo/hello/branches/foo" + } + +You can provide a specific `revision`: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => svn, + source => "svn://svnrepo/hello/branches/foo", + revision => '1234' + } + + +Using a specified Subversion configuration directory +----------------------------- + +Provide a `configuration` parameter which should be a directory path on the local system where your svn configuration +files are. Typically, it is /path/to/.subversion: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => svn, + source => "svn://svnrepo/hello/branches/foo", + configuration => "/path/to/.subversion" + } + + +For sources that use SSH (eg, `svn+ssh://...`) +---------------------------------------------- + +Manage your SSH keys with Puppet and use `require` in your `vcsrepo` +to ensure they are present. For more information, see the `require` +metaparameter documentation[1]. + +More Examples +------------- + +For examples you can run, see `examples/svn/` + +[1]: http://docs.puppetlabs.com/references/stable/metaparameter.html#require diff --git a/puppet/modules/vcsrepo/README.markdown b/puppet/modules/vcsrepo/README.markdown new file mode 100644 index 0000000..8487256 --- /dev/null +++ b/puppet/modules/vcsrepo/README.markdown @@ -0,0 +1,32 @@ +vcsrepo +======= + +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-vcsrepo.png?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-vcsrepo) + +Purpose +------- + +This provides a single type, `vcsrepo`. + +This type can be used to describe: + +* A working copy checked out from a (remote or local) source, at an + arbitrary revision +* A "blank" working copy not associated with a source (when it makes + sense for the VCS being used) +* A "blank" central repository (when the distinction makes sense for the VCS + being used) + +Supported Version Control Systems +--------------------------------- + +This module supports a wide range of VCS types, each represented by a +separate provider. + +For information on how to use this module with a specific VCS, see +`README..markdown`. + +License +------- + +See LICENSE. diff --git a/puppet/modules/vcsrepo/Rakefile b/puppet/modules/vcsrepo/Rakefile new file mode 100644 index 0000000..cd3d379 --- /dev/null +++ b/puppet/modules/vcsrepo/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/puppet/modules/vcsrepo/examples/bzr/branch.pp b/puppet/modules/vcsrepo/examples/bzr/branch.pp new file mode 100644 index 0000000..0ed0705 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/bzr/branch.pp @@ -0,0 +1,6 @@ +vcsrepo { '/tmp/vcstest-bzr-branch': + ensure => present, + provider => bzr, + source => 'lp:do', + revision => '1312', +} diff --git a/puppet/modules/vcsrepo/examples/bzr/init_repo.pp b/puppet/modules/vcsrepo/examples/bzr/init_repo.pp new file mode 100644 index 0000000..1129dd7 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/bzr/init_repo.pp @@ -0,0 +1,4 @@ +vcsrepo { '/tmp/vcstest-bzr-init': + ensure => present, + provider => bzr, +} diff --git a/puppet/modules/vcsrepo/examples/cvs/local.pp b/puppet/modules/vcsrepo/examples/cvs/local.pp new file mode 100644 index 0000000..155742e --- /dev/null +++ b/puppet/modules/vcsrepo/examples/cvs/local.pp @@ -0,0 +1,11 @@ +vcsrepo { '/tmp/vcstest-cvs-repo': + ensure => present, + provider => cvs, +} + +vcsrepo { '/tmp/vcstest-cvs-workspace-local': + ensure => present, + provider => cvs, + source => '/tmp/vcstest-cvs-repo', + require => Vcsrepo['/tmp/vcstest-cvs-repo'], +} diff --git a/puppet/modules/vcsrepo/examples/cvs/remote.pp b/puppet/modules/vcsrepo/examples/cvs/remote.pp new file mode 100644 index 0000000..eb9665a --- /dev/null +++ b/puppet/modules/vcsrepo/examples/cvs/remote.pp @@ -0,0 +1,5 @@ +vcsrepo { '/tmp/vcstest-cvs-workspace-remote': + ensure => present, + provider => cvs, + source => ':pserver:anonymous@cvs.sv.gnu.org:/sources/leetcvrt', +} diff --git a/puppet/modules/vcsrepo/examples/git/bare_init.pp b/puppet/modules/vcsrepo/examples/git/bare_init.pp new file mode 100644 index 0000000..4166f6e --- /dev/null +++ b/puppet/modules/vcsrepo/examples/git/bare_init.pp @@ -0,0 +1,4 @@ +vcsrepo { '/tmp/vcstest-git-bare': + ensure => bare, + provider => git, +} diff --git a/puppet/modules/vcsrepo/examples/git/clone.pp b/puppet/modules/vcsrepo/examples/git/clone.pp new file mode 100644 index 0000000..b29a4fd --- /dev/null +++ b/puppet/modules/vcsrepo/examples/git/clone.pp @@ -0,0 +1,5 @@ +vcsrepo { '/tmp/vcstest-git-clone': + ensure => present, + provider => git, + source => 'git://github.com/bruce/rtex.git', +} diff --git a/puppet/modules/vcsrepo/examples/git/working_copy_init.pp b/puppet/modules/vcsrepo/examples/git/working_copy_init.pp new file mode 100644 index 0000000..e3352eb --- /dev/null +++ b/puppet/modules/vcsrepo/examples/git/working_copy_init.pp @@ -0,0 +1,4 @@ +vcsrepo { '/tmp/vcstest-git-wc': + ensure => present, + provider => git, +} diff --git a/puppet/modules/vcsrepo/examples/hg/clone.pp b/puppet/modules/vcsrepo/examples/hg/clone.pp new file mode 100644 index 0000000..be2d955 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/hg/clone.pp @@ -0,0 +1,6 @@ +vcsrepo { '/tmp/vcstest-hg-clone': + ensure => present, + provider => hg, + source => 'http://hg.basho.com/riak', + revision => 'riak-0.5.3', +} diff --git a/puppet/modules/vcsrepo/examples/hg/init_repo.pp b/puppet/modules/vcsrepo/examples/hg/init_repo.pp new file mode 100644 index 0000000..a890804 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/hg/init_repo.pp @@ -0,0 +1,4 @@ +vcsrepo { '/tmp/vcstest-hg-init': + ensure => present, + provider => hg, +} diff --git a/puppet/modules/vcsrepo/examples/svn/checkout.pp b/puppet/modules/vcsrepo/examples/svn/checkout.pp new file mode 100644 index 0000000..f9fc273 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/svn/checkout.pp @@ -0,0 +1,5 @@ +vcsrepo { '/tmp/vcstest-svn-checkout': + ensure => present, + provider => svn, + source => 'http://svn.edgewall.org/repos/babel/trunk', +} diff --git a/puppet/modules/vcsrepo/examples/svn/server.pp b/puppet/modules/vcsrepo/examples/svn/server.pp new file mode 100644 index 0000000..de7c390 --- /dev/null +++ b/puppet/modules/vcsrepo/examples/svn/server.pp @@ -0,0 +1,4 @@ +vcsrepo { '/tmp/vcstest-svn-server': + ensure => present, + provider => svn, +} diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo.rb new file mode 100644 index 0000000..8793e63 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo.rb @@ -0,0 +1,42 @@ +require 'tmpdir' +require 'digest/md5' +require 'fileutils' + +# Abstract +class Puppet::Provider::Vcsrepo < Puppet::Provider + + private + + def set_ownership + owner = @resource.value(:owner) || nil + group = @resource.value(:group) || nil + FileUtils.chown_R(owner, group, @resource.value(:path)) + end + + def path_exists? + File.directory?(@resource.value(:path)) + end + + def path_empty? + # Path is empty if the only entries are '.' and '..' + d = Dir.new(@resource.value(:path)) + d.read # should return '.' + d.read # should return '..' + d.read.nil? + end + + # Note: We don't rely on Dir.chdir's behavior of automatically returning the + # value of the last statement -- for easier stubbing. + def at_path(&block) #:nodoc: + value = nil + Dir.chdir(@resource.value(:path)) do + value = yield + end + value + end + + def tempdir + @tempdir ||= File.join(Dir.tmpdir, 'vcsrepo-' + Digest::MD5.hexdigest(@resource.value(:path))) + end + +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb new file mode 100644 index 0000000..6688ce8 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb @@ -0,0 +1,85 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:bzr, :parent => Puppet::Provider::Vcsrepo) do + desc "Supports Bazaar repositories" + + optional_commands :bzr => 'bzr' + has_features :reference_tracking + + def create + if !@resource.value(:source) + create_repository(@resource.value(:path)) + else + clone_repository(@resource.value(:revision)) + end + end + + def working_copy_exists? + File.directory?(File.join(@resource.value(:path), '.bzr')) + end + + def exists? + working_copy_exists? + end + + def destroy + FileUtils.rm_rf(@resource.value(:path)) + end + + def revision + at_path do + current_revid = bzr('version-info')[/^revision-id:\s+(\S+)/, 1] + desired = @resource.value(:revision) + begin + desired_revid = bzr('revision-info', desired).strip.split(/\s+/).last + rescue Puppet::ExecutionFailure + # Possible revid available during update (but definitely not current) + desired_revid = nil + end + if current_revid == desired_revid + desired + else + current_revid + end + end + end + + def revision=(desired) + at_path do + begin + bzr('update', '-r', desired) + rescue Puppet::ExecutionFailure + bzr('update', '-r', desired, ':parent') + end + end + end + + def latest + at_path do + bzr('version-info', ':parent')[/^revision-id:\s+(\S+)/, 1] + end + end + + def latest? + at_path do + return self.revision == self.latest + end + end + + private + + def create_repository(path) + bzr('init', path) + end + + def clone_repository(revision) + args = ['branch'] + if revision + args.push('-r', revision) + end + args.push(@resource.value(:source), + @resource.value(:path)) + bzr(*args) + end + +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb new file mode 100644 index 0000000..206e732 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb @@ -0,0 +1,137 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:cvs, :parent => Puppet::Provider::Vcsrepo) do + desc "Supports CVS repositories/workspaces" + + optional_commands :cvs => 'cvs' + has_features :gzip_compression, :reference_tracking, :modules, :cvs_rsh + + def create + if !@resource.value(:source) + create_repository(@resource.value(:path)) + else + checkout_repository + end + update_owner + end + + def exists? + if @resource.value(:source) + directory = File.join(@resource.value(:path), 'CVS') + else + directory = File.join(@resource.value(:path), 'CVSROOT') + end + File.directory?(directory) + end + + def working_copy_exists? + File.directory?(File.join(@resource.value(:path), 'CVS')) + end + + def destroy + FileUtils.rm_rf(@resource.value(:path)) + end + + def latest? + debug "Checking for updates because 'ensure => latest'" + at_path do + # We cannot use -P to prune empty dirs, otherwise + # CVS would report those as "missing", regardless + # if they have contents or updates. + is_current = (runcvs('-nq', 'update', '-d').strip == "") + if (!is_current) then debug "There are updates available on the checkout's current branch/tag." end + return is_current + end + end + + def latest + # CVS does not have a conecpt like commit-IDs or change + # sets, so we can only have the current branch name (or the + # requested one, if that differs) as the "latest" revision. + should = @resource.value(:revision) + current = self.revision + return should != current ? should : current + end + + def revision + if !@rev + if File.exist?(tag_file) + contents = File.read(tag_file).strip + # Note: Doesn't differentiate between N and T entries + @rev = contents[1..-1] + else + @rev = 'HEAD' + end + debug "Checkout is on branch/tag '#{@rev}'" + end + return @rev + end + + def revision=(desired) + at_path do + runcvs('update', '-dr', desired, '.') + update_owner + @rev = desired + end + end + + private + + def tag_file + File.join(@resource.value(:path), 'CVS', 'Tag') + end + + def checkout_repository + dirname, basename = File.split(@resource.value(:path)) + Dir.chdir(dirname) do + args = ['-d', @resource.value(:source)] + if @resource.value(:compression) + args.push('-z', @resource.value(:compression)) + end + args.push('checkout') + if @resource.value(:revision) + args.push('-r', @resource.value(:revision)) + end + args.push('-d', basename, module_name) + runcvs(*args) + end + end + + # When the source: + # * Starts with ':' (eg, :pserver:...) + def module_name + if (m = @resource.value(:module)) + m + elsif (source = @resource.value(:source)) + source[0, 1] == ':' ? File.basename(source) : '.' + end + end + + def create_repository(path) + runcvs('-d', path, 'init') + end + + def update_owner + if @resource.value(:owner) or @resource.value(:group) + set_ownership + end + end + + def runcvs(*args) + if @resource.value(:cvs_rsh) + debug "Using CVS_RSH = " + @resource.value(:cvs_rsh) + e = { :CVS_RSH => @resource.value(:cvs_rsh) } + else + e = {} + end + + # The location of withenv changed from Puppet 2.x to 3.x + withenv = Puppet::Util.method(:withenv) if Puppet::Util.respond_to?(:withenv) + withenv = Puppet::Util::Execution.method(:withenv) if Puppet::Util::Execution.respond_to?(:withenv) + fail("Cannot set custom environment #{e}") if e && !withenv + + withenv.call e do + Puppet.debug cvs *args + end + end +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb new file mode 100644 index 0000000..f7b4e54 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb @@ -0,0 +1,12 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:dummy, :parent => Puppet::Provider::Vcsrepo) do + desc "Dummy default provider" + + defaultfor :vcsrepo => :dummy + + def working_copy_exists? + providers = @resource.class.providers.map{|x| x.to_s}.sort.reject{|x| x == "dummy"}.join(", ") rescue "none" + raise("vcsrepo resource must have a provider, available: #{providers}") + end +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/git.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/git.rb new file mode 100644 index 0000000..47e84d2 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/git.rb @@ -0,0 +1,323 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) do + desc "Supports Git repositories" + + ##TODO modify the commands below so that the su - is included + optional_commands :git => 'git', + :su => 'su' + has_features :bare_repositories, :reference_tracking, :ssh_identity, :multiple_remotes, :user + + def create + if !@resource.value(:source) + init_repository(@resource.value(:path)) + else + clone_repository(@resource.value(:source), @resource.value(:path)) + if @resource.value(:revision) + if @resource.value(:ensure) == :bare + notice "Ignoring revision for bare repository" + else + checkout + end + end + if @resource.value(:ensure) != :bare + update_submodules + end + end + update_owner_and_excludes + end + + def destroy + FileUtils.rm_rf(@resource.value(:path)) + end + + def latest? + at_path do + return self.revision == self.latest + end + end + + def latest + branch = on_branch? + if branch == 'master' + return get_revision("#{@resource.value(:remote)}/HEAD") + elsif branch == '(no branch)' + return get_revision('HEAD') + else + return get_revision("#{@resource.value(:remote)}/%s" % branch) + end + end + + def revision + update_references + current = at_path { git_with_identity('rev-parse', 'HEAD').chomp } + return current unless @resource.value(:revision) + + if tag_revision?(@resource.value(:revision)) + canonical = at_path { git_with_identity('show', @resource.value(:revision)).scan(/^commit (.*)/).to_s } + else + # if it's not a tag, look for it as a local ref + canonical = at_path { git_with_identity('rev-parse', '--revs-only', @resource.value(:revision)).chomp } + if canonical.empty? + # git rev-parse executed properly but didn't find the ref; + # look for it in the remote + remote_ref = at_path { git_with_identity('ls-remote', '--heads', '--tags', @resource.value(:remote), @resource.value(:revision)).chomp } + if remote_ref.empty? + fail("#{@resource.value(:revision)} is not a local or remote ref") + end + + # $ git ls-remote --heads --tags origin feature/cvs + # 7d4244b35e72904e30130cad6d2258f901c16f1a refs/heads/feature/cvs + canonical = remote_ref.split.first + end + end + + if current == canonical + @resource.value(:revision) + else + current + end + end + + def revision=(desired) + checkout(desired) + if local_branch_revision?(desired) + # reset instead of pull to avoid merge conflicts. assuming remote is + # authoritative. + # might be worthwhile to have an allow_local_changes param to decide + # whether to reset or pull when we're ensuring latest. + at_path { git_with_identity('reset', '--hard', "#{@resource.value(:remote)}/#{desired}") } + end + if @resource.value(:ensure) != :bare + update_submodules + end + update_owner_and_excludes + end + + def bare_exists? + bare_git_config_exists? && !working_copy_exists? + end + + def working_copy_exists? + File.directory?(File.join(@resource.value(:path), '.git')) + end + + def exists? + working_copy_exists? || bare_exists? + end + + def update_remote_origin_url + current = git_with_identity('config', 'remote.origin.url') + unless @resource.value(:source).nil? + if current.nil? or current.strip != @resource.value(:source) + git_with_identity('config', 'remote.origin.url', @resource.value(:source)) + end + end + end + + def update_references + at_path do + update_remote_origin_url + git_with_identity('fetch', @resource.value(:remote)) + git_with_identity('fetch', '--tags', @resource.value(:remote)) + update_owner_and_excludes + end + end + + private + + def bare_git_config_exists? + File.exist?(File.join(@resource.value(:path), 'config')) + end + + def clone_repository(source, path) + check_force + args = ['clone'] + if @resource.value(:ensure) == :bare + args << '--bare' + end + if !File.exist?(File.join(@resource.value(:path), '.git')) + args.push(source, path) + Dir.chdir("/") do + git_with_identity(*args) + end + else + notice "Repo has already been cloned" + end + end + + def check_force + if path_exists? and not path_empty? + if @resource.value(:force) + notice "Removing %s to replace with vcsrepo." % @resource.value(:path) + destroy + else + raise Puppet::Error, "Could not create repository (non-repository at path)" + end + end + end + + def init_repository(path) + check_force + if @resource.value(:ensure) == :bare && working_copy_exists? + convert_working_copy_to_bare + elsif @resource.value(:ensure) == :present && bare_exists? + convert_bare_to_working_copy + else + # normal init + FileUtils.mkdir(@resource.value(:path)) + FileUtils.chown(@resource.value(:user), nil, @resource.value(:path)) if @resource.value(:user) + args = ['init'] + if @resource.value(:ensure) == :bare + args << '--bare' + end + at_path do + git_with_identity(*args) + end + end + end + + # Convert working copy to bare + # + # Moves: + # /.git + # to: + # / + def convert_working_copy_to_bare + notice "Converting working copy repository to bare repository" + FileUtils.mv(File.join(@resource.value(:path), '.git'), tempdir) + FileUtils.rm_rf(@resource.value(:path)) + FileUtils.mv(tempdir, @resource.value(:path)) + end + + # Convert bare to working copy + # + # Moves: + # / + # to: + # /.git + def convert_bare_to_working_copy + notice "Converting bare repository to working copy repository" + FileUtils.mv(@resource.value(:path), tempdir) + FileUtils.mkdir(@resource.value(:path)) + FileUtils.mv(tempdir, File.join(@resource.value(:path), '.git')) + if commits_in?(File.join(@resource.value(:path), '.git')) + reset('HEAD') + git_with_identity('checkout', '--force') + update_owner_and_excludes + end + end + + def commits_in?(dot_git) + Dir.glob(File.join(dot_git, 'objects/info/*'), File::FNM_DOTMATCH) do |e| + return true unless %w(. ..).include?(File::basename(e)) + end + false + end + + def checkout(revision = @resource.value(:revision)) + if !local_branch_revision? && remote_branch_revision? + at_path { git_with_identity('checkout', '-b', revision, '--track', "#{@resource.value(:remote)}/#{revision}") } + else + at_path { git_with_identity('checkout', '--force', revision) } + end + end + + def reset(desired) + at_path do + git_with_identity('reset', '--hard', desired) + end + end + + def update_submodules + at_path do + git_with_identity('submodule', 'update', '--init', '--recursive') + end + end + + def remote_branch_revision?(revision = @resource.value(:revision)) + # git < 1.6 returns '#{@resource.value(:remote)}/#{revision}' + # git 1.6+ returns 'remotes/#{@resource.value(:remote)}/#{revision}' + branch = at_path { branches.grep /(remotes\/)?#{@resource.value(:remote)}\/#{revision}/ } + branch unless branch.empty? + end + + def local_branch_revision?(revision = @resource.value(:revision)) + at_path { branches.include?(revision) } + end + + def tag_revision?(revision = @resource.value(:revision)) + at_path { tags.include?(revision) } + end + + def branches + at_path { git_with_identity('branch', '-a') }.gsub('*', ' ').split(/\n/).map { |line| line.strip } + end + + def on_branch? + at_path { git_with_identity('branch', '-a') }.split(/\n/).grep(/\*/).first.to_s.gsub('*', '').strip + end + + def tags + at_path { git_with_identity('tag', '-l') }.split(/\n/).map { |line| line.strip } + end + + def set_excludes + at_path { open('.git/info/exclude', 'w') { |f| @resource.value(:excludes).each { |ex| f.write(ex + "\n") }}} + end + + def get_revision(rev) + if !working_copy_exists? + create + end + at_path do + update_remote_origin_url + git_with_identity('fetch', @resource.value(:remote)) + git_with_identity('fetch', '--tags', @resource.value(:remote)) + end + current = at_path { git_with_identity('rev-parse', rev).strip } + if @resource.value(:revision) + if local_branch_revision? + canonical = at_path { git_with_identity('rev-parse', @resource.value(:revision)).strip } + elsif remote_branch_revision? + canonical = at_path { git_with_identity('rev-parse', "#{@resource.value(:remote)}/" + @resource.value(:revision)).strip } + end + current = @resource.value(:revision) if current == canonical + end + update_owner_and_excludes + return current + end + + def update_owner_and_excludes + if @resource.value(:owner) or @resource.value(:group) + set_ownership + end + if @resource.value(:excludes) + set_excludes + end + end + + def git_with_identity(*args) + if @resource.value(:identity) + Tempfile.open('git-helper') do |f| + f.puts '#!/bin/sh' + f.puts "exec ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -oConnectTimeout=120 -i #{@resource.value(:identity)} $*" + f.close + + FileUtils.chmod(0755, f.path) + env_save = ENV['GIT_SSH'] + ENV['GIT_SSH'] = f.path + + ret = git(*args) + + ENV['GIT_SSH'] = env_save + + return ret + end + elsif @resource.value(:user) + su(@resource.value(:user), '-c', "git #{args.join(' ')}" ) + else + git(*args) + end + end +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/hg.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/hg.rb new file mode 100644 index 0000000..4886b7a --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/hg.rb @@ -0,0 +1,115 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) do + desc "Supports Mercurial repositories" + + optional_commands :hg => 'hg', + :su => 'su' + has_features :reference_tracking, :ssh_identity, :user + + def create + if !@resource.value(:source) + create_repository(@resource.value(:path)) + else + clone_repository(@resource.value(:revision)) + end + update_owner + end + + def working_copy_exists? + File.directory?(File.join(@resource.value(:path), '.hg')) + end + + def exists? + working_copy_exists? + end + + def destroy + FileUtils.rm_rf(@resource.value(:path)) + end + + def latest? + at_path do + return self.revision == self.latest + end + end + + def latest + at_path do + begin + hg_wrapper('incoming', '--branch', '.', '--newest-first', '--limit', '1')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] + rescue Puppet::ExecutionFailure + # If there are no new changesets, return the current nodeid + self.revision + end + end + end + + def revision + at_path do + current = hg_wrapper('parents')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] + desired = @resource.value(:revision) + if desired + # Return the tag name if it maps to the current nodeid + mapped = hg_wrapper('tags')[/^#{Regexp.quote(desired)}\s+\d+:(\S+)/m, 1] + if current == mapped + desired + else + current + end + else + current + end + end + end + + def revision=(desired) + at_path do + begin + hg_wrapper('pull') + rescue + end + begin + hg_wrapper('merge') + rescue Puppet::ExecutionFailure + # If there's nothing to merge, just skip + end + hg_wrapper('update', '--clean', '-r', desired) + end + update_owner + end + + private + + def create_repository(path) + hg_wrapper('init', path) + end + + def clone_repository(revision) + args = ['clone'] + if revision + args.push('-u', revision) + end + args.push(@resource.value(:source), + @resource.value(:path)) + hg_wrapper(*args) + end + + def update_owner + if @resource.value(:owner) or @resource.value(:group) + set_ownership + end + end + + def hg_wrapper(*args) + if @resource.value(:identity) + args += ["--ssh", "ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -i #{@resource.value(:identity)}"] + end + if @resource.value(:user) + args.map! { |a| if a =~ /\s/ then "'#{a}'" else a end } # Adds quotes to arguments with whitespaces. + su(@resource.value(:user), '-c', "hg #{args.join(' ')}") + else + hg(*args) + end + end +end diff --git a/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/svn.rb b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/svn.rb new file mode 100644 index 0000000..e0d5b21 --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/provider/vcsrepo/svn.rb @@ -0,0 +1,124 @@ +require File.join(File.dirname(__FILE__), '..', 'vcsrepo') + +Puppet::Type.type(:vcsrepo).provide(:svn, :parent => Puppet::Provider::Vcsrepo) do + desc "Supports Subversion repositories" + + optional_commands :svn => 'svn', + :svnadmin => 'svnadmin', + :svnlook => 'svnlook' + + has_features :filesystem_types, :reference_tracking, :basic_auth, :configuration + + def create + if !@resource.value(:source) + create_repository(@resource.value(:path)) + else + checkout_repository(@resource.value(:source), + @resource.value(:path), + @resource.value(:revision)) + end + update_owner + end + + def working_copy_exists? + if File.directory?(@resource.value(:path)) + # :path is an svn checkout + return true if File.directory?(File.join(@resource.value(:path), '.svn')) + # :path is an svn server + return true if svnlook('uuid', @resource.value(:path)) + end + false + end + + def exists? + working_copy_exists? + end + + def destroy + FileUtils.rm_rf(@resource.value(:path)) + end + + def latest? + at_path do + (self.revision >= self.latest) and (@resource.value(:source) == self.sourceurl) + end + end + + def buildargs + args = ['--non-interactive'] + if @resource.value(:basic_auth_username) && @resource.value(:basic_auth_password) + args.push('--username', @resource.value(:basic_auth_username)) + args.push('--password', @resource.value(:basic_auth_password)) + args.push('--no-auth-cache') + end + + if @resource.value(:force) + args.push('--force') + end + + if @resource.value(:configuration) + args.push('--config-dir', @resource.value(:configuration)) + end + + args + end + + def latest + args = buildargs.push('info', '-r', 'HEAD') + at_path do + svn(*args)[/^Revision:\s+(\d+)/m, 1] + end + end + + def sourceurl + args = buildargs.push('info') + at_path do + svn(*args)[/^URL:\s+(\S+)/m, 1] + end + end + + def revision + args = buildargs.push('info') + at_path do + svn(*args)[/^Revision:\s+(\d+)/m, 1] + end + end + + def revision=(desired) + args = if @resource.value(:source) + buildargs.push('switch', '-r', desired, @resource.value(:source)) + else + buildargs.push('update', '-r', desired) + end + at_path do + svn(*args) + end + update_owner + end + + private + + def checkout_repository(source, path, revision) + args = buildargs.push('checkout') + if revision + args.push('-r', revision) + end + args.push(source, path) + svn(*args) + end + + def create_repository(path) + args = ['create'] + if @resource.value(:fstype) + args.push('--fs-type', @resource.value(:fstype)) + end + args << path + svnadmin(*args) + end + + def update_owner + if @resource.value(:owner) or @resource.value(:group) + set_ownership + end + end +end diff --git a/puppet/modules/vcsrepo/lib/puppet/type/vcsrepo.rb b/puppet/modules/vcsrepo/lib/puppet/type/vcsrepo.rb new file mode 100644 index 0000000..ad90ced --- /dev/null +++ b/puppet/modules/vcsrepo/lib/puppet/type/vcsrepo.rb @@ -0,0 +1,198 @@ +require 'pathname' + +Puppet::Type.newtype(:vcsrepo) do + desc "A local version control repository" + + feature :gzip_compression, + "The provider supports explicit GZip compression levels" + feature :basic_auth, + "The provider supports HTTP Basic Authentication" + feature :bare_repositories, + "The provider differentiates between bare repositories + and those with working copies", + :methods => [:bare_exists?, :working_copy_exists?] + + feature :filesystem_types, + "The provider supports different filesystem types" + + feature :reference_tracking, + "The provider supports tracking revision references that can change + over time (eg, some VCS tags and branch names)" + + feature :ssh_identity, + "The provider supports a configurable SSH identity file" + + feature :user, + "The provider can run as a different user" + + feature :modules, + "The repository contains modules that can be chosen of" + + feature :multiple_remotes, + "The repository tracks multiple remote repositories" + + feature :configuration, + "The configuration directory to use" + + feature :cvs_rsh, + "The provider understands the CVS_RSH environment variable" + + ensurable do + attr_accessor :latest + + def insync?(is) + @should ||= [] + + case should + when :present + return true unless [:absent, :purged, :held].include?(is) + when :latest + if is == :latest + return true + else + return false + end + when :bare + return is == :bare + end + end + + newvalue :present do + notice "Creating repository from present" + provider.create + end + + newvalue :bare, :required_features => [:bare_repositories] do + if !provider.exists? + provider.create + end + end + + newvalue :absent do + provider.destroy + end + + newvalue :latest, :required_features => [:reference_tracking] do + if provider.exists? + if provider.respond_to?(:update_references) + provider.update_references + end + if provider.respond_to?(:latest?) + reference = provider.latest || provider.revision + else + reference = resource.value(:revision) || provider.revision + end + notice "Updating to latest '#{reference}' revision" + provider.revision = reference + else + notice "Creating repository from latest" + provider.create + end + end + + def retrieve + prov = @resource.provider + if prov + if prov.working_copy_exists? + (@should.include?(:latest) && prov.latest?) ? :latest : :present + elsif prov.class.feature?(:bare_repositories) and prov.bare_exists? + :bare + else + :absent + end + else + raise Puppet::Error, "Could not find provider" + end + end + + end + + newparam :path do + desc "Absolute path to repository" + isnamevar + validate do |value| + path = Pathname.new(value) + unless path.absolute? + raise ArgumentError, "Path must be absolute: #{path}" + end + end + end + + newparam :source do + desc "The source URI for the repository" + end + + newparam :fstype, :required_features => [:filesystem_types] do + desc "Filesystem type" + end + + newproperty :revision do + desc "The revision of the repository" + newvalue(/^\S+$/) + end + + newparam :owner do + desc "The user/uid that owns the repository files" + end + + newparam :group do + desc "The group/gid that owns the repository files" + end + + newparam :user do + desc "The user to run for repository operations" + end + + newparam :excludes do + desc "Files to be excluded from the repository" + end + + newparam :force do + desc "Force repository creation, destroying any files on the path in the process." + newvalues(:true, :false) + defaultto false + end + + newparam :compression, :required_features => [:gzip_compression] do + desc "Compression level" + validate do |amount| + unless Integer(amount).between?(0, 6) + raise ArgumentError, "Unsupported compression level: #{amount} (expected 0-6)" + end + end + end + + newparam :basic_auth_username, :required_features => [:basic_auth] do + desc "HTTP Basic Auth username" + end + + newparam :basic_auth_password, :required_features => [:basic_auth] do + desc "HTTP Basic Auth password" + end + + newparam :identity, :required_features => [:ssh_identity] do + desc "SSH identity file" + end + + newparam :module, :required_features => [:modules] do + desc "The repository module to manage" + end + + newparam :remote, :required_features => [:multiple_remotes] do + desc "The remote repository to track" + defaultto "origin" + end + + newparam :configuration, :required_features => [:configuration] do + desc "The configuration directory to use" + end + + newparam :cvs_rsh, :required_features => [:cvs_rsh] do + desc "The value to be used for the CVS_RSH environment variable." + end + + autorequire(:package) do + ['git', 'git-core'] + end + +end diff --git a/puppet/modules/vcsrepo/metadata.json b/puppet/modules/vcsrepo/metadata.json new file mode 100644 index 0000000..8096daf --- /dev/null +++ b/puppet/modules/vcsrepo/metadata.json @@ -0,0 +1,171 @@ +{ + "name": "puppetlabs/vcsrepo", + "version": "0.2.0", + "source": "UNKNOWN", + "author": "puppetlabs", + "license": "Apache License, Version 2.0", + "summary": "Manage repositories from various version control systems", + "description": "Manage repositories from various version control systems", + "project_page": "UNKNOWN", + "dependencies": [ + + ], + "types": [ + { + "name": "vcsrepo", + "doc": "A local version control repository", + "properties": [ + { + "name": "ensure", + "doc": " Valid values are `present`, `bare`, `absent`, `latest`." + }, + { + "name": "revision", + "doc": "The revision of the repository Values can match `/^\\S+$/`." + } + ], + "parameters": [ + { + "name": "path", + "doc": "Absolute path to repository" + }, + { + "name": "source", + "doc": "The source URI for the repository" + }, + { + "name": "fstype", + "doc": "Filesystem type Requires features filesystem_types." + }, + { + "name": "owner", + "doc": "The user/uid that owns the repository files" + }, + { + "name": "group", + "doc": "The group/gid that owns the repository files" + }, + { + "name": "user", + "doc": "The user to run for repository operations" + }, + { + "name": "excludes", + "doc": "Files to be excluded from the repository" + }, + { + "name": "force", + "doc": "Force repository creation, destroying any files on the path in the process. Valid values are `true`, `false`." + }, + { + "name": "compression", + "doc": "Compression level Requires features gzip_compression." + }, + { + "name": "basic_auth_username", + "doc": "HTTP Basic Auth username Requires features basic_auth." + }, + { + "name": "basic_auth_password", + "doc": "HTTP Basic Auth password Requires features basic_auth." + }, + { + "name": "identity", + "doc": "SSH identity file Requires features ssh_identity." + }, + { + "name": "module", + "doc": "The repository module to manage Requires features modules." + }, + { + "name": "remote", + "doc": "The remote repository to track Requires features multiple_remotes." + }, + { + "name": "configuration", + "doc": "The configuration directory to use Requires features configuration." + }, + { + "name": "cvs_rsh", + "doc": "The value to be used for the CVS_RSH environment variable. Requires features cvs_rsh." + } + ], + "providers": [ + { + "name": "bzr", + "doc": "Supports Bazaar repositories\n\nRequired binaries: `bzr`. Supported features: `reference_tracking`." + }, + { + "name": "cvs", + "doc": "Supports CVS repositories/workspaces\n\nRequired binaries: `cvs`. Supported features: `cvs_rsh`, `gzip_compression`, `modules`, `reference_tracking`." + }, + { + "name": "dummy", + "doc": "Dummy default provider\n\nDefault for `vcsrepo` == `dummy`." + }, + { + "name": "git", + "doc": "Supports Git repositories\n\nRequired binaries: `git`, `su`. Supported features: `bare_repositories`, `multiple_remotes`, `reference_tracking`, `ssh_identity`, `user`." + }, + { + "name": "hg", + "doc": "Supports Mercurial repositories\n\nRequired binaries: `hg`, `su`. Supported features: `reference_tracking`, `ssh_identity`, `user`." + }, + { + "name": "svn", + "doc": "Supports Subversion repositories\n\nRequired binaries: `svn`, `svnadmin`, `svnlook`. Supported features: `basic_auth`, `configuration`, `filesystem_types`, `reference_tracking`." + } + ] + } + ], + "checksums": { + "CHANGELOG": "c41bec2dddc2a3de4c10b56e4d348492", + "Gemfile": "a25ee68d266f452c3cf19537736e778d", + "Gemfile.lock": "aac999a29c92e12f0af120c97bda73f7", + "LICENSE": "b8d96fef1f55096f9d39326408122136", + "Modulefile": "e69308b7a49f10b2695057c33eeba5f6", + "README.BZR.markdown": "97f638d169a1c39d461c3f2c0e2ec32f", + "README.CVS.markdown": "7bc2fd4def5d18451dc8d5fc86d2910c", + "README.GIT.markdown": "9adc244b55c7441076541dfc7fdd1a68", + "README.HG.markdown": "438ebb7b5262edea0a5b69856dfc9415", + "README.SVN.markdown": "4f8de2b336022700aa557a59c7770e57", + "README.markdown": "aa36edae60f06e5cb0fef00c3d5b6618", + "Rakefile": "0254db5d3fc38c67a2c160d7296a24f8", + "examples/bzr/branch.pp": "05c66419324a576b9b28df876673580d", + "examples/bzr/init_repo.pp": "fadd2321866ffb0aacff698d2dc1f0ca", + "examples/cvs/local.pp": "7fbde03a5c71edf168267ae42d0bbcbc", + "examples/cvs/remote.pp": "491f18f752752bec6133a88de242c44d", + "examples/git/bare_init.pp": "7cf56abffdf99f379153166f18f961f8", + "examples/git/clone.pp": "0e3181990c095efee1498ccfca5897fb", + "examples/git/working_copy_init.pp": "99d92d9957e78a0c03f9cbed989c79ca", + "examples/hg/clone.pp": "c92bbd704a4c2da55fff5f45955ce6d1", + "examples/hg/init_repo.pp": "bf5fa0ab48a2f5a1ccb63768d961413d", + "examples/svn/checkout.pp": "9ef7a8fbd3a763fa3894efa864047023", + "examples/svn/server.pp": "94b26f6e50d5e411b33b1ded1bc2138a", + "lib/puppet/provider/vcsrepo/bzr.rb": "52f4d40153e0a3bc54be1b7dfa18b5f1", + "lib/puppet/provider/vcsrepo/cvs.rb": "1ce8d98a2ffad4bf0c575af014270c8b", + "lib/puppet/provider/vcsrepo/dummy.rb": "2f8159468d6ecc8087debde858a80dd6", + "lib/puppet/provider/vcsrepo/git.rb": "7c453bfe9abe5367902f090b554c51e2", + "lib/puppet/provider/vcsrepo/hg.rb": "01887f986db627ffc1a8ff7a52328ddb", + "lib/puppet/provider/vcsrepo/svn.rb": "03b14667e002db9452c597e1b21718dd", + "lib/puppet/provider/vcsrepo.rb": "dbd72590771291f1db23a41ac048ed9d", + "lib/puppet/type/vcsrepo.rb": "bf01ae48b0d2ae542bc8c0f65da93c64", + "spec/fixtures/bzr_version_info.txt": "5edb13429faf2f0b9964b4326ef49a65", + "spec/fixtures/git_branch_a.txt": "2371229e7c1706c5ab8f90f0cd57230f", + "spec/fixtures/git_branch_feature_bar.txt": "70903a4dc56f7300fbaa54c295b52c4f", + "spec/fixtures/git_branch_none.txt": "acaa61de6a7f0f5ca39b763799dcb9a6", + "spec/fixtures/hg_parents.txt": "efc28a1bd3f1ce7fb4481f76feed3f6e", + "spec/fixtures/hg_tags.txt": "8383048b15adb3d58a92ea0c8b887537", + "spec/fixtures/svn_info.txt": "978db25720a098e5de48388fe600c062", + "spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c", + "spec/spec_helper.rb": "ce4d39194e1b8486de8ec25f639f6762", + "spec/support/filesystem_helpers.rb": "eb2a8eb3769865004c84e971ccb1396c", + "spec/support/fixture_helpers.rb": "61781d99ea201e9da6d23c64a25cc285", + "spec/unit/puppet/provider/vcsrepo/bzr_spec.rb": "320b5be01c84f3424ac99729e42b4562", + "spec/unit/puppet/provider/vcsrepo/cvs_spec.rb": "24f760cb53be365ca185cd196c03743a", + "spec/unit/puppet/provider/vcsrepo/git_spec.rb": "2159d3a06a2a764dcf8e3da141390734", + "spec/unit/puppet/provider/vcsrepo/hg_spec.rb": "b6eabd1167753f1a6a87eeef897bc1c5", + "spec/unit/puppet/provider/vcsrepo/svn_spec.rb": "957328714f6df1e90b663514615f460e", + "spec/unit/puppet/type/README.markdown": "de26a7643813abd6c2e7e28071b1ef94" + } +} \ No newline at end of file diff --git a/puppet/modules/vcsrepo/spec/fixtures/bzr_version_info.txt b/puppet/modules/vcsrepo/spec/fixtures/bzr_version_info.txt new file mode 100644 index 0000000..88a56a1 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/bzr_version_info.txt @@ -0,0 +1,5 @@ +revision-id: menesis@pov.lt-20100309191856-4wmfqzc803fj300x +date: 2010-03-09 21:18:56 +0200 +build-date: 2010-03-14 00:42:43 -0800 +revno: 2634 +branch-nick: mytest diff --git a/puppet/modules/vcsrepo/spec/fixtures/git_branch_a.txt b/puppet/modules/vcsrepo/spec/fixtures/git_branch_a.txt new file mode 100644 index 0000000..2c99829 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/git_branch_a.txt @@ -0,0 +1,14 @@ + feature/foo + feature/bar + feature/baz + feature/quux + only/local +* master + refactor/foo + origin/HEAD + origin/feature/foo + origin/feature/bar + origin/feature/baz + origin/feature/quux + origin/only/remote + origin/master diff --git a/puppet/modules/vcsrepo/spec/fixtures/git_branch_feature_bar.txt b/puppet/modules/vcsrepo/spec/fixtures/git_branch_feature_bar.txt new file mode 100644 index 0000000..72d5e20 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/git_branch_feature_bar.txt @@ -0,0 +1,14 @@ + feature/foo +* feature/bar + feature/baz + feature/quux + only/local + master + refactor/foo + origin/HEAD + origin/feature/foo + origin/feature/bar + origin/feature/baz + origin/feature/quux + origin/only/remote + origin/master diff --git a/puppet/modules/vcsrepo/spec/fixtures/git_branch_none.txt b/puppet/modules/vcsrepo/spec/fixtures/git_branch_none.txt new file mode 100644 index 0000000..7207c37 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/git_branch_none.txt @@ -0,0 +1,15 @@ + feature/foo + feature/bar + feature/baz + feature/quux + only/local + master +* (no branch) + refactor/foo + origin/HEAD + origin/feature/foo + origin/feature/bar + origin/feature/baz + origin/feature/quux + origin/only/remote + origin/master diff --git a/puppet/modules/vcsrepo/spec/fixtures/hg_parents.txt b/puppet/modules/vcsrepo/spec/fixtures/hg_parents.txt new file mode 100644 index 0000000..46173df --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/hg_parents.txt @@ -0,0 +1,6 @@ +changeset: 3:34e6012c783a +parent: 2:21ea4598c962 +parent: 1:9d0ff0028458 +user: Test User +date: Fri Aug 07 13:13:02 2009 -0400 +summary: merge diff --git a/puppet/modules/vcsrepo/spec/fixtures/hg_tags.txt b/puppet/modules/vcsrepo/spec/fixtures/hg_tags.txt new file mode 100644 index 0000000..53792e5 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/hg_tags.txt @@ -0,0 +1,18 @@ +tip 1019:bca3f20b249b +0.9.1 1017:76ce7cca95d8 +0.9 1001:dbaa6f4ec585 +0.8 839:65b66ac0fc83 +0.7.1 702:e1357f00129f +0.7 561:7b2af3b4c968 +0.6.3 486:e38077f4e4aa +0.6.2 405:07bb099b7b10 +0.6.1 389:93750f3fbbe2 +0.6 369:34e6012c783a +0.5.3 321:5ffa6ae7e699 +0.5.2 318:fdc2c2e4cebe +0.5.1 315:33a5ea0cbe7a +0.5 313:47490716f4c9 +0.4 240:47fa3a14cc63 +0.3.1 132:bc231db18e1c +0.3 130:661615e510dd +0.2 81:f98d13b442f6 diff --git a/puppet/modules/vcsrepo/spec/fixtures/svn_info.txt b/puppet/modules/vcsrepo/spec/fixtures/svn_info.txt new file mode 100644 index 0000000..d2a975b --- /dev/null +++ b/puppet/modules/vcsrepo/spec/fixtures/svn_info.txt @@ -0,0 +1,10 @@ +Path: . +URL: http://example.com/svn/trunk +Repository Root: http://example.com/svn +Repository UUID: 75246ace-e253-0410-96dd-a7613ca8dc81 +Revision: 4 +Node Kind: directory +Schedule: normal +Last Changed Author: jon +Last Changed Rev: 3 +Last Changed Date: 2008-08-07 11:34:25 -0700 (Thu, 07 Aug 2008) diff --git a/puppet/modules/vcsrepo/spec/spec.opts b/puppet/modules/vcsrepo/spec/spec.opts new file mode 100644 index 0000000..91cd642 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/puppet/modules/vcsrepo/spec/spec_helper.rb b/puppet/modules/vcsrepo/spec/spec_helper.rb new file mode 100644 index 0000000..acfae0c --- /dev/null +++ b/puppet/modules/vcsrepo/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'puppetlabs_spec_helper/module_spec_helper' +require 'simplecov' +require 'support/filesystem_helpers' +require 'support/fixture_helpers' + +SimpleCov.start do + add_filter "/spec/" +end + +RSpec.configure do |c| + c.include FilesystemHelpers + c.include FixtureHelpers +end diff --git a/puppet/modules/vcsrepo/spec/support/filesystem_helpers.rb b/puppet/modules/vcsrepo/spec/support/filesystem_helpers.rb new file mode 100644 index 0000000..15e2ca7 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/support/filesystem_helpers.rb @@ -0,0 +1,18 @@ +module FilesystemHelpers + + def expects_chdir(path = resource.value(:path)) + Dir.expects(:chdir).with(path).at_least_once.yields + end + + def expects_mkdir(path = resource.value(:path)) + Dir.expects(:mkdir).with(path).at_least_once + end + + def expects_rm_rf(path = resource.value(:path)) + FileUtils.expects(:rm_rf).with(path) + end + + def expects_directory?(returns = true, path = resource.value(:path)) + File.expects(:directory?).with(path).returns(returns) + end +end diff --git a/puppet/modules/vcsrepo/spec/support/fixture_helpers.rb b/puppet/modules/vcsrepo/spec/support/fixture_helpers.rb new file mode 100644 index 0000000..8a0e0a0 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/support/fixture_helpers.rb @@ -0,0 +1,7 @@ +module FixtureHelpers + + def fixture(name, ext = '.txt') + File.read(File.join(File.dirname(__FILE__), '..', 'fixtures', name.to_s + ext)) + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb new file mode 100644 index 0000000..488ddc0 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vcsrepo).provider(:bzr_provider) do + + let(:resource) { Puppet::Type.type(:vcsrepo).new({ + :name => 'test', + :ensure => :present, + :provider => :bzr, + :revision => '2634', + :source => 'lp:do', + :path => '/tmp/test', + })} + + let(:provider) { resource.provider } + + before :each do + Puppet::Util.stubs(:which).with('bzr').returns('/usr/bin/bzr') + end + + describe 'creating' do + context 'with defaults' do + it "should execute 'bzr clone -r' with the revision" do + provider.expects(:bzr).with('branch', '-r', resource.value(:revision), resource.value(:source), resource.value(:path)) + provider.create + end + end + + context 'without revision' do + it "should just execute 'bzr clone' without a revision" do + resource.delete(:revision) + provider.expects(:bzr).with('branch', resource.value(:source), resource.value(:path)) + provider.create + end + end + + context 'without source' do + it "should execute 'bzr init'" do + resource.delete(:source) + provider.expects(:bzr).with('init', resource.value(:path)) + provider.create + end + end + end + + describe 'destroying' do + it "it should remove the directory" do + provider.destroy + end + end + + describe "checking existence" do + it "should check for the directory" do + File.expects(:directory?).with(File.join(resource.value(:path), '.bzr')).returns(true) + provider.exists? + end + end + + describe "checking the revision property" do + before do + expects_chdir + provider.expects(:bzr).with('version-info').returns(File.read(fixtures('bzr_version_info.txt'))) + @current_revid = 'menesis@pov.lt-20100309191856-4wmfqzc803fj300x' + end + + context "when given a non-revid as the resource revision" do + context "when its revid is not different than the current revid" do + it "should return the ref" do + resource[:revision] = '2634' + provider.expects(:bzr).with('revision-info', '2634').returns("2634 menesis@pov.lt-20100309191856-4wmfqzc803fj300x\n") + provider.revision.should == resource.value(:revision) + end + end + context "when its revid is different than the current revid" do + it "should return the current revid" do + resource[:revision] = '2636' + provider.expects(:bzr).with('revision-info', resource.value(:revision)).returns("2635 foo\n") + provider.revision.should == @current_revid + end + end + end + + context "when given a revid as the resource revision" do + context "when it is the same as the current revid" do + it "should return it" do + resource[:revision] = 'menesis@pov.lt-20100309191856-4wmfqzc803fj300x' + provider.expects(:bzr).with('revision-info', resource.value(:revision)).returns("1234 #{resource.value(:revision)}\n") + provider.revision.should == resource.value(:revision) + end + end + context "when it is not the same as the current revid" do + it "should return the current revid" do + resource[:revision] = 'menesis@pov.lt-20100309191856-4wmfqzc803fj300y' + provider.expects(:bzr).with('revision-info', resource.value(:revision)).returns("2636 foo\n") + provider.revision.should == @current_revid + end + end + + end + end + + describe "setting the revision property" do + it "should use 'bzr update -r' with the revision" do + Dir.expects(:chdir).with('/tmp/test').at_least_once.yields + provider.expects(:bzr).with('update', '-r', 'somerev') + provider.revision = 'somerev' + end + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb new file mode 100644 index 0000000..efa4b33 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vcsrepo).provider(:cvs_provider) do + + let(:resource) { Puppet::Type.type(:vcsrepo).new({ + :name => 'test', + :ensure => :present, + :provider => :cvs, + :revision => '2634', + :source => 'lp:do', + :path => '/tmp/test', + })} + + let(:provider) { resource.provider } + + before :each do + Puppet::Util.stubs(:which).with('cvs').returns('/usr/bin/cvs') + end + + describe 'creating' do + context "with a source" do + it "should execute 'cvs checkout'" do + resource[:source] = ':ext:source@example.com:/foo/bar' + resource[:revision] = 'an-unimportant-value' + expects_chdir('/tmp') + provider.expects(:cvs).with('-d', resource.value(:source), 'checkout', '-r', 'an-unimportant-value', '-d', 'test', 'bar') + provider.create + end + + it "should just execute 'cvs checkout' without a revision" do + resource[:source] = ':ext:source@example.com:/foo/bar' + resource.delete(:revision) + provider.expects(:cvs).with('-d', resource.value(:source), 'checkout', '-d', File.basename(resource.value(:path)), File.basename(resource.value(:source))) + provider.create + end + + context "with a compression" do + it "should just execute 'cvs checkout' without a revision" do + resource[:source] = ':ext:source@example.com:/foo/bar' + resource[:compression] = '3' + resource.delete(:revision) + provider.expects(:cvs).with('-d', resource.value(:source), '-z', '3', 'checkout', '-d', File.basename(resource.value(:path)), File.basename(resource.value(:source))) + provider.create + end + end + end + + context "when a source is not given" do + it "should execute 'cvs init'" do + resource.delete(:source) + provider.expects(:cvs).with('-d', resource.value(:path), 'init') + provider.create + end + end + end + + describe 'destroying' do + it "it should remove the directory" do + provider.destroy + end + end + + describe "checking existence" do + it "should check for the CVS directory with source" do + resource[:source] = ':ext:source@example.com:/foo/bar' + File.expects(:directory?).with(File.join(resource.value(:path), 'CVS')) + provider.exists? + end + + it "should check for the CVSROOT directory without source" do + resource.delete(:source) + File.expects(:directory?).with(File.join(resource.value(:path), 'CVSROOT')) + provider.exists? + end + end + + describe "checking the revision property" do + before do + @tag_file = File.join(resource.value(:path), 'CVS', 'Tag') + end + + context "when CVS/Tag exists" do + before do + @tag = 'TAG' + File.expects(:exist?).with(@tag_file).returns(true) + end + it "should read CVS/Tag" do + File.expects(:read).with(@tag_file).returns("T#{@tag}") + provider.revision.should == @tag + end + end + + context "when CVS/Tag does not exist" do + before do + File.expects(:exist?).with(@tag_file).returns(false) + end + it "assumes HEAD" do + provider.revision.should == 'HEAD' + end + end + end + + describe "when setting the revision property" do + before do + @tag = 'SOMETAG' + end + + it "should use 'cvs update -dr'" do + expects_chdir + provider.expects(:cvs).with('update', '-dr', @tag, '.') + provider.revision = @tag + end + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/git_spec.rb b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/git_spec.rb new file mode 100644 index 0000000..15fee53 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/git_spec.rb @@ -0,0 +1,369 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vcsrepo).provider(:git_provider) do + + let(:resource) { Puppet::Type.type(:vcsrepo).new({ + :name => 'test', + :ensure => :present, + :provider => :git, + :revision => '2634', + :source => 'git@repo', + :path => '/tmp/test', + })} + + let(:provider) { resource.provider } + + before :each do + Puppet::Util.stubs(:which).with('git').returns('/usr/bin/git') + end + + context 'creating' do + context "with a revision that is a remote branch" do + it "should execute 'git clone' and 'git checkout -b'" do + resource[:revision] = 'only/remote' + Dir.expects(:chdir).with('/').at_least_once.yields + Dir.expects(:chdir).with('/tmp/test').at_least_once.yields + provider.expects(:git).with('clone', resource.value(:source), resource.value(:path)) + provider.expects(:update_submodules) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('checkout', '--force', resource.value(:revision)) + provider.create + end + end + + context "with a revision that is not a remote branch" do + it "should execute 'git clone' and 'git reset --hard'" do + resource[:revision] = 'a-commit-or-tag' + Dir.expects(:chdir).with('/').at_least_once.yields + Dir.expects(:chdir).with('/tmp/test').at_least_once.yields + provider.expects(:git).with('clone', resource.value(:source), resource.value(:path)) + provider.expects(:update_submodules) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('checkout', '--force', resource.value(:revision)) + provider.create + end + + it "should execute 'git clone' and submodule commands" do + resource.delete(:revision) + provider.expects(:git).with('clone', resource.value(:source), resource.value(:path)) + provider.expects(:update_submodules) + provider.create + end + end + + context "with an ensure of bare" do + context "with revision" do + it "should just execute 'git clone --bare'" do + resource[:ensure] = :bare + provider.expects(:git).with('clone', '--bare', resource.value(:source), resource.value(:path)) + provider.create + end + end + context "without revision" do + it "should just execute 'git clone --bare'" do + resource[:ensure] = :bare + resource.delete(:revision) + provider.expects(:git).with('clone', '--bare', resource.value(:source), resource.value(:path)) + provider.create + end + end + end + + context "when a source is not given" do + context "when the path does not exist" do + it "should execute 'git init'" do + resource[:ensure] = :present + resource.delete(:source) + expects_mkdir + expects_chdir + expects_directory?(false) + + provider.expects(:bare_exists?).returns(false) + provider.expects(:git).with('init') + provider.create + end + end + + context "when the path is a bare repository" do + it "should convert it to a working copy" do + resource[:ensure] = :present + resource.delete(:source) + provider.expects(:bare_exists?).returns(true) + provider.expects(:convert_bare_to_working_copy) + provider.create + end + end + + context "when the path is not empty and not a repository" do + it "should raise an exception" do + provider.expects(:path_exists?).returns(true) + provider.expects(:path_empty?).returns(false) + proc { provider.create }.should raise_error(Puppet::Error) + end + end + end + + context "when the path does not exist" do + it "should execute 'git init --bare'" do + resource[:ensure] = :bare + resource.delete(:source) + expects_chdir + expects_mkdir + expects_directory?(false) + provider.expects(:working_copy_exists?).returns(false) + provider.expects(:git).with('init', '--bare') + provider.create + end + end + + context "when the path is a working copy repository" do + it "should convert it to a bare repository" do + resource[:ensure] = :bare + resource.delete(:source) + provider.expects(:working_copy_exists?).returns(true) + provider.expects(:convert_working_copy_to_bare) + provider.create + end + end + + context "when the path is not empty and not a repository" do + it "should raise an exception" do + expects_directory?(true) + provider.expects(:path_empty?).returns(false) + proc { provider.create }.should raise_error(Puppet::Error) + end + end + end + + + context 'destroying' do + it "it should remove the directory" do + #expects_rm_rf + provider.destroy + end + end + + context "checking the revision property" do + before do + expects_chdir('/tmp/test') + resource[:revision] = 'currentsha' + resource.delete(:source) + provider.expects(:git).with('rev-parse', 'HEAD').returns('currentsha') + end + + context "when its SHA is not different than the current SHA" do + it "should return the ref" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.revision.should == resource.value(:revision) + end + end + + context "when its SHA is different than the current SHA" do + it "should return the current SHA" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('othersha') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.revision.should == 'currentsha' + end + end + + context "when its a ref to a remote head" do + it "should return the revision" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('') + provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns("newsha refs/heads/#{resource.value(:revision)}") + provider.revision.should == 'currentsha' + end + end + + context "when its a ref to non existant remote head" do + it "should fail" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('') + provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns('') + expect { provider.revision }.to raise_error(Puppet::Error, /not a local or remote ref$/) + end + end + + context "when the source is modified" do + it "should update the origin url" do + resource[:source] = 'git://git@foo.com/bar.git' + provider.expects(:git).with('config', 'remote.origin.url').returns('old') + provider.expects(:git).with('config', 'remote.origin.url', 'git://git@foo.com/bar.git') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.revision.should == resource.value(:revision) + end + end + end + + context "setting the revision property" do + before do + expects_chdir + end + context "when it's an existing local branch" do + it "should use 'git fetch' and 'git reset'" do + resource[:revision] = 'feature/foo' + provider.expects(:update_submodules) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('checkout', '--force', resource.value(:revision)) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('reset', '--hard', "origin/#{resource.value(:revision)}") + provider.revision = resource.value(:revision) + end + end + context "when it's a remote branch" do + it "should use 'git fetch' and 'git reset'" do + resource[:revision] = 'only/remote' + provider.expects(:update_submodules) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('checkout', '--force', resource.value(:revision)) + provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) + provider.expects(:git).with('reset', '--hard', "origin/#{resource.value(:revision)}") + provider.revision = resource.value(:revision) + end + end + context "when it's a commit or tag" do + it "should use 'git fetch' and 'git reset'" do + resource[:revision] = 'a-commit-or-tag' + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a)) + provider.expects(:git).with('checkout', '--force', resource.value(:revision)) + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a)) + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a)) + provider.expects(:git).with('submodule', 'update', '--init', '--recursive') + provider.revision = resource.value(:revision) + end + end + end + + context "updating references" do + it "should use 'git fetch --tags'" do + resource.delete(:source) + expects_chdir + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.update_references + end + end + + context "checking if revision" do + before do + expects_chdir + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a)) + end + context "is a local branch" do + context "when it's listed in 'git branch -a'" do + it "should return true" do + resource[:revision] = 'feature/foo' + provider.should be_local_branch_revision + end + end + context "when it's not listed in 'git branch -a'" do + it "should return false" do + resource[:revision] = 'feature/notexist' + provider.should_not be_local_branch_revision + end + end + end + context "is a remote branch" do + context "when it's listed in 'git branch -a' with an 'origin/' prefix" do + it "should return true" do + resource[:revision] = 'only/remote' + provider.should be_remote_branch_revision + end + end + context "when it's not listed in 'git branch -a' with an 'origin/' prefix" do + it "should return false" do + resource[:revision] = 'only/local' + provider.should_not be_remote_branch_revision + end + end + end + end + + describe 'latest?' do + before do + expects_chdir('/tmp/test') + end + context 'when true' do + it do + provider.expects(:revision).returns('testrev') + provider.expects(:latest).returns('testrev') + provider.latest?.should be_true + end + end + context 'when false' do + it do + provider.expects(:revision).returns('master') + provider.expects(:latest).returns('testrev') + provider.latest?.should be_false + end + end + end + + describe 'latest' do + before do + provider.expects(:get_revision).returns('master') + expects_chdir + end + context 'on master' do + it do + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a)) + provider.latest.should == 'master' + end + end + context 'no branch' do + it do + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_none)) + provider.latest.should == 'master' + end + end + context 'feature/bar' do + it do + provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_feature_bar)) + provider.latest.should == 'master' + end + end + end + + describe 'convert_working_copy_to_bare' do + it do + FileUtils.expects(:mv).returns(true) + FileUtils.expects(:rm_rf).returns(true) + FileUtils.expects(:mv).returns(true) + + provider.instance_eval { convert_working_copy_to_bare } + end + end + + describe 'convert_bare_to_working_copy' do + it do + FileUtils.expects(:mv).returns(true) + FileUtils.expects(:mkdir).returns(true) + FileUtils.expects(:mv).returns(true) + provider.expects(:commits_in?).returns(true) + # If you forget to stub these out you lose 3 hours of rspec work. + provider.expects(:reset).with('HEAD').returns(true) + provider.expects(:git_with_identity).returns(true) + provider.expects(:update_owner_and_excludes).returns(true) + + provider.instance_eval { convert_bare_to_working_copy } + end + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/hg_spec.rb b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/hg_spec.rb new file mode 100644 index 0000000..7fd5348 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/hg_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vcsrepo).provider(:hg) do + + let(:resource) { Puppet::Type.type(:vcsrepo).new({ + :name => 'test', + :ensure => :present, + :provider => :hg, + :path => '/tmp/vcsrepo', + })} + + let(:provider) { resource.provider } + + before :each do + Puppet::Util.stubs(:which).with('hg').returns('/usr/bin/hg') + end + + describe 'creating' do + context 'with source and revision' do + it "should execute 'hg clone -u' with the revision" do + resource[:source] = 'something' + resource[:revision] = '1' + provider.expects(:hg).with('clone', '-u', + resource.value(:revision), + resource.value(:source), + resource.value(:path)) + provider.create + end + end + + context 'without revision' do + it "should just execute 'hg clone' without a revision" do + resource[:source] = 'something' + provider.expects(:hg).with('clone', resource.value(:source), resource.value(:path)) + provider.create + end + end + + context "when a source is not given" do + it "should execute 'hg init'" do + provider.expects(:hg).with('init', resource.value(:path)) + provider.create + end + end + end + + describe 'destroying' do + it "it should remove the directory" do + expects_rm_rf + provider.destroy + end + end + + describe "checking existence" do + it "should check for the directory" do + expects_directory?(true, File.join(resource.value(:path), '.hg')) + provider.exists? + end + end + + describe "checking the revision property" do + before do + expects_chdir + end + + context "when given a non-SHA as the resource revision" do + before do + provider.expects(:hg).with('parents').returns(fixture(:hg_parents)) + provider.expects(:hg).with('tags').returns(fixture(:hg_tags)) + end + + context "when its SHA is not different than the current SHA" do + it "should return the ref" do + resource[:revision] = '0.6' + provider.revision.should == '0.6' + end + end + + context "when its SHA is different than the current SHA" do + it "should return the current SHA" do + resource[:revision] = '0.5.3' + provider.revision.should == '34e6012c783a' + end + end + end + context "when given a SHA as the resource revision" do + before do + provider.expects(:hg).with('parents').returns(fixture(:hg_parents)) + end + + context "when it is the same as the current SHA", :resource => {:revision => '34e6012c783a'} do + it "should return it" do + resource[:revision] = '34e6012c783a' + provider.expects(:hg).with('tags').returns(fixture(:hg_tags)) + provider.revision.should == resource.value(:revision) + end + end + + context "when it is not the same as the current SHA", :resource => {:revision => 'not-the-same'} do + it "should return the current SHA" do + resource[:revision] = 'not-the-same' + provider.expects(:hg).with('tags').returns(fixture(:hg_tags)) + provider.revision.should == '34e6012c783a' + end + end + end + end + + describe "setting the revision property" do + before do + @revision = '6aa99e9b3ab1' + end + it "should use 'hg update ---clean -r'" do + expects_chdir + provider.expects(:hg).with('pull') + provider.expects(:hg).with('merge') + provider.expects(:hg).with('update', '--clean', '-r', @revision) + provider.revision = @revision + end + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/svn_spec.rb b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/svn_spec.rb new file mode 100644 index 0000000..f44e314 --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/provider/vcsrepo/svn_spec.rb @@ -0,0 +1,105 @@ +require 'spec_helper' + +describe Puppet::Type.type(:vcsrepo).provider(:svn) do + + let(:resource) { Puppet::Type.type(:vcsrepo).new({ + :name => 'test', + :ensure => :present, + :provider => :svn, + :path => '/tmp/vcsrepo', + })} + + let(:provider) { resource.provider } + + before :each do + Puppet::Util.stubs(:which).with('git').returns('/usr/bin/git') + end + + describe 'creating' do + context 'with source and revision' do + it "should execute 'svn checkout' with a revision" do + resource[:source] = 'exists' + resource[:revision] = '1' + provider.expects(:svn).with('--non-interactive', 'checkout', '-r', + resource.value(:revision), + resource.value(:source), + resource.value(:path)) + provider.create + end + end + context 'with source' do + it "should just execute 'svn checkout' without a revision" do + resource[:source] = 'exists' + provider.expects(:svn).with('--non-interactive', 'checkout', + resource.value(:source), + resource.value(:path)) + provider.create + end + end + + context 'with fstype' do + it "should execute 'svnadmin create' with an '--fs-type' option" do + resource[:fstype] = 'ext4' + provider.expects(:svnadmin).with('create', '--fs-type', + resource.value(:fstype), + resource.value(:path)) + provider.create + end + end + context 'without fstype' do + it "should execute 'svnadmin create' without an '--fs-type' option" do + provider.expects(:svnadmin).with('create', resource.value(:path)) + provider.create + end + end + end + + describe 'destroying' do + it "it should remove the directory" do + expects_rm_rf + provider.destroy + end + end + + describe "checking existence" do + it "should check for the directory" do + expects_directory?(true, resource.value(:path)) + expects_directory?(true, File.join(resource.value(:path), '.svn')) + provider.exists? + end + end + + describe "checking the revision property" do + before do + provider.expects(:svn).with('--non-interactive', 'info').returns(fixture(:svn_info)) + end + it "should use 'svn info'" do + expects_chdir + provider.revision.should == '4' # From 'Revision', not 'Last Changed Rev' + end + end + + describe "setting the revision property" do + before do + @revision = '30' + end + it "should use 'svn update'" do + expects_chdir + provider.expects(:svn).with('--non-interactive', 'update', '-r', @revision) + provider.revision = @revision + end + end + + describe "setting the revision property and repo source" do + before do + @revision = '30' + end + it "should use 'svn switch'" do + resource[:source] = 'an-unimportant-value' + expects_chdir + provider.expects(:svn).with('--non-interactive', 'switch', '-r', @revision, 'an-unimportant-value') + provider.revision = @revision + end + end + +end diff --git a/puppet/modules/vcsrepo/spec/unit/puppet/type/README.markdown b/puppet/modules/vcsrepo/spec/unit/puppet/type/README.markdown new file mode 100644 index 0000000..1ee19ac --- /dev/null +++ b/puppet/modules/vcsrepo/spec/unit/puppet/type/README.markdown @@ -0,0 +1,4 @@ +Resource Type Specs +=================== + +Define specs for your resource types in this directory.