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.
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,29 @@
1
+ require 'fileutils'
2
+ module Cnvrg
3
+ class Result
4
+ attr_reader :msg, :msg_color, :e_msg, :e_msg_backtrace
5
+
6
+ def initialize(success, msg, e_msg = "", e_backtrace = "")
7
+ begin
8
+ @success = success
9
+ @msg = msg
10
+ @e_msg = e_msg
11
+ @e_msg_backtrace = e_backtrace
12
+ if !@success
13
+ @msg_color = Thor::Shell::Color::RED
14
+ else
15
+ @msg_color = Thor::Shell::Color::GREEN
16
+
17
+ end
18
+ rescue => e
19
+ end
20
+
21
+ end
22
+ def is_success?
23
+ return @success
24
+ end
25
+
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,49 @@
1
+ require 'cnvrg'
2
+
3
+ module Cnvrg
4
+ class Runner
5
+ # Allow everything fun to be injected from the outside while defaulting to normal implementations.
6
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
7
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
8
+ end
9
+
10
+ def execute!
11
+ exit_code = begin
12
+ # Thor accesses these streams directly rather than letting them be injected, so we replace them...
13
+ $stderr = @stderr
14
+ $stdin = @stdin
15
+ $stdout = @stdout
16
+
17
+ # Run our normal Thor app the way we know and love.
18
+ Cnvrg::CLI.start(@argv)
19
+
20
+ # Thor::Base#start does not have a return value, assume success if no exception is raised.
21
+ 0
22
+ rescue StandardError => e
23
+ # The ruby interpreter would pipe this to STDERR and exit 1 in the case of an unhandled exception
24
+ b = e.backtrace
25
+ @stderr.puts("#{b.shift}: #{e.message} (#{e.class})")
26
+ @stderr.puts(b.map{|s| "\tfrom #{s}"}.join("\n"))
27
+ 1
28
+ rescue SystemExit => e
29
+ e.status
30
+ ensure
31
+ # TODO: reset your app here, free up resources, etc.
32
+ # Examples:
33
+ # MyApp.logger.flush
34
+ # MyApp.logger.close
35
+ # MyApp.logger = nil
36
+ #
37
+ # MyApp.reset_singleton_instance_variables
38
+
39
+ # ...then we put the streams back.
40
+ $stderr = STDERR
41
+ $stdin = STDIN
42
+ $stdout = STDOUT
43
+ end
44
+
45
+ # Proxy our exit code back to the injected kernel.
46
+ @kernel.exit(exit_code)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,94 @@
1
+ require 'fileutils'
2
+ require 'cnvrg/files'
3
+ require 'net/ssh'
4
+
5
+
6
+ module Cnvrg
7
+ class Ssh
8
+ attr_reader :is_ssh
9
+
10
+
11
+ def initialize(resp)
12
+ begin
13
+ @is_ssh = false
14
+ sts_path = resp["result"]["sts_path"]
15
+
16
+ uri = URI.parse(sts_path)
17
+
18
+ http_object = Net::HTTP.new(uri.host, uri.port)
19
+ http_object.use_ssl = true if uri.scheme == 'https'
20
+ request = Net::HTTP::Get.new(sts_path)
21
+
22
+ body = ""
23
+ http_object.start do |http|
24
+ response = http.request request
25
+ body = response.read_body
26
+ end
27
+
28
+ URLcrypt::key = [body].pack('H*')
29
+ ip = URLcrypt.decrypt(resp["result"]["machine_i"])
30
+
31
+ @user = URLcrypt.decrypt(resp["result"]["machine_u"])
32
+ key = URLcrypt.decrypt(resp["result"]["machine_k"])
33
+ @container = URLcrypt.decrypt(resp["result"]["machine_c"])
34
+
35
+ tempssh = Tempfile.new "sshkey"
36
+ tempssh.write open(key).read
37
+ tempssh.rewind
38
+ key_path = tempssh.path
39
+ count = 0
40
+ while count < 5
41
+
42
+ begin
43
+ @ssh = Net::SSH.start(ip, user=@user, :keys => key_path, :timeout => 10)
44
+ if !@ssh.nil?
45
+ @is_ssh = true
46
+ return
47
+ else
48
+ count+=1
49
+ sleep(2)
50
+
51
+ end
52
+ rescue
53
+ count+=1
54
+ sleep(2)
55
+
56
+
57
+ end
58
+ end
59
+ if tempssh
60
+ tempssh.close
61
+ tempssh.unlink
62
+ end
63
+ return false
64
+ rescue => e
65
+
66
+ puts e
67
+ end
68
+ end
69
+
70
+
71
+
72
+ def exec_command(command)
73
+ exec_command = "sudo -i -u #{@user} cnvrg exec_container #{@container} \"#{command}\" "
74
+ return @ssh.exec!(exec_command)
75
+ end
76
+
77
+
78
+ def close_ssh()
79
+
80
+
81
+ begin
82
+
83
+ @ssh.close
84
+ rescue => e
85
+
86
+ end
87
+
88
+
89
+ end
90
+ end
91
+
92
+
93
+
94
+ end
@@ -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
@@ -0,0 +1,165 @@
1
+ module Cnvrg
2
+ class Task
3
+ attr_accessor :title, :path, :source
4
+ def initialize(project_path, path: nil, content: {})
5
+ @path = path
6
+ @content = content
7
+ @source = 'flow' if @content.present?
8
+ @source = 'file' if @path.present?
9
+ @project = Cnvrg::Project.new(project_path)
10
+ @project_path = project_path
11
+ #generic
12
+ @type = nil #type can be exec, data, library, deploy
13
+ @compute = 'medium'
14
+ @title = nil
15
+ @uid = nil
16
+ @params = {}
17
+
18
+ #exec
19
+ @cmd = nil
20
+ @params_path = nil
21
+
22
+ #library
23
+ @library = nil
24
+
25
+ #dataset
26
+ @dataset = nil
27
+ @query = nil
28
+
29
+ #deploy
30
+ @function = nil
31
+
32
+
33
+ @base_resource = @project.base_resource + "/tasks"
34
+
35
+ self.reload_task
36
+ end
37
+
38
+ def save
39
+ path = @path || gen_path
40
+ File.open(path, 'w'){|f| f.write(get_content.to_yaml)}
41
+ end
42
+
43
+ def reload_task
44
+ task_raw = get_content
45
+ @title = task_raw[:title]
46
+ @type = task_raw[:type]
47
+ @uid = task_raw[:uid]
48
+ @title = task_raw[:title] || @uid
49
+ @compute = task_raw[:compute] || @compute
50
+
51
+ case @type
52
+ when 'exec'
53
+ @cmd = task_raw[:cmd]
54
+ init_params(task_raw)
55
+ when 'library'
56
+ @library = task_raw[:library]
57
+ init_params(task_raw)
58
+ when 'data'
59
+ @dataset = task_raw[:dataset]
60
+ @query = task_raw[:query]
61
+ when 'deploy'
62
+ @cmd = task_raw[:cmd]
63
+ @function = task_raw[:function]
64
+ else
65
+ error("Cant parse task of type #{@type}")
66
+ end
67
+ end
68
+
69
+ def verify_task
70
+ case @type
71
+ when 'exec'
72
+ verify_exec
73
+ when 'data'
74
+ verify_data
75
+ when 'deploy'
76
+ verify_deploy
77
+ when 'library'
78
+ verify_library
79
+ else
80
+ error("Cant parse task of type #{@type}")
81
+ end
82
+ end
83
+
84
+
85
+ def to_api
86
+ get_content.merge(params: @params)
87
+ end
88
+
89
+ def run
90
+ verify_task
91
+ if @type == 'data'
92
+ raise StandardError.new("Data Tasks are not runnable")
93
+ end
94
+ resp = Cnvrg::API.request(@base_resource, "POST", {task: to_api})
95
+ Cnvrg::CLI.is_response_success(resp, true)
96
+ Cnvrg::Helpers.remote_url + resp['result']['url']
97
+ end
98
+
99
+ private
100
+ def verify_compute
101
+ unless @project.check_machine(@compute)
102
+ raise StandardError.new("Cant find #{@compute} machine in project.")
103
+ end
104
+ end
105
+
106
+ def init_params(task_raw)
107
+ @params_path = task_raw[:params_path].presence
108
+ @params = task_raw[:params] || @params
109
+ if @params_path.present?
110
+ @hyper = Cnvrg::Hyper.new(@project_path, @params_path)
111
+ @params = @hyper.resolve_params
112
+ end
113
+ end
114
+
115
+ def verify_exec
116
+ if @cmd.blank?
117
+ error("Cant find command")
118
+ end
119
+ verify_compute
120
+ end
121
+
122
+ def verify_data
123
+ if @dataset.blank?
124
+ error("Cant find dataset slug")
125
+ end
126
+ end
127
+
128
+ def verify_deploy
129
+ error("Cant find command") if @cmd.blank?
130
+ error("Cant find function") if @function.blank?
131
+ verify_compute
132
+ end
133
+
134
+ def verify_library
135
+ error("Cant find library") if @library.blank?
136
+ end
137
+
138
+ def get_content
139
+ return @content if @source == 'flow'
140
+ unless File.exists? @path
141
+ raise StandardError.new("Cant find task in #{@path}")
142
+ end
143
+ YAML.load_file(@path)
144
+ end
145
+
146
+ def gen_path
147
+ @title ||= "#{@type.capitalize}Task"
148
+ unless File.exists? @title
149
+ @path = "#{@title}.task.yaml"
150
+ return @path
151
+ end
152
+ i = 0
153
+ while File.exists? "#{@title}_#{i}.task.yaml"
154
+ i += 1
155
+ end
156
+ @path = "#{@title}.task.yaml"
157
+ return @path
158
+ end
159
+
160
+ def error(msg)
161
+ raise StandardError.new("task: #{@uid} - #{msg}")
162
+ end
163
+
164
+ end
165
+ end