aspera-cli 4.7.0 → 4.8.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 (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -0
  3. data/README.md +844 -861
  4. data/bin/ascli +20 -1
  5. data/bin/asession +37 -34
  6. data/docs/test_env.conf +11 -3
  7. data/examples/aoc.rb +13 -12
  8. data/examples/dascli +26 -0
  9. data/examples/faspex4.rb +34 -29
  10. data/examples/transfer.rb +30 -29
  11. data/lib/aspera/aoc.rb +151 -143
  12. data/lib/aspera/ascmd.rb +56 -45
  13. data/lib/aspera/ats_api.rb +6 -5
  14. data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
  15. data/lib/aspera/cli/extended_value.rb +32 -30
  16. data/lib/aspera/cli/formater.rb +103 -111
  17. data/lib/aspera/cli/info.rb +2 -1
  18. data/lib/aspera/cli/listener/line_dump.rb +1 -0
  19. data/lib/aspera/cli/listener/logger.rb +1 -0
  20. data/lib/aspera/cli/listener/progress.rb +13 -12
  21. data/lib/aspera/cli/listener/progress_multi.rb +21 -20
  22. data/lib/aspera/cli/main.rb +106 -89
  23. data/lib/aspera/cli/manager.rb +96 -85
  24. data/lib/aspera/cli/plugin.rb +50 -32
  25. data/lib/aspera/cli/plugins/alee.rb +6 -5
  26. data/lib/aspera/cli/plugins/aoc.rb +521 -426
  27. data/lib/aspera/cli/plugins/ats.rb +84 -83
  28. data/lib/aspera/cli/plugins/bss.rb +30 -27
  29. data/lib/aspera/cli/plugins/config.rb +483 -397
  30. data/lib/aspera/cli/plugins/console.rb +17 -15
  31. data/lib/aspera/cli/plugins/cos.rb +26 -35
  32. data/lib/aspera/cli/plugins/faspex.rb +201 -168
  33. data/lib/aspera/cli/plugins/faspex5.rb +109 -74
  34. data/lib/aspera/cli/plugins/node.rb +378 -189
  35. data/lib/aspera/cli/plugins/orchestrator.rb +71 -65
  36. data/lib/aspera/cli/plugins/preview.rb +131 -122
  37. data/lib/aspera/cli/plugins/server.rb +94 -93
  38. data/lib/aspera/cli/plugins/shares.rb +42 -28
  39. data/lib/aspera/cli/plugins/sync.rb +15 -14
  40. data/lib/aspera/cli/transfer_agent.rb +56 -52
  41. data/lib/aspera/cli/version.rb +2 -1
  42. data/lib/aspera/colors.rb +29 -28
  43. data/lib/aspera/command_line_builder.rb +50 -43
  44. data/lib/aspera/cos_node.rb +64 -38
  45. data/lib/aspera/data_repository.rb +1 -0
  46. data/lib/aspera/environment.rb +18 -8
  47. data/lib/aspera/fasp/agent_base.rb +26 -23
  48. data/lib/aspera/fasp/agent_connect.rb +35 -30
  49. data/lib/aspera/fasp/agent_direct.rb +68 -60
  50. data/lib/aspera/fasp/agent_httpgw.rb +71 -64
  51. data/lib/aspera/fasp/agent_node.rb +24 -23
  52. data/lib/aspera/fasp/agent_trsdk.rb +19 -20
  53. data/lib/aspera/fasp/error.rb +2 -1
  54. data/lib/aspera/fasp/error_info.rb +79 -68
  55. data/lib/aspera/fasp/installation.rb +122 -114
  56. data/lib/aspera/fasp/listener.rb +1 -0
  57. data/lib/aspera/fasp/parameters.rb +44 -41
  58. data/lib/aspera/fasp/resume_policy.rb +14 -11
  59. data/lib/aspera/fasp/transfer_spec.rb +6 -5
  60. data/lib/aspera/fasp/uri.rb +25 -24
  61. data/lib/aspera/faspex_gw.rb +83 -72
  62. data/lib/aspera/hash_ext.rb +10 -12
  63. data/lib/aspera/id_generator.rb +8 -7
  64. data/lib/aspera/keychain/encrypted_hash.rb +60 -45
  65. data/lib/aspera/keychain/macos_security.rb +26 -24
  66. data/lib/aspera/log.rb +34 -38
  67. data/lib/aspera/nagios.rb +14 -13
  68. data/lib/aspera/node.rb +19 -19
  69. data/lib/aspera/oauth.rb +121 -101
  70. data/lib/aspera/open_application.rb +6 -5
  71. data/lib/aspera/persistency_action_once.rb +9 -8
  72. data/lib/aspera/persistency_folder.rb +10 -9
  73. data/lib/aspera/preview/file_types.rb +261 -266
  74. data/lib/aspera/preview/generator.rb +74 -73
  75. data/lib/aspera/preview/image_error.png +0 -0
  76. data/lib/aspera/preview/options.rb +7 -6
  77. data/lib/aspera/preview/utils.rb +30 -33
  78. data/lib/aspera/preview/video_error.png +0 -0
  79. data/lib/aspera/proxy_auto_config.rb +25 -23
  80. data/lib/aspera/rest.rb +73 -74
  81. data/lib/aspera/rest_call_error.rb +1 -0
  82. data/lib/aspera/rest_error_analyzer.rb +11 -9
  83. data/lib/aspera/rest_errors_aspera.rb +5 -4
  84. data/lib/aspera/secret_hider.rb +68 -0
  85. data/lib/aspera/ssh.rb +12 -10
  86. data/lib/aspera/sync.rb +49 -47
  87. data/lib/aspera/temp_file_manager.rb +7 -5
  88. data/lib/aspera/timer_limiter.rb +9 -8
  89. data/lib/aspera/uri_reader.rb +11 -14
  90. data/lib/aspera/web_auth.rb +17 -15
  91. data.tar.gz.sig +0 -0
  92. metadata +117 -34
  93. metadata.gz.sig +2 -0
  94. data/bin/dascli +0 -13
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'aspera/fasp/transfer_spec'
3
4
  require 'aspera/cli/listener/logger'
4
5
  require 'aspera/cli/listener/progress_multi'
@@ -10,10 +11,10 @@ module Aspera
10
11
  # provides CLI options to select one of the transfer agents (FASP/ascp client)
11
12
  class TransferAgent
12
13
  # special value for --sources : read file list from arguments
13
- FILE_LIST_FROM_ARGS='@args'
14
+ FILE_LIST_FROM_ARGS = '@args'
14
15
  # special value for --sources : read file list from transfer spec (--ts)
15
- FILE_LIST_FROM_TRANSFER_SPEC='@ts'
16
- DEFAULT_TRANSFER_NOTIF_TMPL=<<~END_OF_TEMPLATE
16
+ FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
17
+ DEFAULT_TRANSFER_NOTIF_TMPL = <<~END_OF_TEMPLATE
17
18
  From: <%=from_name%> <<%=from_email%>>
18
19
  To: <<%=to%>>
19
20
  Subject: <%=subject%>
@@ -24,28 +25,28 @@ module Aspera
24
25
  END_OF_TEMPLATE
25
26
  #% (formating bug in eclipse)
26
27
  private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:DEFAULT_TRANSFER_NOTIF_TMPL
27
- TRANSFER_AGENTS=[:direct,:node,:connect,:httpgw,:trsdk]
28
+ TRANSFER_AGENTS = %i[direct node connect httpgw trsdk].freeze
28
29
 
29
30
  # @param env external objects: option manager, config file manager
30
31
  def initialize(opt_mgr,config)
31
- @opt_mgr=opt_mgr
32
- @config=config
32
+ @opt_mgr = opt_mgr
33
+ @config = config
33
34
  # command line can override transfer spec
34
- @transfer_spec_cmdline={'create_dir'=>true}
35
+ @transfer_spec_cmdline = {'create_dir' => true}
35
36
  # the currently selected transfer agent
36
- @agent=nil
37
- @progress_listener=Listener::ProgressMulti.new
37
+ @agent = nil
38
+ @progress_listener = Listener::ProgressMulti.new
38
39
  # source/destination pair, like "paths" of transfer spec
39
- @transfer_paths=nil
40
+ @transfer_paths = nil
40
41
  @opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
41
- @opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts,:optional)}")
42
- @opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume,:optional)}")
42
+ @opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts)}")
43
+ @opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume)}")
43
44
  @opt_mgr.add_opt_simple(:to_folder,'destination folder for downloaded files')
