diff options
Diffstat (limited to 'lib/puppet/provider')
-rw-r--r-- | lib/puppet/provider/vcsrepo.rb | 42 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/bzr.rb | 93 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/cvs.rb | 135 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/dummy.rb | 12 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/git.rb | 483 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/hg.rb | 130 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/p4.rb | 278 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/svn.rb | 139 |
8 files changed, 0 insertions, 1312 deletions
diff --git a/lib/puppet/provider/vcsrepo.rb b/lib/puppet/provider/vcsrepo.rb deleted file mode 100644 index 8793e632..00000000 --- a/lib/puppet/provider/vcsrepo.rb +++ /dev/null @@ -1,42 +0,0 @@ -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/lib/puppet/provider/vcsrepo/bzr.rb b/lib/puppet/provider/vcsrepo/bzr.rb deleted file mode 100644 index 797d84d2..00000000 --- a/lib/puppet/provider/vcsrepo/bzr.rb +++ /dev/null @@ -1,93 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:bzr, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports Bazaar repositories" - - 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 - update_owner - 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) - update_owner - end - - def clone_repository(revision) - args = ['branch'] - if revision - args.push('-r', revision) - end - args.push(@resource.value(:source), - @resource.value(:path)) - bzr(*args) - update_owner - end - - def update_owner - if @resource.value(:owner) or @resource.value(:group) - set_ownership - end - end -end diff --git a/lib/puppet/provider/vcsrepo/cvs.rb b/lib/puppet/provider/vcsrepo/cvs.rb deleted file mode 100644 index 7a8f6ef3..00000000 --- a/lib/puppet/provider/vcsrepo/cvs.rb +++ /dev/null @@ -1,135 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:cvs, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports CVS repositories/workspaces" - - commands :cvs => 'cvs' - has_features :gzip_compression, :reference_tracking, :modules, :cvs_rsh, :user - - 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? - Puppet.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 Puppet.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 - Puppet.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) - Puppet.debug "Using CVS_RSH = " + @resource.value(:cvs_rsh) - e = { :CVS_RSH => @resource.value(:cvs_rsh) } - else - e = {} - end - - if @resource.value(:user) and @resource.value(:user) != Facter['id'].value - Puppet.debug "Running as user " + @resource.value(:user) - Puppet::Util::Execution.execute([:cvs, *args], :uid => @resource.value(:user), :custom_environment => e) - else - Puppet::Util::Execution.execute([:cvs, *args], :custom_environment => e) - end - end -end diff --git a/lib/puppet/provider/vcsrepo/dummy.rb b/lib/puppet/provider/vcsrepo/dummy.rb deleted file mode 100644 index 27bfbbed..00000000 --- a/lib/puppet/provider/vcsrepo/dummy.rb +++ /dev/null @@ -1,12 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:dummy, :parent => Puppet::Provider::Vcsrepo) do - desc "Dummy default provider" - - defaultfor :feature => :posix - - 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/lib/puppet/provider/vcsrepo/git.rb b/lib/puppet/provider/vcsrepo/git.rb deleted file mode 100644 index 9d18b474..00000000 --- a/lib/puppet/provider/vcsrepo/git.rb +++ /dev/null @@ -1,483 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports Git repositories" - - has_command(:git, 'git') do - environment({ 'HOME' => ENV['HOME'] }) - end - - has_features :bare_repositories, :reference_tracking, :ssh_identity, :multiple_remotes, :user, :depth, :branch, :submodules - - def create - if @resource.value(:revision) and ensure_bare_or_mirror? - fail("Cannot set a revision (#{@resource.value(:revision)}) on a bare repository") - end - if !@resource.value(:source) - if @resource.value(:ensure) == :mirror - fail("Cannot init repository with mirror option, try bare instead") - end - - init_repository(@resource.value(:path)) - else - clone_repository(default_url, @resource.value(:path)) - update_remotes - - if @resource.value(:revision) - checkout - end - if !ensure_bare_or_mirror? && @resource.value(:submodules) == :true - update_submodules - end - - end - update_owner_and_excludes - end - - def destroy - FileUtils.rm_rf(@resource.value(:path)) - end - - # Checks to see if the current revision is equal to the revision on the - # remote (whether on a branch, tag, or reference) - # - # @return [Boolean] Returns true if the repo is on the latest revision - def latest? - return revision == latest_revision - end - - # Just gives the `should` value that we should be setting the repo to if - # latest? returns false - # - # @return [String] Returns the target sha/tag/branch - def latest - if not @resource.value(:revision) and branch = on_branch? - return branch - else - return @resource.value(:revision) - end - end - - # Get the current revision of the repo (tag/branch/sha) - # - # @return [String] Returns the branch/tag if the current sha matches the - # remote; otherwise returns the current sha. - def revision - #HEAD is the default, but lets just be explicit here. - get_revision('HEAD') - end - - # Is passed the desired reference, whether a tag, rev, or branch. Should - # handle transitions from a rev/branch/tag to a rev/branch/tag. Detached - # heads should be treated like bare revisions. - # - # @param [String] desired The desired revision to which the repo should be - # set. - def revision=(desired) - #just checkout tags and shas; fetch has already happened so they should be updated. - checkout(desired) - #branches require more work. - if local_branch_revision?(desired) - #reset instead of pull to avoid merge conflicts. assuming remote is - #updated and authoritative. - #TODO might be worthwhile to have an allow_local_changes param to decide - #whether to reset or pull when we're ensuring latest. - if @resource.value(:source) - at_path { git_with_identity('reset', '--hard', "#{@resource.value(:remote)}/#{desired}") } - else - at_path { git_with_identity('reset', '--hard', "#{desired}") } - end - end - #TODO Would this ever reach here if it is bare? - if !ensure_bare_or_mirror? && @resource.value(:submodules) == :true - update_submodules - end - update_owner_and_excludes - end - - def bare_exists? - bare_git_config_exists? && !working_copy_exists? - end - - def ensure_bare_or_mirror? - [:bare, :mirror].include? @resource.value(:ensure) - end - - # If :source is set to a hash (for supporting multiple remotes), - # we search for the URL for :remote. If it doesn't exist, - # we throw an error. If :source is just a string, we use that - # value for the default URL. - def default_url - if @resource.value(:source).is_a?(Hash) - if @resource.value(:source).has_key?(@resource.value(:remote)) - @resource.value(:source)[@resource.value(:remote)] - else - fail("You must specify the URL for #{@resource.value(:remote)} in the :source hash") - end - else - @resource.value(:source) - end - end - - def working_copy_exists? - if @resource.value(:source) and File.exists?(File.join(@resource.value(:path), '.git', 'config')) - File.readlines(File.join(@resource.value(:path), '.git', 'config')).grep(/#{Regexp.escape(default_url)}/).any? - else - File.directory?(File.join(@resource.value(:path), '.git')) - end - end - - def exists? - working_copy_exists? || bare_exists? - end - - def update_remote_url(remote_name, remote_url) - do_update = false - current = git_with_identity('config', '-l') - - unless remote_url.nil? - # Check if remote exists at all, regardless of URL. - # If remote doesn't exist, add it - if not current.include? "remote.#{remote_name}.url" - git_with_identity('remote','add', remote_name, remote_url) - return true - - # If remote exists, but URL doesn't match, update URL - elsif not current.include? "remote.#{remote_name}.url=#{remote_url}" - git_with_identity('remote','set-url', remote_name, remote_url) - return true - else - return false - end - end - - end - - def update_remotes - do_update = false - - # If supplied source is a hash of remote name and remote url pairs, then - # we loop around the hash. Otherwise, we assume single url specified - # in source property - if @resource.value(:source).is_a?(Hash) - @resource.value(:source).keys.sort.each do |remote_name| - remote_url = @resource.value(:source)[remote_name] - at_path { do_update |= update_remote_url(remote_name, remote_url) } - end - else - at_path { do_update |= update_remote_url(@resource.value(:remote), @resource.value(:source)) } - end - - # If at least one remote was added or updated, then we must - # call the 'git remote update' command - if do_update == true - at_path { git_with_identity('remote','update') } - end - - end - - def update_references - at_path do - update_remotes - git_with_identity('fetch', @resource.value(:remote)) - git_with_identity('fetch', '--tags', @resource.value(:remote)) - update_owner_and_excludes - end - end - - private - - def valid_repo? - Dir.chdir(@resource.value(:path)){ system('git rev-parse > /dev/null 2>&1')} - end - - def bare_git_config_exists? - File.exist?(File.join(@resource.value(:path), 'config')) && valid_repo? - end - - # @!visibility private - def clone_repository(source, path) - check_force - args = ['clone'] - if @resource.value(:depth) and @resource.value(:depth).to_i > 0 - args.push('--depth', @resource.value(:depth).to_s) - if @resource.value(:revision) - args.push('--branch', @resource.value(:revision).to_s) - end - end - if @resource.value(:branch) - args.push('--branch', @resource.value(:branch).to_s) - end - - case @resource.value(:ensure) - when :bare then args << '--bare' - when :mirror then args << '--mirror' - end - - if @resource.value(:remote) != 'origin' - args.push('--origin', @resource.value(:remote)) - end - if !working_copy_exists? - args.push(source, path) - Dir.chdir("/") do - git_with_identity(*args) - end - else - notice "Repo has already been cloned" - end - end - - # @!visibility private - def check_force - if path_exists? and not path_empty? - if @resource.value(:force) && !valid_repo? - 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 - - # @!visibility private - 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: - # <path>/.git - # to: - # <path>/ - # @!visibility private - 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: - # <path>/ - # to: - # <path>/.git - # @!visibility private - 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 - - # @!visibility private - 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 - - # Will checkout a rev/branch/tag using the locally cached versions. Does not - # handle upstream branch changes - # @!visibility private - def checkout(revision = @resource.value(:revision)) - if !local_branch_revision?(revision) && remote_branch_revision?(revision) - #non-locally existant branches (perhaps switching to a branch that has never been checked out) - at_path { git_with_identity('checkout', '--force', '-b', revision, '--track', "#{@resource.value(:remote)}/#{revision}") } - else - #tags, locally existant branches (perhaps outdated), and shas - at_path { git_with_identity('checkout', '--force', revision) } - end - end - - # @!visibility private - def reset(desired) - at_path do - git_with_identity('reset', '--hard', desired) - end - end - - # @!visibility private - def update_submodules - at_path do - git_with_identity('submodule', 'update', '--init', '--recursive') - end - end - - # Determins if the branch exists at the upstream but has not yet been locally committed - # @!visibility private - 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 - - # Determins if the branch is already cached locally - # @!visibility private - def local_branch_revision?(revision = @resource.value(:revision)) - at_path { branches.include?(revision) } - end - - # @!visibility private - def tag_revision?(revision = @resource.value(:revision)) - at_path { tags.include?(revision) } - end - - # @!visibility private - def branches - at_path { git_with_identity('branch', '-a') }.gsub('*', ' ').split(/\n/).map { |line| line.strip } - end - - # git < 2.4 returns 'detached from' - # git 2.4+ returns 'HEAD detached at' - # @!visibility private - def on_branch? - at_path { - matches = git_with_identity('branch', '-a').match /\*\s+(.*)/ - matches[1] unless matches[1].match /(\(detached from|\(HEAD detached at|\(no branch)/ - } - end - - # @!visibility private - def tags - at_path { git_with_identity('tag', '-l') }.split(/\n/).map { |line| line.strip } - end - - # @!visibility private - def set_excludes - # Excludes may be an Array or a String. - at_path do - open('.git/info/exclude', 'w') do |f| - if @resource.value(:excludes).respond_to?(:each) - @resource.value(:excludes).each { |ex| f.puts ex } - else - f.puts @resource.value(:excludes) - end - end - end - end - - # Finds the latest revision or sha of the current branch if on a branch, or - # of HEAD otherwise. - # @note Calls create which can forcibly destroy and re-clone the repo if - # force => true - # @see get_revision - # - # @!visibility private - # @return [String] Returns the output of get_revision - def latest_revision - #TODO Why is create called here anyway? - create if @resource.value(:force) && working_copy_exists? - create if !working_copy_exists? - - if branch = on_branch? - return get_revision("#{@resource.value(:remote)}/#{branch}") - else - return get_revision - end - end - - # Returns the current revision given if the revision is a tag or branch and - # matches the current sha. If the current sha does not match the sha of a tag - # or branch, then it will just return the sha (ie, is not in sync) - # - # @!visibility private - # - # @param [String] rev The revision of which to check if it is current - # @return [String] Returns the tag/branch of the current repo if it's up to - # date; otherwise returns the sha of the requested revision. - def get_revision(rev = 'HEAD') - if @resource.value(:source) - update_references - else - status = at_path { git_with_identity('status')} - is_it_new = status =~ /Initial commit/ - if is_it_new - status =~ /On branch (.*)/ - branch = $1 - return branch - end - end - current = at_path { git_with_identity('rev-parse', rev).strip } - if @resource.value(:revision) - if tag_revision? - # git-rev-parse will give you the hash of the tag object itself rather - # than the commit it points to by default. Using tag^0 will return the - # actual commit. - canonical = at_path { git_with_identity('rev-parse', "#{@resource.value(:revision)}^0").strip } - elsif 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 } - else - #look for a sha (could match invalid shas) - canonical = at_path { git_with_identity('rev-parse', '--revs-only', @resource.value(:revision)).strip } - end - fail("#{@resource.value(:revision)} is not a local or remote ref") if canonical.nil? or canonical.empty? - current = @resource.value(:revision) if current == canonical - end - return current - end - - # @!visibility private - def update_owner_and_excludes - if @resource.value(:owner) or @resource.value(:group) - set_ownership - end - if @resource.value(:excludes) - set_excludes - end - end - - # @!visibility private - def git_with_identity(*args) - if @resource.value(:identity) - Tempfile.open('git-helper', Puppet[:statedir]) do |f| - f.puts '#!/bin/sh' - f.puts 'export SSH_AUTH_SOCKET=' - 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) and @resource.value(:user) != Facter['id'].value - env = Etc.getpwnam(@resource.value(:user)) - Puppet::Util::Execution.execute("git #{args.join(' ')}", :uid => @resource.value(:user), :failonfail => true, :custom_environment => {'HOME' => env['dir']}) - else - git(*args) - end - end -end diff --git a/lib/puppet/provider/vcsrepo/hg.rb b/lib/puppet/provider/vcsrepo/hg.rb deleted file mode 100644 index 294c2a97..00000000 --- a/lib/puppet/provider/vcsrepo/hg.rb +++ /dev/null @@ -1,130 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports Mercurial repositories" - - commands :hg => 'hg' - - has_features :reference_tracking, :ssh_identity, :user, :basic_auth - - 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', { :remote => true })[/^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', { :remote => true }) - 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)) - args.push({ :remote => true }) - hg_wrapper(*args) - end - - def update_owner - if @resource.value(:owner) or @resource.value(:group) - set_ownership - end - end - - def hg_wrapper(*args) - options = { :remote => false } - if args.length > 0 and args[-1].is_a? Hash - options.merge!(args.pop) - end - - if @resource.value(:basic_auth_username) && @resource.value(:basic_auth_password) - args += [ - "--config", "\"auth.x.prefix=#{@resource.value(:source)}\"", - "--config", "\"auth.x.username=#{@resource.value(:basic_auth_username)}\"", - "--config", "\"auth.x.password=#{@resource.value(:basic_auth_password)}\"", - "--config", "\"auth.x.schemes=http https\"" - ] - end - - if options[:remote] and @resource.value(:identity) - args += ["--ssh", "ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -i #{@resource.value(:identity)}"] - end - if @resource.value(:user) and @resource.value(:user) != Facter['id'].value - args.map! { |a| if a =~ /\s/ then "'#{a}'" else a end } # Adds quotes to arguments with whitespaces. - Puppet::Util::Execution.execute("hg #{args.join(' ')}", :uid => @resource.value(:user), :failonfail => true) - else - hg(*args) - end - end -end diff --git a/lib/puppet/provider/vcsrepo/p4.rb b/lib/puppet/provider/vcsrepo/p4.rb deleted file mode 100644 index b429bcbb..00000000 --- a/lib/puppet/provider/vcsrepo/p4.rb +++ /dev/null @@ -1,278 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:p4, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports Perforce depots" - - has_features :filesystem_types, :reference_tracking, :p4config - - def create - # create or update client - create_client(client_name) - - # if source provided, sync client - source = @resource.value(:source) - if source - revision = @resource.value(:revision) - sync_client(source, revision) - end - - update_owner - end - - def working_copy_exists? - # Check if the server is there, or raise error - p4(['info'], {:marshal => false}) - - # Check if workspace is setup - args = ['where'] - args.push(@resource.value(:path) + "...") - hash = p4(args, {:raise => false}) - - return (hash['code'] != "error") - end - - def exists? - working_copy_exists? - end - - def destroy - args = ['client'] - args.push('-d', '-f') - args.push(client_name) - p4(args) - FileUtils.rm_rf(@resource.value(:path)) - end - - def latest? - rev = self.revision - if rev - (rev >= self.latest) - else - true - end - end - - def latest - args = ['changes'] - args.push('-m1', @resource.value(:source)) - hash = p4(args) - - return hash['change'].to_i - end - - def revision - args = ['cstat'] - args.push(@resource.value(:source)) - hash = p4(args, {:marshal => false}) - hash = marshal_cstat(hash) - - revision = 0 - if hash && hash['code'] != 'error' - hash['data'].each do |c| - if c['status'] == 'have' - change = c['change'].to_i - revision = change if change > revision - end - end - end - return revision - end - - def revision=(desired) - sync_client(@resource.value(:source), desired) - update_owner - end - - private - - def update_owner - if @resource.value(:owner) or @resource.value(:group) - set_ownership - end - end - - # Sync the client workspace files to head or specified revision. - # Params: - # +source+:: Depot path to sync - # +revision+:: Perforce change list to sync to (optional) - def sync_client(source, revision) - Puppet.debug "Syncing: #{source}" - args = ['sync'] - if revision - args.push(source + "@#{revision}") - else - args.push(source) - end - p4(args) - end - - # Returns the name of the Perforce client workspace - def client_name - p4config = @resource.value(:p4config) - - # default (generated) client name - path = @resource.value(:path) - host = Facter.value('hostname') - default = "puppet-" + Digest::MD5.hexdigest(path + host) - - # check config for client name - set_client = nil - if p4config && File.file?(p4config) - open(p4config) do |f| - m = f.grep(/^P4CLIENT=/).pop - p = /^P4CLIENT=(.*)$/ - set_client = p.match(m)[1] if m - end - end - - return set_client || ENV['P4CLIENT'] || default - end - - # Create (or update) a client workspace spec. - # If a client name is not provided then a hash based on the path is used. - # Params: - # +client+:: Name of client workspace - # +path+:: The Root location of the Perforce client workspace - def create_client(client) - Puppet.debug "Creating client: #{client}" - - # fetch client spec - hash = parse_client(client) - hash['Root'] = @resource.value(:path) - hash['Description'] = "Generated by Puppet VCSrepo" - - # check is source is a Stream - source = @resource.value(:source) - if source - parts = source.split(/\//) - if parts && parts.length >= 4 - source = "//" + parts[2] + "/" + parts[3] - streams = p4(['streams', source], {:raise => false}) - if streams['code'] == "stat" - hash['Stream'] = streams['Stream'] - notice "Streams" + streams['Stream'].inspect - end - end - end - - # save client spec - save_client(hash) - end - - - # Fetches a client workspace spec from Perforce and returns a hash map representation. - # Params: - # +client+:: name of the client workspace - def parse_client(client) - args = ['client'] - args.push('-o', client) - hash = p4(args) - - return hash - end - - - # Saves the client workspace spec from the given hash - # Params: - # +hash+:: hash map of client spec - def save_client(hash) - spec = String.new - view = "\nView:\n" - - hash.keys.sort.each do |k| - v = hash[k] - next if( k == "code" ) - if(k.to_s =~ /View/ ) - view += "\t#{v}\n" - else - spec += "#{k.to_s}: #{v.to_s}\n" - end - end - spec += view - - args = ['client'] - args.push('-i') - p4(args, {:input => spec, :marshal => false}) - end - - # Sets Perforce Configuration environment. - # P4CLIENT generated, but overwitten if defined in config. - def config - p4config = @resource.value(:p4config) - - cfg = Hash.new - cfg.store 'P4CONFIG', p4config if p4config - cfg.store 'P4CLIENT', client_name - return cfg - end - - def p4(args, options = {}) - # Merge custom options with defaults - opts = { - :raise => true, # Raise errors - :marshal => true, # Marshal output - }.merge(options) - - cmd = ['p4'] - cmd.push '-R' if opts[:marshal] - cmd.push args - cmd_str = cmd.respond_to?(:join) ? cmd.join(' ') : cmd - - Puppet.debug "environment: #{config}" - Puppet.debug "command: #{cmd_str}" - - hash = Hash.new - Open3.popen3(config, cmd_str) do |i, o, e, t| - # Send input stream if provided - if(opts[:input]) - Puppet.debug "input:\n" + opts[:input] - i.write opts[:input] - i.close - end - - if(opts[:marshal]) - hash = Marshal.load(o) - else - hash['data'] = o.read - end - - # Raise errors, Perforce or Exec - if(opts[:raise] && !e.eof && t.value != 0) - raise Puppet::Error, "\nP4: #{e.read}" - end - if(opts[:raise] && hash['code'] == 'error' && t.value != 0) - raise Puppet::Error, "\nP4: #{hash['data']}" - end - end - - Puppet.debug "hash: #{hash}\n" - return hash - end - - # helper method as cstat does not Marshal - def marshal_cstat(hash) - data = hash['data'] - code = 'error' - - list = Array.new - change = Hash.new - data.each_line do |l| - p = /^\.\.\. (.*) (.*)$/ - m = p.match(l) - if m - change[m[1]] = m[2] - if m[1] == 'status' - code = 'stat' - list.push change - change = Hash.new - end - end - end - - hash = Hash.new - hash.store 'code', code - hash.store 'data', list - return hash - end - -end diff --git a/lib/puppet/provider/vcsrepo/svn.rb b/lib/puppet/provider/vcsrepo/svn.rb deleted file mode 100644 index fccfaa5a..00000000 --- a/lib/puppet/provider/vcsrepo/svn.rb +++ /dev/null @@ -1,139 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'vcsrepo') - -Puppet::Type.type(:vcsrepo).provide(:svn, :parent => Puppet::Provider::Vcsrepo) do - desc "Supports Subversion repositories" - - commands :svn => 'svn', - :svnadmin => 'svnadmin', - :svnlook => 'svnlook' - - has_features :filesystem_types, :reference_tracking, :basic_auth, :configuration, :conflict, :depth - - def create - if !@resource.value(:source) - create_repository(@resource.value(:path)) - else - checkout_repository(@resource.value(:source), - @resource.value(:path), - @resource.value(:revision), - @resource.value(:depth)) - 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')) - if File.file?(File.join(@resource.value(:path), 'format')) - # :path is an svn server - return true if svnlook('uuid', @resource.value(:path)) - end - 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 - - if @resource.value(:trust_server_cert) != :false - args.push('--trust-server-cert') - 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 - - if @resource.value(:conflict) - args.push('--accept', @resource.value(:conflict)) - end - - at_path do - svn(*args) - end - update_owner - end - - private - - def checkout_repository(source, path, revision, depth) - args = buildargs.push('checkout') - if revision - args.push('-r', revision) - end - if depth - args.push('--depth', depth) - 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 |