aspera-cli 4.13.0 → 4.15.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 (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +81 -7
  4. data/CONTRIBUTING.md +22 -6
  5. data/README.md +2038 -1080
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/dascli +1 -1
  9. data/examples/proxy.pac +1 -1
  10. data/examples/rubyc +24 -0
  11. data/lib/aspera/aoc.rb +219 -159
  12. data/lib/aspera/ascmd.rb +25 -14
  13. data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
  14. data/lib/aspera/cli/error.rb +17 -0
  15. data/lib/aspera/cli/extended_value.rb +47 -12
  16. data/lib/aspera/cli/formatter.rb +260 -179
  17. data/lib/aspera/cli/hints.rb +80 -0
  18. data/lib/aspera/cli/main.rb +104 -156
  19. data/lib/aspera/cli/manager.rb +259 -209
  20. data/lib/aspera/cli/plugin.rb +123 -63
  21. data/lib/aspera/cli/plugins/alee.rb +2 -3
  22. data/lib/aspera/cli/plugins/aoc.rb +341 -261
  23. data/lib/aspera/cli/plugins/ats.rb +22 -21
  24. data/lib/aspera/cli/plugins/bss.rb +5 -5
  25. data/lib/aspera/cli/plugins/config.rb +578 -627
  26. data/lib/aspera/cli/plugins/console.rb +44 -6
  27. data/lib/aspera/cli/plugins/cos.rb +15 -17
  28. data/lib/aspera/cli/plugins/faspex.rb +114 -100
  29. data/lib/aspera/cli/plugins/faspex5.rb +411 -264
  30. data/lib/aspera/cli/plugins/node.rb +354 -259
  31. data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
  32. data/lib/aspera/cli/plugins/preview.rb +82 -90
  33. data/lib/aspera/cli/plugins/server.rb +79 -32
  34. data/lib/aspera/cli/plugins/shares.rb +55 -42
  35. data/lib/aspera/cli/sync_actions.rb +68 -0
  36. data/lib/aspera/cli/transfer_agent.rb +66 -73
  37. data/lib/aspera/cli/transfer_progress.rb +74 -0
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/colors.rb +12 -8
  40. data/lib/aspera/command_line_builder.rb +14 -11
  41. data/lib/aspera/cos_node.rb +3 -2
  42. data/lib/aspera/data/6 +0 -0
  43. data/lib/aspera/environment.rb +24 -9
  44. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  45. data/lib/aspera/fasp/agent_base.rb +31 -77
  46. data/lib/aspera/fasp/agent_connect.rb +25 -21
  47. data/lib/aspera/fasp/agent_direct.rb +89 -103
  48. data/lib/aspera/fasp/agent_httpgw.rb +231 -149
  49. data/lib/aspera/fasp/agent_node.rb +41 -34
  50. data/lib/aspera/fasp/agent_trsdk.rb +75 -32
  51. data/lib/aspera/fasp/error_info.rb +4 -2
  52. data/lib/aspera/fasp/faux_file.rb +52 -0
  53. data/lib/aspera/fasp/installation.rb +53 -195
  54. data/lib/aspera/fasp/management.rb +244 -0
  55. data/lib/aspera/fasp/parameters.rb +71 -37
  56. data/lib/aspera/fasp/parameters.yaml +76 -8
  57. data/lib/aspera/fasp/products.rb +162 -0
  58. data/lib/aspera/fasp/resume_policy.rb +3 -3
  59. data/lib/aspera/fasp/transfer_spec.rb +7 -6
  60. data/lib/aspera/fasp/uri.rb +26 -24
  61. data/lib/aspera/faspex_gw.rb +2 -2
  62. data/lib/aspera/faspex_postproc.rb +2 -2
  63. data/lib/aspera/hash_ext.rb +14 -4
  64. data/lib/aspera/json_rpc.rb +49 -0
  65. data/lib/aspera/keychain/macos_security.rb +13 -13
  66. data/lib/aspera/line_logger.rb +23 -0
  67. data/lib/aspera/log.rb +58 -16
  68. data/lib/aspera/node.rb +157 -92
  69. data/lib/aspera/oauth.rb +37 -19
  70. data/lib/aspera/open_application.rb +4 -4
  71. data/lib/aspera/persistency_action_once.rb +1 -1
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -2
  74. data/lib/aspera/preview/generator.rb +22 -35
  75. data/lib/aspera/preview/options.rb +2 -0
  76. data/lib/aspera/preview/terminal.rb +73 -16
  77. data/lib/aspera/preview/utils.rb +21 -28
  78. data/lib/aspera/proxy_auto_config.js +2 -2
  79. data/lib/aspera/rest.rb +136 -68
  80. data/lib/aspera/rest_call_error.rb +1 -1
  81. data/lib/aspera/rest_error_analyzer.rb +15 -14
  82. data/lib/aspera/rest_errors_aspera.rb +37 -34
  83. data/lib/aspera/secret_hider.rb +18 -15
  84. data/lib/aspera/ssh.rb +5 -2
  85. data/lib/aspera/sync.rb +127 -119
  86. data/lib/aspera/temp_file_manager.rb +10 -3
  87. data/lib/aspera/web_auth.rb +10 -7
  88. data/lib/aspera/web_server_simple.rb +9 -4
  89. data.tar.gz.sig +0 -0
  90. metadata +34 -17
  91. metadata.gz.sig +0 -0
  92. data/docs/test_env.conf +0 -186
  93. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  94. data/lib/aspera/cli/listener/logger.rb +0 -22
  95. data/lib/aspera/cli/listener/progress.rb +0 -50
  96. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  97. data/lib/aspera/cli/plugins/sync.rb +0 -44
  98. data/lib/aspera/data/7 +0 -0
  99. data/lib/aspera/fasp/listener.rb +0 -13
@@ -2,12 +2,13 @@
2
2
  # accepted_types : accepted types for non-enum
3
3
  # default : default value if not specified
4
4
  # enum : set with list of values for enum types accepted in transfer spec
5
- # agents : supported agents (for doc only)
5
+ # agents : supported agents (for doc only), if not specified: all
6
6
  # required : optional, default: false
7
7
  # cli.type : ascp: type of parameter, one of CLI_OPTION_TYPES
8
8
  # cli.switch : ascp: switch for ascp command line
9
9
  # cli.convert : ascp: transform value: either a Hash with conversion values, or name of class
10
10
  # cli.variable : ascp: name of env var
11
+ # cspell:words dgram dnat dnats faspmgr asperanoded xattrs keepalive datagram
11
12
  ---
12
13
  cipher:
13
14
  :desc: "In transit encryption type."
@@ -78,7 +79,11 @@ destination_root:
78
79
  :cli:
79
80
  :type: :special
80
81
  destination_root_id:
81
- :desc: The file ID of the destination root directory. Required when using Bearer token auth for the destination node.
82
+ :desc: |-
83
+ The file ID of the destination root directory.
84
+ Required when using Bearer token auth for the destination node.
85
+
86
+
82
87
  :accepted_types: :string
83
88
  :agents:
84
89
  - :sdk
@@ -176,7 +181,7 @@ multi_session:
176
181
 
177
182
  :accepted_types: :int
178
183
  :cli:
179
- :type: :ignore
184
+ :type: :special
180
185
  :switch: "-C"
181
186
  multi_session_threshold:
182
187
  :desc: |-
@@ -226,15 +231,37 @@ precalculate_job_size:
226
231
  :cli:
227
232
  :type: :opt_without_arg
228
233
  preserve_access_time:
234
+ :desc: |-
235
+ Preserve the source-file access timestamps at the destination.
236
+ Because source access times are updated by the transfer operation,
237
+ the timestamp that is preserved is the one just before to the transfer.
238
+
239
+
229
240
  :cli:
230
241
  :type: :opt_without_arg
231
242
  preserve_creation_time:
243
+ :desc: |-
244
+ (Windows only) Preserve source-file creation timestamps at the destination.
245
+ Only Windows systems retain information about creation time.
246
+ If the destination is not a Windows computer, this option is ignored.
247
+
248
+
232
249
  :cli:
233
250
  :type: :opt_without_arg
234
251
  preserve_modification_time:
252
+ :desc: |-
253
+ Set the modification time, the last time a file or directory was modified (written), of a transferred file
254
+ to the modification of the source file or directory.
255
+ Preserve source-file modification timestamps at the destination.
256
+
257
+
235
258
  :cli:
236
259
  :type: :opt_without_arg
237
260
  preserve_times:
261
+ :desc: Preserve file timestamps.
262
+ :agents:
263
+ - :sdk
264
+ - :node
238
265
  :cli:
239
266
  :type: :opt_without_arg
240
267
  rate_policy:
@@ -436,6 +463,7 @@ use_ascp4:
436
463
  :cli:
437
464
  :type: :special
438
465
  use_system_ssh:
466
+ :desc: TODO, comment...
439
467
  :accepted_types: :string
440
468
  :agents:
441
469
  - :sdk
@@ -453,22 +481,46 @@ source_root:
453
481
  :switch: "--source-prefix64"
454
482
  :convert: Aspera::Fasp::Parameters.convert_base64
455
483
  min_rate_cap_kbps:
484
+ :desc: |-
485
+ The highest minimum rate that an incoming transfer can request, in kilobits per second.
486
+ Client minimum rate requests that exceed the minimum rate cap are ignored.
487
+ The default value of unlimited applies no cap to the minimum rate. (Default: 0)
488
+
489
+
456
490
  :accepted_types: :int
457
491
  :cli:
458
492
  :type: :ignore
459
493
  lock_rate_policy:
494
+ :desc: If true, lock the rate policy to the default value.
460
495
  :accepted_types: :bool
496
+ :agents:
497
+ - :sdk
498
+ - :connect
461
499
  :cli:
462
500
  :type: :ignore
463
501
  lock_target_rate_kbps:
502
+ :desc: |-
503
+ If true, lock the target transfer rate to the default value set for target_rate_kbps.
504
+ If false, users can adjust the transfer rate up to the value set for target_rate_cap_kbps.
505
+
506
+
464
507
  :accepted_types: :bool
465
508
  :cli:
466
509
  :type: :ignore
467
510
  lock_min_rate_kbps:
511
+ :desc: |-
512
+ If true, lock the minimum transfer rate to the value set for min_rate_kbps.
513
+ If false, users can adjust the transfer rate up to the value set for target_rate_cap_kbps.
514
+
515
+
468
516
  :accepted_types: :bool
517
+ :agents:
518
+ - :sdk
519
+ - :connect
469
520
  :cli:
470
521
  :type: :ignore
471
522
  apply_local_docroot:
523
+ :desc: Apply local docroot to source paths.
472
524
  :agents:
473
525
  - :direct
474
526
  - :sdk
@@ -560,7 +612,7 @@ remove_empty_source_directory:
560
612
  :type: :opt_without_arg
561
613
  EX_at_rest_password:
562
614
  :desc: Content protection password
563
- :deprecation: "Use standard spec parameter: content_protection_password"
615
+ :deprecation: "(4.13) Use standard spec parameter: content_protection_password"
564
616
  :agents:
565
617
  - :direct
566
618
  :cli:
@@ -569,9 +621,10 @@ EX_at_rest_password:
569
621
  EX_proxy_password:
570
622
  :desc: |-
571
623
  Password used for Aspera proxy server authentication.
572
- May be overridden by password in URL EX_fasp_proxy_url.
624
+ May be overridden by password in URL provided in parameter: proxy.
573
625
 
574
626
 
627
+ :deprecation: (4.14) Use env var ASPERA_PROXY_PASS
575
628
  :agents:
576
629
  - :direct
577
630
  :cli:
@@ -579,14 +632,17 @@ EX_proxy_password:
579
632
  :variable: ASPERA_PROXY_PASS
580
633
  EX_license_text:
581
634
  :desc: "License file text override.\nBy default ascp looks for license file near executable."
635
+ :deprecation: (4.14) Use env var ASPERA_SCP_LICENSE
582
636
  :agents:
583
637
  - :direct
584
638
  :cli:
585
639
  :type: :envvar
586
640
  :variable: ASPERA_SCP_LICENSE
587
641
  keepalive:
642
+ :desc: The session is running in persistent session mode.
588
643
  :agents:
589
- - :none
644
+ - :sdk
645
+ - :direct
590
646
  :cli:
591
647
  :type: :opt_without_arg
592
648
  dgram_size:
@@ -609,6 +665,7 @@ sshfp:
609
665
  :switch: "--check-sshfp"
610
666
  EX_http_proxy_url:
611
667
  :desc: Specify the proxy server address used by HTTP Fallback
668
+ :deprecation: (4.14) TODO, use proxy option ?
612
669
  :agents:
613
670
  - :direct
614
671
  :cli:
@@ -616,6 +673,7 @@ EX_http_proxy_url:
616
673
  :switch: "-x"
617
674
  EX_ssh_key_paths:
618
675
  :desc: Use public key authentication for SSH and specify the private key file paths
676
+ :deprecation: (4.14) Use option transfer_info.ascp_args
619
677
  :accepted_types: :array
620
678
  :agents:
621
679
  - :direct
@@ -624,6 +682,7 @@ EX_ssh_key_paths:
624
682
  :switch: "-i"
625
683
  EX_http_transfer_jpeg:
626
684
  :desc: HTTP transfers as JPEG file
685
+ :deprecation: (4.14) Use option transfer_info.ascp_args
627
686
  :accepted_types: :int
628
687
  :default: '0'
629
688
  :agents:
@@ -633,6 +692,7 @@ EX_http_transfer_jpeg:
633
692
  :switch: "-j"
634
693
  EX_no_read:
635
694
  :desc: no read source
695
+ :deprecation: (4.14) Use option transfer_info.ascp_args
636
696
  :agents:
637
697
  - :direct
638
698
  :cli:
@@ -640,12 +700,14 @@ EX_no_read:
640
700
  :switch: "--no-read"
641
701
  EX_no_write:
642
702
  :desc: no write on destination
703
+ :deprecation: (4.14) Use option transfer_info.ascp_args
643
704
  :agents:
644
705
  - :direct
645
706
  :cli:
646
707
  :type: :opt_without_arg
647
708
  :switch: "--no-write"
648
709
  target_rate_percentage:
710
+ :desc: "TODO: remove ?"
649
711
  :cli:
650
712
  :type: :ignore
651
713
  rate_policy_allowed:
@@ -660,15 +722,18 @@ rate_policy_allowed:
660
722
  :cli:
661
723
  :type: :ignore
662
724
  fasp_url:
725
+ :desc: Only used in Faspex.
663
726
  :agents:
664
727
  - :unknown
665
728
  :cli:
666
729
  :type: :ignore
667
730
  lock_min_rate:
731
+ :desc: "TODO: remove ?"
668
732
  :accepted_types: :bool
669
733
  :cli:
670
734
  :type: :ignore
671
735
  lock_target_rate:
736
+ :desc: "TODO: remove ?"
672
737
  :accepted_types: :bool
673
738
  :cli:
674
739
  :type: :ignore
@@ -683,13 +748,15 @@ cipher_allowed:
683
748
  :cli:
684
749
  :type: :ignore
685
750
  obfuscate_file_names:
751
+ :desc: HTTP Gateway obfuscates file names when set to true.
686
752
  :accepted_types: :bool
687
753
  :agents:
688
- - :unknown
754
+ - :httpgw
689
755
  :cli:
690
756
  :type: :ignore
691
757
  EX_file_list:
692
758
  :desc: source file list
759
+ :deprecation: (4.14) Use command line file list, or option transfer_info.ascp_args
693
760
  :agents:
694
761
  - :direct
695
762
  :cli:
@@ -697,6 +764,7 @@ EX_file_list:
697
764
  :switch: "--file-list"
698
765
  EX_file_pair_list:
699
766
  :desc: source file pair list
767
+ :deprecation: (4.14) Use command line file pair list, or option transfer_info.ascp_args
700
768
  :agents:
701
769
  - :direct
702
770
  :cli:
@@ -704,7 +772,7 @@ EX_file_pair_list:
704
772
  :switch: "--file-pair-list"
705
773
  EX_ascp_args:
706
774
  :desc: Add native command line arguments to ascp
707
- :deprecation: Use parameter ascp_args in option transfer_info
775
+ :deprecation: (4.13) Use option transfer_info.ascp_args
708
776
  :accepted_types: :array
709
777
  :agents:
710
778
  - :direct
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ # cspell:ignore LOCALAPPDATA
4
+ require 'aspera/environment'
5
+
6
+ module Aspera
7
+ module Fasp
8
+ # find Aspera standard products installation in standard paths
9
+ class Products
10
+ # known product names
11
+ CONNECT = 'IBM Aspera Connect'
12
+ ASPERA = 'IBM Aspera (Client)'
13
+ CLI_V1 = 'Aspera CLI (deprecated)'
14
+ DRIVE = 'Aspera Drive (deprecated)'
15
+ HSTS = 'IBM Aspera High-Speed Transfer Server'
16
+ # product information manifest: XML (part of aspera product)
17
+ INFO_META_FILE = 'product-info.mf'
18
+ BIN_SUBFOLDER = 'bin'
19
+ ETC_SUBFOLDER = 'etc'
20
+ VAR_RUN_SUBFOLDER = File.join('var', 'run')
21
+
22
+ @@found_products = nil # rubocop:disable Style/ClassVars
23
+ class << self
24
+ # @return product folders depending on OS fields
25
+ # :expected M app name is taken from the manifest if present, else defaults to this value
26
+ # :app_root M main folder for the application
27
+ # :log_root O location of log files (Linux uses syslog)
28
+ # :run_root O only for Connect Client, location of http port file
29
+ # :sub_bin O subfolder with executables, default : bin
30
+ def product_locations_on_current_os
31
+ result =
32
+ case Aspera::Environment.os
33
+ when Aspera::Environment::OS_WINDOWS then [{
34
+ expected: CONNECT,
35
+ app_root: File.join(ENV.fetch('LOCALAPPDATA', nil), 'Programs', 'Aspera', 'Aspera Connect'),
36
+ log_root: File.join(ENV.fetch('LOCALAPPDATA', nil), 'Aspera', 'Aspera Connect', 'var', 'log'),
37
+ run_root: File.join(ENV.fetch('LOCALAPPDATA', nil), 'Aspera', 'Aspera Connect')
38
+ }, {
39
+ expected: CLI_V1,
40
+ app_root: File.join('C:', 'Program Files', 'Aspera', 'cli'),
41
+ log_root: File.join('C:', 'Program Files', 'Aspera', 'cli', 'var', 'log')
42
+ }, {
43
+ expected: HSTS,
44
+ app_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server'),
45
+ log_root: File.join('C:', 'Program Files', 'Aspera', 'Enterprise Server', 'var', 'log')
46
+ }]
47
+ when Aspera::Environment::OS_X then [{
48
+ expected: CONNECT,
49
+ app_root: File.join(Dir.home, 'Applications', 'Aspera Connect.app'),
50
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
51
+ run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
52
+ sub_bin: File.join('Contents', 'Resources')
53
+ }, {
54
+ expected: CONNECT,
55
+ app_root: File.join('', 'Applications', 'Aspera Connect.app'),
56
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Connect'),
57
+ run_root: File.join(Dir.home, 'Library', 'Application Support', 'Aspera', 'Aspera Connect'),
58
+ sub_bin: File.join('Contents', 'Resources')
59
+ }, {
60
+ expected: CLI_V1,
61
+ app_root: File.join(Dir.home, 'Applications', 'Aspera CLI'),
62
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
63
+ }, {
64
+ expected: HSTS,
65
+ app_root: File.join('', 'Library', 'Aspera'),
66
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera')
67
+ }, {
68
+ expected: DRIVE,
69
+ app_root: File.join('', 'Applications', 'Aspera Drive.app'),
70
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'Aspera_Drive'),
71
+ sub_bin: File.join('Contents', 'Resources')
72
+ }, {
73
+ expected: ASPERA,
74
+ app_root: File.join('', 'Applications', 'IBM Aspera.app'),
75
+ log_root: File.join(Dir.home, 'Library', 'Logs', 'IBM Aspera'),
76
+ sub_bin: File.join('Contents', 'Resources', 'sdk', 'aspera', 'bin')
77
+ }]
78
+ else [{ # other: Linux and Unix family
79
+ expected: CONNECT,
80
+ app_root: File.join(Dir.home, '.aspera', 'connect'),
81
+ run_root: File.join(Dir.home, '.aspera', 'connect')
82
+ }, {
83
+ expected: CLI_V1,
84
+ app_root: File.join(Dir.home, '.aspera', 'cli')
85
+ }, {
86
+ expected: HSTS,
87
+ app_root: File.join('', 'opt', 'aspera')
88
+ }]
89
+ end
90
+ result # .each {|item| item.deep_do {|h, _k, _v, _m|h.freeze}}.freeze
91
+ end
92
+
93
+ # @return the list of installed products in format of product_locations_on_current_os
94
+ def installed_products
95
+ if @@found_products.nil?
96
+ scan_locations = product_locations_on_current_os.clone
97
+ # add SDK as first search path
98
+ scan_locations.unshift({
99
+ expected: 'SDK',
100
+ app_root: Installation.instance.sdk_folder,
101
+ sub_bin: ''
102
+ })
103
+ # search installed products: with ascp
104
+ @@found_products = scan_locations.select! do |item| # rubocop:disable Style/ClassVars
105
+ # skip if not main folder
106
+ next false unless Dir.exist?(item[:app_root])
107
+ Log.log.debug{"Found #{item[:app_root]}"}
108
+ sub_bin = item[:sub_bin] || BIN_SUBFOLDER
109
+ item[:ascp_path] = File.join(item[:app_root], sub_bin, ascp_filename)
110
+ # skip if no ascp
111
+ next false unless File.exist?(item[:ascp_path])
112
+ # read info from product info file if present
113
+ product_info_file = "#{item[:app_root]}/#{INFO_META_FILE}"
114
+ if File.exist?(product_info_file)
115
+ res_s = XmlSimple.xml_in(File.read(product_info_file), {'ForceArray' => false})
116
+ item[:name] = res_s['name']
117
+ item[:version] = res_s['version']
118
+ else
119
+ item[:name] = item[:expected]
120
+ end
121
+ true # select this version
122
+ end
123
+ end
124
+ return @@found_products
125
+ end
126
+
127
+ # filename for ascp with optional extension (Windows)
128
+ def ascp_filename
129
+ return 'ascp' + Environment.exe_extension
130
+ end
131
+
132
+ # @return folder paths for specified applications
133
+ # @param name Connect or CLI
134
+ def folders(name)
135
+ found = Products.installed_products.select{|i|i[:expected].eql?(name) || i[:name].eql?(name)}
136
+ raise "Product: #{name} not found, please install." if found.empty?
137
+ return found.first
138
+ end
139
+
140
+ # @return the file path of local connect where API's URI can be read
141
+ def connect_uri
142
+ connect = folders(CONNECT)
143
+ folder = File.join(connect[:run_root], VAR_RUN_SUBFOLDER)
144
+ ['', 's'].each do |ext|
145
+ uri_file = File.join(folder, "http#{ext}.uri")
146
+ Log.log.debug{"checking connect port file: #{uri_file}"}
147
+ if File.exist?(uri_file)
148
+ return File.open(uri_file, &:gets).strip
149
+ end
150
+ end
151
+ raise "no connect uri file found in #{folder}"
152
+ end
153
+
154
+ # @ return path to configuration file of aspera CLI
155
+ # def cli_conf_file
156
+ # connect = folders(PRODUCT_CLI_V1)
157
+ # return File.join(connect[:app_root], BIN_SUBFOLDER, '.aspera_cli_conf')
158
+ # end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -49,11 +49,11 @@ module Aspera
49
49
  # failure in ascp
