aspera-cli 4.24.1 → 4.24.2
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +15 -2
- data/README.md +745 -436
- data/bin/ascli +20 -1
- data/bin/asession +23 -27
- data/lib/aspera/agent/base.rb +10 -21
- data/lib/aspera/agent/connect.rb +2 -3
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +49 -32
- data/lib/aspera/agent/factory.rb +31 -0
- data/lib/aspera/api/aoc.rb +79 -49
- data/lib/aspera/api/faspex.rb +212 -0
- data/lib/aspera/api/node.rb +99 -84
- data/lib/aspera/ascp/installation.rb +22 -21
- data/lib/aspera/ascp/management.rb +119 -23
- data/lib/aspera/assert.rb +14 -8
- data/lib/aspera/cli/extended_value.rb +15 -15
- data/lib/aspera/cli/formatter.rb +7 -5
- data/lib/aspera/cli/hints.rb +8 -0
- data/lib/aspera/cli/info.rb +4 -4
- data/lib/aspera/cli/main.rb +55 -70
- data/lib/aspera/cli/manager.rb +7 -4
- data/lib/aspera/cli/plugins/alee.rb +2 -1
- data/lib/aspera/cli/plugins/aoc.rb +110 -186
- data/lib/aspera/cli/plugins/ats.rb +4 -4
- data/lib/aspera/cli/plugins/base.rb +335 -0
- data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
- data/lib/aspera/cli/plugins/config.rb +249 -220
- data/lib/aspera/cli/plugins/console.rb +15 -15
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/factory.rb +78 -0
- data/lib/aspera/cli/plugins/faspex.rb +17 -20
- data/lib/aspera/cli/plugins/faspex5.rb +79 -193
- data/lib/aspera/cli/plugins/faspio.rb +14 -13
- data/lib/aspera/cli/plugins/httpgw.rb +13 -12
- data/lib/aspera/cli/plugins/node.rb +34 -32
- data/lib/aspera/cli/plugins/oauth.rb +48 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -13
- data/lib/aspera/cli/plugins/preview.rb +4 -4
- data/lib/aspera/cli/plugins/server.rb +15 -13
- data/lib/aspera/cli/plugins/shares.rb +18 -15
- data/lib/aspera/cli/sync_actions.rb +1 -1
- data/lib/aspera/cli/transfer_agent.rb +24 -20
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +3 -3
- data/lib/aspera/cli/wizard.rb +65 -53
- data/lib/aspera/colors.rb +6 -0
- data/lib/aspera/command_line_builder.rb +45 -50
- data/lib/aspera/command_line_converter.rb +2 -1
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +10 -7
- data/lib/aspera/faspex_gw.rb +6 -4
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +37 -9
- data/lib/aspera/nagios.rb +1 -1
- data/lib/aspera/oauth/base.rb +17 -10
- data/lib/aspera/oauth/factory.rb +8 -8
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/products/connect.rb +4 -3
- data/lib/aspera/products/desktop.rb +1 -4
- data/lib/aspera/products/other.rb +9 -1
- data/lib/aspera/products/transferd.rb +0 -1
- data/lib/aspera/rest.rb +126 -83
- data/lib/aspera/ssh.rb +3 -3
- data/lib/aspera/sync/args.schema.yaml +46 -3
- data/lib/aspera/sync/conf.schema.yaml +130 -94
- data/lib/aspera/sync/operations.rb +16 -16
- data/lib/aspera/temp_file_manager.rb +17 -5
- data/lib/aspera/transfer/error.rb +16 -7
- data/lib/aspera/transfer/parameters.rb +34 -20
- data/lib/aspera/transfer/resumer.rb +74 -0
- data/lib/aspera/transfer/spec.rb +4 -3
- data/lib/aspera/transfer/spec.schema.yaml +132 -51
- data/lib/aspera/transfer/spec_doc.rb +41 -35
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +6 -6
- data.tar.gz.sig +0 -0
- metadata +9 -7
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
- data/lib/aspera/cli/plugin.rb +0 -333
- data/lib/aspera/cli/plugin_factory.rb +0 -81
- data/lib/aspera/resumer.rb +0 -77
- data/lib/aspera/transfer/error_info.rb +0 -91
data/lib/aspera/cli/wizard.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'aspera/oauth/jwt'
|
|
4
|
+
require 'aspera/assert'
|
|
5
|
+
require 'aspera/cli/plugins/factory'
|
|
4
6
|
|
|
5
7
|
module Aspera
|
|
6
8
|
module Cli
|
|
9
|
+
# The wizard detects applications and generates a config
|
|
7
10
|
class Wizard
|
|
8
11
|
WIZARD_RESULT_KEYS = %i[preset_value test_args].freeze
|
|
9
12
|
DEFAULT_PRIV_KEY_FILENAME = 'my_private_key.pem' # pragma: allowlist secret
|
|
@@ -13,13 +16,17 @@ module Aspera
|
|
|
13
16
|
def initialize(parent, main_folder)
|
|
14
17
|
@parent = parent
|
|
15
18
|
@main_folder = main_folder
|
|
16
|
-
#
|
|
19
|
+
# Wizard options
|
|
17
20
|
options.declare(:override, 'Wizard: override existing value', values: :bool, default: :no)
|
|
18
21
|
options.declare(:default, 'Wizard: set as default configuration for specified plugin (also: update)', values: :bool, default: true)
|
|
19
|
-
options.declare(:test_mode, 'Wizard: skip private key check step', values: :bool, default: false)
|
|
20
22
|
options.declare(:key_path, 'Wizard: path to private key for JWT')
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
# @return false if in test mode to avoid interactive input
|
|
26
|
+
def required
|
|
27
|
+
!ENV['ASCLI_WIZ_TEST']
|
|
28
|
+
end
|
|
29
|
+
|
|
23
30
|
def options
|
|
24
31
|
@parent.options
|
|
25
32
|
end
|
|
@@ -32,6 +39,10 @@ module Aspera
|
|
|
32
39
|
@parent.config
|
|
33
40
|
end
|
|
34
41
|
|
|
42
|
+
def check_email(email)
|
|
43
|
+
Aspera.assert(email =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i, type: ParameterError){"Username shall be an email: #{email}"}
|
|
44
|
+
end
|
|
45
|
+
|
|
35
46
|
# Find a plugin, and issue the "require"
|
|
36
47
|
# @return [Hash] plugin info: { product:, name:, url:, version: }
|
|
37
48
|
def identify_plugins_for_url
|
|
@@ -40,19 +51,19 @@ module Aspera
|
|
|
40
51
|
check_only = check_only.to_sym unless check_only.nil?
|
|
41
52
|
found_apps = []
|
|
42
53
|
my_self_plugin_sym = self.class.name.split('::').last.downcase.to_sym
|
|
43
|
-
|
|
44
|
-
#
|
|
54
|
+
Plugins::Factory.instance.plugin_list.each do |plugin_name_sym|
|
|
55
|
+
# No detection for internal plugin
|
|
45
56
|
next if plugin_name_sym.eql?(my_self_plugin_sym)
|
|
46
57
|
next if check_only && !check_only.eql?(plugin_name_sym)
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
next unless
|
|
58
|
+
# Load plugin class
|
|
59
|
+
plugin_klass = Plugins::Factory.instance.plugin_class(plugin_name_sym)
|
|
60
|
+
# Requires detection method
|
|
61
|
+
next unless plugin_klass.respond_to?(:detect)
|
|
51
62
|
detection_info = nil
|
|
52
63
|
begin
|
|
53
64
|
Log.log.debug{"detecting #{plugin_name_sym} at #{app_url}"}
|
|
54
65
|
formatter.long_operation_running("#{plugin_name_sym}\r")
|
|
55
|
-
detection_info =
|
|
66
|
+
detection_info = plugin_klass.detect(app_url)
|
|
56
67
|
rescue OpenSSL::SSL::SSLError => e
|
|
57
68
|
Log.log.warn(e.message)
|
|
58
69
|
Log.log.warn('Use option --insecure=yes to allow unchecked certificate') if e.message.include?('cert')
|
|
@@ -63,8 +74,8 @@ module Aspera
|
|
|
63
74
|
next if detection_info.nil?
|
|
64
75
|
Aspera.assert_type(detection_info, Hash)
|
|
65
76
|
Aspera.assert_type(detection_info[:url], String) if detection_info.key?(:url)
|
|
66
|
-
app_name =
|
|
67
|
-
#
|
|
77
|
+
app_name = plugin_klass.respond_to?(:application_name) ? plugin_klass.application_name : plugin_klass.name.split('::').last
|
|
78
|
+
# If there is a redirect, then the detector can override the url.
|
|
68
79
|
found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
|
|
69
80
|
end
|
|
70
81
|
raise "No known application found at #{app_url}" if found_apps.empty?
|
|
@@ -72,6 +83,39 @@ module Aspera
|
|
|
72
83
|
return found_apps
|
|
73
84
|
end
|
|
74
85
|
|
|
86
|
+
# To be called in public wizard method to get private key
|
|
87
|
+
# @return [Array] Private key path, pub key PEM
|
|
88
|
+
def ask_private_key(user:, url:, page:)
|
|
89
|
+
# Lets see if path to priv key is provided
|
|
90
|
+
private_key_path = options.get_option(:key_path)
|
|
91
|
+
# Give a chance to provide
|
|
92
|
+
if private_key_path.nil?
|
|
93
|
+
formatter.display_status('Path to private RSA key (leave empty to generate):')
|
|
94
|
+
private_key_path = options.get_option(:key_path, mandatory: true).to_s
|
|
95
|
+
end
|
|
96
|
+
# Else generate path
|
|
97
|
+
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME) if private_key_path.empty?
|
|
98
|
+
if File.exist?(private_key_path)
|
|
99
|
+
formatter.display_status('Using existing key:')
|
|
100
|
+
else
|
|
101
|
+
formatter.display_status("Generating #{OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
|
|
102
|
+
OAuth::Jwt.generate_rsa_private_key(path: private_key_path)
|
|
103
|
+
formatter.display_status('Created key:')
|
|
104
|
+
end
|
|
105
|
+
formatter.display_status(private_key_path)
|
|
106
|
+
private_key_pem = File.read(private_key_path)
|
|
107
|
+
pub_key_pem = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
|
|
108
|
+
options.set_option(:private_key, private_key_pem)
|
|
109
|
+
formatter.display_status("Please Log in as user #{user.red} at: #{url.red}")
|
|
110
|
+
formatter.display_status("Navigate to: #{page}")
|
|
111
|
+
formatter.display_status("Check or update the value to (#{'including BEGIN/END lines'.red}):".blink)
|
|
112
|
+
formatter.display_status(pub_key_pem, hide_secrets: false)
|
|
113
|
+
formatter.display_status('Once updated or validated, press [Enter].')
|
|
114
|
+
Environment.instance.open_uri(url)
|
|
115
|
+
$stdin.gets if required
|
|
116
|
+
private_key_path
|
|
117
|
+
end
|
|
118
|
+
|
|
75
119
|
# Wizard function, creates configuration
|
|
76
120
|
# @param apps [Array] list of detected apps
|
|
77
121
|
def find(apps)
|
|
@@ -88,49 +132,18 @@ module Aspera
|
|
|
88
132
|
Log.dump(:identification, identification)
|
|
89
133
|
wiz_url = identification[:url]
|
|
90
134
|
formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
|
|
91
|
-
#
|
|
135
|
+
# Set url for instantiation of plugin
|
|
92
136
|
options.add_option_preset({url: wiz_url}, 'wizard')
|
|
93
|
-
#
|
|
94
|
-
|
|
95
|
-
Aspera.assert(
|
|
137
|
+
# Instantiate plugin: command line options will be known, e.g. private_key, and wizard can be called
|
|
138
|
+
plugin_instance = Plugins::Factory.instance.plugin_class(identification[:product]).new(context: @parent.context)
|
|
139
|
+
Aspera.assert(plugin_instance.respond_to?(:wizard), type: Cli::BadArgument) do
|
|
96
140
|
"Detected: #{identification[:product]}, but this application has no wizard"
|
|
97
141
|
end
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
wiz_params = {
|
|
101
|
-
object: plugin_instance
|
|
102
|
-
}
|
|
103
|
-
# is private key needed ?
|
|
104
|
-
if options.known_options.key?(:private_key) &&
|
|
105
|
-
(!wiz_plugin_class.respond_to?(:private_key_required?) || wiz_plugin_class.private_key_required?(wiz_url))
|
|
106
|
-
# lets see if path to priv key is provided
|
|
107
|
-
private_key_path = options.get_option(:key_path)
|
|
108
|
-
# give a chance to provide
|
|
109
|
-
if private_key_path.nil?
|
|
110
|
-
formatter.display_status('Path to private RSA key (leave empty to generate):')
|
|
111
|
-
private_key_path = options.get_option(:key_path, mandatory: true).to_s
|
|
112
|
-
end
|
|
113
|
-
# else generate path
|
|
114
|
-
private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME) if private_key_path.empty?
|
|
115
|
-
if File.exist?(private_key_path)
|
|
116
|
-
formatter.display_status('Using existing key:')
|
|
117
|
-
else
|
|
118
|
-
formatter.display_status("Generating #{OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
|
|
119
|
-
OAuth::Jwt.generate_rsa_private_key(path: private_key_path)
|
|
120
|
-
formatter.display_status('Created key:')
|
|
121
|
-
end
|
|
122
|
-
formatter.display_status(private_key_path)
|
|
123
|
-
private_key_pem = File.read(private_key_path)
|
|
124
|
-
options.set_option(:private_key, private_key_pem)
|
|
125
|
-
wiz_params[:private_key_path] = private_key_path
|
|
126
|
-
wiz_params[:pub_key_pem] = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
|
|
127
|
-
end
|
|
128
|
-
Log.dump(:wiz_params, wiz_params)
|
|
129
|
-
# finally, call the wizard
|
|
130
|
-
wizard_result = wiz_plugin_class.wizard(**wiz_params)
|
|
142
|
+
# Call the wizard
|
|
143
|
+
wizard_result = plugin_instance.wizard(self, wiz_url)
|
|
131
144
|
Log.log.debug{"wizard result: #{wizard_result}"}
|
|
132
145
|
Aspera.assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
|
|
133
|
-
#
|
|
146
|
+
# Get preset name from user or default
|
|
134
147
|
if wiz_preset_name.empty?
|
|
135
148
|
elements = [
|
|
136
149
|
identification[:product],
|
|
@@ -139,18 +152,17 @@ module Aspera
|
|
|
139
152
|
elements.push(options.get_option(:username, mandatory: true)) unless wizard_result[:preset_value].key?(:link) rescue nil
|
|
140
153
|
wiz_preset_name = elements.join('_').strip.downcase.gsub(/[^a-z0-9]/, '_').squeeze('_')
|
|
141
154
|
end
|
|
142
|
-
# test mode does not change conf file
|
|
143
|
-
return Main.result_single_object(wizard_result) if options.get_option(:test_mode)
|
|
144
155
|
# Write configuration file
|
|
145
156
|
formatter.display_status("Preparing preset: #{wiz_preset_name}")
|
|
146
|
-
#
|
|
157
|
+
# Init defaults if necessary
|
|
147
158
|
option_override = options.get_option(:override, mandatory: true)
|
|
148
159
|
option_default = options.get_option(:default, mandatory: true)
|
|
149
160
|
config.defaults_set(identification[:product], wiz_preset_name, wizard_result[:preset_value].stringify_keys, option_default, option_override)
|
|
150
161
|
test_args = wizard_result[:test_args]
|
|
151
162
|
test_args = "-P#{wiz_preset_name} #{test_args}" unless option_default
|
|
152
163
|
# TODO: actually test the command
|
|
153
|
-
|
|
164
|
+
test_cmd = "#{Info::CMD_NAME} #{identification[:product]} #{test_args}"
|
|
165
|
+
return Main.result_status("You can test with:\n#{test_cmd.red}")
|
|
154
166
|
end
|
|
155
167
|
end
|
|
156
168
|
end
|
data/lib/aspera/colors.rb
CHANGED
|
@@ -57,10 +57,16 @@ class String
|
|
|
57
57
|
define_method(name){self}
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
+
|
|
60
61
|
# Transform capitalized to snake case
|
|
61
62
|
def capital_to_snake
|
|
62
63
|
return gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
63
64
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
64
65
|
.downcase
|
|
65
66
|
end
|
|
67
|
+
|
|
68
|
+
# Transform snake case to capitalized
|
|
69
|
+
def snake_to_capital
|
|
70
|
+
split('_').map(&:capitalize).join
|
|
71
|
+
end
|
|
66
72
|
end
|
|
@@ -4,43 +4,31 @@ require 'aspera/log'
|
|
|
4
4
|
require 'aspera/assert'
|
|
5
5
|
require 'yaml'
|
|
6
6
|
module Aspera
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# process_param is called repeatedly with all known parameters
|
|
10
|
-
# add_env_args is called to get resulting param list and env var (also checks that all params were used)
|
|
7
|
+
# Helper class to build command line from a parameter list (key-value hash)
|
|
8
|
+
# Constructor takes hash: `{ 'param1':'value1', ...}`
|
|
9
|
+
# `process_param` is called repeatedly with all known parameters
|
|
10
|
+
# `add_env_args` is called to get resulting param list and env var (also checks that all params were used)
|
|
11
11
|
class CommandLineBuilder
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
items
|
|
33
|
-
properties
|
|
34
|
-
required
|
|
35
|
-
$comment
|
|
36
|
-
x-cli-envvar
|
|
37
|
-
x-cli-option
|
|
38
|
-
x-cli-switch
|
|
39
|
-
x-cli-special
|
|
40
|
-
x-cli-convert
|
|
41
|
-
x-agents
|
|
42
|
-
x-ts-name
|
|
43
|
-
x-deprecation
|
|
12
|
+
# Supported keys in JSON schema
|
|
13
|
+
PROPERTY_KEYS = [
|
|
14
|
+
'description', # [String] Description
|
|
15
|
+
'type', # [String,Array] Accepted type(s) for non-enum
|
|
16
|
+
'default', # [String] Default value if not specified
|
|
17
|
+
'enum', # [Array] Set with list of values for enum types accepted in transfer spec
|
|
18
|
+
'items', # [Array]
|
|
19
|
+
'properties', # [Array]
|
|
20
|
+
'required', # [Array]
|
|
21
|
+
'$comment', # [String]
|
|
22
|
+
'x-cli-envvar', # [String] Name of env var
|
|
23
|
+
'x-cli-option', # [String] Command line option (starts with "-")
|
|
24
|
+
'x-cli-short', # [String] Command line option (starts with "-")
|
|
25
|
+
'x-cli-switch', # [Bool] `true` if option has no arg, else by default option has a value
|
|
26
|
+
'x-cli-special', # [Bool] `true` if special handling (deferred)
|
|
27
|
+
'x-cli-convert', # [String,Hash] Method name for Convert object or Conversion for enum ts to arg
|
|
28
|
+
'x-agents', # [Array] Supported agents (for doc only), if not specified: all
|
|
29
|
+
'x-ts-name', # [Bool,String] (async) true if same name in transfer spec, else real name in transfer spec, else ignored
|
|
30
|
+
'x-ts-convert', # [String] (async) Name of methods to convert value from transfer spec to `conf` API.
|
|
31
|
+
'x-deprecation' # [String] Deprecation message for doc
|
|
44
32
|
].freeze
|
|
45
33
|
|
|
46
34
|
CLI_AGENT = 'direct'
|
|
@@ -53,17 +41,23 @@ module Aspera
|
|
|
53
41
|
!properties.key?('x-agents') || properties['x-agents'].include?(agent)
|
|
54
42
|
end
|
|
55
43
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
# Fill default values for some fields in the schema
|
|
45
|
+
# @param schema [Hash] The JSON schema
|
|
46
|
+
def validate_schema(schema, ascp: false)
|
|
47
|
+
schema['properties'].each do |name, info|
|
|
59
48
|
Aspera.assert_type(info, Hash){"#{info.class} for #{name}"}
|
|
60
49
|
unsupported_keys = info.keys - PROPERTY_KEYS
|
|
61
50
|
Aspera.assert(unsupported_keys.empty?){"Unsupported definition keys: #{unsupported_keys}"}
|
|
62
|
-
#
|
|
63
|
-
info['type'] ||= info['x-cli-switch'] ? 'boolean' : 'string'
|
|
64
|
-
|
|
65
|
-
info['
|
|
51
|
+
# By default : string, unless it's without arg (switch)
|
|
52
|
+
# info['type'] ||= info['x-cli-switch'] ? 'boolean' : 'string'
|
|
53
|
+
Aspera.assert(info.key?('type') || info.key?('enum')){"Missing type for #{name} in #{schema['description']}"}
|
|
54
|
+
Aspera.assert(info['type'].eql?('boolean')){"switch must be bool: #{name}"} if info['x-cli-switch']
|
|
55
|
+
# Add default cli option name if not present, and if supported in "direct".
|
|
56
|
+
info['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if info['x-cli-option'].eql?(true) || (info['x-cli-switch'].eql?(true) && !info.key?('x-cli-option'))
|
|
57
|
+
Aspera.assert(%w[x-cli-option x-cli-envvar x-cli-special].any?{ |i| info.key?(i)}, type: :warn){name} if ascp && supported_by_agent(CLI_AGENT, info)
|
|
58
|
+
# info['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if ascp && !info.key?('x-cli-option') && !info['x-cli-envvar'] && (info.key?('x-cli-switch') || supported_by_agent(CLI_AGENT, info))
|
|
66
59
|
info.freeze
|
|
60
|
+
validate_schema(info, ascp: ascp) if info['type'].eql?('object') && info['properties']
|
|
67
61
|
end
|
|
68
62
|
end
|
|
69
63
|
|
|
@@ -98,10 +92,10 @@ module Aspera
|
|
|
98
92
|
# Add processed parameters to env and args, warns about unused parameters
|
|
99
93
|
# @param [Hash] env_args with :env and :args
|
|
100
94
|
def add_env_args(env_args)
|
|
101
|
-
Log.
|
|
95
|
+
Log.dump(:env_args, @result)
|
|
102
96
|
# warn about non translated arguments
|
|
103
97
|
@object.each_pair do |name, value|
|
|
104
|
-
Log.log.warn{
|
|
98
|
+
Log.log.warn{"Unknown transfer spec parameter: #{name} = \"#{value}\""} unless @processed_parameters.include?(name)
|
|
105
99
|
end
|
|
106
100
|
# set result
|
|
107
101
|
env_args[:env].merge!(@result[:env])
|
|
@@ -109,9 +103,10 @@ module Aspera
|
|
|
109
103
|
return
|
|
110
104
|
end
|
|
111
105
|
|
|
112
|
-
#
|
|
113
|
-
def add_command_line_options(options)
|
|
114
|
-
|
|
106
|
+
# Add options directly to command line
|
|
107
|
+
def add_command_line_options(*options)
|
|
108
|
+
options = options.first if options.first.is_a?(Array) && options.length.eql?(1)
|
|
109
|
+
Aspera.assert_type(options, Array)
|
|
115
110
|
options.each{ |o| @result[:args].push(o.to_s)}
|
|
116
111
|
end
|
|
117
112
|
|
|
@@ -198,13 +193,13 @@ module Aspera
|
|
|
198
193
|
else Aspera.error_unexpected_value(parameter_value){name}
|
|
199
194
|
end
|
|
200
195
|
# add_param = !add_param if properties[:add_on_false]
|
|
201
|
-
add_command_line_options(
|
|
196
|
+
add_command_line_options(properties['x-cli-option']) if add_param
|
|
202
197
|
else
|
|
203
198
|
# transform into command line option with value
|
|
204
199
|
# parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
|
|
205
200
|
parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
|
|
206
201
|
# if transfer_spec value is an array, applies option many times
|
|
207
|
-
parameter_value.each{ |v| add_command_line_options(
|
|
202
|
+
parameter_value.each{ |v| add_command_line_options(properties['x-cli-option'], v)}
|
|
208
203
|
end
|
|
209
204
|
end
|
|
210
205
|
end
|
data/lib/aspera/coverage.rb
CHANGED
|
@@ -5,7 +5,7 @@ if ENV.key?('ENABLE_COVERAGE')
|
|
|
5
5
|
require 'simplecov'
|
|
6
6
|
require 'securerandom'
|
|
7
7
|
# compute development top folder based on this source location
|
|
8
|
-
development_root =
|
|
8
|
+
development_root = File.dirname(File.realpath(__FILE__), 3)
|
|
9
9
|
SimpleCov.root(development_root)
|
|
10
10
|
SimpleCov.enable_for_subprocesses if SimpleCov.respond_to?(:enable_for_subprocesses)
|
|
11
11
|
# keep cache data for 1 day (must be longer that time to run the whole test suite)
|
|
@@ -19,7 +19,7 @@ module Aspera
|
|
|
19
19
|
# @return [String] decoded data
|
|
20
20
|
def item(name)
|
|
21
21
|
index = ELEMENTS.index(name)
|
|
22
|
-
raise
|
|
22
|
+
raise ParameterError, "Unknown data item #{name} (#{name.class})" unless index
|
|
23
23
|
raw_data = data(START_INDEX + index)
|
|
24
24
|
case name
|
|
25
25
|
when :dsa, :rsa
|
data/lib/aspera/environment.rb
CHANGED
|
@@ -38,6 +38,8 @@ module Aspera
|
|
|
38
38
|
WINDOWS_FILENAME_INVALID_CHARACTERS = '<>:"/\\|?*'
|
|
39
39
|
REPLACE_CHARACTER = '_'
|
|
40
40
|
|
|
41
|
+
RB_EXT = '.rb'
|
|
42
|
+
|
|
41
43
|
class << self
|
|
42
44
|
def ruby_version
|
|
43
45
|
return RbConfig::CONFIG['RUBY_PROGRAM_VERSION']
|
|
@@ -186,7 +188,7 @@ module Aspera
|
|
|
186
188
|
end
|
|
187
189
|
end
|
|
188
190
|
attr_accessor :url_method, :file_illegal_characters
|
|
189
|
-
attr_reader :os, :cpu, :
|
|
191
|
+
attr_reader :os, :cpu, :default_gui_mode
|
|
190
192
|
|
|
191
193
|
def initialize
|
|
192
194
|
initialize_fields
|
|
@@ -218,7 +220,7 @@ module Aspera
|
|
|
218
220
|
CPU_ARM64
|
|
219
221
|
else Aspera.error_unexpected_value(RbConfig::CONFIG['host_cpu']){'host_cpu'}
|
|
220
222
|
end
|
|
221
|
-
@executable_extension = @os.eql?(OS_WINDOWS) ? 'exe' : nil
|
|
223
|
+
@executable_extension = @os.eql?(OS_WINDOWS) ? '.exe' : nil
|
|
222
224
|
# :text or :graphical depending on the environment
|
|
223
225
|
@default_gui_mode =
|
|
224
226
|
if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(os) ||
|
|
@@ -239,8 +241,10 @@ module Aspera
|
|
|
239
241
|
"#{@os}-#{@cpu}"
|
|
240
242
|
end
|
|
241
243
|
|
|
242
|
-
# executable file extension for current OS
|
|
243
|
-
|
|
244
|
+
# Add executable file extension (e.g. ".exe") for current OS
|
|
245
|
+
# @param name [String,nil] Path or file name
|
|
246
|
+
# @return [String] Executable name with extension
|
|
247
|
+
def exe_file(name = nil)
|
|
244
248
|
return name unless @executable_extension
|
|
245
249
|
return "#{name}#{@executable_extension}"
|
|
246
250
|
end
|
|
@@ -314,9 +318,8 @@ module Aspera
|
|
|
314
318
|
# Windows does not allow file name:
|
|
315
319
|
# - with control characters anywhere
|
|
316
320
|
# - ending with space or dot
|
|
317
|
-
filename = filename
|
|
318
|
-
|
|
319
|
-
.sub(/[. ]+\z/, safe_char)
|
|
321
|
+
filename = filename.gsub(/[\x00-\x1F\x7F]/, safe_char)
|
|
322
|
+
filename = filename.chop while filename.end_with?(' ', '.')
|
|
320
323
|
if @file_illegal_characters&.size.to_i >= 2
|
|
321
324
|
# replace all illegal characters with safe_char
|
|
322
325
|
filename = filename.tr(@file_illegal_characters[1..-1], safe_char)
|
data/lib/aspera/faspex_gw.rb
CHANGED
|
@@ -8,10 +8,12 @@ require 'json'
|
|
|
8
8
|
module Aspera
|
|
9
9
|
# Simulate the Faspex 4 /send API and creates a package on Aspera on Cloud or Faspex 5
|
|
10
10
|
class Faspex4GWServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
11
|
+
AOC_API = 'Aspera::Api::AoC'
|
|
12
|
+
FX_API = 'Aspera::Api::Faspex'
|
|
11
13
|
# @param app_api [Rest] API object
|
|
12
14
|
# @param app_context [String] workspace id (aoc only)
|
|
13
15
|
def initialize(server, app_api, app_context)
|
|
14
|
-
Aspera.assert_values(app_api.class.name, [
|
|
16
|
+
Aspera.assert_values(app_api.class.name, [AOC_API, FX_API])
|
|
15
17
|
super(server)
|
|
16
18
|
@app_api = app_api
|
|
17
19
|
@app_context = app_context
|
|
@@ -49,7 +51,7 @@ module Aspera
|
|
|
49
51
|
transfer_spec = @app_api.call(
|
|
50
52
|
operation: 'POST',
|
|
51
53
|
subpath: "packages/#{package['id']}/transfer_spec/upload",
|
|
52
|
-
query: {transfer_type:
|
|
54
|
+
query: {transfer_type: Api::Faspex::TRANSFER_CONNECT},
|
|
53
55
|
content_type: Rest::MIME_JSON,
|
|
54
56
|
body: {paths: [{'destination'=>'/'}]},
|
|
55
57
|
headers: {'Accept' => Rest::MIME_JSON}
|
|
@@ -72,9 +74,9 @@ module Aspera
|
|
|
72
74
|
# compare string, as class is not yet known here
|
|
73
75
|
faspex_package_create_result =
|
|
74
76
|
case @app_api.class.name
|
|
75
|
-
when
|
|
77
|
+
when AOC_API
|
|
76
78
|
faspex4_send_to_aoc(faspex_pkg_parameters)
|
|
77
|
-
when
|
|
79
|
+
when FX_API
|
|
78
80
|
faspex4_send_to_faspex5(faspex_pkg_parameters)
|
|
79
81
|
else Aspera.error_unexpected_value(@app_api.class.name)
|
|
80
82
|
end
|
|
@@ -8,7 +8,7 @@ require 'aspera/log'
|
|
|
8
8
|
require 'aspera/assert'
|
|
9
9
|
|
|
10
10
|
module Aspera
|
|
11
|
-
#
|
|
11
|
+
# Start a Faspex-4 style post-processing script using Faspex-5 webhook
|
|
12
12
|
class Faspex4PostProcServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
13
13
|
ALLOWED_PARAMETERS = %i[root script_folder fail_on_error timeout_seconds].freeze
|
|
14
14
|
def initialize(server, parameters)
|
|
@@ -73,7 +73,7 @@ module Aspera
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def list(options = {})
|
|
76
|
-
Aspera.assert_values(options[:domain], DOMAINS, type:
|
|
76
|
+
Aspera.assert_values(options[:domain], DOMAINS, type: ParameterError){'domain'} unless options[:domain].nil?
|
|
77
77
|
key_chains(execute('list-keychains', options, LIST_OPTIONS))
|
|
78
78
|
end
|
|
79
79
|
|
data/lib/aspera/log.rb
CHANGED
|
@@ -15,7 +15,7 @@ $VERBOSE = nil
|
|
|
15
15
|
|
|
16
16
|
# Extend Ruby logger with trace levels
|
|
17
17
|
class Logger
|
|
18
|
-
# Two
|
|
18
|
+
# Two additional trace levels
|
|
19
19
|
TRACE_MAX = 2
|
|
20
20
|
|
|
21
21
|
# Add custom level to logger severity, below debug level
|
|
@@ -23,9 +23,12 @@ class Logger
|
|
|
23
23
|
1.upto(TRACE_MAX).each{ |level| const_set(:"TRACE#{level}", - level)}
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# Hash
|
|
26
|
+
# Hash
|
|
27
|
+
# key [Integer] Log level (e.g. 0 for DEBUG)
|
|
28
|
+
# value [Symbol] Uppercase log level label (e.g. :DEBUG)
|
|
27
29
|
SEVERITY_LABEL = Severity.constants.each_with_object({}){ |name, hash| hash[Severity.const_get(name)] = name}
|
|
28
30
|
|
|
31
|
+
# Override
|
|
29
32
|
# @param severity [Integer] Log severity as int
|
|
30
33
|
# @return [String] Log severity upper case label
|
|
31
34
|
def format_severity(severity)
|
|
@@ -106,6 +109,31 @@ module Aspera
|
|
|
106
109
|
ensure
|
|
107
110
|
$stderr = real_stderr
|
|
108
111
|
end
|
|
112
|
+
|
|
113
|
+
# Returns the last 2 containers (module/class) and method caller
|
|
114
|
+
def caller_method
|
|
115
|
+
stack = caller
|
|
116
|
+
i = stack.rindex{ |line| line.include?('Logger')}
|
|
117
|
+
frame = stack[i + 1] if i && stack[i + 1]
|
|
118
|
+
return '???' unless frame
|
|
119
|
+
# Extract the "Class::Module::Method" or "Class#method" part
|
|
120
|
+
full = frame[/'([^']+)'/, 1]
|
|
121
|
+
return '???' unless full
|
|
122
|
+
# Split into class/module and method parts
|
|
123
|
+
parts = full.split(/(::|#)/)
|
|
124
|
+
# Reconstruct keeping only last two class/module names + separator + method
|
|
125
|
+
if parts.include?('#')
|
|
126
|
+
sep_index = parts.index('#')
|
|
127
|
+
classes = parts[0...sep_index].join
|
|
128
|
+
method = parts[sep_index + 1]
|
|
129
|
+
else
|
|
130
|
+
classes = parts[0..-2].join
|
|
131
|
+
method = parts.last
|
|
132
|
+
end
|
|
133
|
+
class_parts = classes.split('::')
|
|
134
|
+
selected_classes = class_parts.last(2).join('::')
|
|
135
|
+
"#{selected_classes}.#{method}"
|
|
136
|
+
end
|
|
109
137
|
end
|
|
110
138
|
|
|
111
139
|
attr_reader :logger_type, :logger
|
|
@@ -119,6 +147,7 @@ module Aspera
|
|
|
119
147
|
# Set log level of underlying logger given symbol level
|
|
120
148
|
# @param new_level [Symbol] One of LEVELS
|
|
121
149
|
def level=(new_level)
|
|
150
|
+
Aspera.assert_values(new_level, LEVELS)
|
|
122
151
|
@logger.level = Logger::Severity.const_get(new_level.to_sym.upcase)
|
|
123
152
|
end
|
|
124
153
|
|
|
@@ -140,10 +169,8 @@ module Aspera
|
|
|
140
169
|
# Get symbol of debug level of underlying logger
|
|
141
170
|
# @return [Symbol] One of LEVELS
|
|
142
171
|
def level
|
|
143
|
-
Logger::
|
|
144
|
-
|
|
145
|
-
end
|
|
146
|
-
Aspera.error_unexpected_value(@logger.level){'log level'}
|
|
172
|
+
Aspera.assert(Logger::SEVERITY_LABEL.key?(@logger.level))
|
|
173
|
+
Logger::SEVERITY_LABEL[@logger.level].downcase
|
|
147
174
|
end
|
|
148
175
|
|
|
149
176
|
# Change underlying logger, but keep log level
|
|
@@ -158,8 +185,8 @@ module Aspera
|
|
|
158
185
|
@logger = Logger.new($stdout, progname: @program_name, formatter: DEFAULT_FORMATTER)
|
|
159
186
|
when :syslog
|
|
160
187
|
require 'syslog/logger'
|
|
161
|
-
#
|
|
162
|
-
#
|
|
188
|
+
# The syslog class automatically creates methods from the severity names.
|
|
189
|
+
# We just need to add the mapping (but syslog lowest is DEBUG)
|
|
163
190
|
1.upto(Logger::TRACE_MAX).each do |level|
|
|
164
191
|
Syslog::Logger.const_get(:LEVEL_MAP)[Logger.const_get("TRACE#{level}")] = Syslog::LOG_DEBUG
|
|
165
192
|
end
|
|
@@ -207,7 +234,8 @@ module Aspera
|
|
|
207
234
|
# pre-defined formatters
|
|
208
235
|
FORMATTERS = {
|
|
209
236
|
standard: Logger::Formatter.new,
|
|
210
|
-
default: DEFAULT_FORMATTER
|
|
237
|
+
default: DEFAULT_FORMATTER,
|
|
238
|
+
caller: ->(s, _d, _p, m){"#{LVL_COLOR[s]} #{Log.caller_method}\n#{m}\n"}
|
|
211
239
|
}.freeze
|
|
212
240
|
|
|
213
241
|
private_constant :LVL_DECO, :LVL_COLOR, :DEFAULT_FORMATTER, :FORMATTERS
|
data/lib/aspera/nagios.rb
CHANGED
|
@@ -57,7 +57,7 @@ module Aspera
|
|
|
57
57
|
# compare remote time with local time
|
|
58
58
|
def check_time_offset(remote_date, component)
|
|
59
59
|
# check date if specified : 2015-10-13T07:32:01Z
|
|
60
|
-
remote_time = Time.
|
|
60
|
+
remote_time = Time.parse(remote_date)
|
|
61
61
|
diff_time = (remote_time - Time.now).abs
|
|
62
62
|
diff_rounded = diff_time.round(-2)
|
|
63
63
|
Log.log.debug{"DATE: #{remote_date} #{remote_time} diff=#{diff_rounded}"}
|