cnvrg 1.9.9.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/bin/cnvrg +9 -0
  3. data/cnvrg.gemspec +47 -0
  4. data/lib/cnvrg.rb +7 -0
  5. data/lib/cnvrg/Images.rb +351 -0
  6. data/lib/cnvrg/api.rb +247 -0
  7. data/lib/cnvrg/api_v2.rb +14 -0
  8. data/lib/cnvrg/auth.rb +79 -0
  9. data/lib/cnvrg/cli.rb +5715 -0
  10. data/lib/cnvrg/cli/flow.rb +166 -0
  11. data/lib/cnvrg/cli/library_cli.rb +33 -0
  12. data/lib/cnvrg/cli/subcommand.rb +28 -0
  13. data/lib/cnvrg/cli/task.rb +116 -0
  14. data/lib/cnvrg/colors.rb +8 -0
  15. data/lib/cnvrg/connect_job_ssh.rb +31 -0
  16. data/lib/cnvrg/data.rb +335 -0
  17. data/lib/cnvrg/datafiles.rb +1325 -0
  18. data/lib/cnvrg/dataset.rb +892 -0
  19. data/lib/cnvrg/downloader/client.rb +101 -0
  20. data/lib/cnvrg/downloader/clients/azure_client.rb +45 -0
  21. data/lib/cnvrg/downloader/clients/gcp_client.rb +50 -0
  22. data/lib/cnvrg/downloader/clients/s3_client.rb +78 -0
  23. data/lib/cnvrg/experiment.rb +209 -0
  24. data/lib/cnvrg/files.rb +1047 -0
  25. data/lib/cnvrg/flow.rb +137 -0
  26. data/lib/cnvrg/helpers.rb +422 -0
  27. data/lib/cnvrg/helpers/agent.rb +188 -0
  28. data/lib/cnvrg/helpers/executer.rb +213 -0
  29. data/lib/cnvrg/hyper.rb +21 -0
  30. data/lib/cnvrg/image.rb +113 -0
  31. data/lib/cnvrg/image_cli.rb +25 -0
  32. data/lib/cnvrg/job_cli.rb +73 -0
  33. data/lib/cnvrg/job_ssh.rb +48 -0
  34. data/lib/cnvrg/logger.rb +111 -0
  35. data/lib/cnvrg/org_helpers.rb +5 -0
  36. data/lib/cnvrg/project.rb +822 -0
  37. data/lib/cnvrg/result.rb +29 -0
  38. data/lib/cnvrg/runner.rb +49 -0
  39. data/lib/cnvrg/ssh.rb +94 -0
  40. data/lib/cnvrg/storage.rb +128 -0
  41. data/lib/cnvrg/task.rb +165 -0
  42. data/lib/cnvrg/version.rb +3 -0
  43. 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
@@ -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,5 @@
1
+ module Cnvrg
2
+ class OrgHelpers
3
+
4
+ end
5
+ 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