aspera-cli 4.10.0 → 4.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +19 -0
  4. data/CHANGELOG.md +528 -0
  5. data/CONTRIBUTING.md +143 -0
  6. data/README.md +977 -589
  7. data/bin/ascli +4 -4
  8. data/bin/asession +12 -12
  9. data/docs/test_env.conf +29 -19
  10. data/examples/aoc.rb +6 -6
  11. data/examples/dascli +18 -16
  12. data/examples/faspex4.rb +15 -15
  13. data/examples/node.rb +12 -12
  14. data/examples/proxy.pac +2 -2
  15. data/examples/server.rb +12 -12
  16. data/lib/aspera/aoc.rb +344 -272
  17. data/lib/aspera/ascmd.rb +56 -54
  18. data/lib/aspera/ats_api.rb +4 -4
  19. data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
  20. data/lib/aspera/cli/extended_value.rb +9 -9
  21. data/lib/aspera/cli/{formater.rb → formatter.rb} +69 -69
  22. data/lib/aspera/cli/listener/line_dump.rb +1 -1
  23. data/lib/aspera/cli/listener/logger.rb +1 -1
  24. data/lib/aspera/cli/listener/progress.rb +5 -6
  25. data/lib/aspera/cli/listener/progress_multi.rb +16 -21
  26. data/lib/aspera/cli/main.rb +72 -73
  27. data/lib/aspera/cli/manager.rb +112 -112
  28. data/lib/aspera/cli/plugin.rb +68 -48
  29. data/lib/aspera/cli/plugins/alee.rb +4 -4
  30. data/lib/aspera/cli/plugins/aoc.rb +322 -720
  31. data/lib/aspera/cli/plugins/ats.rb +50 -52
  32. data/lib/aspera/cli/plugins/bss.rb +10 -10
  33. data/lib/aspera/cli/plugins/config.rb +514 -410
  34. data/lib/aspera/cli/plugins/console.rb +12 -12
  35. data/lib/aspera/cli/plugins/cos.rb +18 -20
  36. data/lib/aspera/cli/plugins/faspex.rb +134 -136
  37. data/lib/aspera/cli/plugins/faspex5.rb +235 -70
  38. data/lib/aspera/cli/plugins/node.rb +378 -309
  39. data/lib/aspera/cli/plugins/orchestrator.rb +52 -49
  40. data/lib/aspera/cli/plugins/preview.rb +129 -120
  41. data/lib/aspera/cli/plugins/server.rb +137 -83
  42. data/lib/aspera/cli/plugins/shares.rb +77 -52
  43. data/lib/aspera/cli/plugins/sync.rb +13 -33
  44. data/lib/aspera/cli/transfer_agent.rb +61 -61
  45. data/lib/aspera/cli/version.rb +2 -1
  46. data/lib/aspera/colors.rb +3 -3
  47. data/lib/aspera/command_line_builder.rb +78 -74
  48. data/lib/aspera/cos_node.rb +31 -29
  49. data/lib/aspera/data_repository.rb +1 -1
  50. data/lib/aspera/environment.rb +30 -28
  51. data/lib/aspera/fasp/agent_base.rb +17 -15
  52. data/lib/aspera/fasp/agent_connect.rb +34 -32
  53. data/lib/aspera/fasp/agent_direct.rb +70 -73
  54. data/lib/aspera/fasp/agent_httpgw.rb +79 -74
  55. data/lib/aspera/fasp/agent_node.rb +26 -26
  56. data/lib/aspera/fasp/agent_trsdk.rb +20 -20
  57. data/lib/aspera/fasp/error.rb +3 -2
  58. data/lib/aspera/fasp/error_info.rb +11 -8
  59. data/lib/aspera/fasp/installation.rb +80 -80
  60. data/lib/aspera/fasp/listener.rb +2 -2
  61. data/lib/aspera/fasp/parameters.rb +103 -92
  62. data/lib/aspera/fasp/parameters.yaml +313 -214
  63. data/lib/aspera/fasp/resume_policy.rb +10 -10
  64. data/lib/aspera/fasp/transfer_spec.rb +22 -2
  65. data/lib/aspera/fasp/uri.rb +7 -7
  66. data/lib/aspera/faspex_gw.rb +80 -159
  67. data/lib/aspera/faspex_postproc.rb +77 -0
  68. data/lib/aspera/hash_ext.rb +3 -3
  69. data/lib/aspera/id_generator.rb +5 -5
  70. data/lib/aspera/keychain/encrypted_hash.rb +23 -28
  71. data/lib/aspera/keychain/macos_security.rb +21 -20
  72. data/lib/aspera/log.rb +13 -13
  73. data/lib/aspera/nagios.rb +24 -23
  74. data/lib/aspera/node.rb +217 -38
  75. data/lib/aspera/oauth.rb +78 -74
  76. data/lib/aspera/open_application.rb +19 -11
  77. data/lib/aspera/persistency_action_once.rb +4 -4
  78. data/lib/aspera/persistency_folder.rb +13 -13
  79. data/lib/aspera/preview/file_types.rb +8 -8
  80. data/lib/aspera/preview/generator.rb +67 -67
  81. data/lib/aspera/preview/utils.rb +27 -27
  82. data/lib/aspera/proxy_auto_config.js +63 -63
  83. data/lib/aspera/proxy_auto_config.rb +19 -19
  84. data/lib/aspera/rest.rb +65 -67
  85. data/lib/aspera/rest_call_error.rb +2 -1
  86. data/lib/aspera/rest_error_analyzer.rb +22 -21
  87. data/lib/aspera/rest_errors_aspera.rb +16 -16
  88. data/lib/aspera/secret_hider.rb +17 -14
  89. data/lib/aspera/ssh.rb +15 -14
  90. data/lib/aspera/sync.rb +177 -62
  91. data/lib/aspera/temp_file_manager.rb +2 -2
  92. data/lib/aspera/uri_reader.rb +4 -4
  93. data/lib/aspera/web_auth.rb +13 -64
  94. data/lib/aspera/web_server_simple.rb +76 -0
  95. data.tar.gz.sig +0 -0
  96. metadata +11 -6
  97. metadata.gz.sig +0 -0
