-
-
Save tko/7768c3cc87fd29559ea0 to your computer and use it in GitHub Desktop.
puppet provider for R packages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
# puppet/modules/r/lib/puppet/provider/package/r.rb | |
# | |
# puppet provider for R packages that should handle local files, puppet files, and CRAN repositories | |
# | |
# Usage: | |
# package { "ggplot2": provider => r, source => "cran+http://cran-mirror.cs.uu.nl" } | |
# package { "ggplot2": provider => r, source => "puppet:///modules/whatever/ggplot2_1.0.0.tar.gz" } | |
# package { "ggplot2": provider => r, source => ["puppet:///modules/whatever/ggplot2_${my_nonexistent_version}.tar.gz", | |
# "cran+http://cran-mirror.cs.uu.nl", | |
# ]} | |
# package { "ggplot2": provider => r, ensure => absent } | |
# | |
require 'tempfile' | |
require "puppet/provider/package" | |
require "puppet/file_serving" | |
Puppet::Type.type(:package).provide :r, :parent => Puppet::Provider::Package do | |
include Puppet::Util | |
# has_feature :unversionable | |
has_feature :uninstallable | |
commands :rcmd => "R" | |
def self.instances | |
instances = [] | |
output = rcmd("--vanilla", "--slave", "-e", "pkgs=installed.packages(); cat(pkgs[, 'Version'], labels=rownames(pkgs), fill=1)") | |
output.each_line { |line| | |
name, version = line.split() | |
instances << new({:provider => self.name, :name => name, :ensure => version}) | |
} | |
return instances | |
end | |
def validate_source(sources) | |
sources = [sources] unless sources.is_a?(Array) | |
sources.each do |source| | |
next if Puppet::Util.absolute_path?(source) | |
begin | |
uri = URI.parse(URI.escape(source)) | |
rescue => detail | |
self.fail "Could not understand source #{source}: #{detail}" | |
end | |
self.fail "Cannot use relative URLs '#{source}'" unless uri.absolute? | |
self.fail "Cannot use opaque URLs '#{source}'" unless uri.hierarchical? | |
unless %w{file puppet cran+http cran+https}.include?(uri.scheme) | |
self.fail "Cannot use URLs of type '#{uri.scheme}' as source for R modules" | |
end | |
end | |
end | |
def query | |
begin | |
return self.check_installed | |
rescue Puppet::ExecutionFailure => detail | |
end | |
return nil | |
end | |
def check_installed | |
version = rcmd("--vanilla", "--slave", "-e", "cat(installed.packages()[#{self.resource[:name].inspect}, 'Version'])") | |
return { :ensure => version, :name => self.resource[:name] } | |
end | |
def install | |
self.fail "source is required" unless self.resource[:source] | |
sources = self.resource[:source] | |
sources = [sources] unless sources.is_a?(Array) | |
we_tried = false | |
sources.each do |source| | |
next unless self.maybe_install_from(source) | |
we_tried = true | |
break | |
end | |
fail "Could not retrieve information from environment #{Puppet[:environment]} source(s) #{sources.join(", ")}" unless we_tried | |
begin | |
# For some reason install.packages() might silently decide to not install | |
# the package, only download it. Check to make sure. | |
self.check_installed | |
rescue Puppet::ExecutionFailure => detail | |
fail("Installation failed") | |
end | |
end | |
def maybe_install_from(source) | |
uri = URI.parse(URI.escape(source)) | |
if %w{cran+http cran+https}.include?(uri.scheme) | |
repos = source.sub('cran+', '') | |
self.install_from_repository(repos) | |
return true | |
else | |
return self.maybe_install_from_file(source) | |
end | |
end | |
def maybe_install_from_file(source) | |
self.with_tarball_path(source) { |path| | |
rcmd("--vanilla", "--slave", "CMD", "INSTALL", path) | |
return true | |
} | |
return false | |
end | |
def install_from_repository(repos) | |
package = self.resource[:name] | |
rcmd("--vanilla", "--slave", "-e", "options(warn=2); install.packages(#{package.inspect}, repos=#{repos.inspect})") | |
end | |
def uninstall | |
rcmd("--vanilla", "--slave", "CMD", "REMOVE", self.resource[:name]) | |
end | |
def with_tarball_path(source) | |
return nil unless metadata = self.metadata(source) | |
unless content = Puppet::FileServing::Content.indirection.find(metadata.source) | |
fail "Could not find any content at #{metadata.source}" | |
end | |
if Puppet::Util.absolute_path?(metadata.path) | |
yield metadata.path | |
else | |
Tempfile.open(self.resource[:name]) { |file| | |
file.write content.content | |
file.flush | |
# FIXME: checksums? | |
yield file.path | |
} | |
end | |
end | |
def metadata(source) | |
begin | |
if data = Puppet::FileServing::Metadata.indirection.find(source) | |
metadata = data | |
metadata.source = source | |
return metadata | |
end | |
rescue => detail | |
fail detail, "Could not retrieve file metadata for #{source}: #{detail}" | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment