aspera-cli 4.27.3 → 4.27.5

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 (79) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +118 -0
  4. data/CONTRIBUTING.md +5 -2
  5. data/bin/ascli +1 -0
  6. data/docs/README.md +224 -42
  7. data/lib/aspera/agent/base.rb +7 -2
  8. data/lib/aspera/agent/connect.rb +0 -4
  9. data/lib/aspera/agent/desktop.rb +0 -4
  10. data/lib/aspera/agent/direct.rb +49 -21
  11. data/lib/aspera/agent/node.rb +6 -5
  12. data/lib/aspera/agent/transferd.rb +2 -2
  13. data/lib/aspera/api/faspex.rb +2 -0
  14. data/lib/aspera/api/httpgw.rb +1 -2
  15. data/lib/aspera/api/node.rb +3 -3
  16. data/lib/aspera/ascp/installation.rb +1 -1
  17. data/lib/aspera/cli/async_transfer_store.rb +10 -9
  18. data/lib/aspera/cli/bootstrapper.rb +3 -1
  19. data/lib/aspera/cli/command_registry.rb +69 -7
  20. data/lib/aspera/cli/command_spec.rb +1 -1
  21. data/lib/aspera/cli/extended_value.rb +4 -3
  22. data/lib/aspera/cli/formatter.rb +10 -8
  23. data/lib/aspera/cli/http.rb +8 -20
  24. data/lib/aspera/cli/option_types.rb +3 -1
  25. data/lib/aspera/cli/option_value.rb +16 -19
  26. data/lib/aspera/cli/options.schema.yaml +86 -10
  27. data/lib/aspera/cli/parser.rb +30 -18
  28. data/lib/aspera/cli/plugins/aoc.rb +133 -156
  29. data/lib/aspera/cli/plugins/ats.rb +1 -7
  30. data/lib/aspera/cli/plugins/base.rb +40 -34
  31. data/lib/aspera/cli/plugins/config.rb +24 -11
  32. data/lib/aspera/cli/plugins/console.rb +1 -1
  33. data/lib/aspera/cli/plugins/faspex5.rb +30 -11
  34. data/lib/aspera/cli/plugins/faspio.rb +1 -1
  35. data/lib/aspera/cli/plugins/node.rb +26 -19
  36. data/lib/aspera/cli/plugins/orchestrator.rb +73 -44
  37. data/lib/aspera/cli/plugins/preview.rb +21 -19
  38. data/lib/aspera/cli/plugins/server.rb +3 -4
  39. data/lib/aspera/cli/plugins/shares.rb +21 -24
  40. data/lib/aspera/cli/preset_actions.rb +28 -18
  41. data/lib/aspera/cli/preset_manager.rb +34 -19
  42. data/lib/aspera/cli/prompt.rb +2 -1
  43. data/lib/aspera/cli/result.rb +33 -22
  44. data/lib/aspera/cli/runner.rb +1 -5
  45. data/lib/aspera/cli/sync_actions.rb +17 -16
  46. data/lib/aspera/cli/transfer_actions.rb +14 -3
  47. data/lib/aspera/cli/transfer_agent.rb +6 -4
  48. data/lib/aspera/cli/transfer_progress.rb +290 -55
  49. data/lib/aspera/cli/version.rb +1 -1
  50. data/lib/aspera/cli/wizard.rb +1 -1
  51. data/lib/aspera/coverage.rb +0 -1
  52. data/lib/aspera/environment.rb +29 -5
  53. data/lib/aspera/keychain/encrypted_hash.rb +1 -1
  54. data/lib/aspera/keychain/factory.rb +2 -1
  55. data/lib/aspera/log.rb +25 -2
  56. data/lib/aspera/node_emulator.rb +759 -0
  57. data/lib/aspera/oauth/base.rb +2 -1
  58. data/lib/aspera/oauth/factory.rb +6 -3
  59. data/lib/aspera/oauth/json_credentials.rb +34 -0
  60. data/lib/aspera/oauth/jwt.rb +3 -4
  61. data/lib/aspera/oauth.rb +1 -0
  62. data/lib/aspera/persistency_folder.rb +1 -3
  63. data/lib/aspera/preview/generator.rb +4 -1
  64. data/lib/aspera/preview/options.schema.yaml +119 -0
  65. data/lib/aspera/rest/aspera_errors.rb +12 -0
  66. data/lib/aspera/rest/client.rb +28 -19
  67. data/lib/aspera/rest/list.rb +14 -8
  68. data/lib/aspera/rest/parameters.rb +2 -2
  69. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
  70. data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
  71. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
  72. data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
  73. data/lib/aspera/schema/reader.rb +12 -18
  74. data/lib/aspera/schema/registry.rb +6 -1
  75. data.tar.gz.sig +0 -0
  76. metadata +5 -3
  77. metadata.gz.sig +0 -0
  78. data/lib/aspera/node_simulator.rb +0 -345
  79. data/lib/aspera/preview/options.rb +0 -45