50
50
  if e.retryable?
51
51
  # exit if we exceed the max number of retry
52
- raise Fasp::Error, 'Maximum number of retry reached' if remaining_resumes <= 0
52
+ raise Fasp::Error, "Maximum number of retry reached (#{@parameters[:iter_max]})" if remaining_resumes <= 0
53
53
  else
54
54
  # give one chance only to non retryable errors
55
55
  unless remaining_resumes.eql?(@parameters[:iter_max])
56
- Log.log.error('non-retryable error')
56
+ Log.log.error('non-retryable error'.red.blink)
57
57
  raise e
58
58
  end
59
59
  end
@@ -61,7 +61,7 @@ module Aspera
61
61
 
62
62
  # take this retry in account
63
63
  remaining_resumes -= 1
64
- Log.log.warn{"resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})"}
64
+ Log.log.warn{"Resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})"}
65
65
 
66
66
  # wait a bit before retrying, maybe network condition will be better
67
67
  sleep(sleep_seconds)
@@ -17,11 +17,12 @@ module Aspera
17
17
  }.freeze
18
18
  # reserved tag for Aspera
19
19
  TAG_RESERVED = 'aspera'
20
- # define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128
21
- Aspera::Fasp::Parameters.description.each do |k, v|
22
- next unless v[:enum].is_a?(Array)
23
- v[:enum].each do |enum|
24
- TransferSpec.const_set("#{k.to_s.upcase}_#{enum.upcase.gsub(/[^A-Z0-9]/, '_')}", enum.freeze)
20
+ # define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
21
+ Aspera::Fasp::Parameters.description.each do |name, description|
22
+ next unless description[:enum].is_a?(Array)
23
+ TransferSpec.const_set("#{name.to_s.upcase}_ENUM_VALUES", description[:enum])
24
+ description[:enum].each do |enum|
25
+ TransferSpec.const_set("#{name.to_s.upcase}_#{enum.upcase.gsub(/[^A-Z0-9]/, '_')}", enum.freeze)
25
26
  end