@@ -15,7 +15,7 @@ module Aspera
15
15
  FILE_LIST_FROM_ARGS = '@args'
16
16
  # special value for --sources : read file list from transfer spec (--ts)
17
17
  FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
18
- FILE_LIST_OPTIONS=[FILE_LIST_FROM_ARGS,FILE_LIST_FROM_TRANSFER_SPEC,'Array'].freeze
18
+ FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
19
19
  DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
20
20
  From: <%=from_name%> <<%=from_email%>>
21
21
  To: <<%=to%>>
@@ -25,8 +25,10 @@ module Aspera
25
25
 
26
26
  <%=ts.to_yaml%>
27
27
  END_OF_TEMPLATE
28
- #% (formating bug in eclipse)
29
- private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:FILE_LIST_OPTIONS,
28
+ # % (formatting bug in eclipse)
29
+ private_constant :FILE_LIST_FROM_ARGS,
30
+ :FILE_LIST_FROM_TRANSFER_SPEC,
31
+ :FILE_LIST_OPTIONS,
30
32
  :DEFAULT_TRANSFER_NOTIF_TMPL
31
33
  TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
32
34
 
@@ -41,7 +43,7 @@ module Aspera
41
43
  end
42
44
 
43
45
  # @param env external objects: option manager, config file manager
44
- def initialize(opt_mgr,config)
46
+ def initialize(opt_mgr, config)
45
47
  @opt_mgr = opt_mgr
46
48
  @config = config
47
49
  # command line can override transfer spec
@@ -51,18 +53,17 @@ module Aspera
51
53
  @progress_listener = Listener::ProgressMulti.new
52
54
  # source/destination pair, like "paths" of transfer spec
53
55
  @transfer_paths = nil
