Skip to content

Commit

Permalink
Merge pull request #2704 from mhashizume/FACT-636/main/cloud-proxy-po…
Browse files Browse the repository at this point in the history
…sitional-arg

Allow cloud metadata to ignore proxy settings
  • Loading branch information
joshcooper authored Apr 12, 2024
2 parents 39b48ad + 35963ca commit 5335371
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 155 deletions.
8 changes: 8 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ Style/Documentation:
- 'spec_integration/**/*'
- 'scripts/*'
- 'install.rb'

# While it would be preferable to use a keyword argument for the proxy setting in #get_request and #put_request, if we
# add keyword arguments to those methods Ruby < 3 misinterprets earlier positional arguments as a keyword arguments.
# This is because those positional arguments are hashes that use symbols as keys.
# TODO: revisit this after we drop Ruby < 3 support.
Style/OptionalBooleanParameter:
Exclude:
- 'lib/facter/util/resolvers/http.rb'
2 changes: 1 addition & 1 deletion lib/facter/resolvers/az.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def read_facts(fact_name)

def get_data_from(url)
headers = { Metadata: 'true' }
Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout })
Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout }, false)
end

def determine_session_timeout
Expand Down
2 changes: 1 addition & 1 deletion lib/facter/resolvers/ec2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def build_path_component(line)
def get_data_from(url)
headers = {}
headers['X-aws-ec2-metadata-token'] = v2_token if v2_token
Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout })
Facter::Util::Resolvers::Http.get_request(url, headers, { session: determine_session_timeout }, false)
end

def determine_session_timeout
Expand Down
2 changes: 1 addition & 1 deletion lib/facter/resolvers/gce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def read_facts(fact_name)
end

def query_for_metadata
gce_data = extract_to_hash(Facter::Util::Resolvers::Http.get_request(METADATA_URL, HEADERS))
gce_data = extract_to_hash(Facter::Util::Resolvers::Http.get_request(METADATA_URL, HEADERS, false))
parse_instance(gce_data)

gce_data.empty? ? nil : gce_data
Expand Down
51 changes: 31 additions & 20 deletions lib/facter/util/resolvers/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,37 @@ class << self
CONNECTION_TIMEOUT = 0.6
SESSION_TIMEOUT = 5

# Makes a GET http request and returns its response.
# Makes a GET HTTP request and returns its response.
#
# Params:
# url: String which contains the address to which the request will be made
# headers: Hash which contains the headers you need to add to your request.
# Default headers is an empty hash
# Example: { "Accept": 'application/json' }
# timeouts: Hash that includes the values for the session and connection timeouts.
# Example: { session: 2.4. connection: 5 }
#
# Return value:
# is a string with the response body if the response code is 200.
# If the response code is not 200, an empty string is returned.
def get_request(url, headers = {}, timeouts = {})
make_request(url, headers, timeouts, 'GET')
# @param url [String] the address to which the request will be made.
# @param headers [Hash] the headers you need to add to your request.
# Defaults to an empty hash.
# @param timeouts [Hash] Values for the session and connection
# timeouts.
# @param proxy [Boolean] Whether to use proxy settings when calling
# Net::HTTP.new. Defaults to true.
# @returns [String] the response body if the response code is 200.
# If the response code is not 200, an empty string is returned.
# @example
# get_request('https://example.com', { "Accept": 'application/json' }, { session: 2.4, connection: 5 })
def get_request(url, headers = {}, timeouts = {}, proxy = true)
make_request(url, headers, timeouts, 'GET', proxy)
end

def put_request(url, headers = {}, timeouts = {})
make_request(url, headers, timeouts, 'PUT')
# Makes a PUT HTTP request and returns its response
# @param (see #get_request)
# @return (see #get_request)
def put_request(url, headers = {}, timeouts = {}, proxy = true)
make_request(url, headers, timeouts, 'PUT', proxy)
end

private

def make_request(url, headers, timeouts, request_type)
def make_request(url, headers, timeouts, request_type, proxy)
require 'net/http'

uri = URI.parse(url)
http = http_obj(uri, timeouts)
http = http_obj(uri, timeouts, proxy)
request = request_obj(headers, uri, request_type)

# The Windows implementation of sockets does not respect net/http
Expand All @@ -56,8 +59,16 @@ def make_request(url, headers, timeouts, request_type)
''
end

def http_obj(parsed_url, timeouts)
http = Net::HTTP.new(parsed_url.host)
def http_obj(parsed_url, timeouts, proxy)
# If get_request or put_request are called and set proxy to false,
# manually set Net::HTTP.new's p_addr (proxy address) positional
# argument to nil to override anywhere else a proxy may be set
# (e.g. the http_proxy environment variable).
http = if proxy
Net::HTTP.new(parsed_url.host)
else
Net::HTTP.new(parsed_url.host, 80, nil)
end
http.read_timeout = timeouts[:session] || SESSION_TIMEOUT
http.open_timeout = timeouts[:connection] || CONNECTION_TIMEOUT

Expand Down
14 changes: 13 additions & 1 deletion spec/facter/resolvers/az_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

before do
allow(Facter::Util::Resolvers::Http).to receive(:get_request)
.with(uri, { Metadata: 'true' }, { session: 5 }).and_return(output)
.with(uri, { Metadata: 'true' }, { session: 5 }, false).and_return(output)
az.instance_variable_set(:@log, log_spy)
end

Expand All @@ -24,6 +24,18 @@
end
end

context "when a proxy is set with ENV['http_proxy']" do
before do
stub_const('ENV', { 'http_proxy' => 'http://example.com' })
end

let(:output) { '{"azEnvironment":"AzurePublicCloud"}' }

it 'returns az metadata' do
expect(az.resolve(:metadata)).to eq({ 'azEnvironment' => 'AzurePublicCloud' })
end
end

context 'when an exception is thrown' do
let(:output) { '' }

Expand Down
12 changes: 12 additions & 0 deletions spec/facter/resolvers/ec2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,16 @@
expect(ec2.resolve(:userdata)).to eql(expected_str)
end
end

context "when a proxy is set with ENV['http_proxy']" do
before do
stub_const('ENV', { 'http_proxy' => 'http://example.com' })
stub_request(:put, token_uri).to_return(status: 200, body: token)
end

let(:headers) { { 'Accept' => '*/*' } }
let(:token) { 'v2_token' }

it_behaves_like 'ec2'
end
end
Loading

0 comments on commit 5335371

Please sign in to comment.