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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/bin/singularity +14 -2
- data/lib/singularity.rb +154 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f917e15b139df05230f76923b7c1296caa65d62
|
4
|
+
data.tar.gz: 230d2d431af444026ae30b1ece3e4fcdd65edca8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f294ca9b8811d3a3092475baa0f302ffdb10c14a40866d556bb2d96e57e8421ed111b7c41ba2b5320693121e09e24783a3afd734294a805dba2d31ddcec86b59
|
7
|
+
data.tar.gz: 79759aa9d74a1692304e4c5e738d69f0d13e26904dbc01b9665a74729d5b584786594182b1ac615e8657b5d455232c18742adb84954865ab12b8cf4f5afb1cea
|
data/README.md
CHANGED
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.
|
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-
|
12
|
+
date: 2016-10-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|