26
27
  end
27
28
  class << self
@@ -40,7 +41,7 @@ module Aspera
40
41
  return case tspec['direction']
41
42
  when DIRECTION_SEND then :upload
42
43
  when DIRECTION_RECEIVE then :download
43
- else raise 'Error: upload or download only'
44
+ else raise "Error: upload or download only, not #{tspec['direction']} (#{tspec['direction'].class})"
44
45
  end
45
46
  end
46
47
  end
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:words httpport targetrate minrate bwcap createpath lockpolicy lockminrate faspe
4
+
3
5
  require 'aspera/log'
6
+ require 'aspera/rest'
4
7
  require 'aspera/command_line_builder'
5
8
 
6
9
  module Aspera
7
10
  module Fasp
8
11
  # translates a "faspe:" URI (used in Faspex 4) into transfer spec hash
9
12
  class Uri
10
- def initialize(fasplink)
11
- @fasp_uri = URI.parse(fasplink.gsub(' ', '%20'))
13
+ SCHEME = 'faspe'
14
+ def initialize(fasp_link)
15
+ @fasp_uri = URI.parse(fasp_link.gsub(' ', '%20'))
12
16
  # TODO: check scheme is faspe
13
17
  end
14
18
 
@@ -18,32 +22,30 @@ module Aspera
18
22
  result_ts['remote_user'] = @fasp_uri.user