44
45
  @opt_mgr.add_opt_simple(:sources,'list of source files (see doc)')
45
46
  @opt_mgr.add_opt_simple(:transfer_info,'parameters for transfer agent')
46
- @opt_mgr.add_opt_list(:src_type,[:list,:pair],'type of file list')
47
+ @opt_mgr.add_opt_list(:src_type,%i[list pair],'type of file list')
47
48
  @opt_mgr.add_opt_list(:transfer,TRANSFER_AGENTS,'type of transfer agent')
48
- @opt_mgr.add_opt_list(:progress,[:none,:native,:multi],'type of progress bar')
49
+ @opt_mgr.add_opt_list(:progress,%i[none native multi],'type of progress bar')
49
50
  @opt_mgr.set_option(:transfer,:direct)
50
51
  @opt_mgr.set_option(:src_type,:list)
51
52
  @opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
@@ -60,11 +61,11 @@ module Aspera
60
61
  def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
61
62
 
62
63
  def agent_instance=(instance)
63
- @agent=instance
64
+ @agent = instance
64
65
  @agent.add_listener(Listener::Logger.new)
65
66
  # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
66
- if @opt_mgr.get_option(:progress,:mandatory).eql?(:multi) ||
67
- (@opt_mgr.get_option(:progress,:mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
67
+ if @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:multi) ||
68
+ (@opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
68
69
  @agent.add_listener(@progress_listener)
69
70
  end
70
71
  end
@@ -72,25 +73,27 @@ module Aspera
72
73
  # analyze options and create new agent if not already created or set
73
74
  def set_agent_by_options
74
75
  return nil unless @agent.nil?
75
- agent_type=@opt_mgr.get_option(:transfer,:mandatory)
76
+ agent_type = @opt_mgr.get_option(:transfer,is_type: :mandatory)
77
+ # agent plugin is loaded on demand to avoid loading unnecessary dependencies
76
78
  require "aspera/fasp/agent_#{agent_type}"
77
- agent_options=@opt_mgr.get_option(:transfer_info,:optional)
78
- raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), use either @json:<json> or @preset:<parameter set name>" unless [Hash,NilClass].include?(agent_options.class)
79
+ agent_options = @opt_mgr.get_option(:transfer_info)
80
+ raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), "\
81
+ 'use either @json:<json> or @preset:<parameter set name>' unless [Hash,NilClass].include?(agent_options.class)
79
82
  # special case
