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.
258 259 260 261 262 |
# File 'lib/teleport/util.rb', line 258 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 217 218 219 220 221 |
# File 'lib/teleport/util.rb', line 212 def chmod(file, mode) if File.stat(file).mode != mode begin FileUtils.chmod(mode, file, :verbose => verbose?) rescue NoMethodError # workaround https://bugs.ruby-lang.org/issues/8547 FileUtils.chmod(mode, file) end 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.
270 271 272 273 |
# File 'lib/teleport/util.rb', line 270 def fatal(msg) (msg, RED) exit(1) end |
#gem_if_necessary(gem) ⇒ Object
Install gem if necessary.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/teleport/util.rb', line 294 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
313 314 315 316 317 318 319 |
# File 'lib/teleport/util.rb', line 313 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.
237 238 239 |
# File 'lib/teleport/util.rb', line 237 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.
243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/teleport/util.rb', line 243 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
338 339 340 341 342 343 344 345 346 |
# File 'lib/teleport/util.rb', line 338 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.
286 287 288 289 290 291 |
# File 'lib/teleport/util.rb', line 286 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.
281 282 283 |
# File 'lib/teleport/util.rb', line 281 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.
323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/teleport/util.rb', line 323 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
224 225 226 |
# File 'lib/teleport/util.rb', line 224 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.
229 230 231 232 233 234 |
# File 'lib/teleport/util.rb', line 229 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.
265 266 267 |
# File 'lib/teleport/util.rb', line 265 def warning(msg) ("Warning: #{msg}", YELLOW) end |
#whoami ⇒ Object
Who owns this process?
276 277 278 |
# File 'lib/teleport/util.rb', line 276 def whoami @whoami ||= Etc.getpwuid(Process.uid).name end |