54
- @opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
55
- @opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
56
- @opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume)}")
57
- @opt_mgr.add_opt_simple(:to_folder,'destination folder for transfered files')
58
- @opt_mgr.add_opt_simple(:sources,"how list of transfered files is provided (#{FILE_LIST_OPTIONS.join(',')})")
59
- @opt_mgr.add_opt_list(:src_type,%i[list pair],'type of file list')
60
- @opt_mgr.add_opt_list(:transfer,TRANSFER_AGENTS,'type of transfer agent')
61
- @opt_mgr.add_opt_simple(:transfer_info,'parameters for transfer agent')
62
- @opt_mgr.add_opt_list(:progress,%i[none native multi],'type of progress bar')
63
- @opt_mgr.set_option(:transfer,:direct)
64
- @opt_mgr.set_option(:src_type,:list)
65
- @opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
56
+ @opt_mgr.set_obj_attr(:ts, self, :option_transfer_spec)
57
+ @opt_mgr.add_opt_simple(:ts, "Override transfer spec values (Hash, e.g. use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
58
+ @opt_mgr.add_opt_simple(:to_folder, 'Destination folder for transferred files')
59
+ @opt_mgr.add_opt_simple(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
60
+ @opt_mgr.add_opt_list(:src_type, %i[list pair], 'Type of file list')
61
+ @opt_mgr.add_opt_list(:transfer, TRANSFER_AGENTS, 'Type of transfer agent')
62
+ @opt_mgr.add_opt_simple(:transfer_info, 'Parameters for transfer agent')
63
+ @opt_mgr.add_opt_list(:progress, %i[none native multi], 'Type of progress bar')
64
+ @opt_mgr.set_option(:transfer, :direct)
65
+ @opt_mgr.set_option(:src_type, :list)
66
+ @opt_mgr.set_option(:progress, :native) # use native ascp progress bar as it is more reliable
66
67
  @opt_mgr.parse_options!
67
68
  end
68
69
 
@@ -77,8 +78,8 @@ module Aspera
77
78
  @agent = instance
78
79
  @agent.add_listener(Listener::Logger.new)
79
80
  # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
80
- if @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:multi) ||
81
- (@opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
81
+ if @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:multi) ||
82
+ (@opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
82
83
  @agent.add_listener(@progress_listener)
83
84
  end
84
85
  end
@@ -86,20 +87,20 @@ module Aspera
86
87
  # analyze options and create new agent if not already created or set
87
88
  def set_agent_by_options
88
89
  return nil unless @agent.nil?
89
- agent_type = @opt_mgr.get_option(:transfer,is_type: :mandatory)
90
+ agent_type = @opt_mgr.get_option(:transfer, is_type: :mandatory)
90
91
  # agent plugin is loaded on demand to avoid loading unnecessary dependencies
91
92
  require "aspera/fasp/agent_#{agent_type}"
92
93
  agent_options = @opt_mgr.get_option(:transfer_info)
93
- raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
94
- 'use either @json:<json> or @preset:<parameter set name>' unless [Hash,NilClass].include?(agent_options.class)
94
+ raise CliBadArgument, "the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
95
+ 'use either @json:<json> or @preset:<parameter set name>' unless [Hash, NilClass].include?(agent_options.class)
95
96
  # special case
96
97
  if agent_type.eql?(:node) && agent_options.nil?
97
98
  param_set_name = @config.get_plugin_default_config_name(:node)
98
- raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.tr('_','-')}" if param_set_name.nil?
99
+ raise CliBadArgument, "No default node configured, Please specify --#{:transfer_info.to_s.tr('_', '-')}" if param_set_name.nil?
99
100
  agent_options = @config.preset_by_name(param_set_name)
100
101
  end
101
102
  # special case
102
- if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native)
103
+ if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress, is_type: :mandatory).eql?(:native)
103
104
  agent_options = {} if agent_options.nil?
104
105
  agent_options[:quiet] = false
105
106
  end
@@ -128,105 +129,104 @@ module Aspera
128
129
  return dest_folder
129
130
  end
130
131
 
131
- # This is how the list of files to be transfered is specified
132
+ def source_list
133
+ return ts_source_paths.map do |i|
134
+ i['source']
135
+ end
136
+ end
137
+
138
+ # This is how the list of files to be transferred is specified
132
139
  # get paths suitable for transfer spec from command line
133
- # @return {source: (mandatory), destination: (optional)}
140
+ # @return [Hash] {source: (mandatory), destination: (optional)}
134
141
  # computation is done only once, cache is kept in @transfer_paths
135
142
  def ts_source_paths
136
143
  # return cache if set
137
144
  return @transfer_paths unless @transfer_paths.nil?
138
145
  # start with lower priority : get paths from transfer spec on command line
139
- @transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
146
+ @transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.key?('paths')
140
147
  # is there a source list option ?
141
148
  file_list = @opt_mgr.get_option(:sources)
142
149
  case file_list
143
- when nil,FILE_LIST_FROM_ARGS
150
+ when nil, FILE_LIST_FROM_ARGS
144
151
  Log.log.debug('getting file list as parameters')
145
152
  # get remaining arguments
146
- file_list = @opt_mgr.get_next_argument('source file list',expected: :multiple)
147
- raise CliBadArgument,'specify at least one file on command line or use '\
153
+ file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
154
+ raise CliBadArgument, 'specify at least one file on command line or use '\
148
155
  "--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
149
156
  when FILE_LIST_FROM_TRANSFER_SPEC
150
157
  Log.log.debug('assume list provided in transfer spec')
151
158
  special_case_direct_with_list =
152
- @opt_mgr.get_option(:transfer,is_type: :mandatory).eql?(:direct) &&
153
- Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline)
154
- raise CliBadArgument,'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
159
+ @opt_mgr.get_option(:transfer, is_type: :mandatory).eql?(:direct) &&
160
+ Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline, @opt_mgr.get_option(:transfer_info))
161
+ raise CliBadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
155
162
  # here we assume check of sources is made in transfer agent
156
163
  return @transfer_paths
157
164
  when Array
158
165
  Log.log.debug('getting file list as extended value')
159
- raise CliBadArgument,'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
166
+ raise CliBadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
160
167
  else