80
83
  if agent_type.eql?(:node) && agent_options.nil?
81
- param_set_name=@config.get_plugin_default_config_name(:node)
82
- raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
83
- agent_options=@config.preset_by_name(param_set_name)
84
+ param_set_name = @config.get_plugin_default_config_name(:node)
85
+ raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.tr('_','-')}" if param_set_name.nil?
86
+ agent_options = @config.preset_by_name(param_set_name)
84
87
  end
85
88
  # special case
86
- if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,:mandatory).eql?(:native)
87
- agent_options={} if agent_options.nil?
88
- agent_options[:quiet]=false
89
+ if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,is_type: :mandatory).eql?(:native)
90
+ agent_options = {} if agent_options.nil?
91
+ agent_options[:quiet] = false
89
92
  end
90
- agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
93
+ agent_options = agent_options.symbolize_keys if agent_options.is_a?(Hash)
91
94
  # get agent instance
92
- new_agent=Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
93
- self.agent_instance=new_agent
95
+ new_agent = Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
96
+ self.agent_instance = new_agent
94
97
  return nil
95
98
  end
96
99
 
@@ -98,14 +101,14 @@ module Aspera
98
101
  # sets default if needed
99
102
  # param: 'send' or 'receive'
100
103
  def destination_folder(direction)
101
- dest_folder=@opt_mgr.get_option(:to_folder,:optional)
104
+ dest_folder = @opt_mgr.get_option(:to_folder)
102
105
  return File.expand_path(dest_folder) unless dest_folder.nil?
