Module: Teleport::Util
- Extended by:
- Util
- Included in:
- Infer, Infer::Apt, Install, Main, Util
- Defined in:
- lib/teleport/util.rb
Overview
Helper module for executing commands and printing stuff out.
The general idea is to only print commands that are actually interesting. For example, mkdir_if_necessary won’t print anything if the directory already exists. That way we can scan teleport output and see what changes were made without getting lost in repetitive commands that had no actual effect.
Defined Under Namespace
Classes: RunError
Constant Summary collapse
- RESET =
"\e[0m"
- RED =
"\e[1;37;41m"
- GREEN =
"\e[1;37;42m"
- YELLOW =
"\e[1;37;43m"
- BLUE =
"\e[1;37;44m"
- MAGENTA =
"\e[1;37;45m"
- CYAN =
"\e[1;37;46m"
Instance Method Summary collapse
-
#banner(s, color = GREEN) ⇒ Object
A nice printout in green.
-
#chmod(file, mode) ⇒ Object
Chmod file to a new mode.
-
#chown(file, user) ⇒ Object
Chown file to be owned by user.
-
#copy_metadata(src, dst) ⇒ Object
Copy perms and timestamps from src file to dst.
-
#copy_perms(src, dst) ⇒ Object
Copy perms from src file to dst.
-
#cp(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst.
-
#cp_if_necessary(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst, but ONLY if dst doesn’t exist or has different contents than src.
-
#cp_with_mkdir(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst, but create the dst directory first if necessary.
-
#different?(a, b) ⇒ Boolean
Are two files different?.
-
#fails?(command) ⇒ Boolean
Run a command, return true if it fails.
-
#fatal(msg) ⇒ Object
Print a fatal error in red, then exit.
-
#gem_if_necessary(gem) ⇒ Object
Install gem if necessary.
-
#gem_version(name) ⇒ Object
Returns the newest currently installed version of the named gem or false if the gem is not installed.
-
#ln(src, dst) ⇒ Object
Create a symlink from src to dst.
-
#ln_if_necessary(src, dst) ⇒ Object
Create a symlink from src to dst, but only if it hasn’t already been created.
-
#md5sum(path) ⇒ Object
Calculate the md5 checksum for a file.
-
#mkdir(dir, owner = nil, mode = nil) ⇒ Object
Like mkdir -p.
-
#mkdir_if_necessary(dir, owner = nil, mode = nil) ⇒ Object
mkdir only if the directory doesn’t already exist.
-
#mv(src, dst) ⇒ Object
Move src to dst.
-
#mv_with_mkdir(src, dst) ⇒ Object
Move src to dst, but create the dst directory first if necessary.
-
#package_if_necessary(pkg) ⇒ Object
Install pkg if necessary.
-
#package_is_installed?(pkg) ⇒ Boolean
Returns true if the pkg is installed.
-
#process_by_pid?(pidfile) ⇒ Boolean
Returns true if the pidfile exists and that process id exists as well.
-
#rm(file) ⇒ Object
rm a file.
-
#rm_and_mkdir(dir) ⇒ Object
rm a dir and recreate it.
-
#rm_if_necessary(file) ⇒ Object
rm a file, but only if it exists.
-
#run(command, args = nil) ⇒ Object
Run a command, raise an error upon failure.
-
#run_capture(command, *args) ⇒ Object
Run a command, raise an error upon failure.
-
#run_capture_lines(command, *args) ⇒ Object
Run a command and split the result into lines, raise an error upon failure.
-
#run_quietly(command, *args) ⇒ Object
Run a command but don’t send any output to $stdout/$stderr.
-
#run_verbose! ⇒ Object
Make all commands echo before running.
-
#shell(commands) ⇒ Object
Run one or several commands, separate by newlines.
-
#shell_escape(s) ⇒ Object
Escape some text for the shell and enclose it in single quotes if necessary.
-
#succeeds?(command) ⇒ Boolean
Run a command, return true if it succeeds.
-
#warning(msg) ⇒ Object
Print a warning in yellow.
-
#whoami ⇒ Object
Who owns this process?.
Instance Method Details
#banner(s, color = GREEN) ⇒ Object
A nice printout in green.
253 254 255 256 257 |
# File 'lib/teleport/util.rb', line 253 def (s, color = GREEN) s = "#{s} ".ljust(72, " ") $stderr.write "#{color}[#{Time.new.strftime('%H:%M:%S')}] #{s}#{RESET}\n" $stderr.flush end |
#chmod(file, mode) ⇒ Object
Chmod file to a new mode.
212 213 214 215 216 |
# File 'lib/teleport/util.rb', line 212 def chmod(file, mode) if File.stat(file).mode != mode FileUtils.chmod(mode, file, :verbose => verbose?) end end |
#chown(file, user) ⇒ Object
Chown file to be owned by user.
200 201 202 203 204 205 206 207 208 209 |
# File 'lib/teleport/util.rb', line 200 def chown(file, user) user = user.to_s # who is the current owner? @uids ||= {} @uids[user] ||= Etc.getpwnam(user).uid uid = @uids[user] if File.stat(file).uid != uid run "chown #{user}:#{user} '#{file}'" end end |
#copy_metadata(src, dst) ⇒ Object
Copy perms and timestamps from src file to dst.
151 152 153 154 155 |
# File 'lib/teleport/util.rb', line 151 def (src, dst) stat = File.stat(src) File.chmod(stat.mode, dst) File.utime(stat.atime, stat.mtime, dst) end |
#copy_perms(src, dst) ⇒ Object
Copy perms from src file to dst.
145 146 147 148 |
# File 'lib/teleport/util.rb', line 145 def copy_perms(src, dst) stat = File.stat(src) File.chmod(stat.mode, dst) end |
#cp(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst. Optionally, set the mode and owner of dst.
159 160 161 162 163 164 165 166 167 |
# File 'lib/teleport/util.rb', line 159 def cp(src, dst, owner = nil, mode = nil) FileUtils.cp_r(src, dst, :preserve => true, :verbose => verbose?) if owner && !File.symlink?(dst) chown(dst, owner) end if mode chmod(dst, mode) end end |
#cp_if_necessary(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst, but ONLY if dst doesn’t exist or has different contents than src. Optionally, set the mode and owner of dst.
179 180 181 182 183 184 |
# File 'lib/teleport/util.rb', line 179 def cp_if_necessary(src, dst, owner = nil, mode = nil) if !File.exists?(dst) || different?(src, dst) cp(src, dst, owner, mode) true end end |
#cp_with_mkdir(src, dst, owner = nil, mode = nil) ⇒ Object
Copy file or dir from src to dst, but create the dst directory first if necessary. Optionally, set the mode and owner of dst.
171 172 173 174 |
# File 'lib/teleport/util.rb', line 171 def cp_with_mkdir(src, dst, owner = nil, mode = nil) mkdir_if_necessary(File.dirname(dst)) cp(src, dst, owner, mode) end |
#different?(a, b) ⇒ Boolean
Are two files different?
140 141 142 |
# File 'lib/teleport/util.rb', line 140 def different?(a, b) !FileUtils.compare_file(a, b) end |
#fails?(command) ⇒ Boolean
Run a command, return true if it fails.
105 106 107 |
# File 'lib/teleport/util.rb', line 105 def fails?(command) !succeeds?(command) end |
#fatal(msg) ⇒ Object
Print a fatal error in red, then exit.
265 266 267 268 |
# File 'lib/teleport/util.rb', line 265 def fatal(msg) (msg, RED) exit(1) end |
#gem_if_necessary(gem) ⇒ Object
Install gem if necessary.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/teleport/util.rb', line 289 def gem_if_necessary(gem) grep = args = nil if gem =~ /(.*)-(\d+\.\d+\.\d+)$/ gem, version = $1, $2 grep = "^#{gem}.*#{version}" args = " --version #{version}" else grep = "^#{gem}" end if fails?("gem list #{gem} | grep '#{grep}'") "#{gem}..." run "gem install #{gem} #{args} --no-rdoc --no-ri" return true end false end |
#gem_version(name) ⇒ Object
Returns the newest currently installed version of the named gem or false if the gem is not installed
308 309 310 311 312 313 314 |
# File 'lib/teleport/util.rb', line 308 def gem_version(name) spec_out = `gem specification #{name} 2> /dev/null` if !spec_out.empty? spec = Gem::Specification.from_yaml(spec_out) spec.version end end |
#ln(src, dst) ⇒ Object
Create a symlink from src to dst.
232 233 234 |
# File 'lib/teleport/util.rb', line 232 def ln(src, dst) FileUtils.ln_sf(src, dst, :verbose => verbose?) end |
#ln_if_necessary(src, dst) ⇒ Object
Create a symlink from src to dst, but only if it hasn’t already been created.
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/teleport/util.rb', line 238 def ln_if_necessary(src, dst) ln = false if !File.symlink?(dst) ln = true elsif File.readlink(dst) != src rm(dst) ln = true end if ln ln(src, dst) true end end |
#md5sum(path) ⇒ Object
Calculate the md5 checksum for a file
333 334 335 336 337 338 339 340 341 |
# File 'lib/teleport/util.rb', line 333 def md5sum(path) digest, buf = Digest::MD5.new, "" File.open(path) do |f| while f.read(4096, buf) digest.update(buf) end end digest.hexdigest end |
#mkdir(dir, owner = nil, mode = nil) ⇒ Object
Like mkdir -p. Optionally, set the owner and mode.
121 122 123 124 125 |
# File 'lib/teleport/util.rb', line 121 def mkdir(dir, owner = nil, mode = nil) FileUtils.mkdir_p(dir, :verbose => verbose?) chmod(dir, mode) if mode chown(dir, owner) if owner end |
#mkdir_if_necessary(dir, owner = nil, mode = nil) ⇒ Object
mkdir only if the directory doesn’t already exist. Optionally, set the owner and mode.
129 130 131 |
# File 'lib/teleport/util.rb', line 129 def mkdir_if_necessary(dir, owner = nil, mode = nil) mkdir(dir, owner, mode) if !(File.exists?(dir) || File.symlink?(dir)) end |
#mv(src, dst) ⇒ Object
Move src to dst. Because this uses FileUtils, it works even if dst is on a different partition.
188 189 190 |
# File 'lib/teleport/util.rb', line 188 def mv(src, dst) FileUtils.mv(src, dst, :verbose => verbose?) end |
#mv_with_mkdir(src, dst) ⇒ Object
Move src to dst, but create the dst directory first if necessary.
194 195 196 197 |
# File 'lib/teleport/util.rb', line 194 def mv_with_mkdir(src, dst) mkdir_if_necessary(File.dirname(dst)) mv(src, dst) end |
#package_if_necessary(pkg) ⇒ Object
Install pkg if necessary.
281 282 283 284 285 286 |
# File 'lib/teleport/util.rb', line 281 def package_if_necessary(pkg) if !package_is_installed?(pkg) "#{pkg}..." run "apt-get -y install #{pkg}" end end |
#package_is_installed?(pkg) ⇒ Boolean
Returns true if the pkg is installed.
276 277 278 |
# File 'lib/teleport/util.rb', line 276 def package_is_installed?(pkg) succeeds?("dpkg-query -f='${Status}' -W #{pkg} 2>&1 | grep 'install ok installed'") end |
#process_by_pid?(pidfile) ⇒ Boolean
Returns true if the pidfile exists and that process id exists as well.
318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/teleport/util.rb', line 318 def process_by_pid?(pidfile) begin if File.exists?(pidfile) pid = File.read(pidfile).to_i if pid != 0 Process.kill(0, pid) return true end end rescue Errno::ENOENT, Errno::ESRCH end false end |
#rm(file) ⇒ Object
rm a file
219 220 221 |
# File 'lib/teleport/util.rb', line 219 def rm(file) FileUtils.rm(file, :force => true, :verbose => verbose?) end |
#rm_and_mkdir(dir) ⇒ Object
rm a dir and recreate it.
134 135 136 137 |
# File 'lib/teleport/util.rb', line 134 def rm_and_mkdir(dir) raise "don't do this" if dir == "" run "rm -rf #{dir} && mkdir -p #{dir}" end |
#rm_if_necessary(file) ⇒ Object
rm a file, but only if it exists.
224 225 226 227 228 229 |
# File 'lib/teleport/util.rb', line 224 def rm_if_necessary(file) if File.exists?(file) rm(file) true end end |
#run(command, args = nil) ⇒ Object
Run a command, raise an error upon failure. Output goes to $stdout/$stderr.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/teleport/util.rb', line 40 def run(command, args = nil) line = nil if args args = args.map(&:to_s) line = "#{command} #{args.join(" ")}" vputs line system(command, *args) else line = command vputs line system(command) end if $? != 0 if $?.termsig == Signal.list["INT"] raise "#{line} interrupted" end raise RunError, "#{line} failed : #{$?.to_i / 256}" end end |
#run_capture(command, *args) ⇒ Object
Run a command, raise an error upon failure. The output is captured as a string and returned.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/teleport/util.rb', line 62 def run_capture(command, *args) if !args.empty? args = args.flatten.map { |i| shell_escape(i) }.join(" ") command = "#{command} #{args}" end result = `#{command}` if $? != 0 if $?.termsig == Signal.list["INT"] raise "#{command} interrupted" end raise RunError, "#{command} failed : #{$?.to_i / 256} #{result.inspect}" end result end |
#run_capture_lines(command, *args) ⇒ Object
Run a command and split the result into lines, raise an error upon failure. The output is captured as an array of strings and returned.
80 81 82 |
# File 'lib/teleport/util.rb', line 80 def run_capture_lines(command, *args) run_capture(command, args).split("\n") end |
#run_quietly(command, *args) ⇒ Object
Run a command but don’t send any output to $stdout/$stderr.
85 86 87 88 89 90 91 |
# File 'lib/teleport/util.rb', line 85 def run_quietly(command, *args) if !args.empty? args = args.flatten.map { |i| shell_escape(i) }.join(" ") command = "#{command} #{args}" end run("#{command} > /dev/null 2> /dev/null") end |
#run_verbose! ⇒ Object
Make all commands echo before running.
34 35 36 |
# File 'lib/teleport/util.rb', line 34 def run_verbose! @run_verbose = true end |
#shell(commands) ⇒ Object
Run one or several commands, separate by newlines.
94 95 96 |
# File 'lib/teleport/util.rb', line 94 def shell(commands) commands.split("\n").each { |i| run(i) } end |
#shell_escape(s) ⇒ Object
Escape some text for the shell and enclose it in single quotes if necessary.
111 112 113 114 115 116 117 118 |
# File 'lib/teleport/util.rb', line 111 def shell_escape(s) s = s.to_s if s !~ /^[0-9A-Za-z+,.\/:=@_-]+$/ s = s.gsub("'") { "'\\''" } s = "'#{s}'" end s end |
#succeeds?(command) ⇒ Boolean
Run a command, return true if it succeeds.
99 100 101 102 |
# File 'lib/teleport/util.rb', line 99 def succeeds?(command) system("#{command} > /dev/null 2> /dev/null") $? == 0 end |
#warning(msg) ⇒ Object
Print a warning in yellow.
260 261 262 |
# File 'lib/teleport/util.rb', line 260 def warning(msg) ("Warning: #{msg}", YELLOW) end |
#whoami ⇒ Object
Who owns this process?
271 272 273 |
# File 'lib/teleport/util.rb', line 271 def whoami @whoami ||= Etc.getpwuid(Process.uid).name end |