161
- raise CliBadArgument,"sources must be a Array, not #{file_list.class}"
168
+ raise CliBadArgument, "sources must be a Array, not #{file_list.class}"
162
169
  end
163
170
  # here, file_list is an Array or String
164
171
  if !@transfer_paths.nil?
165
172
  Log.log.warn('--sources overrides paths from --ts')
166
173
  end
167
- case @opt_mgr.get_option(:src_type,is_type: :mandatory)
174
+ case @opt_mgr.get_option(:src_type, is_type: :mandatory)
168
175
  when :list
169
176
  # when providing a list, just specify source
170
177
  @transfer_paths = file_list.map{|i|{'source' => i}}
171
178
  when :pair
172
- raise CliBadArgument,"When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
173
- @transfer_paths = file_list.each_slice(2).to_a.map{|s,d|{'source' => s,'destination' => d}}
179
+ raise CliBadArgument, "When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
180
+ @transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
174
181
  else raise 'Unsupported src_type'
175
182
  end
176
- Log.log.debug("paths=#{@transfer_paths}")
183
+ Log.log.debug{"paths=#{@transfer_paths}"}
177
184
  return @transfer_paths
178
185
  end
179
186
 
180
187
  # start a transfer and wait for completion, plugins shall use this method
181
- # @param transfer_spec
182
- # @param tr_opts specific options for the transfer_agent
183
- # tr_opts[:src] specifies how destination_root is set (how transfer spec was generated)
184
- # other options are carried to specific agent
185
- def start(transfer_spec,tr_opts)
188
+ # @param transfer_spec [Hash]
189
+ # @param rest_token [Rest] if oauth token regeneration supported
190
+ def start(transfer_spec, rest_token: nil)
186
191
  # check parameters
187
192
  raise 'transfer_spec must be hash' unless transfer_spec.is_a?(Hash)
188
- raise 'tr_opts must be hash' unless tr_opts.is_a?(Hash)
189
193
  # process :src option
190
194
  case transfer_spec['direction']
191
195
  when Fasp::TransferSpec::DIRECTION_RECEIVE
192
196
  # init default if required in any case
193
197
  @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
194
198
  when Fasp::TransferSpec::DIRECTION_SEND
195
- case tr_opts[:src]
196
- when :direct
197
- # init default if required
198
- @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
199
- when :node_gen3
199
+ if transfer_spec.dig('tags', 'aspera', 'node', 'access_key')
200
+ # gen4
201
+ @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.key?('destination_root_id')
202
+ elsif transfer_spec.key?('token')
203
+ # gen3
200
204
  # in that case, destination is set in return by application (API/upload_setup)
201
205
  # but to_folder was used in initial API call
202
206
  @transfer_spec_cmdline.delete('destination_root')
203
- when :node_gen4
204
- @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
205
207
  else
206
- raise StandardError,"InternalError: unsupported value: #{tr_opts[:src]}"
208
+ # init default if required
209
+ @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
207
210
  end
208
211
  end
209
-
210
- # only used here
211
- tr_opts.delete(:src)
212
-
213
212
  # update command line paths, unless destination already has one
214
213
  @transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
215
-
216
214
  transfer_spec.merge!(@transfer_spec_cmdline)
215
+ # remove values that are nil (user wants to delete)
216
+ transfer_spec.delete_if { |_key, value| value.nil? }
217
217
  # create transfer agent
218
218
  set_agent_by_options
219
- Log.log.debug("transfer agent is a #{@agent.class}")
220
- @agent.start_transfer(transfer_spec,tr_opts)
219
+ Log.log.debug{"transfer agent is a #{@agent.class}"}
220
+ @agent.start_transfer(transfer_spec, token_regenerator: rest_token)
221
221
  # list of : :success or error message
222
222
  result = @agent.wait_for_transfers_completion
223
223
  @progress_listener.reset
224
224
  Fasp::AgentBase.validate_status_list(result)
225
- send_email_transfer_notification(transfer_spec,result)
225
+ send_email_transfer_notification(transfer_spec, result)
226
226
  return result
227
227
  end
228
228
 
229
- def send_email_transfer_notification(transfer_spec,statuses)
229
+ def send_email_transfer_notification(transfer_spec, statuses)
230
230
  return if @opt_mgr.get_option(:notif_to).nil?
231
231
  global_status = self.class.session_status(statuses)
232
232
  email_vars = {
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Aspera
4
4
  module Cli
5
- VERSION = '4.10.0'
5
+ # for beta add extension : .beta1
6
+ VERSION = '4.12.0'
6
7
  end
7
8
  end
data/lib/aspera/colors.rb CHANGED
@@ -5,7 +5,7 @@ class String
5
5
  class << self
6
6
  private
7
7
 
8
- def vtcmd(code);"\e[#{code}m";end
8
+ def vtcmd(code); "\e[#{code}m"; end
9
9
  end
10
10
  # see https://en.wikipedia.org/wiki/ANSI_escape_code
11
11
  # symbol is the method name added to String
@@ -35,11 +35,11 @@ class String
35
35
  }.freeze
