-
-
Notifications
You must be signed in to change notification settings - Fork 176
/
Copy pathutil.rb
196 lines (173 loc) · 6.71 KB
/
util.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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 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, "Previous error(s) resulted in Puppet being unable to retrieve information from environment #{Puppet['environment']} source(s) #{url}'\nMost probable cause is file not found." unless status
File.open(filepath, 'wb') { |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, Puppet::HTTP::RouteError
# 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
@osfamily = Facter.value(:osfamily)
if @osfamily == 'windows'
# Get the 'ssl_trust_store' setting from the puppet agent
Puppet.settings.preferred_run_mode = :agent
puppet_ssl_trust_store = Puppet.settings.to_h[:ssl_trust_store].value || nil
# Prefer 'ssl_trust_store' from the puppet agent, then SSL_CERT_FILE from the
# environment, and the bundled pem file as a last resort
@ssl_trust_store = if puppet_ssl_trust_store && File.exist?(puppet_ssl_trust_store)
puppet_ssl_trust_store
elsif ENV.key?('SSL_CERT_FILE')
ENV['SSL_CERT_FILE']
else
File.expand_path(File.join(__FILE__, '..', 'cacert.pem'))
end
end
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
http_opts[:ca_file] = @ssl_trust_store if @osfamily == 'windows'
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