singularity-cli 0.2.1 → 0.3.0

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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/bin/singularity +14 -2
  4. data/lib/singularity.rb +154 -9
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f1b969a0fecc12a57a4d7526bd79b88f074755c
4
- data.tar.gz: dd4d88b3fd58799b2baaa6f34f5b85b8d3f32e63
3
+ metadata.gz: 4f917e15b139df05230f76923b7c1296caa65d62
4
+ data.tar.gz: 230d2d431af444026ae30b1ece3e4fcdd65edca8
5
5
  SHA512:
6
- metadata.gz: e3ae653b12db18fc8334de1cbfe83ccc543e122269681ec777a9e4681380da7710de35649efe9774a7381bcba0159e0eeb2e7347dc86e3fc0d699a942b4c65ac
7
- data.tar.gz: 7334989a24708803cf21db7d5e1a8b4d63419a93a02a327bf7c2c0171ddadfa5ab4c47d7ef6ded6e1de5c920dfcd4bfcb443e15f7f8bd161d5f70164342af4d7
6
+ metadata.gz: f294ca9b8811d3a3092475baa0f302ffdb10c14a40866d556bb2d96e57e8421ed111b7c41ba2b5320693121e09e24783a3afd734294a805dba2d31ddcec86b59
7
+ data.tar.gz: 79759aa9d74a1692304e4c5e738d69f0d13e26904dbc01b9665a74729d5b584786594182b1ac615e8657b5d455232c18742adb84954865ab12b8cf4f5afb1cea
data/README.md CHANGED
@@ -5,3 +5,4 @@ Authors:
5
5
  # Usage:
6
6
  singularity delete <uri> <file>
7
7
  singularity deploy <uri> <file> <release>
8
+ singularity run <uri> <file/script> <release>
data/bin/singularity CHANGED
@@ -5,7 +5,7 @@ require 'singularity'
5
5
  ARGV << '--help' if ARGV.empty?
6
6
 
7
7
  def print_usage
8
- puts "Usage:\n\tsingularity delete <uri> <file>\n\tsingularity deploy <uri> <file> <release>"
8
+ puts "Usage:\n\tsingularity delete <uri> <file.json>\n\tsingularity deploy <uri> <file.json> <release>\n\tsingularity run <uri> <file.json> <release> <script>"
9
9
  exit
10
10
  end
11
11
 
@@ -14,7 +14,6 @@ uri = ARGV[1]
14
14
  file = ARGV[2]
15
15
 
16
16
  case action
17
-
18
17
  when "delete"
19
18
  print_usage unless ARGV.size == 3
20
19
  Singularity::Deleter.new(uri,file).delete
@@ -24,6 +23,19 @@ case action
24
23
  release = ARGV[3]
25
24
  Singularity::Deployer.new(uri,file,release).deploy
26
25
 
26
+ when "run"
27
+ # remove the word "run" from ARGV, pass the remainder to Runner
28
+ ARGV.shift
29
+ Singularity::Runner.new(ARGV).runner
30
+
31
+ when "runx"
32
+ # this option is to skip the use of /sbin/my_init
33
+ # (some commands won't run correctly when both are used)
34
+ Singularity::Runner.new(ARGV).runner
35
+
36
+ when "ssh"
37
+ Singularity::Runner.new("ssh").runner
38
+
27
39
  else
28
40
  print_usage
29
41
  end
data/lib/singularity.rb CHANGED
@@ -2,18 +2,17 @@ require 'erb'
2
2
  require 'json'
3
3
  require 'rest-client'
4
4
  require 'colorize'
5
+ require 'yaml'
5
6
 
6
7
  module Singularity
7
8
  class Request
8
9
  attr_accessor :release, :cpus, :mem, :envs, :schedule, :cmd, :arguments, :request_id, :repo, :release_string, :release_id_string
9
-
10
10
  def get_binding
11
11
  binding()
12
12
  end
13
13
  end
14
14
 
15
15
  class Deployer
16
-
17
16
  def initialize(uri, file, release)
18
17
  @uri = uri
19
18
  @file = file
@@ -25,7 +24,6 @@ module Singularity
25
24
  print @data['id']
26
25
  end
27
26
 
28
-
29
27
  def is_paused
30
28
  begin