103
- dest_folder=@transfer_spec_cmdline['destination_root']
106
+ dest_folder = @transfer_spec_cmdline['destination_root']
104
107
  return dest_folder unless dest_folder.nil?
105
108
  # default: / on remote, . on local
106
109
  case direction.to_s
107
- when Fasp::TransferSpec::DIRECTION_SEND then dest_folder='/'
108
- when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder='.'
110
+ when Fasp::TransferSpec::DIRECTION_SEND then dest_folder = '/'
111
+ when Fasp::TransferSpec::DIRECTION_RECEIVE then dest_folder = '.'
109
112
  else raise "wrong direction: #{direction}"
110
113
  end
111
114
  return dest_folder
@@ -119,18 +122,19 @@ module Aspera
119
122
  # return cache if set
120
123
  return @transfer_paths unless @transfer_paths.nil?
121
124
  # start with lower priority : get paths from transfer spec on command line
122
- @transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
125
+ @transfer_paths = @transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
123
126
  # is there a source list option ?
124
- file_list=@opt_mgr.get_option(:sources,:optional)
127
+ file_list = @opt_mgr.get_option(:sources)
125
128
  case file_list
126
129
  when nil,FILE_LIST_FROM_ARGS
127
130
  Log.log.debug('getting file list as parameters')
128
131
  # get remaining arguments
129
- file_list=@opt_mgr.get_next_argument('source file list',:multiple)
130
- raise CliBadArgument,"specify at least one file on command line or use --sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
132
+ file_list = @opt_mgr.get_next_argument('source file list',expected: :multiple)
133
+ raise CliBadArgument,'specify at least one file on command line or use '\
134
+ "--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
131
135
  when FILE_LIST_FROM_TRANSFER_SPEC
132
136
  Log.log.debug('assume list provided in transfer spec')
133
- special_case_direct_with_list=@opt_mgr.get_option(:transfer,:mandatory).eql?(:direct) and Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
137
+ (special_case_direct_with_list = @opt_mgr.get_option(:transfer,is_type: :mandatory).eql?(:direct)) && Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
134
138
  raise CliBadArgument,'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
135
139
  # here we assume check of sources is made in transfer agent
136
140
  return @transfer_paths
@@ -144,13 +148,13 @@ module Aspera
144
148
  if !@transfer_paths.nil?
145
149
  Log.log.warn('--sources overrides paths from --ts')
146
150
  end
147
- case @opt_mgr.get_option(:src_type,:mandatory)
151
+ case @opt_mgr.get_option(:src_type,is_type: :mandatory)
148
152
  when :list
149
153
  # when providing a list, just specify source
150
- @transfer_paths=file_list.map{|i|{'source'=>i}}
154
+ @transfer_paths = file_list.map{|i|{'source' => i}}
151
155
  when :pair
152
156
  raise CliBadArgument,"When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
153
- @transfer_paths=file_list.each_slice(2).to_a.map{|s,d|{'source'=>s,'destination'=>d}}
157
+ @transfer_paths = file_list.each_slice(2).to_a.map{|s,d|{'source' => s,'destination' => d}}
154
158
  else raise 'Unsupported src_type'
155
159
  end
156
160
  Log.log.debug("paths=#{@transfer_paths}")
@@ -170,12 +174,12 @@ module Aspera
170
174
  case transfer_spec['direction']
171
175
  when Fasp::TransferSpec::DIRECTION_RECEIVE
172
176
  # init default if required in any case
173
- @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
177
+ @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
174
178
  when Fasp::TransferSpec::DIRECTION_SEND
175
179
  case tr_opts[:src]
176
180
  when :direct
177
181
  # init default if required
178
- @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
182
+ @transfer_spec_cmdline['destination_root'] ||= destination_folder(transfer_spec['direction'])
179
183
  when :node_gen3
180
184
  # in that case, destination is set in return by application (API/upload_setup)
181
185
  # but to_folder was used in initial API call
@@ -191,7 +195,7 @@ module Aspera
191
195
  tr_opts.delete(:src)
192
196
 
193
197
  # update command line paths, unless destination already has one
194
- @transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
198
+ @transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
195
199
 
196
200
  transfer_spec.merge!(@transfer_spec_cmdline)
197
201
  # create transfer agent
