mybot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -56,7 +56,7 @@ module Mybot
56
56
 
57
57
  def tpl_file(file)
58
58
  tpl = File.join(TPL_PATH, file)
59
- fatal "template file '#{tpl}' does not exists" unless File.exists?(tpl)
59
+ error "template file '#{tpl}' does not exists" unless File.exists?(tpl)
60
60
  Template.new(File.read(tpl))
61
61
  end
62
62
 
@@ -65,15 +65,15 @@ module Mybot
65
65
  $stdin.gets
66
66
  end
67
67
 
68
- def ask(q)
69
- $stdout.print "#{q}?: "
68
+ def ask(q = "")
69
+ $stdout.print "#{q}"
70
70
  $stdin.gets.chomp
71
71
  end
72
72
 
73
- def yes?(q)
73
+ def yes?(q = "")
74
74
  result = ""
75
75
  loop do
76
- $stdout.print "#{q} (y/n)?: "
76
+ $stdout.print "#{q}"
77
77
  result = $stdin.gets.chomp
78
78
  break if result =~ /y|yes|Y|YES|Yes|n|no|N|NO|No/
79
79
  end
@@ -0,0 +1,21 @@
1
+ module Kernel
2
+ def qualified_const_get(str)
3
+ path = str.to_s.split('::')
4
+ from_root = path[0].empty?
5
+ if from_root
6
+ from_root = []
7
+ path = path[1..-1]
8
+ else
9
+ start_ns = ((Class === self) || (Module === self)) ? self : self.class
10
+ from_root = start_ns.to_s.split('::')
11
+ end
12
+ until from_root.empty?
13
+ begin
14
+ return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
15
+ rescue NameError
16
+ from_root.delete_at(-1)
17
+ end
18
+ end
19
+ path.inject(Object) { |ns,name| ns.const_get(name) }
20
+ end
21
+ end
@@ -33,10 +33,10 @@ module Mybot
33
33
  def write(data)
34
34
  write_format = "WRITE"
35
35
  write_format = write_format.blue.bold if @use_color
36
- enter_format = "<ENTER>"
37
- enter_format = enter_format.red.bold if @use_color
36
+ lf_format = "<LF>"
37
+ lf_format = lf_format.red.bold if @use_color
38
38
  if @show_stdin
39
- $stdout.print "#{prompt} #{write_format} #{data}#{enter_format}\n"
39
+ $stdout.print "\n#{prompt} #{write_format} #{data}#{lf_format}\n"
40
40
  end
41
41
  end
42
42
 
@@ -70,10 +70,6 @@ module Mybot
70
70
  error_format = "ERROR"
71
71
  error_format = error_format.bold.red if @use_color
72
72
  $stdout.print "#{prompt} #{error_format} #{msg}\n"
73
- end
74
-
75
- def fatal(msg)
76
- error(msg)
77
73
  abort
78
74
  end
79
75
 
@@ -0,0 +1,9 @@
1
+ module Mybot
2
+ class Lib
3
+ include Messages
4
+ include Commands
5
+ def method_missing(name, *args, &block)
6
+ error "library '#{self.class}' does not provide method #{name}"
7
+ end
8
+ end
9
+ end
@@ -1,20 +1,26 @@
1
+ require "colored"
2
+
1
3
  module Mybot
2
4
  module Messages
3
5
  def info(msg)
4
- puts "info: #{msg}"
6
+ info_format = "INFO".blue.bold
7
+ puts "#{info_format} #{msg}"
5
8
  end
6
9
 
7
10
  def warning(msg)
8
- puts "warning: #{msg}"
11
+ warning_format = "WARNING".magenta.bold
12
+ puts "#{warning_format} #{msg}"
9
13
  end
10
14
 
11
15
  def error(msg)
12
- puts "error: #{msg}"
16
+ error_format = "ERROR".red.bold
17
+ puts "#{error_format} #{msg}"
18
+ abort
13
19
  end
14
20
 
15
- def fatal(msg)
16
- puts "fatal: #{msg}"
17
- abort
21
+ def task_info(name)
22
+ task_format = "TASK".green.bold
23
+ puts "#{task_format} #{name}"
18
24
  end
19
25
  end
20
26
  end
@@ -3,11 +3,13 @@ require "net/sftp"
3
3
 
4
4
  module Mybot
5
5
  class Node
6
- attr_accessor :host, :user, :ssh, :sftp, :fmt
6
+ include Messages
7
+ attr_accessor :host, :user, :options, :ssh, :sftp, :fmt
7
8
 
8
9
  def initialize(host, user, options = {})
