diff --git a/lib/puppet/forge.rb b/lib/puppet/forge.rb index 722f03b95..93c5f6b01 100644 --- a/lib/puppet/forge.rb +++ b/lib/puppet/forge.rb @@ -1,98 +1,95 @@ require 'net/http' require 'open-uri' require 'pathname' require 'uri' require 'puppet/forge/cache' require 'puppet/forge/repository' module Puppet::Forge # Return a list of module metadata hashes that match the search query. # This return value is used by the module_tool face install search, # and displayed to on the console. # # Example return value: # # [ # { # "author" => "puppetlabs", # "name" => "bacula", # "tag_list" => ["backup", "bacula"], # "releases" => [{"version"=>"0.0.1"}, {"version"=>"0.0.2"}], # "full_name" => "puppetlabs/bacula", # "version" => "0.0.2", # "project_url" => "http://github.com/puppetlabs/puppetlabs-bacula", # "desc" => "bacula" # } # ] # def self.search(term) server = Puppet.settings[:module_repository].sub(/^(?!https?:\/\/)/, 'http://') Puppet.notice "Searching #{server} ..." - request = Net::HTTP::Get.new("/modules.json?q=#{URI.escape(term)}") - response = repository.make_http_request(request) + response = repository.make_http_request("/modules.json?q=#{URI.escape(term)}") case response.code when "200" matches = PSON.parse(response.body) else raise RuntimeError, "Could not execute search (HTTP #{response.code})" - matches = [] end matches end def self.remote_dependency_info(author, mod_name, version) version_string = version ? "&version=#{version}" : '' - request = Net::HTTP::Get.new("/api/v1/releases.json?module=#{author}/#{mod_name}" + version_string) - response = repository.make_http_request(request) + response = repository.make_http_request("/api/v1/releases.json?module=#{author}/#{mod_name}#{version_string}") json = PSON.parse(response.body) rescue {} case response.code when "200" return json else error = json['error'] || '' if error =~ /^Module #{author}\/#{mod_name} has no release/ return [] else raise RuntimeError, "Could not find release information for this module (#{author}/#{mod_name}) (HTTP #{response.code})" end end end def self.get_release_packages_from_repository(install_list) install_list.map do |release| modname, version, file = release cache_path = nil if file begin cache_path = repository.retrieve(file) rescue OpenURI::HTTPError => e raise RuntimeError, "Could not download module: #{e.message}" end else raise RuntimeError, "Malformed response from module repository." end cache_path end end # Locate a module release package on the local filesystem and move it # into the `Puppet.settings[:module_working_dir]`. Do not unpack it, just # return the location of the package on disk. def self.get_release_package_from_filesystem(filename) if File.exist?(File.expand_path(filename)) repository = Repository.new('file:///') uri = URI.parse("file://#{URI.escape(File.expand_path(filename))}") cache_path = repository.retrieve(uri) else raise ArgumentError, "File does not exists: #{filename}" end cache_path end def self.repository @repository ||= Puppet::Forge::Repository.new end end diff --git a/lib/puppet/forge/repository.rb b/lib/puppet/forge/repository.rb index cacfdc6ea..118872264 100644 --- a/lib/puppet/forge/repository.rb +++ b/lib/puppet/forge/repository.rb @@ -1,102 +1,103 @@ require 'net/http' require 'digest/sha1' require 'uri' module Puppet::Forge # = Repository # # This class is a file for accessing remote repositories with modules. class Repository attr_reader :uri, :cache # Instantiate a new repository instance rooted at the optional string # +url+, else an instance of the default Puppet modules repository. def initialize(url=Puppet[:module_repository]) @uri = url.is_a?(::URI) ? url : ::URI.parse(url.sub(/^(?!https?:\/\/)/, 'http://')) @cache = Cache.new(self) end # Read HTTP proxy configurationm from Puppet's config file, or the # http_proxy environment variable. def http_proxy_env proxy_env = ENV["http_proxy"] || ENV["HTTP_PROXY"] || nil begin return URI.parse(proxy_env) if proxy_env rescue URI::InvalidURIError return nil end return nil end def http_proxy_host env = http_proxy_env if env and env.host then return env.host end if Puppet.settings[:http_proxy_host] == 'none' return nil end return Puppet.settings[:http_proxy_host] end def http_proxy_port env = http_proxy_env if env and env.port then return env.port end return Puppet.settings[:http_proxy_port] end - # Return a Net::HTTPResponse read for this +request+. - def make_http_request(request, options = {}) + # Return a Net::HTTPResponse read for this +request_path+. + def make_http_request(request_path) + request = Net::HTTP::Get.new(request_path) if ! @uri.user.nil? && ! @uri.password.nil? request.basic_auth(@uri.user, @uri.password) end return read_response(request) end # Return a Net::HTTPResponse read from this HTTPRequest +request+. def read_response(request) begin Net::HTTP::Proxy( http_proxy_host, http_proxy_port ).start(@uri.host, @uri.port) do |http| http.request(request) end rescue Errno::ECONNREFUSED, SocketError msg = "Error: Could not connect to #{@uri}\n" msg << " There was a network communications problem\n" msg << " Check your network connection and try again\n" Puppet.err msg exit(1) end end # Return the local file name containing the data downloaded from the # repository at +release+ (e.g. "myuser-mymodule"). def retrieve(release) return cache.retrieve(@uri + release) end # Return the URI string for this repository. def to_s return @uri.to_s end # Return the cache key for this repository, this a hashed string based on # the URI. def cache_key return @cache_key ||= [ @uri.to_s.gsub(/[^[:alnum:]]+/, '_').sub(/_$/, ''), Digest::SHA1.hexdigest(@uri.to_s) ].join('-') end end end diff --git a/spec/unit/forge/repository_spec.rb b/spec/unit/forge/repository_spec.rb index bbfc0d136..d3abc39ae 100644 --- a/spec/unit/forge/repository_spec.rb +++ b/spec/unit/forge/repository_spec.rb @@ -1,56 +1,49 @@ require 'spec_helper' require 'net/http' require 'puppet/forge/repository' require 'puppet/forge/cache' describe Puppet::Forge::Repository do - describe 'instances' do + let(:repository) { Puppet::Forge::Repository.new('http://fake.com') } - let(:repository) { Puppet::Forge::Repository.new('http://fake.com') } + it "retrieve accesses the cache" do + uri = URI.parse('http://some.url.com') + repository.cache.expects(:retrieve).with(uri) - describe '#retrieve' do - before do - @uri = URI.parse('http://some.url.com') - end + repository.retrieve(uri) + end + + describe 'http_proxy support' do + after :each do + ENV["http_proxy"] = nil + end + + it "supports environment variable for port and host" do + ENV["http_proxy"] = "http://test.com:8011" + + repository.http_proxy_host.should == "test.com" + repository.http_proxy_port.should == 8011 + end + + it "supports puppet configuration for port and host" do + ENV["http_proxy"] = nil + proxy_settings_of('test.com', 7456) + + repository.http_proxy_port.should == 7456 + repository.http_proxy_host.should == "test.com" + end + + it "uses environment variable before puppet settings" do + ENV["http_proxy"] = "http://test1.com:8011" + proxy_settings_of('test2.com', 7456) - it "should access the cache" do - repository.cache.expects(:retrieve).with(@uri) - repository.retrieve(@uri) - end + repository.http_proxy_host.should == "test1.com" + repository.http_proxy_port.should == 8011 end - describe 'http_proxy support' do - before :each do - ENV["http_proxy"] = nil - end - - after :each do - ENV["http_proxy"] = nil - end - - it "should support environment variable for port and host" do - ENV["http_proxy"] = "http://test.com:8011" - repository.http_proxy_host.should == "test.com" - repository.http_proxy_port.should == 8011 - end - - it "should support puppet configuration for port and host" do - ENV["http_proxy"] = nil - Puppet.settings.stubs(:[]).with(:http_proxy_host).returns('test.com') - Puppet.settings.stubs(:[]).with(:http_proxy_port).returns(7456) - - repository.http_proxy_port.should == 7456 - repository.http_proxy_host.should == "test.com" - end - - it "should use environment variable before puppet settings" do - ENV["http_proxy"] = "http://test1.com:8011" - Puppet.settings.stubs(:[]).with(:http_proxy_host).returns('test2.com') - Puppet.settings.stubs(:[]).with(:http_proxy_port).returns(7456) - - repository.http_proxy_host.should == "test1.com" - repository.http_proxy_port.should == 8011 - end + def proxy_settings_of(host, port) + Puppet.settings.stubs(:[]).with(:http_proxy_host).returns(host) + Puppet.settings.stubs(:[]).with(:http_proxy_port).returns(port) end end end diff --git a/spec/unit/forge_spec.rb b/spec/unit/forge_spec.rb index 95e47a03e..61d975bca 100644 --- a/spec/unit/forge_spec.rb +++ b/spec/unit/forge_spec.rb @@ -1,56 +1,48 @@ require 'spec_helper' require 'puppet/forge' require 'net/http' require 'puppet/module_tool' describe Puppet::Forge do - include PuppetSpec::Files - let(:response_body) do <<-EOF [ { "author": "puppetlabs", "name": "bacula", "tag_list": ["backup", "bacula"], "releases": [{"version": "0.0.1"}, {"version": "0.0.2"}], "full_name": "puppetlabs/bacula", "version": "0.0.2", "project_url": "http://github.com/puppetlabs/puppetlabs-bacula", "desc": "bacula" } ] EOF end - let(:response) { stub(:body => response_body, :code => '200') } - before do + def repository_responds_with(response) Puppet::Forge::Repository.any_instance.stubs(:make_http_request).returns(response) - Puppet::Forge::Repository.any_instance.stubs(:retrieve).returns("/tmp/foo") end - describe "the behavior of the search method" do - context "when there are matches for the search term" do - before do - Puppet::Forge::Repository.any_instance.stubs(:make_http_request).returns(response) - end + it "returns a list of matches from the forge when there are matches for the search term" do + response = stub(:body => response_body, :code => '200') + repository_responds_with(response) - it "should return a list of matches from the forge" do - Puppet::Forge.search('bacula').should == PSON.load(response_body) - end - end + Puppet::Forge.search('bacula').should == PSON.load(response_body) + end - context "when the connection to the forge fails" do - let(:response) { stub(:body => '{}', :code => '404') } + context "when the connection to the forge fails" do + before :each do + repository_responds_with(stub(:body => '{}', :code => '404')) + end - it "should raise an error for search" do - lambda { Puppet::Forge.search('bacula') }.should raise_error RuntimeError - end + it "raises an error for search" do + expect { Puppet::Forge.search('bacula') }.should raise_error RuntimeError + end - it "should raise an error for remote_dependency_info" do - lambda { Puppet::Forge.remote_dependency_info('puppetlabs', 'bacula', '0.0.1') }.should raise_error RuntimeError - end + it "raises an error for remote_dependency_info" do + expect { Puppet::Forge.remote_dependency_info('puppetlabs', 'bacula', '0.0.1') }.should raise_error RuntimeError end end - end