mobilize-ssh 1.29 → 1.31
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.
- data/README.md +75 -8
- data/lib/mobilize-ssh/handlers/git.rb +120 -0
- data/lib/mobilize-ssh/handlers/ssh.rb +32 -69
- data/lib/mobilize-ssh/helpers/ssh_helper.rb +37 -0
- data/lib/mobilize-ssh/version.rb +1 -1
- data/lib/mobilize-ssh.rb +1 -1
- data/lib/samples/git.yml +34 -0
- data/lib/samples/ssh.yml +0 -3
- data/mobilize-ssh.gemspec +1 -1
- data/test/code2.sh +1 -1
- data/test/mobilize-ssh_test.rb +5 -2
- data/test/ssh_job_rows.yml +13 -6
- metadata +34 -16
- checksums.yaml +0 -7
- data/lib/mobilize-ssh/extensions/string.rb +0 -5
data/README.md
CHANGED
@@ -13,6 +13,7 @@ Table Of Contents
|
|
13
13
|
* [Install Dirs and Files](#section_Install_Dirs_and_Files)
|
14
14
|
* [Configure](#section_Configure)
|
15
15
|
* [Ssh](#section_Configure_Ssh)
|
16
|
+
* [Git](#section_Configure_Git)
|
16
17
|
* [Start](#section_Start)
|
17
18
|
* [Create Job](#section_Start_Create_Job)
|
18
19
|
* [Run Test](#section_Start_Run_Test)
|
@@ -79,9 +80,6 @@ Configure
|
|
79
80
|
### Configure Ssh
|
80
81
|
|
81
82
|
The Ssh configuration consists of:
|
82
|
-
* tmp_file_dir, which is where files will be stored before being scp'd
|
83
|
-
over to the nodes. They will be deleted afterwards, unless the job
|
84
|
-
fails in mid-copy. By default this is tmp/file/.
|
85
83
|
* nodes, identified by aliases, such as `test_node`. This alias is what you should
|
86
84
|
pass into the "node" param over in the ssh.run task.
|
87
85
|
* if no node is specified, commands will default to the first node listed.
|
@@ -109,7 +107,6 @@ Sample ssh.yml:
|
|
109
107
|
``` yml
|
110
108
|
---
|
111
109
|
development:
|
112
|
-
tmp_file_dir: tmp/file/
|
113
110
|
nodes:
|
114
111
|
dev_node:
|
115
112
|
sudoers:
|
@@ -126,7 +123,6 @@ development:
|
|
126
123
|
port: 22
|
127
124
|
user: gateway_user
|
128
125
|
test:
|
129
|
-
tmp_file_dir: tmp/file/
|
130
126
|
nodes:
|
131
127
|
test_node:
|
132
128
|
sudoers:
|
@@ -143,7 +139,6 @@ test:
|
|
143
139
|
port: 22
|
144
140
|
user: gateway_user
|
145
141
|
production:
|
146
|
-
tmp_file_dir: tmp/file/
|
147
142
|
nodes:
|
148
143
|
prod_node:
|
149
144
|
sudoers:
|
@@ -161,6 +156,67 @@ production:
|
|
161
156
|
user: gateway_user
|
162
157
|
```
|
163
158
|
|
159
|
+
<a name='section_Configure_Git'></a>
|
160
|
+
### Configure Git
|
161
|
+
|
162
|
+
Git configuration is not required but recommended, as it allows you to
|
163
|
+
pull files directly from public or private Git repositories.
|
164
|
+
|
165
|
+
The Git configuration consists of:
|
166
|
+
* domains, identified by aliases, such as `private` and `public`.
|
167
|
+
* domains are passed into the source parameters in the git.run task.
|
168
|
+
* if no domain is specified, commands will default to the first domain listed.
|
169
|
+
|
170
|
+
Each domain has:
|
171
|
+
* a host;
|
172
|
+
* a user, which is the user used for the git clone command.
|
173
|
+
* a set of repo keys (optional) which correspond to "deploy keys" on
|
174
|
+
github. Each repo must have its own ssh key, and the public key must be
|
175
|
+
stored in the repo.
|
176
|
+
* if your repo doesn't need an ssh key to work, this is not necessary
|
177
|
+
to add.
|
178
|
+
|
179
|
+
|
180
|
+
Sample git.yml:
|
181
|
+
|
182
|
+
``` yml
|
183
|
+
---
|
184
|
+
development:
|
185
|
+
domains:
|
186
|
+
private:
|
187
|
+
host: github.private.com
|
188
|
+
user: git
|
189
|
+
repo_keys:
|
190
|
+
"repo_path_1": 'local/path/to/key1'
|
191
|
+
"repo_path_2": 'local/path/to/key1'
|
192
|
+
public:
|
193
|
+
host: github.com
|
194
|
+
user: git
|
195
|
+
test:
|
196
|
+
domains:
|
197
|
+
public:
|
198
|
+
host: github.com
|
199
|
+
user: git
|
200
|
+
private:
|
201
|
+
host: github.private.com
|
202
|
+
user: git
|
203
|
+
repo_keys:
|
204
|
+
"repo_path_1": 'local/path/to/key1'
|
205
|
+
"repo_path_2": 'local/path/to/key1'
|
206
|
+
production:
|
207
|
+
domains:
|
208
|
+
private:
|
209
|
+
host: github.private.com
|
210
|
+
user: git
|
211
|
+
repo_keys:
|
212
|
+
"repo_path_1": 'local/path/to/key1'
|
213
|
+
"repo_path_2": 'local/path/to/key1'
|
214
|
+
public:
|
215
|
+
host: github.com
|
216
|
+
user: git
|
217
|
+
```
|
218
|
+
|
219
|
+
|
164
220
|
<a name='section_Start'></a>
|
165
221
|
Start
|
166
222
|
-----
|
@@ -169,10 +225,21 @@ Start
|
|
169
225
|
### Create Job
|
170
226
|
|
171
227
|
* For mobilize-ssh, the following task is available:
|
172
|
-
* ssh.run `node: <node_alias>, cmd: <command>, user: user, sources:[*<source_paths>]`, which reads sources, copies them to a temporary folder on the selected node, and runs the command inside that folder.
|
173
|
-
* user, sources, and
|
228
|
+
* ssh.run `node: <node_alias>, cmd: <command>, user: user, sources:[*<source_paths>], params:[{<key,value pairs>}]`, which reads sources, copies them to a temporary folder on the selected node, and runs the command inside that folder.
|
229
|
+
* user, sources, node, and params are optional; cmd is required.
|
174
230
|
* specifying user will cause the command to be prefixed with sudo su <user> -c.
|
175
231
|
* non-google sources will also be read as the specified user.
|
232
|
+
* git sources can be specified with syntax `git://<domain>/<repo_owner>/<repo_name>/<file_path>`.
|
233
|
+
* Accessing private repos requires that you add the Mobilize public key to the repository as a deploy key.
|
234
|
+
* there is no user-level access control for git repositories at this time.
|
235
|
+
* domain defaults to the first one listed, if not included.
|
236
|
+
* params are also optional for all of the below. They replace tokens in sources and the command.
|
237
|
+
* params are passed as a YML or JSON, as in:
|
238
|
+
* `ssh.run source:<source_path>, params:{'date':'2013-03-01', 'unit':'widgets'}`
|
239
|
+
* this example replaces all the keys, preceded by '@' in all source hqls with the value.
|
240
|
+
* The preceding '@' is used to keep from replacing instances
|
241
|
+
of "date" and "unit" in the command/source file; you should have `@date` and `@unit` in your actual HQL
|
242
|
+
if you'd like to replace those tokens.
|
176
243
|
* not specifying node will cause the command to be run on the default node.
|
177
244
|
* ssh sources can be specified with syntax
|
178
245
|
`ssh://<node><file_full_path>`. If node is omitted, default node will be used.
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Mobilize
|
2
|
+
module Git
|
3
|
+
def Git.config
|
4
|
+
Base.config('git')
|
5
|
+
end
|
6
|
+
|
7
|
+
def Git.host(domain)
|
8
|
+
Git.config['domains'][domain]['host']
|
9
|
+
end
|
10
|
+
|
11
|
+
def Git.domains
|
12
|
+
Git.config['domains'].keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def Git.default_domain
|
16
|
+
Git.domains.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def Git.repo_key(domain,repo)
|
20
|
+
begin
|
21
|
+
Git.config['domains'][domain]['repo_keys'][repo]
|
22
|
+
rescue
|
23
|
+
nil #no key for public repos
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# converts a source path or target path to a dst in the context of handler and stage
|
28
|
+
def Git.path_to_dst(path,stage_path,gdrive_slot)
|
29
|
+
red_path = path.split("://").last
|
30
|
+
git_url = Git.url_by_path(red_path)
|
31
|
+
return Dataset.find_or_create_by_url(git_url)
|
32
|
+
end
|
33
|
+
|
34
|
+
def Git.url_by_path(path)
|
35
|
+
path_nodes = path.split("/")
|
36
|
+
domain = path_nodes.first.to_s
|
37
|
+
revision = "HEAD"
|
38
|
+
if Git.domains.include?(domain)
|
39
|
+
repo = path_nodes[1..2].join("/")
|
40
|
+
file_path = path_nodes[3..-1].join("/")
|
41
|
+
else
|
42
|
+
domain = Git.default_domain
|
43
|
+
repo = path_nodes[0..1].join("/")
|
44
|
+
file_path = path_nodes[2..-1].join("/")
|
45
|
+
end
|
46
|
+
url = "git://#{domain}/#{repo}/#{revision}/#{file_path}"
|
47
|
+
return url
|
48
|
+
end
|
49
|
+
|
50
|
+
#return path to tar.gz of git repo
|
51
|
+
def Git.pack(domain,repo,revision="HEAD")
|
52
|
+
repo_dir = Git.pull(domain,repo,revision)
|
53
|
+
repo_name = repo.split("/").last
|
54
|
+
tar_gz_path = "#{repo_dir}/../#{repo_name}.tar.gz"
|
55
|
+
pack_cmd = "cd #{repo_dir} && git archive #{revision} --format=tar.gz > #{tar_gz_path}"
|
56
|
+
pack_cmd.bash(true)
|
57
|
+
FileUtils.rm_r(repo_dir,:force=>true)
|
58
|
+
return tar_gz_path
|
59
|
+
end
|
60
|
+
|
61
|
+
#confirm that git file exists
|
62
|
+
def Git.exists?(url)
|
63
|
+
domain,repo,revision,file_path=[]
|
64
|
+
url.split("/").ie do |url_nodes|
|
65
|
+
domain = url_nodes[2]
|
66
|
+
repo = url_nodes[3..4].join("/")
|
67
|
+
revision = url_nodes[5]
|
68
|
+
file_path = url_nodes[6..-1].join("/")
|
69
|
+
end
|
70
|
+
repo_dir = Git.pull(domain,repo,revision)
|
71
|
+
full_path = "#{repo_dir}/#{file_path}"
|
72
|
+
exists = File.exists?(full_path)
|
73
|
+
if exists
|
74
|
+
FileUtils.rm_r(repo_dir,:force=>true)
|
75
|
+
return exists
|
76
|
+
else
|
77
|
+
raise "Unable to find #{full_path}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#pulls a git repo and sets it to the specified revision in the
|
82
|
+
#specified folder
|
83
|
+
def Git.pull(domain,repo,revision,run_dir=Dir.mktmpdir)
|
84
|
+
domain_properties = Git.config['domains'][domain]
|
85
|
+
user,host= ['user','host'].map{|k| domain_properties[k]}
|
86
|
+
key = Git.repo_key(domain,repo)
|
87
|
+
#create folder for repo and command
|
88
|
+
run_file_path = run_dir + "/cmd.sh"
|
89
|
+
#put together command
|
90
|
+
git_prefix = key ? "ssh-add #{Base.root}/#{key};" : ""
|
91
|
+
git_suffix = (revision=="HEAD" ? " --depth=1" : "; git checkout -q #{revision}")
|
92
|
+
#add keys, clone repo, go to specific revision, execute command
|
93
|
+
full_cmd = "cd #{run_dir};#{git_prefix}git clone -q #{user}@#{host}:#{repo}.git#{git_suffix}"
|
94
|
+
#put command in file, run ssh-agent bash on it
|
95
|
+
File.open(run_file_path,"w") {|f| f.print(full_cmd)}
|
96
|
+
run_cmd = "ssh-agent bash #{run_file_path}"
|
97
|
+
#run the command, it will return an exception if there are issues
|
98
|
+
run_cmd.bash(true)
|
99
|
+
repo_name = repo.split("/").last
|
100
|
+
repo_dir = "#{run_dir}/#{repo_name}"
|
101
|
+
return repo_dir
|
102
|
+
end
|
103
|
+
|
104
|
+
def Git.read_by_dataset_path(dst_path,user_name,*args)
|
105
|
+
domain,repo,revision,file_path = []
|
106
|
+
dst_path.split("/").ie do |path_nodes|
|
107
|
+
domain = path_nodes[0]
|
108
|
+
repo = path_nodes[1..2].join("/")
|
109
|
+
revision = path_nodes[3]
|
110
|
+
file_path = path_nodes[4..-1].join("/")
|
111
|
+
end
|
112
|
+
#slash in front of path
|
113
|
+
repo_dir = Git.pull(domain,repo,revision)
|
114
|
+
full_path = "#{repo_dir}/#{file_path}"
|
115
|
+
result = "cat #{full_path}".bash(true)
|
116
|
+
FileUtils.rm_r(repo_dir,:force=>true)
|
117
|
+
return result
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -1,45 +1,8 @@
|
|
1
1
|
module Mobilize
|
2
2
|
module Ssh
|
3
|
-
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
|
-
def Ssh.tmp_file_dir
|
8
|
-
Ssh.config['tmp_file_dir']
|
9
|
-
end
|
10
|
-
|
11
|
-
def Ssh.host(node)
|
12
|
-
Ssh.config['nodes'][node]['host']
|
13
|
-
end
|
14
|
-
|
15
|
-
def Ssh.gateway(node)
|
16
|
-
Ssh.config['nodes'][node]['gateway']
|
17
|
-
end
|
18
|
-
|
19
|
-
def Ssh.sudoers(node)
|
20
|
-
Ssh.config['nodes'][node]['sudoers']
|
21
|
-
end
|
22
|
-
|
23
|
-
def Ssh.su_all_users(node)
|
24
|
-
Ssh.config['nodes'][node]['su_all_users']
|
25
|
-
end
|
26
|
-
|
27
|
-
def Ssh.nodes
|
28
|
-
Ssh.config['nodes'].keys
|
29
|
-
end
|
30
|
-
|
31
|
-
def Ssh.default_node
|
32
|
-
Ssh.nodes.first
|
33
|
-
end
|
34
|
-
|
35
|
-
#determine if current machine is on host domain, needs gateway if one is provided and it is not
|
36
|
-
def Ssh.needs_gateway?(node)
|
37
|
-
host_domain_name = Ssh.host(node)['name'].split(".")[-2..-1].join(".")
|
38
|
-
return true if Ssh.gateway(node) and Socket.domain_name != host_domain_name
|
39
|
-
end
|
40
|
-
|
3
|
+
#adds convenience methods
|
4
|
+
require "#{File.dirname(__FILE__)}/../helpers/ssh_helper"
|
41
5
|
def Ssh.pop_comm_dir(comm_dir,file_hash)
|
42
|
-
FileUtils.rm_r comm_dir, :force=>true
|
43
6
|
file_hash.each do |fname,fdata|
|
44
7
|
fpath = "#{comm_dir}/#{fname}"
|
45
8
|
#for now, only gz is binary
|
@@ -50,13 +13,6 @@ module Mobilize
|
|
50
13
|
return true if file_hash.keys.length>0
|
51
14
|
end
|
52
15
|
|
53
|
-
def Ssh.set_key_permissions(key_path)
|
54
|
-
#makes sure permissions are set as appropriate for ssh key
|
55
|
-
raise "could not find ssh key at #{key_path}" unless File.exists?(key_path)
|
56
|
-
File.chmod(0600,key_path) unless File.stat(key_path).mode.to_s(8)[3..5] == "600"
|
57
|
-
return true
|
58
|
-
end
|
59
|
-
|
60
16
|
# converts a source path or target path to a dst in the context of handler and stage
|
61
17
|
def Ssh.path_to_dst(path,stage_path,gdrive_slot)
|
62
18
|
has_handler = true if path.index("://")
|
@@ -97,7 +53,6 @@ module Mobilize
|
|
97
53
|
def Ssh.scp(node,from_path,to_path)
|
98
54
|
name,key,port,user = Ssh.host(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
99
55
|
key_path = "#{Base.root}/#{key}"
|
100
|
-
Ssh.set_key_permissions(key_path)
|
101
56
|
opts = {:port=>(port || 22),:keys=>key_path}
|
102
57
|
if Ssh.needs_gateway?(node)
|
103
58
|
gname,gkey,gport,guser = Ssh.gateway(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
@@ -112,25 +67,30 @@ module Mobilize
|
|
112
67
|
return true
|
113
68
|
end
|
114
69
|
|
115
|
-
def Ssh.run(node,command,
|
116
|
-
|
117
|
-
key_path = "#{Base.root}/#{key}"
|
118
|
-
Ssh.set_key_permissions(key_path)
|
70
|
+
def Ssh.run(node,command,user_name,file_hash={},run_params=nil)
|
71
|
+
default_user_name = Ssh.host(node)['user']
|
119
72
|
file_hash ||= {}
|
73
|
+
run_params ||={}
|
120
74
|
#make sure the dir for this command is clear
|
121
|
-
comm_md5 = [
|
122
|
-
comm_dir =
|
123
|
-
#
|
75
|
+
comm_md5 = [user_name,node,command,file_hash.keys.to_s,Time.now.to_f.to_s].join.to_md5
|
76
|
+
comm_dir = Dir.mktmpdir
|
77
|
+
#replace any params in the file_hash and command
|
78
|
+
run_params.each do |k,v|
|
79
|
+
command.gsub!("@#{k}",v)
|
80
|
+
file_hash.each do |name,data|
|
81
|
+
data.gsub!("@#{k}",v)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
#populate comm dir with any files
|
124
85
|
Ssh.pop_comm_dir(comm_dir,file_hash)
|
125
|
-
#move any files up to the node
|
126
|
-
rem_dir = nil
|
127
86
|
#make sure user starts in rem_dir
|
128
87
|
rem_dir = "#{comm_md5}/"
|
129
88
|
#make sure the rem_dir is gone
|
130
89
|
Ssh.fire!(node,"sudo rm -rf #{rem_dir}")
|
131
90
|
if File.exists?(comm_dir)
|
132
91
|
Ssh.scp(node,comm_dir,rem_dir)
|
133
|
-
|
92
|
+
#make sure comm_dir is removed
|
93
|
+
FileUtils.rm_r(comm_dir,:force=>true)
|
134
94
|
else
|
135
95
|
#create folder
|
136
96
|
mkdir_command = "mkdir #{rem_dir}"
|
@@ -142,9 +102,9 @@ module Mobilize
|
|
142
102
|
Ssh.write(node,command,cmd_path)
|
143
103
|
full_cmd = "(cd #{rem_dir} && sh #{cmd_file})"
|
144
104
|
#fire_cmd runs sh on cmd_path, optionally with sudo su
|
145
|
-
if
|
105
|
+
if user_name != default_user_name
|
146
106
|
#make sure user owns the folder and all files
|
147
|
-
fire_cmd = %{sudo chown -R #{
|
107
|
+
fire_cmd = %{sudo chown -R #{user_name} #{rem_dir}; sudo su #{user_name} -c "#{full_cmd}"}
|
148
108
|
rm_cmd = %{sudo rm -rf #{rem_dir}}
|
149
109
|
else
|
150
110
|
fire_cmd = full_cmd
|
@@ -158,7 +118,6 @@ module Mobilize
|
|
158
118
|
def Ssh.fire!(node,cmd)
|
159
119
|
name,key,port,user = Ssh.host(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
160
120
|
key_path = "#{Base.root}/#{key}"
|
161
|
-
Ssh.set_key_permissions(key_path)
|
162
121
|
opts = {:port=>(port || 22),:keys=>key_path}
|
163
122
|
response = if Ssh.needs_gateway?(node)
|
164
123
|
gname,gkey,gport,guser = Ssh.gateway(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
|
@@ -188,17 +147,15 @@ module Mobilize
|
|
188
147
|
def Ssh.write(node,fdata,to_path,binary=false)
|
189
148
|
from_path = Ssh.tmp_file(fdata,binary)
|
190
149
|
Ssh.scp(node,from_path,to_path)
|
191
|
-
|
150
|
+
#make sure local is removed
|
151
|
+
FileUtils.rm_r(from_path,:force=>true)
|
192
152
|
return true
|
193
153
|
end
|
194
154
|
|
195
155
|
def Ssh.tmp_file(fdata,binary=false,fpath=nil)
|
196
156
|
#creates a file under tmp/files with an md5 from the data
|
197
|
-
tmp_file_path = fpath || "#{
|
157
|
+
tmp_file_path = fpath || "#{Dir.mktmpdir}/#{(fdata + Time.now.utc.to_f.to_s).to_md5}"
|
198
158
|
write_mode = binary ? "wb" : "w"
|
199
|
-
#make sure folder is created
|
200
|
-
tmp_file_dir = tmp_file_path.split("/")[0..-2].join("/")
|
201
|
-
FileUtils.mkdir_p(tmp_file_dir)
|
202
159
|
#write data to path
|
203
160
|
File.open(tmp_file_path,write_mode) {|f| f.print(fdata)}
|
204
161
|
return tmp_file_path
|
@@ -208,7 +165,7 @@ module Mobilize
|
|
208
165
|
s = Stage.where(:path=>stage_path).first
|
209
166
|
u = s.job.runner.user
|
210
167
|
user_name = s.params['user']
|
211
|
-
node = s.params['node']
|
168
|
+
node = s.params['node']
|
212
169
|
node = Ssh.default_node unless Ssh.nodes.include?(node)
|
213
170
|
if user_name and !Ssh.sudoers(node).include?(u.name)
|
214
171
|
raise "#{u.name} does not have su permissions for this node"
|
@@ -226,9 +183,12 @@ module Mobilize
|
|
226
183
|
s.sources(gdrive_slot).each do |sdst|
|
227
184
|
split_path = sdst.path.split("/")
|
228
185
|
#if path is to stage output, name with stage name
|
229
|
-
file_name = if split_path.last == "out" and
|
230
|
-
|
186
|
+
file_name = if (split_path.last == "out" and (1..5).to_a.map{|n| "stage#{n.to_s}"}.include?(split_path[-2].to_s))
|
187
|
+
#<jobname>/stage1/out
|
231
188
|
"#{split_path[-2]}.out"
|
189
|
+
elsif (1..5).to_a.map{|n| "stage#{n.to_s}"}.include?(split_path.last[-6..-1])
|
190
|
+
#runner<jobname>stage1
|
191
|
+
"#{split_path.last[-6..-1]}.out"
|
232
192
|
else
|
233
193
|
split_path.last
|
234
194
|
end
|
@@ -255,11 +215,14 @@ module Mobilize
|
|
255
215
|
user_name = Ssh.user_name_by_stage_path(stage_path)
|
256
216
|
file_hash = Ssh.file_hash_by_stage_path(stage_path,gdrive_slot)
|
257
217
|
Gdrive.unslot_worker_by_path(stage_path)
|
258
|
-
|
218
|
+
run_params = params['params']
|
219
|
+
result = Ssh.run(node,command,user_name,file_hash,run_params)
|
259
220
|
#use Gridfs to cache result
|
260
221
|
response = {}
|
261
222
|
response['out_url'] = Dataset.write_by_url("gridfs://#{s.path}/out",result['stdout'].to_s,Gdrive.owner_name)
|
262
223
|
response['err_url'] = Dataset.write_by_url("gridfs://#{s.path}/err",result['stderr'].to_s,Gdrive.owner_name) if result['stderr'].to_s.length>0
|
224
|
+
#is an error if there is no out and there is an err, regardless of signal
|
225
|
+
result['exit_code'] = 500 if result['stdout'].to_s.strip.length==0 and result['stderr'].to_s.strip.length>0
|
263
226
|
response['signal'] = result['exit_code']
|
264
227
|
response
|
265
228
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Mobilize
|
2
|
+
module Ssh
|
3
|
+
def self.config
|
4
|
+
Base.config('ssh')
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.host(node)
|
8
|
+
self.config['nodes'][node]['host']
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.gateway(node)
|
12
|
+
self.config['nodes'][node]['gateway']
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.sudoers(node)
|
16
|
+
self.config['nodes'][node]['sudoers']
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.su_all_users(node)
|
20
|
+
self.config['nodes'][node]['su_all_users']
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.nodes
|
24
|
+
self.config['nodes'].keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.default_node
|
28
|
+
self.nodes.first
|
29
|
+
end
|
30
|
+
|
31
|
+
#determine if current machine is on host domain, needs gateway if one is provided and it is not
|
32
|
+
def self.needs_gateway?(node)
|
33
|
+
host_domain_name = self.host(node)['name'].split(".")[-2..-1].join(".")
|
34
|
+
return true if self.gateway(node) and Socket.domain_name != host_domain_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/mobilize-ssh/version.rb
CHANGED
data/lib/mobilize-ssh.rb
CHANGED
@@ -6,10 +6,10 @@ require "net/scp"
|
|
6
6
|
require "mobilize-ssh/extensions/net-ssh-connection-session"
|
7
7
|
require "mobilize-ssh/extensions/net-ssh-gateway"
|
8
8
|
require "mobilize-ssh/extensions/socket"
|
9
|
-
require "mobilize-ssh/extensions/string"
|
10
9
|
|
11
10
|
module Mobilize
|
12
11
|
module Ssh
|
13
12
|
end
|
14
13
|
end
|
15
14
|
require "mobilize-ssh/handlers/ssh"
|
15
|
+
require "mobilize-ssh/handlers/git"
|
data/lib/samples/git.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
---
|
2
|
+
development:
|
3
|
+
domains:
|
4
|
+
private:
|
5
|
+
host: github.private.com
|
6
|
+
user: git
|
7
|
+
repo_keys:
|
8
|
+
"repo_path_1": 'local/path/to/key1'
|
9
|
+
"repo_path_2": 'local/path/to/key1'
|
10
|
+
public:
|
11
|
+
host: github.com
|
12
|
+
user: git
|
13
|
+
test:
|
14
|
+
domains:
|
15
|
+
public:
|
16
|
+
host: github.com
|
17
|
+
user: git
|
18
|
+
private:
|
19
|
+
host: github.private.com
|
20
|
+
user: git
|
21
|
+
repo_keys:
|
22
|
+
"repo_path_1": 'local/path/to/key1'
|
23
|
+
"repo_path_2": 'local/path/to/key1'
|
24
|
+
production:
|
25
|
+
domains:
|
26
|
+
private:
|
27
|
+
host: github.private.com
|
28
|
+
user: git
|
29
|
+
repo_keys:
|
30
|
+
"repo_path_1": 'local/path/to/key1'
|
31
|
+
"repo_path_2": 'local/path/to/key1'
|
32
|
+
public:
|
33
|
+
host: github.com
|
34
|
+
user: git
|
data/lib/samples/ssh.yml
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
---
|
2
2
|
development:
|
3
|
-
tmp_file_dir: tmp/file/
|
4
3
|
nodes:
|
5
4
|
dev_node:
|
6
5
|
sudoers:
|
@@ -17,7 +16,6 @@ development:
|
|
17
16
|
port: 22
|
18
17
|
user: gateway_user
|
19
18
|
test:
|
20
|
-
tmp_file_dir: tmp/file/
|
21
19
|
nodes:
|
22
20
|
test_node:
|
23
21
|
sudoers:
|
@@ -34,7 +32,6 @@ test:
|
|
34
32
|
port: 22
|
35
33
|
user: gateway_user
|
36
34
|
production:
|
37
|
-
tmp_file_dir: tmp/file/
|
38
35
|
nodes:
|
39
36
|
prod_node:
|
40
37
|
sudoers:
|
data/mobilize-ssh.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
-
gem.add_runtime_dependency "mobilize-base","1.
|
19
|
+
gem.add_runtime_dependency "mobilize-base","1.3"
|
20
20
|
gem.add_runtime_dependency "net-ssh"
|
21
21
|
gem.add_runtime_dependency "net-scp"
|
22
22
|
gem.add_runtime_dependency "net-ssh-gateway"
|
data/test/code2.sh
CHANGED
@@ -1 +1 @@
|
|
1
|
-
tail
|
1
|
+
tail @file
|
data/test/mobilize-ssh_test.rb
CHANGED
@@ -14,8 +14,8 @@ describe "Mobilize" do
|
|
14
14
|
|
15
15
|
gdrive_slot = Mobilize::Gdrive.owner_email
|
16
16
|
puts "create user 'mobilize'"
|
17
|
-
|
18
|
-
u = Mobilize::User.where(:name=>
|
17
|
+
user_name = gdrive_slot.split("@").first
|
18
|
+
u = Mobilize::User.where(:name=>user_name).first
|
19
19
|
r = u.runner
|
20
20
|
|
21
21
|
rb_code_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/code.rb",gdrive_slot)
|
@@ -42,6 +42,7 @@ describe "Mobilize" do
|
|
42
42
|
ssh_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_1.out",gdrive_slot)
|
43
43
|
ssh_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_2.out",gdrive_slot)
|
44
44
|
ssh_target_sheet_3 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_3.out",gdrive_slot)
|
45
|
+
ssh_target_sheet_4 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_4.out",gdrive_slot)
|
45
46
|
[ssh_target_sheet_1,ssh_target_sheet_2,ssh_target_sheet_3].each {|s| s.delete if s}
|
46
47
|
|
47
48
|
ssh_job_rows = ::YAML.load_file("#{Mobilize::Base.root}/test/ssh_job_rows.yml")
|
@@ -59,10 +60,12 @@ describe "Mobilize" do
|
|
59
60
|
ssh_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_1.out",gdrive_slot)
|
60
61
|
ssh_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_2.out",gdrive_slot)
|
61
62
|
ssh_target_sheet_3 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_3.out",gdrive_slot)
|
63
|
+
ssh_target_sheet_4 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_4.out",gdrive_slot)
|
62
64
|
|
63
65
|
assert ssh_target_sheet_1.to_tsv.length > 100
|
64
66
|
assert ssh_target_sheet_2.to_tsv.length > 100
|
65
67
|
assert ssh_target_sheet_3.to_tsv.length > 3
|
68
|
+
assert ssh_target_sheet_4.to_tsv.length > 100
|
66
69
|
|
67
70
|
end
|
68
71
|
|
data/test/ssh_job_rows.yml
CHANGED
@@ -1,18 +1,25 @@
|
|
1
|
+
---
|
1
2
|
- name: test_ssh_1
|
2
3
|
active: true
|
3
4
|
trigger: once
|
4
5
|
status: ""
|
5
|
-
stage1:
|
6
|
-
stage2:
|
6
|
+
stage1: ssh.run node:"test_node", cmd:"ruby code.rb", user:"root", sources:["code.rb", "code.sh"]
|
7
|
+
stage2: gsheet.write source:"stage1", target:"test_ssh_1.out"
|
7
8
|
- name: test_ssh_2
|
8
9
|
active: true
|
9
10
|
trigger: "after test_ssh_1"
|
10
11
|
status: ""
|
11
|
-
stage1:
|
12
|
-
stage2:
|
12
|
+
stage1: ssh.run cmd:"sh code2.sh", user:"root", sources:["code2.sh","test_node/var/log/syslog"], params:{file:"syslog"}
|
13
|
+
stage2: gsheet.write source:"stage1", target:"test_ssh_2.out"
|
13
14
|
- name: test_ssh_3
|
14
15
|
active: true
|
15
16
|
trigger: "after test_ssh_2"
|
16
17
|
status: ""
|
17
|
-
stage1:
|
18
|
-
stage2:
|
18
|
+
stage1: ssh.run cmd:"echo '@test_param'", params:{test_param:"test param successful"}
|
19
|
+
stage2: gsheet.write source:"stage1", target:"test_ssh_3.out"
|
20
|
+
- name: test_ssh_4
|
21
|
+
active: true
|
22
|
+
trigger: "after test_ssh_3"
|
23
|
+
status: ""
|
24
|
+
stage1: ssh.run node:"test_node", user:root, sources:["git://DeNA/mobilize-ssh/test/code.rb","git://DeNA/mobilize-ssh/test/code.sh"], cmd:"ruby code.rb"
|
25
|
+
stage2: gsheet.write source:stage1, target:"test_ssh_4.out"
|
metadata
CHANGED
@@ -1,69 +1,78 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobilize-ssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.31'
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Cassio Paes-Leme
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2013-
|
12
|
+
date: 2013-04-18 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: mobilize-base
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
16
18
|
requirements:
|
17
19
|
- - '='
|
18
20
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
21
|
+
version: '1.3'
|
20
22
|
type: :runtime
|
21
23
|
prerelease: false
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
23
26
|
requirements:
|
24
27
|
- - '='
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
29
|
+
version: '1.3'
|
27
30
|
- !ruby/object:Gem::Dependency
|
28
31
|
name: net-ssh
|
29
32
|
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
30
34
|
requirements:
|
31
|
-
- - '>='
|
35
|
+
- - ! '>='
|
32
36
|
- !ruby/object:Gem::Version
|
33
37
|
version: '0'
|
34
38
|
type: :runtime
|
35
39
|
prerelease: false
|
36
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
37
42
|
requirements:
|
38
|
-
- - '>='
|
43
|
+
- - ! '>='
|
39
44
|
- !ruby/object:Gem::Version
|
40
45
|
version: '0'
|
41
46
|
- !ruby/object:Gem::Dependency
|
42
47
|
name: net-scp
|
43
48
|
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
44
50
|
requirements:
|
45
|
-
- - '>='
|
51
|
+
- - ! '>='
|
46
52
|
- !ruby/object:Gem::Version
|
47
53
|
version: '0'
|
48
54
|
type: :runtime
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
51
58
|
requirements:
|
52
|
-
- - '>='
|
59
|
+
- - ! '>='
|
53
60
|
- !ruby/object:Gem::Version
|
54
61
|
version: '0'
|
55
62
|
- !ruby/object:Gem::Dependency
|
56
63
|
name: net-ssh-gateway
|
57
64
|
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
58
66
|
requirements:
|
59
|
-
- - '>='
|
67
|
+
- - ! '>='
|
60
68
|
- !ruby/object:Gem::Version
|
61
69
|
version: '0'
|
62
70
|
type: :runtime
|
63
71
|
prerelease: false
|
64
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
65
74
|
requirements:
|
66
|
-
- - '>='
|
75
|
+
- - ! '>='
|
67
76
|
- !ruby/object:Gem::Version
|
68
77
|
version: '0'
|
69
78
|
description: mobilize-ssh allows you to automate ssh commands and files across hosts
|
@@ -82,10 +91,12 @@ files:
|
|
82
91
|
- lib/mobilize-ssh/extensions/net-ssh-connection-session.rb
|
83
92
|
- lib/mobilize-ssh/extensions/net-ssh-gateway.rb
|
84
93
|
- lib/mobilize-ssh/extensions/socket.rb
|
85
|
-
- lib/mobilize-ssh/
|
94
|
+
- lib/mobilize-ssh/handlers/git.rb
|
86
95
|
- lib/mobilize-ssh/handlers/ssh.rb
|
96
|
+
- lib/mobilize-ssh/helpers/ssh_helper.rb
|
87
97
|
- lib/mobilize-ssh/tasks.rb
|
88
98
|
- lib/mobilize-ssh/version.rb
|
99
|
+
- lib/samples/git.yml
|
89
100
|
- lib/samples/ssh.yml
|
90
101
|
- mobilize-ssh.gemspec
|
91
102
|
- test/code.rb
|
@@ -97,26 +108,33 @@ files:
|
|
97
108
|
- test/test_helper.rb
|
98
109
|
homepage: http://github.com/dena/mobilize-ssh
|
99
110
|
licenses: []
|
100
|
-
metadata: {}
|
101
111
|
post_install_message:
|
102
112
|
rdoc_options: []
|
103
113
|
require_paths:
|
104
114
|
- lib
|
105
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
106
117
|
requirements:
|
107
|
-
- - '>='
|
118
|
+
- - ! '>='
|
108
119
|
- !ruby/object:Gem::Version
|
109
120
|
version: '0'
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
hash: -1792437726743818090
|
110
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
111
126
|
requirements:
|
112
|
-
- - '>='
|
127
|
+
- - ! '>='
|
113
128
|
- !ruby/object:Gem::Version
|
114
129
|
version: '0'
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
hash: -1792437726743818090
|
115
133
|
requirements: []
|
116
134
|
rubyforge_project:
|
117
|
-
rubygems_version:
|
135
|
+
rubygems_version: 1.8.25
|
118
136
|
signing_key:
|
119
|
-
specification_version:
|
137
|
+
specification_version: 3
|
120
138
|
summary: extend mobilize-base with the ability to run files across hosts
|
121
139
|
test_files:
|
122
140
|
- test/code.rb
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: ab5d4bcff9f392506c76c0685ca82796ca9683b5
|
4
|
-
data.tar.gz: 1f722cc3f065c0f586124f36ccc3b4fe30664382
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: 07c7407655e448f6d961a271bb6d491ab8e61cec75000c21af9937450e535eb764b394a42b3de5852354a4c80f089cc2af8d0445eaefbcfbdf8eb1e45ff05c46
|
7
|
-
data.tar.gz: 71073a430e5aee47305e05f671af47693af7661f3c1f0a6226f18e3298b123870059a82aa9a38fbb8cc602e4b3cdb357e9a6dc24a2aa9b70deed65a070533708
|