9
10
  @host, @user, @options = host, user, options
10
11
  @fmt = Fmt.new(@host, @user)
12
+ @libs = {}
11
13
  end
12
14
 
13
15
  def ssh
@@ -18,22 +20,78 @@ module Mybot
18
20
  @sftp ||= Net::SFTP.start(@host, @user, @options)
19
21
  end
20
22
 
23
+ def use(lib)
24
+ if lib.is_a?(Array)
25
+ lib.each do |l|
26
+ _use(l)
27
+ end
28
+ else
29
+ _use(lib)
30
+ end
31
+ end
32
+
33
+ def _use(lib)
34
+ lib_name = lib.split("/").last
35
+ lib_file = File.join(LIB_PATH, "#{lib}.rb")
36
+
37
+ unless File.exists?(lib_file)
38
+ error "cannot load lib '#{lib_file}'"
39
+ end
40
+
41
+ require lib_file
42
+ const = lib.split("/").map { |x| x.capitalize }.join("::")
43
+ klass = Kernel.qualified_const_get(const)
44
+
45
+ if klass.instance_method(:initialize).arity == 1
46
+ @libs[lib_name.to_sym] ||= klass.new(self)
47
+ else
48
+ warning "#{lib_name.capitalize} library constructor should " +
49
+ "take one argument"
50
+ end
51
+ end
52
+
53
+ def method_missing(name, *args, &block)
54
+ if @libs[name.to_sym]
55
+ @libs[name.to_sym]
56
+ else
57
+ error "no such library loaded: #{name}"
58
+ end
59
+ end
60
+
21
61
  def run(options, &block)
22
- @fmt.fatal "cmd not specified" unless options[:cmd]
62
+ error "cmd not specified" unless options[:cmd]
23
63
 
24
64
  command = Command.new(self)
25
65
  yield command if block_given?
26
66
 
27
67
  ssh.open_channel do |ch|
28
68
  ch.request_pty do |ch, ok|
29
- @fmt.fatal "cannot request pty" unless ok
69
+ @fmt.error "cannot request pty" unless ok
30
70
  end
31
71
 
32
72
  command.channel = ch
73
+
74
+ if options[:env]
75
+ env = []
76
+ options[:env].each do |k, v|
77
+ env << "#{k}='#{v}'"
78
+ end
79
+ options[:cmd] = "#{env.join(" ")} #{options[:cmd]}"
80
+ end
81
+
82
+ if options[:sudo]
83
+ options[:cmd] = "sudo #{options[:cmd]}"
84
+ command.handle_sudo
85
+ end
86
+
87
+ if options[:cwd]
88
+ options[:cmd] = "cd #{options[:cwd]} && #{options[:cmd]}"
89
+ end
90
+
33
91
  @fmt.run(options[:cmd])
34
92
 
35
93
  ch.exec options[:cmd] do |ch, ok|
36
- @fmt.fatal "run: cannot exec #{options[:cmd]}" unless ok
94
+ @fmt.error "run: cannot exec #{options[:cmd]}" unless ok
37
95
 
38
96
  ch.on_data do |ch, data|
39
97
  command.handle_stdout(data)
@@ -56,69 +114,5 @@ module Mybot
56
114
  ssh.loop
57
115
  return command.stdout_all
58
116
  end
59
-
60
- def exists?(options)
61
- @fmt.fatal "exists?: file not specified" unless options[:file]
62
-
63
- options[:cmd] = "test -e #{options[:file]} && echo EXISTS"
64
- run(options).include?("EXISTS") ? true : false
65
- end
66
-
67
- def create(options)
68
- @fmt.fatal "create: file not specified" unless options[:file]
69
-
70
- options[:cmd] = "mktemp /tmp/mybot.XXXXX"
71
- tmp = run(options)
72
-
73
- options[:content] ||= ""
74
- sftp.file.open(tmp, "w") { |f| f.write options[:content] }
75
- options[:cmd] = "mv #{tmp} #{options[:file]}"
76
- run(options)
77
- end
78
-
79
- def read(options)
80
- @fmt.fatal "read: file not specified" unless options[:file]
81
-
82
- options[:cmd] = "cat #{options[:file]}"
83
- run(options) do |cmd|
84
- cmd.on "No such file or directory" do
85
- @fmt.error "read: file '#{options[:file]}' does not exists"
86
- return ""
87
- end
88
- end
89
- end
90
-
91
- def upload(options)
92
- @fmt.fatal "upload: from not specified" unless options[:from]
93
- @fmt.fatal "upload: to not specified" unless options[:to]
94
- @fmt.upload(options[:from], options[:to])
95
-
96
- sftp.upload!(options[:from], options[:to]) do |event, uploader, *args|
97
- case event
98
- when :put
99
- n = (args[1].to_f * 100 / args[0].size.to_f).to_i
100
- @fmt.progress(n)
101
- when :finish
102
- @fmt.progress(100)
103
- end
104
- end
105
- end
106
-
107
- def download(options)
108
- @fmt.fatal "download: from not specified" unless options[:from]
109
- @fmt.fatal "download: to not specified" unless options[:to]
110
- @fmt.download(options[:from], options[:to])
111
-
112
- sftp.download!(options[:from], options[:to]) do |event, uploader, *args|
113
- case event
114
- when :get
115
- size = args[0].size ? args[0].size : sftp.stat!(options[:from]).size
116
- n = (args[1].to_f * 100 / size.to_f).to_i
117
- @fmt.progress(n)
118
- when :finish
119
- @fmt.progress(100)
120
- end
121
- end
122
- end
123
117
  end
