aspera-cli 4.10.0 → 4.12.0

Sign up to get free protection for your applications and to get access to all the features.
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