19
23
  result_ts['ssh_port'] = @fasp_uri.port
20
24
  result_ts['paths'] = [{'source' => URI.decode_www_form_component(@fasp_uri.path)}]
21
- # faspex does not encode trailing base64 encoded tags, fix that
25
+ # faspex does not encode trailing base64 padding, fix that to be able to decode properly
22
26
  fixed_query = @fasp_uri.query.gsub(/(=+)$/){|x|'%3D' * x.length}
23
27
 
24
- URI.decode_www_form(fixed_query).each do |i|
25
- name = i[0]
26
- value = i[1]
28
+ Rest.decode_query(fixed_query).each do |name, value|
27
29
  case name
28
- when 'cookie' then result_ts['cookie'] = value
29
- when 'token' then result_ts['token'] = value
30
- when 'sshfp' then result_ts['sshfp'] = value
31
- when 'policy' then result_ts['rate_policy'] = value
32
- when 'httpport' then result_ts['http_fallback_port'] = value.to_i
33
- when 'targetrate' then result_ts['target_rate_kbps'] = value.to_i
34
- when 'minrate' then result_ts['min_rate_kbps'] = value.to_i
35
- when 'port' then result_ts['fasp_port'] = value.to_i
36
- when 'bwcap' then result_ts['target_rate_cap_kbps'] = value.to_i
37
- when 'enc' then result_ts['cipher'] = value.gsub(/^aes/, 'aes-').gsub(/cfb$/, '-cfb').gsub(/gcm$/, '-gcm').gsub(/--/, '-')
38
- when 'tags64' then result_ts['tags'] = JSON.parse(Base64.strict_decode64(value))
39
- when 'createpath' then result_ts['create_dir'] = CommandLineBuilder.yes_to_true(value)
40
- when 'fallback' then result_ts['http_fallback'] = CommandLineBuilder.yes_to_true(value)
41
- when 'lockpolicy' then result_ts['lock_rate_policy'] = CommandLineBuilder.yes_to_true(value)
30
+ when 'cookie' then result_ts['cookie'] = value
31
+ when 'token' then result_ts['token'] = value
32
+ when 'sshfp' then result_ts['sshfp'] = value
33
+ when 'policy' then result_ts['rate_policy'] = value
34
+ when 'httpport' then result_ts['http_fallback_port'] = value.to_i
35
+ when 'targetrate' then result_ts['target_rate_kbps'] = value.to_i
36
+ when 'minrate' then result_ts['min_rate_kbps'] = value.to_i
37
+ when 'port' then result_ts['fasp_port'] = value.to_i
38
+ when 'bwcap' then result_ts['target_rate_cap_kbps'] = value.to_i
39
+ when 'enc' then result_ts['cipher'] = value.gsub(/^aes/, 'aes-').gsub(/cfb$/, '-cfb').gsub(/gcm$/, '-gcm').gsub(/--/, '-')
40
+ when 'tags64' then result_ts['tags'] = JSON.parse(Base64.strict_decode64(value))
41
+ when 'createpath' then result_ts['create_dir'] = CommandLineBuilder.yes_to_true(value)
42
+ when 'fallback' then result_ts['http_fallback'] = CommandLineBuilder.yes_to_true(value)
43
+ when 'lockpolicy' then result_ts['lock_rate_policy'] = CommandLineBuilder.yes_to_true(value)
42
44
  when 'lockminrate' then result_ts['lock_min_rate'] = CommandLineBuilder.yes_to_true(value)