31
29
  resp = RestClient.get "#{@uri}/api/requests/request/#{@data['id']}"
@@ -45,7 +43,6 @@ module Singularity
45
43
  # create or update the request
46
44
  resp = RestClient.post "#{@uri}/api/requests", @data.to_json, :content_type => :json
47
45
  end
48
-
49
46
  # deploy the request
50
47
  @data['requestId'] = @data['id']
51
48
  @data['id'] = "#{@release}.#{Time.now.to_i}"
@@ -54,9 +51,7 @@ module Singularity
54
51
  'user' => `whoami`.chomp,
55
52
  'unpauseOnSuccessfulDeploy' => false
56
53
  }
57
-
58
54
  resp = RestClient.post "#{@uri}/api/deploys", deploy.to_json, :content_type => :json
59
-
60
55
  puts " DEPLOYED".green
61
56
  rescue Exception => e
62
57
  puts " #{e.response}".red
@@ -65,12 +60,10 @@ module Singularity
65
60
  end
66
61
 
67
62
  class Deleter
68
-
69
63
  def initialize(uri, file)
70
64
  @uri = uri
71
65
  @file = file
72
66
  end
73
-
74
67
  # Deleter.delete -- arguments are <uri>, <file>
75
68
  def delete
76
69
  begin
@@ -82,6 +75,158 @@ module Singularity
82
75
  puts "#{task_id} #{$!.response}"
83
76
  end
84
77
  end
