singularity-cli 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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