envoi-mam-agent 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@ require 'envoi/mam/iconik/agent'
10
10
  current_command = ARGV.shift
11
11
  ARGV << '--help' if ARGV.empty?
12
12
 
13
- default_config_file_paths = Envoi::Mam::Agent::Cli::CONFIG_FILE_PATHS
13
+ default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
14
14
 
15
15
  aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
16
16
  default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
@@ -11,7 +11,7 @@ require 'envoi/mam/mediasilo/agent'
11
11
  current_command = ARGV.shift
12
12
  ARGV << '--help' if ARGV.empty?
13
13
 
14
- default_config_file_paths = Envoi::Mam::Agent::Cli::CONFIG_FILE_PATHS
14
+ default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
15
15
 
16
16
  aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
17
17
  default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ lib_path = __FILE__ == '(irb)' ? File.join(Dir.cwd, 'lib') : File.expand_path('../../../lib', __FILE__)
3
+ $:.unshift(lib_path) unless $:.include?(lib_path) or !File.exists?(lib_path)
4
+ require 'rubygems'
5
+ require 'optparse'
6
+ require 'open3'
7
+
8
+ require 'envoi/mam/agent/cli'
9
+ require 'envoi/restore/agent'
10
+
11
+ default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
12
+
13
+ aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
14
+ default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
15
+
16
+ current_command = ARGV.shift
17
+ args = {
18
+ :config_file_path => default_config_file_paths,
19
+ :default_ascp_path => default_aspera_ascp_path,
20
+ :dry_run => false,
21
+ :operation => :download,
22
+ :preserve_path => true,
23
+ :transfer_type => '',
24
+ }
25
+
26
+ args[:config_service_app_id] = ENV['ENVOI_MAM_AGENT_APP_ID']
27
+ args[:config_service_app_token] = ENV['ENVOI_MAM_AGENT_APP_TOKEN']
28
+ args[:config_service_app_url] = ENV['ENVOI_MAM_AGENT_APP_URL']
29
+
30
+ op = OptionParser.new
31
+ op.on('-c', '--config-file-path FILEPATH', 'The path to the configuration file.',
32
+ "Default Path(s): #{args[:config_file_path]}") { |v| args[:config_file_path] = v }
33
+ # op.on('--agent-app-id ID', '') { |v| args[:config_service_app_id] = v }
34
+ # op.on('--agent-token TOKEN', '') { |v| args[:config_service_app_token] = v }
35
+ # op.on('--agent-service-api URL', '') { |v| args[:config_service_api_url] = v }
36
+
37
+ op.on('-h', '--help', 'Show this message.') { puts op; exit }
38
+ op.load
39
+ op.parse!
40
+
41
+ begin
42
+ agent = Envoi::Restore::Agent.load_config_and_init(args)
43
+ rescue => e
44
+ abort(e.message)
45
+ end
46
+
47
+ begin
48
+ agent.run
49
+ rescue Interrupt
50
+ agent.stop
51
+ end
52
+
@@ -8,7 +8,7 @@ require 'open3'
8
8
  require 'envoi/mam/agent/cli'
9
9
  require 'envoi/mam/vidispine/agent'
10
10
 
11
- default_config_file_paths = Envoi::Mam::Agent::Cli::CONFIG_FILE_PATHS
11
+ default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
12
12
 
13
13
  aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
14
14
  default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
@@ -11,7 +11,7 @@ require 'envoi/mam/iconik/agent'
11
11
  current_command = ARGV.shift
12
12
  ARGV << '--help' if ARGV.empty?
13
13
 
14
- default_config_file_paths = Envoi::Mam::Agent::Cli::CONFIG_FILE_PATHS
14
+ default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
15
15
  # test_args = %w(--download --send-to-envoi --destination-path /tmp)
16
16
  # ARGV.concat test_args
