Class: Aspera::Fasp::Parameters

Inherits:
Object
  • Object
show all
Defined in:
lib/aspera/fasp/parameters.rb

Overview

translate transfer specification to ascp parameter list

Constant Summary collapse

SUPPORTED_AGENTS_SHORT =

Short names of columns in manual

SUPPORTED_AGENTS.map{|a|a.to_s[0].to_sym}
SUPPORTED_OPTIONS =
i[ascp_args wss].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(job_spec, options) ⇒ Parameters

Returns a new instance of Parameters.

Parameters:

  • options (Hash)

    key: :wss: bool, :ascp_args: array of strings



121
122
123
124
125
126
127
128
129
130
# File 'lib/aspera/fasp/parameters.rb', line 121

def initialize(job_spec, options)
  @job_spec = job_spec
  # check necessary options
  raise 'Internal: missing options' unless (SUPPORTED_OPTIONS - options.keys).empty?
  @options = SUPPORTED_OPTIONS.each_with_object({}){|o, h| h[o] = options[o]}
  Log.dump(:options, @options)
  raise 'ascp args must be an Array' unless @options[:ascp_args].is_a?(Array)
  raise 'ascp args must be an Array of String' if @options[:ascp_args].any?{|i|!i.is_a?(String)}
  @builder = Aspera::CommandLineBuilder.new(@job_spec, self.class.description)
end

Class Attribute Details

.file_list_folderObject

static methods



117
118
119
# File 'lib/aspera/fasp/parameters.rb', line 117

def file_list_folder
  @file_list_folder
end

Class Method Details

.convert_base64(v) ⇒ Object

special encoding methods used in YAML (key: :convert)



96
# File 'lib/aspera/fasp/parameters.rb', line 96

def convert_base64(v); Base64.strict_encode64(v); end

.convert_json64(v) ⇒ Object

special encoding methods used in YAML (key: :convert)



93
# File 'lib/aspera/fasp/parameters.rb', line 93

def convert_json64(v); Base64.strict_encode64(JSON.generate(v)); end

.convert_remove_hyphen(v) ⇒ Object

special encoding methods used in YAML (key: :convert)



90
# File 'lib/aspera/fasp/parameters.rb', line 90

def convert_remove_hyphen(v); v.tr('-', ''); end

.descriptionObject

Returns normalized description of transfer spec parameters, direct from yaml.

Returns:

  • normalized description of transfer spec parameters, direct from yaml



34
35
36
37
38
39
40
41
# File 'lib/aspera/fasp/parameters.rb', line 34

def description
  if @param_description_cache.nil?
    # config file in same folder with same name as this source
    description_from_yaml = YAML.load_file("#{__FILE__[0..-3]}yaml")
    @param_description_cache = Aspera::CommandLineBuilder.normalize_description(description_from_yaml)
  end
  return @param_description_cache
end

.man_tableObject

Returns a table suitable to display in manual.

Parameters:

  • to_text (bool)

    replace HTML entities with text equivalent

Returns:

  • a table suitable to display in manual



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
# File 'lib/aspera/fasp/parameters.rb', line 45

def man_table
  result = []
  description.each do |name, options|
    param = {name: name, type: [options[:accepted_types]].flatten.join(','), description: options[:desc]}
    # add flags for supported agents in doc
    SUPPORTED_AGENTS.each do |a|
      param[a.to_s[0].to_sym] = options[:agents].nil? || options[:agents].include?(a) ? 'Y' : ''
    end
    # only keep lines that are usable in supported agents
    next if SUPPORTED_AGENTS_SHORT.inject(true){|m, j|m && param[j].empty?}
    param[:cli] =
      case options[:cli][:type]
      when :envvar then 'env:' + options[:cli][:variable]
      when :opt_without_arg then options[:cli][:switch]
      when :opt_with_arg
        values = if options.key?(:enum)
          ['enum']
        elsif options[:accepted_types].is_a?(Array)
          options[:accepted_types]
        elsif !options[:accepted_types].nil?
          [options[:accepted_types]]
        else
          raise "error: #{param}"
        end.map{|n|"{#{n}}"}.join('|')
        conv = options[:cli].key?(:convert) ? '(conversion)' : ''
        "#{options[:cli][:switch]} #{conv}#{values}"
      else ''
      end
    if options.key?(:enum)
      param[:description] += "\nAllowed values: #{options[:enum].join(', ')}"
    end
    # replace "solidus" HTML entity with its text value
    param[:description] = param[:description].gsub('/', '\\')
    result.push(param)
  end
  return result.sort do |a, b|
    if a[:name].start_with?('EX_').eql?(b[:name].start_with?('EX_'))
      a[:name] <=> b[:name]
    else
      b[:name] <=> a[:name]
    end
  end
end

.ts_has_ascp_file_list(ts, ascp_args) ⇒ Object

file list is provided directly with ascp arguments



99
100
101
102
103
104
105
106
# File 'lib/aspera/fasp/parameters.rb', line 99

def ts_has_ascp_file_list(ts, ascp_args)
  # it can also be option transfer_info
  ascp_args = ascp_args['ascp_args'] if ascp_args.is_a?(Hash)
  (ts['EX_ascp_args'].is_a?(Array) && ts['EX_ascp_args'].any?{|i|FILE_LIST_OPTIONS.include?(i)}) ||
    (ascp_args.is_a?(Array) && ascp_args.any?{|i|FILE_LIST_OPTIONS.include?(i)}) ||
    ts.key?('EX_file_list') ||
    ts.key?('EX_file_pair_list')