@@ -199,7 +203,7 @@ module Aspera
199
203
  Log.log.debug("transfer agent is a #{@agent.class}")
200
204
  @agent.start_transfer(transfer_spec,tr_opts)
201
205
  # list of : :success or error message
202
- result=@agent.wait_for_transfers_completion
206
+ result = @agent.wait_for_transfers_completion
203
207
  @progress_listener.reset
204
208
  Fasp::AgentBase.validate_status_list(result)
205
209
  send_email_transfer_notification(transfer_spec,result)
@@ -207,13 +211,13 @@ module Aspera
207
211
  end
208
212
 
209
213
  def send_email_transfer_notification(transfer_spec,statuses)
210
- return if @opt_mgr.get_option(:notif_to,:optional).nil?
211
- global_status=self.class.session_status(statuses)
212
- email_vars={
214
+ return if @opt_mgr.get_option(:notif_to).nil?
215
+ global_status = self.class.session_status(statuses)
216
+ email_vars = {
213
217
  global_transfer_status: global_status,
214
- subject: "ascli transfer: #{global_status}",
215
- body: "Transfer is: #{global_status}",
216
- ts: transfer_spec
218
+ subject: "ascli transfer: #{global_status}",
219
+ body: "Transfer is: #{global_status}",
220
+ ts: transfer_spec
217
221
  }
218
222
  @config.send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
219
223
  end
@@ -221,7 +225,7 @@ module Aspera
221
225
  # @return :success if all sessions statuses returned by "start" are success
222
226
  # else return the first error exception object
223
227
  def self.session_status(statuses)
224
- error_statuses=statuses.reject{|i|i.eql?(:success)}
228
+ error_statuses = statuses.reject{|i|i.eql?(:success)}
225
229
  return :success if error_statuses.empty?
226
230
  return error_statuses.first
227
231
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Aspera
3
4
  module Cli
4
- VERSION = '4.7.0'
5
+ VERSION = '4.8.0'
5
6
  end
6
7
  end
data/lib/aspera/colors.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # simple vt100 colors
3
4
  class String
4
- class<<self
5
+ class << self
5
6
  private
6
7
 
7
8
  def vtcmd(code);"\e[#{code}m";end
@@ -10,39 +11,39 @@ class String
10
11
  # symbol is the method name added to String
11
12
  # it adds control chars to set color (and reset at the end).
12
13
  VTSTYLES = {
13
- bold: 1,
14
- italic: 3,
15
- underline: 4,
16
- blink: 5,
14
+ bold: 1,
15
+ italic: 3,
16
+ underline: 4,
17
+ blink: 5,
17
18
  reverse_color: 7,
18
- black: 30,
19
- red: 31,
20
- green: 32,
21
- brown: 33,
22
- blue: 34,
23
- magenta: 35,
24
- cyan: 36,
25
- gray: 37,
26
- bg_black: 40,
27
- bg_red: 41,
28
- bg_green: 42,
29
- bg_brown: 43,
30
- bg_blue: 44,
31
- bg_magenta: 45,
32
- bg_cyan: 46,
33
- bg_gray: 47
34
- }
19
+ black: 30,
20
+ red: 31,
21
+ green: 32,
22
+ brown: 33,
23
+ blue: 34,
24
+ magenta: 35,
25
+ cyan: 36,
26
+ gray: 37,
27
+ bg_black: 40,
28
+ bg_red: 41,
29
+ bg_green: 42,
30
+ bg_brown: 43,
31
+ bg_blue: 44,
32
+ bg_magenta: 45,
33
+ bg_cyan: 46,
34
+ bg_gray: 47
35
+ }.freeze
35
36
  private_constant :VTSTYLES
36
37
  # defines methods to String, one per entry in VTSTYLES
37
38
  VTSTYLES.each do |name,code|
38
39
  if $stderr.tty?
39
- begin_seq=vtcmd(code)
40
- end_code=
41
- if code >= 10 then 0
42
- elsif code.eql?(1) then 22
43
- else code+20
40
+ begin_seq = vtcmd(code)
41
+ end_code = 0 # by default reset all
42
+ if code <= 7 then 20 + code
43
+ elsif code <= 37 then 39
44
+ elsif code <= 47 then 49
44
45
  end