43
- when 'auth' then Log.log.debug{"ignoring auth #{name}=#{value}"} # TODO: translate into transfer spec ? yes/no
44
- when 'v' then Log.log.debug{"ignoring v #{name}=#{value}"} # TODO: translate into transfer spec ? 2
45
- when 'protect' then Log.log.debug{"ignoring protect #{name}=#{value}"} # TODO: translate into transfer spec ?
46
- else Log.log.warn{"URI parameter ignored: #{name} = #{value}"}
45
+ when 'auth' then Log.log.debug{"ignoring #{name}=#{value}"} # Not used (yes/no)
46
+ when 'v' then Log.log.debug{"ignoring #{name}=#{value}"} # rubocop:disable Lint/DuplicateBranch Not used (2)
47
+ when 'protect' then Log.log.debug{"ignoring #{name}=#{value}"} # rubocop:disable Lint/DuplicateBranch TODO: what is this ?
48
+ else Log.log.warn{"URI parameter ignored: #{name} = #{value}"}
47
49
  end
48
50
  end
49
51
  return result_ts
@@ -68,9 +68,9 @@ module Aspera
68
68
  faspex_pkg_parameters = JSON.parse(request.body)
69
69
  Log.log.debug{"faspex pkg create parameters=#{faspex_pkg_parameters}"}