36
36
  private_constant :VTSTYLES
37
37
  # defines methods to String, one per entry in VTSTYLES
38
- VTSTYLES.each do |name,code|
38
+ VTSTYLES.each do |name, code|
39
39
  if $stderr.tty?
40
40
  begin_seq = vtcmd(code)
41
41
  end_code = 0 # by default reset all
42
- if code <= 7 then 20 + code
42
+ if code <= 7 then code + 20
43
43
  elsif code <= 37 then 39
44
44
  elsif code <= 47 then 49
45
45
  end
@@ -6,9 +6,15 @@ module Aspera
6
6
  # process_param is called repeatedly with all known parameters
7
7
  # add_env_args is called to get resulting param list and env var (also checks that all params were used)
8
8
  class CommandLineBuilder
9
- # parameter with onne of those tags is an ascp command line option with --
10
- CLTYPE_OPTIONS=%i[opt_without_arg opt_with_arg].freeze
11
- class<<self
9
+ # parameter with one of those tags is a command line option with --
10
+ CLI_OPTION_TYPE_SWITCH = %i[opt_without_arg opt_with_arg].freeze
11
+ CLI_OPTION_TYPES = %i[special ignore envvar].concat(CLI_OPTION_TYPE_SWITCH).freeze
12
+ OPTIONS_KEYS = %i[desc accepted_types default enum agents required cli ts].freeze
13
+ CLI_KEYS = %i[type switch convert variable].freeze
14
+
15
+ private_constant :CLI_OPTION_TYPE_SWITCH, :OPTIONS_KEYS, :CLI_KEYS
16
+
17
+ class << self
12
18
  # transform yes/no to true/false
13
19
  def yes_to_true(value)
14
20
  case value
@@ -19,40 +25,37 @@ module Aspera
19
25
  end
20
26
 
21
27
  # Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
22
- def normalize_description(d)
23
- d.each do |param_name,options|
24
- raise "Expecting Hash, but have #{options.class} in #{param_name}" unless options.is_a?(Hash)
25
- #options[:accepted_types]=:bool if options[:cltype].eql?(:envvar) and !options.has_key?(:accepted_types)
26
- # by default : not mandatory
28
+ def normalize_description(full_description)
29
+ full_description.each do |name, options|
30
+ raise "Expecting Hash, but have #{options.class} in #{name}" unless options.is_a?(Hash)
31
+ unsupported_keys = options.keys - OPTIONS_KEYS
32
+ raise "Unsupported definition keys: #{unsupported_keys}" unless unsupported_keys.empty?
33
+ raise "Missing key: cli for #{name}" unless options.key?(:cli)
34
+ raise 'Key: cli must be Hash' unless options[:cli].is_a?(Hash)
35
+ raise 'Missing key: cli.type' unless options[:cli].key?(:type)
36
+ raise "Unsupported processing type for #{name}: #{options[:cli][:type]}" unless CLI_OPTION_TYPES.include?(options[:cli][:type])
37
+ # by default : optional
27
38
  options[:mandatory] ||= false
28
39
  options[:desc] ||= ''
40
+ cli = options[:cli]
41
+ unsupported_cli_keys = cli.keys - CLI_KEYS
42
+ raise "Unsupported cli keys: #{unsupported_cli_keys}" unless unsupported_cli_keys.empty?
29
43
  # by default : string, unless it's without arg
30
- if !options.has_key?(:accepted_types)
31
- options[:accepted_types] = options[:cltype].eql?(:opt_without_arg) ? :bool : :string
32
- end
44
+ options[:accepted_types] ||= options[:cli][:type].eql?(:opt_without_arg) ? :bool : :string
33
45
  # single type is placed in array
