cnvrg 1.2.7 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/cnvrg.gemspec +3 -2
- data/lib/cnvrg/api.rb +2 -2
- data/lib/cnvrg/cli.rb +51 -36
- data/lib/cnvrg/data.rb +14 -3
- data/lib/cnvrg/datafiles.rb +20 -62
- data/lib/cnvrg/dataset.rb +198 -144
- data/lib/cnvrg/downloader/client.rb +53 -0
- data/lib/cnvrg/downloader/clients/azure_client.rb +22 -0
- data/lib/cnvrg/downloader/clients/gcp_client.rb +46 -0
- data/lib/cnvrg/downloader/clients/s3_client.rb +51 -0
- data/lib/cnvrg/files.rb +17 -44
- data/lib/cnvrg/helpers/executer.rb +171 -0
- data/lib/cnvrg/job_cli.rb +33 -0
- data/lib/cnvrg/project.rb +45 -6
- data/lib/cnvrg/storage.rb +128 -0
- data/lib/cnvrg/version.rb +1 -1
- metadata +28 -7
- data/lib/cnvrg/job.rb +0 -40
@@ -0,0 +1,33 @@
|
|
1
|
+
module Cnvrg
|
2
|
+
class JobCli < SubCommandBase
|
3
|
+
|
4
|
+
desc 'log', description: '', hide: true
|
5
|
+
method_option :level, :type => :string, :aliases => ["-l", "--level"], :default => 'info'
|
6
|
+
method_option :step, :type => :string, :aliases => ["-s", "--step"], :default => nil
|
7
|
+
def log(*logs)
|
8
|
+
Cnvrg::CLI.new.log_start(__method__, args, options)
|
9
|
+
@project = Project.new(owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
10
|
+
@project.job_log(logs, level: options['level'], step: options['step'])
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
desc 'start', description: '', hide: true
|
15
|
+
def start(*logs)
|
16
|
+
cli = Cnvrg::CLI.new
|
17
|
+
cli.log_start(__method__, args, options)
|
18
|
+
@project = Project.new(nil, owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
19
|
+
@executer = Helpers::Executer.new(project: @project, job_type: ENV['CNVRG_JOB_TYPE'], job_id: ENV['CNVRG_JOB_ID'])
|
20
|
+
commands = @executer.fetch_commands
|
21
|
+
@executer.execute_cmds(commands)
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'requirements', description: '', hide: true
|
25
|
+
def requirements
|
26
|
+
cli = Cnvrg::CLI.new
|
27
|
+
cli.log_start(__method__, args, options)
|
28
|
+
project_dir = cli.get_project_home
|
29
|
+
@project = Project.new(project_dir, owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/cnvrg/project.rb
CHANGED
@@ -7,11 +7,11 @@ module Cnvrg
|
|
7
7
|
IDXParallelThreads ||= Cnvrg::Helpers.parallel_threads
|
8
8
|
|
9
9
|
|
10
|
-
def initialize(project_home)
|
10
|
+
def initialize(project_home=nil, slug: nil, owner: nil)
|
11
11
|
begin
|
12
12
|
@local_path = project_home
|
13
13
|
@working_dir = project_home
|
14
|
-
config = YAML.load_file(project_home + "/.cnvrg/config.yml")
|
14
|
+
config = YAML.load_file(project_home + "/.cnvrg/config.yml") rescue {owner: owner, project_slug: slug}
|
15
15
|
@title = config[:project_name]
|
16
16
|
@slug = config[:project_slug]
|
17
17
|
@owner = config[:owner]
|
@@ -126,7 +126,7 @@ module Cnvrg
|
|
126
126
|
|
127
127
|
# Create project
|
128
128
|
|
129
|
-
def self.create(project_name, clean, with_docker = false)
|
129
|
+
def self.create(project_name, clean, with_docker = false, bucket: nil)
|
130
130
|
if clean
|
131
131
|
list_dirs = [project_name, project_name + "/.cnvrg"]
|
132
132
|
else
|
@@ -154,7 +154,7 @@ module Cnvrg
|
|
154
154
|
begin
|
155
155
|
|
156
156
|
owner = Cnvrg::CLI.get_owner()
|
157
|
-
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: with_docker})
|
157
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: with_docker, bucket: bucket})
|
158
158
|
Cnvrg::CLI.is_response_success(response)
|
159
159
|
response = JSON.parse response["result"]
|
160
160
|
project_slug = response["slug"]
|
@@ -177,7 +177,7 @@ module Cnvrg
|
|
177
177
|
return true
|
178
178
|
end
|
179
179
|
|
180
|
-
def self.link(owner, project_name, docker = false, git = false)
|
180
|
+
def self.link(owner, project_name, docker = false, git = false, bucket: nil)
|
181
181
|
ignore_exits = File.exist? ".cnvrgignore"
|
182
182
|
list_dirs = [".cnvrg"
|
183
183
|
]
|
@@ -192,7 +192,7 @@ module Cnvrg
|
|
192
192
|
cnvrgreadme = Helpers.readme_content
|
193
193
|
cnvrgignore = Helpers.cnvrgignore_content
|
194
194
|
begin
|
195
|
-
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: docker})
|
195
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: docker, bucket: bucket})
|
196
196
|
Cnvrg::CLI.is_response_success(response)
|
197
197
|
response = JSON.parse response["result"]
|
198
198
|
project_slug = response["slug"]
|
@@ -324,6 +324,30 @@ module Cnvrg
|
|
324
324
|
YAML.load_file(@working_dir + "/.cnvrg/config.yml") rescue {}
|
325
325
|
end
|
326
326
|
|
327
|
+
def get_storage_client
|
328
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/client", 'GET')
|
329
|
+
if Cnvrg::CLI.is_response_success(response, false)
|
330
|
+
client_params = response['client']
|
331
|
+
else
|
332
|
+
client_params = get_storage_client_fallback
|
333
|
+
end
|
334
|
+
Cnvrg::Downloader::Client.factory(client_params)
|
335
|
+
end
|
336
|
+
|
337
|
+
def get_storage_client_fallback
|
338
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/download_files", "POST", {files: [], commit: get_latest_commit})
|
339
|
+
raise StandardError.new("Can't find project credentials") unless Cnvrg::CLI.is_response_success(response, false)
|
340
|
+
files = response['result']
|
341
|
+
storage = files['is_s3'] ? 's3' : 'minio'
|
342
|
+
files['storage'] = storage
|
343
|
+
files
|
344
|
+
end
|
345
|
+
|
346
|
+
def get_latest_commit
|
347
|
+
resp = clone(0, '')
|
348
|
+
resp['result']['commit']
|
349
|
+
end
|
350
|
+
|
327
351
|
def set_config(config)
|
328
352
|
slug = config[:project_slug] rescue nil
|
329
353
|
owner = config[:owner] rescue nil
|
@@ -631,6 +655,21 @@ module Cnvrg
|
|
631
655
|
self.set_config(config)
|
632
656
|
end
|
633
657
|
|
658
|
+
def job_log(logs, level: 'info', step: nil, job_type: nil, job_id: nil)
|
659
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
660
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
661
|
+
logs = [logs].flatten
|
662
|
+
if job_type.blank? or job_id.blank?
|
663
|
+
raise StandardError.new("Cant find job env variables")
|
664
|
+
end
|
665
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/log", "POST", {job_type: job_type, job_id: job_id, logs: logs, log_level: level, step: step, timestamp: Time.now})
|
666
|
+
end
|
667
|
+
|
668
|
+
|
669
|
+
def job_commands
|
670
|
+
job_type, job_id = ENV['CNVRG_JOB_TYPE'], ENV['CNVRG_JOB_ID']
|
671
|
+
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/commands", "GET")
|
672
|
+
end
|
634
673
|
|
635
674
|
def spot_will_terminate
|
636
675
|
restart = false
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Cnvrg
|
2
|
+
class Storage
|
3
|
+
def initialize(dataset: nil, project: nil, root_path: nil)
|
4
|
+
@element = dataset || project
|
5
|
+
@root_path = root_path
|
6
|
+
@client = @element.get_storage_client
|
7
|
+
end
|
8
|
+
|
9
|
+
def log_error(action: nil, error: '')
|
10
|
+
"[#{Time.now}] (#{action || 'default'}) #{error}"
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def get_chunks_size
|
15
|
+
(ENV['CNVRG_STORAGE_CHUNK_SIZE'] || 10).to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def init_progress_bar(size: nil, title: "Download Progress")
|
19
|
+
@progressbar = ProgressBar.create(:title => title,
|
20
|
+
:progress_mark => '=',
|
21
|
+
:format => "%b>>%i| %p%% %t",
|
22
|
+
:starting_at => 0,
|
23
|
+
:total => size,
|
24
|
+
:autofinish => true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def make_progress(size: 1)
|
28
|
+
@progressbar.progress += size
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def clone(commit: nil)
|
33
|
+
files_generator = Proc.new do |params|
|
34
|
+
@element.get_clone_chunk(commit: commit, chunk_size: params[:limit], offset: params[:offset])
|
35
|
+
end
|
36
|
+
action = Proc.new do |storage, local|
|
37
|
+
@client.download(storage, local)
|
38
|
+
end
|
39
|
+
|
40
|
+
@stats = @element.get_stats
|
41
|
+
progress = {size: @stats['commit_size'], title: "Clone Progress"}
|
42
|
+
|
43
|
+
storage_action(files_generator: files_generator, action: action, progress: progress)
|
44
|
+
end
|
45
|
+
|
46
|
+
def upload_files(commit: nil)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def upload_files(files_generator, progress: {size: 0, title: ''})
|
51
|
+
init_progress_bar(progress)
|
52
|
+
@storage_errors = []
|
53
|
+
@finished = false
|
54
|
+
@files = Queue.new
|
55
|
+
t = Thread.new{file_gen_thread(files_generator)}
|
56
|
+
do_parallel{|file| self.upload_files_thread(file); self.make_progress(size: file['size'])}
|
57
|
+
t.join
|
58
|
+
handle_errors
|
59
|
+
end
|
60
|
+
|
61
|
+
def file_gen_upload_thread(files_generator)
|
62
|
+
while true
|
63
|
+
files = files_generator
|
64
|
+
files.each{|f| @files.push(f)}
|
65
|
+
break if files.blank?
|
66
|
+
end
|
67
|
+
@finished = true
|
68
|
+
end
|
69
|
+
|
70
|
+
def storage_action(files_generator: nil, action: nil, progress: {size: 0, title: ''})
|
71
|
+
### the generator files should have {path (encrypted), name, size}
|
72
|
+
init_progress_bar(progress)
|
73
|
+
@storage_errors = []
|
74
|
+
@finished = false
|
75
|
+
@files = Queue.new
|
76
|
+
t = Thread.new{file_gen_thread(files_generator)}
|
77
|
+
do_parallel do |file|
|
78
|
+
self.download_file_thread(file) do |local, storage|
|
79
|
+
action.call(local, storage)
|
80
|
+
end
|
81
|
+
self.make_progress(size: file['size'])
|
82
|
+
end
|
83
|
+
t.join
|
84
|
+
handle_errors
|
85
|
+
end
|
86
|
+
|
87
|
+
def file_gen_thread(file_gen)
|
88
|
+
offset = 0
|
89
|
+
chunk_size = get_chunks_size
|
90
|
+
while true
|
91
|
+
files = file_gen.call(limit: chunk_size, offset: offset)
|
92
|
+
break if files.blank?
|
93
|
+
files.each{|f| @files.push(f)}
|
94
|
+
offset += files.size
|
95
|
+
end
|
96
|
+
@finished = true
|
97
|
+
end
|
98
|
+
|
99
|
+
def handle_errors
|
100
|
+
if @storage_errors.present?
|
101
|
+
File.open(@element.working_dir + "/.cnvrg/errors.yml", "w+"){|f| f.write @storage_errors.to_yaml}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def do_parallel
|
106
|
+
Parallel.each( -> { @files.empty? ? (@finished ? Parallel::Stop : sleep(1)) : @files.pop }, in_threads: get_chunks_size) do |file|
|
107
|
+
if file == 1
|
108
|
+
next
|
109
|
+
end
|
110
|
+
yield(file)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def download_file_thread(file)
|
115
|
+
return if file.blank?
|
116
|
+
local_path = file['name']
|
117
|
+
storage_path = file['path']
|
118
|
+
(0..5).each do
|
119
|
+
begin
|
120
|
+
# @client.download(storage_path, "#{@root_path}/#{local_path}")
|
121
|
+
break
|
122
|
+
rescue => e
|
123
|
+
log_error(action: "download #{local_path}", error: e.message)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/cnvrg/version.rb
CHANGED
metadata
CHANGED
@@ -1,30 +1,31 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cnvrg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yochay Ettun
|
8
8
|
- Leah Kolben
|
9
|
+
- Omer Shacham
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2019-
|
13
|
+
date: 2019-05-05 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: bundler
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
requirements:
|
18
|
-
- - "
|
19
|
+
- - ">="
|
19
20
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
21
|
+
version: '0'
|
21
22
|
type: :development
|
22
23
|
prerelease: false
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
|
-
- - "
|
26
|
+
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
28
|
+
version: '0'
|
28
29
|
- !ruby/object:Gem::Dependency
|
29
30
|
name: rake
|
30
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -217,6 +218,20 @@ dependencies:
|
|
217
218
|
- - "~>"
|
218
219
|
- !ruby/object:Gem::Version
|
219
220
|
version: '2'
|
221
|
+
- !ruby/object:Gem::Dependency
|
222
|
+
name: google-cloud-storage
|
223
|
+
requirement: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - "~>"
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '1.16'
|
228
|
+
type: :runtime
|
229
|
+
prerelease: false
|
230
|
+
version_requirements: !ruby/object:Gem::Requirement
|
231
|
+
requirements:
|
232
|
+
- - "~>"
|
233
|
+
- !ruby/object:Gem::Version
|
234
|
+
version: '1.16'
|
220
235
|
- !ruby/object:Gem::Dependency
|
221
236
|
name: sucker_punch
|
222
237
|
requirement: !ruby/object:Gem::Requirement
|
@@ -379,18 +394,24 @@ files:
|
|
379
394
|
- lib/cnvrg/data.rb
|
380
395
|
- lib/cnvrg/datafiles.rb
|
381
396
|
- lib/cnvrg/dataset.rb
|
397
|
+
- lib/cnvrg/downloader/client.rb
|
398
|
+
- lib/cnvrg/downloader/clients/azure_client.rb
|
399
|
+
- lib/cnvrg/downloader/clients/gcp_client.rb
|
400
|
+
- lib/cnvrg/downloader/clients/s3_client.rb
|
382
401
|
- lib/cnvrg/experiment.rb
|
383
402
|
- lib/cnvrg/files.rb
|
384
403
|
- lib/cnvrg/flow.rb
|
385
404
|
- lib/cnvrg/helpers.rb
|
405
|
+
- lib/cnvrg/helpers/executer.rb
|
386
406
|
- lib/cnvrg/hyper.rb
|
387
|
-
- lib/cnvrg/
|
407
|
+
- lib/cnvrg/job_cli.rb
|
388
408
|
- lib/cnvrg/logger.rb
|
389
409
|
- lib/cnvrg/org_helpers.rb
|
390
410
|
- lib/cnvrg/project.rb
|
391
411
|
- lib/cnvrg/result.rb
|
392
412
|
- lib/cnvrg/runner.rb
|
393
413
|
- lib/cnvrg/ssh.rb
|
414
|
+
- lib/cnvrg/storage.rb
|
394
415
|
- lib/cnvrg/task.rb
|
395
416
|
- lib/cnvrg/version.rb
|
396
417
|
homepage: https://cnvrg.io
|
data/lib/cnvrg/job.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'mimemagic'
|
2
|
-
require 'aws-sdk'
|
3
|
-
require 'URLcrypt'
|
4
|
-
|
5
|
-
require 'sucker_punch'
|
6
|
-
module Cnvrg
|
7
|
-
|
8
|
-
class LogJob
|
9
|
-
include SuckerPunch::Job
|
10
|
-
|
11
|
-
def perform(upload_resp, file_path)
|
12
|
-
begin
|
13
|
-
sts_path = upload_resp["result"]["path_sts"]
|
14
|
-
uri = URI.parse(sts_path)
|
15
|
-
http_object = Net::HTTP.new(uri.host, uri.port)
|
16
|
-
http_object.use_ssl = true if uri.scheme == 'https'
|
17
|
-
request = Net::HTTP::Get.new(sts_path)
|
18
|
-
body = ""
|
19
|
-
http_object.start do |http|
|
20
|
-
response = http.request request
|
21
|
-
body = response.read_body
|
22
|
-
end
|
23
|
-
URLcrypt::key = [body].pack('H*')
|
24
|
-
s3 = Aws::S3::Resource.new(
|
25
|
-
:access_key_id => URLcrypt.decrypt(upload_resp["result"]["sts_a"]),
|
26
|
-
:secret_access_key => URLcrypt.decrypt(upload_resp["result"]["sts_s"]),
|
27
|
-
:session_token => URLcrypt.decrypt(upload_resp["result"]["sts_st"]),
|
28
|
-
:region => URLcrypt.decrypt(upload_resp["result"]["region"]))
|
29
|
-
resp = s3.bucket(URLcrypt.decrypt(upload_resp["result"]["bucket"])).
|
30
|
-
object(upload_resp["result"]["path"]+"/"+File.basename(file_path)).
|
31
|
-
upload_file(file_path)
|
32
|
-
return resp
|
33
|
-
rescue =>e
|
34
|
-
return false
|
35
|
-
|
36
|
-
end
|
37
|
-
return true
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|