45
- end_seq=vtcmd(end_code)
46
+ end_seq = vtcmd(end_code)
46
47
  define_method(name){"#{begin_seq}#{self}#{end_seq}"}
47
48
  else
48
49
  define_method(name){self}
@@ -1,38 +1,44 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Aspera
3
4
  # helper class to build command line from a parameter list (key-value hash)
4
5
  # constructor takes hash: { 'param1':'value1', ...}
5
6
  # process_param is called repeatedly with all known parameters
6
7
  # add_env_args is called to get resulting param list and env var (also checks that all params were used)
7
8
  class CommandLineBuilder
8
- # transform yes/no to true/false
9
- def self.yes_to_true(value)
10
- case value
11
- when 'yes' then return true
12
- when 'no' then return false
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
12
+ # transform yes/no to true/false
13
+ def yes_to_true(value)
14
+ case value
15
+ when 'yes' then return true
16
+ when 'no' then return false
17
+ end
18
+ raise "unsupported value: #{value}"
13
19
  end
14
- raise "unsupported value: #{value}"
15
- end
16
20
 
17
- # Called by provider of definition before constructor of this class so that params_definition has all mandatory fields
18
- def self.normalize_description(d)
19
- d.each do |param_name,options|
20
- raise "Expecting Hash, but have #{options.class} in #{param_name}" unless options.is_a?(Hash)
21
- #options[:accepted_types]=:bool if options[:cltype].eql?(:envvar) and !options.has_key?(:accepted_types)
22
- # by default : not mandatory
23
- options[:mandatory]||=false
24
- options[:desc]||=''
25
- # by default : string, unless it's without arg
26
- if !options.has_key?(:accepted_types)
27
- options[:accepted_types]=options[:cltype].eql?(:opt_without_arg) ? :bool : :string
28
- end
29
- # single type is placed in array
30
- options[:accepted_types]=[options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
31
- if !options.has_key?(:clswitch) && options.has_key?(:cltype) && [:opt_without_arg,:opt_with_arg].include?(options[:cltype])
32
- options[:clswitch]='--'+param_name.to_s.gsub('_','-')
21
+ # 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
27
+ options[:mandatory] ||= false
28
+ options[:desc] ||= ''
29
+ # 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
33
+ # single type is placed in array
34
+ options[:accepted_types] = [options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
35
+ # 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('_','-')
38
+ end
33
39
  end
34
40
  end
35
- end
41
+ end
36
42
 
37
43
  private
38
44
 
@@ -47,11 +53,11 @@ module Aspera
47
53
 
48
54
  # @param param_hash
49
55
  def initialize(param_hash,params_definition)
50
- @param_hash=param_hash # keep reference so that it can be modified by caller before calling `process_params`
51
- @params_definition=params_definition
52
- @result_env={}
53
- @result_args=[]
54
- @used_param_names=[]
56
+ @param_hash = param_hash # keep reference so that it can be modified by caller before calling `process_params`
57
+ @params_definition = params_definition
58
+ @result_env = {}
59
+ @result_args = []
60
+ @used_param_names = []
55
61
  end
56
62
 
57
63
  def warn_unrecognized_params
@@ -86,21 +92,21 @@ module Aspera
86
92
  # @param action : type of processing: ignore getvalue envvar opt_without_arg opt_with_arg defer
87
93
  # @param options : options for type
88
94
  def process_param(param_name,action=nil)
89
- options=@params_definition[param_name]
95
+ options = @params_definition[param_name]
90
96
  # should not happen
91
97
  if options.nil?
92
98
  Log.log.warn("Unknown parameter #{param_name}")
93
99
  return
94
100
  end
95
- action=options[:cltype] if action.nil?
101
+ action = options[:cltype] if action.nil?
96
102
  # check mandatory parameter (nil is valid value)
97
103
  raise Fasp::Error, "Missing mandatory parameter: #{param_name}" if options[:mandatory] && !@param_hash.has_key?(param_name)
98
- parameter_value=@param_hash[param_name]
104
+ parameter_value = @param_hash[param_name]
99
105
 
100
106
  #parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
101
107
 
102
108
  # Check parameter type
103
- expected_classes=options[:accepted_types].map do |s|
109
+ expected_classes = options[:accepted_types].map do |s|
104
110
  case s
105
111
  when :string then String
106
112
  when :array then Array
@@ -110,7 +116,8 @@ module Aspera
110
116
  else raise "INTERNAL: unexpected value: #{s}"
111
117
  end
112
118
  end.flatten
113
- raise Fasp::Error,"#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " unless parameter_value.nil? || expected_classes.include?(parameter_value.class)
119
+ raise Fasp::Error,"#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, " \
120
+ unless parameter_value.nil? || expected_classes.include?(parameter_value.class)
114
121
  @used_param_names.push(param_name) unless action.eql?(:defer)
115
122
 
116
123
  # process only non-nil values
@@ -123,15 +130,15 @@ module Aspera
123
130
  case options[:clconvert]
124
131
  when Hash
125
132
  # translate using conversion table
126
- new_value=options[:clconvert][parameter_value]
133
+ new_value = options[:clconvert][parameter_value]
127
134
  raise "unsupported value: #{parameter_value}, expect: #{options[:clconvert].keys.join(', ')}" if new_value.nil?
128
- parameter_value=new_value
135
+ parameter_value = new_value
129
136
  when String
130
137
  # :clconvert has name of class and encoding method
131
- convclass,convmethod=options[:clconvert].split('.')
132
- newvalue=Kernel.const_get(convclass).send(convmethod,parameter_value)
138
+ convclass,convmethod = options[:clconvert].split('.')
139
+ newvalue = Kernel.const_get(convclass).send(convmethod,parameter_value)
133
140
  raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}" if newvalue.nil?
134
- parameter_value=newvalue
141
+ parameter_value = newvalue
135
142
  when NilClass
136
143
  else raise "not expected type for clconvert #{options[:clconvert].class} for #{param_name}"
137
144
  end
@@ -145,17 +152,17 @@ module Aspera
145
152
  # define ascp parameter in env var from transfer spec
146
153
  @result_env[env_name(param_name,options)] = parameter_value
147
154
  when :opt_without_arg # if present and true : just add option without value
148
- add_param=false
155
+ add_param = false
149
156
  case parameter_value
150
157
  when false then nil # nothing to put on command line, no creation by default
151
- when true then add_param=true
158
+ when true then add_param = true
152
159
  else raise Fasp::Error, "unsupported #{param_name}: #{parameter_value}"
153
160
  end
154
- add_param= !add_param if options[:add_on_false]
161
+ add_param = !add_param if options[:add_on_false]
155
162
  add_command_line_options([options[:clswitch]]) if add_param
156
163
  when :opt_with_arg # transform into command line option with value
157
164
  #parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
158
- parameter_value=[parameter_value] unless parameter_value.is_a?(Array)
165
+ parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
159
166
  # if transfer_spec value is an array, applies option many times
160
167
  parameter_value.each{|v|add_command_line_options([options[:clswitch],v])}
161
168
  when NilClass
@@ -1,64 +1,90 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'aspera/log'
3
4
  require 'aspera/rest'
4
5
  require 'xmlsimple'
5
6
 
6
7
  module Aspera
7
8
  class CosNode < Rest
9
+ class << self
10
+ def parameters_from_svc_creds(service_credentials,bucket_region)
11
+ # check necessary contents
12
+ raise 'service_credentials must be a Hash' unless service_credentials.is_a?(Hash)
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)
15
+ end
16
+ Aspera::Log.dump('service_credentials',service_credentials)
17
+ # read endpoints from service provided in service credentials
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)
21
+ raise "no such region: #{bucket_region}" if storage_endpoint.nil?
22
+ return {
23
+ instance_id: service_credentials['resource_instance_id'],
24
+ service_api_key: service_credentials['apikey'],
25
+ storage_endpoint: "https://#{storage_endpoint}"
26
+ }
27
+ end
28
+ end
29
+ IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
30
+ TOKEN_FIELD = 'delegated_refresh_token'
8
31
  attr_reader :add_ts