end

Instance Method Details

#ascp_argsObject

translate transfer spec to env vars and command line arguments for ascp NOTE: parameters starting with “EX_” (extended) are not standard



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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/aspera/fasp/parameters.rb', line 181

def ascp_args
  env_args = {
    args:         [],
    env:          {},
    ascp_version: :ascp
  }
  # some ssh credentials are required to avoid interactive password input
  if !@job_spec.key?('remote_password') &&
      !@job_spec.key?('ssh_private_key') &&
      !@job_spec.key?('EX_ssh_key_paths')
    raise Fasp::Error, 'required: password or ssh key (value or path)'
  end

  # special cases
  @job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?

  # use web socket session initiation ?
  if @builder.read_param('wss_enabled') && (@options[:wss] || !@job_spec.key?('fasp_port'))
    # by default use web socket session if available, unless removed by user
    @builder.add_command_line_options(['--ws-connect'])
    # TODO: option to give order ssh,ws (legacy http is implied bu ssh)
    # This will need to be cleaned up in aspera core
    @job_spec['ssh_port'] = @builder.read_param('wss_port')
    @job_spec.delete('fasp_port')
    @job_spec.delete('EX_ssh_key_paths')
    @job_spec.delete('sshfp')
    # set location for CA bundle to be the one of Ruby, see env var SSL_CERT_FILE / SSL_CERT_DIR
    @job_spec['EX_ssh_key_paths'] = [OpenSSL::X509::DEFAULT_CERT_FILE]
    Log.log.debug('CA certs: EX_ssh_key_paths <- DEFAULT_CERT_FILE from openssl')
  else
    # remove unused parameter (avoid warning)
    @job_spec.delete('wss_port')
  end

  # process parameters as specified in table
  @builder.process_params

  # symbol must be index of Installation.paths
  if @builder.read_param('use_ascp4')
    env_args[:ascp_version] = :ascp4
  else
    env_args[:ascp_version] = :ascp
    # destination will be base64 encoded, put before path arguments
    @builder.add_command_line_options(['--dest64'])
  end
  # get list of files to transfer and build arg for ascp
  process_file_list
  # optional args, at the end to override previous ones (to allow override)
  @builder.add_command_line_options(@builder.read_param('EX_ascp_args'))
  @builder.add_command_line_options(@options[:ascp_args])
  # process destination folder
  destination_folder = @builder.read_param('destination_root') || '/'
  # ascp4 does not support base64 encoding of destination
  destination_folder = Base64.strict_encode64(destination_folder) unless env_args[:ascp_version].eql?(:ascp4)
  # destination MUST be last command line argument to ascp
  @builder.add_command_line_options([destination_folder])

  @builder.add_env_args(env_args[:env], env_args[:args])

  return env_args
end

#process_file_listObject



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
# File 'lib/aspera/fasp/parameters.rb', line 132

def process_file_list
  # is the file list provided through EX_ parameters?
  ascp_file_list_provided = self.class.ts_has_ascp_file_list(@job_spec, @options[:ascp_args])
  # set if paths is mandatory in ts
  @builder.params_definition['paths'][:mandatory] = !@job_spec.key?('keepalive') && !ascp_file_list_provided
  # get paths in transfer spec (after setting if it is mandatory)
  ts_paths_array = @builder.read_param('paths')
  if ascp_file_list_provided && !ts_paths_array.nil?
    raise 'file list provided both in transfer spec and ascp file list. Remove one of them.'
  end
  # option 1: EX_file_list
  file_list_file = @builder.read_param('EX_file_list')
  if file_list_file.nil?
    # option 2: EX_file_pair_list
    file_list_file = @builder.read_param('EX_file_pair_list')
    if !file_list_file.nil?
      option = '--file-pair-list'
    elsif !ts_paths_array.nil?
      # option 3: in TS, it is an array
      if self.class.file_list_folder.nil?
        # not safe for special characters ? (maybe not, depends on OS)
        Log.log.debug('placing source file list on command line (no file list file)')
        @builder.add_command_line_options(ts_paths_array.map{|i|i['source']})
      else
        raise "All elements of paths must have a 'source' key" unless ts_paths_array.all?{|i|i.key?('source')}
        is_pair_list = ts_paths_array.any?{|i|i.key?('destination')}
        raise "All elements of paths must be consistent with 'destination' key" if is_pair_list && !ts_paths_array.all?{|i|i.key?('destination')}
        # safer option: generate a file list file if there is storage defined for it
        # if there is one destination in paths, then use file-pair-list
        if is_pair_list
          option = '--file-pair-list'
          lines = ts_paths_array.each_with_object([]){|e, m|m.push(e['source'], e['destination'] || e['source']); }
        else
          option = '--file-list'
          lines = ts_paths_array.map{|i|i['source']}
        end
        file_list_file = Aspera::TempFileManager.instance.new_file_path_in_folder(self.class.file_list_folder)
        File.write(file_list_file, lines.join("\n"))
        Log.log.debug{"#{option}=\n#{File.read(file_list_file)}".red}
      end
    end
  else
    option = '--file-list'
  end
  @builder.add_command_line_options(["#{option}=#{file_list_file}"]) unless option.nil?
end