70
70
  faspex_package_create_result =
71
- if @app_api.is_a?(Aspera::AoC)
71
+ if @app_api.class.name.eql?('Aspera::AoC')
72
72
  faspex4_send_to_aoc(faspex_pkg_parameters)
73
- elsif @app_api.is_a?(Aspera::Rest)
73
+ elsif @app_api.class.name.eql?('Aspera::Rest')
74
74
  faspex4_send_to_faspex5(faspex_pkg_parameters)
75
75
  else
76
76
  raise "No such adapter: #{@app_api.class}"
@@ -13,7 +13,7 @@ module Aspera
13
13
  def initialize(server, parameters)
14
14
  raise 'parameters must be Hash' unless parameters.is_a?(Hash)
15
15
  @parameters = parameters.symbolize_keys
16
- Log.dump(:postproc_parameters, @parameters)
16
+ Log.log.debug{Log.dump(:post_proc_parameters, @parameters)}
17
17
  raise "unexpected key in parameters config: only: #{ALLOWED_PARAMETERS.join(', ')}" if @parameters.keys.any?{|k|!ALLOWED_PARAMETERS.include?(k)}
18
18
  @parameters[:script_folder] ||= '.'
19
19
  @parameters[:fail_on_error] ||= false
@@ -44,7 +44,7 @@ module Aspera
44
44
  script_path = File.join(@parameters[:script_folder], script_file)