9
- IBM_CLOUD_TOKEN_URL='https://iam.cloud.ibm.com/identity'
10
- TOKEN_FIELD='delegated_refresh_token'
11
32
  def initialize(bucket_name,storage_endpoint,instance_id,api_key,auth_url=IBM_CLOUD_TOKEN_URL)
12
- @auth_url=auth_url
13
- @api_key=api_key
14
- s3_api=Aspera::Rest.new({
15
- base_url: storage_endpoint,
16
- not_auth_codes: ['401','403'], # error codes when not authorized
17
- headers: {'ibm-service-instance-id' => instance_id},
18
- auth: {
19
- type: :oauth2,
20
- base_url: @auth_url,
21
- crtype: :generic,
22
- generic: {
23
- grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
24
- response_type: 'cloud_iam',
25
- apikey: @api_key
26
- }}})
33
+ @auth_url = auth_url
34
+ @api_key = api_key
35
+ s3_api = Aspera::Rest.new({
36
+ base_url: storage_endpoint,
37
+ not_auth_codes: %w[401 403], # error codes when not authorized
38
+ headers: {'ibm-service-instance-id' => instance_id},
39
+ auth: {
40
+ type: :oauth2,
41
+ base_url: @auth_url,
42
+ crtype: :generic,
43
+ generic: {
44
+ grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
45
+ response_type: 'cloud_iam',
46
+ apikey: @api_key
47
+ }}})
27
48
  # read FASP connection information for bucket