85
- end
78
+ end
79
+
80
+ class Runner
81
+ def initialize(script)
82
+ #########################################################
83
+ # TODO
84
+ # check to see that .mescal.json and mesos-deploy.yml exist
85
+ #########################################################
86
+ @script = script
87
+ # read .mescal.json for ssh command, image, release number, cpus, mem
88
+ @configData = JSON.parse(ERB.new(open(File.join(Dir.pwd, ".mescal.json")).read).result(Request.new.get_binding))
89
+ @sshCmd = @configData['sshCmd']
90
+ @image = @configData['image'].split(':')[0]
91
+ @release = @configData['image'].split(':')[1]
92
+
93
+ # read mesos-deploy.yml for singularity url
94
+ @mesosDeployConfig = YAML.load_file(File.join(Dir.pwd, "mesos-deploy.yml"))
95
+ @uri = @mesosDeployConfig['singularity_url']
96
+
97
+ # create request/deploy json data
98
+ @data = {
99
+ 'command' => "/sbin/my_init",
100
+ 'resources' => {
101
+ 'memoryMb' => @configData['mem'],
102
+ 'cpus' => @configData['cpus'],
103
+ 'numPorts' => 1
104
+ },
105
+ 'env' => {
106
+ 'APPLICATION_ENV' => "production"
107
+ },
108
+ 'requestType' => "RUN_ONCE",
109
+ 'containerInfo' => {
110
+ 'type' => "DOCKER",
111
+ 'docker' => {
112
+ 'image' => @configData['image'],
113
+ 'network' => "BRIDGE",
114
+ 'portMappings' => [{
115
+ 'containerPortType': "LITERAL",
116
+ 'containerPort': 22,
117
+ 'hostPortType': "FROM_OFFER",
118
+ 'hostPort': 0
119
+ }]
120
+ }
121
+ }
122
+ }
123
+ # either we typed 'singularity ssh'
124
+ if @script == "ssh"
125
+ @data['id'] = Dir.pwd.split('/').last + "_SSH"
126
+ @data['command'] = "#{@sshCmd}"
127
+ # or we passed a script/commands to 'singularity run'
128
+ else
129
+ # if we passed "runx", then skip use of /sbin/my_init
130
+ if @script[0] == "runx"
131
+ @data['arguments'] = [] # don't use "--" as first argument
132
+ @data['command'] = @script[1] #remove "runx" from commands
133
+ @script.shift
134
+ @data['id'] = @script.join("-").tr('@/\*?% []#$', '_')
135
+ @data['id'][0] = ''
136
+ @script.shift
137
+ # else join /sbin/my_init with your commands
138
+ else
139
+ @data['arguments'] = ["--"]
140
+ @data['id'] = @script.join("-").tr('@/\*?% []#$', '_')
141
+ @data['id'][0] = ''
142
+ end
143
+ @script.each { |i| @data['arguments'].push i }
144
+ end
145
+ end
146
+
147
+ def is_paused
148
+ begin
149
+ resp = RestClient.get "#{@uri}/api/requests/request/#{@data['id']}"
150
+ JSON.parse(resp)['state'] == 'PAUSED'
151
+ rescue
152
+ print " Deploying request...".light_green
153
+ false
154
+ end
155
+ end
156
+
157
+ def runner
158
+ begin
159
+ if is_paused()
160
+ puts " PAUSED, SKIPPING.".yellow
161
+ return
162
+ else
163
+ # create or update the request
164
+ RestClient.post "#{@uri}/api/requests", @data.to_json, :content_type => :json
165
+ end
166
+
167
+ # deploy the request
168
+ @data['requestId'] = @data['id']
169
+ @data['id'] = "#{@release}.#{Time.now.to_i}"
170
+ @deploy = {
171
+ 'deploy' => @data,
172
+ 'user' => `whoami`.chomp,
173
+ 'unpauseOnSuccessfulDeploy' => false
174
+ }
175
+ RestClient.post "#{@uri}/api/deploys", @deploy.to_json, :content_type => :json
176
+ puts " Deploy succeeded.".green
177
+
178
+ # get active tasks until ours shows up so we can get IP/PORT
179
+ begin
180
+ @thisTask = ''
181
+ @tasks = RestClient.get "#{@uri}/api/tasks/active", :content_type => :json
182
+ @tasks = JSON.parse(@tasks)
183
+ @tasks.each do |entry|
184
+ if entry['taskRequest']['request']['id'] == @data['requestId']
185
+ @thisTask = entry
186
+ end
187
+ end
188
+ end until @thisTask != ''
189
+ @ip = @thisTask['offer']['url']['address']['ip']
190
+ @port = @thisTask['mesosTask']['container']['docker']['portMappings'][0]['hostPort']
191
+
192
+ # SSH into the machine
193
+ if @script == "ssh"
194
+ # uses "begin end until" because "system" will keep returning "false" unless the command exits with success
195
+ # this makes sure that the docker image has completely started and the SSH command succeeds
196
+ where = Dir.pwd.split('/').last
197
+ puts " Opening a shell to #{where}, please wait a moment...".light_blue
198
+ puts " STDOUT / STDERR will print to console when session is over.".light_blue
199
+ begin end until system "ssh -o LogLevel=quiet -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{@ip} -p #{@port}"
200
+ else
201
+ puts " Deployed and running #{@data['command']} #{@data['arguments']}".light_green
202
+ end
203
+
204
+ # need to wait for "task_finished" or "task_running & ssh" before we can ask for STDOUT/STDERR
205
+ begin
206
+ @taskState = RestClient.get "#{@uri}/api/history/task/#{@thisTask['taskId']['id']}"
207
+ @taskState = JSON.parse(@taskState)
208
+ @taskState["taskUpdates"].each do |update|
209
+ @taskState = update['taskState']
210
+ end
211
+ end until @taskState == "TASK_FINISHED" || (@taskState == "TASK_RUNNING" and @script == "ssh")
212
+
213
+ # output STDOUT / STDERR to shell
214
+ puts ""
215
+ stdout = RestClient.get "#{@uri}/api/sandbox/#{@thisTask['taskId']['id']}/read", {params: {path: "stdout", length: 30000, offset: 0}}
216
+ stdout = JSON.parse(stdout)
217
+ puts "stdout: ".cyan
218
+ puts stdout['data'].light_cyan
219
+ stderr = RestClient.get "#{@uri}/api/sandbox/#{@thisTask['taskId']['id']}/read", {params: {path: "stderr", length: 30000, offset: 0}}
220
+ stderr = JSON.parse(stderr)
221
+ puts "stderr: ".red
222
+ puts stderr['data'].light_magenta
86
223
 
224
+ # finally, delete the request (which also deletes the corresponding task)
225
+ RestClient.delete "#{@uri}/api/requests/request/#{@data['requestId']}"
226
+
227
+ rescue Exception => e
228
+ puts " #{e.response}".red
229
+ end
230
+ end
231
+ end
87
232
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: singularity-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Webb
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-26 00:00:00.000000000 Z
12
+ date: 2016-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client