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 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
- fatal "mybot is already installed"
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
- fatal "mybot is not installed"
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
- fatal "mybot is not installed"
32
+ error "mybot is not installed"
33
33
  end
34
34
 
35
35
  info "updating mybot"
36
- FileUtils.rm_rf File.join(LIB_PATH, "mybot")
37
- FileUtils.mkdir File.join(LIB_PATH, "mybot")
38
- FileUtils.cp_r File.join(VENDOR_PATH, "lib", "mybot"),
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, "mybot")
42
- FileUtils.mkdir File.join(TPL_PATH, "mybot")
43
- FileUtils.cp_r File.join(VENDOR_PATH, "tpl", "mybot"),
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
 
@@ -0,0 +1,7 @@
1
+ ### 0.0.2
2
+ * started CHANGELOG
3
+ * updated README to cover all available techniques
4
+ * added handling sudo via `options[:sudo]`
5
+ * renamed `~/.mybot/tasks` to `~/.mybot/recipes`
6
+ * lib support
7
+ * added Kernel.qualified_const_get in core-ext
data/README.md CHANGED
@@ -1,31 +1,307 @@
1
- # Mybot
1
+ # mybot
2
+
3
+ **mybot**, my personal bot
2
4
 
3
5
  ## Installation
4
6
 
5
- gem install mybot
6
- bot install
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
- * `~/.mybot/tasks/`
12
- * `.mybot/`
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
- ## Tasks
16
- This is example task
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
- require "mybot"
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
- and you can run it by typing `bot foo:bar`
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
- bot uninstall
31
- gem uninstall -Iax mybot
304
+ ```
305
+ bot uninstall
306
+ gem uninstall -Iax mybot
307
+ ```
@@ -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"
@@ -1,10 +1,11 @@
1
1
  module Mybot
2
2
  module Cli
3
+ include Messages
3
4
  extend self
4
5
 
5
6
  def start(args)
6
7
  unless args[0]
7
- abort "usage: bot <task>"
8
+ error "usage: bot <task>"
8
9
  end
9
10
 
10
11
  Base.load_recipes
@@ -1,6 +1,6 @@
1
1
  module Mybot
2
2
  class Command
3
- attr_accessor :out, :stdout, :stderr, :channel, :exit_status
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.strip
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.strip
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