124
118
  end
@@ -1,9 +1,11 @@
1
1
  module Mybot
2
2
  module Recipes
3
+ include Messages
4
+
3
5
  def recipes
4
6
  @recipes ||= (
5
7
  Dir[File.join(GEM_PATH, "Botfile")] +
6
- Dir[File.join(MYBOT_PATH, "tasks", "**", "*.rb")].flatten +
8
+ Dir[File.join(MYBOT_PATH, "recipes", "**", "*.rb")].flatten +
7
9
  Dir[File.join(".mybot", "**", "*.rb")].flatten +
8
10
  Dir["Botfile"]
9
11
  ).reject { |f| !File.exists?(f) }
@@ -16,7 +18,7 @@ module Mybot
16
18
  def load_recipe(r)
17
19
  instance_eval(File.read(r), __FILE__, __LINE__)
18
20
  rescue Exception => e
19
- puts "error: cannot load recipe #{r}"
21
+ warning "cannot load recipe #{r}"
20
22
  raise(e)
21
23
  end
22
24
  end
@@ -17,10 +17,11 @@ module Mybot
17
17
  end
18
18
 
19
19
  def run_task(name)
20
+ task_info name
20
21
  task = find_task(name)
21
22
 
22
23
  unless task
23
- error "cannot find task '#{name}'"
24
+ warning "cannot find task '#{name}'"
24
25
  find_task("mybot:tasks:list")[:block].call
25
26
  abort
26
27
  end
@@ -1,3 +1,3 @@
1
1
  module Mybot
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Sebastian Sito"]
10
10
  spec.email = ["sebastian@hypenode.com"]
11
11
  spec.description = %q{Provision, manage and monitor stuff}
12
- spec.summary = %q{Mybot, your personal bot}
12
+ spec.summary = %q{mybot, my personal bot}
13
13
  spec.homepage = "https://github.com/sebastiansito/mybot"
14
14
  spec.license = "MIT"
15
15
 