@@ -8,8 +8,6 @@ require 'aspera/schema/registry'
8
8
  require 'aspera/schema/validator'
9
9
  require 'aspera/log'
10
10
  require 'aspera/assert'
11
- require 'aspera/rainbow'
12
- using Rainbow
13
11
 
14
12
  module Aspera
15
13
  module Cli
@@ -19,7 +17,7 @@ module Aspera
19
17
  attr_reader :option
20
18
  # [Array<Class>, nil] List of allowed types, `nil` for no validation
21
19
  attr_reader :types
22
- # [Symbol] How values are converted: :flag, :boolean, :integer, :enum, :enum_list, :string_list, :other
20
+ # [Symbol] How values are converted: :flag, :boolean, :integer, :float, :enum, :enum_list, :string_list, :other
23
21
  attr_reader :kind
24
22
  # [Symbol, nil] `OptionSource` of current value, `nil` if never set
25
23
  attr_reader :source
@@ -59,8 +57,8 @@ module Aspera
59
57
  @shorthand = shorthand
60
58
  @source = nil
61
59
  @value = nil
62
- @on_set = nil
63
- bind_on_set(on_set) unless on_set.nil?
60
+ Aspera.assert(on_set.nil? || on_set.respond_to?(:call)) { "#{@option}: on_set callback must respond to call" }
61
+ @on_set = on_set
64
62
  @types = nil
65
63
  @values = nil
66
64
  @kind = :other
@@ -68,19 +66,6 @@ module Aspera
68
66
  apply_allowed(allowed) unless allowed.nil?
69
67
  end
70
68
 
71
- # Set the `on_set` callback, called with the new value each time the value is set.
72
- # Safe to call after construction: used by `Parser#on_set` for a target object created after declaration.
73
- # The callback is called with the current value, if any.
74
- # @param callback [#call] e.g. a `Method` or a lambda
75
- # @return [nil]
76
- def bind_on_set(callback)
77
- Aspera.assert(callback.respond_to?(:call)) { "#{@option}: on_set callback must respond to call" }
78
- @on_set = callback
79
- Log.log.trace1 { "bind_on_set: #{@option}".green }
80
- @on_set.call(@value) unless @value.nil?
81
- nil
82
- end
83
-
84
69
  # @return [String] description of the option: explicit one, or first line of schema description
85
70
  def description
86
71
  return @description unless @description.nil?
@@ -232,6 +217,7 @@ module Aspera
232
217
  @kind =
233
218
  if allowed.sort_by(&:name).eql?(Type::BOOLEAN) then :boolean
234
219
  elsif allowed.eql?(Type::INTEGER) then :integer
220
+ elsif allowed.eql?(Type::FLOAT) then :float
235
221
  elsif allowed.eql?(Type::STRING_ARRAY) then :string_list
236
222
  else :other
237
223
  end
@@ -256,7 +242,18 @@ module Aspera
256
242
  when :boolean