17
17
  args = {
@@ -1,4 +1,12 @@
1
1
  require 'envoi/mam/agent/transfer_client'
2
+ require 'asperalm/fasp/local'
3
+
4
+ module Asperalm
5
+ class Log
6
+ def logger=(new_logger) @logger = new_logger end
7
+ end
8
+ end
9
+
2
10
 
3
11
  module Envoi
4
12
  module Mam
@@ -11,12 +19,12 @@ module Envoi
11
19
  DEFAULT_ASCP_ARGS = '-v -k3 --overwrite=diff -P 33001'
12
20
 
13
21
  ASCP_PATHS = [
14
- '/usr/local/bin/ascp',
15
- '/usr/bin/ascp',
16
- '/Library/Aspera/bin/ascp',
17
- '~/Applications/Aspera CLI/bin/ascp',
18
- '~/Applications/Aspera Connect.app/Contents/Resources/ascp',
19
- '/Applications/Aspera Connect.app/Contents/Resources/ascp'
22
+ '/usr/local/bin/ascp',
23
+ '/usr/bin/ascp',
24
+ '/Library/Aspera/bin/ascp',
25
+ '~/Applications/Aspera CLI/bin/ascp',
26
+ '~/Applications/Aspera Connect.app/Contents/Resources/ascp',
27
+ '/Applications/Aspera Connect.app/Contents/Resources/ascp'
20
28
  ]
21
29
  ASCP_PATHS.map { |v| File.expand_path(v) }
22
30
 
@@ -32,52 +40,166 @@ module Envoi
32
40
  File.executable?(@ascp_path)
33
41
  end
34
42
 
35
- def build_ascp_command(config, mode, source_path, target_path)
43
+ def build_asperala_transfer_args(config, mode, source_path, target_path)
44
+
36
45
  aspera_host_address = config['host_address'] || config['host']
37
46
  aspera_username = config['username']
38
47
  aspera_password = config['password']
48
+ aspera_ssh_port = config['ssh_port']
49
+
50
+ aspera_url = config['url'] || config['uri']
51
+ if aspera_url
52
+ aspera_uri = URI(aspera_url)
53
+ aspera_host_address ||= aspera_uri.host if aspera_uri
54
+ un, pw = (aspera_uri.userinfo || '').split(':')
55
+ aspera_username ||= un if un
56
+ aspera_password ||= pw if pw
57
+ aspera_ssh_port = aspera_uri.port
58
+ end
59
+
60
+ force_username_password = config.fetch('force_username_password', false)
61
+
39
62
  aspera_token = config['aspera_transfer_token'] || config['aspera_token'] || config['token'] || config['transfer_token']
63
+ if aspera_username && aspera_password && !force_username_password && (aspera_token.nil? || aspera_token.empty?)
64
+ aspera_token = %(Basic #{["#{aspera_username}:#{aspera_password}"]
65
+ .pack('m')
66
+ .delete("\r\n")})
67
+ aspera_username = 'xfer'
68
+ aspera_password = nil
69
+ end
70
+
71
+ # @ascp_path = config['ascp_path'] || default_ascp_path
72
+ # @ascp_path = File.expand_path(@ascp_path)
73
+ # aspera_ascp_path = @ascp_path
74
+
75
+ env_vars = { }
76
+ env_vars['ASPERA_SCP_PASS'] = aspera_password if aspera_password
77
+ # env_vars['ASPERA_SCP_TOKEN'] = aspera_token if aspera_token
78
+
79
+ ascp_args = config['ascp_args'] ||
80
+ default_ascp_args ||
81
+ (agent.respond_to?(:default_ascp_args) ? agent.default_ascp_args : '')
82
+
83
+ tags = config['tags'] ||= { }
84
+ aspera_tags = tags['aspera'] ||= { }
85
+ aspera_tags['xfer_id'] ||= SecureRandom.uuid
86
+
87
+ cmdline_args = [
88
+ '--mode', mode,
89
+ '--host', aspera_host_address,
90
+ '--user', aspera_username,
91
+ '--tags64', Base64.strict_encode64(JSON.generate(tags)),
92
+ ]
93
+ cmdline_args.concat [ '-P', aspera_ssh_port ] if aspera_ssh_port
94
+ cmdline_args.concat ascp_args.is_a?(Array) ? ascp_args : ascp_args.split(' ') if ascp_args && !ascp_args.empty?
95
+ if aspera_token && !aspera_token.empty?
96
+ cmdline_args.concat ['-W', aspera_token, '-i', 'asperaweb_id_dsa.openssh' ]
97
+ end
98
+ cmdline_args.concat [ source_path.gsub('"', '\"'), target_path.gsub('"', '\"') ]
99
+
100
+ { env: env_vars, args: cmdline_args, ascp_version: :ascp }
101
+ end
102
+
103
+ def build_ascp_command(config, mode, source_path, target_path)
104
+ aspera_host_address = config['host_address'] || config['host']
105
+ aspera_username = config['username']
106
+ aspera_password = config['password']
107
+ aspera_ssh_port = config['ssh_port']
108
+
109
+ aspera_url = config['url'] || config['uri']
110
+ if aspera_url
111
+ aspera_uri = URI(aspera_url)
112
+ aspera_host_address ||= aspera_uri.host if aspera_uri
113
+ un, pw = (aspera_uri.userinfo || '').split(':')
114
+ aspera_username ||= un if un
115
+ aspera_password ||= pw if pw
116
+ aspera_ssh_port ||= aspera_uri.port
117
+ end
40
118
 
