diff --git a/lib/puppet/functions/archive/artifactory_checksum.rb b/lib/puppet/functions/archive/artifactory_checksum.rb index e5f9a38..089b3dc 100644 --- a/lib/puppet/functions/archive/artifactory_checksum.rb +++ b/lib/puppet/functions/archive/artifactory_checksum.rb @@ -1,26 +1,26 @@ require 'json' -require 'puppet_x/bodeco/util' +require_relative '../../../puppet_x/bodeco/util.rb' Puppet::Functions.create_function(:'archive::artifactory_checksum') do # @summary A function that returns the checksum value of an artifact stored in Artifactory # @param url The URL of the artifact. # @param checksum_type The checksum type. # Note the function will raise an error if you ask for sha256 but your artifactory instance doesn't have the sha256 value calculated. # @return [String] Returns the checksum. dispatch :artifactory_checksum do param 'Stdlib::HTTPUrl', :url optional_param "Enum['sha1','sha256','md5']", :checksum_type return_type 'String' end def artifactory_checksum(url, checksum_type = 'sha1') uri = URI(url.sub('/artifactory/', '/artifactory/api/storage/')) response = PuppetX::Bodeco::Util.content(uri) content = JSON.parse(response) checksum = content['checksums'] && content['checksums'][checksum_type] raise("Could not parse #{checksum_type} from url: #{uri}\nresponse: #{response.body}") unless checksum =~ %r{\b[0-9a-f]{5,64}\b} checksum end end diff --git a/lib/puppet/functions/archive/artifactory_latest_url.rb b/lib/puppet/functions/archive/artifactory_latest_url.rb index 30b0c51..57b24d9 100644 --- a/lib/puppet/functions/archive/artifactory_latest_url.rb +++ b/lib/puppet/functions/archive/artifactory_latest_url.rb @@ -1,45 +1,45 @@ require 'json' -require 'puppet_x/bodeco/util' +require_relative '../../../puppet_x/bodeco/util.rb' Puppet::Functions.create_function(:'archive::artifactory_latest_url') do dispatch :artifactory_latest_url do param 'Variant[Stdlib::HTTPUrl, Stdlib::HTTPSUrl]', :url param 'Hash', :maven_data end def artifactory_latest_url(url, maven_data) # Turn provided artifactory URL into the fileinfo API endpoint of the parent directory uri = URI(url.sub('/artifactory/', '/artifactory/api/storage/')[%r{^(.*)/.*$}, 1]) response = PuppetX::Bodeco::Util.content(uri) content = JSON.parse(response) uris = if maven_data['classifier'] content['children'].select do |child| child['uri'] =~ %r{^/#{maven_data['module']}-#{maven_data['base_rev']}-(SNAPSHOT|(?:(?:[0-9]{8}.[0-9]{6})-(?:[0-9]+)))-#{maven_data['classifier']}\.#{maven_data['ext']}$} && !child['folder'] end else content['children'].select do |child| child['uri'] =~ %r{^/#{maven_data['module']}-#{maven_data['base_rev']}-(SNAPSHOT|(?:(?:[0-9]{8}.[0-9]{6})-(?:[0-9]+)))\.#{maven_data['ext']}$} && !child['folder'] end end raise("Couldn't find any Artifactory artifacts") if uris.empty? latest = uris.sort_by { |x| x['uri'] }.last['uri'] Puppet.debug("Latest artifact found for #{url} was #{latest}") # Now GET the fileinfo endpoint of the resolved latest version file uri = URI("#{content['uri']}#{latest}") response = PuppetX::Bodeco::Util.content(uri) content = JSON.parse(response) url = content['downloadUri'] sha1 = content['checksums'] && content['checksums']['sha1'] { 'url' => url, 'sha1' => sha1 } end end diff --git a/lib/puppet_x/bodeco/util.rb b/lib/puppet_x/bodeco/util.rb index 96ac6df..b7da192 100644 --- a/lib/puppet_x/bodeco/util.rb +++ b/lib/puppet_x/bodeco/util.rb @@ -1,180 +1,180 @@ module PuppetX module Bodeco module Util def self.download(url, filepath, options = {}) uri = URI(url) @connection = PuppetX::Bodeco.const_get(uri.scheme.upcase).new("#{uri.scheme}://#{uri.host}:#{uri.port}", options) @connection.download(uri, filepath) end def self.content(url, options = {}) uri = URI(url) @connection = PuppetX::Bodeco.const_get(uri.scheme.upcase).new("#{uri.scheme}://#{uri.host}:#{uri.port}", options) @connection.content(uri) end # - # This allows you to use a puppet syntax for a file and return it's content. + # This allows you to use a puppet syntax for a file and return its content. # # @example # puppet_download 'puppet:///modules/my_module_name/my_file.dat # # @param [String] url this is the puppet url of the file to be fetched # @param [String] filepath this is path of the file to create # # @raise [ArgumentError] when the file doesn't exist # def self.puppet_download(url, filepath) # Somehow there is no consistent way to determine what terminus to use. So we switch to a # trial and error method. First we start withe the default. And if it doesn't work, we try the # other ones status = load_file_with_any_terminus(url) raise ArgumentError, "Could not retrieve information from environment #{Puppet['environment']} source(s) #{url}'" unless status File.open(filepath, 'w') { |file| file.write(status.content) } end # @private # rubocop:disable HandleExceptions def self.load_file_with_any_terminus(url) termini_to_try = [:file_server, :rest] termini_to_try.each do |terminus| with_terminus(terminus) do begin content = Puppet::FileServing::Content.indirection.find(url) rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::ETIMEDOUT # rescue any network error end return content if content end end nil end # rubocop:enable HandleExceptions def self.with_terminus(terminus) old_terminus = Puppet[:default_file_terminus] Puppet[:default_file_terminus] = terminus value = yield Puppet[:default_file_terminus] = old_terminus value end end class HTTP require 'net/http' FOLLOW_LIMIT = 5 URI_UNSAFE = %r{[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]%]} def initialize(_url, options) @username = options[:username] @password = options[:password] @cookie = options[:cookie] @insecure = options[:insecure] if options[:proxy_server] uri = URI(options[:proxy_server]) unless uri.scheme uri = URI("#{options[:proxy_type]}://#{options[:proxy_server]}") end @proxy_addr = uri.hostname @proxy_port = uri.port end ENV['SSL_CERT_FILE'] = File.expand_path(File.join(__FILE__, '..', 'cacert.pem')) if Facter.value(:osfamily) == 'windows' && !ENV.key?('SSL_CERT_FILE') end def generate_request(uri) header = @cookie && { 'Cookie' => @cookie } request = Net::HTTP::Get.new(uri.request_uri, header) request.basic_auth(@username, @password) if @username && @password request end def follow_redirect(uri, option = { limit: FOLLOW_LIMIT }, &block) http_opts = if uri.scheme == 'https' { use_ssl: true, verify_mode: (@insecure ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER) } else { use_ssl: false } end Net::HTTP.start(uri.host, uri.port, @proxy_addr, @proxy_port, http_opts) do |http| http.request(generate_request(uri)) do |response| case response when Net::HTTPSuccess yield response when Net::HTTPRedirection limit = option[:limit] - 1 raise Puppet::Error, "Redirect limit exceeded, last url: #{uri}" if limit < 0 location = safe_escape(response['location']) new_uri = URI(location) new_uri = URI(uri.to_s + location) if new_uri.relative? follow_redirect(new_uri, limit: limit, &block) else raise Puppet::Error, "HTTP Error Code #{response.code}\nURL: #{uri}\nContent:\n#{response.body}" end end end end def download(uri, file_path, option = { limit: FOLLOW_LIMIT }) follow_redirect(uri, option) do |response| File.open file_path, 'wb' do |io| response.read_body do |chunk| io.write chunk end end end end def content(uri, option = { limit: FOLLOW_LIMIT }) follow_redirect(uri, option) do |response| return response.body end end def safe_escape(uri) uri.to_s.gsub(URI_UNSAFE) do |match| '%' + match.unpack('H2' * match.bytesize).join('%').upcase end end end class HTTPS < HTTP end class FTP require 'net/ftp' def initialize(url, options) uri = URI(url) username = options[:username] password = options[:password] proxy_server = options[:proxy_server] proxy_type = options[:proxy_type] ENV["#{proxy_type}_proxy"] = proxy_server @ftp = Net::FTP.new @ftp.connect(uri.host, uri.port) if username @ftp.login(username, password) else @ftp.login end end def download(uri, file_path) @ftp.getbinaryfile(uri.path, file_path) end end class FILE def initialize(_url, _options) end def download(uri, file_path) FileUtils.copy(uri.path, file_path) end end end end