34
46
  options[:accepted_types] = [options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
35
47
  # add default switch name if not present
36
- if !options.has_key?(:clswitch) && options.has_key?(:cltype) && CLTYPE_OPTIONS.include?(options[:cltype])
37
- options[:clswitch] = '--' + param_name.to_s.tr('_','-')
48
+ if !cli.key?(:switch) && cli.key?(:type) && CLI_OPTION_TYPE_SWITCH.include?(cli[:type])
49
+ cli[:switch] = '--' + name.to_s.tr('_', '-')
38
50
  end
39
51
  end
40
52
  end
41
- end
42
-
43
- private
44
-
45
- # clvarname : command line variable name
46
- def env_name(_param_name,options)
47
- return options[:clvarname]
48
53
  end
49
54
 
50
- public
51
-
52
55
  attr_reader :params_definition
53
56
 
54
57
  # @param param_hash
55
- def initialize(param_hash,params_definition)
58
+ def initialize(param_hash, params_definition)
56
59
  @param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
57
60
  @params_definition = params_definition
58
61
  @result_env = {}
@@ -60,115 +63,116 @@ module Aspera
60
63
  @used_param_names = []
61
64
  end
62
65
 
63
- def warn_unrecognized_params
64
- # warn about non translated arguments
65
- @param_hash.each_pair{|key,val|Log.log.warn("unrecognized parameter: #{key} = \"#{val}\"") if !@used_param_names.include?(key)}
66
- end
67
-
68
66
  # adds keys :env :args with resulting values after processing
69
67
  # warns if some parameters were not used
70
- def add_env_args(env,args)
71
- Log.log.debug("ENV=#{@result_env}, ARGS=#{@result_args}")
72
- warn_unrecognized_params
68
+ def add_env_args(env, args)
69
+ Log.log.debug{"ENV=#{@result_env}, ARGS=#{@result_args}"}
70
+ # warn about non translated arguments
71
+ @param_hash.each_pair{|key, val|Log.log.warn{"unrecognized parameter: #{key} = \"#{val}\""} if !@used_param_names.include?(key)}
72
+ # set result
73
73
  env.merge!(@result_env)
74
74
  args.push(*@result_args)
75
75
  return nil
76
76
  end
77
77
 
78
- # add options directly to ascp command line
78
+ # add options directly to command line
79
79
  def add_command_line_options(options)
80
80
  return if options.nil?
81
81
  options.each{|o|@result_args.push(o.to_s)}
82
82
  end
83
83
 
84
84
  def process_params
85
- @params_definition.keys.each do |k|
85
+ @params_definition.each_key do |k|
86
86
  process_param(k)
87
87
  end
88
88
  end
89
89
 
90
+ def read_param(name)
91
+ return process_param(name, read: true)
92
+ end
93
+
94
+ private
95
+
90
96
  # Process a parameter from transfer specification and generate command line param or env var
91
- # @param param_name : key in transfer spec
92
- # @param action : type of processing: ignore getvalue envvar opt_without_arg opt_with_arg defer
93
- # @param options : options for type
94
- def process_param(param_name,action=nil)
95
- options = @params_definition[param_name]
97
+ # @param name [String] of parameter
98
+ # @param read [TrueClass,FalseClass] read and return value of parameter instead of normal processing (for special)
99
+ def process_param(name, read: false)
100
+ options = @params_definition[name]
96
101
  # should not happen
97
102
  if options.nil?
98
- Log.log.warn("Unknown parameter #{param_name}")
103
+ Log.log.warn{"Unknown parameter #{name}"}
99
104
  return
100
105
  end
101
- action = options[:cltype] if action.nil?
106
+ processing_type = read ? :get_value : options[:cli][:type]
102
107
  # check mandatory parameter (nil is valid value)
103
- raise Fasp::Error, "Missing mandatory parameter: #{param_name}" if options[:mandatory] && !@param_hash.has_key?(param_name)
104
- parameter_value = @param_hash[param_name]
105
-
106
- #parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
107
-
108
+ raise Fasp::Error, "Missing mandatory parameter: #{name}" if options[:mandatory] && !@param_hash.key?(name)
109
+ parameter_value = @param_hash[name]
110
+ # no default setting
111
+ # parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
108
112
  # Check parameter type
109
- expected_classes = options[:accepted_types].map do |s|
110
- case s
113
+ expected_classes = options[:accepted_types].map do |type_symbol|
114
+ case type_symbol
111
115
  when :string then String
112
116
  when :array then Array
113
117
  when :hash then Hash
114
118
  when :int then Integer
115
- when :bool then [TrueClass,FalseClass]
116
- else raise "INTERNAL: unexpected value: #{s}"
119
+ when :bool then [TrueClass, FalseClass]
120
+ else raise "INTERNAL: unexpected value: #{type_symbol}"
117
121
  end
118
122
  end.flatten
119
- raise Fasp::Error,"#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " \
123
+ # check that value is of expected type
124
+ raise Fasp::Error, "#{name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " \
120
125
  unless parameter_value.nil? || expected_classes.include?(parameter_value.class)
121
- @used_param_names.push(param_name) unless action.eql?(:defer)
126
+ # special processing will be requested with type get_value
127
+ @used_param_names.push(name) unless processing_type.eql?(:special)
122
128
 
123
129
  # process only non-nil values
124
130
  return nil if parameter_value.nil?
125
131
 
126
132
  # check that value is of an accepted type (string, int bool)
127
- raise "Value #{parameter_value} is not allowed for #{param_name}" if options.has_key?(:enum) && !options[:enum].include?(parameter_value)
133
+ raise "Enum value #{parameter_value} is not allowed for #{name}" if options.key?(:enum) && !options[:enum].include?(parameter_value)
128
134
 
129
135
  # convert some values if value on command line needs processing from value in structure
130
- case options[:clconvert]
136
+ case options[:cli][:convert]
131
137
  when Hash
132
138
  # translate using conversion table
133
- new_value = options[:clconvert][parameter_value]
134
- raise "unsupported value: #{parameter_value}, expect: #{options[:clconvert].keys.join(', ')}" if new_value.nil?
139
+ new_value = options[:cli][:convert][parameter_value]
140
+ raise "unsupported value: #{parameter_value}, expect: #{options[:cli][:convert].keys.join(', ')}" if new_value.nil?
135
141
  parameter_value = new_value
136
142
  when String
137
- # :clconvert has name of class and encoding method
138
- convclass,convmethod = options[:clconvert].split('.')
139
- newvalue = Kernel.const_get(convclass).send(convmethod,parameter_value)
140
- raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}" if newvalue.nil?
141
- parameter_value = newvalue
143
+ # :convert has name of class and encoding method
144
+ conversion_class, conversion_method = options[:cli][:convert].split('.')
145
+ converted_value = Kernel.const_get(conversion_class).send(conversion_method, parameter_value)
146
+ raise Fasp::Error, "unsupported #{name}: #{parameter_value}" if converted_value.nil?
147
+ parameter_value = converted_value
142
148
  when NilClass