119
+ aspera_token = config['aspera_transfer_token'] || config['aspera_token'] || config['token'] || config['transfer_token']
120
+ if aspera_username && aspera_password && (aspera_token.nil? || aspera_token.empty?)
121
+ aspera_token = %(Basic #{["#{aspera_username}:#{aspera_password}"]
122
+ .pack('m')
123
+ .delete("\r\n")})
124
+ aspera_username = 'xfer'
125
+ end
41
126
  @ascp_path = config['ascp_path'] || default_ascp_path
42
127
  @ascp_path = File.expand_path(@ascp_path)
43
128
 
44
129
  aspera_ascp_path = @ascp_path
45
130
 
131
+ tags = config['tags'] || { }
132
+ aspera_tags = tags['aspera'] || { }
133
+ aspera_tags['xfer_id'] ||= SecureRandom.uuid
134
+
135
+
46
136
  ascp_args = config['ascp_args'] || default_ascp_args || agent.default_ascp_args
137
+
47
138
  # command = %(export ASPERA_SCP_PASS="#{aspera_password}"; "#{aspera_ascp_path}" #{ascp_args} --host="#{aspera_host_address}" --user=#{aspera_username} --mode=#{mode} "#{source_path}" "#{target_path}" )
48
139
  command = ''
49
140
  command << %(export ASPERA_SCP_PASS="#{aspera_password}"; ) if aspera_password
