aspera-cli 4.26.0 → 4.26.1
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 +17 -3
- data/lib/aspera/api/aoc.rb +2 -1
- data/lib/aspera/api/node.rb +2 -2
- data/lib/aspera/ascp/installation.rb +7 -4
- data/lib/aspera/assert.rb +17 -13
- data/lib/aspera/cli/extended_value.rb +6 -2
- data/lib/aspera/cli/formatter.rb +65 -60
- data/lib/aspera/cli/main.rb +69 -10
- data/lib/aspera/cli/manager.rb +130 -76
- data/lib/aspera/cli/options.schema.yaml +82 -0
- data/lib/aspera/cli/plugins/aoc.rb +36 -11
- data/lib/aspera/cli/plugins/base.rb +46 -37
- data/lib/aspera/cli/plugins/config.rb +9 -9
- data/lib/aspera/cli/plugins/faspex.rb +1 -1
- data/lib/aspera/cli/plugins/faspex5.rb +4 -5
- data/lib/aspera/cli/plugins/node.rb +1 -1
- data/lib/aspera/cli/sync_actions.rb +1 -1
- data/lib/aspera/cli/transfer_agent.rb +17 -15
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +22 -18
- data/lib/aspera/environment.rb +3 -3
- data/lib/aspera/formatter_interface.rb +14 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/log.rb +4 -3
- data/lib/aspera/markdown.rb +4 -1
- data/lib/aspera/oauth/factory.rb +1 -1
- data/lib/aspera/proxy_auto_config.rb +3 -0
- data/lib/aspera/rest.rb +1 -1
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62801 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +8898 -0
- data/lib/aspera/schema/documentation.rb +107 -0
- data/lib/aspera/schema/reader.rb +75 -0
- data/lib/aspera/schema/registry.rb +63 -0
- data/lib/aspera/sync/conf.schema.yaml +0 -26
- data/lib/aspera/sync/operations.rb +9 -5
- data/lib/aspera/transfer/faux_file.rb +1 -1
- data/lib/aspera/transfer/resumer.rb +1 -1
- data/lib/aspera/transfer/spec.rb +3 -3
- data/lib/aspera/transfer/spec.schema.yaml +1 -1
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/yaml.rb +4 -2
- data.tar.gz.sig +0 -0
- metadata +9 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/transfer/spec_doc.rb +0 -76
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/agent/factory'
|
|
4
|
+
require 'aspera/markdown'
|
|
5
|
+
|
|
6
|
+
module Aspera
|
|
7
|
+
module Schema
|
|
8
|
+
# Generate documentation from Schema, for Transfer Spec, or async Conf spec
|
|
9
|
+
class Documentation
|
|
10
|
+
# @param formatter [Cli::Formatter] Formatter instance with methods: markdown_text, tick, check_row
|
|
11
|
+
# @param schema [Reader]
|
|
12
|
+
# @param include_option [Boolean] `true`: include CLI options (switches, env vars) in descriptions
|
|
13
|
+
# @param agent_columns [Boolean] `true`: add separate columns for each transfer agent compatibility
|
|
14
|
+
# @param code_highlight [Boolean] `true`: format name and type as code
|
|
15
|
+
def initialize(formatter, schema, include_option: false, agent_columns: false, code_highlight: false)
|
|
16
|
+
@formatter = formatter
|
|
17
|
+
@schema = schema
|
|
18
|
+
@include_option = include_option
|
|
19
|
+
@agent_columns = agent_columns
|
|
20
|
+
@code_highlight = code_highlight
|
|
21
|
+
@columns = %i[name type description]
|
|
22
|
+
@columns.insert(-2, *Agent::Factory::ALL.values.map{ |i| i[:short]}.sort) if @agent_columns
|
|
23
|
+
# @type [Array<Hash<Symbol,String>>]
|
|
24
|
+
@rows = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def rows
|
|
28
|
+
@rows.sort_by{ |i| i[:name]}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Array<String>]
|
|
32
|
+
def columns
|
|
33
|
+
@columns.map(&:to_s)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# First row is the titles
|
|
37
|
+
# @return [Array<Array<String>>]
|
|
38
|
+
def table
|
|
39
|
+
[@columns.map(&:to_s)] + @rows.sort_by{ |i| i[:name]}.map{ |row| @columns.map{ |field| row[field]}}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Generate a documentation table from a JSON schema for transfer specifications
|
|
43
|
+
#
|
|
44
|
+
# Recursively processes a JSON schema to create a formatted table for manual documentation.
|
|
45
|
+
# Handles nested objects, arrays, and extracts metadata (descriptions, types, enums, deprecations).
|
|
46
|
+
#
|
|
47
|
+
# @param schema [Reader] The JSON schema to process
|
|
48
|
+
# @return [Documentation]
|
|
49
|
+
def build(schema = nil)
|
|
50
|
+
code = @code_highlight ? ->(c){"`#{c}`"} : ->(c){c}
|
|
51
|
+
schema ||= @schema
|
|
52
|
+
schema.each_property do |property_schema, _name, property_full_name|
|
|
53
|
+
node = property_schema.current
|
|
54
|
+
# Manual table
|
|
55
|
+
item = {
|
|
56
|
+
name: code.call(property_full_name),
|
|
57
|
+
type: code.call(node['type']),
|
|
58
|
+
description: []
|
|
59
|
+
}
|
|
60
|
+
# Render Markdown formatting and split lines
|
|
61
|
+
item[:description] =
|
|
62
|
+
node['description']
|
|
63
|
+
.gsub(Markdown::FORMATS){@formatter.markdown_text(Regexp.last_match)}
|
|
64
|
+
.split("\n") if node.key?('description')
|
|
65
|
+
item[:description].unshift("DEPRECATED: #{node['x-deprecation']}") if node.key?('x-deprecation')
|
|
66
|
+
# Add flags for supported agents in doc
|
|
67
|
+
agents = []
|
|
68
|
+
Agent::Factory::ALL.each_key do |sym|
|
|
69
|
+
agents.push(sym) if node['x-agents'].nil? || node['x-agents'].include?(sym.to_s)
|
|
70
|
+
end
|
|
71
|
+
Aspera.assert(agents.include?(:direct)){"#{name}: x-cli-option requires agent direct (or nil)"} if node['x-cli-option']
|
|
72
|
+
if @agent_columns
|
|
73
|
+
Agent::Factory::ALL.each do |sym, names|
|
|
74
|
+
item[names[:short]] = @formatter.tick(agents.include?(sym))
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
item[:description].push("(#{agents.map{ |i| Agent::Factory::ALL[i][:short].to_s.upcase}.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length)
|
|
78
|
+
end
|
|
79
|
+
# Only keep lines that are usable in supported agents
|
|
80
|
+
next false if agents.empty?
|
|
81
|
+
item[:description].push("Allowed values: #{node['enum'].map{ |v| @formatter.markdown_text("`#{v}`")}.join(', ')}.") if node.key?('enum')
|
|
82
|
+
item[:description].push("Default: #{code.call(node['default'])}.") if node.key?('default')
|
|
83
|
+
if @include_option
|
|
84
|
+
envvar_prefix = ''
|
|
85
|
+
cli_option =
|
|
86
|
+
if node.key?('x-cli-envvar')
|
|
87
|
+
envvar_prefix = 'env:'
|
|
88
|
+
node['x-cli-envvar']
|
|
89
|
+
elsif node['x-cli-switch']
|
|
90
|
+
node['x-cli-option']
|
|
91
|
+
elsif node['x-cli-option']
|
|
92
|
+
arg_type = node.key?('enum') ? '{enum}' : "{#{[node['type']].flatten.join('|')}}"
|
|
93
|
+
# conversion_tag = node['x-cli-convert']
|
|
94
|
+
conversion_tag = node.key?('x-cli-convert') ? 'conversion' : nil
|
|
95
|
+
sep = node['x-cli-option'].start_with?('--') ? '=' : ' '
|
|
96
|
+
"#{node['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
|
|
97
|
+
end
|
|
98
|
+
short = node.key?('x-cli-short') ? "(#{node['x-cli-short']})" : nil
|
|
99
|
+
item[:description].push("(#{'special:' if node['x-cli-special']}#{envvar_prefix}#{@formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option
|
|
100
|
+
end
|
|
101
|
+
@rows.push(@formatter.check_row(item))
|
|
102
|
+
end
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aspera
|
|
4
|
+
# base class for plugins modules
|
|
5
|
+
module Schema
|
|
6
|
+
# JSON schema reader
|
|
7
|
+
class Reader
|
|
8
|
+
attr_reader :current
|
|
9
|
+
|
|
10
|
+
# Shortcut to access current value at path
|
|
11
|
+
# @param x [String] path element
|
|
12
|
+
# @return [Hash, Array, String, Integer] current value at path
|
|
13
|
+
def [](x)
|
|
14
|
+
@current[x]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Find sub path relative to current
|
|
18
|
+
# Honors $ref
|
|
19
|
+
def dig(*path)
|
|
20
|
+
current = @current
|
|
21
|
+
path.each do |p|
|
|
22
|
+
Aspera.assert(current.key?(p)){"schema: #{p} in #{path}"}
|
|
23
|
+
current = current[p]
|
|
24
|
+
Aspera.assert_type(current, Hash){'schema'}
|
|
25
|
+
if current.key?('$ref')
|
|
26
|
+
ref = current['$ref']
|
|
27
|
+
Aspera.assert(ref.start_with?('#/'))
|
|
28
|
+
current = @root.dig(*ref[2..].split('/'))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
Reader.new(@root, current)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Read schema from file or from cache
|
|
35
|
+
# @param root [Hash] root schema
|
|
36
|
+
# @param current [Hash, nil] current position in
|
|
37
|
+
# @return [Hash, nil] schema
|
|
38
|
+
def initialize(root, current = nil)
|
|
39
|
+
@root = root
|
|
40
|
+
@current = current || root
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Recursively traverse schema properties with a block
|
|
44
|
+
# Handles nested objects and arrays automatically
|
|
45
|
+
# @param prefix [String] Prefix for property names (e.g., 'parent.child.')
|
|
46
|
+
# @yield [property_schema, name, full_name] Yields property info to block
|
|
47
|
+
# @yieldparam property_schema [Reader] Schema reader for this property (use .current to get node hash)
|
|
48
|
+
# @yieldparam name [String] Property name
|
|
49
|
+
# @yieldparam full_name [String] Full property name with prefix
|
|
50
|
+
# @return [nil]
|
|
51
|
+
def each_property(prefix = '', &block)
|
|
52
|
+
properties = dig('properties')
|
|
53
|
+
properties.current.each_key do |name|
|
|
54
|
+
property_full_name = "#{prefix}#{name}"
|
|
55
|
+
property_schema = properties.dig(name)
|
|
56
|
+
node = property_schema.current
|
|
57
|
+
|
|
58
|
+
# Yield current property to block
|
|
59
|
+
yield(property_schema, name, property_full_name)
|
|
60
|
+
|
|
61
|
+
# Recursively process nested structures
|
|
62
|
+
case node['type']
|
|
63
|
+
when 'object'
|
|
64
|
+
property_schema.each_property("#{property_full_name}.", &block) if node['properties']
|
|
65
|
+
when 'array'
|
|
66
|
+
if node['items']
|
|
67
|
+
array_item_schema = property_schema.dig('items')
|
|
68
|
+
array_item_schema.each_property("#{property_full_name}[].", &block) if array_item_schema.current['properties']
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'aspera/yaml'
|
|
5
|
+
require 'aspera/schema/reader'
|
|
6
|
+
|
|
7
|
+
module Aspera
|
|
8
|
+
# base class for plugins modules
|
|
9
|
+
module Schema
|
|
10
|
+
class Registry
|
|
11
|
+
include Singleton
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def known?(sym)
|
|
15
|
+
LOCATIONS.key?(sym)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def req_body(component, endpoint)
|
|
19
|
+
"#{component}:paths./#{endpoint}.requestBody.content.application/json.schema"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
LOCATIONS = {
|
|
24
|
+
spec: 'aspera/transfer/spec.schema.yaml',
|
|
25
|
+
args: 'aspera/sync/args.schema.yaml',
|
|
26
|
+
conf: 'aspera/sync/conf.schema.yaml',
|
|
27
|
+
opts: 'aspera/cli/options.schema.yaml',
|
|
28
|
+
aoc: 'aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml',
|
|
29
|
+
faspex: 'aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
OPTIONS = 'opts'
|
|
33
|
+
TRANSFER_SPEC = 'spec'
|
|
34
|
+
SYNC_CONF = 'conf'
|
|
35
|
+
SYNC_ARGS = 'args'
|
|
36
|
+
AOC = 'aoc'
|
|
37
|
+
FASPEX = 'faspex'
|
|
38
|
+
TRANSFER_INFO = "#{OPTIONS}:components.schemas.TransferInfo"
|
|
39
|
+
|
|
40
|
+
REQ_BODY = '.requestBody.content.application/json.schema'
|
|
41
|
+
|
|
42
|
+
def initialize
|
|
43
|
+
@cache = {}
|
|
44
|
+
@main_folder = File.expand_path('../..', __dir__)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Read schema from file or from cache
|
|
48
|
+
# @param name_path [String] one of the keys in LOCATIONS, with optional :<path> suffix
|
|
49
|
+
# @return [Reader] schema
|
|
50
|
+
def reader(name_path)
|
|
51
|
+
name, path = name_path.split(':', 2)
|
|
52
|
+
sym = name.to_sym
|
|
53
|
+
Aspera.assert(Registry.known?(sym)){"schema: #{sym}"}
|
|
54
|
+
spec_file = File.join(@main_folder, LOCATIONS[sym])
|
|
55
|
+
@cache[sym] = Yaml.safe_load(File.read(spec_file)) if spec_file.end_with?('.yaml') && !@cache.key?(sym)
|
|
56
|
+
@cache[sym] = JSON.parse(File.read(spec_file)) if spec_file.end_with?('.json') && !@cache.key?(sym)
|
|
57
|
+
reader = Reader.new(@cache[sym])
|
|
58
|
+
return reader unless path
|
|
59
|
+
reader.dig(*path.split('.'))
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -16,7 +16,6 @@ properties:
|
|
|
16
16
|
ascp_dir:
|
|
17
17
|
description: Directory containing ascp executable to use.
|
|
18
18
|
type: string
|
|
19
|
-
default: ""
|
|
20
19
|
assume_no_mods:
|
|
21
20
|
description: Assume that the directory structure has not been modified.
|
|
22
21
|
type: boolean
|
|
@@ -52,7 +51,6 @@ properties:
|
|
|
52
51
|
cookie:
|
|
53
52
|
description: User-defined identification string.
|
|
54
53
|
type: string
|
|
55
|
-
default: ""
|
|
56
54
|
x-cli-option: true
|
|
57
55
|
cooloff_max_seconds:
|
|
58
56
|
description:
|
|
@@ -153,7 +151,6 @@ properties:
|
|
|
153
151
|
absolute:
|
|
154
152
|
description: UTC timestamp. Empty value for disabled.
|
|
155
153
|
type: string
|
|
156
|
-
default: ""
|
|
157
154
|
relative_seconds:
|
|
158
155
|
description: Relative to async start time. `-1` for disabled.
|
|
159
156
|
type: integer
|
|
@@ -185,7 +182,6 @@ properties:
|
|
|
185
182
|
pass:
|
|
186
183
|
description: Authenticate the local async with the specified password.
|
|
187
184
|
type: string
|
|
188
|
-
default: ""
|
|
189
185
|
path:
|
|
190
186
|
description: The directory to be synchronized on the local host.
|
|
191
187
|
type: string
|
|
@@ -201,7 +197,6 @@ properties:
|
|
|
201
197
|
description: Use the specified database directory on the local host.
|
|
202
198
|
Default is `.private-asp` at the root level of the synchronized directory.
|
|
203
199
|
type: string
|
|
204
|
-
default: ""
|
|
205
200
|
x-cli-option: true
|
|
206
201
|
x-cli-short: -b
|
|
207
202
|
local_checksum_threads:
|
|
@@ -215,7 +210,6 @@ properties:
|
|
|
215
210
|
Store/Restore the database to/from the specified directory on the local host.
|
|
216
211
|
The value can be an absolute path, an URI or - (use the local sync dir)
|
|
217
212
|
type: string
|
|
218
|
-
default: ""
|
|
219
213
|
x-cli-option: true
|
|
220
214
|
local_force_stat:
|
|
221
215
|
description: Forces the local async to retrieve file information even when no changes are detected by the scanner or monitor.
|
|
@@ -233,12 +227,10 @@ properties:
|
|
|
233
227
|
local_keep_dir:
|
|
234
228
|
description: Move deleted files into the specified directory on the local host.
|
|
235
229
|
type: string
|
|
236
|
-
default: ""
|
|
237
230
|
x-cli-option: --keep-dir-local
|
|
238
231
|
local_mount_signature:
|
|
239
232
|
description: Verify that the file system is mounted by the existence of this file on the local host.
|
|
240
233
|
type: string
|
|
241
|
-
default: ""
|
|
242
234
|
x-cli-option: true
|
|
243
235
|
local_move_cache_timeout_seconds:
|
|
244
236
|
description:
|
|
@@ -299,13 +291,11 @@ properties:
|
|
|
299
291
|
local_dir:
|
|
300
292
|
description: Use the specified logging directory on the local host.
|
|
301
293
|
type: string
|
|
302
|
-
default: ""
|
|
303
294
|
x-cli-option: --alt-logdir
|
|
304
295
|
x-cli-short: -L
|
|
305
296
|
remote_dir:
|
|
306
297
|
description: Use the specified logging directory on the remote host.
|
|
307
298
|
type: string
|
|
308
|
-
default: ""
|
|
309
299
|
x-cli-option: --remote-logdir
|
|
310
300
|
x-cli-short: -R
|
|
311
301
|
manifest_path:
|
|
@@ -313,7 +303,6 @@ properties:
|
|
|
313
303
|
A directory path where ascp will create manifest TEXT files (passed
|
|
314
304
|
to ascp as --file-manifest-path)
|
|
315
305
|
type: string
|
|
316
|
-
default: ""
|
|
317
306
|
mirror:
|
|
318
307
|
description:
|
|
319
308
|
Force the pulling side to be exactly like the pushing side, removing
|
|
@@ -352,7 +341,6 @@ properties:
|
|
|
352
341
|
description: Suppress log messages for ITEM.
|
|
353
342
|
The only currently supported ITEM is 'stats', which suppresses both STATS and PROG log messages.
|
|
354
343
|
type: string
|
|
355
|
-
default: ""
|
|
356
344
|
x-cli-option: true
|
|
357
345
|
no_preserve_root_attrs:
|
|
358
346
|
description: Disable the preservation of attributes on the Sync root.
|
|
@@ -455,7 +443,6 @@ properties:
|
|
|
455
443
|
fingerprint:
|
|
456
444
|
description: Check it against server SSH host key fingerprint.
|
|
457
445
|
type: string
|
|
458
|
-
default: ""
|
|
459
446
|
x-ts-name: sshfp
|
|
460
447
|
host:
|
|
461
448
|
description: Use the specified host name or address of the remote host.
|
|
@@ -465,7 +452,6 @@ properties:
|
|
|
465
452
|
pass:
|
|
466
453
|
description: Authenticate the transfer with the specified password.
|
|
467
454
|
type: string
|
|
468
|
-
default: ""
|
|
469
455
|
x-ts-name: remote_password
|
|
470
456
|
x-cli-option: true
|
|
471
457
|
x-cli-short: -w
|
|
@@ -516,11 +502,9 @@ properties:
|
|
|
516
502
|
host:
|
|
517
503
|
description: Use the specified host name or address of the proxy.
|
|
518
504
|
type: string
|
|
519
|
-
default: ""
|
|
520
505
|
pass:
|
|
521
506
|
description: Authenticate to the proxy with the specified password.
|
|
522
507
|
type: string
|
|
523
|
-
default: ""
|
|
524
508
|
port:
|
|
525
509
|
description: Use the specified port, default is 9091 for dnat, 9092.
|
|
526
510
|
for dnats
|
|
@@ -536,18 +520,15 @@ properties:
|
|
|
536
520
|
user:
|
|
537
521
|
description: Authenticate to the proxy with the specified username.
|
|
538
522
|
type: string
|
|
539
|
-
default: ""
|
|
540
523
|
token:
|
|
541
524
|
description: Token string passed to server's authentication service.
|
|
542
525
|
type: string
|
|
543
|
-
default: ""
|
|
544
526
|
x-ts-name: token
|
|
545
527
|
x-cli-short: -W
|
|
546
528
|
token_node_user:
|
|
547
529
|
description: Node API user identity associated with the token.
|
|
548
530
|
Required for node user bearer tokens
|
|
549
531
|
type: string
|
|
550
|
-
default: ""
|
|
551
532
|
user:
|
|
552
533
|
description: Authenticate the transfer with the specified username.
|
|
553
534
|
type: string
|
|
@@ -563,7 +544,6 @@ properties:
|
|
|
563
544
|
description: Use the specified database directory on the remote host.
|
|
564
545
|
Default is `.private-asp` at the root level of the synchronized directory.
|
|
565
546
|
type: string
|
|
566
|
-
default: ""
|
|
567
547
|
x-cli-option: true
|
|
568
548
|
x-cli-short: -B
|
|
569
549
|
remote_db_store_dir:
|
|
@@ -571,7 +551,6 @@ properties:
|
|
|
571
551
|
Store/Restore the database to/from the specified directory on the remote host.
|
|
572
552
|
The value can be an absolute path, an URI or - (use the remote sync dir).
|
|
573
553
|
type: string
|
|
574
|
-
default: ""
|
|
575
554
|
x-cli-option: true
|
|
576
555
|
remote_force_stat:
|
|
577
556
|
description: Forces the remote async to retrieve file information even when no changes are detected by the scanner or monitor.
|
|
@@ -589,12 +568,10 @@ properties:
|
|
|
589
568
|
remote_keep_dir:
|
|
590
569
|
description: Move deleted files into the specified directory on the remote host.
|
|
591
570
|
type: string
|
|
592
|
-
default: ""
|
|
593
571
|
x-cli-option: --keep-dir-remote
|
|
594
572
|
remote_mount_signature:
|
|
595
573
|
description: Verify that the file system is mounted by the existence of this file on the remote host.
|
|
596
574
|
type: string
|
|
597
|
-
default: ""
|
|
598
575
|
x-cli-option: true
|
|
599
576
|
remote_move_cache_timeout_seconds:
|
|
600
577
|
description:
|
|
@@ -868,7 +845,6 @@ properties:
|
|
|
868
845
|
host:
|
|
869
846
|
description: Use the specified host name or address to connect to the datastore.
|
|
870
847
|
type: string
|
|
871
|
-
default: ""
|
|
872
848
|
port:
|
|
873
849
|
description: Use the specified port.
|
|
874
850
|
type: integer
|
|
@@ -876,10 +852,8 @@ properties:
|
|
|
876
852
|
write_gid:
|
|
877
853
|
description: Try to write files as the specified group.
|
|
878
854
|
type: string
|
|
879
|
-
default: ""
|
|
880
855
|
x-cli-option: true
|
|
881
856
|
write_uid:
|
|
882
857
|
description: Try to write files as the specified user.
|
|
883
858
|
type: string
|
|
884
|
-
default: ""
|
|
885
859
|
x-cli-option: true
|
|
@@ -9,6 +9,7 @@ require 'aspera/command_line_builder'
|
|
|
9
9
|
require 'aspera/log'
|
|
10
10
|
require 'aspera/assert'
|
|
11
11
|
require 'aspera/environment'
|
|
12
|
+
require 'aspera/schema/registry'
|
|
12
13
|
require 'json'
|
|
13
14
|
require 'base64'
|
|
14
15
|
require 'open3'
|
|
@@ -81,7 +82,10 @@ module Aspera
|
|
|
81
82
|
# Start the sync process
|
|
82
83
|
# @param sync_info [Hash] Sync parameters, old or new format
|
|
83
84
|
# @param opt_ts [Hash] Optional transfer spec
|
|
84
|
-
# @
|
|
85
|
+
# @yieldparam direction [Symbol] Sync direction (one of DIRECTIONS: :push, :pull, :bidi)
|
|
86
|
+
# @yieldparam local_dir [String] Local directory path
|
|
87
|
+
# @yieldparam remote_dir [String] Remote directory path
|
|
88
|
+
# @yieldreturn [Hash] Transfer spec to use for authentication
|
|
85
89
|
def start(sync_info, opt_ts = nil)
|
|
86
90
|
Log.dump(:sync_params_initial, sync_info)
|
|
87
91
|
Aspera.assert_type(sync_info, Hash)
|
|
@@ -336,10 +340,10 @@ module Aspera
|
|
|
336
340
|
end
|
|
337
341
|
# Private stuff:
|
|
338
342
|
# Read JSON schema and mapping to command line options
|
|
339
|
-
ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(
|
|
340
|
-
ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA[
|
|
341
|
-
ARGS_INSTANCE_SCHEMA
|
|
342
|
-
CONF_SCHEMA = CommandLineBuilder.read_schema(
|
|
343
|
+
ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::SYNC_ARGS)
|
|
344
|
+
ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA.dig(*%w[properties sessions items])
|
|
345
|
+
ARGS_INSTANCE_SCHEMA.dig('properties').current.delete('sessions')
|
|
346
|
+
CONF_SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::SYNC_CONF)
|
|
343
347
|
CMDLINE_PARAMS_KEYS = %w[instance sessions].freeze
|
|
344
348
|
ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
|
|
345
349
|
PRIVATE_FOLDER = "#{Environment.instance.os.eql?(Environment::OS_WINDOWS) ? '~' : '.'}private-asp"
|
|
@@ -14,7 +14,7 @@ module Aspera
|
|
|
14
14
|
# @return nil if not a faux: scheme, else a FauxFile instance
|
|
15
15
|
def create(name)
|
|
16
16
|
return unless name.start_with?(PREFIX)
|
|
17
|
-
name_params = name
|
|
17
|
+
name_params = name.delete_prefix(PREFIX).split('?', 2)
|
|
18
18
|
raise Error, 'Format: #{PREFIX}<file path>?<size>' unless name_params.length.eql?(2)
|
|
19
19
|
raise Error, "Format: <integer>[#{SIZE_UNITS.join(',')}]" unless (m = name_params[1].downcase.match(/^(\d+)([#{SIZE_UNITS.join('')}])$/))
|
|
20
20
|
size = m[1].to_i
|
|
@@ -32,7 +32,7 @@ module Aspera
|
|
|
32
32
|
# Calls block a number of times (resumes) until success or limit reached
|
|
33
33
|
# This is re-entrant, one resumer can handle multiple transfers in //
|
|
34
34
|
#
|
|
35
|
-
# @
|
|
35
|
+
# @yieldreturn [void] Executes the transfer operation
|
|
36
36
|
def execute_with_resume
|
|
37
37
|
Aspera.assert(block_given?)
|
|
38
38
|
# maximum of retry
|
data/lib/aspera/transfer/spec.rb
CHANGED
|
@@ -50,9 +50,9 @@ module Aspera
|
|
|
50
50
|
transfer_spec['resume_policy'] = POLICY_FIX[transfer_spec['resume_policy']] if transfer_spec.key?('resume_policy')
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
SCHEMA = CommandLineBuilder.read_schema(
|
|
54
|
-
#
|
|
55
|
-
SCHEMA['properties'].each do |name, description|
|
|
53
|
+
SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::TRANSFER_SPEC, ascp: true)
|
|
54
|
+
# Define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
|
|
55
|
+
SCHEMA.current['properties'].each do |name, description|
|
|
56
56
|
next unless description['enum'].is_a?(Array)
|
|
57
57
|
const_set(:"#{name.to_s.upcase}_ENUM_VALUES", description['enum'])
|
|
58
58
|
description['enum'].each do |enum|
|
|
@@ -8,7 +8,7 @@ $comment: >-
|
|
|
8
8
|
x-cli-envvar [String] Name of env var
|
|
9
9
|
x-cli-option [String] Command line option (starts with "-"), or `true`: same as ts, or `false`: not an option
|
|
10
10
|
x-cli-switch [Boolean] `true` if option has no arg, else by default option has a value
|
|
11
|
-
x-cli-special [Boolean] `true` if not
|
|
11
|
+
x-cli-special [Boolean] `true` if not managed by command line generator (special handling: option or argument)
|
|
12
12
|
x-cli-convert [String,Hash] Method name for Convert object or Conversion Hash for enum: ts to arg
|
|
13
13
|
x-agents [Array] Supported agents (for doc only), if not specified: all
|
|
14
14
|
x-deprecation [String] Deprecation message for doc
|
data/lib/aspera/uri_reader.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Aspera
|
|
|
27
27
|
# @return [String] the path of a file:// URL
|
|
28
28
|
def file_path(url)
|
|
29
29
|
Aspera.assert(file?(url)){"use format: #{file_url('<path>')}"}
|
|
30
|
-
File.expand_path(url
|
|
30
|
+
File.expand_path(url.delete_prefix(SCHEME_FILE_PFX2))
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# Read some content from some URI, support file: , http: and https: schemes
|
data/lib/aspera/yaml.rb
CHANGED
|
@@ -34,14 +34,16 @@ module Aspera
|
|
|
34
34
|
duplicate_keys
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# Safely load YAML content
|
|
37
|
+
# Safely load YAML content
|
|
38
|
+
# raising an error if duplicate keys are found
|
|
39
|
+
# Validates the yaml, and then call `YAML.safe_load`
|
|
38
40
|
# @param yaml [String] YAML content
|
|
39
41
|
# @return [Object] Parsed YAML content
|
|
40
42
|
# @raise [RuntimeError] If duplicate keys are found
|
|
41
43
|
def safe_load(yaml)
|
|
42
44
|
duplicate_keys = find_duplicate_keys(Psych.parse_stream(yaml))
|
|
43
45
|
raise "Duplicate keys: #{duplicate_keys.join('; ')}" unless duplicate_keys.empty?
|
|
44
|
-
YAML.safe_load(yaml)
|
|
46
|
+
YAML.safe_load(yaml, permitted_classes: [Time, Date, Symbol])
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
module_function :find_duplicate_keys, :safe_load
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aspera-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.26.
|
|
4
|
+
version: 4.26.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Laurent Martin
|
|
@@ -345,6 +345,7 @@ files:
|
|
|
345
345
|
- lib/aspera/cli/info.rb
|
|
346
346
|
- lib/aspera/cli/main.rb
|
|
347
347
|
- lib/aspera/cli/manager.rb
|
|
348
|
+
- lib/aspera/cli/options.schema.yaml
|
|
348
349
|
- lib/aspera/cli/plugins/alee.rb
|
|
349
350
|
- lib/aspera/cli/plugins/aoc.rb
|
|
350
351
|
- lib/aspera/cli/plugins/ats.rb
|
|
@@ -385,6 +386,7 @@ files:
|
|
|
385
386
|
- lib/aspera/environment.rb
|
|
386
387
|
- lib/aspera/faspex_gw.rb
|
|
387
388
|
- lib/aspera/faspex_postproc.rb
|
|
389
|
+
- lib/aspera/formatter_interface.rb
|
|
388
390
|
- lib/aspera/hash_ext.rb
|
|
389
391
|
- lib/aspera/id_generator.rb
|
|
390
392
|
- lib/aspera/json_rpc.rb
|
|
@@ -425,6 +427,11 @@ files:
|
|
|
425
427
|
- lib/aspera/rest_error_analyzer.rb
|
|
426
428
|
- lib/aspera/rest_errors_aspera.rb
|
|
427
429
|
- lib/aspera/rest_list.rb
|
|
430
|
+
- lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml
|
|
431
|
+
- lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml
|
|
432
|
+
- lib/aspera/schema/documentation.rb
|
|
433
|
+
- lib/aspera/schema/reader.rb
|
|
434
|
+
- lib/aspera/schema/registry.rb
|
|
428
435
|
- lib/aspera/secret_hider.rb
|
|
429
436
|
- lib/aspera/ssh.rb
|
|
430
437
|
- lib/aspera/ssl.rb
|
|
@@ -440,7 +447,6 @@ files:
|
|
|
440
447
|
- lib/aspera/transfer/resumer.rb
|
|
441
448
|
- lib/aspera/transfer/spec.rb
|
|
442
449
|
- lib/aspera/transfer/spec.schema.yaml
|
|
443
|
-
- lib/aspera/transfer/spec_doc.rb
|
|
444
450
|
- lib/aspera/transfer/uri.rb
|
|
445
451
|
- lib/aspera/uri_reader.rb
|
|
446
452
|
- lib/aspera/web_auth.rb
|
|
@@ -473,7 +479,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
473
479
|
version: '0'
|
|
474
480
|
requirements:
|
|
475
481
|
- Read the manual for any requirement
|
|
476
|
-
rubygems_version: 4.0.
|
|
482
|
+
rubygems_version: 4.0.10
|
|
477
483
|
specification_version: 4
|
|
478
484
|
summary: 'Execute actions using command line on IBM Aspera Server products: Aspera
|
|
479
485
|
on Cloud, Faspex, Shares, Node, Console, Orchestrator, High Speed Transfer Server'
|
metadata.gz.sig
CHANGED
|
Binary file
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'aspera/agent/factory'
|
|
4
|
-
require 'aspera/markdown'
|
|
5
|
-
|
|
6
|
-
module Aspera
|
|
7
|
-
module Transfer
|
|
8
|
-
# Generate documentation from Schema, for Transfer Spec, or async Conf spec
|
|
9
|
-
class SpecDoc
|
|
10
|
-
class << self
|
|
11
|
-
# @param formatter [Cli::Formatter] Formatter to use, methods: markdown_text, tick, check_row
|
|
12
|
-
# @param include_option [Boolean] `true` : include CLI options
|
|
13
|
-
# @param agent_columns [Boolean] `true` : include agents columns
|
|
14
|
-
# @param schema [Hash] The JSON spec
|
|
15
|
-
# @return [Array] a table suitable to display in manual
|
|
16
|
-
def man_table(formatter, include_option: false, agent_columns: true, schema: Spec::SCHEMA)
|
|
17
|
-
cols = %i[name type description]
|
|
18
|
-
cols.insert(-2, *Agent::Factory::ALL.values.map{ |i| i[:short]}.sort) if agent_columns
|
|
19
|
-
rows = []
|
|
20
|
-
schema['properties'].each do |name, info|
|
|
21
|
-
rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info).last.map{ |h| h.merge(name: "#{name}.#{h[:name]}")}) if info['type'].eql?('object') && info['properties']
|
|
22
|
-
rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info['items']).last.map{ |h| h.merge(name: "#{name}[].#{h[:name]}")}) if info['type'].eql?('array') && info['items'] && info['items']['properties']
|
|
23
|
-
# Manual table
|
|
24
|
-
columns = {
|
|
25
|
-
name: name,
|
|
26
|
-
type: info['type'],
|
|
27
|
-
description: []
|
|
28
|
-
}
|
|
29
|
-
# Render Markdown formatting and split lines
|
|
30
|
-
columns[:description] =
|
|
31
|
-
info['description']
|
|
32
|
-
.gsub(Markdown::FORMATS){formatter.markdown_text(Regexp.last_match)}
|
|
33
|
-
.split("\n") if info.key?('description')
|
|
34
|
-
columns[:description].unshift("DEPRECATED: #{info['x-deprecation']}") if info.key?('x-deprecation')
|
|
35
|
-
# Add flags for supported agents in doc
|
|
36
|
-
agents = []
|
|
37
|
-
Agent::Factory::ALL.each_key do |sym|
|
|
38
|
-
agents.push(sym) if info['x-agents'].nil? || info['x-agents'].include?(sym.to_s)
|
|
39
|
-
end
|
|
40
|
-
Aspera.assert(agents.include?(:direct)){"#{name}: x-cli-option requires agent direct (or nil)"} if info['x-cli-option']
|
|
41
|
-
if agent_columns
|
|
42
|
-
Agent::Factory::ALL.each do |sym, names|
|
|
43
|
-
columns[names[:short]] = formatter.tick(agents.include?(sym))
|
|
44
|
-
end
|
|
45
|
-
else
|
|
46
|
-
columns[:description].push("(#{agents.map{ |i| Agent::Factory::ALL[i][:short].to_s.upcase}.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length)
|
|
47
|
-
end
|
|
48
|
-
# Only keep lines that are usable in supported agents
|
|
49
|
-
next false if agents.empty?
|
|
50
|
-
columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.markdown_text("`#{v}`")}.join(', ')}") if info.key?('enum')
|
|
51
|
-
if include_option
|
|
52
|
-
envvar_prefix = ''
|
|
53
|
-
cli_option =
|
|
54
|
-
if info.key?('x-cli-envvar')
|
|
55
|
-
envvar_prefix = 'env:'
|
|
56
|
-
info['x-cli-envvar']
|
|
57
|
-
elsif info['x-cli-switch']
|
|
58
|
-
info['x-cli-option']
|
|
59
|
-
elsif info['x-cli-option']
|
|
60
|
-
arg_type = info.key?('enum') ? '{enum}' : "{#{[info['type']].flatten.join('|')}}"
|
|
61
|
-
# conversion_tag = info['x-cli-convert']
|
|
62
|
-
conversion_tag = info.key?('x-cli-convert') ? 'conversion' : nil
|
|
63
|
-
sep = info['x-cli-option'].start_with?('--') ? '=' : ' '
|
|
64
|
-
"#{info['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
|
|
65
|
-
end
|
|
66
|
-
short = info.key?('x-cli-short') ? "(#{info['x-cli-short']})" : nil
|
|
67
|
-
columns[:description].push("(#{'special:' if info['x-cli-special']}#{envvar_prefix}#{formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option
|
|
68
|
-
end
|
|
69
|
-
rows.push(formatter.check_row(columns))
|
|
70
|
-
end
|
|
71
|
-
[cols, rows.sort_by{ |i| i[:name]}]
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|