File without changes
@@ -0,0 +1,185 @@
1
+ module Core
2
+ class Fs < Mybot::Lib
3
+ def initialize(node)
4
+ @node = node
5
+ end
6
+
7
+ def exists?(options = {})
8
+ unless options[:file] || options[:dir]
9
+ error "file or dir is required"
10
+ end
11
+
12
+ if options[:file]
13
+ options[:cmd] = "test -e #{options[:file]} && echo EXISTS"
14
+ elsif options[:dir]
15
+ options[:cmd] = "test -e #{options[:dir]} && echo EXISTS"
16
+ end
17
+ @node.run(options).include?("EXISTS") ? true : false
18
+ end
19
+
20
+ def create(options = {})
21
+ unless options[:file] || options[:dir]
22
+ error "file or dir is required"
23
+ end
24
+
25
+ if options[:file]
26
+ options[:cmd] = "mktemp /tmp/mybot.XXXXX"
27
+
28
+ # do not use sudo when creating tmp file to be able to move file
29
+ sudo = options[:sudo]
30
+ options[:sudo] = false
31
+ tmp = @node.run(options)
32
+ options[:sudo] = sudo
33
+
34
+ options[:content] ||= ""
35
+ @node.sftp.file.open(tmp, "w") { |f| f.write options[:content] }
36
+ options[:cmd] = "mv #{tmp} #{options[:file]}"
37
+ @node.run(options)
38
+ else
39
+ options[:cmd] = "mkdir -p #{options[:dir]}"
40
+ @node.run(options)
41
+ end
42
+ end
43
+
44
+ def remove(options = {})
45
+ unless options[:file] || options[:dir]
46
+ error "file or dir is required"
47
+ end
48
+
49
+ if options[:file]
50
+ options[:cmd] = "rm -f #{options[:file]}"
51
+ else
52
+ options[:cmd] = "rm -rf #{options[:dir]}"
53
+ end
54
+
55
+ @node.run(options)
56
+ end
57
+
58
+ def read(options = {})
59
+ error "file is required" unless options[:file]
60
+
61
+ options[:cmd] = "cat #{options[:file]}"
62
+ @node.run(options) do |cmd|
63
+ cmd.on "No such file or directory" do
64
+ @node.fmt.error "read: file '#{options[:file]}' does not exists"
65
+ return ""
66
+ end
67
+ end
68
+ end
69
+
70
+ def upload(options = {})
71
+ error "from is required" unless options[:from]
72
+ error "to is required" unless options[:to]
73
+ @node.fmt.upload(options[:from], options[:to])
74
+
75
+ @node.sftp.upload!(options[:from],
76
+ options[:to]) do |event, uploader, *args|
77
+ case event
78
+ when :put
79
+ n = (args[1].to_f * 100 / args[0].size.to_f).to_i
80
+ @node.fmt.progress(n)
81
+ when :finish
82
+ @node.fmt.progress(100)
83
+ end
84
+ end
85
+ end
86
+
87
+ def download(options = {})
88
+ error "from is required" unless options[:from]
89
+ error "to is required" unless options[:to]
90
+ @node.fmt.download(options[:from], options[:to])
91
+
92
+ @node.sftp.download!(options[:from],
93
+ options[:to]) do |event, uploader, *args|
94
+ case event
95
+ when :get
96
+ size = 0
97
+ if args[0].size
98
+ size = args[0].size
99
+ else
100
+ size = @node.sftp.stat!(options[:from]).size
101
+ end
102
+ n = (args[1].to_f * 100 / size.to_f).to_i
103
+ @node.fmt.progress(n)
104
+ when :finish
105
+ @node.fmt.progress(100)
106
+ end
107
+ end
108
+ end
109
+
110
+ def compress(options = {})
111
+ error "from is required" unless options[:from]
112
+ error "to is required" unless options[:to]
113
+
114
+ options[:compressor] ||= "gz"
115
+
116
+ unless ["gz", "bzip2"].include?(options[:compressor])
117
+ error "invalid compressor"
118
+ end
119
+
120
+ options[:cmd] = "tar c"
121
+
122
+ if options[:compressor] == "gz"
123
+ options[:cmd] += "z"
124
+ elsif options[:compressor] == "bzip2"
125
+ options[:cmd] += "j"
126
+ end
127
+
128
+ from = options[:from].split("/")
129
+ from_name = from.pop
130
+ from_path = from.join("/")
131
+
132
+ options[:cmd] += "f #{options[:to]} #{from_name}"
133
+ options[:cwd] = from_path
134
+ @node.run(options)
135
+ end
136
+
137
+ def extract(options = {})
138
+ error "from is required" unless options[:from]
139
+ error "to is required" unless options[:to]
140
+
141
+ options[:compressor] ||= "gz"
142
+
143
+ unless ["gz", "bzip2"].include?(options[:compressor])
144
+ error "invalid compressor"
145
+ end
146
+
147
+ options[:cmd] = "tar x"
148
+
149
+ if options[:compressor] == "gz"
150
+ options[:cmd] += "z"
151
+ elsif options[:compressor] == "bzip2"
152
+ options[:cmd] += "j"
153
+ end
154
+
155
+ options[:cmd] += "f #{options[:from]} -C #{options[:to]}"
156
+ @node.run(options)
157
+ end
158
+
159
+ def link(options = {})
160
+ error "from is required" unless options[:from]
161
+ error "to is required" unless options[:to]
162
+
163
+ options[:cmd] = "ln -s #{options[:from]} #{options[:to]}"
164
+ @node.run(options)
165
+ end
166
+
167
+ def chown(options = {})
168
+ error "dir is required" unless options[:dir]
169
+ error "user is required" unless options[:user]
170
+ error "group is required" unless options[:group]
171
+
172
+ options[:cmd] = "chown -R #{options[:user]}:#{options[:group]} "
173
+ options[:cmd] += "#{options[:dir]}"
174
+
175
+ @node.run(options)
176
+ end
177
+
178
+ def touch(options = {})
179
+ error "file is required" unless options[:file]
180
+
181
+ options[:cmd] = "touch #{options[:file]}"
182
+ @node.run(options)
183
+ end
184
+ end
185
+ end