45
45
  Log.log.debug{"script=#{script_path}"}
46
46
  webhook_parameters = JSON.parse(request.body)
47
- Log.dump(:webhook_parameters, webhook_parameters)
47
+ Log.log.debug{Log.dump(:webhook_parameters, webhook_parameters)}
48
48
  # env expects only strings
49
49
  environment = webhook_parameters.each_with_object({}) { |(k, v), h| h[k] = v.to_s }
50
50
  post_proc_pid = Process.spawn(environment, [script_path, script_path])
@@ -2,11 +2,21 @@
2
2
 
3
3
  class ::Hash
4
4
  def deep_merge(second)
5
- merge(second){|_key, v1, v2|Hash === v1 && Hash === v2 ? v1.deep_merge(v2) : v2}
5
+ merge(second){|_key, v1, v2|v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.deep_merge(v2) : v2}
6
6
  end
7
7
 
8
8
  def deep_merge!(second)
9
- merge!(second){|_key, v1, v2|Hash === v1 && Hash === v2 ? v1.deep_merge!(v2) : v2}
9
+ merge!(second){|_key, v1, v2|v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.deep_merge!(v2) : v2}
10
+ end
11
+
12
+ def deep_do(memory=nil, &block)
13
+ each do |key, value|
14
+ if value.is_a?(Hash)
15
+ value.deep_do(memory, &block)
16
+ else
17
+ yield(self, key, value, memory)
18
+ end
19
+ end
10
20
  end