28
- xml_result_text=s3_api.call({operation: 'GET',subpath: bucket_name,headers: {'Accept'=>'application/xml'},url_params: {'faspConnectionInfo'=>nil}})[:http].body
29
- ats_info=XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
49
+ xml_result_text = s3_api.call(
50
+ operation: 'GET',
51
+ subpath: bucket_name,
52
+ headers: {'Accept' => 'application/xml'},
53
+ url_params: {'faspConnectionInfo' => nil}
54
+ )[:http].body
55
+ ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
30
56
  Aspera::Log.dump('ats_info',ats_info)
31
57
  super({
32
- base_url: ats_info['ATSEndpoint'],
33
- auth: {
34
- type: :basic,
35
- username: ats_info['AccessKey']['Id'],
36
- password: ats_info['AccessKey']['Secret']}})
58
+ base_url: ats_info['ATSEndpoint'],
59
+ auth: {
60
+ type: :basic,
61
+ username: ats_info['AccessKey']['Id'],
62
+ password: ats_info['AccessKey']['Secret']}})
37
63
  # prepare transfer spec addition
38
- @add_ts={'tags'=>{'aspera'=>{'node'=>{'storage_credentials'=>{
64
+ @add_ts = {'tags' => {'aspera' => {'node' => {'storage_credentials' => {
39
65
  'type' => 'token',
40
- 'token' => {TOKEN_FIELD=>nil}
41
- }}}}}
66
+ 'token' => {TOKEN_FIELD => nil}
67
+ }}}}}
42
68
  generate_token
43
69
  end
44
70
 
45
71
  # potentially call this if delegated token is expired
46
72
  def generate_token
47
73
  # OAuth API to get delegated token
48
- delegated_oauth=Oauth.new({
74
+ delegated_oauth = Oauth.new({
49
75
  type: :oauth2,
50
76
  base_url: @auth_url,
51
77
  token_field: TOKEN_FIELD,
52
- crtype: :generic,
53
- generic: {
54
- grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
55
- response_type: 'delegated_refresh_token',
56
- apikey: @api_key,
57
- receiver_client_ids: 'aspera_ats'
58
- }})
59
- # get delagated token to be placed in rest call header and in transfer tags
60
- @add_ts['tags']['aspera']['node']['storage_credentials']['token'][TOKEN_FIELD]=delegated_oauth.get_authorization().gsub(/^Bearer /,'')
61
- @params[:headers]={'X-Aspera-Storage-Credentials'=>JSON.generate(@add_ts['tags']['aspera']['node']['storage_credentials'])}
78
+ crtype: :generic,
79
+ generic: {
80
+ grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
81
+ response_type: 'delegated_refresh_token',
82
+ apikey: @api_key,
83
+ receiver_client_ids: 'aspera_ats'
84
+ }})
85
+ # 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'])}
62
88
  end
63
89
  end
64
90
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'aspera/log'
3
4
  require 'singleton'
4
5