Class: Aspera::Ascp::Installation

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/aspera/ascp/installation.rb

Overview

Singleton that tells where to find ascp and other local resources (keys..) , using the “path(:name)” method. It is used by object : AgentDirect to find necessary resources By default it takes the first Aspera product found but the user can specify ascp location by calling: Installation.instance.use_ascp_from_product(product_name) or Installation.instance.ascp_path=“”

Instance Method Summary collapse

Instance Method Details

#ascp_infoObject



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/aspera/ascp/installation.rb', line 176

def ascp_info
  data = file_paths
  # read PATHs from ascp directly, and pvcl modules as well
  Open3.popen3(data['ascp'], '-DDL-') do |_stdin, _stdout, stderr, thread|
    last_line = ''
    while (line = stderr.gets)
      line.chomp!
      last_line = line
      case line
      when /^DBG Path ([^ ]+) (dir|file) +: (.*)$/
        data[Regexp.last_match(1)] = Regexp.last_match(3)
      when /^DBG Added module group:"(?<module>[^"]+)" name:"(?<scheme>[^"]+)", version:"(?<version>[^"]+)" interface:"(?<interface>[^"]+)"$/
        c = Regexp.last_match.named_captures.symbolize_keys
        data[c[:interface]] ||= {}
        data[c[:interface]][c[:module]] ||= []
        data[c[:interface]][c[:module]].push("#{c[:scheme]} v#{c[:version]}")
      when %r{^DBG License result \(/license/(\S+)\): (.+)$}
        data[Regexp.last_match(1)] = Regexp.last_match(2)
      when /^LOG (.+) version ([0-9.]+)$/
        data['product_name'] = Regexp.last_match(1)
        data['product_version'] = Regexp.last_match(2)
      when /^LOG Initializing FASP version ([^,]+),/
        data['ascp_version'] = Regexp.last_match(1)
      end
    end
    if !thread.value.exitstatus.eql?(1) && !data.key?('root')
      raise last_line
    end
  end
  # ascp's openssl directory
  ascp_file = data['ascp']
  File.binread(ascp_file).scan(/[\x20-\x7E]{4,}/) do |match|
    if (m = match.match(/OPENSSLDIR.*"(.*)"/))
      data['openssldir'] = m[1]
    end
  end if File.file?(ascp_file)
  # log is "-" no need to display
  data.delete('log')
  return data
end

#ascp_pathObject



51
52
53
# File 'lib/aspera/ascp/installation.rb', line 51

def ascp_path
  path(:ascp)
end

#ascp_path=(v) ⇒ Object

set ascp executable path



47
48
49
# File 'lib/aspera/ascp/installation.rb', line 47

def ascp_path=(v)
  @path_to_ascp = v
end

#aspera_token_ssh_key_pathsObject



154
155
156
# File 'lib/aspera/ascp/installation.rb', line 154

def aspera_token_ssh_key_paths
  return %i[ssh_private_dsa ssh_private_rsa].map{|i|Installation.instance.path(i)}
end

#check_or_create_sdk_file(filename, force: false, &block) ⇒ Object



104
105
106
# File 'lib/aspera/ascp/installation.rb', line 104

def check_or_create_sdk_file(filename, force: false, &block)
  return Environment.write_file_restricted(File.join(sdk_folder, filename), force: force, mode: 0o644, &block)
end

#file_pathsHash

Returns with key = file name (String), and value = path to file.

Returns:

  • (Hash)

    with key = file name (String), and value = path to file



93
94
95
96
97
98
99
100
101
102
# File 'lib/aspera/ascp/installation.rb', line 93

def file_paths
  return FILES.each_with_object({}) do |v, m|
    m[v.to_s] =
      begin
        path(v)
      rescue => e
        e.message
      end
  end
end

#get_ascp_version(exe_path) ⇒ Object

use in plugin ‘config`



159
160
161
# File 'lib/aspera/ascp/installation.rb', line 159

def get_ascp_version(exe_path)
  return get_exe_version(exe_path, '-A')
end

#get_exe_version(exe_path, vers_arg) ⇒ Object

Check that specified path is ascp and get version



164
165
166
167
168
169
170
171
172
173
174
# File 'lib/aspera/ascp/installation.rb', line 164

def get_exe_version(exe_path, vers_arg)
  raise 'ERROR: nil arg' if exe_path.nil?
  return nil unless File.exist?(exe_path)
  exe_version = nil
  cmd_out = %x("#{exe_path}" #{vers_arg})
  raise "An error occurred when testing #{ascp_filename}: #{cmd_out}" unless $CHILD_STATUS == 0
  # get version from ascp, only after full extract, as windows requires DLLs (SSL/TLS/etc...)
  m = cmd_out.match(/ version ([0-9.]+)/)
  exe_version = m[1] unless m.nil?
  return exe_version
end

#install_sdk(sdk_url) ⇒ Object

download aspera SDK or use local file extracts ascp binary for current system architecture

Returns:

  • ascp version (from execution)



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/aspera/ascp/installation.rb', line 220

def install_sdk(sdk_url)
  # SDK is organized by architecture, check this first, in case architecture is not supported
  arch_filter = "#{Environment.architecture}/"
  require 'zip'
  sdk_zip_path = File.join(Dir.tmpdir, 'sdk.zip')
  if sdk_url.start_with?('file:')
    # require specific file scheme: the path part is "relative", or absolute if there are 4 slash
    raise 'use format: file:///<path>' unless sdk_url.start_with?('file:///')
    sdk_zip_path = sdk_url.gsub(%r{^file:///}, '')
  else
    Aspera::Rest.new(base_url: sdk_url, redirect_max: 3).call(operation: 'GET', save_to_file: sdk_zip_path)
  end
  # rename old install
  if !Dir.empty?(sdk_folder)
    Log.log.warn('Previous install exists, renaming folder.')
    File.rename(sdk_folder, "#{sdk_folder}.#{Time.now.strftime('%Y%m%d%H%M%S')}")
    # TODO: delete old archives ?
  end
  # extract files from archive
  Zip::File.open(sdk_zip_path) do |zip_file|
    zip_file.each do |entry|
      # skip folder entries
      next if entry.name.end_with?('/')
      dest_folder = nil
      # binaries
      dest_folder = sdk_folder if entry.name.include?(arch_filter)
      # ruby adapters
      dest_folder = sdk_ruby_folder if entry.name.end_with?(EXT_RUBY_PROTOBUF)
      next if dest_folder.nil?
      File.open(File.join(dest_folder, File.basename(entry.name)), 'wb') do |output_stream|
        IO.copy_stream(entry.get_input_stream, output_stream)
      end
    end
  end
  File.unlink(sdk_zip_path) rescue nil # Windows may give error
  # ensure license file are generated so that ascp invocation for version works
  path(:aspera_license)
  path(:aspera_conf)
  ascp_file = Products.ascp_filename
  ascp_path = File.join(sdk_folder, ascp_file)
  raise "No #{ascp_file} found in SDK archive" unless File.exist?(ascp_path)
  Environment.restrict_file_access(ascp_path, mode: 0o755)
  Environment.restrict_file_access(ascp_path.gsub('ascp', 'ascp4'), mode: 0o755)
  ascp_version = get_ascp_version(ascp_path)
  trd_path = transferd_filepath
  Log.log.warn{"No #{trd_path} in SDK archive"} unless File.exist?(trd_path)
  Environment.restrict_file_access(trd_path, mode: 0o755) if File.exist?(trd_path)
  transferd_version = get_exe_version(trd_path, 'version')
  sdk_version = transferd_version || ascp_version
  File.write(File.join(sdk_folder, Products::INFO_META_FILE), "<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
  return sdk_version
end

#path(k) ⇒ Object

get path of one resource file of currently activated product keys and certs are generated locally… (they are well known values, arch. independent)



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
# File 'lib/aspera/ascp/installation.rb', line 110

def path(k)
  file_is_optional = false
  case k
  when :ascp, :ascp4
    use_ascp_from_product(FIRST_FOUND) if @path_to_ascp.nil?
    file = @path_to_ascp
    # NOTE: that there might be a .exe at the end
    file = file.gsub('ascp', 'ascp4') if k.eql?(:ascp4)
  when :transferd
    file = transferd_filepath
    file_is_optional = true
  when :ssh_private_dsa, :ssh_private_rsa
    # assume last 3 letters are type
    type = k.to_s[-3..-1].to_sym
    file = check_or_create_sdk_file("aspera_bypass_#{type}.pem") {DataRepository.instance.item(type)}
  when :aspera_license
    file = check_or_create_sdk_file('aspera-license') {DataRepository.instance.item(:license)}
  when :aspera_conf
    file = check_or_create_sdk_file('aspera.conf') {DEFAULT_ASPERA_CONF}
  when :fallback_certificate, :fallback_private_key
    file_key = File.join(sdk_folder, 'aspera_fallback_cert_private_key.pem')
    file_cert = File.join(sdk_folder, 'aspera_fallback_cert.pem')
    if !File.exist?(file_key) || !File.exist?(file_cert)
      require 'openssl'
      # create new self signed certificate for http fallback
      cert = OpenSSL::X509::Certificate.new
      private_key = OpenSSL::PKey::RSA.new(4096)
      WebServerSimple.fill_self_signed_cert(cert, private_key)
      check_or_create_sdk_file('aspera_fallback_cert_private_key.pem', force: true) {private_key.to_pem}
      check_or_create_sdk_file('aspera_fallback_cert.pem', force: true) {cert.to_pem}
    end
    file = k.eql?(:fallback_certificate) ? file_cert : file_key
  else Aspera.error_unexpected_value(k)
  end
  return nil if file_is_optional && !File.exist?(file)
  Aspera.assert(File.exist?(file)){"no such file: #{file}"}
  return file
end

#sdk_folderObject

Returns the path to folder where SDK is installed.

Returns:

  • the path to folder where SDK is installed



72
73
74
75
76
# File 'lib/aspera/ascp/installation.rb', line 72

def sdk_folder
  raise 'SDK path was ot initialized' if @sdk_dir.nil?
  FileUtils.mkdir_p(@sdk_dir)
  @sdk_dir
end

#sdk_folder=(v) ⇒ Object Also known as: folder=

location of SDK files



62
63
64
65
66
# File 'lib/aspera/ascp/installation.rb', line 62

def sdk_folder=(v)
  Log.log.debug{"sdk_folder=#{v}"}
  @sdk_dir = v
  sdk_folder
end

#sdk_ruby_folderObject



55
56
57
58
59
# File 'lib/aspera/ascp/installation.rb', line 55

def sdk_ruby_folder
  ruby_pb_folder = File.join(sdk_folder, RB_SDK_FOLDER)
  FileUtils.mkdir_p(ruby_pb_folder)
  return ruby_pb_folder
end

#ssh_cert_uuidObject

default bypass key phrase



150
151
152
# File 'lib/aspera/ascp/installation.rb', line 150

def ssh_cert_uuid
  return DataRepository.instance.item(:uuid)
end

#use_ascp_from_product(product_name) ⇒ Object

find ascp in named product (use value : FIRST_FOUND=‘FIRST’ to just use first one) or select one from Products.installed_products()



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/aspera/ascp/installation.rb', line 80

def use_ascp_from_product(product_name)
  if product_name.eql?(FIRST_FOUND)
    pl = Products.installed_products.first
    raise "no FASP installation found\nPlease check manual on how to install FASP." if pl.nil?
  else
    pl = Products.installed_products.find{|i|i[:name].eql?(product_name)}
    raise "no such product installed: #{product_name}" if pl.nil?
  end
  self.ascp_path = pl[:ascp_path]
  Log.log.debug{"ascp_path=#{@path_to_ascp}"}
end