143
- else raise "not expected type for clconvert #{options[:clconvert].class} for #{param_name}"
149
+ else raise "not expected type for convert #{options[:cli][:convert].class} for #{name}"
144
150
  end
145
151
 
146
- case action
147
- when :ignore,:defer # ignore this parameter or process later
148
- return
149
- when :get_value # just get value
152
+ case processing_type
153
+ when :get_value # just get value (deferred)
150
154
  return parameter_value
155
+ when :ignore, :special # ignore this parameter or process later
156
+ return
151
157
  when :envvar # set in env var
152
- # define ascp parameter in env var from transfer spec
153
- @result_env[env_name(param_name,options)] = parameter_value
158
+ raise 'error' unless options[:cli].key?(:variable)
159
+ @result_env[options[:cli][:variable]] = parameter_value
154
160
  when :opt_without_arg # if present and true : just add option without value
155
161
  add_param = false
156
162
  case parameter_value
157
163
  when false then nil # nothing to put on command line, no creation by default
158
164
  when true then add_param = true
159
- else raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}"
165
+ else raise Fasp::Error, "unsupported #{name}: #{parameter_value}"
160
166
  end
161
167
  add_param = !add_param if options[:add_on_false]
162
- add_command_line_options([options[:clswitch]]) if add_param
168
+ add_command_line_options([options[:cli][:switch]]) if add_param
163
169
  when :opt_with_arg # transform into command line option with value
164
- #parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
170
+ # parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
165
171
  parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
166
172
  # if transfer_spec value is an array, applies option many times
167
- parameter_value.each{|v|add_command_line_options([options[:clswitch],v])}
168
- when NilClass
169
- Log.log.debug("Ignoring parameter: #{param_name}")
173
+ parameter_value.each{|v|add_command_line_options([options[:cli][:switch], v])}
170
174
  else
171
- raise "ERROR: unknown action: #{action}/#{action.class}"
175
+ raise "ERROR: unknown option processing type: #{processing_type}/#{processing_type.class}"
172
176
  end
173
177
  end
174
178
  end
@@ -5,19 +5,19 @@ require 'aspera/rest'
5
5
  require 'xmlsimple'
6
6
 
7
7
  module Aspera
8
- class CosNode < Rest
8
+ class CosNode < Aspera::Node
9
9
  class << self
10
- def parameters_from_svc_creds(service_credentials,bucket_region)
10
+ def parameters_from_svc_creds(service_credentials, bucket_region)
11
11
  # check necessary contents
12
12
  raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
13
13
  %w[apikey resource_instance_id endpoints].each do |field|
14
- raise "service_credentials must have a field: #{field}" unless service_credentials.has_key?(field)
14
+ raise "service_credentials must have a field: #{field}" unless service_credentials.key?(field)
15
15
  end
16
- Aspera::Log.dump('service_credentials',service_credentials)
16
+ Aspera::Log.dump('service_credentials', service_credentials)
17
17
  # read endpoints from service provided in service credentials
18
18
  endpoints = Aspera::Rest.new({base_url: service_credentials['endpoints']}).read('')[:data]
