cnvrg 1.9.9.9.7
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 +7 -0
- data/bin/cnvrg +9 -0
- data/cnvrg.gemspec +47 -0
- data/lib/cnvrg.rb +7 -0
- data/lib/cnvrg/Images.rb +351 -0
- data/lib/cnvrg/api.rb +247 -0
- data/lib/cnvrg/api_v2.rb +14 -0
- data/lib/cnvrg/auth.rb +79 -0
- data/lib/cnvrg/cli.rb +5715 -0
- data/lib/cnvrg/cli/flow.rb +166 -0
- data/lib/cnvrg/cli/library_cli.rb +33 -0
- data/lib/cnvrg/cli/subcommand.rb +28 -0
- data/lib/cnvrg/cli/task.rb +116 -0
- data/lib/cnvrg/colors.rb +8 -0
- data/lib/cnvrg/connect_job_ssh.rb +31 -0
- data/lib/cnvrg/data.rb +335 -0
- data/lib/cnvrg/datafiles.rb +1325 -0
- data/lib/cnvrg/dataset.rb +892 -0
- data/lib/cnvrg/downloader/client.rb +101 -0
- data/lib/cnvrg/downloader/clients/azure_client.rb +45 -0
- data/lib/cnvrg/downloader/clients/gcp_client.rb +50 -0
- data/lib/cnvrg/downloader/clients/s3_client.rb +78 -0
- data/lib/cnvrg/experiment.rb +209 -0
- data/lib/cnvrg/files.rb +1047 -0
- data/lib/cnvrg/flow.rb +137 -0
- data/lib/cnvrg/helpers.rb +422 -0
- data/lib/cnvrg/helpers/agent.rb +188 -0
- data/lib/cnvrg/helpers/executer.rb +213 -0
- data/lib/cnvrg/hyper.rb +21 -0
- data/lib/cnvrg/image.rb +113 -0
- data/lib/cnvrg/image_cli.rb +25 -0
- data/lib/cnvrg/job_cli.rb +73 -0
- data/lib/cnvrg/job_ssh.rb +48 -0
- data/lib/cnvrg/logger.rb +111 -0
- data/lib/cnvrg/org_helpers.rb +5 -0
- data/lib/cnvrg/project.rb +822 -0
- data/lib/cnvrg/result.rb +29 -0
- data/lib/cnvrg/runner.rb +49 -0
- data/lib/cnvrg/ssh.rb +94 -0
- data/lib/cnvrg/storage.rb +128 -0
- data/lib/cnvrg/task.rb +165 -0
- data/lib/cnvrg/version.rb +3 -0
- metadata +460 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Cnvrg
|
3
|
+
class ImageCli < SubCommandBase
|
4
|
+
|
5
|
+
desc 'image build --image=IMAGE_ID', 'Build image job', :hide => true
|
6
|
+
method_option :image_id, :type => :string, :aliases => ["--image"]
|
7
|
+
def build_image_job()
|
8
|
+
begin
|
9
|
+
@cli = Cnvrg::CLI.new
|
10
|
+
@cli.verify_logged_in(false)
|
11
|
+
@cli.log_start(__method__, args, options)
|
12
|
+
@cli.log_message("build image started", Thor::Shell::Color::BLUE)
|
13
|
+
image = Image.new(options["image_id"])
|
14
|
+
image.build
|
15
|
+
@cli.log_message("Image build completed successfully", Thor::Shell::Color::BLUE)
|
16
|
+
rescue => e
|
17
|
+
@cli.log_message("Image build completed with an error", Thor::Shell::Color::RED)
|
18
|
+
@cli.log_error(e)
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Cnvrg
|
2
|
+
class JobCli < SubCommandBase
|
3
|
+
|
4
|
+
desc 'log', '', :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
|
+
method_option :restart, :type => :boolean, :aliases => ["-r", "--restart"], :default => false
|
8
|
+
def log(*logs)
|
9
|
+
Cnvrg::CLI.new.log_start(__method__, args, options)
|
10
|
+
@project = Project.new(owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
11
|
+
if options['restart'] == @project.check_job_pod_restart[0] or options['step'] == "ready"
|
12
|
+
@project.job_log(logs, level: options['level'], step: options['step'])
|
13
|
+
else
|
14
|
+
@project.job_log(nil, level: options['level'], step: options['step'])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'requirements', '', :hide => true
|
19
|
+
def requirements
|
20
|
+
cli = Cnvrg::CLI.new
|
21
|
+
cli.log_start(__method__, args, options)
|
22
|
+
project_dir = cli.get_project_home
|
23
|
+
@project = Project.new(project_dir, owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'install_reqs', 'Install requirements', :hide => true
|
27
|
+
def install_reqs
|
28
|
+
cli = Cnvrg::CLI.new
|
29
|
+
cli.log_start(__method__, args, options)
|
30
|
+
@project = Project.new(nil, owner: ENV['CNVRG_OWNER'], slug: ENV['CNVRG_PROJECT'])
|
31
|
+
@executer = Helpers::Executer.new(project: @project, job_type: ENV['CNVRG_JOB_TYPE'], job_id: ENV['CNVRG_JOB_ID'])
|
32
|
+
commands = @executer.get_requirements_commands
|
33
|
+
@executer.execute_cmds(commands)
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'start', 'Job Start!', :hide => true
|
37
|
+
def start
|
38
|
+
cli = Cnvrg::CLI.new
|
39
|
+
cli.log_start(__method__, args, options)
|
40
|
+
cli.auth
|
41
|
+
poll_every = (ENV["CNVRG_AGENT_POLL_EVERY"] || '30').to_i
|
42
|
+
owner = ENV["CNVRG_OWNER"]
|
43
|
+
machine_activity_slug = ENV["CNVRG_MACHINE_ACTIVITY"]
|
44
|
+
job_id = ENV["CNVRG_JOB_ID"]
|
45
|
+
@executer = Cnvrg::Helpers::Executer.new(machine_activity: machine_activity_slug, poll_every: poll_every, owner: owner, job_id: job_id)
|
46
|
+
@executer.main_thread
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'stats', 'stats of agent and slave', :hide => true
|
50
|
+
def stats
|
51
|
+
cli = Cnvrg::CLI.new
|
52
|
+
cli.log_start(__method__, args, options)
|
53
|
+
poll_every = (ENV["CNVRG_AGENT_POLL_EVERY"] || '30').to_i
|
54
|
+
owner = ENV["CNVRG_OWNER"]
|
55
|
+
machine_activity_slug = ENV["CNVRG_MACHINE_ACTIVITY"]
|
56
|
+
job_id = ENV["CNVRG_JOB_ID"]
|
57
|
+
@executer = Cnvrg::Helpers::Executer.new(machine_activity: machine_activity_slug, poll_every: poll_every, owner: owner, job_id: job_id)
|
58
|
+
@executer.executer_stats
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'pre_pod_stop', '', :hide => true
|
62
|
+
def pre_pod_stop
|
63
|
+
cli = Cnvrg::CLI.new
|
64
|
+
cli.log_start(__method__, args, options)
|
65
|
+
owner = ENV["CNVRG_OWNER"]
|
66
|
+
machine_activity = YAML.load_file("/conf/.machine_activity.yml") rescue {:slug => ENV["CNVRG_MACHINE_ACTIVITY"]}
|
67
|
+
machine_activity_slug = machine_activity[:slug]
|
68
|
+
job_id = ENV["CNVRG_JOB_ID"]
|
69
|
+
@executer = Cnvrg::Helpers::Executer.new(machine_activity: machine_activity_slug, owner: owner, job_id: job_id)
|
70
|
+
@executer.pre_pod_stop
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Cnvrg
|
2
|
+
class JobSsh < SubCommandBase
|
3
|
+
|
4
|
+
desc 'ssh start', 'stats of agent and slave'
|
5
|
+
method_option :port, :type => :numeric, :aliases => ["-p", "--port"], :desc => "Port to bind into", :default => 2222
|
6
|
+
method_option :username, :type => :string, :aliases => ["-u", "--username"], :desc => "Job container user name", :default => nil
|
7
|
+
method_option :password, :type => :string, :aliases => ["--password"], :desc =>"Job Conatainer user name, will be set by cnvrg", :default => nil
|
8
|
+
method_option :kubeconfig, :type => :string, :aliases => ["--kubeconfig"], :desc => "Path to kubeconfig, if blank default config will be used", :default => nil
|
9
|
+
def start(job_id)
|
10
|
+
Cnvrg::CLI.new.log_start(__method__, args, options)
|
11
|
+
@job_ssh = ConnectJobSsh.new(job_id)
|
12
|
+
@job_ssh.start(options['username'], options['password'])
|
13
|
+
pod_name = nil
|
14
|
+
namespace = "cnvrg"
|
15
|
+
ssh_ready = false
|
16
|
+
while not ssh_ready
|
17
|
+
resp = @job_ssh.status()
|
18
|
+
status = resp["ssh_status"]
|
19
|
+
|
20
|
+
if status == "in_progress"
|
21
|
+
puts("Waiting for ssh to start ...")
|
22
|
+
sleep(3)
|
23
|
+
next
|
24
|
+
elsif status == "finished"
|
25
|
+
password = resp["password"]
|
26
|
+
username = resp["username"]
|
27
|
+
pod_name = resp["pod_name"]
|
28
|
+
namespace = resp["namespace"]
|
29
|
+
ssh_ready = true
|
30
|
+
else
|
31
|
+
puts("Failed to start ssh")
|
32
|
+
break
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if pod_name.blank? or password.blank? or username.blank?
|
36
|
+
puts("Failed to get required params")
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
puts("In order to connect to your job, define your ssh connection with the following params:")
|
41
|
+
puts("host: 127.0.0.1")
|
42
|
+
puts("port: #{options["port"]}")
|
43
|
+
puts("username: #{username}")
|
44
|
+
puts("password: #{password}")
|
45
|
+
@job_ssh.run_portforward_command(pod_name, options["port"], options["kubeconfig"], namespace)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/cnvrg/logger.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Cnvrg
|
2
|
+
module Logger
|
3
|
+
extend self
|
4
|
+
def log_handler
|
5
|
+
begin
|
6
|
+
date = DateTime.now.strftime("%m_%d_%Y")
|
7
|
+
home_dir = File.expand_path('~')
|
8
|
+
|
9
|
+
if !File.directory? home_dir + "/.cnvrg"
|
10
|
+
FileUtils.mkdir_p([home_dir + "/.cnvrg", home_dir + "/.cnvrg/tmp"])
|
11
|
+
end
|
12
|
+
if !File.exist?(home_dir + "/.cnvrg/config.yml")
|
13
|
+
FileUtils.touch [home_dir + "/.cnvrg/config.yml"]
|
14
|
+
end
|
15
|
+
logfile = File.expand_path('~') + "/.cnvrg/log_#{date}.log"
|
16
|
+
if !File.exist? logfile
|
17
|
+
FileUtils.touch([logfile])
|
18
|
+
yesterday = get_start_day - 86399
|
19
|
+
date = yesterday.strftime("%m_%d_%Y")
|
20
|
+
|
21
|
+
logfile_old = File.expand_path('~') + "/.cnvrg/log_#{date}.log"
|
22
|
+
count = 0
|
23
|
+
while not File.exist? logfile_old and count < 60
|
24
|
+
yesterday = yesterday - 86399
|
25
|
+
date = yesterday.strftime("%m_%d_%Y")
|
26
|
+
logfile_old = File.expand_path('~') + "/.cnvrg/log_#{date}.log"
|
27
|
+
count += 1
|
28
|
+
end
|
29
|
+
if File.exist? logfile_old
|
30
|
+
@files = Cnvrg::Files.new(Cnvrg::CLI.get_owner, "")
|
31
|
+
@files.upload_log_file(logfile_old, "log_#{date}.log", yesterday)
|
32
|
+
FileUtils.remove logfile_old
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
config = LogStashLogger.configure do |config|
|
37
|
+
config.customize_event do |event|
|
38
|
+
event.remove('@version')
|
39
|
+
event.remove('severity')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
$log = LogStashLogger.new(type: :file, path: logfile, sync: true, config: config)
|
43
|
+
self.remove_old_log_files
|
44
|
+
rescue
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove_old_log_files()
|
49
|
+
begin
|
50
|
+
last_week = (Time.now - (7 * 24 * 60 * 60)).strftime("%Y-%m-%d")
|
51
|
+
home = File.expand_path('~')
|
52
|
+
log_files = Dir["#{home}/.cnvrg/tmp/*.log"]
|
53
|
+
log_files.each do |l|
|
54
|
+
if File.mtime(l).strftime("%Y-%m-%d") < last_week
|
55
|
+
FileUtils.rm_rf(l)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def info(msg)
|
62
|
+
self.log_info(msg)
|
63
|
+
end
|
64
|
+
|
65
|
+
def log_method(bind: nil)
|
66
|
+
return if bind.blank?
|
67
|
+
Cnvrg::Logger.log_handler if $log.blank?
|
68
|
+
arg = {}
|
69
|
+
bind.local_variables.map do |name|
|
70
|
+
arg[name] = bind.local_variable_get(name).try(:to_s)
|
71
|
+
end
|
72
|
+
method = bind.eval('__method__')
|
73
|
+
$log.info method: method, args: arg
|
74
|
+
end
|
75
|
+
|
76
|
+
def log_error(e)
|
77
|
+
Cnvrg::Logger.log_handler if $log.blank?
|
78
|
+
return if $log.blank?
|
79
|
+
bc = ActiveSupport::BacktraceCleaner.new
|
80
|
+
bc.add_silencer{|line| line =~ /thor/}
|
81
|
+
$log.error message: "An exception #{e.class} was logged during running", type: "error"
|
82
|
+
backtrace = bc.clean(e.backtrace).slice(0,12).map.with_index{|backtrace,idx| "(#{idx}) - #{backtrace}"}.join("; ")
|
83
|
+
$log.error message: e.message, type: "error", backtrace: backtrace
|
84
|
+
end
|
85
|
+
|
86
|
+
def log_error_message(msg)
|
87
|
+
Cnvrg::Logger.log_handler if $log.blank?
|
88
|
+
return if $log.blank?
|
89
|
+
$log.error message: msg, type: "error"
|
90
|
+
end
|
91
|
+
|
92
|
+
def log_info(msg)
|
93
|
+
Cnvrg::Logger.log_handler if $log.blank?
|
94
|
+
return if $log.blank?
|
95
|
+
$log.info message: msg, type: "info"
|
96
|
+
end
|
97
|
+
|
98
|
+
def log_json(json, msg: '')
|
99
|
+
Cnvrg::Logger.log_handler if $log.blank?
|
100
|
+
return if $log.blank?
|
101
|
+
$log.info message: msg, type: "info", data: json
|
102
|
+
end
|
103
|
+
|
104
|
+
def jsonify_message(msg: '', success: true)
|
105
|
+
puts JSON[{
|
106
|
+
"msg": msg,
|
107
|
+
"success": success.to_s
|
108
|
+
}]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,822 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Cnvrg
|
5
|
+
class Project
|
6
|
+
attr_reader :slug, :owner, :title, :local_path, :working_dir, :is_git, :is_branch, :machines
|
7
|
+
|
8
|
+
RemoteURL ||= "https://cnvrg.io"
|
9
|
+
IDXParallelThreads ||= Cnvrg::Helpers.parallel_threads
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(project_home=nil, slug: nil, owner: nil)
|
13
|
+
begin
|
14
|
+
@local_path = project_home
|
15
|
+
@working_dir = project_home
|
16
|
+
config = YAML.load_file(project_home + "/.cnvrg/config.yml") rescue {owner: owner, project_slug: slug}
|
17
|
+
@title = config[:project_name]
|
18
|
+
@slug = config[:project_slug]
|
19
|
+
@owner = config[:owner]
|
20
|
+
@is_branch = config[:is_branch]
|
21
|
+
@is_git = config[:git] || false
|
22
|
+
@base_resource = "users/#{@owner}/projects/#{@slug}/"
|
23
|
+
@machines = nil
|
24
|
+
rescue => e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def base_resource
|
29
|
+
"users/#{@owner}/projects/#{@slug}/"
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def last_local_commit
|
34
|
+
YAML.load_file(@local_path + "/.cnvrg/idx.yml")[:commit] rescue nil
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def url
|
39
|
+
url = Cnvrg::Helpers.remote_url
|
40
|
+
"#{url}/#{self.owner}/projects/#{self.slug}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_ignore_list(new_ignore)
|
44
|
+
if new_ignore.nil? or new_ignore.empty?
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
list = new_ignore.split(",")
|
48
|
+
begin
|
49
|
+
File.open(self.local_path + "/.cnvrgignore", "a+") do |f|
|
50
|
+
f.puts("\n")
|
51
|
+
list.each do |i|
|
52
|
+
f.puts("#{i}\n")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return true
|
56
|
+
rescue
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_ignore_list
|
62
|
+
ignore_list = []
|
63
|
+
if !File.exist? self.local_path + "/.cnvrgignore"
|
64
|
+
return ignore_list
|
65
|
+
end
|
66
|
+
File.open(self.local_path + "/.cnvrgignore", "r").each_line do |line|
|
67
|
+
line = line.strip
|
68
|
+
if line.start_with? "#" or ignore_list.include? line or line.empty?
|
69
|
+
next
|
70
|
+
end
|
71
|
+
if line.end_with? "*"
|
72
|
+
list_regex = Dir.glob("**/#{line}", File::FNM_DOTMATCH).flatten
|
73
|
+
list_regex.each do |l|
|
74
|
+
ignore_list << l
|
75
|
+
if File.directory?(l)
|
76
|
+
all_sub = Dir.glob("#{line}/**/*", File::FNM_DOTMATCH).flatten
|
77
|
+
|
78
|
+
ignore_list << all_sub.flatten
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
elsif line.end_with? "/*"
|
83
|
+
line = line.gsub("/*", "")
|
84
|
+
regex_list = Dir.glob("**/#{line}/**/*", File::FNM_DOTMATCH).flatten
|
85
|
+
ignore_list << regex_list
|
86
|
+
elsif line.include? "*"
|
87
|
+
regex_list = Dir.glob("**/#{line}").flatten
|
88
|
+
ignore_list << regex_list
|
89
|
+
elsif line.end_with? "/" or File.directory?(line)
|
90
|
+
ignore_list << line
|
91
|
+
all_sub = Dir.glob("#{line}/**/*", File::FNM_DOTMATCH).flatten
|
92
|
+
|
93
|
+
ignore_list << all_sub.flatten
|
94
|
+
|
95
|
+
else
|
96
|
+
ignore_list << line
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return ignore_list.flatten
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def send_ignore_list()
|
104
|
+
begin
|
105
|
+
ignore_list = []
|
106
|
+
File.open(self.local_path + "/.cnvrgignore", "r").each_line do |line|
|
107
|
+
line = line.strip
|
108
|
+
if line.start_with? "#" or ignore_list.include? line or line.empty?
|
109
|
+
next
|
110
|
+
end
|
111
|
+
if line.end_with? "/"
|
112
|
+
ignore_list << line.gsub("/", "")
|
113
|
+
ignore_list << line + "."
|
114
|
+
elsif line.include? "*"
|
115
|
+
line = line.gsub("*", ".*")
|
116
|
+
ignore_list << line
|
117
|
+
else
|
118
|
+
ignore_list << line
|
119
|
+
end
|
120
|
+
end
|
121
|
+
return ignore_list.flatten
|
122
|
+
rescue
|
123
|
+
return []
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# Create project
|
129
|
+
|
130
|
+
def self.create(project_name, clean, with_docker = false, bucket: nil)
|
131
|
+
if clean
|
132
|
+
list_dirs = [project_name, project_name + "/.cnvrg"]
|
133
|
+
else
|
134
|
+
|
135
|
+
list_dirs = [project_name,
|
136
|
+
project_name + "/models",
|
137
|
+
project_name + "/notebooks",
|
138
|
+
project_name + "/src",
|
139
|
+
project_name + "/src/visualizations",
|
140
|
+
project_name + "/src/features",
|
141
|
+
project_name + "/.cnvrg"
|
142
|
+
]
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
list_files = [
|
147
|
+
project_name + "/README.md",
|
148
|
+
project_name + "/.cnvrgignore",
|
149
|
+
project_name + "/.cnvrg/config.yml"
|
150
|
+
]
|
151
|
+
cnvrgreadme = Helpers.readme_content
|
152
|
+
cnvrgignore = Helpers.cnvrgignore_content
|
153
|
+
cnvrghyper = Helpers.hyper_content
|
154
|
+
|
155
|
+
begin
|
156
|
+
|
157
|
+
owner = Cnvrg::CLI.get_owner()
|
158
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: with_docker, bucket: bucket})
|
159
|
+
Cnvrg::CLI.is_response_success(response)
|
160
|
+
response = JSON.parse response["result"]
|
161
|
+
project_slug = response["slug"]
|
162
|
+
|
163
|
+
config = {project_name: project_name,
|
164
|
+
project_slug: project_slug,
|
165
|
+
owner: owner,
|
166
|
+
docker: with_docker}
|
167
|
+
FileUtils.mkdir_p list_dirs
|
168
|
+
FileUtils.touch list_files
|
169
|
+
|
170
|
+
File.open(project_name + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
171
|
+
File.open(project_name + "/.cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
172
|
+
File.open(project_name + "/README.md", "w+") {|f| f.write cnvrgreadme}
|
173
|
+
File.open(project_name + "/src/hyper.yaml", "w+") {|f| f.write cnvrghyper}
|
174
|
+
|
175
|
+
rescue
|
176
|
+
return false
|
177
|
+
end
|
178
|
+
return true
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.link(owner, project_name, docker = false, git = false, bucket: nil)
|
182
|
+
ignore_exits = File.exist? ".cnvrgignore"
|
183
|
+
list_dirs = [".cnvrg"
|
184
|
+
]
|
185
|
+
list_files = [
|
186
|
+
".cnvrg/config.yml"
|
187
|
+
]
|
188
|
+
if !ignore_exits
|
189
|
+
list_files <<
|
190
|
+
".cnvrgignore"
|
191
|
+
end
|
192
|
+
|
193
|
+
cnvrgreadme = Helpers.readme_content
|
194
|
+
cnvrgignore = Helpers.cnvrgignore_content
|
195
|
+
begin
|
196
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: docker, bucket: bucket})
|
197
|
+
Cnvrg::CLI.is_response_success(response)
|
198
|
+
response = JSON.parse response["result"]
|
199
|
+
project_slug = response["slug"]
|
200
|
+
|
201
|
+
config = {project_name: project_name,
|
202
|
+
project_slug: project_slug,
|
203
|
+
owner: owner,
|
204
|
+
git: git}
|
205
|
+
FileUtils.mkdir_p list_dirs
|
206
|
+
FileUtils.touch list_files
|
207
|
+
File.open(".cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
208
|
+
File.open(".cnvrgignore", "w+") {|f| f.write cnvrgignore} unless ignore_exits
|
209
|
+
if !File.exist? "README" and !File.exist? "README.md"
|
210
|
+
FileUtils.touch ["README.md"]
|
211
|
+
File.open("README.md", "w+") {|f| f.write cnvrgreadme}
|
212
|
+
end
|
213
|
+
|
214
|
+
rescue => e
|
215
|
+
puts e
|
216
|
+
return false
|
217
|
+
end
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.clone_dir(project_slug, project_owner, project_name, is_git = false)
|
222
|
+
list_dirs = [project_name,
|
223
|
+
project_name + "/.cnvrg"
|
224
|
+
]
|
225
|
+
|
226
|
+
|
227
|
+
list_files = [
|
228
|
+
project_name + "/.cnvrg/config.yml",
|
229
|
+
project_name + "/.cnvrgignore",
|
230
|
+
]
|
231
|
+
begin
|
232
|
+
config = {project_name: project_name,
|
233
|
+
project_slug: project_slug,
|
234
|
+
owner: project_owner,
|
235
|
+
git: is_git}
|
236
|
+
FileUtils.mkdir_p list_dirs
|
237
|
+
FileUtils.touch list_files
|
238
|
+
cnvrgignore = Helpers.cnvrgignore_content
|
239
|
+
|
240
|
+
|
241
|
+
File.open(project_name + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
242
|
+
File.open(project_name + "/.cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
243
|
+
|
244
|
+
rescue
|
245
|
+
return false
|
246
|
+
end
|
247
|
+
return true
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.verify_cnvrgignore_exist(project_name, remote)
|
251
|
+
path = ".cnvrgignore"
|
252
|
+
if !File.exist? path
|
253
|
+
path = "#{project_name}/.cnvrgignore"
|
254
|
+
end
|
255
|
+
ignore_exits = File.exist? path
|
256
|
+
if !ignore_exits
|
257
|
+
begin
|
258
|
+
list_files = [
|
259
|
+
path
|
260
|
+
]
|
261
|
+
FileUtils.touch list_files
|
262
|
+
cnvrgignore = Helpers.cnvrgignore_content
|
263
|
+
File.open(path, "w+") {|f| f.write cnvrgignore}
|
264
|
+
rescue => e
|
265
|
+
return false
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.clone_dir_remote(project_slug, project_owner, project_name, is_git = false)
|
272
|
+
cli = Cnvrg::CLI.new()
|
273
|
+
begin
|
274
|
+
list_dirs = [
|
275
|
+
".cnvrg"
|
276
|
+
]
|
277
|
+
|
278
|
+
|
279
|
+
list_files = [
|
280
|
+
".cnvrg/config.yml",
|
281
|
+
|
282
|
+
]
|
283
|
+
|
284
|
+
|
285
|
+
config = {project_name: project_name,
|
286
|
+
project_slug: project_slug,
|
287
|
+
owner: project_owner,
|
288
|
+
git: is_git
|
289
|
+
}
|
290
|
+
FileUtils.mkdir_p list_dirs
|
291
|
+
FileUtils.touch list_files
|
292
|
+
|
293
|
+
|
294
|
+
File.open(".cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
295
|
+
if !File.exist? ".cnvrgignore"
|
296
|
+
FileUtils.touch ".cnvrgignore"
|
297
|
+
list_files << ".cnvrgignore"
|
298
|
+
cnvrgignore = Helpers.cnvrgignore_content
|
299
|
+
File.open(".cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
300
|
+
|
301
|
+
|
302
|
+
end
|
303
|
+
true
|
304
|
+
rescue => e
|
305
|
+
cli.log_message(e.message)
|
306
|
+
cli.log_error(e)
|
307
|
+
false
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def update_is_new_branch(new_branch)
|
312
|
+
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
313
|
+
config[:new_branch] = new_branch
|
314
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
315
|
+
end
|
316
|
+
|
317
|
+
def get_new_branch
|
318
|
+
begin
|
319
|
+
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
320
|
+
return config[:new_branch]
|
321
|
+
rescue => e
|
322
|
+
return false
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def get_config
|
327
|
+
YAML.load_file(@working_dir + "/.cnvrg/config.yml") rescue {}
|
328
|
+
end
|
329
|
+
|
330
|
+
def get_storage_client
|
331
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/client", 'GET')
|
332
|
+
if Cnvrg::CLI.is_response_success(response, false)
|
333
|
+
|
334
|
+
client_params = response['client']
|
335
|
+
else
|
336
|
+
|
337
|
+
client_params = get_storage_client_fallback
|
338
|
+
end
|
339
|
+
|
340
|
+
Cnvrg::Downloader::Client.factory(client_params)
|
341
|
+
end
|
342
|
+
|
343
|
+
def get_storage_client_fallback
|
344
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/download_files", "POST", {files: [], commit: get_latest_commit})
|
345
|
+
raise StandardError.new("Can't find project credentials") unless Cnvrg::CLI.is_response_success(response, false)
|
346
|
+
files = response['result']
|
347
|
+
storage = files['is_s3'] ? 's3' : 'minio'
|
348
|
+
files['storage'] = storage
|
349
|
+
files
|
350
|
+
end
|
351
|
+
|
352
|
+
def get_latest_commit
|
353
|
+
resp = clone(0, '')
|
354
|
+
|
355
|
+
resp['result']['commit']
|
356
|
+
end
|
357
|
+
|
358
|
+
def set_config(config)
|
359
|
+
slug = config[:project_slug] rescue nil
|
360
|
+
owner = config[:owner] rescue nil
|
361
|
+
name = config[:project_name] rescue nil
|
362
|
+
if slug.blank? or owner.blank? or name.blank?
|
363
|
+
return
|
364
|
+
end
|
365
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
366
|
+
end
|
367
|
+
|
368
|
+
def remove_new_branch
|
369
|
+
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
370
|
+
new_config = config.except(:new_branch)
|
371
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write new_config.to_yaml}
|
372
|
+
end
|
373
|
+
|
374
|
+
def generate_git_diff
|
375
|
+
git_diff = `git diff --name-only`
|
376
|
+
git_diff.split("\n")
|
377
|
+
rescue
|
378
|
+
[]
|
379
|
+
end
|
380
|
+
|
381
|
+
def generate_output_dir(output_dir)
|
382
|
+
Cnvrg::Logger.log_info("Generating output dir for #{output_dir}")
|
383
|
+
upload_list = []
|
384
|
+
list = Dir.glob("#{output_dir}/**/*", File::FNM_DOTMATCH)
|
385
|
+
Parallel.map(list, in_threads: IDXParallelThreads) do |e|
|
386
|
+
next if e.end_with? "/."
|
387
|
+
if File.directory? e
|
388
|
+
|
389
|
+
upload_list << e + "/"
|
390
|
+
else
|
391
|
+
upload_list << e
|
392
|
+
end
|
393
|
+
end
|
394
|
+
if Dir.exists? output_dir
|
395
|
+
upload_list << output_dir + "/"
|
396
|
+
end
|
397
|
+
Cnvrg::Logger.log_info("Uploading: #{upload_list.join(", ")}")
|
398
|
+
upload_list
|
399
|
+
end
|
400
|
+
|
401
|
+
def generate_output_dir_tmp(output_dir)
|
402
|
+
upload_list = []
|
403
|
+
list = Dir.glob("#{output_dir}/**/*", File::FNM_DOTMATCH)
|
404
|
+
Parallel.map(list, in_threads: IDXParallelThreads) do |e|
|
405
|
+
next if e.end_with? "/."
|
406
|
+
if File.directory? e
|
407
|
+
|
408
|
+
next
|
409
|
+
else
|
410
|
+
upload_list << e
|
411
|
+
end
|
412
|
+
end
|
413
|
+
# if Dir.exists? output_dir
|
414
|
+
# upload_list << output_dir + "/"
|
415
|
+
# end
|
416
|
+
|
417
|
+
|
418
|
+
return upload_list
|
419
|
+
|
420
|
+
end
|
421
|
+
|
422
|
+
def generate_idx(deploy: false, files: [])
|
423
|
+
if File.exists? "#{self.local_path}/.cnvrg/idx.yml"
|
424
|
+
old_idx = YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
425
|
+
else
|
426
|
+
old_idx = {:tree => {}, :commit => nil}
|
427
|
+
end
|
428
|
+
|
429
|
+
tree_idx = Hash.new(0)
|
430
|
+
|
431
|
+
### if file specified, just take them, dont calculate everything from scratch
|
432
|
+
list_ignore = self.get_ignore_list()
|
433
|
+
if files.blank?
|
434
|
+
list = Dir.glob("#{self.local_path}/**/*", File::FNM_DOTMATCH).reject {|x| (x =~ /\/\.{1,2}$/) or (x =~ /^#{self.local_path}\/\.cnvrg\/*/) or (x =~ /^#{self.local_path}\/\.git\/*/) or (x =~ /^#{self.local_path}\/\.cnvrgignore.conflict*/) and not (x =~ /^#{self.local_path}\/\.cnvrgignore/)}
|
435
|
+
else
|
436
|
+
list = files
|
437
|
+
end
|
438
|
+
if deploy
|
439
|
+
list_ignore += ["main.py", "main.pyc", "__init__.py", "uwsgi.ini"]
|
440
|
+
list_ignore.flatten!
|
441
|
+
end
|
442
|
+
list_ignore_new = list_ignore.map{|x| x.gsub("//","/")} rescue []
|
443
|
+
# list.each do |e|
|
444
|
+
Parallel.map(list, in_threads: IDXParallelThreads) do |e|
|
445
|
+
label = e.sub(self.local_path + "/", "")
|
446
|
+
|
447
|
+
if list_ignore_new.include? label
|
448
|
+
next
|
449
|
+
end
|
450
|
+
if File.directory? e
|
451
|
+
|
452
|
+
tree_idx[label + "/"] = nil
|
453
|
+
else
|
454
|
+
file_in_idx = old_idx[:tree][label] rescue nil
|
455
|
+
last_modified = File.mtime(e).to_f
|
456
|
+
if file_in_idx.present? and last_modified == file_in_idx[:last_modified]
|
457
|
+
sha1 = file_in_idx[:sha1]
|
458
|
+
else
|
459
|
+
sha1 = OpenSSL::Digest::SHA1.file(e).hexdigest
|
460
|
+
end
|
461
|
+
|
462
|
+
if old_idx.nil? or old_idx.to_h[:tree].nil?
|
463
|
+
tree_idx[label] = {sha1: sha1, commit_time: nil, last_modified: last_modified}
|
464
|
+
elsif file_in_idx.nil? or file_in_idx[:sha1] != sha1 or file_in_idx[:last_modified].blank? or file_in_idx[:last_modified] != last_modified
|
465
|
+
tree_idx[label] = {sha1: sha1, commit_time: nil, last_modified: last_modified}
|
466
|
+
else
|
467
|
+
tree_idx[label] = old_idx[:tree][label]
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
old_idx[:tree] = tree_idx
|
472
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write old_idx.to_yaml}
|
473
|
+
return YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
474
|
+
end
|
475
|
+
|
476
|
+
def get_idx
|
477
|
+
unless File.exists? "#{self.local_path}/.cnvrg/idx.yml"
|
478
|
+
empty_idx = {:commit => nil, :tree => {}}
|
479
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write empty_idx.to_yaml}
|
480
|
+
return empty_idx
|
481
|
+
end
|
482
|
+
YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
483
|
+
end
|
484
|
+
|
485
|
+
def set_idx(idx)
|
486
|
+
FileUtils.mkdir_p("#{self.local_path}/.cnvrg")
|
487
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx.to_yaml}
|
488
|
+
end
|
489
|
+
|
490
|
+
def clone(remote = 0, commit)
|
491
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/clone", 'POST', {project_slug: self.slug, remote: remote, commit: commit})
|
492
|
+
return response
|
493
|
+
end
|
494
|
+
|
495
|
+
def git_download_commit(commit)
|
496
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/git_download_commit", 'POST', {commit_sha1: commit})
|
497
|
+
CLI.is_response_success(response, true)
|
498
|
+
return response
|
499
|
+
end
|
500
|
+
|
501
|
+
def get_job_last_commit(job_type, job_id)
|
502
|
+
base_url = "users/#{self.owner}/projects/#{self.slug}/jobs/#{job_type.underscore}/#{job_id}"
|
503
|
+
resp = Cnvrg::API.request("#{base_url}/last_commit", "GET")
|
504
|
+
commit = resp["commit"]
|
505
|
+
return commit
|
506
|
+
end
|
507
|
+
|
508
|
+
def compare_idx(new_branch, force:false, deploy: false, in_exp:false, specific_files: [], download: false)
|
509
|
+
is_download = download
|
510
|
+
if is_download
|
511
|
+
local_idx = self.get_idx
|
512
|
+
else
|
513
|
+
#upload
|
514
|
+
local_idx = self.generate_idx(deploy: deploy, files: specific_files)
|
515
|
+
end
|
516
|
+
commit = local_idx[:commit]
|
517
|
+
tree = local_idx[:tree]
|
518
|
+
ignore_list = self.send_ignore_list()
|
519
|
+
if force or specific_files.present?
|
520
|
+
added = []
|
521
|
+
if tree.present?
|
522
|
+
added += local_idx[:tree].keys
|
523
|
+
end
|
524
|
+
response = {"result" => {"commit" => nil, "tree" => {"added" => added,
|
525
|
+
"updated_on_server" => [],
|
526
|
+
"updated_on_local" => [],
|
527
|
+
"update_local" => [],
|
528
|
+
"deleted" => [],
|
529
|
+
"conflicts" => []}}}
|
530
|
+
return response
|
531
|
+
end
|
532
|
+
#we dont want to send it on download - we only compare between commits sha1 in download.
|
533
|
+
if is_download
|
534
|
+
#the new server doesnt need the tree, but the old probably needs :X
|
535
|
+
local_idx[:tree] = {} if Cnvrg::Helpers.server_version > 0
|
536
|
+
end
|
537
|
+
response = Cnvrg::API.request(@base_resource + "status", 'POST', {idx: local_idx, new_branch: new_branch,
|
538
|
+
current_commit: commit, ignore: ignore_list, force: force, in_exp: in_exp, download: download})
|
539
|
+
|
540
|
+
CLI.is_response_success(response, true)
|
541
|
+
if is_download
|
542
|
+
if Cnvrg::Helpers.server_version > 0
|
543
|
+
#trying to optimize the download using resolver
|
544
|
+
resolve = response['result']['tree']['resolver'] || tree #tree of file -> sha1 from current commit to check conflicts
|
545
|
+
destination_files = response['result']['tree']['destination'] || {} #tree of file -> sha1 from last commit to check files that already downloaded
|
546
|
+
else
|
547
|
+
resolve = tree
|
548
|
+
destination_files = {}
|
549
|
+
end
|
550
|
+
@files = self.get_files
|
551
|
+
local_tree = @files.calculate_sha1(resolve.keys)
|
552
|
+
changed_files = resolve.keys.select {|file| local_tree[file] != resolve[file]}
|
553
|
+
|
554
|
+
# means that the user changed the file locally
|
555
|
+
response['result']['tree']['update_local'] = changed_files
|
556
|
+
|
557
|
+
# means that we already downloaded this file and we dont need it anymore
|
558
|
+
downloaded_files = destination_files.keys.select {|file| local_tree[file] == destination_files[file]}
|
559
|
+
response['result']['tree']['added'] -= downloaded_files
|
560
|
+
response['result']['tree']['updated_on_server'] -= downloaded_files
|
561
|
+
end
|
562
|
+
Cnvrg::Logger.log_json(response, msg: "Comparing IDX response(tree)")
|
563
|
+
return response
|
564
|
+
end
|
565
|
+
|
566
|
+
def get_files
|
567
|
+
Cnvrg::Files.new(self.owner, self.slug, project_home: @local_path)
|
568
|
+
end
|
569
|
+
|
570
|
+
def jump_idx(destination: self.last_local_commit)
|
571
|
+
local_idx = self.generate_idx
|
572
|
+
ignore_list = self.send_ignore_list
|
573
|
+
current_commit = local_idx[:commit]
|
574
|
+
tree = local_idx[:tree]
|
575
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/jump_to",
|
576
|
+
'POST',
|
577
|
+
{tree: tree, ignore: ignore_list,
|
578
|
+
dest_commit: destination, current_commit: current_commit})
|
579
|
+
CLI.is_response_success(response, false)
|
580
|
+
response
|
581
|
+
end
|
582
|
+
|
583
|
+
def set_on_branch(is_latest)
|
584
|
+
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
585
|
+
@is_branch = !is_latest
|
586
|
+
config[:is_branch] = @is_branch
|
587
|
+
|
588
|
+
File.open(@working_dir + "/.cnvrg/config.yml", 'w') {|f| f.write config.to_yaml}
|
589
|
+
|
590
|
+
end
|
591
|
+
|
592
|
+
def compare_commit(commit)
|
593
|
+
if commit.nil? or commit.empty?
|
594
|
+
commit = last_local_commit
|
595
|
+
end
|
596
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/commit/compare", 'POST', {current_commit: commit})
|
597
|
+
CLI.is_response_success(response, false)
|
598
|
+
update_is_new_branch(response["result"]["new_branch"])
|
599
|
+
return response["result"]["new_branch"]
|
600
|
+
end
|
601
|
+
|
602
|
+
def update_idx_with_files_commits!(files, commit_time)
|
603
|
+
|
604
|
+
idx_hash = YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
605
|
+
files.each do |path|
|
606
|
+
idx_hash[:tree].to_h[path].to_h[:commit_time] = commit_time
|
607
|
+
end
|
608
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx_hash.to_yaml}
|
609
|
+
|
610
|
+
return true
|
611
|
+
end
|
612
|
+
|
613
|
+
def deploy(file_to_run, function, input_params, commit_to_run, instance_type, image_slug, scheduling_query, local_timestamp, workers, file_input, title)
|
614
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/deploy", 'POST', {file_to_run: file_to_run, function: function,
|
615
|
+
image_slug: image_slug, input_params: input_params,
|
616
|
+
commit_sha1: commit_to_run,
|
617
|
+
instance_type: instance_type,
|
618
|
+
scheduling_query: scheduling_query,
|
619
|
+
local_timestamp: local_timestamp,
|
620
|
+
workers: workers, file_input: file_input,
|
621
|
+
title: title})
|
622
|
+
return response
|
623
|
+
end
|
624
|
+
|
625
|
+
def list_commits
|
626
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/commits/list", 'GET')
|
627
|
+
CLI.is_response_success(response)
|
628
|
+
return response
|
629
|
+
end
|
630
|
+
|
631
|
+
|
632
|
+
def get_experiments
|
633
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/experiments/list", 'GET')
|
634
|
+
CLI.is_response_success(response)
|
635
|
+
return response
|
636
|
+
end
|
637
|
+
|
638
|
+
|
639
|
+
def get_experiment(slug)
|
640
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/experiments/#{slug}", 'GET')
|
641
|
+
response['status']=200
|
642
|
+
CLI.is_response_success(response)
|
643
|
+
return response
|
644
|
+
end
|
645
|
+
|
646
|
+
def fetch_webapp_slugs(webapp_slug, slugs: nil)
|
647
|
+
response = Cnvrg::API_V2.request("#{self.owner}/projects/#{self.slug}/webapps/#{webapp_slug}" , 'GET')
|
648
|
+
return response["experiments"]
|
649
|
+
rescue
|
650
|
+
slugs
|
651
|
+
end
|
652
|
+
|
653
|
+
def update_idx_with_commit!(commit, latest: nil)
|
654
|
+
idx_hash = self.get_idx
|
655
|
+
idx_hash[:commit] = commit
|
656
|
+
self.set_on_branch(latest) unless latest.nil?
|
657
|
+
|
658
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx_hash.to_yaml}
|
659
|
+
return true
|
660
|
+
end
|
661
|
+
|
662
|
+
def revert(working_dir)
|
663
|
+
FileUtils.rm_rf working_dir
|
664
|
+
end
|
665
|
+
|
666
|
+
def init_machines
|
667
|
+
Cnvrg::Logger.log_info("Init machines.")
|
668
|
+
resp = Cnvrg::API.request("users/#{@owner}/machines", "GET")
|
669
|
+
return unless Cnvrg::CLI.is_response_success(resp, false)
|
670
|
+
@machines = resp['result']['machines']
|
671
|
+
end
|
672
|
+
|
673
|
+
def get_machines
|
674
|
+
init_machines if @machines.nil?
|
675
|
+
@machines = false if @machines.nil?
|
676
|
+
@machines || []
|
677
|
+
end
|
678
|
+
|
679
|
+
|
680
|
+
def update_job_jupyter_token(job_type, job_id, token)
|
681
|
+
owner = self.owner || ENV['CNVRG_OWNER']
|
682
|
+
slug = self.slug || ENV['CNVRG_PROJECT']
|
683
|
+
base_url = "users/#{owner}/projects/#{slug}/jobs/#{job_type.underscore}/#{job_id}"
|
684
|
+
Cnvrg::API.request("#{base_url}/update_jupyter_token", "POST", {token: token})
|
685
|
+
end
|
686
|
+
|
687
|
+
def check_machine(machine)
|
688
|
+
Cnvrg::Logger.log_info("Check if #{machine} machine exists")
|
689
|
+
machines = get_machines
|
690
|
+
return true if machines.blank?
|
691
|
+
machines.include? machine
|
692
|
+
end
|
693
|
+
|
694
|
+
|
695
|
+
def fetch_project
|
696
|
+
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/get_project", "GET")
|
697
|
+
res = JSON.parse(resp['result']) rescue nil
|
698
|
+
return if res.blank?
|
699
|
+
config = self.get_config
|
700
|
+
config[:is_git] = res['git']
|
701
|
+
config[:project_name] = res['title']
|
702
|
+
self.set_config(config)
|
703
|
+
end
|
704
|
+
|
705
|
+
def job_log(logs, level: 'info', step: nil, job_type: nil, job_id: nil)
|
706
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
707
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
708
|
+
logs = [logs].flatten
|
709
|
+
if job_type.blank? or job_id.blank?
|
710
|
+
raise StandardError.new("Cant find job env variables")
|
711
|
+
end
|
712
|
+
logs.each_slice(10).each do |temp_logs|
|
713
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/log", "POST", {job_type: job_type, job_id: job_id, logs: temp_logs, log_level: level, step: step, timestamp: Time.now})
|
714
|
+
sleep(1)
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
|
719
|
+
def job_commands
|
720
|
+
job_type, job_id = ENV['CNVRG_JOB_TYPE'], ENV['CNVRG_JOB_ID']
|
721
|
+
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/commands", "GET")
|
722
|
+
end
|
723
|
+
|
724
|
+
def spot_will_terminate
|
725
|
+
restart = false
|
726
|
+
config = self.get_config
|
727
|
+
# logic that will prevent multiple restart calls
|
728
|
+
if config[:spot_taken]
|
729
|
+
return false
|
730
|
+
end
|
731
|
+
begin
|
732
|
+
url = URI.parse('http://169.254.169.254/latest/meta-data/spot/termination-time')
|
733
|
+
req = Net::HTTP::Get.new(url.to_s)
|
734
|
+
res = Net::HTTP.start(url.host, url.port) {|http|
|
735
|
+
http.request(req)
|
736
|
+
}
|
737
|
+
unless res.body.include? "404"
|
738
|
+
restart = true
|
739
|
+
end
|
740
|
+
if res.body.include? "Empty reply from server"
|
741
|
+
restart = false
|
742
|
+
end
|
743
|
+
rescue
|
744
|
+
restart = false
|
745
|
+
end
|
746
|
+
|
747
|
+
if restart
|
748
|
+
config[:spot_taken] = true
|
749
|
+
self.set_config(config)
|
750
|
+
end
|
751
|
+
|
752
|
+
return restart
|
753
|
+
end
|
754
|
+
|
755
|
+
def send_restart_request(job_id: nil, job_type: nil, ma_id: nil)
|
756
|
+
Cnvrg::API.request("#{base_resource}/spot_restart", 'POST', {job_type: job_type, job_id: job_id, machine_activity: ma_id})
|
757
|
+
end
|
758
|
+
|
759
|
+
def get_machine_activity
|
760
|
+
begin
|
761
|
+
machine_activity = File.open("#{@local_path}/.cnvrg/machine_activity", "rb").read
|
762
|
+
machine_activity = machine_activity.to_s.strip
|
763
|
+
ma_id = machine_activity.to_i
|
764
|
+
return ma_id
|
765
|
+
rescue
|
766
|
+
return nil
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
def set_job_pod_restart(job_type: nil, job_id: nil)
|
771
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
772
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
773
|
+
if job_type.blank? or job_id.blank?
|
774
|
+
raise StandardError.new("Cant find job env variables")
|
775
|
+
end
|
776
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_pod_restart", "POST", {job_type: job_type, job_id: job_id})
|
777
|
+
end
|
778
|
+
|
779
|
+
def check_job_pod_restart(job_type: nil, job_id: nil)
|
780
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
781
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
782
|
+
if job_type.blank? or job_id.blank?
|
783
|
+
raise StandardError.new("Cant find job env variables")
|
784
|
+
end
|
785
|
+
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/check_pod_restart", "GET", {job_type: job_type, job_id: job_id})
|
786
|
+
return [false, false] if resp.blank?
|
787
|
+
Cnvrg::Logger.log_info("Checked for pod restart got response #{resp}")
|
788
|
+
[resp['project_downloaded'], resp['dataset_downloaded']]
|
789
|
+
end
|
790
|
+
|
791
|
+
def pre_job_pod_restart
|
792
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
793
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
794
|
+
if job_type.blank? or job_id.blank?
|
795
|
+
raise StandardError.new("Cant find job env variables")
|
796
|
+
end
|
797
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/pre_pod_restart", "POST", {job_type: job_type, job_id: job_id})
|
798
|
+
end
|
799
|
+
|
800
|
+
def set_job_started
|
801
|
+
job_type ||= ENV['CNVRG_JOB_TYPE']
|
802
|
+
job_id ||= ENV['CNVRG_JOB_ID']
|
803
|
+
if job_type.blank? or job_id.blank?
|
804
|
+
raise StandardError.new("Cant find job env variables")
|
805
|
+
end
|
806
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_started", "POST", {job_type: job_type, job_id: job_id})
|
807
|
+
end
|
808
|
+
|
809
|
+
def self.stop_if_project_present(project_home, project_name)
|
810
|
+
cli = Cnvrg::CLI.new()
|
811
|
+
config = YAML.load_file(project_home + "/.cnvrg/config.yml")
|
812
|
+
local_commit = YAML.load_file(project_home + "/.cnvrg/idx.yml")[:commit] rescue nil
|
813
|
+
return if local_commit.blank?
|
814
|
+
if config[:project_name] == project_name
|
815
|
+
cli.log_message("Project already present, clone aborted")
|
816
|
+
exit(0)
|
817
|
+
end
|
818
|
+
rescue => e
|
819
|
+
nil
|
820
|
+
end
|
821
|
+
end
|
822
|
+
end
|