mybot 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Botfile +9 -9
- data/CHANGELOG.md +7 -0
- data/README.md +294 -18
- data/lib/mybot.rb +3 -0
- data/lib/mybot/cli.rb +2 -1
- data/lib/mybot/command.rb +23 -4
- data/lib/mybot/commands.rb +5 -5
- data/lib/mybot/core-ext/kernel.rb +21 -0
- data/lib/mybot/fmt.rb +3 -7
- data/lib/mybot/lib.rb +9 -0
- data/lib/mybot/messages.rb +12 -6
- data/lib/mybot/node.rb +62 -68
- data/lib/mybot/recipes.rb +4 -2
- data/lib/mybot/tasks.rb +2 -1
- data/lib/mybot/version.rb +1 -1
- data/mybot.gemspec +1 -1
- data/vendor/lib/{mybot → core}/.gitkeep +0 -0
- data/vendor/lib/core/fs.rb +185 -0
- data/vendor/lib/core/osx/git.rb +31 -0
- data/vendor/lib/core/ubuntu/apache.rb +42 -0
- data/vendor/lib/core/ubuntu/apt.rb +40 -0
- data/vendor/lib/core/ubuntu/git.rb +36 -0
- data/vendor/lib/core/ubuntu/mysql.rb +83 -0
- data/vendor/lib/core/ubuntu/passenger.rb +55 -0
- data/vendor/lib/core/ubuntu/ruby.rb +165 -0
- data/vendor/lib/core/ubuntu/service.rb +30 -0
- data/vendor/lib/core/ubuntu/users.rb +76 -0
- data/vendor/lib/core/ubuntu/vim.rb +31 -0
- data/vendor/{tasks → recipes}/.gitkeep +0 -0
- data/vendor/tpl/{mybot → core}/.gitkeep +0 -0
- data/vendor/tpl/core/ubuntu/apache/apache2.conf.erb +80 -0
- metadata +21 -6
data/Botfile
CHANGED
@@ -6,7 +6,7 @@ include Messages
|
|
6
6
|
namespace :mybot do
|
7
7
|
task :install do
|
8
8
|
if File.exists? Mybot::MYBOT_PATH
|
9
|
-
|
9
|
+
error "mybot is already installed"
|
10
10
|
end
|
11
11
|
|
12
12
|
info "installing mybot to #{Mybot::MYBOT_PATH}"
|
@@ -16,7 +16,7 @@ namespace :mybot do
|
|
16
16
|
|
17
17
|
task :uninstall do
|
18
18
|
unless File.exists? Mybot::MYBOT_PATH
|
19
|
-
|
19
|
+
error "mybot is not installed"
|
20
20
|
end
|
21
21
|
|
22
22
|
info "uninstalling mybot"
|
@@ -29,18 +29,18 @@ namespace :mybot do
|
|
29
29
|
|
30
30
|
task :update do
|
31
31
|
unless File.exists? Mybot::MYBOT_PATH
|
32
|
-
|
32
|
+
error "mybot is not installed"
|
33
33
|
end
|
34
34
|
|
35
35
|
info "updating mybot"
|
36
|
-
FileUtils.rm_rf File.join(LIB_PATH, "
|
37
|
-
FileUtils.mkdir File.join(LIB_PATH, "
|
38
|
-
FileUtils.cp_r File.join(VENDOR_PATH, "lib", "
|
36
|
+
FileUtils.rm_rf File.join(LIB_PATH, "core")
|
37
|
+
FileUtils.mkdir File.join(LIB_PATH, "core")
|
38
|
+
FileUtils.cp_r File.join(VENDOR_PATH, "lib", "core"),
|
39
39
|
File.join(MYBOT_PATH, "lib")
|
40
40
|
|
41
|
-
FileUtils.rm_rf File.join(TPL_PATH, "
|
42
|
-
FileUtils.mkdir File.join(TPL_PATH, "
|
43
|
-
FileUtils.cp_r File.join(VENDOR_PATH, "tpl", "
|
41
|
+
FileUtils.rm_rf File.join(TPL_PATH, "core")
|
42
|
+
FileUtils.mkdir File.join(TPL_PATH, "core")
|
43
|
+
FileUtils.cp_r File.join(VENDOR_PATH, "tpl", "core"),
|
44
44
|
File.join(MYBOT_PATH, "tpl")
|
45
45
|
end
|
46
46
|
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -1,31 +1,307 @@
|
|
1
|
-
#
|
1
|
+
# mybot
|
2
|
+
|
3
|
+
**mybot**, my personal bot
|
2
4
|
|
3
5
|
## Installation
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
```
|
8
|
+
gem install mybot
|
9
|
+
bot mybot:install
|
10
|
+
```
|
11
|
+
|
12
|
+
This will install **myboot** to `$HOME/.mybot`.
|
13
|
+
|
14
|
+
* `.mybot`
|
15
|
+
* `lib` - libraries
|
16
|
+
* `recipes` - your recipes
|
17
|
+
* `tpl` - templates
|
18
|
+
|
19
|
+
## Updating
|
20
|
+
|
21
|
+
```
|
22
|
+
gem update mybot
|
23
|
+
bot mybot:update
|
24
|
+
```
|
25
|
+
|
26
|
+
This will replace `.mybot/lib/mybot/core` and `.mybot/tpl/mybot/core` directories with new, updated ones.
|
7
27
|
|
8
28
|
## Usage
|
9
|
-
**Mybot** searches for tasks in following directories:
|
10
29
|
|
11
|
-
|
12
|
-
|
30
|
+
### Recipes and tasks
|
31
|
+
|
32
|
+
**mybot** searches for recipes in following directories:
|
33
|
+
|
34
|
+
* `~/.mybot/recipes/*.rb`
|
35
|
+
* `.mybot/*.rb`
|
13
36
|
* and in `./Botfile`
|
14
37
|
|
15
|
-
|
16
|
-
|
38
|
+
To list all available tasks type `bot mybot:tasks:list`. Here's example recipe with task `foo:bar`:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
require "mybot"
|
42
|
+
|
43
|
+
namespace :foo do
|
44
|
+
task :bar do
|
45
|
+
puts "foo:bar"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
You can run it by typing `bot foo:bar`.
|
51
|
+
|
52
|
+
### Setting nodes and running commands
|
53
|
+
|
54
|
+
**mybot** operates on nodes which are practically computers you can SSH into. For the most part you will be executing commands using `node.run`:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require "mybot"
|
58
|
+
|
59
|
+
@node = node "1.2.3.4", "user", :password => "123"
|
60
|
+
|
61
|
+
namespace :foo do
|
62
|
+
task :bar do
|
63
|
+
@node.run({ :cmd => "uname -a" })
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
It will execute your command on specified node (mine was Ubuntu Server). The output will be more or less:
|
69
|
+
|
70
|
+
```
|
71
|
+
$ bot foo:bar
|
72
|
+
[ubuntu@1.2.3.4] RUN uname -a
|
73
|
+
Linux ubuntu 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
|
74
|
+
```
|
75
|
+
|
76
|
+
When setting node you can also use SSH keys:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
@node = node "1.2.3.4", "user", :keys => ["/path/to/key"]
|
80
|
+
```
|
81
|
+
|
82
|
+
### Interaction
|
83
|
+
|
84
|
+
Executed commands can ask for your input and you can handle it with `on`:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
task :foo do
|
88
|
+
@node.run({ :cmd => "sudo pwd" }) do |cmd|
|
89
|
+
cmd.on "[sudo] password" do
|
90
|
+
cmd.write "123"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
Here's the output:
|
97
|
+
|
98
|
+
```
|
99
|
+
[ubuntu@192.168.1.7] RUN sudo pwd
|
100
|
+
[sudo] password for ubuntu:
|
101
|
+
[ubuntu@192.168.1.7] WRITE 123<LF>
|
102
|
+
/home/ubuntu
|
103
|
+
```
|
104
|
+
|
105
|
+
### Handling sudo
|
106
|
+
|
107
|
+
Handling sudo is such repetitive task that you can try to handle it automatically by **mybot**:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
task :foo do
|
111
|
+
puts "=> running uname -a"
|
112
|
+
@node.run({ :cmd => "pwd", :sudo => true })
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
It would try to use password provided with node declaration and will fail when the password doesn't work asking you to handle sudo manually. Note that you don't have to prepend command with **sudo** when using `:sudo => true`.
|
117
|
+
|
118
|
+
### Customizing output
|
119
|
+
|
120
|
+
As you saw in previous sections, **mybot** produces fair amount of output when talking with nodes. Everything is controlled by options in `node.fmt`:
|
121
|
+
|
122
|
+
* `use_color` - turn of when your terminal don't support colors
|
123
|
+
* `show_cmd` - show messages like `[user@host] RUN cmd`
|
124
|
+
* `show_stdout` and `show_stdout` - show **STDOUT** and **STDERR** produced by executed commands
|
125
|
+
* `show_stdin` - show input being passed to nodes (messages like `[user@host] WRITE sth<LF>`)
|
126
|
+
|
127
|
+
Each option is set to `true` by default. For example you can customize **mybot** to show only commands without output:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
@node.fmt.show_stdout = false
|
131
|
+
@node.fmt.show_stderr = false
|
132
|
+
@node.fmt.show_stdin = false
|
133
|
+
|
134
|
+
task :foo do
|
135
|
+
@node.run({ :cmd => "uname -a" })
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
or even completely disable it and only display own informations, so adding `@node.fmt.show_cmd = false` to previous example will produce only:
|
140
|
+
|
141
|
+
```
|
142
|
+
=> running uname -a
|
143
|
+
```
|
144
|
+
|
145
|
+
You can use helpers build-in: `info`, `warning` and `error` which aborts execution of the script.
|
146
|
+
|
147
|
+
### Libraries
|
148
|
+
|
149
|
+
You can `use` many provided libraries to extend nodes functionality. The most common is Filesystem library:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
task :foo do
|
153
|
+
if @node.fs.exists?({ :file => "/path/to/file" })
|
154
|
+
content = @node.fs.read({ :file => "/path/to/file" })
|
155
|
+
else
|
156
|
+
@node.fs.create({ :file => "/path/to/file", :content => "abc\n" })
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
### Templates
|
162
|
+
|
163
|
+
There is build-in templating system based on **ERB**.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
@tpl = tpl <<<-TPL
|
167
|
+
This is template
|
168
|
+
param1: <%= param1 %>
|
169
|
+
param2: <%= param2 %>
|
170
|
+
TPL
|
171
|
+
|
172
|
+
task :foo do
|
173
|
+
@node.fs.create({
|
174
|
+
:file => "/path/to/file",
|
175
|
+
:content => @tpl.render({
|
176
|
+
:param1 => "value1",
|
177
|
+
:param2 => "value2"
|
178
|
+
})
|
179
|
+
})
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
In fact you don't want to clutter your recipes with template strings so better use template files:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
@tpl = tpl_file "path/to/tpl"
|
187
|
+
```
|
188
|
+
|
189
|
+
**mybot** will look for templates in `~/.mybot/tpl`
|
190
|
+
|
191
|
+
Downloading and uploading files is simple as:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
task :foo do
|
195
|
+
@node.fs.upload({
|
196
|
+
:from => "/file/to/upload",
|
197
|
+
:to => "/home/ubuntu/file"
|
198
|
+
})
|
199
|
+
|
200
|
+
@node.fs.download({
|
201
|
+
:from => "/home/ubuntu/file,
|
202
|
+
:to => "/tmp/downloaded_file"
|
203
|
+
})
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
There's always *progress bar* so you know if you have time for coffee :)
|
208
|
+
|
209
|
+
There's nice **Ubuntu** library already:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
@node = node "1.2.3.4", "user", :password => "123"
|
213
|
+
@node.use "core/ubuntu/apt"
|
214
|
+
|
215
|
+
task :setup do
|
216
|
+
unless @node.apt.installed?({ :pkg => "name" })
|
217
|
+
@node.apt.install({ :pkg => "name" })
|
218
|
+
else
|
219
|
+
@node.apt.update
|
220
|
+
end
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
Of course you can roll own libraries. Save them to `~/.mybot/lib` folder. Do not touch `~/.mybot/lib/core` because it will be replaced by updates. Your custom lib should look like this:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
class Foo < Mybot::Lib
|
228
|
+
def initialize(node)
|
229
|
+
@node = node
|
230
|
+
end
|
231
|
+
|
232
|
+
def bar
|
233
|
+
puts "foo:bar"
|
234
|
+
end
|
235
|
+
|
236
|
+
def baz?
|
237
|
+
true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
and should be used like this:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
@node.use "foo"
|
246
|
+
|
247
|
+
task :foo
|
248
|
+
if @node.foo.baz?
|
249
|
+
@node.foo.bar
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
### Usefull CLI helpers
|
255
|
+
|
256
|
+
* `wait` will display *Press any key to continue...* message and will wait for any user interaction
|
257
|
+
* `ask` will display question passed as argument and return the answer
|
258
|
+
* `yes?` will display question too but it should return true or false based on user's answer. Only `yes | Yes | y` and similar answers are acceptable. When boolean value cannot be guessed the question is asked again.
|
259
|
+
|
260
|
+
Here's example showing helpers in action:
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
task :foo do
|
264
|
+
wait
|
265
|
+
|
266
|
+
@node.run({ :cmd => "sudo pwd" }) do |cmd|
|
267
|
+
cmd.on "[sudo] password" do
|
268
|
+
# ask's 1st argument is the question and it defaults to empty string
|
269
|
+
# we're displaying STDOUT so question is already asked :)
|
270
|
+
cmd.write ask
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
if yes?("type sth (yes/no): ")
|
275
|
+
puts "you said yes"
|
276
|
+
else
|
277
|
+
puts "you said no"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
```
|
281
|
+
|
282
|
+
Running `foo` task will produce:
|
283
|
+
|
284
|
+
```
|
285
|
+
Press any key to continue...
|
286
|
+
[user@1.2.3.4] RUN sudo pwd
|
287
|
+
[sudo] password for ubuntu: 34
|
288
|
+
|
289
|
+
[user@1.2.3.4] WRITE 34<LF>
|
290
|
+
|
291
|
+
Sorry, try again.
|
292
|
+
[sudo] password for ubuntu: 123
|
17
293
|
|
18
|
-
|
19
|
-
|
20
|
-
namespace :foo do
|
21
|
-
task :bar do
|
22
|
-
puts "foo:bar"
|
23
|
-
end
|
24
|
-
end
|
294
|
+
[user@1.2.3.4] WRITE 123<LF>
|
25
295
|
|
26
|
-
|
296
|
+
/home/ubuntu
|
297
|
+
type sth (yes/no): x
|
298
|
+
type sth (yes/no): y
|
299
|
+
you said yes
|
300
|
+
```
|
27
301
|
|
28
302
|
## Uninstallation
|
29
303
|
|
30
|
-
|
31
|
-
|
304
|
+
```
|
305
|
+
bot uninstall
|
306
|
+
gem uninstall -Iax mybot
|
307
|
+
```
|
data/lib/mybot.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
require "mybot/core-ext/kernel"
|
2
|
+
|
1
3
|
module Mybot
|
2
4
|
autoload :Base, "mybot/base"
|
3
5
|
autoload :Cli, "mybot/cli"
|
4
6
|
autoload :Command, "mybot/command"
|
5
7
|
autoload :Commands, "mybot/commands"
|
6
8
|
autoload :Fmt, "mybot/fmt"
|
9
|
+
autoload :Lib, "mybot/lib"
|
7
10
|
autoload :Messages, "mybot/messages"
|
8
11
|
autoload :Node, "mybot/node"
|
9
12
|
autoload :Recipes, "mybot/recipes"
|
data/lib/mybot/cli.rb
CHANGED
data/lib/mybot/command.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Mybot
|
2
2
|
class Command
|
3
|
-
attr_accessor :out, :stdout, :stderr, :
|
3
|
+
attr_accessor :channel, :out, :stdout, :stderr, :exit_status
|
4
4
|
|
5
5
|
def initialize(node)
|
6
6
|
@node = node
|
@@ -15,7 +15,7 @@ module Mybot
|
|
15
15
|
|
16
16
|
def handle_stdout(data)
|
17
17
|
@out = data
|
18
|
-
@stdout += data
|
18
|
+
@stdout += data
|
19
19
|
@node.fmt.stdout data
|
20
20
|
@handlers.each do |s, proc|
|
21
21
|
proc.call if @out.include? s
|
@@ -24,7 +24,7 @@ module Mybot
|
|
24
24
|
|
25
25
|
def handle_stderr(data)
|
26
26
|
@out = data
|
27
|
-
@stderr += data
|
27
|
+
@stderr += data
|
28
28
|
@node.fmt.stderr data
|
29
29
|
@handlers.each do |s, proc|
|
30
30
|
proc.call if @out.include? s
|
@@ -32,12 +32,31 @@ module Mybot
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def handle_exit
|
35
|
-
@stdout
|
35
|
+
filter(@stdout)
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_sudo
|
39
|
+
on "[sudo] password" do
|
40
|
+
write @node.options[:password]
|
41
|
+
end
|
42
|
+
|
43
|
+
on "Password:" do
|
44
|
+
write @node.options[:password]
|
45
|
+
end
|
46
|
+
|
47
|
+
on "Sorry, try again." do
|
48
|
+
@node.fmt.error "cannot handle sudo automatically, try to it manually"
|
49
|
+
end
|
36
50
|
end
|
37
51
|
|
38
52
|
def write(data)
|
39
53
|
@node.fmt.write data
|
40
54
|
@channel.send_data "#{data}\n"
|
41
55
|
end
|
56
|
+
|
57
|
+
def filter(data)
|
58
|
+
data.gsub!("[sudo] password for #{@node.options[:user]}:", "")
|
59
|
+
data.strip
|
60
|
+
end
|
42
61
|
end
|
43
62
|
end
|