50
141
  command << %("#{aspera_ascp_path}" --mode=#{mode} --host="#{aspera_host_address}" --user="#{aspera_username}")
51
- command << %( #{ascp_args}) if ascp_args && !ascp_args.empty?
142
+ command << %( -P #{aspera_ssh_port}) if aspera_ssh_port
143
+ command << %(--tags64 #{Base64.strict_encode64(JSON.generate(tags))}) if tags && !tags.empty?
144
+ if ascp_args && !ascp_args.empty?
145
+ command << (ascp_args.is_a?(Array)) ? ascp_args.join(' ') : ascp_args
146
+ end
52
147
  command << %( -W "#{aspera_token}") if aspera_token && !aspera_token.empty?
53
148
  command << %( "#{source_path.gsub('"', '\"')}" "#{target_path.gsub('"', '\"')}")
54
149
  end
55
150
 
56
151
  def download(config, path, destination_path = DEFAULT_DESTINATION_PATH)
57
152
  aspera_base_path = config['base_path'] || ''
58
- source_path = File.join(aspera_base_path, path)
59
- command = build_ascp_command(config, 'recv', source_path, destination_path)
153
+ source_path = aspera_base_path.empty? ? path : File.join(aspera_base_path, path)
60
154
 
61
- unless ascp_path_exists?
62
- warn "ASCP not found. '#{ascp_path}'"
63
- return false
64
- end
65
- agent.shell_execute(command)
155
+ mode = 'recv'
156
+ transfer(config, mode, source_path, destination_path)
66
157
  end
67
158
 
68
159
  def upload(config, path, destination_path = DEFAULT_DESTINATION_PATH)
69
160
  aspera_base_path = config['base_path'] || ''
70
161
  source_path = path
71
162
  destination_path = aspera_base_path.empty? ? destination_path : File.join(aspera_base_path, destination_path)
72
- command = build_ascp_command(config, 'send', source_path, destination_path)
163
+
164
+ mode = 'send'
165
+ transfer(config, mode, source_path, destination_path)
166
+ end
167
+
168
+ def transfer(config, mode, source_path, destination_path)
169
+ if true
170
+ transfer_using_asperala(config, mode, source_path, destination_path)
171
+ else
172
+ transfer_using_shell_execute(config, mode, source_path, destination_path)
173
+ end
174
+ end
175
+
176
+ def transfer_using_asperala(config, mode, source_path, destination_path)
177
+ args_out = build_asperala_transfer_args(config, mode, source_path, destination_path)
178
+ @fasp ||= Asperalm::Fasp::Local.instance
179
+ Asperalm::Log.instance.logger = logger
180
+ # Asperalm::Log.instance.level = :debug #logger.level
181
+ @fasp.start_transfer_with_args_env(args_out, {})
182
+ { success: true }
183
+ end
184
+
185
+ def transfer_using_shell_execute(config, mode, source_path, destination_path)
186
+ command = build_ascp_command(config, mode, source_path, destination_path)
73
187
 
74
188
  unless ascp_path_exists?
75
- warn "ASCP not found. '#{ascp_path}'"
76
- return false
189
+ msg = "ASCP not found. '#{ascp_path}'"
190
+ warn msg
191
+ return { message: msg, success: false }
77
192
  end
193
+
78
194
  agent.shell_execute(command)
79
195
  end
80
196
 
197
+ def shutdown(graceful = true)
198
+ if @fasp && @fasp.respond_to?(:shutdown)
199
+ @fasp.shutdown(graceful)
200
+ end
201
+ end
202
+
81
203
  # AsperaTransferClient
82
204
  end
83
205
 
@@ -17,12 +17,20 @@ module Envoi
17
17
  region = config['region']
18
18
  access_key_id = config['access_key_id']
19
19
  secret_access_key = config['secret_access_key']
20
+ profile_name = config['profile'] || config['aws_profile']
20
21
 
21
- resource_args = { }
22
- resource_args[:region] = region if region && !region.empty?
23
- resource_args[:access_key_id] = access_key_id if access_key_id && !access_key_id.empty?
24
- resource_args[:secret_access_key] = secret_access_key if secret_access_key && !secret_access_key.empty?
25
- @s3 = Aws::S3::Resource.new(resource_args)
22
+ aws_config = {}
23
+ aws_config[:credentials] = (access_key_id || secret_access_key) ?
24
+ Aws::Credentials.new(access_key_id, secret_access_key) :
25
+ Aws::SharedCredentials.new(profile_name: profile_name)
26
+ aws_config[:region] = region if region
27
+
28
+ # resource_args = { }
29
+ # resource_args[:region] = region if region && !region.empty?
30
+ # resource_args[:access_key_id] = access_key_id if access_key_id && !access_key_id.empty?
31
+ # resource_args[:secret_access_key] = secret_access_key if secret_access_key && !secret_access_key.empty?
32
+ # @s3 = Aws::S3::Resource.new(resource_args)
33
+ @s3 = Aws::S3::Resource.new(aws_config)
26
34
  end
27
35
 
28
36
  def download(config, path, destination_path = DEFAULT_DESTINATION_PATH)
@@ -1,7 +1,7 @@
1
1
  module Envoi
2
2
  module Mam
3
3
  class Agent
4
- VERSION = '1.1.0'.freeze
4
+ VERSION = '1.2.0'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,353 @@
1
+ require 'cantemo/portal/api/client'
2
+ require 'envoi/mam/agent/transfer_client/aspera'
3
+ require 'envoi/mam/agent/transfer_client/s3'
4
+
5
+ module Envoi
6
+
7
+ module Mam
8
+
9
+ module Cantemo
10
+
11
+ class Agent < Envoi::Mam::Agent
12
+
13
+ DEFAULT_SHAPE_TAG = 'original'
14
+ DEFAULT_DESTINATION_PATH = '.'
15
+ DEFAULT_PRESERVE_FILE_PATH = false
16
+
17
+ attr_accessor :default_aspera_ascp_args,
18
+ :default_aspera_ascp_path,
19
+ :default_vidispine_shape_tag,
20
+ :default_preserve_file_path
21
+
22
+ def after_initialize
23
+ args = initial_args
24
+ @default_aspera_ascp_path = args[:default_aspera_ascp_path]
25
+ @default_aspera_args = args.fetch(:default_ascp_args, Envoi::Mam::Agent::TransferClient::Aspera::DEFAULT_ASCP_ARGS)
26
+ @default_preserve_file_path = args.fetch(:default_preserve_file_path, DEFAULT_PRESERVE_FILE_PATH)
27
+ end
28
+
29
+ def dry_run?;
30
+ @dry_run
31
+ end
32
+
33
+ def agent_config
34
+ @agent_config ||= config['cantemo'] || config || {}
35
+ end
36
+
37
+ def agent_config_storages
38
+ agent_config['storages']
39
+ end
40
+
41
+ def initialize_api_client(args = {})
42
+ api_config = agent_config
43
+ @api_client = args[:api_client] || begin
44
+ client_args = Hash[api_config.map { |k,v| [ k.respond_to?(:to_sym) ? k.to_sym.downcase : k, v ]}]
45
+ client_args[:logger] = logger
46
+ _client = ::Cantemo::Portal::API::Client.new(client_args)
47
+
48
+ begin
49
+ _client.version
50
+ rescue => e
51
+ raise "Error connecting to Portal: #{e.message}"
52
+ end
53
+
54
+ _client
55
+ end
56
+
57
+ @default_vidispine_shape_tag = args[:default_shape_tag] || api_config['default_shape_tag'] || api_config['shape_tag'] || DEFAULT_SHAPE_TAG
58
+
59
+ end
60
+
61
+ def item_get_shape_by_tag(item_id, shape_tag)
62
+ item_shapes_get_response = api_client.item_shapes_get(:item_id => item_id, :tag => shape_tag)
63
+ shape_id = item_shapes_get_response['uri'].first
64
+ end
65
+
66
+ def download(args = {})
67
+
68
+ item_id = args[:item_id]
69
+ shape_id = args[:shape_id]
70
+ unless shape_id && !shape_id.empty?
71
+ shape_tag = args[:shape_tag] || default_vidispine_shape_tag
72
+ shape_id = item_get_shape_by_tag(item_id, shape_tag)
73
+ end
74
+
75
+ logger.info { "Getting file path for Item ID: #{item_id} Shape ID: #{shape_id}" }
76
+ item_shape_get_response = api_client.item_shape_get(:item_id => item_id, :shape_id => shape_id)
77
+
78
+ files = item_shape_get_response['containerComponent']['file']
79
+ logger.debug { "Files: #{files}" }
80
+
81
+ # file = files.first
82
+ files = [files.first] # just do the first file for now
83
+ files.each do |file|
84
+ begin
85
+ download_file(args, file)
86
+ rescue => e
87
+ logger.warn { "Exception: #{$!}" }
88
+ end
89
+ end
90
+ logger.info { 'DONE' }
91
+ end
92
+
93
+ def download_file(args, file)
94
+ logger.debug { "File: #{file}" }
95
+ transfer_type = args[:transfer_type]
96
+
97
+ file_storage_id = file['storage']
98
+ file_path = file['path']
99
+
100
+ file_storage_config = agent_config_storages[file_storage_id]
101
+
102
+ unless file_storage_config && !file_storage_config.empty?
103
+ raise Exception, "No configuration found for storage '#{file_storage_id}'"
104
+ end
105
+
106
+ logger.info { "Transferring File Path: '#{file_path}'" }
107
+ preserve_path = args.fetch(:preserve_path, file_storage_config.fetch('preserve_path', default_preserve_file_path))
108
+
109
+ destination_path = args[:destination_path] || file_storage_config['destination_path'] || DEFAULT_DESTINATION_PATH
110
+ relative_path = preserve_path ? File.dirname(file_path) : nil
111
+ relative_path = nil if relative_path == '.'
112
+
113
+ target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
114
+ target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
115
+
116
+ aspera_config = file_storage_config['aspera']
117
+ if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
118
+ client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
119
+ return client.download(aspera_config, file_path, target_path)
120
+ # download_using_aspera(aspera_config, file_path, target_path)
121
+ end
122
+
123
+ s3_config = file_storage_config['s3']
124
+ if (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
125
+ target_path = File.expand_path(target_path) if target_path == '.'
126
+ target_path = File.join(target_path, File.basename(file_path))
127
+ client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
128
+ return client.download(s3_config, file_path, target_path)
129
+ end
130
+
131
+ logger.warn { "No Supported TransferClient Configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}' " : ''}Found in Storage Configuration." }
132
+ end
133
+
134
+ # @param [Hash] args
135
+ # @option args [String] :file_path
136
+ # @option args [String] :storage_id
137
+ # @option args [String] :transfer_type ('')
138
+ # @option args [Boolean] :import_file (true)
139
+ # @option args [Boolean] :preserve_path (storage_config['preserve_path'] || default_preserve_path)
140
+ #
141
+ # @return [Hash]
142
+ def upload(args = {})
143
+ _response = {}
144
+
145
+ file_path = args[:file_path]
146
+ raise ArgumentError, "Path not found: '#{file_path}'" unless File.exists?(file_path)
147
+
148
+ if File.directory?(file_path)
149
+ # Non-recursive directory upload
150
+ file_paths = Dir.glob(File.join(file_path, '*.*'))
151
+ logger.debug { "File Paths: #{file_paths}" }
152
+ file_paths.map { |fp| upload(args.merge(file_path: fp)) }
153
+ return file_paths
154
+ end
155
+ logger.debug { "Preparing to upload '#{file_path}'" }
156
+
157
+ transfer_type = args[:transfer_type] || ''
158
+ storage_id = args[:storage_id]
159
+ storage_config = agent_config_storages[storage_id]
160
+
161
+ unless storage_config && !storage_config.empty?
162
+ raise "No configuration found for storage '#{storage_id}'"
163
+ end
164
+
165
+ should_import_file = args.fetch(:import_file, storage_config.fetch('import', true))
166
+
167
+ should_preserve_path = args.fetch(:preserve_path,
168
+ storage_config.fetch('preserve_path', default_preserve_file_path))
169
+
170
+ destination_path = args[:destination_path] || storage_config['destination_path'] || '/'
171
+ relative_path = should_preserve_path ? File.dirname(file_path) : nil
172
+ relative_path = File.expand_path(relative_path) if relative_path == '.'
173
+
174
+ target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
175
+ target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
176
+
177
+
178
+ # upload file
179
+
180
+ transfer_response = begin
181
+ response = nil
182
+ aspera_config = storage_config['aspera']
183
+ begin
184
+ if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
185
+ client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
186
+ response = client.upload(aspera_config, file_path, target_path)
187
+ end
188
+ rescue => e
189
+ logger.error { "Aspera Transfer Failed. '#{e.message}'\n#{e.backtrace.first}" }
190
+ end
191
+
192
+ s3_config = storage_config['s3']
193
+ begin
194
+ if !response && (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
195
+ _target_path = target_path
196
+ _target_path = File.expand_path(_target_path) if target_path == '.'
197
+ _target_path = File.join(_target_path, File.basename(file_path))
198
+ client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
199
+ response = client.upload(s3_config, file_path, _target_path)
200
+ end
201
+ rescue => e
202
+ logger.error { "S3 Transfer Failed. '#{e.message}'" }
203
+ end
204
+
205
+ response
206
+ end
207
+ transfer_response = { success: transfer_response } if transfer_response == true || transfer_response == false
208
+
209
+ logger.debug { "Transfer Response: #{transfer_response}" }
210
+ _response[:transfer_response] = transfer_response
211
+
212
+ if transfer_response.nil?
213
+ logger.warn { "No supported TransferClient configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}'" : ''} found in storage configuration." }
214
+ _response[:success] = false
215
+ return _response
216
+ end
217
+
218
+ unless transfer_response[:success]
219
+ logger.error { "Error transferring file." }
220
+ _response[:success] = false
221
+ return _response
222
+ end
223
+
224
+ unless should_import_file
225
+ _response[:success] = transfer_response[:success]
226
+ return _response
227
+ end
228
+
229
+
230
+ ### IMPORT - START
231
+ import_file_args = args.dup
232
+ import_file_args[:response_object] = _response
233
+ import_file_args[:target_path] = target_path
234
+ import_file(import_file_args)
235
+ _response[:success] = true unless _response[:success] === false
236
+ ### IMPORT - END
237
+
238
+ _response
239
+ rescue => e
240
+ logger.error { "Exception: #{e.message}" }
241
+ _response[:exception] = e
242
+ return _response
243
+ end
244
+
245
+ # @param [Hash] args
246
+ # @option args [String] :file_path
247
+ # @option args [Hash] :import_args ({})
248
+ # @option args [Hash] :import_options ({})
249
+ # @option args [String] :item_id
250
+ # @option args [Hash] :response_object ({})
251
+ # @option args [String] :shape_tag (@default_vidispine_shape_tag)
252
+ # @option args [String] :storage_id
253
+ # @option args [String] :target_path
254
+ #
255
+ # @return [Hash{ :success->Boolean, :file_create_response->{}, :import_response->{} }]
256
+ def import_file(args = {})
257
+ logger.debug { "Agent - Import File - Args: #{args}"}
258
+ _response = args[:response_object] || {}
259
+
260
+ file_path = args[:file_path]
261
+ storage_id = args[:storage_id]
262
+
263
+ target_path = args[:target_path]
264
+
265
+ item_id = args[:item_id]
266
+ shape_tag = args[:shape_tag] || default_vidispine_shape_tag
267
+
268
+ item_add_args_in = args[:item_add_args] || { }
269
+ item_add_options_in = args[:item_add_options] || { }
270
+
271
+ # attach file to item as shape
272
+ path_on_storage = File.join(target_path, File.basename(file_path))
273
+ path_on_storage = path_on_storage[1..-1] if path_on_storage.start_with?('/')
274
+ # file_create_response = api_client.storage_file_create(storage_id: storage_id,
275
+ # path: path_on_storage, state: 'CLOSED')
276
+ file_create_response = api_client.storage_file_get_or_create(storage_id, path_on_storage, { :extended_response => true })
277
+ _response[:file_create_response] = file_create_response
278
+ file = file_create_response[:file]
279
+
280
+ file_id = file['id']
281
+
282
+ unless file_id
283
+ _file = file.dup
284
+ _file.keep_if { |k, v| v }
285
+ logger.error { "Failed to create file. #{_file}" }
286
+ _response[:success] = false
287
+ return _response
288
+ end
289
+
290
+ item = (file['item'] || []).first
291
+
292
+ if item
293
+ shape = (item['shape'] || []).first
294
+ msg = "File already exist and is associated to item #{item['id']} as shape #{shape['id']}."
295
+ logger.warn { "#{msg} #{file}" }
296
+ _response[:error] = { :message => msg }
297
+ _response[:success] = false
298
+ return _response
299
+ end
300
+
301
+ if item_id
302
+ item_shape_import_response = api_client.item_shape_import(item_id: item_id,
303
+ tag: shape_tag, file_id: file_id)
304
+ else
305
+ item_add_args = {
306
+ storage_id: storage_id,
307
+ file_path: path_on_storage,
308
+ file_id: file_id,
309
+ storage_path_map: { '/' => storage_id }
310
+ }
311
+ item_add_args.merge!(item_add_args_in) if item_add_args_in.is_a?(Hash)
312
+
313
+ item_add_options = { }
314
+ item_add_options.merge!(item_add_options_in) if item_add_options_in.is_a?(Hash)
315
+
316
+ logger.debug { "Executing Item Add/Import. ARGS: #{item_add_args} OPTS: #{item_add_options}" }
317
+ item_shape_import_response = api_client.item_add_using_file_path(item_add_args, item_add_options)
318
+ end
319
+ _response[:import_response] = item_shape_import_response
320
+
321
+ _response
322
+ end
323
+
324
+ def ingest_file
325
+ # 1) export ASPERA_SCP_TOKEN="ATB3_AEA9dUI5dd2u1k8XBMVD0qInR-Lyylkz8AylTXLVt5_SMVGR8SO7Sdc0lj6RkoHK_DvAAEWac8bllF_Yan1NbbDTyPj_3BTA"
326
+ # 2) ascp -i asperaweb_id_dsa.openssh -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
327
+ #
328
+ # single line equivalent
329
+ # ascp -i asperaweb_id_dsa.openssh -W 'ATB3_AEAiBdrf-7sMQu782sPoQ4Q7Yh4LadgvYK9BP4Kt1hcVlZGR8SO7Sdc0lj6RkoHK_DvAAG5XlBBIZGEINicYOxMRE7g_3BTA' -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
330
+
331
+ # Aspera:
332
+ # `GET /aspera/ping/` - to check if Aspera upload is available
333
+ # `POST /vs/item/new/` to create a placeholder to upload to
334
+ # `POST /aspera/upload_setup/` - Aspera token
335
+ # ... upload the file with Aspera ...
336
+ # `POST /API/v1/placeholder/VX-3012/associatefile/?filepath=Screen%20Shot%202018-08-07%20at%2013.06.58.png&storageid=VX-11&platform=mac` - associate new file to placeholder
337
+
338
+ # HTTP upload:
339
+ # `POST /vs/item/new/` - create a placeholder
340
+ # then streamind POST (?) requests to `VSAPI/item/VX-3014/shape/essence/raw?transferId=2D30030A-6EC3-4364-9598-5BF20972A218&throttle=false&filename=Screen%20Shot%202018-08-07%20at%2014.34.15.png&jobmetadata=portal_groups:StringArray%3dAdmin` to upload the data.
341
+ # that end-point for HTTP is documented in http://apidoc.vidispine.com/latest/ref/item/shape.html#import-a-shape-using-the-request-body
342
+ #
343
+ end
344
+
345
+ # Agent
346
+ end
347
+
348
+ # Cantemo
349
+ end
350
+
351
+ end
352
+
353
+ end