19
- Aspera::Log.dump('endpoints',endpoints)
20
- storage_endpoint = endpoints.dig('service-endpoints','regional',bucket_region,'public',bucket_region)
19
+ Aspera::Log.dump('endpoints', endpoints)
20
+ storage_endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
21
21
  raise "no such region: #{bucket_region}" if storage_endpoint.nil?
22
22
  return {
23
23
  instance_id: service_credentials['resource_instance_id'],
@@ -28,8 +28,8 @@ module Aspera
28
28
  end
29
29
  IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
30
30
  TOKEN_FIELD = 'delegated_refresh_token'
31
- attr_reader :add_ts
32
- def initialize(bucket_name,storage_endpoint,instance_id,api_key,auth_url=IBM_CLOUD_TOKEN_URL)
31
+
32
+ def initialize(bucket_name, storage_endpoint, instance_id, api_key, auth_url= IBM_CLOUD_TOKEN_URL)
33
33
  @auth_url = auth_url
34
34
  @api_key = api_key
35
35
  s3_api = Aspera::Rest.new({
@@ -37,10 +37,10 @@ module Aspera
37
37
  not_auth_codes: %w[401 403], # error codes when not authorized
38
38
  headers: {'ibm-service-instance-id' => instance_id},
39
39
  auth: {
40
- type: :oauth2,
41
- base_url: @auth_url,
42
- crtype: :generic,
43
- generic: {
40
+ type: :oauth2,
41
+ base_url: @auth_url,
42
+ grant_method: :generic,
43
+ generic: {
44
44
  grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
45
45
  response_type: 'cloud_iam',
46
46
  apikey: @api_key
@@ -53,18 +53,20 @@ module Aspera
53
53
  url_params: {'faspConnectionInfo' => nil}
54
54
  )[:http].body
55
55
  ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
56
- Aspera::Log.dump('ats_info',ats_info)
57
- super({
58
- base_url: ats_info['ATSEndpoint'],
59
- auth: {
60
- type: :basic,
61
- username: ats_info['AccessKey']['Id'],
62
- password: ats_info['AccessKey']['Secret']}})
63
- # prepare transfer spec addition
64
- @add_ts = {'tags' => {'aspera' => {'node' => {'storage_credentials' => {
56
+ Aspera::Log.dump('ats_info', ats_info)
57
+ @storage_credentials = {
65
58
  'type' => 'token',
66
59
  'token' => {TOKEN_FIELD => nil}
67
- }}}}}
60
+ }
61
+ super(
62
+ params: {
63
+ base_url: ats_info['ATSEndpoint'],
64
+ auth: {
65
+ type: :basic,
66
+ username: ats_info['AccessKey']['Id'],
67
+ password: ats_info['AccessKey']['Secret']}},
68
+ add_tspec: {'tags'=>{'aspera'=>{'node'=>{'storage_credentials'=>@storage_credentials}}}})
69
+ # update storage_credentials AND Rest params
68
70
  generate_token
69
71
  end
70
72
 
@@ -72,19 +74,19 @@ module Aspera
72
74
  def generate_token
73
75
  # OAuth API to get delegated token
74
76
  delegated_oauth = Oauth.new({
75
- type: :oauth2,
76
- base_url: @auth_url,
77
- token_field: TOKEN_FIELD,
78
- crtype: :generic,
79
- generic: {
77
+ type: :oauth2,
78
+ base_url: @auth_url,
79
+ token_field: TOKEN_FIELD,
80
+ grant_method: :generic,
81
+ generic: {
80
82
  grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
81
83
  response_type: 'delegated_refresh_token',
82
84
  apikey: @api_key,
83
85
  receiver_client_ids: 'aspera_ats'
84
86
  }})
85
87
  # get delegated token to be placed in rest call header and in transfer tags
86
- @add_ts['tags']['aspera']['node']['storage_credentials']['token'][TOKEN_FIELD] = delegated_oauth.get_authorization.gsub(/^Bearer /,'')
87
- @params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@add_ts['tags']['aspera']['node']['storage_credentials'])}
88
+ @storage_credentials['token'][TOKEN_FIELD] = delegated_oauth.get_authorization.gsub(/^Bearer /, '')
89
+ @params[:headers] = {'X-Aspera-Storage-Credentials' => JSON.generate(@storage_credentials)}
88
90
  end
89
91
  end
90
92
  end
@@ -9,7 +9,7 @@ module Aspera
9
9
  include Singleton
10
10
  # get binary value from data repository
11
11
  def data(id)
12
- File.read(File.join(__dir__,'data',id.to_s),mode: 'rb')
12
+ File.read(File.join(__dir__, 'data', id.to_s), mode: 'rb')
13
13
  end
14
14
  end
15
15
  end