aspera-cli 4.12.0 → 4.14.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +45 -5
- data/CONTRIBUTING.md +113 -22
- data/README.md +1289 -754
- data/bin/ascli +3 -3
- data/examples/dascli +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +63 -74
- data/lib/aspera/ascmd.rb +5 -3
- data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
- data/lib/aspera/cli/extended_value.rb +24 -37
- data/lib/aspera/cli/formatter.rb +23 -25
- data/lib/aspera/cli/info.rb +2 -4
- data/lib/aspera/cli/main.rb +27 -27
- data/lib/aspera/cli/manager.rb +143 -120
- data/lib/aspera/cli/plugin.rb +88 -43
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +235 -104
- data/lib/aspera/cli/plugins/ats.rb +16 -18
- data/lib/aspera/cli/plugins/bss.rb +3 -3
- data/lib/aspera/cli/plugins/config.rb +190 -373
- data/lib/aspera/cli/plugins/console.rb +4 -6
- data/lib/aspera/cli/plugins/cos.rb +12 -13
- data/lib/aspera/cli/plugins/faspex.rb +21 -21
- data/lib/aspera/cli/plugins/faspex5.rb +399 -150
- data/lib/aspera/cli/plugins/node.rb +260 -174
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
- data/lib/aspera/cli/plugins/preview.rb +40 -62
- data/lib/aspera/cli/plugins/server.rb +33 -16
- data/lib/aspera/cli/plugins/shares.rb +24 -33
- data/lib/aspera/cli/plugins/sync.rb +6 -6
- data/lib/aspera/cli/transfer_agent.rb +47 -30
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +9 -7
- data/lib/aspera/command_line_builder.rb +2 -1
- data/lib/aspera/cos_node.rb +1 -1
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +7 -3
- data/lib/aspera/fasp/agent_connect.rb +6 -1
- data/lib/aspera/fasp/agent_direct.rb +17 -17
- data/lib/aspera/fasp/agent_httpgw.rb +138 -60
- data/lib/aspera/fasp/agent_node.rb +14 -4
- data/lib/aspera/fasp/agent_trsdk.rb +2 -0
- data/lib/aspera/fasp/error_info.rb +2 -0
- data/lib/aspera/fasp/installation.rb +19 -19
- data/lib/aspera/fasp/parameters.rb +29 -20
- data/lib/aspera/fasp/parameters.yaml +5 -2
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +8 -5
- data/lib/aspera/fasp/uri.rb +23 -21
- data/lib/aspera/faspex_gw.rb +1 -0
- data/lib/aspera/faspex_postproc.rb +3 -3
- data/lib/aspera/hash_ext.rb +12 -2
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +1 -0
- data/lib/aspera/node.rb +73 -84
- data/lib/aspera/oauth.rb +4 -3
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/preview/file_types.rb +8 -6
- data/lib/aspera/preview/generator.rb +23 -11
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +80 -0
- data/lib/aspera/preview/utils.rb +11 -11
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +42 -4
- data/lib/aspera/rest_call_error.rb +3 -1
- data/lib/aspera/secret_hider.rb +10 -5
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/sync.rb +41 -33
- data/lib/aspera/web_server_simple.rb +22 -18
- data.tar.gz.sig +0 -0
- metadata +40 -48
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -179
- data/examples/aoc.rb +0 -30
- data/examples/faspex4.rb +0 -94
- data/examples/node.rb +0 -96
- data/examples/server.rb +0 -93
- data/lib/aspera/data/7 +0 -0
data/lib/aspera/cli/plugin.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'aspera/cli/extended_value'
|
4
|
+
|
3
5
|
module Aspera
|
4
6
|
module Cli
|
5
7
|
# base class for plugins modules
|
@@ -8,13 +10,16 @@ module Aspera
|
|
8
10
|
GLOBAL_OPS = %i[create list].freeze
|
9
11
|
# operations with id
|
10
12
|
INSTANCE_OPS = %i[modify delete show].freeze
|
13
|
+
# all standard operations
|
11
14
|
ALL_OPS = [GLOBAL_OPS, INSTANCE_OPS].flatten.freeze
|
12
|
-
# max number of items for list command
|
15
|
+
# special query parameter: max number of items for list command
|
13
16
|
MAX_ITEMS = 'max'
|
14
|
-
# max number of pages for list command
|
17
|
+
# special query parameter: max number of pages for list command
|
15
18
|
MAX_PAGES = 'pmax'
|
16
19
|
# used when all resources are selected
|
17
20
|
VAL_ALL = 'ALL'
|
21
|
+
# special identifier format: look for this name to find where supported
|
22
|
+
REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/.freeze
|
18
23
|
|
19
24
|
# global for inherited classes
|
20
25
|
@@options_created = false # rubocop:disable Style/ClassVars
|
@@ -24,46 +29,54 @@ module Aspera
|
|
24
29
|
# env.each_key {|k| raise "wrong agent key #{k}" unless AGENTS.include?(k)}
|
25
30
|
@agents = env
|
26
31
|
# check presence in descendant of mandatory method and constant
|
27
|
-
raise StandardError, "
|
32
|
+
raise StandardError, "Missing method 'execute_action' in #{self.class}" unless respond_to?(:execute_action)
|
28
33
|
raise StandardError, 'ACTIONS shall be redefined by subclass' unless self.class.constants.include?(:ACTIONS)
|
29
34
|
options.parser.separator('')
|
30
35
|
options.parser.separator("COMMAND: #{self.class.name.split('::').last.downcase}")
|
31
36
|
options.parser.separator("SUBCOMMANDS: #{self.class.const_get(:ACTIONS).map(&:to_s).sort.join(' ')}")
|
32
37
|
options.parser.separator('OPTIONS:')
|
33
38
|
return if @@options_created
|
34
|
-
options.
|
35
|
-
options.
|
36
|
-
|
37
|
-
|
38
|
-
options.
|
39
|
-
options.
|
40
|
-
options.
|
41
|
-
options.
|
39
|
+
options.declare(:query, 'Additional filter for for some commands (list/delete)', types: Hash)
|
40
|
+
options.declare(
|
41
|
+
:value, 'Value for create, update, list filter', types: Hash,
|
42
|
+
deprecation: 'Use positional value for create/modify or option: query for list/delete')
|
43
|
+
options.declare(:property, 'Name of property to set (modify operation)')
|
44
|
+
options.declare(:id, 'Resource identifier', deprecation: "Use identifier after verb (#{INSTANCE_OPS.join(',')})")
|
45
|
+
options.declare(:bulk, 'Bulk operation (only some)', values: :bool, default: :no)
|
46
|
+
options.declare(:bfail, 'Bulk operation error handling', values: :bool, default: :yes)
|
42
47
|
options.parse_options!
|
43
48
|
@@options_created = true # rubocop:disable Style/ClassVars
|
44
49
|
end
|
45
50
|
|
46
|
-
# must be called AFTER the instance action
|
47
|
-
|
51
|
+
# must be called AFTER the instance action, ... folder browse <call instance_identifier>
|
52
|
+
# @param description [String] description of the identifier
|
53
|
+
# @param block [Proc] block to search for identifier based on attribute value
|
54
|
+
# @return [String] identifier
|
55
|
+
def instance_identifier(description: 'identifier', &block)
|
48
56
|
res_id = options.get_option(:id)
|
49
|
-
res_id = options.get_next_argument(
|
57
|
+
res_id = options.get_next_argument(description) if res_id.nil?
|
58
|
+
# cab be an Array
|
59
|
+
if res_id.is_a?(String) && (m = res_id.match(REGEX_LOOKUP_ID_BY_FIELD))
|
60
|
+
if block
|
61
|
+
res_id = yield(m[1], ExtendedValue.instance.evaluate(m[2]))
|
62
|
+
else
|
63
|
+
raise CliBadArgument, "Percent syntax for #{description} not supported in this context"
|
64
|
+
end
|
65
|
+
end
|
50
66
|
return res_id
|
51
67
|
end
|
52
68
|
|
53
|
-
# TODO
|
54
|
-
# def get_next_id_command(instance_ops: INSTANCE_OPS,global_ops: GLOBAL_OPS)
|
55
|
-
# return get_next_argument('command',expected: command_list)
|
56
|
-
# end
|
57
|
-
|
58
69
|
# For create and delete operations: execute one actin or multiple if bulk is yes
|
59
|
-
# @param
|
70
|
+
# @param single_or_array [Object] single hash, or array of hash for bulk
|
60
71
|
# @param success_msg deleted or created
|
72
|
+
# @param id_result [String] key in result hash to use as identifier
|
73
|
+
# @param fields [Array] fields to display
|
61
74
|
def do_bulk_operation(single_or_array, success_msg, id_result: 'id', fields: :default)
|
62
75
|
raise 'programming error: missing block' unless block_given?
|
63
76
|
params = options.get_option(:bulk) ? single_or_array : [single_or_array]
|
64
77
|
raise 'expecting Array for bulk operation' unless params.is_a?(Array)
|
65
78
|
Log.log.warn('Empty list given for bulk operation') if params.empty?
|
66
|
-
Log.dump(:
|
79
|
+
Log.dump(:bulk_operation, params)
|
67
80
|
result_list = []
|
68
81
|
params.each do |param|
|
69
82
|
# init for delete
|
@@ -96,11 +109,14 @@ module Aspera
|
|
96
109
|
# @param id_default [String] default identifier to use for existing entity commands (show, modify)
|
97
110
|
# @param item_list_key [String] result is in a sub key of the json
|
98
111
|
# @param id_as_arg [String] if set, the id is provided as url argument ?<id_as_arg>=<id>
|
112
|
+
# @param is_singleton [Boolean] if true, res_class_path is the full path to the resource
|
99
113
|
# @return result suitable for CLI result
|
100
|
-
def entity_command(command, rest_api, res_class_path, display_fields: nil, id_default: nil, item_list_key: false, id_as_arg: false)
|
101
|
-
if
|
114
|
+
def entity_command(command, rest_api, res_class_path, display_fields: nil, id_default: nil, item_list_key: false, id_as_arg: false, is_singleton: false, &block)
|
115
|
+
if is_singleton
|
116
|
+
one_res_path = res_class_path
|
117
|
+
elsif INSTANCE_OPS.include?(command)
|
102
118
|
begin
|
103
|
-
one_res_id = instance_identifier
|
119
|
+
one_res_id = instance_identifier(&block)
|
104
120
|
rescue StandardError => e
|
105
121
|
raise e if id_default.nil?
|
106
122
|
one_res_id = id_default
|
@@ -108,29 +124,24 @@ module Aspera
|
|
108
124
|
one_res_path = "#{res_class_path}/#{one_res_id}"
|
109
125
|
one_res_path = "#{res_class_path}?#{id_as_arg}=#{one_res_id}" if id_as_arg
|
110
126
|
end
|
111
|
-
|
112
|
-
if %i[create modify].include?(command)
|
113
|
-
parameters = options.get_option(:value, is_type: :mandatory)
|
114
|
-
end
|
115
|
-
# parameters optional for list
|
116
|
-
if %i[list delete].include?(command)
|
117
|
-
parameters = options.get_option(:value)
|
118
|
-
end
|
127
|
+
|
119
128
|
case command
|
120
129
|
when :create
|
121
|
-
|
130
|
+
raise 'cannot create singleton' if is_singleton
|
131
|
+
return do_bulk_operation(value_create_modify(command: command, type: :bulk_hash), 'created', fields: display_fields) do |params|
|
122
132
|
raise 'expecting Hash' unless params.is_a?(Hash)
|
123
133
|
rest_api.create(res_class_path, params)[:data]
|
124
134
|
end
|
125
135
|
when :delete
|
136
|
+
raise 'cannot delete singleton' if is_singleton
|
126
137
|
return do_bulk_operation(one_res_id, 'deleted') do |one_id|
|
127
|
-
rest_api.delete("#{res_class_path}/#{one_id}",
|
138
|
+
rest_api.delete("#{res_class_path}/#{one_id}", old_query_read_delete)
|
128
139
|
{'id' => one_id}
|
129
140
|
end
|
130
141
|
when :show
|
131
142
|
return {type: :single_object, data: rest_api.read(one_res_path)[:data], fields: display_fields}
|
132
143
|
when :list
|
133
|
-
resp = rest_api.read(res_class_path,
|
144
|
+
resp = rest_api.read(res_class_path, old_query_read_delete)
|
134
145
|
data = resp[:data]
|
135
146
|
# TODO: not generic : which application is this for ?
|
136
147
|
if resp[:http]['Content-Type'].start_with?('application/vnd.api+json')
|
@@ -140,11 +151,7 @@ module Aspera
|
|
140
151
|
if item_list_key
|
141
152
|
item_list = data[item_list_key]
|
142
153
|
total_count = data['total_count']
|
143
|
-
|
144
|
-
count_msg = "Items: #{item_list.length}/#{total_count}"
|
145
|
-
count_msg = count_msg.bg_red unless item_list.length.eql?(total_count.to_i)
|
146
|
-
formatter.display_status(count_msg)
|
147
|
-
end
|
154
|
+
formatter.display_item_count(item_list.length, total_count) unless total_count.nil?
|
148
155
|
data = item_list
|
149
156
|
end
|
150
157
|
case data
|
@@ -157,6 +164,7 @@ module Aspera
|
|
157
164
|
raise "An error occurred: unexpected result type for list: #{data.class}"
|
158
165
|
end
|
159
166
|
when :modify
|
167
|
+
parameters = value_create_modify(command: command, type: Hash)
|
160
168
|
property = options.get_option(:property)
|
161
169
|
parameters = {property => parameters} unless property.nil?
|
162
170
|
rest_api.update(one_res_path, parameters)
|
@@ -173,8 +181,8 @@ module Aspera
|
|
173
181
|
return entity_command(command, rest_api, res_class_path, **opts)
|
174
182
|
end
|
175
183
|
|
176
|
-
# query for list
|
177
|
-
def
|
184
|
+
# query parameters in URL suitable for REST list/GET and delete/DELETE
|
185
|
+
def query_read_delete(default: nil)
|
178
186
|
query = options.get_option(:query)
|
179
187
|
# dup default, as it could be frozen
|
180
188
|
query = default.dup if query.nil?
|
@@ -183,11 +191,48 @@ module Aspera
|
|
183
191
|
# check it is suitable
|
184
192
|
URI.encode_www_form(query) unless query.nil?
|
185
193
|
rescue StandardError => e
|
186
|
-
raise CliBadArgument, "
|
194
|
+
raise CliBadArgument, "Query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
|
187
195
|
end
|
188
196
|
return query
|
189
197
|
end
|
190
198
|
|
199
|
+
# TODO: when deprecation of `value` is completed: remove this method, replace with query_read_delete
|
200
|
+
def old_query_read_delete
|
201
|
+
query = options.get_option(:value) # legacy, deprecated, remove, one day...
|
202
|
+
query = query_read_delete if query.nil?
|
203
|
+
return query
|
204
|
+
end
|
205
|
+
|
206
|
+
# TODO: when deprecation of `value` is completed: remove this method, replace with options.get_option(:query)
|
207
|
+
def value_or_query(mandatory: false, allowed_types: nil)
|
208
|
+
value = options.get_option(:value, mandatory: false, allowed_types: allowed_types)
|
209
|
+
value = options.get_option(:query, mandatory: mandatory, allowed_types: allowed_types) if value.nil?
|
210
|
+
return value
|
211
|
+
end
|
212
|
+
|
213
|
+
# Retrieves an extended value from command line, used for creation or modification of entities
|
214
|
+
# @param default [Object] default value if not provided
|
215
|
+
# @param command [String] command name for error message
|
216
|
+
# @param type [Class] expected type of value
|
217
|
+
# TODO: when deprecation of `value` is completed: remove line with :value
|
218
|
+
def value_create_modify(default: nil, command: 'command', type: nil)
|
219
|
+
value = options.get_option(:value)
|
220
|
+
value = options.get_next_argument("parameters for #{command}", mandatory: default.nil?) if value.nil?
|
221
|
+
value = default if value.nil?
|
222
|
+
if type.nil?
|
223
|
+
# nothing to do
|
224
|
+
elsif type.is_a?(Class)
|
225
|
+
raise CliBadArgument, "Value must be a #{type}" unless value.is_a?(type)
|
226
|
+
elsif type.is_a?(Array)
|
227
|
+
raise CliBadArgument, "Value must be one of #{type.join(', ')}" unless type.any?{|t| value.is_a?(t)}
|
228
|
+
elsif type.eql?(:bulk_hash)
|
229
|
+
raise CliBadArgument, 'Value must be a Hash or Array of Hash' unless value.is_a?(Hash) || (value.is_a?(Array) && value.all?(Hash))
|
230
|
+
else
|
231
|
+
raise "Internal error: #{type}"
|
232
|
+
end
|
233
|
+
return value
|
234
|
+
end
|
235
|
+
|
191
236
|
# shortcuts helpers for plugin environment
|
192
237
|
%i[options transfer config formatter persistency].each do |name|
|
193
238
|
define_method(name){@agents[name]}
|
@@ -13,8 +13,8 @@ module Aspera
|
|
13
13
|
command = options.get_next_command(ACTIONS)
|
14
14
|
case command
|
15
15
|
when :entitlement
|
16
|
-
entitlement_id = options.get_option(:username,
|
17
|
-
customer_id = options.get_option(:password,
|
16
|
+
entitlement_id = options.get_option(:username, mandatory: true)
|
17
|
+
customer_id = options.get_option(:password, mandatory: true)
|
18
18
|
api_metering = AoC.metering_api(entitlement_id, customer_id)
|
19
19
|
return {type: :single_object, data: api_metering.read('entitlement')[:data]}
|
20
20
|
end
|