257
243
  BoolValue.true?(value.is_a?(String) ? Parser.get_from_list(value, @option, BoolValue::ALL) : value)
258
244
  when :integer
259
- value.nil? ? value : Integer(value)
245
+ # Decimal, also with leading zeros (e.g. `08`)
246
+ if value.is_a?(String)
247
+ Integer(value, 10, exception: false).tap { |i| raise BadArgument, "Option #{@option}: invalid integer: #{value}" if i.nil? }
248
+ else
249
+ value.nil? ? value : Integer(value)
250
+ end
251
+ when :float
252
+ if value.is_a?(String)
253
+ Float(value, exception: false).tap { |f| raise BadArgument, "Option #{@option}: invalid number: #{value}" if f.nil? }
254
+ else
255
+ value.nil? ? value : Float(value)
256
+ end
260
257
  when :string_list
261
258
  value.is_a?(String) ? [value] : value
262
259
  when :enum_list
@@ -102,7 +102,10 @@ components:
102
102
  default: true
103
103
  quiet:
104
104
  type: boolean
105
- description: Suppress the `ascp` progress bar display.
105
+ description: >-
106
+ Suppress the `ascp` progress bar display.
107
+
108
+ If `false`, the progress bar of option `progress_bar` is not displayed, unless that option is set.
106
109
  default: true
107
110
  file_list:
108
111
  type: boolean
@@ -595,18 +598,20 @@ components:
595
598
  Token expiry timestamp in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`).
596
599
  Computed automatically from `_validity` when absent.
597
600
  example: "2030-01-01T00:00:00Z"
598
- NodeSimulatorOptions:
601
+ NodeEmulatorOptions:
599
602
  type: object
600
603
  description: >-
601
- Configuration for the embedded Node API simulator (`node simulator`).
602
- The simulator starts a local WEBrick server that answers `/ops/transfers`,
603
- `/ops/transfers/{id}`, `/files/browse`, and `/info` — sufficient to test
604
- CLI commands against a live Node API without a real HSTS instance.
604
+ Configuration for the embedded Node API emulator (`node emulator`), for testing without HSTS.
605
+ The emulator starts a local web server that answers a subset of the Node API:
606
+ `/info`, `/ops/transfers` (list, start), `/ops/transfers/{id}` (show, modify, cancel) and `/files/browse`.
607
+ Transfers are executed by the Transfer Daemon (`transferd`), started by the emulator.
608
+ Clients authenticate with HTTP Basic when `username` and `password` are set.
609
+ additionalProperties: false
605
610
  properties:
606
611
  url:
607
612
  type: string
608
613
  description: >-
609
- Address and port the simulator listens on.
614
+ Address and port the emulator listens on.
610
615
  Use `https://` with `cert`/`key` for TLS.
611
616
  default: http://localhost:8080
612
617
  cert:
@@ -624,13 +629,29 @@ components:
624
629
  type: string
625
630
  description: Path to the PEM certificate chain file (appended as extra chain certificates).
626
631
  example: /path/to/chain.pem
627
- browse_root:
632
+ docroot:
628
633
  type: string
629
634
  description: >-
630
- Root directory the simulator is allowed to expose via `/files/browse`.
631
- Requests for paths outside this directory are rejected.
635
+ Local folder of the node files.
636
+ Paths of `/files/browse` and local paths of transfers (sources of `send`, destination of `receive`) are relative to it, and confined in it.
632
637
  Defaults to the current working directory.
633
638
  example: /data/aspera
639
+ retention_sec:
640
+ type: integer
641
+ minimum: 1
642
+ description: Time in seconds a transfer stays in the list of transfers after it ended (completed, failed or canceled, and no more retried).
643
+ default: 86400
644
+ username:
645
+ type: string
646
+ description: >-
647
+ Username expected from clients in HTTP Basic authentication.
648
+ Set together with `password`.
649
+ When not set, requests are accepted without authentication.
650
+ example: node_user
651
+ password:
652
+ type: string
653
+ description: Password expected from clients in HTTP Basic authentication, for the above `username`.
654
+ example: my_password
634
655
  NodeTelemetryOptions:
635
656
  type: object
636
657
  description: >-
@@ -685,6 +706,61 @@ components:
685
706
  Name of the output variable of `step` returned as result.
686
707
  Requires `step`, and implies `synchronous`.
687
708
  example: Complete_status_message
709
+ OrchestratorInitiateParameters:
710
+ type: object
711
+ description: >-
712
+ Input parameters for the workflow (key-value pairs).
713
+ Valid keys are defined by the workflow's input specification.
714
+ Use `orchestrator workflows inputs WORKFLOW_ID` to discover required parameters.
715
+ additionalProperties: true
716
+ OrchestratorImportWorkflow:
717
+ type: object
718
+ description: Workflow definition to import into Orchestrator.
719
+ required:
720
+ - name
721
+ properties:
722
+ name:
723
+ type: string
724
+ description: Workflow name.
725
+ example: My Workflow
726
+ description:
727
+ type: string
728
+ description: Workflow description.
729
+ example: This is a sample workflow
730
+ workflow_data:
731
+ type: object
732
+ description: Complete workflow definition.
733
+ OrchestratorImportWithConstraints:
734
+ type: object
735
+ description: Workflow definition to import with constraint resolution mappings.
736
+ required:
737
+ - workflow_data
738
+ - constraints
739
+ properties:
740
+ workflow_data:
741
+ type: object
742
+ description: Complete workflow definition to import.
743
+ constraints:
744
+ type: object
745
+ description: Constraint resolution mappings for conflicting resources.
746
+ example:
747
+ resource_pools: {}
748
+ queues: {}
749
+ monitors: {}
750
+ OrchestratorReloadPluginSet:
751
+ type: object
752
+ description: Set of plugins to reload.
753
+ required:
754
+ - plugin_names
755
+ properties:
756
+ plugin_names:
757
+ type: array
758
+ description: List of plugin names to reload.
759
+ items:
760
+ type: string
761
+ example:
762
+ - soap_requests
763
+ - frameio_operations
688
764
  TableOptions:
689
765
  type: object
690
766
  description: Terminal table rendering options.
@@ -97,7 +97,7 @@ module Aspera
97
97
  when 'true', BoolValue::YES_SYM.to_s then true
98
98
  when 'false', BoolValue::NO_SYM.to_s then false
99
99
  else
100
- Integer(value, exception: false) ||
100
+ Integer(value, 10, exception: false) ||
101
101
  Float(value, exception: false) ||
102
102
  ExtendedValue.instance.evaluate(value, context: 'dotted expression')
103
103
  end
@@ -228,14 +228,15 @@ module Aspera
228
228
 
229
229
  # Resource identifier as positional parameter
230
230
  #
231
- # @param description [String] description of the identifier
232
- # @param block [Proc] block to search for identifier based on attribute value
233
- # @return [String, Array<String>] identifier or list of IDs (if `bulk` option is set)
231
+ # @param description [String] description of the identifier
232
+ # @param multiple [Boolean] `true`: read a list of IDs (bulk operation)
233
+ # @param block [Proc] block to search for identifier based on attribute value
234
+ # @return [String, Array<String>] identifier or list of IDs (if `multiple`)
234
235
  # @yieldparam field [String] The field name from percent selector
235
236
  # @yieldparam value [String] The value from percent selector
236
237
  # @yieldreturn [String] Resolved identifier
237
- def instance_identifier(description: 'identifier', &block)
238
- res_id = get_next_argument(description, multiple: get_option(:bulk))
238
+ def instance_identifier(description: 'identifier', multiple: false, &block)
239
+ res_id = get_next_argument(description, multiple: multiple)
239
240
  # Can be an Array
240
241
  if res_id.is_a?(String) && (m = Parser.percent_selector(res_id))
241
242
  Aspera.assert(block_given?, type: Cli::BadArgument) { "Percent syntax for #{description} not supported in this context" }