11
21
  end
12
22
 
@@ -14,8 +24,8 @@ end
14
24
  unless Hash.method_defined?(:transform_keys)
15
25
  class Hash
16
26
  def transform_keys
17
- return each_with_object({}){|(k, v), memo|memo[yield(k)] = v} if block_given?
18
- raise 'missing block'
27
+ raise 'missing block' unless block_given?
28
+ return each_with_object({}){|(k, v), memo|memo[yield(k)] = v}
19
29
  end
20
30
  end
21
31
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # cspell:ignore blankslate
4
+
5
+ require 'aspera/rest_error_analyzer'
6
+ require 'blankslate'
7
+
8
+ Aspera::RestErrorAnalyzer.instance.add_simple_handler(name: 'JSON RPC', path: %w[error message], always: true)
9
+
10
+ module Aspera
11
+ # a very simple JSON RPC client
12
+ class JsonRpcClient < BlankSlate
13
+ JSON_RPC_VERSION = '2.0'
14
+ reveal :instance_variable_get
15
+ reveal :inspect
16
+ reveal :to_s
17
+
18
+ def initialize(api, namespace = nil)
19
+ super()
20
+ @api = api
21
+ @namespace = namespace
22
+ @request_id = 0
23
+ end
24
+
25
+ def respond_to_missing?(sym, include_private = false)
26
+ true
27
+ end
28
+
29
+ def method_missing(method, *args, &block)
30
+ args = args.first if args.size == 1 && args.first.is_a?(Hash)
31
+ data = @api.create('', {
32
+ jsonrpc: JSON_RPC_VERSION,
33
+ method: "#{@namespace}#{method}",
34
+ params: args,
35
+ id: @request_id += 1
36
+ })[:data]
37
+ raise 'response shall be Hash' unless data.is_a?(Hash)
38
+ raise 'bad version in response' unless data['jsonrpc'] == JSON_RPC_VERSION
39
+ raise 'missing id in response' unless data.key?('id')
40
+ raise 'both error and response' if data.key?('error') && data.key?('result')
41
+ raise 'bad error response' unless
42
+ !data.key?('error') ||
43
+ data['error'].is_a?(Hash) &&
44
+ data['error']['code'].is_a?(Integer) &&
45
+ data['error']['message'].is_a?(String)
46
+ return data['result']
47
+ end
48
+ end
49
+ end