@@ -312,16 +313,6 @@ module Aspera
312
313
  option_def(option_symbol).clear
313
314
  end
314
315
 
315
- # Bind (or re-bind) an `on_set` callback to an already-declared option, for a target object created after declaration.
316
- # The callback is called with the current value, if any.
317
- # @param option_symbol [Symbol] name of the already-declared option
318
- # @param callback [#call] called with the new value each time the value is set (e.g. a `Method`)
319
- # @return [nil]
320
- def on_set(option_symbol, callback)
321
- Aspera.assert_type(option_symbol, Symbol)
322
- option_def(option_symbol).bind_on_set(callback)
323
- end
324
-
325
316
  # Adds each of the keys of specified hash as an option.
326
317
  # Values are applied by the next parse, and never override a value from env or command line.
327
318
  # @param preset_hash [Hash] Options to add
@@ -354,6 +345,14 @@ module Aspera
354
345
  @command_line.pending_arguments.empty?
355
346
  end
356
347
 
348
+ # Check the next pending positional argument, without consuming it
349
+ # @param values [Array<String>] values to compare with
350
+ # @return [Boolean] true if the next pending positional argument is one of values
351
+ def next_argument_in?(values)
352
+ ensure_parsed
353
+ values.include?(@command_line.pending_arguments.first)
354
+ end
355
+
357
356
  # Check for unprocessed options or arguments error messages
358
357
  # @return [Array<String>] list of error messages for unprocessed tokens
359
358
  def final_errors
@@ -410,6 +409,18 @@ module Aspera
410
409
  Log.log.trace1 { "unprocessed options: #{@command_line.pending_options}" }
411
410
  end
412
411
 
412
+ # Execute the block with interactive input of missing mandatory options and arguments, as with `--interactive=yes`.
413
+ # The previous state is restored afterwards, so that interactive input does not leak to the rest of the command.
414
+ # @param enabled [Boolean] `false`: execute the block with current state
415
+ # @return [Object] result of the block
416
+ def with_interactive(enabled: true)
417
+ previous = @ask_missing_mandatory
418
+ @ask_missing_mandatory = true if enabled
419
+ yield
420
+ ensure
421
+ @ask_missing_mandatory = previous
422
+ end
423
+
413
424
  # Prompt user for missing option or argument, or raise if not interactive
414
425
  # @param descr [String] option name, or argument description
415
426
  # @param multiple [Boolean, String] `true` if multiple values expected
@@ -428,7 +439,7 @@ module Aspera
428
439
  end
429
440
  # Ask interactively
430
441
  result = []
431
- puts(' (one per line, end with empty line)') if multiple
442
+ $stderr.puts(' (one per line, end with empty line)') if multiple # rubocop:disable Style/StderrPuts
432
443
  loop do
433
444
  prompt = default_prompt
434
445
  prompt = "#{accept_list.join(' ')}\n#{default_prompt}" if accept_list
@@ -527,7 +538,7 @@ module Aspera
527
538
  # if value comes from JSON/YAML, it may come as Integer
528
539
  return value.to_s if value.is_a?(Integer) && validation.eql?(Type::STRING)
529
540
  return value unless value.is_a?(String) && validation.eql?(Type::INTEGER)
530
- Integer(value, exception: false).tap { |i| raise Cli::BadArgument, "Invalid integer: #{value}" if i.nil? }
541
+ Integer(value, 10, exception: false).tap { |i| raise Cli::BadArgument, "Invalid integer: #{value}" if i.nil? }
531
542
  end
532
543
 
533
544
  # Validate a single argument value.
@@ -554,6 +565,7 @@ module Aspera
554
565
  when :flag then nil
555
566
  when :boolean then 'yes|no'
556
567
  when :integer then 'INT'
568
+ when :float then 'FLOAT'
557
569
  when :enum
558
570
  opt.values&.any? && opt.values.length <= 4 ? opt.values.join('|') : 'ENUM'
559
571
  else